]> xenbits.xensource.com Git - people/julieng/freebsd.git/commitdiff
Update hostapd/wpa_supplicant to version 2.5.
authorrpaulo <rpaulo@FreeBSD.org>
Sun, 18 Oct 2015 21:38:25 +0000 (21:38 +0000)
committerrpaulo <rpaulo@FreeBSD.org>
Sun, 18 Oct 2015 21:38:25 +0000 (21:38 +0000)
Tested by several people on current@/wireless@.

Relnotes: yes

275 files changed:
1  2 
contrib/wpa/hostapd/ChangeLog
contrib/wpa/hostapd/config_file.c
contrib/wpa/hostapd/config_file.h
contrib/wpa/hostapd/ctrl_iface.c
contrib/wpa/hostapd/defconfig
contrib/wpa/hostapd/hlr_auc_gw.c
contrib/wpa/hostapd/hlr_auc_gw.milenage_db
contrib/wpa/hostapd/hostapd.conf
contrib/wpa/hostapd/hostapd_cli.c
contrib/wpa/hostapd/main.c
contrib/wpa/hs20/client/Makefile
contrib/wpa/hs20/client/osu_client.c
contrib/wpa/hs20/client/spp_client.c
contrib/wpa/patches/openssl-0.9.8zf-tls-extensions.patch
contrib/wpa/src/ap/accounting.c
contrib/wpa/src/ap/acs.c
contrib/wpa/src/ap/ap_config.c
contrib/wpa/src/ap/ap_config.h
contrib/wpa/src/ap/ap_drv_ops.c
contrib/wpa/src/ap/ap_drv_ops.h
contrib/wpa/src/ap/ap_list.c
contrib/wpa/src/ap/ap_list.h
contrib/wpa/src/ap/authsrv.c
contrib/wpa/src/ap/beacon.c
contrib/wpa/src/ap/beacon.h
contrib/wpa/src/ap/ctrl_iface_ap.c
contrib/wpa/src/ap/dfs.c
contrib/wpa/src/ap/drv_callbacks.c
contrib/wpa/src/ap/eap_user_db.c
contrib/wpa/src/ap/hostapd.c
contrib/wpa/src/ap/hostapd.h
contrib/wpa/src/ap/hw_features.c
contrib/wpa/src/ap/hw_features.h
contrib/wpa/src/ap/ieee802_11.c
contrib/wpa/src/ap/ieee802_11.h
contrib/wpa/src/ap/ieee802_11_auth.c
contrib/wpa/src/ap/ieee802_11_auth.h
contrib/wpa/src/ap/ieee802_11_ht.c
contrib/wpa/src/ap/ieee802_11_vht.c
contrib/wpa/src/ap/ieee802_1x.c
contrib/wpa/src/ap/ieee802_1x.h
contrib/wpa/src/ap/ndisc_snoop.c
contrib/wpa/src/ap/sta_info.c
contrib/wpa/src/ap/sta_info.h
contrib/wpa/src/ap/utils.c
contrib/wpa/src/ap/vlan_init.c
contrib/wpa/src/ap/vlan_init.h
contrib/wpa/src/ap/vlan_util.c
contrib/wpa/src/ap/wmm.c
contrib/wpa/src/ap/wpa_auth.c
contrib/wpa/src/ap/wpa_auth.h
contrib/wpa/src/ap/wpa_auth_ft.c
contrib/wpa/src/ap/wpa_auth_glue.c
contrib/wpa/src/ap/wpa_auth_i.h
contrib/wpa/src/ap/wpa_auth_ie.c
contrib/wpa/src/ap/wps_hostapd.c
contrib/wpa/src/ap/x_snoop.c
contrib/wpa/src/common/common_module_tests.c
contrib/wpa/src/common/defs.h
contrib/wpa/src/common/hw_features_common.c
contrib/wpa/src/common/hw_features_common.h
contrib/wpa/src/common/ieee802_11_common.c
contrib/wpa/src/common/ieee802_11_common.h
contrib/wpa/src/common/ieee802_11_defs.h
contrib/wpa/src/common/privsep_commands.h
contrib/wpa/src/common/qca-vendor.h
contrib/wpa/src/common/sae.c
contrib/wpa/src/common/sae.h
contrib/wpa/src/common/version.h
contrib/wpa/src/common/wpa_common.c
contrib/wpa/src/common/wpa_common.h
contrib/wpa/src/common/wpa_ctrl.c
contrib/wpa/src/common/wpa_ctrl.h
contrib/wpa/src/crypto/crypto.h
contrib/wpa/src/crypto/crypto_module_tests.c
contrib/wpa/src/crypto/crypto_openssl.c
contrib/wpa/src/crypto/dh_groups.c
contrib/wpa/src/crypto/fips_prf_openssl.c
contrib/wpa/src/crypto/ms_funcs.c
contrib/wpa/src/crypto/ms_funcs.h
contrib/wpa/src/crypto/random.c
contrib/wpa/src/crypto/sha1-tlsprf.c
contrib/wpa/src/crypto/sha1-tprf.c
contrib/wpa/src/crypto/sha256-kdf.c
contrib/wpa/src/crypto/sha384-prf.c
contrib/wpa/src/crypto/sha384.h
contrib/wpa/src/crypto/tls.h
contrib/wpa/src/crypto/tls_gnutls.c
contrib/wpa/src/crypto/tls_internal.c
contrib/wpa/src/crypto/tls_none.c
contrib/wpa/src/crypto/tls_openssl.c
contrib/wpa/src/drivers/driver.h
contrib/wpa/src/drivers/driver_bsd.c
contrib/wpa/src/drivers/driver_ndis.c
contrib/wpa/src/drivers/driver_nl80211.h
contrib/wpa/src/drivers/driver_nl80211_android.c
contrib/wpa/src/drivers/driver_nl80211_capa.c
contrib/wpa/src/drivers/driver_nl80211_event.c
contrib/wpa/src/drivers/driver_nl80211_scan.c
contrib/wpa/src/drivers/driver_privsep.c
contrib/wpa/src/drivers/drivers.c
contrib/wpa/src/eap_common/eap_common.c
contrib/wpa/src/eap_common/eap_fast_common.c
contrib/wpa/src/eap_common/eap_pwd_common.c
contrib/wpa/src/eap_common/eap_pwd_common.h
contrib/wpa/src/eap_common/eap_sake_common.c
contrib/wpa/src/eap_common/ikev2_common.c
contrib/wpa/src/eap_peer/eap.c
contrib/wpa/src/eap_peer/eap.h
contrib/wpa/src/eap_peer/eap_aka.c
contrib/wpa/src/eap_peer/eap_eke.c
contrib/wpa/src/eap_peer/eap_fast.c
contrib/wpa/src/eap_peer/eap_gpsk.c
contrib/wpa/src/eap_peer/eap_i.h
contrib/wpa/src/eap_peer/eap_mschapv2.c
contrib/wpa/src/eap_peer/eap_pax.c
contrib/wpa/src/eap_peer/eap_peap.c
contrib/wpa/src/eap_peer/eap_pwd.c
contrib/wpa/src/eap_peer/eap_sake.c
contrib/wpa/src/eap_peer/eap_sim.c
contrib/wpa/src/eap_peer/eap_tls.c
contrib/wpa/src/eap_peer/eap_tls_common.c
contrib/wpa/src/eap_peer/eap_tls_common.h
contrib/wpa/src/eap_peer/eap_ttls.c
contrib/wpa/src/eap_peer/eap_wsc.c
contrib/wpa/src/eap_server/eap.h
contrib/wpa/src/eap_server/eap_i.h
contrib/wpa/src/eap_server/eap_server.c
contrib/wpa/src/eap_server/eap_server_eke.c
contrib/wpa/src/eap_server/eap_server_fast.c
contrib/wpa/src/eap_server/eap_server_mschapv2.c
contrib/wpa/src/eap_server/eap_server_peap.c
contrib/wpa/src/eap_server/eap_server_pwd.c
contrib/wpa/src/eap_server/eap_server_tls.c
contrib/wpa/src/eap_server/eap_server_tls_common.c
contrib/wpa/src/eap_server/eap_server_ttls.c
contrib/wpa/src/eap_server/eap_tls_common.h
contrib/wpa/src/eapol_auth/eapol_auth_sm.c
contrib/wpa/src/eapol_auth/eapol_auth_sm.h
contrib/wpa/src/eapol_supp/eapol_supp_sm.c
contrib/wpa/src/fst/Makefile
contrib/wpa/src/fst/fst.c
contrib/wpa/src/fst/fst.h
contrib/wpa/src/fst/fst_ctrl_aux.c
contrib/wpa/src/fst/fst_ctrl_aux.h
contrib/wpa/src/fst/fst_ctrl_defs.h
contrib/wpa/src/fst/fst_ctrl_iface.c
contrib/wpa/src/fst/fst_ctrl_iface.h
contrib/wpa/src/fst/fst_defs.h
contrib/wpa/src/fst/fst_group.c
contrib/wpa/src/fst/fst_group.h
contrib/wpa/src/fst/fst_iface.c
contrib/wpa/src/fst/fst_iface.h
contrib/wpa/src/fst/fst_internal.h
contrib/wpa/src/fst/fst_session.c
contrib/wpa/src/fst/fst_session.h
contrib/wpa/src/p2p/p2p.c
contrib/wpa/src/p2p/p2p.h
contrib/wpa/src/p2p/p2p_build.c
contrib/wpa/src/p2p/p2p_dev_disc.c
contrib/wpa/src/p2p/p2p_go_neg.c
contrib/wpa/src/p2p/p2p_group.c
contrib/wpa/src/p2p/p2p_i.h
contrib/wpa/src/p2p/p2p_invitation.c
contrib/wpa/src/p2p/p2p_parse.c
contrib/wpa/src/p2p/p2p_pd.c
contrib/wpa/src/p2p/p2p_utils.c
contrib/wpa/src/radius/radius.c
contrib/wpa/src/radius/radius_das.c
contrib/wpa/src/radius/radius_server.c
contrib/wpa/src/radius/radius_server.h
contrib/wpa/src/rsn_supp/tdls.c
contrib/wpa/src/rsn_supp/wpa.c
contrib/wpa/src/rsn_supp/wpa_ft.c
contrib/wpa/src/rsn_supp/wpa_ie.c
contrib/wpa/src/rsn_supp/wpa_ie.h
contrib/wpa/src/tls/libtommath.c
contrib/wpa/src/tls/tlsv1_client.c
contrib/wpa/src/tls/tlsv1_client.h
contrib/wpa/src/tls/tlsv1_server.c
contrib/wpa/src/tls/tlsv1_server.h
contrib/wpa/src/tls/x509v3.c
contrib/wpa/src/utils/browser-wpadebug.c
contrib/wpa/src/utils/common.c
contrib/wpa/src/utils/common.h
contrib/wpa/src/utils/eloop.c
contrib/wpa/src/utils/http_curl.c
contrib/wpa/src/utils/includes.h
contrib/wpa/src/utils/os.h
contrib/wpa/src/utils/os_internal.c
contrib/wpa/src/utils/os_none.c
contrib/wpa/src/utils/os_unix.c
contrib/wpa/src/utils/os_win32.c
contrib/wpa/src/utils/radiotap.c
contrib/wpa/src/utils/utils_module_tests.c
contrib/wpa/src/utils/wpa_debug.c
contrib/wpa/src/utils/wpa_debug.h
contrib/wpa/src/utils/wpabuf.c
contrib/wpa/src/wps/http_client.c
contrib/wpa/src/wps/http_server.c
contrib/wpa/src/wps/httpread.c
contrib/wpa/src/wps/ndef.c
contrib/wpa/src/wps/wps.c
contrib/wpa/src/wps/wps.h
contrib/wpa/src/wps/wps_attr_parse.c
contrib/wpa/src/wps/wps_attr_parse.h
contrib/wpa/src/wps/wps_common.c
contrib/wpa/src/wps/wps_defs.h
contrib/wpa/src/wps/wps_enrollee.c
contrib/wpa/src/wps/wps_er.c
contrib/wpa/src/wps/wps_er_ssdp.c
contrib/wpa/src/wps/wps_module_tests.c
contrib/wpa/src/wps/wps_registrar.c
contrib/wpa/src/wps/wps_upnp.c
contrib/wpa/src/wps/wps_upnp_ap.c
contrib/wpa/src/wps/wps_upnp_event.c
contrib/wpa/src/wps/wps_upnp_ssdp.c
contrib/wpa/src/wps/wps_upnp_web.c
contrib/wpa/src/wps/wps_validate.c
contrib/wpa/wpa_supplicant/ChangeLog
contrib/wpa/wpa_supplicant/ap.c
contrib/wpa/wpa_supplicant/ap.h
contrib/wpa/wpa_supplicant/bss.c
contrib/wpa/wpa_supplicant/bss.h
contrib/wpa/wpa_supplicant/config.c
contrib/wpa/wpa_supplicant/config.h
contrib/wpa/wpa_supplicant/config_file.c
contrib/wpa/wpa_supplicant/config_ssid.h
contrib/wpa/wpa_supplicant/ctrl_iface.c
contrib/wpa/wpa_supplicant/ctrl_iface_named_pipe.c
contrib/wpa/wpa_supplicant/ctrl_iface_udp.c
contrib/wpa/wpa_supplicant/ctrl_iface_unix.c
contrib/wpa/wpa_supplicant/dbus/dbus_new.c
contrib/wpa/wpa_supplicant/dbus/dbus_new.h
contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers.c
contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers.h
contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers_p2p.c
contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers_p2p.h
contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers_wps.c
contrib/wpa/wpa_supplicant/dbus/dbus_new_helpers.c
contrib/wpa/wpa_supplicant/dbus/dbus_new_introspect.c
contrib/wpa/wpa_supplicant/dbus/dbus_old.c
contrib/wpa/wpa_supplicant/dbus/dbus_old_handlers.c
contrib/wpa/wpa_supplicant/defconfig
contrib/wpa/wpa_supplicant/driver_i.h
contrib/wpa/wpa_supplicant/eapol_test.c
contrib/wpa/wpa_supplicant/eapol_test.py
contrib/wpa/wpa_supplicant/events.c
contrib/wpa/wpa_supplicant/hs20_supplicant.c
contrib/wpa/wpa_supplicant/ibss_rsn.c
contrib/wpa/wpa_supplicant/interworking.c
contrib/wpa/wpa_supplicant/main.c
contrib/wpa/wpa_supplicant/mesh.c
contrib/wpa/wpa_supplicant/mesh_mpm.c
contrib/wpa/wpa_supplicant/mesh_rsn.c
contrib/wpa/wpa_supplicant/notify.c
contrib/wpa/wpa_supplicant/notify.h
contrib/wpa/wpa_supplicant/p2p_supplicant.c
contrib/wpa/wpa_supplicant/p2p_supplicant.h
contrib/wpa/wpa_supplicant/p2p_supplicant_sd.c
contrib/wpa/wpa_supplicant/preauth_test.c
contrib/wpa/wpa_supplicant/scan.c
contrib/wpa/wpa_supplicant/sme.c
contrib/wpa/wpa_supplicant/wpa_cli.c
contrib/wpa/wpa_supplicant/wpa_priv.c
contrib/wpa/wpa_supplicant/wpa_supplicant.c
contrib/wpa/wpa_supplicant/wpa_supplicant.conf
contrib/wpa/wpa_supplicant/wpa_supplicant_i.h
contrib/wpa/wpa_supplicant/wpas_glue.c
contrib/wpa/wpa_supplicant/wpas_glue.h
contrib/wpa/wpa_supplicant/wps_supplicant.c
contrib/wpa/wpa_supplicant/wps_supplicant.h
usr.sbin/wpa/Makefile.crypto
usr.sbin/wpa/hostapd/Makefile
usr.sbin/wpa/wpa_supplicant/Makefile

index e6f8c6a03e07942ce77fb6f8074b26adfee47b4e,0000000000000000000000000000000000000000..af54e1e5b4e4cf9df6f5d6a4b57983e46539c595
mode 100644,000000..100644
--- /dev/null
@@@ -1,1035 -1,0 +1,1071 @@@
 +ChangeLog for hostapd
 +
++2015-09-27 - v2.5
++      * fixed WPS UPnP vulnerability with HTTP chunked transfer encoding
++        [http://w1.fi/security/2015-2/] (CVE-2015-4141)
++      * fixed WMM Action frame parser
++        [http://w1.fi/security/2015-3/] (CVE-2015-4142)
++      * fixed EAP-pwd server missing payload length validation
++        [http://w1.fi/security/2015-4/]
++        (CVE-2015-4143, CVE-2015-4144, CVE-2015-4145)
++      * fixed validation of WPS and P2P NFC NDEF record payload length
++        [http://w1.fi/security/2015-5/]
++      * nl80211:
++        - fixed vendor command handling to check OUI properly
++      * fixed hlr_auc_gw build with OpenSSL
++      * hlr_auc_gw: allow Milenage RES length to be reduced
++      * disable HT for a station that does not support WMM/QoS
++      * added support for hashed password (NtHash) in EAP-pwd server
++      * fixed and extended dynamic VLAN cases
++      * added EAP-EKE server support for deriving Session-Id
++      * set Acct-Session-Id to a random value to make it more likely to be
++        unique even if the device does not have a proper clock
++      * added more 2.4 GHz channels for 20/40 MHz HT co-ex scan
++      * modified SAE routines to be more robust and PWE generation to be
++        stronger against timing attacks
++      * added support for Brainpool Elliptic Curves with SAE
++      * increases maximum value accepted for cwmin/cwmax
++      * added support for CCMP-256 and GCMP-256 as group ciphers with FT
++      * added Fast Session Transfer (FST) module
++      * removed optional fields from RSNE when using FT with PMF
++        (workaround for interoperability issues with iOS 8.4)
++      * added EAP server support for TLS session resumption
++      * fixed key derivation for Suite B 192-bit AKM (this breaks
++        compatibility with the earlier version)
++      * added mechanism to track unconnected stations and do minimal band
++        steering
++      * number of small fixes
++
 +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
 +      * improved debug logging (human readable event names, interface name
 +        included in more entries)
 +      * added number of small changes to make it easier for static analyzers
 +        to understand the implementation
 +      * added a workaround for Windows 7 Michael MIC failure reporting and
 +        use of the Secure bit in EAPOL-Key msg 3/4
 +      * fixed number of small bugs (see git logs for more details)
 +      * changed OpenSSL to read full certificate chain from server_cert file
 +      * nl80211: number of updates to use new cfg80211/nl80211 functionality
 +        - replace monitor interface with nl80211 commands
 +        - additional information for driver-based AP SME
 +      * EAP-pwd:
 +        - fix KDF for group 21 and zero-padding
 +        - added support for fragmentation
 +        - increased maximum number of hunting-and-pecking iterations
 +      * avoid excessive Probe Response retries for broadcast Probe Request
 +        frames (only with drivers using hostapd SME/MLME)
 +      * added preliminary support for using TLS v1.2 (CONFIG_TLSV12=y)
 +      * fixed WPS operation stopping on dual concurrent AP
 +      * added wps_rf_bands configuration parameter for overriding RF Bands
 +        value for WPS
 +      * added support for getting per-device PSK from RADIUS Tunnel-Password
 +      * added support for libnl 3.2 and newer
 +      * increased initial group key handshake retransmit timeout to 500 ms
 +      * added a workaround for 4-way handshake to update SNonce even after
 +        having sent EAPOL-Key 3/4 to avoid issues with some supplicant
 +        implementations that can change SNonce for each EAP-Key 2/4
 +      * added a workaround for EAPOL-Key 4/4 using incorrect type value in
 +        WPA2 mode (some deployed stations use WPA type in that message)
 +      * added a WPS workaround for mixed mode AP Settings with Windows 7
 +      * changed WPS AP PIN disabling mechanism to disable the PIN after 10
 +        consecutive failures in addition to using the exponential lockout
 +        period
 +      * added support for WFA Hotspot 2.0
 +        - GAS/ANQP advertisement of network information
 +        - disable_dgaf parameter to disable downstream group-addressed
 +          forwarding
 +      * simplified licensing terms by selecting the BSD license as the only
 +        alternative
 +      * EAP-SIM: fixed re-authentication not to update pseudonym
 +      * EAP-SIM: use Notification round before EAP-Failure
 +      * EAP-AKA: added support for AT_COUNTER_TOO_SMALL
 +      * EAP-AKA: skip AKA/Identity exchange if EAP identity is recognized
 +      * EAP-AKA': fixed identity for MK derivation
 +      * EAP-AKA': updated to RFC 5448 (username prefixes changed); note: this
 +        breaks interoperability with older versions
 +      * EAP-SIM/AKA: allow pseudonym to be used after unknown reauth id
 +      * changed ANonce to be a random number instead of Counter-based
 +      * added support for canceling WPS operations with hostapd_cli wps_cancel
 +      * fixed EAP/WPS to PSK transition on reassociation in cases where
 +        deauthentication is missed
 +      * hlr_auc_gw enhancements:
 +        - a new command line parameter -u can be used to enable updating of
 +          SQN in Milenage file
 +        - use 5 bit IND for SQN updates
 +        - SQLite database can now be used to store Milenage information
 +      * EAP-SIM/AKA DB: added optional use of SQLite database for pseudonyms
 +        and reauth data
 +      * added support for Chargeable-User-Identity (RFC 4372)
 +      * added radius_auth_req_attr and radius_acct_req_attr configuration
 +        parameters to allow adding/overriding of RADIUS attributes in
 +        Access-Request and Accounting-Request packets
 +      * added support for RADIUS dynamic authorization server (RFC 5176)
 +      * added initial support for WNM operations
 +        - BSS max idle period
 +        - WNM-Sleep Mode
 +      * added new WPS NFC ctrl_iface mechanism
 +        - removed obsoleted WPS_OOB command (including support for deprecated
 +          UFD config_method)
 +      * added FT support for drivers that implement MLME internally
 +      * added SA Query support for drivers that implement MLME internally
 +      * removed default ACM=1 from AC_VO and AC_VI
 +      * changed VENDOR-TEST EAP method to use proper private enterprise number
 +        (this will not interoperate with older versions)
 +      * added hostapd.conf parameter vendor_elements to allow arbitrary vendor
 +        specific elements to be added to the Beacon and Probe Response frames
 +      * added support for configuring GCMP cipher for IEEE 802.11ad
 +      * added support for 256-bit AES with internal TLS implementation
 +      * changed EAPOL transmission to use AC_VO if WMM is active
 +      * fixed EAP-TLS/PEAP/TTLS/FAST server to validate TLS Message Length
 +        correctly; invalid messages could have caused the hostapd process to
 +        terminate before this fix [CVE-2012-4445]
 +      * limit number of active wildcard PINs for WPS Registrar to one to avoid
 +        confusing behavior with multiple wildcard PINs
 +      * added a workaround for WPS PBC session overlap detection to avoid
 +        interop issues with deployed station implementations that do not
 +        remove active PBC indication from Probe Request frames properly
 +      * added support for using SQLite for the eap_user database
 +      * added Acct-Session-Id attribute into Access-Request messages
 +      * fixed EAPOL frame transmission to non-QoS STAs with nl80211
 +        (do not send QoS frames if the STA did not negotiate use of QoS for
 +        this association)
 +
 +2012-05-10 - v1.0
 +      * Add channel selection support in hostapd. See hostapd.conf.
 +      * Add support for IEEE 802.11v Time Advertisement mechanism with UTC
 +        TSF offset. See hostapd.conf for config info.
 +      * Delay STA entry removal until Deauth/Disassoc TX status in AP mode.
 +        This allows the driver to use PS buffering of Deauthentication and
 +        Disassociation frames when the STA is in power save sleep. Only
 +        available with drivers that provide TX status events for Deauth/
 +        Disassoc frames (nl80211).
 +      * Allow PMKSA caching to be disabled on the Authenticator. See
 +        hostap.conf config parameter disable_pmksa_caching.
 +      * atheros: Add support for IEEE 802.11w configuration.
 +      * bsd: Add support for setting HT values in IFM_MMASK.
 +      * Allow client isolation to be configured with ap_isolate. Client
 +        isolation can be used to prevent low-level bridging of frames
 +        between associated stations in the BSS. By default, this bridging
 +        is allowed.
 +      * Allow coexistance of HT BSSes with WEP/TKIP BSSes.
 +      * Add require_ht config parameter, which can be used to configure
 +        hostapd to reject association with any station that does not support
 +        HT PHY.
 +      * Add support for writing debug log to a file using "-f" option. Also
 +        add relog CLI command to re-open the log file.
 +      * Add bridge handling for WDS STA interfaces. By default they are
 +        added to the configured bridge of the AP interface (if present),
 +        but the user can also specify a separate bridge using cli command
 +        wds_bridge.
 +      * hostapd_cli:
 +        - Add wds_bridge command for specifying bridge for WDS STA
 +          interfaces.
 +        - Add relog command for reopening log file.
 +        - Send AP-STA-DISCONNECTED event when an AP disconnects a station
 +          due to inactivity.
 +        - Add wps_config ctrl_interface command for configuring AP. This
 +          command can be used to configure the AP using the internal WPS
 +          registrar. It works in the same way as new AP settings received
 +          from an ER.
 +        - Many WPS/WPS ER commands - see WPS/WPS ER sections for details.
 +        - Add command get version, that returns hostapd version string.
 +      * WNM: Add BSS Transition Management Request for ESS Disassoc Imminent.
 +        Use hostapd_cli ess_disassoc (STA addr) (URL) to send the
 +        notification to the STA.
 +      * Allow AP mode to disconnect STAs based on low ACK condition (when
 +        the data connection is not working properly, e.g., due to the STA
 +        going outside the range of the AP). Disabled by default, enable by
 +        config option disassoc_low_ack.
 +      * Add WPA_IGNORE_CONFIG_ERRORS build option to continue in case of bad
 +        config file.
 +      * WPS:
 +        - Send AP Settings as a wrapped Credential attribute to ctrl_iface
 +          in WPS-NEW-AP-SETTINGS.
 +        - Dispatch more WPS events through hostapd ctrl_iface.
 +        - Add mechanism for indicating non-standard WPS errors.
 +        - Change concurrent radio AP to use only one WPS UPnP instance.
 +        - Add wps_check_pin command for processing PIN from user input.
 +          UIs can use this command to process a PIN entered by a user and to
 +          validate the checksum digit (if present).
 +        - Add hostap_cli get_config command to display current AP config.
 +        - Add new hostapd_cli command, wps_ap_pin, to manage AP PIN at
 +          runtime and support dynamic AP PIN management.
 +        - Disable AP PIN after 10 consecutive failures. Slow down attacks
 +          on failures up to 10.
 +        - Allow AP to start in Enrollee mode without AP PIN for probing,
 +          to be compatible with Windows 7.
 +        - Add Config Error into WPS-FAIL events to provide more info
 +          to the user on how to resolve the issue.
 +        - When controlling multiple interfaces:
 +           - apply WPS commands to all interfaces configured to use WPS
 +           - apply WPS config changes to all interfaces that use WPS
 +           - when an attack is detected on any interface, disable AP PIN on
 +             all interfaces
 +      * WPS ER:
 +        - Show SetSelectedRegistrar events as ctrl_iface events.
 +        - Add special AP Setup Locked mode to allow read only ER.
 +          ap_setup_locked=2 can now be used to enable a special mode where
 +          WPS ER can learn the current AP settings, but cannot change them.
 +      * WPS 2.0: Add support for WPS 2.0 (CONFIG_WPS2)
 +        - Add build option CONFIG_WPS_EXTENSIBILITY_TESTING to enable tool
 +          for testing protocol extensibility.
 +        - Add build option CONFIG_WPS_STRICT to allow disabling of WPS
 +          workarounds.
 +        - Add support for AuthorizedMACs attribute.
 +      * TDLS:
 +        - Allow TDLS use or TDLS channel switching in the BSS to be
 +          prohibited in the BSS, using config params tdls_prohibit and
 +          tdls_prohibit_chan_switch.
 +      * EAP server: Add support for configuring fragment size (see
 +        fragment_size in hostapd.conf).
 +      * wlantest: Add a tool wlantest for IEEE802.11 protocol testing.
 +        wlantest can be used to capture frames from a monitor interface
 +        for realtime capturing or from pcap files for offline analysis.
 +      * Interworking: Support added for 802.11u. Enable in .config with
 +        CONFIG_INTERWORKING. See hostapd.conf for config parameters for
 +        interworking.
 +      * Android: Add build and runtime support for Android hostapd.
 +      * Add a new debug message level for excessive information. Use
 +        -ddd to enable.
 +      * TLS: Add support for tls_disable_time_checks=1 in client mode.
 +      * Internal TLS:
 +        - Add support for TLS v1.1 (RFC 4346). Enable with build parameter
 +          CONFIG_TLSV11.
 +        - Add domainComponent parser for X.509 names
 +      * Reorder some IEs to get closer to IEEE 802.11 standard. Move
 +        WMM into end of Beacon, Probe Resp and (Re)Assoc Resp frames.
 +        Move HT IEs to be later in (Re)Assoc Resp.
 +      * Many bugfixes.
 +
 +2010-04-18 - v0.7.2
 +      * fix WPS internal Registrar use when an external Registrar is also
 +        active
 +      * bsd: Cleaned up driver wrapper and added various low-level
 +        configuration options
 +      * TNC: fixed issues with fragmentation
 +      * EAP-TNC: add Flags field into fragment acknowledgement (needed to
 +        interoperate with other implementations; may potentially breaks
 +        compatibility with older wpa_supplicant/hostapd versions)
 +      * cleaned up driver wrapper API for multi-BSS operations
 +      * nl80211: fix multi-BSS and VLAN operations
 +      * fix number of issues with IEEE 802.11r/FT; this version is not
 +        backwards compatible with old versions
 +      * add SA Query Request processing in AP mode (IEEE 802.11w)
 +      * fix IGTK PN in group rekeying (IEEE 802.11w)
 +      * fix WPS PBC session overlap detection to use correct attribute
 +      * hostapd_notif_Assoc() can now be called with all IEs to simplify
 +        driver wrappers
 +      * work around interoperability issue with some WPS External Registrar
 +        implementations
 +      * nl80211: fix WPS IE update
 +      * hostapd_cli: add support for action script operations (run a script
 +        on hostapd events)
 +      * fix DH padding with internal crypto code (mainly, for WPS)
 +      * fix WPS association with both WPS IE and WPA/RSN IE present with
 +        driver wrappers that use hostapd MLME (e.g., nl80211)
 +
 +2010-01-16 - v0.7.1
 +      * cleaned up driver wrapper API (struct wpa_driver_ops); the new API
 +        is not fully backwards compatible, so out-of-tree driver wrappers
 +        will need modifications
 +      * cleaned up various module interfaces
 +      * merge hostapd and wpa_supplicant developers' documentation into a
 +        single document
 +      * fixed HT Capabilities IE with nl80211 drivers
 +      * moved generic AP functionality code into src/ap
 +      * WPS: handle Selected Registrar as union of info from all Registrars
 +      * remove obsolte Prism54.org driver wrapper
 +      * added internal debugging mechanism with backtrace support and memory
 +        allocation/freeing validation, etc. tests (CONFIG_WPA_TRACE=y)
 +      * EAP-FAST server: piggyback Phase 2 start with the end of Phase 1
 +      * WPS: add support for dynamically selecting whether to provision the
 +        PSK as an ASCII passphrase or PSK
 +      * added support for WDS (4-address frame) mode with per-station virtual
 +        interfaces (wds_sta=1 in config file; only supported with
 +        driver=nl80211 for now)
 +      * fixed WPS Probe Request processing to handle missing required
 +        attribute
 +      * fixed PKCS#12 use with OpenSSL 1.0.0
 +      * detect bridge interface automatically so that bridge parameter in
 +        hostapd.conf becomes optional (though, it may now be used to
 +        automatically add then WLAN interface into a bridge with
 +        driver=nl80211)
 +
 +2009-11-21 - v0.7.0
 +      * increased hostapd_cli ping interval to 5 seconds and made this
 +        configurable with a new command line options (-G<seconds>)
 +      * driver_nl80211: use Linux socket filter to improve performance
 +      * added support for external Registrars with WPS (UPnP transport)
 +      * 802.11n: scan for overlapping BSSes before starting 20/40 MHz channel
 +      * driver_nl80211: fixed STA accounting data collection (TX/RX bytes
 +        reported correctly; TX/RX packets not yet available from kernel)
 +      * added support for WPS USBA out-of-band mechanism with USB Flash
 +        Drives (UFD) (CONFIG_WPS_UFD=y)
 +      * fixed EAPOL/EAP reauthentication when using an external RADIUS
 +        authentication server
 +      * fixed TNC with EAP-TTLS
 +      * fixed IEEE 802.11r key derivation function to match with the standard
 +        (note: this breaks interoperability with previous version) [Bug 303]
 +      * fixed SHA-256 based key derivation function to match with the
 +        standard when using CCMP (for IEEE 802.11r and IEEE 802.11w)
 +        (note: this breaks interoperability with previous version) [Bug 307]
 +      * added number of code size optimizations to remove unnecessary
 +        functionality from the program binary based on build configuration
 +        (part of this automatic; part configurable with CONFIG_NO_* build
 +        options)
 +      * use shared driver wrapper files with wpa_supplicant
 +      * driver_nl80211: multiple updates to provide support for new Linux
 +        nl80211/mac80211 functionality
 +      * updated management frame protection to use IEEE Std 802.11w-2009
 +      * fixed number of small WPS issues and added workarounds to
 +        interoperate with common deployed broken implementations
 +      * added some IEEE 802.11n co-existence rules to disable 40 MHz channels
 +        or modify primary/secondary channels if needed based on neighboring
 +        networks
 +      * added support for NFC out-of-band mechanism with WPS
 +      * added preliminary support for IEEE 802.11r RIC processing
 +
 +2009-01-06 - v0.6.7
 +      * added support for Wi-Fi Protected Setup (WPS)
 +        (hostapd can now be configured to act as an integrated WPS Registrar
 +        and provision credentials for WPS Enrollees using PIN and PBC
 +        methods; external wireless Registrar can configure the AP, but
 +        external WLAN Manager Registrars are not supported); WPS support can
 +        be enabled by adding CONFIG_WPS=y into .config and setting the
 +        runtime configuration variables in hostapd.conf (see WPS section in
 +        the example configuration file); new hostapd_cli commands wps_pin and
 +        wps_pbc are used to configure WPS negotiation; see README-WPS for
 +        more details
 +      * added IEEE 802.11n HT capability configuration (ht_capab)
 +      * added support for generating Country IE based on nl80211 regulatory
 +        information (added if ieee80211d=1 in configuration)
 +      * fixed WEP authentication (both Open System and Shared Key) with
 +        mac80211
 +      * added support for EAP-AKA' (draft-arkko-eap-aka-kdf)
 +      * added support for using driver_test over UDP socket
 +      * changed EAP-GPSK to use the IANA assigned EAP method type 51
 +      * updated management frame protection to use IEEE 802.11w/D7.0
 +      * fixed retransmission of EAP requests if no response is received
 +
 +2008-11-23 - v0.6.6
 +      * added a new configuration option, wpa_ptk_rekey, that can be used to
 +        enforce frequent PTK rekeying, e.g., to mitigate some attacks against
 +        TKIP deficiencies
 +      * updated OpenSSL code for EAP-FAST to use an updated version of the
 +        session ticket overriding API that was included into the upstream
 +        OpenSSL 0.9.9 tree on 2008-11-15 (no additional OpenSSL patch is
 +        needed with that version anymore)
 +      * changed channel flags configuration to read the information from
 +        the driver (e.g., via driver_nl80211 when using mac80211) instead of
 +        using hostapd as the source of the regulatory information (i.e.,
 +        information from CRDA is now used with mac80211); this allows 5 GHz
 +        channels to be used with hostapd (if allowed in the current
 +        regulatory domain)
 +      * fixed EAP-TLS message processing for the last TLS message if it is
 +        large enough to require fragmentation (e.g., if a large Session
 +        Ticket data is included)
 +      * fixed listen interval configuration for nl80211 drivers
 +
 +2008-11-01 - v0.6.5
 +      * added support for SHA-256 as X.509 certificate digest when using the
 +        internal X.509/TLSv1 implementation
 +      * fixed EAP-FAST PAC-Opaque padding (0.6.4 broke this for some peer
 +        identity lengths)
 +      * fixed internal TLSv1 implementation for abbreviated handshake (used
 +        by EAP-FAST server)
 +      * added support for setting VLAN ID for STAs based on local MAC ACL
 +        (accept_mac_file) as an alternative for RADIUS server-based
 +        configuration
 +      * updated management frame protection to use IEEE 802.11w/D6.0
 +        (adds a new association ping to protect against unauthenticated
 +        authenticate or (re)associate request frames dropping association)
 +      * added support for using SHA256-based stronger key derivation for WPA2
 +        (IEEE 802.11w)
 +      * added new "driver wrapper" for RADIUS-only configuration
 +        (driver=none in hostapd.conf; CONFIG_DRIVER_NONE=y in .config)
 +      * fixed WPA/RSN IE validation to verify that the proto (WPA vs. WPA2)
 +        is enabled in configuration
 +      * changed EAP-FAST configuration to use separate fields for A-ID and
 +        A-ID-Info (eap_fast_a_id_info) to allow A-ID to be set to a fixed
 +        16-octet len binary value for better interoperability with some peer
 +        implementations; eap_fast_a_id is now configured as a hex string
 +      * driver_nl80211: Updated to match the current Linux mac80211 AP mode
 +        configuration (wireless-testing.git and Linux kernel releases
 +        starting from 2.6.29)
 +
 +2008-08-10 - v0.6.4
 +      * added peer identity into EAP-FAST PAC-Opaque and skip Phase 2
 +        Identity Request if identity is already known
 +      * added support for EAP Sequences in EAP-FAST Phase 2
 +      * added support for EAP-TNC (Trusted Network Connect)
 +        (this version implements the EAP-TNC method and EAP-TTLS/EAP-FAST
 +        changes needed to run two methods in sequence (IF-T) and the IF-IMV
 +        and IF-TNCCS interfaces from TNCS)
 +      * added support for optional cryptobinding with PEAPv0
 +      * added fragmentation support for EAP-TNC
 +      * added support for fragmenting EAP-TTLS/PEAP/FAST Phase 2 (tunneled)
 +        data
 +      * added support for opportunistic key caching (OKC)
 +
 +2008-02-22 - v0.6.3
 +      * fixed Reassociation Response callback processing when using internal
 +        MLME (driver_{hostap,nl80211,test}.c)
 +      * updated FT support to use the latest draft, IEEE 802.11r/D9.0
 +      * copy optional Proxy-State attributes into RADIUS response when acting
 +        as a RADIUS authentication server
 +      * fixed EAPOL state machine to handle a case in which no response is
 +        received from the RADIUS authentication server; previous version
 +        could have triggered a crash in some cases after a timeout
 +      * fixed EAP-SIM/AKA realm processing to allow decorated usernames to
 +        be used
 +      * added a workaround for EAP-SIM/AKA peers that include incorrect null
 +        termination in the username
 +      * fixed EAP-SIM/AKA protected result indication to include AT_COUNTER
 +        attribute in notification messages only when using fast
 +        reauthentication
 +      * fixed EAP-SIM Start response processing for fast reauthentication
 +        case
 +      * added support for pending EAP processing in EAP-{PEAP,TTLS,FAST}
 +        phase 2 to allow EAP-SIM and EAP-AKA to be used as the Phase 2 method
 +
 +2008-01-01 - v0.6.2
 +      * fixed EAP-SIM and EAP-AKA message parser to validate attribute
 +        lengths properly to avoid potential crash caused by invalid messages
 +      * added data structure for storing allocated buffers (struct wpabuf);
 +        this does not affect hostapd usage, but many of the APIs changed
 +        and various interfaces (e.g., EAP) is not compatible with old
 +        versions
 +      * added support for protecting EAP-AKA/Identity messages with
 +        AT_CHECKCODE (optional feature in RFC 4187)
 +      * added support for protected result indication with AT_RESULT_IND for
 +        EAP-SIM and EAP-AKA (eap_sim_aka_result_ind=1)
 +      * added support for configuring EAP-TTLS phase 2 non-EAP methods in
 +        EAP server configuration; previously all four were enabled for every
 +        phase 2 user, now all four are disabled by default and need to be
 +        enabled with new method names TTLS-PAP, TTLS-CHAP, TTLS-MSCHAP,
 +        TTLS-MSCHAPV2
 +      * removed old debug printing mechanism and the related 'debug'
 +        parameter in the configuration file; debug verbosity is now set with
 +        -d (or -dd) command line arguments
 +      * added support for EAP-IKEv2 (draft-tschofenig-eap-ikev2-15.txt);
 +        only shared key/password authentication is supported in this version
 +
 +2007-11-24 - v0.6.1
 +      * added experimental, integrated TLSv1 server implementation with the
 +        needed X.509/ASN.1/RSA/bignum processing (this can be enabled by
 +        setting CONFIG_TLS=internal and CONFIG_INTERNAL_LIBTOMMATH=y in
 +        .config); this can be useful, e.g., if the target system does not
 +        have a suitable TLS library and a minimal code size is required
 +      * added support for EAP-FAST server method to the integrated EAP
 +        server
 +      * updated EAP Generalized Pre-Shared Key (EAP-GPSK) to use the latest
 +        draft (draft-ietf-emu-eap-gpsk-07.txt)
 +      * added a new configuration parameter, rsn_pairwise, to allow different
 +        pairwise cipher suites to be enabled for WPA and RSN/WPA2
 +        (note: if wpa_pairwise differs from rsn_pairwise, the driver will
 +        either need to support this or will have to use the WPA/RSN IEs from
 +        hostapd; currently, the included madwifi and bsd driver interfaces do
 +        not have support for this)
 +      * updated FT support to use the latest draft, IEEE 802.11r/D8.0
 +
 +2007-05-28 - v0.6.0
 +      * added experimental IEEE 802.11r/D6.0 support
 +      * updated EAP-SAKE to RFC 4763 and the IANA-allocated EAP type 48
 +      * updated EAP-PSK to use the IANA-allocated EAP type 47
 +      * fixed EAP-PSK bit ordering of the Flags field
 +      * fixed configuration reloading (SIGHUP) to re-initialize WPA PSKs
 +        by reading wpa_psk_file [Bug 181]
 +      * fixed EAP-TTLS AVP parser processing for too short AVP lengths
 +      * fixed IPv6 connection to RADIUS accounting server
 +      * updated EAP Generalized Pre-Shared Key (EAP-GPSK) to use the latest
 +        draft (draft-ietf-emu-eap-gpsk-04.txt)
 +      * hlr_auc_gw: read GSM triplet file into memory and rotate through the
 +        entries instead of only using the same three triplets every time
 +        (this does not work properly with tests using multiple clients, but
 +        provides bit better triplet data for testing a single client; anyway,
 +        if a better quality triplets are needed, GSM-Milenage should be used
 +        instead of hardcoded triplet file)
 +      * fixed EAP-MSCHAPv2 server to use a space between S and M parameters
 +        in Success Request [Bug 203]
 +      * added support for sending EAP-AKA Notifications in error cases
 +      * updated to use IEEE 802.11w/D2.0 for management frame protection
 +        (still experimental)
 +      * RADIUS server: added support for processing duplicate messages
 +        (retransmissions from RADIUS client) by replying with the previous
 +        reply
 +
 +2006-11-24 - v0.5.6
 +      * added support for configuring and controlling multiple BSSes per
 +        radio interface (bss=<ifname> in hostapd.conf); this is only
 +        available with Devicescape and test driver interfaces
 +      * fixed PMKSA cache update in the end of successful RSN
 +        pre-authentication
 +      * added support for dynamic VLAN configuration (i.e., selecting VLAN-ID
 +        for each STA based on RADIUS Access-Accept attributes); this requires
 +        VLAN support from the kernel driver/802.11 stack and this is
 +        currently only available with Devicescape and test driver interfaces
 +      * driver_madwifi: fixed configuration of unencrypted modes (plaintext
 +        and IEEE 802.1X without WEP)
 +      * removed STAKey handshake since PeerKey handshake has replaced it in
 +        IEEE 802.11ma and there are no known deployments of STAKey
 +      * updated EAP Generalized Pre-Shared Key (EAP-GPSK) to use the latest
 +        draft (draft-ietf-emu-eap-gpsk-01.txt)
 +      * added preliminary implementation of IEEE 802.11w/D1.0 (management
 +        frame protection)
 +        (Note: this requires driver support to work properly.)
 +        (Note2: IEEE 802.11w is an unapproved draft and subject to change.)
 +      * hlr_auc_gw: added support for GSM-Milenage (for EAP-SIM)
 +      * hlr_auc_gw: added support for reading per-IMSI Milenage keys and
 +        parameters from a text file to make it possible to implement proper
 +        GSM/UMTS authentication server for multiple SIM/USIM cards using
 +        EAP-SIM/EAP-AKA
 +      * fixed session timeout processing with drivers that do not use
 +        ieee802_11.c (e.g., madwifi)
 +
 +2006-08-27 - v0.5.5
 +      * added 'hostapd_cli new_sta <addr>' command for adding a new STA into
 +        hostapd (e.g., to initialize wired network authentication based on an
 +        external signal)
 +      * fixed hostapd to add PMKID KDE into 4-Way Handshake Message 1 when
 +        using WPA2 even if PMKSA caching is not used
 +      * added -P<pid file> argument for hostapd to write the current process
 +        id into a file
 +      * added support for RADIUS Authentication Server MIB (RFC 2619)
 +
 +2006-06-20 - v0.5.4
 +      * fixed nt_password_hash build [Bug 144]
 +      * added PeerKey handshake implementation for IEEE 802.11e
 +        direct link setup (DLS) to replace STAKey handshake
 +      * added support for EAP Generalized Pre-Shared Key (EAP-GPSK,
 +        draft-clancy-emu-eap-shared-secret-00.txt)
 +      * fixed a segmentation fault when RSN pre-authentication was completed
 +        successfully [Bug 152]
 +
 +2006-04-27 - v0.5.3
 +      * do not build nt_password_hash and hlr_auc_gw by default to avoid
 +        requiring a TLS library for a successful build; these programs can be
 +        build with 'make nt_password_hash' and 'make hlr_auc_gw'
 +      * added a new configuration option, eapol_version, that can be used to
 +        set EAPOL version to 1 (default is 2) to work around broken client
 +        implementations that drop EAPOL frames which use version number 2
 +        [Bug 89]
 +      * added support for EAP-SAKE (no EAP method number allocated yet, so
 +        this is using the same experimental type 255 as EAP-PSK)
 +      * fixed EAP-MSCHAPv2 message length validation
 +
 +2006-03-19 - v0.5.2
 +      * fixed stdarg use in hostapd_logger(): if both stdout and syslog
 +        logging was enabled, hostapd could trigger a segmentation fault in
 +        vsyslog on some CPU -- C library combinations
 +      * moved HLR/AuC gateway implementation for EAP-SIM/AKA into an external
 +        program to make it easier to use for implementing real SS7 gateway;
 +        eap_sim_db is not anymore used as a file name for GSM authentication
 +        triplets; instead, it is path to UNIX domain socket that will be used
 +        to communicate with the external gateway program (e.g., hlr_auc_gw)
 +      * added example HLR/AuC gateway implementation, hlr_auc_gw, that uses
 +        local information (GSM authentication triplets from a text file and
 +        hardcoded AKA authentication data); this can be used to test EAP-SIM
 +        and EAP-AKA
 +      * added Milenage algorithm (example 3GPP AKA algorithm) to hlr_auc_gw
 +        to make it possible to test EAP-AKA with real USIM cards (this is
 +        disabled by default; define AKA_USE_MILENAGE when building hlr_auc_gw
 +        to enable this)
 +      * driver_madwifi: added support for getting station RSN IE from
 +        madwifi-ng svn r1453 and newer; this fixes RSN that was apparently
 +        broken with earlier change (r1357) in the driver
 +      * changed EAP method registration to use a dynamic list of methods
 +        instead of a static list generated at build time
 +      * fixed WPA message 3/4 not to encrypt Key Data field (WPA IE)
 +        [Bug 125]
 +      * added ap_max_inactivity configuration parameter
 +
 +2006-01-29 - v0.5.1
 +      * driver_test: added better support for multiple APs and STAs by using
 +        a directory with sockets that include MAC address for each device in
 +        the name (test_socket=DIR:/tmp/test)
 +      * added support for EAP expanded type (vendor specific EAP methods)
 +
 +2005-12-18 - v0.5.0 (beginning of 0.5.x development releases)
 +      * added experimental STAKey handshake implementation for IEEE 802.11e
 +        direct link setup (DLS); note: this is disabled by default in both
 +        build and runtime configuration (can be enabled with CONFIG_STAKEY=y
 +        and stakey=1)
 +      * added support for EAP methods to use callbacks to external programs
 +        by buffering a pending request and processing it after the EAP method
 +        is ready to continue
 +      * improved EAP-SIM database interface to allow external request to GSM
 +        HLR/AuC without blocking hostapd process
 +      * added support for using EAP-SIM pseudonyms and fast re-authentication
 +      * added support for EAP-AKA in the integrated EAP authenticator
 +      * added support for matching EAP identity prefixes (e.g., "1"*) in EAP
 +        user database to allow EAP-SIM/AKA selection without extra roundtrip
 +        for EAP-Nak negotiation
 +      * added support for storing EAP user password as NtPasswordHash instead
 +        of plaintext password when using MSCHAP or MSCHAPv2 for
 +        authentication (hash:<16-octet hex value>); added nt_password_hash
 +        tool for hashing password to generate NtPasswordHash
 +
 +2005-11-20 - v0.4.7 (beginning of 0.4.x stable releases)
 +      * driver_wired: fixed EAPOL sending to optionally use PAE group address
 +        as the destination instead of supplicant MAC address; this is
 +        disabled by default, but should be enabled with use_pae_group_addr=1
 +        in configuration file if the wired interface is used by only one
 +        device at the time (common switch configuration)
 +      * driver_madwifi: configure driver to use TKIP countermeasures in order
 +        to get correct behavior (IEEE 802.11 association failing; previously,
 +        association succeeded, but hostpad forced disassociation immediately)
 +      * driver_madwifi: added support for madwifi-ng
 +
 +2005-10-27 - v0.4.6
 +      * added support for replacing user identity from EAP with RADIUS
 +        User-Name attribute from Access-Accept message, if that is included,
 +        for the RADIUS accounting messages (e.g., for EAP-PEAP/TTLS to get
 +        tunneled identity into accounting messages when the RADIUS server
 +        does not support better way of doing this with Class attribute)
 +      * driver_madwifi: fixed EAPOL packet receive for configuration where
 +        ath# is part of a bridge interface
 +      * added a configuration file and log analyzer script for logwatch
 +      * fixed EAPOL state machine step function to process all state
 +        transitions before processing new events; this resolves a race
 +        condition in which EAPOL-Start message could trigger hostapd to send
 +        two EAP-Response/Identity frames to the authentication server
 +
 +2005-09-25 - v0.4.5
 +      * added client CA list to the TLS certificate request in order to make
 +        it easier for the client to select which certificate to use
 +      * added experimental support for EAP-PSK
 +      * added support for WE-19 (hostap, madwifi)
 +
 +2005-08-21 - v0.4.4
 +      * fixed build without CONFIG_RSN_PREAUTH
 +      * fixed FreeBSD build
 +
 +2005-06-26 - v0.4.3
 +      * fixed PMKSA caching to copy User-Name and Class attributes so that
 +        RADIUS accounting gets correct information
 +      * start RADIUS accounting only after successful completion of WPA
 +        4-Way Handshake if WPA-PSK is used
 +      * fixed PMKSA caching for the case where STA (re)associates without
 +        first disassociating
 +
 +2005-06-12 - v0.4.2
 +      * EAP-PAX is now registered as EAP type 46
 +      * fixed EAP-PAX MAC calculation
 +      * fixed EAP-PAX CK and ICK key derivation
 +      * renamed eap_authenticator configuration variable to eap_server to
 +        better match with RFC 3748 (EAP) terminology
 +      * driver_test: added support for testing hostapd with wpa_supplicant
 +        by using test driver interface without any kernel drivers or network
 +        cards
 +
 +2005-05-22 - v0.4.1
 +      * fixed RADIUS server initialization when only auth or acct server
 +        is configured and the other one is left empty
 +      * driver_madwifi: added support for RADIUS accounting
 +      * driver_madwifi: added preliminary support for compiling against 'BSD'
 +        branch of madwifi CVS tree
 +      * driver_madwifi: fixed pairwise key removal to allow WPA reauth
 +        without disassociation
 +      * added support for reading additional certificates from PKCS#12 files
 +        and adding them to the certificate chain
 +      * fixed RADIUS Class attribute processing to only use Access-Accept
 +        packets to update Class; previously, other RADIUS authentication
 +        packets could have cleared Class attribute
 +      * added support for more than one Class attribute in RADIUS packets
 +      * added support for verifying certificate revocation list (CRL) when
 +        using integrated EAP authenticator for EAP-TLS; new hostapd.conf
 +        options 'check_crl'; CRL must be included in the ca_cert file for now
 +
 +2005-04-25 - v0.4.0 (beginning of 0.4.x development releases)
 +      * added support for including network information into
 +        EAP-Request/Identity message (ASCII-0 (nul) in eap_message)
 +        (e.g., to implement draft-adrange-eap-network-discovery-07.txt)
 +      * fixed a bug which caused some RSN pre-authentication cases to use
 +        freed memory and potentially crash hostapd
 +      * fixed private key loading for cases where passphrase is not set
 +      * added support for sending TLS alerts and aborting authentication
 +        when receiving a TLS alert
 +      * fixed WPA2 to add PMKSA cache entry when using integrated EAP
 +        authenticator
 +      * fixed PMKSA caching (EAP authentication was not skipped correctly
 +        with the new state machine changes from IEEE 802.1X draft)
 +      * added support for RADIUS over IPv6; own_ip_addr, auth_server_addr,
 +        and acct_server_addr can now be IPv6 addresses (CONFIG_IPV6=y needs
 +        to be added to .config to include IPv6 support); for RADIUS server,
 +        radius_server_ipv6=1 needs to be set in hostapd.conf and addresses
 +        in RADIUS clients file can then use IPv6 format
 +      * added experimental support for EAP-PAX
 +      * replaced hostapd control interface library (hostapd_ctrl.[ch]) with
 +        the same implementation that wpa_supplicant is using (wpa_ctrl.[ch])
 +
 +2005-02-12 - v0.3.7 (beginning of 0.3.x stable releases)
 +
 +2005-01-23 - v0.3.5
 +      * added support for configuring a forced PEAP version based on the
 +        Phase 1 identity
 +      * fixed PEAPv1 to use tunneled EAP-Success/Failure instead of EAP-TLV
 +        to terminate authentication
 +      * fixed EAP identifier duplicate processing with the new IEEE 802.1X
 +        draft
 +      * clear accounting data in the driver when starting a new accounting
 +        session
 +      * driver_madwifi: filter wireless events based on ifindex to allow more
 +        than one network interface to be used
 +      * fixed WPA message 2/4 processing not to cancel timeout for TimeoutEvt
 +        setting if the packet does not pass MIC verification (e.g., due to
 +        incorrect PSK); previously, message 1/4 was not tried again if an
 +        invalid message 2/4 was received
 +      * fixed reconfiguration of RADIUS client retransmission timer when
 +        adding a new message to the pending list; previously, timer was not
 +        updated at this point and if there was a pending message with long
 +        time for the next retry, the new message needed to wait that long for
 +        its first retry, too
 +
 +2005-01-09 - v0.3.4
 +      * added support for configuring multiple allowed EAP types for Phase 2
 +        authentication (EAP-PEAP, EAP-TTLS)
 +      * fixed EAPOL-Start processing to trigger WPA reauthentication
 +        (previously, only EAPOL authentication was done)
 +
 +2005-01-02 - v0.3.3
 +      * added support for EAP-PEAP in the integrated EAP authenticator
 +      * added support for EAP-GTC in the integrated EAP authenticator
 +      * added support for configuring list of EAP methods for Phase 1 so that
 +        the integrated EAP authenticator can, e.g., use the wildcard entry
 +        for EAP-TLS and EAP-PEAP
 +      * added support for EAP-TTLS in the integrated EAP authenticator
 +      * added support for EAP-SIM in the integrated EAP authenticator
 +      * added support for using hostapd as a RADIUS authentication server
 +        with the integrated EAP authenticator taking care of EAP
 +        authentication (new hostapd.conf options: radius_server_clients and
 +        radius_server_auth_port); this is not included in default build; use
 +        CONFIG_RADIUS_SERVER=y in .config to include
 +
 +2004-12-19 - v0.3.2
 +      * removed 'daemonize' configuration file option since it has not really
 +        been used at all for more than year
 +      * driver_madwifi: fixed group key setup and added get_ssid method
 +      * added support for EAP-MSCHAPv2 in the integrated EAP authenticator
 +
 +2004-12-12 - v0.3.1
 +      * added support for integrated EAP-TLS authentication (new hostapd.conf
 +        variables: ca_cert, server_cert, private_key, private_key_passwd);
 +        this enabled dynamic keying (WPA2/WPA/IEEE 802.1X/WEP) without
 +        external RADIUS server
 +      * added support for reading PKCS#12 (PFX) files (as a replacement for
 +        PEM/DER) to get certificate and private key (CONFIG_PKCS12)
 +
 +2004-12-05 - v0.3.0 (beginning of 0.3.x development releases)
 +      * added support for Acct-{Input,Output}-Gigawords
 +      * added support for Event-Timestamp (in RADIUS Accounting-Requests)
 +      * added support for RADIUS Authentication Client MIB (RFC2618)
 +      * added support for RADIUS Accounting Client MIB (RFC2620)
 +      * made EAP re-authentication period configurable (eap_reauth_period)
 +      * fixed EAPOL reauthentication to trigger WPA/WPA2 reauthentication
 +      * fixed EAPOL state machine to stop if STA is removed during
 +        eapol_sm_step(); this fixes at least one segfault triggering bug with
 +        IEEE 802.11i pre-authentication
 +      * added support for multiple WPA pre-shared keys (e.g., one for each
 +        client MAC address or keys shared by a group of clients);
 +        new hostapd.conf field wpa_psk_file for setting path to a text file
 +        containing PSKs, see hostapd.wpa_psk for an example
 +      * added support for multiple driver interfaces to allow hostapd to be
 +        used with other drivers
 +      * added wired authenticator driver interface (driver=wired in
 +        hostapd.conf, see wired.conf for example configuration)
 +      * added madwifi driver interface (driver=madwifi in hostapd.conf, see
 +        madwifi.conf for example configuration; Note: include files from
 +        madwifi project is needed for building and a configuration file,
 +        .config, needs to be created in hostapd directory with
 +        CONFIG_DRIVER_MADWIFI=y to include this driver interface in hostapd
 +        build)
 +      * fixed an alignment issue that could cause SHA-1 to fail on some
 +        platforms (e.g., Intel ixp425 with a compiler that does not 32-bit
 +        align variables)
 +      * fixed RADIUS reconnection after an error in sending interim
 +        accounting packets
 +      * added hostapd control interface for external programs and an example
 +        CLI, hostapd_cli (like wpa_cli for wpa_supplicant)
 +      * started adding dot11, dot1x, radius MIBs ('hostapd_cli mib',
 +        'hostapd_cli sta <addr>')
 +      * finished update from IEEE 802.1X-2001 to IEEE 802.1X-REV (now d11)
 +      * added support for strict GTK rekeying (wpa_strict_rekey in
 +        hostapd.conf)
 +      * updated IAPP to use UDP port 3517 and multicast address 224.0.1.178
 +        (instead of broadcast) for IAPP ADD-notify (moved from draft 3 to
 +        IEEE 802.11F-2003)
 +      * added Prism54 driver interface (driver=prism54 in hostapd.conf;
 +        note: .config needs to be created in hostapd directory with
 +        CONFIG_DRIVER_PRISM54=y to include this driver interface in hostapd
 +        build)
 +      * dual-licensed hostapd (GPLv2 and BSD licenses)
 +      * fixed RADIUS accounting to generate a new session id for cases where
 +        a station reassociates without first being complete deauthenticated
 +      * fixed STA disassociation handler to mark next timeout state to
 +        deauthenticate the station, i.e., skip long wait for inactivity poll
 +        and extra disassociation, if the STA disassociates without
 +        deauthenticating
 +      * added integrated EAP authenticator that can be used instead of
 +        external RADIUS authentication server; currently, only EAP-MD5 is
 +        supported, so this cannot yet be used for key distribution; the EAP
 +        method interface is generic, though, so adding new EAP methods should
 +        be straightforward; new hostapd.conf variables: 'eap_authenticator'
 +        and 'eap_user_file'; this obsoletes "minimal authentication server"
 +        ('minimal_eap' in hostapd.conf) which is now removed
 +      * added support for FreeBSD and driver interface for the BSD net80211
 +        layer (driver=bsd in hostapd.conf and CONFIG_DRIVER_BSD=y in
 +        .config); please note that some of the required kernel mods have not
 +        yet been committed
 +
 +2004-07-17 - v0.2.4 (beginning of 0.2.x stable releases)
 +      * fixed some accounting cases where Accounting-Start was sent when
 +        IEEE 802.1X port was being deauthorized
 +
 +2004-06-20 - v0.2.3
 +      * modified RADIUS client to re-connect the socket in case of certain
 +        error codes that are generated when a network interface state is
 +        changes (e.g., when IP address changes or the interface is set UP)
 +      * fixed couple of cases where EAPOL state for a station was freed
 +        twice causing a segfault for hostapd
 +      * fixed couple of bugs in processing WPA deauthentication (freed data
 +        was used)
 +
 +2004-05-31 - v0.2.2
 +      * fixed WPA/WPA2 group rekeying to use key index correctly (GN/GM)
 +      * fixed group rekeying to send zero TSC in EAPOL-Key messages to fix
 +        cases where STAs dropped multicast frames as replay attacks
 +      * added support for copying RADIUS Attribute 'Class' from
 +        authentication messages into accounting messages
 +      * send canned EAP failure if RADIUS server sends Access-Reject without
 +        EAP message (previously, Supplicant was not notified in this case)
 +      * fixed mixed WPA-PSK and WPA-EAP mode to work with WPA-PSK (i.e., do
 +        not start EAPOL state machines if the STA selected to use WPA-PSK)
 +
 +2004-05-06 - v0.2.1
 +      * added WPA and IEEE 802.11i/RSN (WPA2) Authenticator functionality
 +        - based on IEEE 802.11i/D10.0 but modified to interoperate with WPA
 +          (i.e., IEEE 802.11i/D3.0)
 +        - supports WPA-only, RSN-only, and mixed WPA/RSN mode
 +        - both WPA-PSK and WPA-RADIUS/EAP are supported
 +        - PMKSA caching and pre-authentication
 +        - new hostapd.conf variables: wpa, wpa_psk, wpa_passphrase,
 +          wpa_key_mgmt, wpa_pairwise, wpa_group_rekey, wpa_gmk_rekey,
 +          rsn_preauth, rsn_preauth_interfaces
 +      * fixed interim accounting to remove any pending accounting messages
 +        to the STA before sending a new one
 +
 +2004-02-15 - v0.2.0
 +      * added support for Acct-Interim-Interval:
 +        - draft-ietf-radius-acct-interim-01.txt
 +        - use Acct-Interim-Interval attribute from Access-Accept if local
 +          'radius_acct_interim_interval' is not set
 +        - allow different update intervals for each STA
 +      * fixed event loop to call signal handlers only after returning from
 +        the real signal handler
 +      * reset sta->timeout_next after successful association to make sure
 +        that the previously registered inactivity timer will not remove the
 +        STA immediately (e.g., if STA deauthenticates and re-associates
 +        before the timer is triggered).
 +      * added new hostapd.conf variable, nas_identifier, that can be used to
 +        add an optional RADIUS Attribute, NAS-Identifier, into authentication
 +        and accounting messages
 +      * added support for Accounting-On and Accounting-Off messages
 +      * fixed accounting session handling to send Accounting-Start only once
 +        per session and not to send Accounting-Stop if the session was not
 +        initialized properly
 +      * fixed Accounting-Stop statistics in cases where the message was
 +        previously sent after the kernel entry for the STA (and/or IEEE
 +        802.1X data) was removed
 +
 +
 +Note:
 +
 +Older changes up to and including v0.1.0 are included in the ChangeLog
 +of the Host AP driver.
index 53143f76cfe8d76902df46dd4fec2b1f061eec94,0000000000000000000000000000000000000000..82ac61d7729a89190f56b1b2a0d68ddb7ace6a74
mode 100644,000000..100644
--- /dev/null
@@@ -1,3402 -1,0 +1,3536 @@@
-               cw == 63 || cw == 127 || cw == 255 || cw == 511 || cw == 1023);
 +/*
 + * hostapd / Configuration file parser
 + * 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 "utils/includes.h"
 +#ifndef CONFIG_NATIVE_WINDOWS
 +#include <grp.h>
 +#endif /* CONFIG_NATIVE_WINDOWS */
 +
 +#include "utils/common.h"
 +#include "utils/uuid.h"
 +#include "common/ieee802_11_defs.h"
 +#include "drivers/driver.h"
 +#include "eap_server/eap.h"
 +#include "radius/radius_client.h"
 +#include "ap/wpa_auth.h"
 +#include "ap/ap_config.h"
 +#include "config_file.h"
 +
 +
 +#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
 +static int hostapd_config_read_vlan_file(struct hostapd_bss_config *bss,
 +                                       const char *fname)
 +{
 +      FILE *f;
 +      char buf[128], *pos, *pos2;
 +      int line = 0, vlan_id;
 +      struct hostapd_vlan *vlan;
 +
 +      f = fopen(fname, "r");
 +      if (!f) {
 +              wpa_printf(MSG_ERROR, "VLAN file '%s' not readable.", fname);
 +              return -1;
 +      }
 +
 +      while (fgets(buf, sizeof(buf), f)) {
 +              line++;
 +
 +              if (buf[0] == '#')
 +                      continue;
 +              pos = buf;
 +              while (*pos != '\0') {
 +                      if (*pos == '\n') {
 +                              *pos = '\0';
 +                              break;
 +                      }
 +                      pos++;
 +              }
 +              if (buf[0] == '\0')
 +                      continue;
 +
 +              if (buf[0] == '*') {
 +                      vlan_id = VLAN_ID_WILDCARD;
 +                      pos = buf + 1;
 +              } else {
 +                      vlan_id = strtol(buf, &pos, 10);
 +                      if (buf == pos || vlan_id < 1 ||
 +                          vlan_id > MAX_VLAN_ID) {
 +                              wpa_printf(MSG_ERROR, "Invalid VLAN ID at "
 +                                         "line %d in '%s'", line, fname);
 +                              fclose(f);
 +                              return -1;
 +                      }
 +              }
 +
 +              while (*pos == ' ' || *pos == '\t')
 +                      pos++;
 +              pos2 = pos;
 +              while (*pos2 != ' ' && *pos2 != '\t' && *pos2 != '\0')
 +                      pos2++;
 +              *pos2 = '\0';
 +              if (*pos == '\0' || os_strlen(pos) > IFNAMSIZ) {
 +                      wpa_printf(MSG_ERROR, "Invalid VLAN ifname at line %d "
 +                                 "in '%s'", line, fname);
 +                      fclose(f);
 +                      return -1;
 +              }
 +
 +              vlan = os_zalloc(sizeof(*vlan));
 +              if (vlan == NULL) {
 +                      wpa_printf(MSG_ERROR, "Out of memory while reading "
 +                                 "VLAN interfaces from '%s'", fname);
 +                      fclose(f);
 +                      return -1;
 +              }
 +
 +              vlan->vlan_id = vlan_id;
 +              os_strlcpy(vlan->ifname, pos, sizeof(vlan->ifname));
 +              vlan->next = bss->vlan;
 +              bss->vlan = vlan;
 +      }
 +
 +      fclose(f);
 +
 +      return 0;
 +}
 +#endif /* CONFIG_NO_VLAN */
 +
 +
 +static int hostapd_acl_comp(const void *a, const void *b)
 +{
 +      const struct mac_acl_entry *aa = a;
 +      const struct mac_acl_entry *bb = b;
 +      return os_memcmp(aa->addr, bb->addr, sizeof(macaddr));
 +}
 +
 +
 +static int hostapd_config_read_maclist(const char *fname,
 +                                     struct mac_acl_entry **acl, int *num)
 +{
 +      FILE *f;
 +      char buf[128], *pos;
 +      int line = 0;
 +      u8 addr[ETH_ALEN];
 +      struct mac_acl_entry *newacl;
 +      int vlan_id;
 +
 +      if (!fname)
 +              return 0;
 +
 +      f = fopen(fname, "r");
 +      if (!f) {
 +              wpa_printf(MSG_ERROR, "MAC list file '%s' not found.", fname);
 +              return -1;
 +      }
 +
 +      while (fgets(buf, sizeof(buf), f)) {
 +              int i, rem = 0;
 +
 +              line++;
 +
 +              if (buf[0] == '#')
 +                      continue;
 +              pos = buf;
 +              while (*pos != '\0') {
 +                      if (*pos == '\n') {
 +                              *pos = '\0';
 +                              break;
 +                      }
 +                      pos++;
 +              }
 +              if (buf[0] == '\0')
 +                      continue;
 +              pos = buf;
 +              if (buf[0] == '-') {
 +                      rem = 1;
 +                      pos++;
 +              }
 +
 +              if (hwaddr_aton(pos, addr)) {
 +                      wpa_printf(MSG_ERROR, "Invalid MAC address '%s' at "
 +                                 "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')
 +                      pos++;
 +              while (*pos == ' ' || *pos == '\t')
 +                      pos++;
 +              if (*pos != '\0')
 +                      vlan_id = atoi(pos);
 +
 +              newacl = os_realloc_array(*acl, *num + 1, sizeof(**acl));
 +              if (newacl == NULL) {
 +                      wpa_printf(MSG_ERROR, "MAC list reallocation failed");
 +                      fclose(f);
 +                      return -1;
 +              }
 +
 +              *acl = newacl;
 +              os_memcpy((*acl)[*num].addr, addr, ETH_ALEN);
 +              (*acl)[*num].vlan_id = vlan_id;
 +              (*num)++;
 +      }
 +
 +      fclose(f);
 +
 +      qsort(*acl, *num, sizeof(**acl), hostapd_acl_comp);
 +
 +      return 0;
 +}
 +
 +
 +#ifdef EAP_SERVER
 +static int hostapd_config_read_eap_user(const char *fname,
 +                                      struct hostapd_bss_config *conf)
 +{
 +      FILE *f;
 +      char buf[512], *pos, *start, *pos2;
 +      int line = 0, ret = 0, num_methods;
 +      struct hostapd_eap_user *user = NULL, *tail = NULL, *new_user = NULL;
 +
 +      if (!fname)
 +              return 0;
 +
 +      if (os_strncmp(fname, "sqlite:", 7) == 0) {
++#ifdef CONFIG_SQLITE
 +              os_free(conf->eap_user_sqlite);
 +              conf->eap_user_sqlite = os_strdup(fname + 7);
 +              return 0;
++#else /* CONFIG_SQLITE */
++              wpa_printf(MSG_ERROR,
++                         "EAP user file in SQLite DB, but CONFIG_SQLITE was not enabled in the build.");
++              return -1;
++#endif /* CONFIG_SQLITE */
 +      }
 +
 +      f = fopen(fname, "r");
 +      if (!f) {
 +              wpa_printf(MSG_ERROR, "EAP user file '%s' not found.", fname);
 +              return -1;
 +      }
 +
 +      /* Lines: "user" METHOD,METHOD2 "password" (password optional) */
 +      while (fgets(buf, sizeof(buf), f)) {
 +              line++;
 +
 +              if (buf[0] == '#')
 +                      continue;
 +              pos = buf;
 +              while (*pos != '\0') {
 +                      if (*pos == '\n') {
 +                              *pos = '\0';
 +                              break;
 +                      }
 +                      pos++;
 +              }
 +              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] != '*') {
 +                      wpa_printf(MSG_ERROR, "Invalid EAP identity (no \" in "
 +                                 "start) on line %d in '%s'", line, fname);
 +                      goto failed;
 +              }
 +
 +              user = os_zalloc(sizeof(*user));
 +              if (user == NULL) {
 +                      wpa_printf(MSG_ERROR, "EAP user allocation failed");
 +                      goto failed;
 +              }
 +              user->force_version = -1;
 +
 +              if (buf[0] == '*') {
 +                      pos = buf;
 +              } else {
 +                      pos = buf + 1;
 +                      start = pos;
 +                      while (*pos != '"' && *pos != '\0')
 +                              pos++;
 +                      if (*pos == '\0') {
 +                              wpa_printf(MSG_ERROR, "Invalid EAP identity "
 +                                         "(no \" in end) on line %d in '%s'",
 +                                         line, fname);
 +                              goto failed;
 +                      }
 +
 +                      user->identity = os_malloc(pos - start);
 +                      if (user->identity == NULL) {
 +                              wpa_printf(MSG_ERROR, "Failed to allocate "
 +                                         "memory for EAP identity");
 +                              goto failed;
 +                      }
 +                      os_memcpy(user->identity, start, pos - start);
 +                      user->identity_len = pos - start;
 +
 +                      if (pos[0] == '"' && pos[1] == '*') {
 +                              user->wildcard_prefix = 1;
 +                              pos++;
 +                      }
 +              }
 +              pos++;
 +              while (*pos == ' ' || *pos == '\t')
 +                      pos++;
 +
 +              if (*pos == '\0') {
 +                      wpa_printf(MSG_ERROR, "No EAP method on line %d in "
 +                                 "'%s'", line, fname);
 +                      goto failed;
 +              }
 +
 +              start = pos;
 +              while (*pos != ' ' && *pos != '\t' && *pos != '\0')
 +                      pos++;
 +              if (*pos == '\0') {
 +                      pos = NULL;
 +              } else {
 +                      *pos = '\0';
 +                      pos++;
 +              }
 +              num_methods = 0;
 +              while (*start) {
 +                      char *pos3 = os_strchr(start, ',');
 +                      if (pos3) {
 +                              *pos3++ = '\0';
 +                      }
 +                      user->methods[num_methods].method =
 +                              eap_server_get_type(
 +                                      start,
 +                                      &user->methods[num_methods].vendor);
 +                      if (user->methods[num_methods].vendor ==
 +                          EAP_VENDOR_IETF &&
 +                          user->methods[num_methods].method == EAP_TYPE_NONE)
 +                      {
 +                              if (os_strcmp(start, "TTLS-PAP") == 0) {
 +                                      user->ttls_auth |= EAP_TTLS_AUTH_PAP;
 +                                      goto skip_eap;
 +                              }
 +                              if (os_strcmp(start, "TTLS-CHAP") == 0) {
 +                                      user->ttls_auth |= EAP_TTLS_AUTH_CHAP;
 +                                      goto skip_eap;
 +                              }
 +                              if (os_strcmp(start, "TTLS-MSCHAP") == 0) {
 +                                      user->ttls_auth |=
 +                                              EAP_TTLS_AUTH_MSCHAP;
 +                                      goto skip_eap;
 +                              }
 +                              if (os_strcmp(start, "TTLS-MSCHAPV2") == 0) {
 +                                      user->ttls_auth |=
 +                                              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);
 +                              goto failed;
 +                      }
 +
 +                      num_methods++;
 +                      if (num_methods >= EAP_MAX_METHODS)
 +                              break;
 +              skip_eap:
 +                      if (pos3 == NULL)
 +                              break;
 +                      start = pos3;
 +              }
 +              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;
 +              }
 +
 +              if (pos == NULL)
 +                      goto done;
 +
 +              while (*pos == ' ' || *pos == '\t')
 +                      pos++;
 +              if (*pos == '\0')
 +                      goto done;
 +
 +              if (os_strncmp(pos, "[ver=0]", 7) == 0) {
 +                      user->force_version = 0;
 +                      goto done;
 +              }
 +
 +              if (os_strncmp(pos, "[ver=1]", 7) == 0) {
 +                      user->force_version = 1;
 +                      goto done;
 +              }
 +
 +              if (os_strncmp(pos, "[2]", 3) == 0) {
 +                      user->phase2 = 1;
 +                      goto done;
 +              }
 +
 +              if (*pos == '"') {
 +                      pos++;
 +                      start = pos;
 +                      while (*pos != '"' && *pos != '\0')
 +                              pos++;
 +                      if (*pos == '\0') {
 +                              wpa_printf(MSG_ERROR, "Invalid EAP password "
 +                                         "(no \" in end) on line %d in '%s'",
 +                                         line, fname);
 +                              goto failed;
 +                      }
 +
 +                      user->password = os_malloc(pos - start);
 +                      if (user->password == NULL) {
 +                              wpa_printf(MSG_ERROR, "Failed to allocate "
 +                                         "memory for EAP password");
 +                              goto failed;
 +                      }
 +                      os_memcpy(user->password, start, pos - start);
 +                      user->password_len = pos - start;
 +
 +                      pos++;
 +              } else if (os_strncmp(pos, "hash:", 5) == 0) {
 +                      pos += 5;
 +                      pos2 = pos;
 +                      while (*pos2 != '\0' && *pos2 != ' ' &&
 +                             *pos2 != '\t' && *pos2 != '#')
 +                              pos2++;
 +                      if (pos2 - pos != 32) {
 +                              wpa_printf(MSG_ERROR, "Invalid password hash "
 +                                         "on line %d in '%s'", line, fname);
 +                              goto failed;
 +                      }
 +                      user->password = os_malloc(16);
 +                      if (user->password == NULL) {
 +                              wpa_printf(MSG_ERROR, "Failed to allocate "
 +                                         "memory for EAP password hash");
 +                              goto failed;
 +                      }
 +                      if (hexstr2bin(pos, user->password, 16) < 0) {
 +                              wpa_printf(MSG_ERROR, "Invalid hash password "
 +                                         "on line %d in '%s'", line, fname);
 +                              goto failed;
 +                      }
 +                      user->password_len = 16;
 +                      user->password_hash = 1;
 +                      pos = pos2;
 +              } else {
 +                      pos2 = pos;
 +                      while (*pos2 != '\0' && *pos2 != ' ' &&
 +                             *pos2 != '\t' && *pos2 != '#')
 +                              pos2++;
 +                      if ((pos2 - pos) & 1) {
 +                              wpa_printf(MSG_ERROR, "Invalid hex password "
 +                                         "on line %d in '%s'", line, fname);
 +                              goto failed;
 +                      }
 +                      user->password = os_malloc((pos2 - pos) / 2);
 +                      if (user->password == NULL) {
 +                              wpa_printf(MSG_ERROR, "Failed to allocate "
 +                                         "memory for EAP password");
 +                              goto failed;
 +                      }
 +                      if (hexstr2bin(pos, user->password,
 +                                     (pos2 - pos) / 2) < 0) {
 +                              wpa_printf(MSG_ERROR, "Invalid hex password "
 +                                         "on line %d in '%s'", line, fname);
 +                              goto failed;
 +                      }
 +                      user->password_len = (pos2 - pos) / 2;
 +                      pos = pos2;
 +              }
 +
 +              while (*pos == ' ' || *pos == '\t')
 +                      pos++;
 +              if (os_strncmp(pos, "[2]", 3) == 0) {
 +                      user->phase2 = 1;
 +              }
 +
 +      done:
 +              if (tail == NULL) {
 +                      tail = new_user = user;
 +              } else {
 +                      tail->next = user;
 +                      tail = user;
 +              }
 +              continue;
 +
 +      failed:
 +              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 */
 +
 +
 +#ifndef CONFIG_NO_RADIUS
 +static int
 +hostapd_config_read_radius_addr(struct hostapd_radius_server **server,
 +                              int *num_server, const char *val, int def_port,
 +                              struct hostapd_radius_server **curr_serv)
 +{
 +      struct hostapd_radius_server *nserv;
 +      int ret;
 +      static int server_index = 1;
 +
 +      nserv = os_realloc_array(*server, *num_server + 1, sizeof(*nserv));
 +      if (nserv == NULL)
 +              return -1;
 +
 +      *server = nserv;
 +      nserv = &nserv[*num_server];
 +      (*num_server)++;
 +      (*curr_serv) = nserv;
 +
 +      os_memset(nserv, 0, sizeof(*nserv));
 +      nserv->port = def_port;
 +      ret = hostapd_parse_ip_addr(val, &nserv->addr);
 +      nserv->index = server_index++;
 +
 +      return ret;
 +}
 +
 +
 +static struct hostapd_radius_attr *
 +hostapd_parse_radius_attr(const char *value)
 +{
 +      const char *pos;
 +      char syntax;
 +      struct hostapd_radius_attr *attr;
 +      size_t len;
 +
 +      attr = os_zalloc(sizeof(*attr));
 +      if (attr == NULL)
 +              return NULL;
 +
 +      attr->type = atoi(value);
 +
 +      pos = os_strchr(value, ':');
 +      if (pos == NULL) {
 +              attr->val = wpabuf_alloc(1);
 +              if (attr->val == NULL) {
 +                      os_free(attr);
 +                      return NULL;
 +              }
 +              wpabuf_put_u8(attr->val, 0);
 +              return attr;
 +      }
 +
 +      pos++;
 +      if (pos[0] == '\0' || pos[1] != ':') {
 +              os_free(attr);
 +              return NULL;
 +      }
 +      syntax = *pos++;
 +      pos++;
 +
 +      switch (syntax) {
 +      case 's':
 +              attr->val = wpabuf_alloc_copy(pos, os_strlen(pos));
 +              break;
 +      case 'x':
 +              len = os_strlen(pos);
 +              if (len & 1)
 +                      break;
 +              len /= 2;
 +              attr->val = wpabuf_alloc(len);
 +              if (attr->val == NULL)
 +                      break;
 +              if (hexstr2bin(pos, wpabuf_put(attr->val, len), len) < 0) {
 +                      wpabuf_free(attr->val);
 +                      os_free(attr);
 +                      return NULL;
 +              }
 +              break;
 +      case 'd':
 +              attr->val = wpabuf_alloc(4);
 +              if (attr->val)
 +                      wpabuf_put_be32(attr->val, atoi(pos));
 +              break;
 +      default:
 +              os_free(attr);
 +              return NULL;
 +      }
 +
 +      if (attr->val == NULL) {
 +              os_free(attr);
 +              return NULL;
 +      }
 +
 +      return attr;
 +}
 +
 +
 +static int hostapd_parse_das_client(struct hostapd_bss_config *bss,
 +                                  const char *val)
 +{
 +      char *secret;
 +
 +      secret = os_strchr(val, ' ');
 +      if (secret == NULL)
 +              return -1;
 +
 +      secret++;
 +
 +      if (hostapd_parse_ip_addr(val, &bss->radius_das_client_addr))
 +              return -1;
 +
 +      os_free(bss->radius_das_shared_secret);
 +      bss->radius_das_shared_secret = (u8 *) os_strdup(secret);
 +      if (bss->radius_das_shared_secret == NULL)
 +              return -1;
 +      bss->radius_das_shared_secret_len = os_strlen(secret);
 +
 +      return 0;
 +}
 +#endif /* CONFIG_NO_RADIUS */
 +
 +
 +static int hostapd_config_parse_key_mgmt(int line, const char *value)
 +{
 +      int val = 0, last;
 +      char *start, *end, *buf;
 +
 +      buf = os_strdup(value);
 +      if (buf == NULL)
 +              return -1;
 +      start = buf;
 +
 +      while (*start != '\0') {
 +              while (*start == ' ' || *start == '\t')
 +                      start++;
 +              if (*start == '\0')
 +                      break;
 +              end = start;
 +              while (*end != ' ' && *end != '\t' && *end != '\0')
 +                      end++;
 +              last = *end == '\0';
 +              *end = '\0';
 +              if (os_strcmp(start, "WPA-PSK") == 0)
 +                      val |= WPA_KEY_MGMT_PSK;
 +              else if (os_strcmp(start, "WPA-EAP") == 0)
 +                      val |= WPA_KEY_MGMT_IEEE8021X;
 +#ifdef CONFIG_IEEE80211R
 +              else if (os_strcmp(start, "FT-PSK") == 0)
 +                      val |= WPA_KEY_MGMT_FT_PSK;
 +              else if (os_strcmp(start, "FT-EAP") == 0)
 +                      val |= WPA_KEY_MGMT_FT_IEEE8021X;
 +#endif /* CONFIG_IEEE80211R */
 +#ifdef CONFIG_IEEE80211W
 +              else if (os_strcmp(start, "WPA-PSK-SHA256") == 0)
 +                      val |= WPA_KEY_MGMT_PSK_SHA256;
 +              else if (os_strcmp(start, "WPA-EAP-SHA256") == 0)
 +                      val |= WPA_KEY_MGMT_IEEE8021X_SHA256;
 +#endif /* CONFIG_IEEE80211W */
 +#ifdef CONFIG_SAE
 +              else if (os_strcmp(start, "SAE") == 0)
 +                      val |= WPA_KEY_MGMT_SAE;
 +              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);
 +                      os_free(buf);
 +                      return -1;
 +              }
 +
 +              if (last)
 +                      break;
 +              start = end + 1;
 +      }
 +
 +      os_free(buf);
 +      if (val == 0) {
 +              wpa_printf(MSG_ERROR, "Line %d: no key_mgmt values "
 +                         "configured.", line);
 +              return -1;
 +      }
 +
 +      return val;
 +}
 +
 +
 +static int hostapd_config_parse_cipher(int line, const char *value)
 +{
 +      int val = wpa_parse_cipher(value);
 +      if (val < 0) {
 +              wpa_printf(MSG_ERROR, "Line %d: invalid cipher '%s'.",
 +                         line, value);
 +              return -1;
 +      }
 +      if (val == 0) {
 +              wpa_printf(MSG_ERROR, "Line %d: no cipher values configured.",
 +                         line);
 +              return -1;
 +      }
 +      return val;
 +}
 +
 +
 +static int hostapd_config_read_wep(struct hostapd_wep_keys *wep, int keyidx,
 +                                 char *val)
 +{
 +      size_t len = os_strlen(val);
 +
 +      if (keyidx < 0 || keyidx > 3 || wep->key[keyidx] != NULL)
 +              return -1;
 +
 +      if (val[0] == '"') {
 +              if (len < 2 || val[len - 1] != '"')
 +                      return -1;
 +              len -= 2;
 +              wep->key[keyidx] = os_malloc(len);
 +              if (wep->key[keyidx] == NULL)
 +                      return -1;
 +              os_memcpy(wep->key[keyidx], val + 1, len);
 +              wep->len[keyidx] = len;
 +      } else {
 +              if (len & 1)
 +                      return -1;
 +              len /= 2;
 +              wep->key[keyidx] = os_malloc(len);
 +              if (wep->key[keyidx] == NULL)
 +                      return -1;
 +              wep->len[keyidx] = len;
 +              if (hexstr2bin(val, wep->key[keyidx], len) < 0)
 +                      return -1;
 +      }
 +
 +      wep->keys_set++;
 +
 +      return 0;
 +}
 +
 +
++static int hostapd_parse_chanlist(struct hostapd_config *conf, char *val)
++{
++      char *pos;
++
++      /* for backwards compatibility, translate ' ' in conf str to ',' */
++      pos = val;
++      while (pos) {
++              pos = os_strchr(pos, ' ');
++              if (pos)
++                      *pos++ = ',';
++      }
++      if (freq_range_list_parse(&conf->acs_ch_list, val))
++              return -1;
++
++      return 0;
++}
++
++
 +static int hostapd_parse_intlist(int **int_list, char *val)
 +{
 +      int *list;
 +      int count;
 +      char *pos, *end;
 +
 +      os_free(*int_list);
 +      *int_list = NULL;
 +
 +      pos = val;
 +      count = 0;
 +      while (*pos != '\0') {
 +              if (*pos == ' ')
 +                      count++;
 +              pos++;
 +      }
 +
 +      list = os_malloc(sizeof(int) * (count + 2));
 +      if (list == NULL)
 +              return -1;
 +      pos = val;
 +      count = 0;
 +      while (*pos != '\0') {
 +              end = os_strchr(pos, ' ');
 +              if (end)
 +                      *end = '\0';
 +
 +              list[count++] = atoi(pos);
 +              if (!end)
 +                      break;
 +              pos = end + 1;
 +      }
 +      list[count] = -1;
 +
 +      *int_list = list;
 +      return 0;
 +}
 +
 +
 +static int hostapd_config_bss(struct hostapd_config *conf, const char *ifname)
 +{
 +      struct hostapd_bss_config **all, *bss;
 +
 +      if (*ifname == '\0')
 +              return -1;
 +
 +      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 = all;
 +
 +      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->bss[conf->num_bss++] = bss;
 +      conf->last_bss = bss;
 +
 +      hostapd_config_defaults_bss(bss);
 +      os_strlcpy(bss->iface, ifname, sizeof(bss->iface));
 +      os_memcpy(bss->ssid.vlan, bss->iface, IFNAMSIZ + 1);
 +
 +      return 0;
 +}
 +
 +
 +/* convert floats with one decimal place to value*10 int, i.e.,
 + * "1.5" will return 15 */
 +static int hostapd_config_read_int10(const char *value)
 +{
 +      int i, d;
 +      char *pos;
 +
 +      i = atoi(value);
 +      pos = os_strchr(value, '.');
 +      d = 0;
 +      if (pos) {
 +              pos++;
 +              if (*pos >= '0' && *pos <= '9')
 +                      d = *pos - '0';
 +      }
 +
 +      return i * 10 + d;
 +}
 +
 +
 +static int valid_cw(int cw)
 +{
 +      return (cw == 1 || cw == 3 || cw == 7 || cw == 15 || cw == 31 ||
- static int hostapd_config_tx_queue(struct hostapd_config *conf, char *name,
-                                  char *val)
++              cw == 63 || cw == 127 || cw == 255 || cw == 511 || cw == 1023 ||
++              cw == 2047 || cw == 4095 || cw == 8191 || cw == 16383 ||
++              cw == 32767);
 +}
 +
 +
 +enum {
 +      IEEE80211_TX_QUEUE_DATA0 = 0, /* used for EDCA AC_VO data */
 +      IEEE80211_TX_QUEUE_DATA1 = 1, /* used for EDCA AC_VI data */
 +      IEEE80211_TX_QUEUE_DATA2 = 2, /* used for EDCA AC_BE data */
 +      IEEE80211_TX_QUEUE_DATA3 = 3 /* used for EDCA AC_BK data */
 +};
 +
-       char *pos;
++static int hostapd_config_tx_queue(struct hostapd_config *conf,
++                                 const char *name, const char *val)
 +{
 +      int num;
-       if (os_strstr(capab, "[MU-BEAMFORMEE]"))
-               conf->vht_capab |= VHT_CAP_MU_BEAMFORMEE_CAPABLE;
++      const char *pos;
 +      struct hostapd_tx_queue_params *queue;
 +
 +      /* skip 'tx_queue_' prefix */
 +      pos = name + 9;
 +      if (os_strncmp(pos, "data", 4) == 0 &&
 +          pos[4] >= '0' && pos[4] <= '9' && pos[5] == '_') {
 +              num = pos[4] - '0';
 +              pos += 6;
 +      } else if (os_strncmp(pos, "after_beacon_", 13) == 0 ||
 +                 os_strncmp(pos, "beacon_", 7) == 0) {
 +              wpa_printf(MSG_INFO, "DEPRECATED: '%s' not used", name);
 +              return 0;
 +      } else {
 +              wpa_printf(MSG_ERROR, "Unknown tx_queue name '%s'", pos);
 +              return -1;
 +      }
 +
 +      if (num >= NUM_TX_QUEUES) {
 +              /* for backwards compatibility, do not trigger failure */
 +              wpa_printf(MSG_INFO, "DEPRECATED: '%s' not used", name);
 +              return 0;
 +      }
 +
 +      queue = &conf->tx_queue[num];
 +
 +      if (os_strcmp(pos, "aifs") == 0) {
 +              queue->aifs = atoi(val);
 +              if (queue->aifs < 0 || queue->aifs > 255) {
 +                      wpa_printf(MSG_ERROR, "Invalid AIFS value %d",
 +                                 queue->aifs);
 +                      return -1;
 +              }
 +      } else if (os_strcmp(pos, "cwmin") == 0) {
 +              queue->cwmin = atoi(val);
 +              if (!valid_cw(queue->cwmin)) {
 +                      wpa_printf(MSG_ERROR, "Invalid cwMin value %d",
 +                                 queue->cwmin);
 +                      return -1;
 +              }
 +      } else if (os_strcmp(pos, "cwmax") == 0) {
 +              queue->cwmax = atoi(val);
 +              if (!valid_cw(queue->cwmax)) {
 +                      wpa_printf(MSG_ERROR, "Invalid cwMax value %d",
 +                                 queue->cwmax);
 +                      return -1;
 +              }
 +      } else if (os_strcmp(pos, "burst") == 0) {
 +              queue->burst = hostapd_config_read_int10(val);
 +      } else {
 +              wpa_printf(MSG_ERROR, "Unknown tx_queue field '%s'", pos);
 +              return -1;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +#ifdef CONFIG_IEEE80211R
 +static int add_r0kh(struct hostapd_bss_config *bss, char *value)
 +{
 +      struct ft_remote_r0kh *r0kh;
 +      char *pos, *next;
 +
 +      r0kh = os_zalloc(sizeof(*r0kh));
 +      if (r0kh == NULL)
 +              return -1;
 +
 +      /* 02:01:02:03:04:05 a.example.com 000102030405060708090a0b0c0d0e0f */
 +      pos = value;
 +      next = os_strchr(pos, ' ');
 +      if (next)
 +              *next++ = '\0';
 +      if (next == NULL || hwaddr_aton(pos, r0kh->addr)) {
 +              wpa_printf(MSG_ERROR, "Invalid R0KH MAC address: '%s'", pos);
 +              os_free(r0kh);
 +              return -1;
 +      }
 +
 +      pos = next;
 +      next = os_strchr(pos, ' ');
 +      if (next)
 +              *next++ = '\0';
 +      if (next == NULL || next - pos > FT_R0KH_ID_MAX_LEN) {
 +              wpa_printf(MSG_ERROR, "Invalid R0KH-ID: '%s'", pos);
 +              os_free(r0kh);
 +              return -1;
 +      }
 +      r0kh->id_len = next - pos - 1;
 +      os_memcpy(r0kh->id, pos, r0kh->id_len);
 +
 +      pos = next;
 +      if (hexstr2bin(pos, r0kh->key, sizeof(r0kh->key))) {
 +              wpa_printf(MSG_ERROR, "Invalid R0KH key: '%s'", pos);
 +              os_free(r0kh);
 +              return -1;
 +      }
 +
 +      r0kh->next = bss->r0kh_list;
 +      bss->r0kh_list = r0kh;
 +
 +      return 0;
 +}
 +
 +
 +static int add_r1kh(struct hostapd_bss_config *bss, char *value)
 +{
 +      struct ft_remote_r1kh *r1kh;
 +      char *pos, *next;
 +
 +      r1kh = os_zalloc(sizeof(*r1kh));
 +      if (r1kh == NULL)
 +              return -1;
 +
 +      /* 02:01:02:03:04:05 02:01:02:03:04:05
 +       * 000102030405060708090a0b0c0d0e0f */
 +      pos = value;
 +      next = os_strchr(pos, ' ');
 +      if (next)
 +              *next++ = '\0';
 +      if (next == NULL || hwaddr_aton(pos, r1kh->addr)) {
 +              wpa_printf(MSG_ERROR, "Invalid R1KH MAC address: '%s'", pos);
 +              os_free(r1kh);
 +              return -1;
 +      }
 +
 +      pos = next;
 +      next = os_strchr(pos, ' ');
 +      if (next)
 +              *next++ = '\0';
 +      if (next == NULL || hwaddr_aton(pos, r1kh->id)) {
 +              wpa_printf(MSG_ERROR, "Invalid R1KH-ID: '%s'", pos);
 +              os_free(r1kh);
 +              return -1;
 +      }
 +
 +      pos = next;
 +      if (hexstr2bin(pos, r1kh->key, sizeof(r1kh->key))) {
 +              wpa_printf(MSG_ERROR, "Invalid R1KH key: '%s'", pos);
 +              os_free(r1kh);
 +              return -1;
 +      }
 +
 +      r1kh->next = bss->r1kh_list;
 +      bss->r1kh_list = r1kh;
 +
 +      return 0;
 +}
 +#endif /* CONFIG_IEEE80211R */
 +
 +
 +#ifdef CONFIG_IEEE80211N
 +static int hostapd_config_ht_capab(struct hostapd_config *conf,
 +                                 const char *capab)
 +{
 +      if (os_strstr(capab, "[LDPC]"))
 +              conf->ht_capab |= HT_CAP_INFO_LDPC_CODING_CAP;
 +      if (os_strstr(capab, "[HT40-]")) {
 +              conf->ht_capab |= HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET;
 +              conf->secondary_channel = -1;
 +      }
 +      if (os_strstr(capab, "[HT40+]")) {
 +              conf->ht_capab |= HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET;
 +              conf->secondary_channel = 1;
 +      }
 +      if (os_strstr(capab, "[SMPS-STATIC]")) {
 +              conf->ht_capab &= ~HT_CAP_INFO_SMPS_MASK;
 +              conf->ht_capab |= HT_CAP_INFO_SMPS_STATIC;
 +      }
 +      if (os_strstr(capab, "[SMPS-DYNAMIC]")) {
 +              conf->ht_capab &= ~HT_CAP_INFO_SMPS_MASK;
 +              conf->ht_capab |= HT_CAP_INFO_SMPS_DYNAMIC;
 +      }
 +      if (os_strstr(capab, "[GF]"))
 +              conf->ht_capab |= HT_CAP_INFO_GREEN_FIELD;
 +      if (os_strstr(capab, "[SHORT-GI-20]"))
 +              conf->ht_capab |= HT_CAP_INFO_SHORT_GI20MHZ;
 +      if (os_strstr(capab, "[SHORT-GI-40]"))
 +              conf->ht_capab |= HT_CAP_INFO_SHORT_GI40MHZ;
 +      if (os_strstr(capab, "[TX-STBC]"))
 +              conf->ht_capab |= HT_CAP_INFO_TX_STBC;
 +      if (os_strstr(capab, "[RX-STBC1]")) {
 +              conf->ht_capab &= ~HT_CAP_INFO_RX_STBC_MASK;
 +              conf->ht_capab |= HT_CAP_INFO_RX_STBC_1;
 +      }
 +      if (os_strstr(capab, "[RX-STBC12]")) {
 +              conf->ht_capab &= ~HT_CAP_INFO_RX_STBC_MASK;
 +              conf->ht_capab |= HT_CAP_INFO_RX_STBC_12;
 +      }
 +      if (os_strstr(capab, "[RX-STBC123]")) {
 +              conf->ht_capab &= ~HT_CAP_INFO_RX_STBC_MASK;
 +              conf->ht_capab |= HT_CAP_INFO_RX_STBC_123;
 +      }
 +      if (os_strstr(capab, "[DELAYED-BA]"))
 +              conf->ht_capab |= HT_CAP_INFO_DELAYED_BA;
 +      if (os_strstr(capab, "[MAX-AMSDU-7935]"))
 +              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, "[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;
 +
 +      return 0;
 +}
 +#endif /* CONFIG_IEEE80211N */
 +
 +
 +#ifdef CONFIG_IEEE80211AC
 +static int hostapd_config_vht_capab(struct hostapd_config *conf,
 +                                  const char *capab)
 +{
 +      if (os_strstr(capab, "[MAX-MPDU-7991]"))
 +              conf->vht_capab |= VHT_CAP_MAX_MPDU_LENGTH_7991;
 +      if (os_strstr(capab, "[MAX-MPDU-11454]"))
 +              conf->vht_capab |= VHT_CAP_MAX_MPDU_LENGTH_11454;
 +      if (os_strstr(capab, "[VHT160]"))
 +              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, "[RXLDPC]"))
 +              conf->vht_capab |= VHT_CAP_RXLDPC;
 +      if (os_strstr(capab, "[SHORT-GI-80]"))
 +              conf->vht_capab |= VHT_CAP_SHORT_GI_80;
 +      if (os_strstr(capab, "[SHORT-GI-160]"))
 +              conf->vht_capab |= VHT_CAP_SHORT_GI_160;
 +      if (os_strstr(capab, "[TX-STBC-2BY1]"))
 +              conf->vht_capab |= VHT_CAP_TXSTBC;
 +      if (os_strstr(capab, "[RX-STBC-1]"))
 +              conf->vht_capab |= VHT_CAP_RXSTBC_1;
 +      if (os_strstr(capab, "[RX-STBC-12]"))
 +              conf->vht_capab |= VHT_CAP_RXSTBC_2;
 +      if (os_strstr(capab, "[RX-STBC-123]"))
 +              conf->vht_capab |= VHT_CAP_RXSTBC_3;
 +      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_SU_BEAMFORMER_CAPABLE;
 +      if (os_strstr(capab, "[SU-BEAMFORMEE]"))
 +              conf->vht_capab |= VHT_CAP_SU_BEAMFORMEE_CAPABLE;
 +      if (os_strstr(capab, "[BF-ANTENNA-2]") &&
 +          (conf->vht_capab & VHT_CAP_SU_BEAMFORMEE_CAPABLE))
 +              conf->vht_capab |= (1 << VHT_CAP_BEAMFORMEE_STS_OFFSET);
++      if (os_strstr(capab, "[BF-ANTENNA-3]") &&
++          (conf->vht_capab & VHT_CAP_SU_BEAMFORMEE_CAPABLE))
++              conf->vht_capab |= (2 << VHT_CAP_BEAMFORMEE_STS_OFFSET);
++      if (os_strstr(capab, "[BF-ANTENNA-4]") &&
++          (conf->vht_capab & VHT_CAP_SU_BEAMFORMEE_CAPABLE))
++              conf->vht_capab |= (3 << VHT_CAP_BEAMFORMEE_STS_OFFSET);
 +      if (os_strstr(capab, "[SOUNDING-DIMENSION-2]") &&
 +          (conf->vht_capab & VHT_CAP_SU_BEAMFORMER_CAPABLE))
 +              conf->vht_capab |= (1 << VHT_CAP_SOUNDING_DIMENSION_OFFSET);
++      if (os_strstr(capab, "[SOUNDING-DIMENSION-3]") &&
++          (conf->vht_capab & VHT_CAP_SU_BEAMFORMER_CAPABLE))
++              conf->vht_capab |= (2 << VHT_CAP_SOUNDING_DIMENSION_OFFSET);
++      if (os_strstr(capab, "[SOUNDING-DIMENSION-4]") &&
++          (conf->vht_capab & VHT_CAP_SU_BEAMFORMER_CAPABLE))
++              conf->vht_capab |= (3 << VHT_CAP_SOUNDING_DIMENSION_OFFSET);
 +      if (os_strstr(capab, "[MU-BEAMFORMER]"))
 +              conf->vht_capab |= VHT_CAP_MU_BEAMFORMER_CAPABLE;
-       if (str == NULL || slen < 1 || slen > HOSTAPD_MAX_SSID_LEN) {
 +      if (os_strstr(capab, "[VHT-TXOP-PS]"))
 +              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-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;
 +      if (os_strstr(capab, "[VHT-LINK-ADAPT3]") &&
 +          (conf->vht_capab & VHT_CAP_HTC_VHT))
 +              conf->vht_capab |= VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB;
 +      if (os_strstr(capab, "[RX-ANTENNA-PATTERN]"))
 +              conf->vht_capab |= VHT_CAP_RX_ANTENNA_PATTERN;
 +      if (os_strstr(capab, "[TX-ANTENNA-PATTERN]"))
 +              conf->vht_capab |= VHT_CAP_TX_ANTENNA_PATTERN;
 +      return 0;
 +}
 +#endif /* CONFIG_IEEE80211AC */
 +
 +
 +#ifdef CONFIG_INTERWORKING
 +static int parse_roaming_consortium(struct hostapd_bss_config *bss, char *pos,
 +                                  int line)
 +{
 +      size_t len = os_strlen(pos);
 +      u8 oi[MAX_ROAMING_CONSORTIUM_LEN];
 +
 +      struct hostapd_roaming_consortium *rc;
 +
 +      if ((len & 1) || len < 2 * 3 || len / 2 > MAX_ROAMING_CONSORTIUM_LEN ||
 +          hexstr2bin(pos, oi, len / 2)) {
 +              wpa_printf(MSG_ERROR, "Line %d: invalid roaming_consortium "
 +                         "'%s'", line, pos);
 +              return -1;
 +      }
 +      len /= 2;
 +
 +      rc = os_realloc_array(bss->roaming_consortium,
 +                            bss->roaming_consortium_count + 1,
 +                            sizeof(struct hostapd_roaming_consortium));
 +      if (rc == NULL)
 +              return -1;
 +
 +      os_memcpy(rc[bss->roaming_consortium_count].oi, oi, len);
 +      rc[bss->roaming_consortium_count].len = len;
 +
 +      bss->roaming_consortium = rc;
 +      bss->roaming_consortium_count++;
 +
 +      return 0;
 +}
 +
 +
 +static int parse_lang_string(struct hostapd_lang_string **array,
 +                           unsigned int *count, char *pos)
 +{
 +      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)
 +              goto fail;
 +      *sep++ = '\0';
 +
 +      clen = os_strlen(pos);
 +      if (clen < 2 || clen > sizeof(ls->lang))
 +              goto fail;
 +      nlen = os_strlen(sep);
 +      if (nlen > 252)
 +              goto fail;
 +
 +      ls = os_realloc_array(*array, *count + 1,
 +                            sizeof(struct hostapd_lang_string));
 +      if (ls == NULL)
 +              goto fail;
 +
 +      *array = ls;
 +      ls = &(*array)[*count];
 +      (*count)++;
 +
 +      os_memset(ls->lang, 0, sizeof(ls->lang));
 +      os_memcpy(ls->lang, pos, clen);
 +      ls->name_len = nlen;
 +      os_memcpy(ls->name, sep, nlen);
 +
 +      ret = 0;
 +fail:
 +      os_free(str);
 +      return ret;
 +}
 +
 +
 +static int parse_venue_name(struct hostapd_bss_config *bss, char *pos,
 +                          int line)
 +{
 +      if (parse_lang_string(&bss->venue_name, &bss->venue_name_count, pos)) {
 +              wpa_printf(MSG_ERROR, "Line %d: Invalid venue_name '%s'",
 +                         line, pos);
 +              return -1;
 +      }
 +      return 0;
 +}
 +
 +
 +static int parse_3gpp_cell_net(struct hostapd_bss_config *bss, char *buf,
 +                             int line)
 +{
 +      size_t count;
 +      char *pos;
 +      u8 *info = NULL, *ipos;
 +
 +      /* format: <MCC1,MNC1>[;<MCC2,MNC2>][;...] */
 +
 +      count = 1;
 +      for (pos = buf; *pos; pos++) {
 +              if ((*pos < '0' || *pos > '9') && *pos != ';' && *pos != ',')
 +                      goto fail;
 +              if (*pos == ';')
 +                      count++;
 +      }
 +      if (1 + count * 3 > 0x7f)
 +              goto fail;
 +
 +      info = os_zalloc(2 + 3 + count * 3);
 +      if (info == NULL)
 +              return -1;
 +
 +      ipos = info;
 +      *ipos++ = 0; /* GUD - Version 1 */
 +      *ipos++ = 3 + count * 3; /* User Data Header Length (UDHL) */
 +      *ipos++ = 0; /* PLMN List IEI */
 +      /* ext(b8) | Length of PLMN List value contents(b7..1) */
 +      *ipos++ = 1 + count * 3;
 +      *ipos++ = count; /* Number of PLMNs */
 +
 +      pos = buf;
 +      while (pos && *pos) {
 +              char *mcc, *mnc;
 +              size_t mnc_len;
 +
 +              mcc = pos;
 +              mnc = os_strchr(pos, ',');
 +              if (mnc == NULL)
 +                      goto fail;
 +              *mnc++ = '\0';
 +              pos = os_strchr(mnc, ';');
 +              if (pos)
 +                      *pos++ = '\0';
 +
 +              mnc_len = os_strlen(mnc);
 +              if (os_strlen(mcc) != 3 || (mnc_len != 2 && mnc_len != 3))
 +                      goto fail;
 +
 +              /* BC coded MCC,MNC */
 +              /* MCC digit 2 | MCC digit 1 */
 +              *ipos++ = ((mcc[1] - '0') << 4) | (mcc[0] - '0');
 +              /* MNC digit 3 | MCC digit 3 */
 +              *ipos++ = (((mnc_len == 2) ? 0xf0 : ((mnc[2] - '0') << 4))) |
 +                      (mcc[2] - '0');
 +              /* MNC digit 2 | MNC digit 1 */
 +              *ipos++ = ((mnc[1] - '0') << 4) | (mnc[0] - '0');
 +      }
 +
 +      os_free(bss->anqp_3gpp_cell_net);
 +      bss->anqp_3gpp_cell_net = info;
 +      bss->anqp_3gpp_cell_net_len = 2 + 3 + 3 * count;
 +      wpa_hexdump(MSG_MSGDUMP, "3GPP Cellular Network information",
 +                  bss->anqp_3gpp_cell_net, bss->anqp_3gpp_cell_net_len);
 +
 +      return 0;
 +
 +fail:
 +      wpa_printf(MSG_ERROR, "Line %d: Invalid anqp_3gpp_cell_net: %s",
 +                 line, buf);
 +      os_free(info);
 +      return -1;
 +}
 +
 +
 +static int parse_nai_realm(struct hostapd_bss_config *bss, char *buf, int line)
 +{
 +      struct hostapd_nai_realm_data *realm;
 +      size_t i, j, len;
 +      int *offsets;
 +      char *pos, *end, *rpos;
 +
 +      offsets = os_calloc(bss->nai_realm_count * MAX_NAI_REALMS,
 +                          sizeof(int));
 +      if (offsets == NULL)
 +              return -1;
 +
 +      for (i = 0; i < bss->nai_realm_count; i++) {
 +              realm = &bss->nai_realm_data[i];
 +              for (j = 0; j < MAX_NAI_REALMS; j++) {
 +                      offsets[i * MAX_NAI_REALMS + j] =
 +                              realm->realm[j] ?
 +                              realm->realm[j] - realm->realm_buf : -1;
 +              }
 +      }
 +
 +      realm = os_realloc_array(bss->nai_realm_data, bss->nai_realm_count + 1,
 +                               sizeof(struct hostapd_nai_realm_data));
 +      if (realm == NULL) {
 +              os_free(offsets);
 +              return -1;
 +      }
 +      bss->nai_realm_data = realm;
 +
 +      /* patch the pointers after realloc */
 +      for (i = 0; i < bss->nai_realm_count; i++) {
 +              realm = &bss->nai_realm_data[i];
 +              for (j = 0; j < MAX_NAI_REALMS; j++) {
 +                      int offs = offsets[i * MAX_NAI_REALMS + j];
 +                      if (offs >= 0)
 +                              realm->realm[j] = realm->realm_buf + offs;
 +                      else
 +                              realm->realm[j] = NULL;
 +              }
 +      }
 +      os_free(offsets);
 +
 +      realm = &bss->nai_realm_data[bss->nai_realm_count];
 +      os_memset(realm, 0, sizeof(*realm));
 +
 +      pos = buf;
 +      realm->encoding = atoi(pos);
 +      pos = os_strchr(pos, ',');
 +      if (pos == NULL)
 +              goto fail;
 +      pos++;
 +
 +      end = os_strchr(pos, ',');
 +      if (end) {
 +              len = end - pos;
 +              *end = '\0';
 +      } else {
 +              len = os_strlen(pos);
 +      }
 +
 +      if (len > MAX_NAI_REALMLEN) {
 +              wpa_printf(MSG_ERROR, "Too long a realm string (%d > max %d "
 +                         "characters)", (int) len, MAX_NAI_REALMLEN);
 +              goto fail;
 +      }
 +      os_memcpy(realm->realm_buf, pos, len);
 +
 +      if (end)
 +              pos = end + 1;
 +      else
 +              pos = NULL;
 +
 +      while (pos && *pos) {
 +              struct hostapd_nai_realm_eap *eap;
 +
 +              if (realm->eap_method_count >= MAX_NAI_EAP_METHODS) {
 +                      wpa_printf(MSG_ERROR, "Too many EAP methods");
 +                      goto fail;
 +              }
 +
 +              eap = &realm->eap_method[realm->eap_method_count];
 +              realm->eap_method_count++;
 +
 +              end = os_strchr(pos, ',');
 +              if (end == NULL)
 +                      end = pos + os_strlen(pos);
 +
 +              eap->eap_method = atoi(pos);
 +              for (;;) {
 +                      pos = os_strchr(pos, '[');
 +                      if (pos == NULL || pos > end)
 +                              break;
 +                      pos++;
 +                      if (eap->num_auths >= MAX_NAI_AUTH_TYPES) {
 +                              wpa_printf(MSG_ERROR, "Too many auth params");
 +                              goto fail;
 +                      }
 +                      eap->auth_id[eap->num_auths] = atoi(pos);
 +                      pos = os_strchr(pos, ':');
 +                      if (pos == NULL || pos > end)
 +                              goto fail;
 +                      pos++;
 +                      eap->auth_val[eap->num_auths] = atoi(pos);
 +                      pos = os_strchr(pos, ']');
 +                      if (pos == NULL || pos > end)
 +                              goto fail;
 +                      pos++;
 +                      eap->num_auths++;
 +              }
 +
 +              if (*end != ',')
 +                      break;
 +
 +              pos = end + 1;
 +      }
 +
 +      /* Split realm list into null terminated realms */
 +      rpos = realm->realm_buf;
 +      i = 0;
 +      while (*rpos) {
 +              if (i >= MAX_NAI_REALMS) {
 +                      wpa_printf(MSG_ERROR, "Too many realms");
 +                      goto fail;
 +              }
 +              realm->realm[i++] = rpos;
 +              rpos = os_strchr(rpos, ';');
 +              if (rpos == NULL)
 +                      break;
 +              *rpos++ = '\0';
 +      }
 +
 +      bss->nai_realm_count++;
 +
 +      return 0;
 +
 +fail:
 +      wpa_printf(MSG_ERROR, "Line %d: invalid nai_realm '%s'", line, buf);
 +      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 */
 +
 +
 +#ifdef CONFIG_HS20
 +static int hs20_parse_conn_capab(struct hostapd_bss_config *bss, char *buf,
 +                               int line)
 +{
 +      u8 *conn_cap;
 +      char *pos;
 +
 +      if (bss->hs20_connection_capability_len >= 0xfff0)
 +              return -1;
 +
 +      conn_cap = os_realloc(bss->hs20_connection_capability,
 +                            bss->hs20_connection_capability_len + 4);
 +      if (conn_cap == NULL)
 +              return -1;
 +
 +      bss->hs20_connection_capability = conn_cap;
 +      conn_cap += bss->hs20_connection_capability_len;
 +      pos = buf;
 +      conn_cap[0] = atoi(pos);
 +      pos = os_strchr(pos, ':');
 +      if (pos == NULL)
 +              return -1;
 +      pos++;
 +      WPA_PUT_LE16(conn_cap + 1, atoi(pos));
 +      pos = os_strchr(pos, ':');
 +      if (pos == NULL)
 +              return -1;
 +      pos++;
 +      conn_cap[3] = atoi(pos);
 +      bss->hs20_connection_capability_len += 4;
 +
 +      return 0;
 +}
 +
 +
 +static int hs20_parse_wan_metrics(struct hostapd_bss_config *bss, char *buf,
 +                                int line)
 +{
 +      u8 *wan_metrics;
 +      char *pos;
 +
 +      /* <WAN Info>:<DL Speed>:<UL Speed>:<DL Load>:<UL Load>:<LMD> */
 +
 +      wan_metrics = os_zalloc(13);
 +      if (wan_metrics == NULL)
 +              return -1;
 +
 +      pos = buf;
 +      /* WAN Info */
 +      if (hexstr2bin(pos, wan_metrics, 1) < 0)
 +              goto fail;
 +      pos += 2;
 +      if (*pos != ':')
 +              goto fail;
 +      pos++;
 +
 +      /* Downlink Speed */
 +      WPA_PUT_LE32(wan_metrics + 1, atoi(pos));
 +      pos = os_strchr(pos, ':');
 +      if (pos == NULL)
 +              goto fail;
 +      pos++;
 +
 +      /* Uplink Speed */
 +      WPA_PUT_LE32(wan_metrics + 5, atoi(pos));
 +      pos = os_strchr(pos, ':');
 +      if (pos == NULL)
 +              goto fail;
 +      pos++;
 +
 +      /* Downlink Load */
 +      wan_metrics[9] = atoi(pos);
 +      pos = os_strchr(pos, ':');
 +      if (pos == NULL)
 +              goto fail;
 +      pos++;
 +
 +      /* Uplink Load */
 +      wan_metrics[10] = atoi(pos);
 +      pos = os_strchr(pos, ':');
 +      if (pos == NULL)
 +              goto fail;
 +      pos++;
 +
 +      /* LMD */
 +      WPA_PUT_LE16(wan_metrics + 11, atoi(pos));
 +
 +      os_free(bss->hs20_wan_metrics);
 +      bss->hs20_wan_metrics = wan_metrics;
 +
 +      return 0;
 +
 +fail:
 +      wpa_printf(MSG_ERROR, "Line %d: Invalid hs20_wan_metrics '%s'",
 +                 line, buf);
 +      os_free(wan_metrics);
 +      return -1;
 +}
 +
 +
 +static int hs20_parse_oper_friendly_name(struct hostapd_bss_config *bss,
 +                                       char *pos, int line)
 +{
 +      if (parse_lang_string(&bss->hs20_oper_friendly_name,
 +                            &bss->hs20_oper_friendly_name_count, pos)) {
 +              wpa_printf(MSG_ERROR, "Line %d: Invalid "
 +                         "hs20_oper_friendly_name '%s'", line, pos);
 +              return -1;
 +      }
 +      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);
-                              char *buf, char *pos, int line)
++      if (str == NULL || slen < 1 || slen > SSID_MAX_LEN) {
 +              wpa_printf(MSG_ERROR, "Line %d: Invalid SSID '%s'", line, pos);
 +              os_free(str);
 +              return -1;
 +      }
 +
 +      os_memcpy(bss->osu_ssid, str, slen);
 +      bss->osu_ssid_len = slen;
 +      os_free(str);
 +
 +      return 0;
 +}
 +
 +
 +static int hs20_parse_osu_server_uri(struct hostapd_bss_config *bss,
 +                                   char *pos, int line)
 +{
 +      struct hs20_osu_provider *p;
 +
 +      p = os_realloc_array(bss->hs20_osu_providers,
 +                           bss->hs20_osu_providers_count + 1, sizeof(*p));
 +      if (p == NULL)
 +              return -1;
 +
 +      bss->hs20_osu_providers = p;
 +      bss->last_osu = &bss->hs20_osu_providers[bss->hs20_osu_providers_count];
 +      bss->hs20_osu_providers_count++;
 +      os_memset(bss->last_osu, 0, sizeof(*p));
 +      bss->last_osu->server_uri = os_strdup(pos);
 +
 +      return 0;
 +}
 +
 +
 +static int hs20_parse_osu_friendly_name(struct hostapd_bss_config *bss,
 +                                      char *pos, int line)
 +{
 +      if (bss->last_osu == NULL) {
 +              wpa_printf(MSG_ERROR, "Line %d: Unexpected OSU field", line);
 +              return -1;
 +      }
 +
 +      if (parse_lang_string(&bss->last_osu->friendly_name,
 +                            &bss->last_osu->friendly_name_count, pos)) {
 +              wpa_printf(MSG_ERROR, "Line %d: Invalid osu_friendly_name '%s'",
 +                         line, pos);
 +              return -1;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +static int hs20_parse_osu_nai(struct hostapd_bss_config *bss,
 +                            char *pos, int line)
 +{
 +      if (bss->last_osu == NULL) {
 +              wpa_printf(MSG_ERROR, "Line %d: Unexpected OSU field", line);
 +              return -1;
 +      }
 +
 +      os_free(bss->last_osu->osu_nai);
 +      bss->last_osu->osu_nai = os_strdup(pos);
 +      if (bss->last_osu->osu_nai == NULL)
 +              return -1;
 +
 +      return 0;
 +}
 +
 +
 +static int hs20_parse_osu_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 */
 +
 +
 +#ifdef CONFIG_WPS_NFC
 +static struct wpabuf * hostapd_parse_bin(const char *buf)
 +{
 +      size_t len;
 +      struct wpabuf *ret;
 +
 +      len = os_strlen(buf);
 +      if (len & 0x01)
 +              return NULL;
 +      len /= 2;
 +
 +      ret = wpabuf_alloc(len);
 +      if (ret == NULL)
 +              return NULL;
 +
 +      if (hexstr2bin(buf, wpabuf_put(ret, len), len)) {
 +              wpabuf_free(ret);
 +              return NULL;
 +      }
 +
 +      return ret;
 +}
 +#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,
-               if (bss->ssid.ssid_len > HOSTAPD_MAX_SSID_LEN ||
++                             const char *buf, char *pos, int line)
 +{
 +      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 (str == NULL || slen < 1 || slen > HOSTAPD_MAX_SSID_LEN) {
++              if (bss->ssid.ssid_len > SSID_MAX_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 (os_strcmp(pos, "a") == 0)
++              if (str == NULL || slen < 1 || slen > SSID_MAX_LEN) {
 +                      wpa_printf(MSG_ERROR, "Line %d: invalid SSID '%s'",
 +                                 line, pos);
 +                      os_free(str);
 +                      return 1;
 +              }
 +              os_memcpy(bss->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))
 +                      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, "tls_session_lifetime") == 0) {
++              bss->tls_session_lifetime = 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);
 +                      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);
 +#endif /* EAP_SERVER_SIM */
 +#ifdef EAP_SERVER_TNC
 +      } 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);
 +#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;
 +              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));
 +#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);
 +                      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, "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);
 +                      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) {
 +              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);
 +#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_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) {
 +#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 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_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, "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 if (os_strcmp(pos, "any") == 0)
++                      conf->hw_mode = HOSTAPD_MODE_IEEE80211ANY;
 +              else {
 +                      wpa_printf(MSG_ERROR, "Line %d: unknown hw_mode '%s'",
 +                                 line, pos);
 +                      return 1;
 +              }
 +      } else if (os_strcmp(buf, "wps_rf_bands") == 0) {
-               } else
++              if (os_strcmp(pos, "ad") == 0)
++                      bss->wps_rf_bands = WPS_RF_60GHZ;
++              else 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->acs = 1;
 +                      conf->channel = 0;
 +#endif /* CONFIG_ACS */
-               if (hostapd_parse_intlist(&conf->chanlist, pos)) {
++              } else {
 +                      conf->channel = atoi(pos);
++                      conf->acs = conf->channel == 0;
++              }
 +      } else if (os_strcmp(buf, "chanlist") == 0) {
-               if (os_strlen(pos) > 32) {
++              if (hostapd_parse_chanlist(conf, 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);
 +                      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) {
 +              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);
 +                      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, "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);
 +                      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);
 +                      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);
 +#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);
 +                      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) {
-                     struct hostapd_bss_config *bss, char *field, char *value)
++              if (os_strlen(pos) > WPS_DEV_NAME_MAX_LEN) {
 +                      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);
 +                      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) {
 +              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) {
 +              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);
 +#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);
 +                      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);
 +#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);
 +                      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)
 +                      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);
 +#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, "proxy_arp") == 0) {
 +              bss->proxy_arp = atoi(pos);
++      } else if (os_strcmp(buf, "na_mcast_to_ucast") == 0) {
++              bss->na_mcast_to_ucast = atoi(pos);
 +      } else if (os_strcmp(buf, "osen") == 0) {
 +              bss->osen = atoi(pos);
 +      } else if (os_strcmp(buf, "anqp_domain_id") == 0) {
 +              bss->anqp_domain_id = atoi(pos);
 +      } else if (os_strcmp(buf, "hs20_deauth_req_timeout") == 0) {
 +              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.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);
++      } else if (os_strcmp(buf, "own_ie_override") == 0) {
++              struct wpabuf *tmp;
++              size_t len = os_strlen(pos) / 2;
++
++              tmp = wpabuf_alloc(len);
++              if (!tmp)
++                      return 1;
++
++              if (hexstr2bin(pos, wpabuf_put(tmp, len), len)) {
++                      wpabuf_free(tmp);
++                      wpa_printf(MSG_ERROR,
++                                 "Line %d: Invalid own_ie_override '%s'",
++                                 line, pos);
++                      return 1;
++              }
++
++              wpabuf_free(bss->own_ie_override);
++              bss->own_ie_override = tmp;
 +#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;
 +              }
 +
 +              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);
++#ifdef CONFIG_FST
++      } else if (os_strcmp(buf, "fst_group_id") == 0) {
++              size_t len = os_strlen(pos);
++
++              if (!len || len >= sizeof(conf->fst_cfg.group_id)) {
++                      wpa_printf(MSG_ERROR,
++                                 "Line %d: Invalid fst_group_id value '%s'",
++                                 line, pos);
++                      return 1;
++              }
++
++              if (conf->fst_cfg.group_id[0]) {
++                      wpa_printf(MSG_ERROR,
++                                 "Line %d: Duplicate fst_group value '%s'",
++                                 line, pos);
++                      return 1;
++              }
++
++              os_strlcpy(conf->fst_cfg.group_id, pos,
++                         sizeof(conf->fst_cfg.group_id));
++      } else if (os_strcmp(buf, "fst_priority") == 0) {
++              char *endp;
++              long int val;
++
++              if (!*pos) {
++                      wpa_printf(MSG_ERROR,
++                                 "Line %d: fst_priority value not supplied (expected 1..%u)",
++                                 line, FST_MAX_PRIO_VALUE);
++                      return -1;
++              }
++
++              val = strtol(pos, &endp, 0);
++              if (*endp || val < 1 || val > FST_MAX_PRIO_VALUE) {
++                      wpa_printf(MSG_ERROR,
++                                 "Line %d: Invalid fst_priority %ld (%s) (expected 1..%u)",
++                                 line, val, pos, FST_MAX_PRIO_VALUE);
++                      return 1;
++              }
++              conf->fst_cfg.priority = (u8) val;
++      } else if (os_strcmp(buf, "fst_llt") == 0) {
++              char *endp;
++              long int val;
++
++              if (!*pos) {
++                      wpa_printf(MSG_ERROR,
++                                 "Line %d: fst_llt value not supplied (expected 1..%u)",
++                                 line, FST_MAX_LLT_MS);
++                      return -1;
++              }
++              val = strtol(pos, &endp, 0);
++              if (*endp || val < 1 || val > FST_MAX_LLT_MS) {
++                      wpa_printf(MSG_ERROR,
++                                 "Line %d: Invalid fst_llt %ld (%s) (expected 1..%u)",
++                                 line, val, pos, FST_MAX_LLT_MS);
++                      return 1;
++              }
++              conf->fst_cfg.llt = (u32) val;
++#endif /* CONFIG_FST */
++      } else if (os_strcmp(buf, "track_sta_max_num") == 0) {
++              conf->track_sta_max_num = atoi(pos);
++      } else if (os_strcmp(buf, "track_sta_max_age") == 0) {
++              conf->track_sta_max_age = atoi(pos);
++      } else if (os_strcmp(buf, "no_probe_resp_if_seen_on") == 0) {
++              os_free(bss->no_probe_resp_if_seen_on);
++              bss->no_probe_resp_if_seen_on = os_strdup(pos);
++      } else if (os_strcmp(buf, "no_auth_if_seen_on") == 0) {
++              os_free(bss->no_auth_if_seen_on);
++              bss->no_auth_if_seen_on = os_strdup(pos);
 +      } else {
 +              wpa_printf(MSG_ERROR,
 +                         "Line %d: unknown configuration item '%s'",
 +                         line, buf);
 +              return 1;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +/**
 + * hostapd_config_read - Read and parse a configuration file
 + * @fname: Configuration file name (including path, if needed)
 + * Returns: Allocated configuration data structure
 + */
 +struct hostapd_config * hostapd_config_read(const char *fname)
 +{
 +      struct hostapd_config *conf;
 +      FILE *f;
 +      char buf[512], *pos;
 +      int line = 0;
 +      int errors = 0;
 +      size_t i;
 +
 +      f = fopen(fname, "r");
 +      if (f == NULL) {
 +              wpa_printf(MSG_ERROR, "Could not open configuration file '%s' "
 +                         "for reading.", fname);
 +              return NULL;
 +      }
 +
 +      conf = hostapd_config_defaults();
 +      if (conf == NULL) {
 +              fclose(f);
 +              return NULL;
 +      }
 +
 +      /* set default driver based on configuration */
 +      conf->driver = wpa_drivers[0];
 +      if (conf->driver == NULL) {
 +              wpa_printf(MSG_ERROR, "No driver wrappers registered!");
 +              hostapd_config_free(conf);
 +              fclose(f);
 +              return NULL;
 +      }
 +
 +      conf->last_bss = conf->bss[0];
 +
 +      while (fgets(buf, sizeof(buf), f)) {
 +              struct hostapd_bss_config *bss;
 +
 +              bss = conf->last_bss;
 +              line++;
 +
 +              if (buf[0] == '#')
 +                      continue;
 +              pos = buf;
 +              while (*pos != '\0') {
 +                      if (*pos == '\n') {
 +                              *pos = '\0';
 +                              break;
 +                      }
 +                      pos++;
 +              }
 +              if (buf[0] == '\0')
 +                      continue;
 +
 +              pos = os_strchr(buf, '=');
 +              if (pos == NULL) {
 +                      wpa_printf(MSG_ERROR, "Line %d: invalid line '%s'",
 +                                 line, buf);
 +                      errors++;
 +                      continue;
 +              }
 +              *pos = '\0';
 +              pos++;
 +              errors += hostapd_config_fill(conf, bss, buf, pos, line);
 +      }
 +
 +      fclose(f);
 +
 +      for (i = 0; i < conf->num_bss; i++)
 +              hostapd_set_security_params(conf->bss[i], 1);
 +
 +      if (hostapd_config_check(conf, 1))
 +              errors++;
 +
 +#ifndef WPA_IGNORE_CONFIG_ERRORS
 +      if (errors) {
 +              wpa_printf(MSG_ERROR, "%d errors found in configuration file "
 +                         "'%s'", errors, fname);
 +              hostapd_config_free(conf);
 +              conf = NULL;
 +      }
 +#endif /* WPA_IGNORE_CONFIG_ERRORS */
 +
 +      return conf;
 +}
 +
 +
 +int hostapd_set_iface(struct hostapd_config *conf,
++                    struct hostapd_bss_config *bss, const char *field,
++                    char *value)
 +{
 +      int errors;
 +      size_t i;
 +
 +      errors = hostapd_config_fill(conf, bss, field, value, 0);
 +      if (errors) {
 +              wpa_printf(MSG_INFO, "Failed to set configuration field '%s' "
 +                         "to value '%s'", field, value);
 +              return -1;
 +      }
 +
 +      for (i = 0; i < conf->num_bss; i++)
 +              hostapd_set_security_params(conf->bss[i], 0);
 +
 +      if (hostapd_config_check(conf, 0)) {
 +              wpa_printf(MSG_ERROR, "Configuration check failed");
 +              return -1;
 +      }
 +
 +      return 0;
 +}
index fba57b87ad7c605652c6f0a9d01c76c9b2e7cf61,0000000000000000000000000000000000000000..c98bdb683ba109fd0fcb2776d11edd631383d2c5
mode 100644,000000..100644
--- /dev/null
@@@ -1,17 -1,0 +1,17 @@@
-                     struct hostapd_bss_config *bss, char *field,
 +/*
 + * hostapd / Configuration file parser
 + * Copyright (c) 2003-2009, Jouni Malinen <j@w1.fi>
 + *
 + * This software may be distributed under the terms of the BSD license.
 + * See README for more details.
 + */
 +
 +#ifndef CONFIG_FILE_H
 +#define CONFIG_FILE_H
 +
 +struct hostapd_config * hostapd_config_read(const char *fname);
 +int hostapd_set_iface(struct hostapd_config *conf,
++                    struct hostapd_bss_config *bss, const char *field,
 +                    char *value);
 +
 +#endif /* CONFIG_FILE_H */
index 86f1aa6dfdb2f5381a473dca1de418587c6fe0bd,0000000000000000000000000000000000000000..cb6fb1757708237bedc9e785684c7f623e1807ff
mode 100644,000000..100644
--- /dev/null
@@@ -1,2649 -1,0 +1,3217 @@@
-               if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK) {
-                       ret = os_snprintf(pos, end - pos, "WPA-PSK ");
-                       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 (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 (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 (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 (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 (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;
-               }
 +/*
 + * hostapd / UNIX domain socket -based control interface
 + * 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"
 +
 +#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/common.h"
 +#include "utils/eloop.h"
 +#include "common/version.h"
 +#include "common/ieee802_11_defs.h"
 +#include "crypto/tls.h"
 +#include "drivers/driver.h"
++#include "eapol_auth/eapol_auth_sm.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/wpa_auth.h"
 +#include "ap/ieee802_11.h"
 +#include "ap/sta_info.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 "fst/fst_ctrl_iface.h"
 +#include "config_file.h"
 +#include "ctrl_iface.h"
 +
 +
++#define HOSTAPD_CLI_DUP_VALUE_MAX_LEN 256
++
 +struct wpa_ctrl_dst {
 +      struct wpa_ctrl_dst *next;
 +      struct sockaddr_un addr;
 +      socklen_t addrlen;
 +      int debug_level;
 +      int errors;
 +};
 +
 +
 +static void hostapd_ctrl_iface_send(struct hostapd_data *hapd, int level,
++                                  enum wpa_msg_type type,
 +                                  const char *buf, size_t len);
 +
 +
 +static int hostapd_ctrl_iface_attach(struct hostapd_data *hapd,
 +                                   struct sockaddr_un *from,
 +                                   socklen_t fromlen)
 +{
 +      struct wpa_ctrl_dst *dst;
 +
 +      dst = os_zalloc(sizeof(*dst));
 +      if (dst == NULL)
 +              return -1;
 +      os_memcpy(&dst->addr, from, sizeof(struct sockaddr_un));
 +      dst->addrlen = fromlen;
 +      dst->debug_level = MSG_INFO;
 +      dst->next = hapd->ctrl_dst;
 +      hapd->ctrl_dst = dst;
 +      wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor attached",
 +                  (u8 *) from->sun_path,
 +                  fromlen - offsetof(struct sockaddr_un, sun_path));
 +      return 0;
 +}
 +
 +
 +static int hostapd_ctrl_iface_detach(struct hostapd_data *hapd,
 +                                   struct sockaddr_un *from,
 +                                   socklen_t fromlen)
 +{
 +      struct wpa_ctrl_dst *dst, *prev = NULL;
 +
 +      dst = hapd->ctrl_dst;
 +      while (dst) {
 +              if (fromlen == dst->addrlen &&
 +                  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);
 +                      return 0;
 +              }
 +              prev = dst;
 +              dst = dst->next;
 +      }
 +      return -1;
 +}
 +
 +
 +static int hostapd_ctrl_iface_level(struct hostapd_data *hapd,
 +                                  struct sockaddr_un *from,
 +                                  socklen_t fromlen,
 +                                  char *level)
 +{
 +      struct wpa_ctrl_dst *dst;
 +
 +      wpa_printf(MSG_DEBUG, "CTRL_IFACE LEVEL %s", level);
 +
 +      dst = hapd->ctrl_dst;
 +      while (dst) {
 +              if (fromlen == dst->addrlen &&
 +                  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));
 +                      dst->debug_level = atoi(level);
 +                      return 0;
 +              }
 +              dst = dst->next;
 +      }
 +
 +      return -1;
 +}
 +
 +
 +static int hostapd_ctrl_iface_new_sta(struct hostapd_data *hapd,
 +                                    const char *txtaddr)
 +{
 +      u8 addr[ETH_ALEN];
 +      struct sta_info *sta;
 +
 +      wpa_printf(MSG_DEBUG, "CTRL_IFACE NEW_STA %s", txtaddr);
 +
 +      if (hwaddr_aton(txtaddr, addr))
 +              return -1;
 +
 +      sta = ap_get_sta(hapd, addr);
 +      if (sta)
 +              return 0;
 +
 +      wpa_printf(MSG_DEBUG, "Add new STA " MACSTR " based on ctrl_iface "
 +                 "notification", MAC2STR(addr));
 +      sta = ap_sta_add(hapd, addr);
 +      if (sta == NULL)
 +              return -1;
 +
 +      hostapd_new_assoc_sta(hapd, sta, 0);
 +      return 0;
 +}
 +
 +
 +#ifdef CONFIG_IEEE80211W
 +#ifdef NEED_AP_MLME
 +static int hostapd_ctrl_iface_sa_query(struct hostapd_data *hapd,
 +                                     const char *txtaddr)
 +{
 +      u8 addr[ETH_ALEN];
 +      u8 trans_id[WLAN_SA_QUERY_TR_ID_LEN];
 +
 +      wpa_printf(MSG_DEBUG, "CTRL_IFACE SA_QUERY %s", txtaddr);
 +
 +      if (hwaddr_aton(txtaddr, addr) ||
 +          os_get_random(trans_id, WLAN_SA_QUERY_TR_ID_LEN) < 0)
 +              return -1;
 +
 +      ieee802_11_send_sa_query_req(hapd, addr, trans_id);
 +
 +      return 0;
 +}
 +#endif /* NEED_AP_MLME */
 +#endif /* CONFIG_IEEE80211W */
 +
 +
 +#ifdef CONFIG_WPS
 +static int hostapd_ctrl_iface_wps_pin(struct hostapd_data *hapd, char *txt)
 +{
 +      char *pin = os_strchr(txt, ' ');
 +      char *timeout_txt;
 +      int timeout;
 +      u8 addr_buf[ETH_ALEN], *addr = NULL;
 +      char *pos;
 +
 +      if (pin == NULL)
 +              return -1;
 +      *pin++ = '\0';
 +
 +      timeout_txt = os_strchr(pin, ' ');
 +      if (timeout_txt) {
 +              *timeout_txt++ = '\0';
 +              timeout = atoi(timeout_txt);
 +              pos = os_strchr(timeout_txt, ' ');
 +              if (pos) {
 +                      *pos++ = '\0';
 +                      if (hwaddr_aton(pos, addr_buf) == 0)
 +                              addr = addr_buf;
 +              }
 +      } else
 +              timeout = 0;
 +
 +      return hostapd_wps_add_pin(hapd, addr, txt, pin, timeout);
 +}
 +
 +
 +static int hostapd_ctrl_iface_wps_check_pin(
 +      struct hostapd_data *hapd, char *cmd, char *buf, size_t buflen)
 +{
 +      char pin[9];
 +      size_t len;
 +      char *pos;
 +      int ret;
 +
 +      wpa_hexdump_ascii_key(MSG_DEBUG, "WPS_CHECK_PIN",
 +                            (u8 *) cmd, os_strlen(cmd));
 +      for (pos = cmd, len = 0; *pos != '\0'; pos++) {
 +              if (*pos < '0' || *pos > '9')
 +                      continue;
 +              pin[len++] = *pos;
 +              if (len == 9) {
 +                      wpa_printf(MSG_DEBUG, "WPS: Too long PIN");
 +                      return -1;
 +              }
 +      }
 +      if (len != 4 && len != 8) {
 +              wpa_printf(MSG_DEBUG, "WPS: Invalid PIN length %d", (int) len);
 +              return -1;
 +      }
 +      pin[len] = '\0';
 +
 +      if (len == 8) {
 +              unsigned int pin_val;
 +              pin_val = atoi(pin);
 +              if (!wps_pin_valid(pin_val)) {
 +                      wpa_printf(MSG_DEBUG, "WPS: Invalid checksum digit");
 +                      ret = os_snprintf(buf, buflen, "FAIL-CHECKSUM\n");
 +                      if (os_snprintf_error(buflen, ret))
 +                              return -1;
 +                      return ret;
 +              }
 +      }
 +
 +      ret = os_snprintf(buf, buflen, "%s", pin);
 +      if (os_snprintf_error(buflen, ret))
 +              return -1;
 +
 +      return ret;
 +}
 +
 +
 +#ifdef CONFIG_WPS_NFC
 +static int hostapd_ctrl_iface_wps_nfc_tag_read(struct hostapd_data *hapd,
 +                                             char *pos)
 +{
 +      size_t len;
 +      struct wpabuf *buf;
 +      int ret;
 +
 +      len = os_strlen(pos);
 +      if (len & 0x01)
 +              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;
 +      }
 +
 +      ret = hostapd_wps_nfc_tag_read(hapd, buf);
 +      wpabuf_free(buf);
 +
 +      return ret;
 +}
 +
 +
 +static int hostapd_ctrl_iface_wps_nfc_config_token(struct hostapd_data *hapd,
 +                                                 char *cmd, char *reply,
 +                                                 size_t max_len)
 +{
 +      int ndef;
 +      struct wpabuf *buf;
 +      int res;
 +
 +      if (os_strcmp(cmd, "WPS") == 0)
 +              ndef = 0;
 +      else if (os_strcmp(cmd, "NDEF") == 0)
 +              ndef = 1;
 +      else
 +              return -1;
 +
 +      buf = hostapd_wps_nfc_config_token(hapd, ndef);
 +      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;
 +}
 +
 +
 +static int hostapd_ctrl_iface_wps_nfc_token_gen(struct hostapd_data *hapd,
 +                                              char *reply, size_t max_len,
 +                                              int ndef)
 +{
 +      struct wpabuf *buf;
 +      int res;
 +
 +      buf = hostapd_wps_nfc_token_gen(hapd, ndef);
 +      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;
 +}
 +
 +
 +static int hostapd_ctrl_iface_wps_nfc_token(struct hostapd_data *hapd,
 +                                          char *cmd, char *reply,
 +                                          size_t max_len)
 +{
 +      if (os_strcmp(cmd, "WPS") == 0)
 +              return hostapd_ctrl_iface_wps_nfc_token_gen(hapd, reply,
 +                                                          max_len, 0);
 +
 +      if (os_strcmp(cmd, "NDEF") == 0)
 +              return hostapd_ctrl_iface_wps_nfc_token_gen(hapd, reply,
 +                                                          max_len, 1);
 +
 +      if (os_strcmp(cmd, "enable") == 0)
 +              return hostapd_wps_nfc_token_enable(hapd);
 +
 +      if (os_strcmp(cmd, "disable") == 0) {
 +              hostapd_wps_nfc_token_disable(hapd);
 +              return 0;
 +      }
 +
 +      return -1;
 +}
 +
 +
 +static int hostapd_ctrl_iface_nfc_get_handover_sel(struct hostapd_data *hapd,
 +                                                 char *cmd, char *reply,
 +                                                 size_t max_len)
 +{
 +      struct wpabuf *buf;
 +      int res;
 +      char *pos;
 +      int ndef;
 +
 +      pos = os_strchr(cmd, ' ');
 +      if (pos == NULL)
 +              return -1;
 +      *pos++ = '\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-CR") == 0)
 +              buf = hostapd_wps_nfc_hs_cr(hapd, ndef);
 +      else
 +              buf = NULL;
 +      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;
 +}
 +
 +
 +static int hostapd_ctrl_iface_nfc_report_handover(struct hostapd_data *hapd,
 +                                                char *cmd)
 +{
 +      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 */
 +
 +
 +static int hostapd_ctrl_iface_wps_ap_pin(struct hostapd_data *hapd, char *txt,
 +                                       char *buf, size_t buflen)
 +{
 +      int timeout = 300;
 +      char *pos;
 +      const char *pin_txt;
 +
 +      pos = os_strchr(txt, ' ');
 +      if (pos)
 +              *pos++ = '\0';
 +
 +      if (os_strcmp(txt, "disable") == 0) {
 +              hostapd_wps_ap_pin_disable(hapd);
 +              return os_snprintf(buf, buflen, "OK\n");
 +      }
 +
 +      if (os_strcmp(txt, "random") == 0) {
 +              if (pos)
 +                      timeout = atoi(pos);
 +              pin_txt = hostapd_wps_ap_pin_random(hapd, timeout);
 +              if (pin_txt == NULL)
 +                      return -1;
 +              return os_snprintf(buf, buflen, "%s", pin_txt);
 +      }
 +
 +      if (os_strcmp(txt, "get") == 0) {
 +              pin_txt = hostapd_wps_ap_pin_get(hapd);
 +              if (pin_txt == NULL)
 +                      return -1;
 +              return os_snprintf(buf, buflen, "%s", pin_txt);
 +      }
 +
 +      if (os_strcmp(txt, "set") == 0) {
 +              char *pin;
 +              if (pos == NULL)
 +                      return -1;
 +              pin = pos;
 +              pos = os_strchr(pos, ' ');
 +              if (pos) {
 +                      *pos++ = '\0';
 +                      timeout = atoi(pos);
 +              }
 +              if (os_strlen(pin) > buflen)
 +                      return -1;
 +              if (hostapd_wps_ap_pin_set(hapd, pin, timeout) < 0)
 +                      return -1;
 +              return os_snprintf(buf, buflen, "%s", pin);
 +      }
 +
 +      return -1;
 +}
 +
 +
 +static int hostapd_ctrl_iface_wps_config(struct hostapd_data *hapd, char *txt)
 +{
 +      char *pos;
 +      char *ssid, *auth, *encr = NULL, *key = NULL;
 +
 +      ssid = txt;
 +      pos = os_strchr(txt, ' ');
 +      if (!pos)
 +              return -1;
 +      *pos++ = '\0';
 +
 +      auth = pos;
 +      pos = os_strchr(pos, ' ');
 +      if (pos) {
 +              *pos++ = '\0';
 +              encr = pos;
 +              pos = os_strchr(pos, ' ');
 +              if (pos) {
 +                      *pos++ = '\0';
 +                      key = pos;
 +              }
 +      }
 +
 +      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
 +
 +static int hostapd_ctrl_iface_disassoc_imminent(struct hostapd_data *hapd,
 +                                              const char *cmd)
 +{
 +      u8 addr[ETH_ALEN];
 +      int disassoc_timer;
 +      struct sta_info *sta;
 +
 +      if (hwaddr_aton(cmd, addr))
 +              return -1;
 +      if (cmd[17] != ' ')
 +              return -1;
 +      disassoc_timer = atoi(cmd + 17);
 +
 +      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 wnm_send_disassoc_imminent(hapd, sta, disassoc_timer);
 +}
 +
 +
 +static int hostapd_ctrl_iface_ess_disassoc(struct hostapd_data *hapd,
 +                                         const char *cmd)
 +{
 +      u8 addr[ETH_ALEN];
 +      const char *url, *timerstr;
 +      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;
 +      timerstr++;
 +      disassoc_timer = atoi(timerstr);
 +      if (disassoc_timer < 0 || disassoc_timer > 65535)
 +              return -1;
 +
 +      url = os_strchr(timerstr, ' ');
 +      if (url == NULL)
 +              return -1;
 +      url++;
 +
 +      return wnm_send_ess_disassoc_imminent(hapd, sta, url, disassoc_timer);
 +}
 +
 +
 +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;
 +
 +      if (hwaddr_aton(cmd, addr)) {
 +              wpa_printf(MSG_DEBUG, "Invalid STA MAC address");
 +              return -1;
 +      }
 +
 +      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;
 +      }
 +
 +      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;
 +              }
 +      }
 +
 +      pos = os_strstr(cmd, " valid_int=");
 +      if (pos) {
 +              pos += 11;
 +              valid_int = atoi(pos);
 +      }
 +
 +      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));
 +      }
 +
 +
 +      /*
 +       * 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 */
 +
 +
++static int hostapd_ctrl_iface_get_key_mgmt(struct hostapd_data *hapd,
++                                         char *buf, size_t buflen)
++{
++      int ret = 0;
++      char *pos, *end;
++
++      pos = buf;
++      end = buf + buflen;
++
++      WPA_ASSERT(hapd->conf->wpa_key_mgmt);
++
++      if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK) {
++              ret = os_snprintf(pos, end - pos, "WPA-PSK ");
++              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 (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 (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 (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 (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 (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;
++      }
++
++      if (pos > buf && *(pos - 1) == ' ') {
++              *(pos - 1) = '\0';
++              pos--;
++      }
++
++      return pos - buf;
++}
++
++
 +static int hostapd_ctrl_iface_get_config(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, "bssid=" MACSTR "\n"
 +                        "ssid=%s\n",
 +                        MAC2STR(hapd->own_addr),
 +                        wpa_ssid_txt(hapd->conf->ssid.ssid,
 +                                     hapd->conf->ssid.ssid_len));
 +      if (os_snprintf_error(end - pos, ret))
 +              return pos - buf;
 +      pos += ret;
 +
 +#ifdef CONFIG_WPS
 +      ret = os_snprintf(pos, end - pos, "wps_state=%s\n",
 +                        hapd->conf->wps_state == 0 ? "disabled" :
 +                        (hapd->conf->wps_state == 1 ? "not configured" :
 +                         "configured"));
 +      if (os_snprintf_error(end - pos, ret))
 +              return pos - buf;
 +      pos += ret;
 +
 +      if (hapd->conf->wps_state && hapd->conf->wpa &&
 +          hapd->conf->ssid.wpa_passphrase) {
 +              ret = os_snprintf(pos, end - pos, "passphrase=%s\n",
 +                                hapd->conf->ssid.wpa_passphrase);
 +              if (os_snprintf_error(end - pos, ret))
 +                      return pos - buf;
 +              pos += ret;
 +      }
 +
 +      if (hapd->conf->wps_state && hapd->conf->wpa &&
 +          hapd->conf->ssid.wpa_psk &&
 +          hapd->conf->ssid.wpa_psk->group) {
 +              char hex[PMK_LEN * 2 + 1];
 +              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 (os_snprintf_error(end - pos, ret))
 +                      return pos - buf;
 +              pos += ret;
 +      }
 +#endif /* CONFIG_WPS */
 +
++      if (hapd->conf->wpa) {
++              ret = os_snprintf(pos, end - pos, "wpa=%d\n", hapd->conf->wpa);
++              if (os_snprintf_error(end - pos, ret))
++                      return pos - buf;
++              pos += ret;
++      }
++
 +      if (hapd->conf->wpa && hapd->conf->wpa_key_mgmt) {
 +              ret = os_snprintf(pos, end - pos, "key_mgmt=");
 +              if (os_snprintf_error(end - pos, ret))
 +                      return pos - buf;
 +              pos += ret;
 +
-       const struct iphdr *ip;
++              pos += hostapd_ctrl_iface_get_key_mgmt(hapd, pos, end - pos);
 +
 +              ret = os_snprintf(pos, end - pos, "\n");
 +              if (os_snprintf_error(end - pos, ret))
 +                      return pos - buf;
 +              pos += ret;
 +      }
 +
 +      if (hapd->conf->wpa) {
 +              ret = os_snprintf(pos, end - pos, "group_cipher=%s\n",
 +                                wpa_cipher_txt(hapd->conf->wpa_group));
 +              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 (os_snprintf_error(end - pos, ret))
 +                      return pos - buf;
 +              pos += ret;
 +
 +              ret = wpa_write_ciphers(pos, end, hapd->conf->rsn_pairwise,
 +                                      " ");
 +              if (ret < 0)
 +                      return pos - buf;
 +              pos += ret;
 +
 +              ret = os_snprintf(pos, end - pos, "\n");
 +              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 (os_snprintf_error(end - pos, ret))
 +                      return pos - buf;
 +              pos += ret;
 +
 +              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 (os_snprintf_error(end - pos, ret))
 +                      return pos - buf;
 +              pos += ret;
 +      }
 +
 +      return pos - buf;
 +}
 +
 +
 +static int hostapd_ctrl_iface_set(struct hostapd_data *hapd, char *cmd)
 +{
 +      char *value;
 +      int ret = 0;
 +
 +      value = os_strchr(cmd, ' ');
 +      if (value == NULL)
 +              return -1;
 +      *value++ = '\0';
 +
 +      wpa_printf(MSG_DEBUG, "CTRL_IFACE SET '%s'='%s'", cmd, value);
 +      if (0) {
 +#ifdef CONFIG_WPS_TESTING
 +      } else if (os_strcasecmp(cmd, "wps_version_number") == 0) {
 +              long int val;
 +              val = strtol(value, NULL, 0);
 +              if (val < 0 || val > 0xff) {
 +                      ret = -1;
 +                      wpa_printf(MSG_DEBUG, "WPS: Invalid "
 +                                 "wps_version_number %ld", val);
 +              } else {
 +                      wps_version_number = val;
 +                      wpa_printf(MSG_DEBUG, "WPS: Testing - force WPS "
 +                                 "version %u.%u",
 +                                 (wps_version_number & 0xf0) >> 4,
 +                                 wps_version_number & 0x0f);
 +                      hostapd_wps_update_ie(hapd);
 +              }
 +      } else if (os_strcasecmp(cmd, "wps_testing_dummy_cred") == 0) {
 +              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) {
 +              int val = atoi(value);
 +              if (val <= 0)
 +                      ret = -1;
 +              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;
 +}
 +
 +
 +static int hostapd_ctrl_iface_get(struct hostapd_data *hapd, char *cmd,
 +                                char *buf, size_t buflen)
 +{
 +      int res;
 +
 +      wpa_printf(MSG_DEBUG, "CTRL_IFACE GET '%s'", cmd);
 +
 +      if (os_strcmp(cmd, "version") == 0) {
 +              res = os_snprintf(buf, buflen, "%s", VERSION_STR);
 +              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;
 +      }
 +
 +      return -1;
 +}
 +
 +
 +static int hostapd_ctrl_iface_enable(struct hostapd_iface *iface)
 +{
 +      if (hostapd_enable_iface(iface) < 0) {
 +              wpa_printf(MSG_ERROR, "Enabling of interface failed");
 +              return -1;
 +      }
 +      return 0;
 +}
 +
 +
 +static int hostapd_ctrl_iface_reload(struct hostapd_iface *iface)
 +{
 +      if (hostapd_reload_iface(iface) < 0) {
 +              wpa_printf(MSG_ERROR, "Reloading of interface failed");
 +              return -1;
 +      }
 +      return 0;
 +}
 +
 +
 +static int hostapd_ctrl_iface_disable(struct hostapd_iface *iface)
 +{
 +      if (hostapd_disable_iface(iface) < 0) {
 +              wpa_printf(MSG_ERROR, "Disabling of interface failed");
 +              return -1;
 +      }
 +      return 0;
 +}
 +
 +
 +#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;
-       ip = (const struct iphdr *) (eth + 1);
-       pos = (const u8 *) (ip + 1);
++      struct iphdr ip;
 +      const u8 *pos;
 +      unsigned int i;
 +
 +      if (len != HWSIM_PACKETLEN)
 +              return;
 +
 +      eth = (const struct ether_header *) buf;
-       if (ip->ihl != 5 || ip->version != 4 ||
-           ntohs(ip->tot_len) != HWSIM_IP_LEN)
++      os_memcpy(&ip, eth + 1, sizeof(ip));
++      pos = &buf[sizeof(*eth) + sizeof(ip)];
 +
-       for (i = 0; i < HWSIM_IP_LEN - sizeof(*ip); i++) {
++      if (ip.ihl != 5 || ip.version != 4 ||
++          ntohs(ip.tot_len) != HWSIM_IP_LEN)
 +              return;
 +
-       u8 buf[HWSIM_PACKETLEN];
++      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;
-       eth = (struct ether_header *) buf;
++      u8 buf[2 + 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;
 +
-       ip->saddr = htonl(192 << 24 | 168 << 16 | 1 << 8 | 1);
-       ip->daddr = htonl(192 << 24 | 168 << 16 | 1 << 8 | 2);
++      eth = (struct ether_header *) &buf[2];
 +      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;
-       if (l2_packet_send(hapd->l2_test, dst, ETHERTYPE_IP, buf,
++      ip->saddr = htonl(192U << 24 | 168 << 16 | 1 << 8 | 1);
++      ip->daddr = htonl(192U << 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;
 +
- static void hostapd_ctrl_iface_receive(int sock, void *eloop_ctx,
-                                      void *sock_ctx)
++      if (l2_packet_send(hapd->l2_test, dst, ETHERTYPE_IP, &buf[2],
 +                         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 */
 +}
 +
++
++static int hostapd_ctrl_test_fail(struct hostapd_data *hapd, char *cmd)
++{
++#ifdef WPA_TRACE_BFD
++      extern char wpa_trace_test_fail_func[256];
++      extern unsigned int wpa_trace_test_fail_after;
++      char *pos;
++
++      wpa_trace_test_fail_after = atoi(cmd);
++      pos = os_strchr(cmd, ':');
++      if (pos) {
++              pos++;
++              os_strlcpy(wpa_trace_test_fail_func, pos,
++                         sizeof(wpa_trace_test_fail_func));
++      } else {
++              wpa_trace_test_fail_after = 0;
++      }
++
++      return 0;
++#else /* WPA_TRACE_BFD */
++      return -1;
++#endif /* WPA_TRACE_BFD */
++}
++
++
++static int hostapd_ctrl_get_fail(struct hostapd_data *hapd,
++                               char *buf, size_t buflen)
++{
++#ifdef WPA_TRACE_BFD
++      extern char wpa_trace_test_fail_func[256];
++      extern unsigned int wpa_trace_test_fail_after;
++
++      return os_snprintf(buf, buflen, "%u:%s", wpa_trace_test_fail_after,
++                         wpa_trace_test_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;
 +}
 +
 +
-       struct hostapd_data *hapd = eloop_ctx;
-       char buf[4096];
-       int res;
-       struct sockaddr_un from;
-       socklen_t fromlen = sizeof(from);
-       char *reply;
-       const int reply_size = 4096;
-       int reply_len;
-       int level = MSG_DEBUG;
++static int hostapd_ctrl_iface_eapol_reauth(struct hostapd_data *hapd,
++                                         const char *cmd)
 +{
-       res = recvfrom(sock, buf, sizeof(buf) - 1, 0,
-                      (struct sockaddr *) &from, &fromlen);
-       if (res < 0) {
-               wpa_printf(MSG_ERROR, "recvfrom(ctrl_iface): %s",
-                          strerror(errno));
-               return;
++      u8 addr[ETH_ALEN];
++      struct sta_info *sta;
 +
-       buf[res] = '\0';
-       if (os_strcmp(buf, "PING") == 0)
-               level = MSG_EXCESSIVE;
-       wpa_hexdump_ascii(level, "RX ctrl_iface", (u8 *) buf, res);
++      if (hwaddr_aton(cmd, addr))
++              return -1;
++
++      sta = ap_get_sta(hapd, addr);
++      if (!sta || !sta->eapol_sm)
++              return -1;
++
++      eapol_auth_reauthenticate(sta->eapol_sm);
++      return 0;
++}
++
++
++static int hostapd_ctrl_iface_eapol_set(struct hostapd_data *hapd, char *cmd)
++{
++      u8 addr[ETH_ALEN];
++      struct sta_info *sta;
++      char *pos = cmd, *param;
++
++      if (hwaddr_aton(pos, addr) || pos[17] != ' ')
++              return -1;
++      pos += 18;
++      param = pos;
++      pos = os_strchr(pos, ' ');
++      if (!pos)
++              return -1;
++      *pos++ = '\0';
++
++      sta = ap_get_sta(hapd, addr);
++      if (!sta || !sta->eapol_sm)
++              return -1;
++
++      return eapol_auth_set_conf(sta->eapol_sm, param, pos);
++}
++
++
++static int hostapd_ctrl_iface_log_level(struct hostapd_data *hapd, char *cmd,
++                                      char *buf, size_t buflen)
++{
++      char *pos, *end, *stamp;
++      int ret;
++
++      /* cmd: "LOG_LEVEL [<level>]" */
++      if (*cmd == '\0') {
++              pos = buf;
++              end = buf + buflen;
++              ret = os_snprintf(pos, end - pos, "Current level: %s\n"
++                                "Timestamp: %d\n",
++                                debug_level_str(wpa_debug_level),
++                                wpa_debug_timestamp);
++              if (os_snprintf_error(end - pos, ret))
++                      ret = 0;
++
++              return ret;
 +      }
-       reply = os_malloc(reply_size);
-       if (reply == NULL) {
-               if (sendto(sock, "FAIL\n", 5, 0, (struct sockaddr *) &from,
-                          fromlen) < 0) {
-                       wpa_printf(MSG_DEBUG, "CTRL: sendto failed: %s",
-                                  strerror(errno));
 +
-               return;
++      while (*cmd == ' ')
++              cmd++;
++
++      stamp = os_strchr(cmd, ' ');
++      if (stamp) {
++              *stamp++ = '\0';
++              while (*stamp == ' ') {
++                      stamp++;
 +              }
-               if (hostapd_ctrl_iface_attach(hapd, &from, fromlen))
 +      }
 +
++      if (os_strlen(cmd)) {
++              int level = str_to_debug_level(cmd);
++              if (level < 0)
++                      return -1;
++              wpa_debug_level = level;
++      }
++
++      if (stamp && os_strlen(stamp))
++              wpa_debug_timestamp = atoi(stamp);
++
++      os_memcpy(buf, "OK\n", 3);
++      return 3;
++}
++
++
++#ifdef NEED_AP_MLME
++static int hostapd_ctrl_iface_track_sta_list(struct hostapd_data *hapd,
++                                           char *buf, size_t buflen)
++{
++      struct hostapd_iface *iface = hapd->iface;
++      char *pos, *end;
++      struct hostapd_sta_info *info;
++      struct os_reltime now;
++
++      sta_track_expire(iface, 0);
++
++      pos = buf;
++      end = buf + buflen;
++
++      os_get_reltime(&now);
++      dl_list_for_each_reverse(info, &iface->sta_seen,
++                               struct hostapd_sta_info, list) {
++              struct os_reltime age;
++              int ret;
++
++              os_reltime_sub(&now, &info->last_seen, &age);
++              ret = os_snprintf(pos, end - pos, MACSTR " %u\n",
++                                MAC2STR(info->addr), (unsigned int) age.sec);
++              if (os_snprintf_error(end - pos, ret))
++                      break;
++              pos += ret;
++      }
++
++      return pos - buf;
++}
++#endif /* NEED_AP_MLME */
++
++
++static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
++                                            char *buf, char *reply,
++                                            int reply_size,
++                                            struct sockaddr_un *from,
++                                            socklen_t fromlen)
++{
++      int reply_len, res;
++
 +      os_memcpy(reply, "OK\n", 3);
 +      reply_len = 3;
 +
 +      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, "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) {
 +                      res = wpa_get_mib(hapd->wpa_auth, reply + reply_len,
 +                                        reply_size - reply_len);
 +                      if (res < 0)
 +                              reply_len = -1;
 +                      else
 +                              reply_len += res;
 +              }
 +              if (reply_len >= 0) {
 +                      res = ieee802_1x_get_mib(hapd, reply + reply_len,
 +                                               reply_size - reply_len);
 +                      if (res < 0)
 +                              reply_len = -1;
 +                      else
 +                              reply_len += res;
 +              }
 +#ifndef CONFIG_NO_RADIUS
 +              if (reply_len >= 0) {
 +                      res = radius_client_get_mib(hapd->radius,
 +                                                  reply + reply_len,
 +                                                  reply_size - reply_len);
 +                      if (res < 0)
 +                              reply_len = -1;
 +                      else
 +                              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);
 +      } else if (os_strncmp(buf, "STA ", 4) == 0) {
 +              reply_len = hostapd_ctrl_iface_sta(hapd, buf + 4, reply,
 +                                                 reply_size);
 +      } else if (os_strncmp(buf, "STA-NEXT ", 9) == 0) {
 +              reply_len = hostapd_ctrl_iface_sta_next(hapd, buf + 9, reply,
 +                                                      reply_size);
 +      } else if (os_strcmp(buf, "ATTACH") == 0) {
-               if (hostapd_ctrl_iface_detach(hapd, &from, fromlen))
++              if (hostapd_ctrl_iface_attach(hapd, from, fromlen))
 +                      reply_len = -1;
 +      } else if (os_strcmp(buf, "DETACH") == 0) {
-               if (hostapd_ctrl_iface_level(hapd, &from, fromlen,
++              if (hostapd_ctrl_iface_detach(hapd, from, fromlen))
 +                      reply_len = -1;
 +      } else if (os_strncmp(buf, "LEVEL ", 6) == 0) {
- static void hostapd_ctrl_iface_msg_cb(void *ctx, int level, int global,
++              if (hostapd_ctrl_iface_level(hapd, from, fromlen,
 +                                                  buf + 6))
 +                      reply_len = -1;
 +      } else if (os_strncmp(buf, "NEW_STA ", 8) == 0) {
 +              if (hostapd_ctrl_iface_new_sta(hapd, buf + 8))
 +                      reply_len = -1;
 +      } else if (os_strncmp(buf, "DEAUTHENTICATE ", 15) == 0) {
 +              if (hostapd_ctrl_iface_deauthenticate(hapd, buf + 15))
 +                      reply_len = -1;
 +      } 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) {
 +              if (hostapd_ctrl_iface_sa_query(hapd, buf + 9))
 +                      reply_len = -1;
 +#endif /* NEED_AP_MLME */
 +#endif /* CONFIG_IEEE80211W */
 +#ifdef CONFIG_WPS
 +      } else if (os_strncmp(buf, "WPS_PIN ", 8) == 0) {
 +              if (hostapd_ctrl_iface_wps_pin(hapd, buf + 8))
 +                      reply_len = -1;
 +      } else if (os_strncmp(buf, "WPS_CHECK_PIN ", 14) == 0) {
 +              reply_len = hostapd_ctrl_iface_wps_check_pin(
 +                      hapd, buf + 14, reply, reply_size);
 +      } else if (os_strcmp(buf, "WPS_PBC") == 0) {
 +              if (hostapd_wps_button_pushed(hapd, NULL))
 +                      reply_len = -1;
 +      } else if (os_strcmp(buf, "WPS_CANCEL") == 0) {
 +              if (hostapd_wps_cancel(hapd))
 +                      reply_len = -1;
 +      } else if (os_strncmp(buf, "WPS_AP_PIN ", 11) == 0) {
 +              reply_len = hostapd_ctrl_iface_wps_ap_pin(hapd, buf + 11,
 +                                                        reply, reply_size);
 +      } 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))
 +                      reply_len = -1;
 +      } else if (os_strncmp(buf, "WPS_NFC_CONFIG_TOKEN ", 21) == 0) {
 +              reply_len = hostapd_ctrl_iface_wps_nfc_config_token(
 +                      hapd, buf + 21, reply, reply_size);
 +      } else if (os_strncmp(buf, "WPS_NFC_TOKEN ", 14) == 0) {
 +              reply_len = hostapd_ctrl_iface_wps_nfc_token(
 +                      hapd, buf + 14, reply, reply_size);
 +      } else if (os_strncmp(buf, "NFC_GET_HANDOVER_SEL ", 21) == 0) {
 +              reply_len = hostapd_ctrl_iface_nfc_get_handover_sel(
 +                      hapd, buf + 21, reply, reply_size);
 +      } else if (os_strncmp(buf, "NFC_REPORT_HANDOVER ", 20) == 0) {
 +              if (hostapd_ctrl_iface_nfc_report_handover(hapd, buf + 20))
 +                      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))
 +                      reply_len = -1;
 +      } 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,
 +                                                        reply_size);
 +      } else if (os_strncmp(buf, "SET ", 4) == 0) {
 +              if (hostapd_ctrl_iface_set(hapd, buf + 4))
 +                      reply_len = -1;
 +      } else if (os_strncmp(buf, "GET ", 4) == 0) {
 +              reply_len = hostapd_ctrl_iface_get(hapd, buf + 4, reply,
 +                                                 reply_size);
 +      } else if (os_strncmp(buf, "ENABLE", 6) == 0) {
 +              if (hostapd_ctrl_iface_enable(hapd->iface))
 +                      reply_len = -1;
 +      } else if (os_strncmp(buf, "RELOAD", 6) == 0) {
 +              if (hostapd_ctrl_iface_reload(hapd->iface))
 +                      reply_len = -1;
 +      } 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);
++      } else if (os_strncmp(buf, "TEST_FAIL ", 10) == 0) {
++              if (hostapd_ctrl_test_fail(hapd, buf + 10) < 0)
++                      reply_len = -1;
++      } else if (os_strcmp(buf, "GET_FAIL") == 0) {
++              reply_len = hostapd_ctrl_get_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 if (os_strncmp(buf, "EAPOL_REAUTH ", 13) == 0) {
++              if (hostapd_ctrl_iface_eapol_reauth(hapd, buf + 13))
++                      reply_len = -1;
++      } else if (os_strncmp(buf, "EAPOL_SET ", 10) == 0) {
++              if (hostapd_ctrl_iface_eapol_set(hapd, buf + 10))
++                      reply_len = -1;
++      } else if (os_strncmp(buf, "LOG_LEVEL", 9) == 0) {
++              reply_len = hostapd_ctrl_iface_log_level(
++                      hapd, buf + 9, reply, reply_size);
++#ifdef NEED_AP_MLME
++      } else if (os_strcmp(buf, "TRACK_STA_LIST") == 0) {
++              reply_len = hostapd_ctrl_iface_track_sta_list(
++                      hapd, reply, reply_size);
++#endif /* NEED_AP_MLME */
 +      } else {
 +              os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
 +              reply_len = 16;
 +      }
 +
 +      if (reply_len < 0) {
 +              os_memcpy(reply, "FAIL\n", 5);
 +              reply_len = 5;
 +      }
++
++      return reply_len;
++}
++
++
++static void hostapd_ctrl_iface_receive(int sock, void *eloop_ctx,
++                                     void *sock_ctx)
++{
++      struct hostapd_data *hapd = eloop_ctx;
++      char buf[4096];
++      int res;
++      struct sockaddr_un from;
++      socklen_t fromlen = sizeof(from);
++      char *reply;
++      const int reply_size = 4096;
++      int reply_len;
++      int level = MSG_DEBUG;
++
++      res = recvfrom(sock, buf, sizeof(buf) - 1, 0,
++                     (struct sockaddr *) &from, &fromlen);
++      if (res < 0) {
++              wpa_printf(MSG_ERROR, "recvfrom(ctrl_iface): %s",
++                         strerror(errno));
++              return;
++      }
++      buf[res] = '\0';
++      if (os_strcmp(buf, "PING") == 0)
++              level = MSG_EXCESSIVE;
++      wpa_hexdump_ascii(level, "RX ctrl_iface", (u8 *) buf, res);
++
++      reply = os_malloc(reply_size);
++      if (reply == NULL) {
++              if (sendto(sock, "FAIL\n", 5, 0, (struct sockaddr *) &from,
++                         fromlen) < 0) {
++                      wpa_printf(MSG_DEBUG, "CTRL: sendto failed: %s",
++                                 strerror(errno));
++              }
++              return;
++      }
++
++      reply_len = hostapd_ctrl_iface_receive_process(hapd, buf,
++                                                     reply, reply_size,
++                                                     &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);
 +}
 +
 +
 +static char * hostapd_ctrl_iface_path(struct hostapd_data *hapd)
 +{
 +      char *buf;
 +      size_t len;
 +
 +      if (hapd->conf->ctrl_interface == NULL)
 +              return NULL;
 +
 +      len = os_strlen(hapd->conf->ctrl_interface) +
 +              os_strlen(hapd->conf->iface) + 2;
 +      buf = os_malloc(len);
 +      if (buf == NULL)
 +              return NULL;
 +
 +      os_snprintf(buf, len, "%s/%s",
 +                  hapd->conf->ctrl_interface, hapd->conf->iface);
 +      buf[len - 1] = '\0';
 +      return buf;
 +}
 +
 +
-       hostapd_ctrl_iface_send(hapd, level, txt, len);
++static void hostapd_ctrl_iface_msg_cb(void *ctx, int level,
++                                    enum wpa_msg_type type,
 +                                    const char *txt, size_t len)
 +{
 +      struct hostapd_data *hapd = ctx;
 +      if (hapd == NULL)
 +              return;
-       char reply[24];
++      hostapd_ctrl_iface_send(hapd, level, type, txt, len);
 +}
 +
 +
 +int hostapd_ctrl_iface_init(struct hostapd_data *hapd)
 +{
 +      struct sockaddr_un addr;
 +      int s = -1;
 +      char *fname = NULL;
 +
 +      if (hapd->ctrl_sock > -1) {
 +              wpa_printf(MSG_DEBUG, "ctrl_iface already exists!");
 +              return 0;
 +      }
 +
 +      if (hapd->conf->ctrl_interface == NULL)
 +              return 0;
 +
 +      if (mkdir(hapd->conf->ctrl_interface, S_IRWXU | S_IRWXG) < 0) {
 +              if (errno == EEXIST) {
 +                      wpa_printf(MSG_DEBUG, "Using existing control "
 +                                 "interface directory.");
 +              } else {
 +                      wpa_printf(MSG_ERROR, "mkdir[ctrl_interface]: %s",
 +                                 strerror(errno));
 +                      goto fail;
 +              }
 +      }
 +
 +      if (hapd->conf->ctrl_interface_gid_set &&
 +          chown(hapd->conf->ctrl_interface, -1,
 +                hapd->conf->ctrl_interface_gid) < 0) {
 +              wpa_printf(MSG_ERROR, "chown[ctrl_interface]: %s",
 +                         strerror(errno));
 +              return -1;
 +      }
 +
 +      if (!hapd->conf->ctrl_interface_gid_set &&
 +          hapd->iface->interfaces->ctrl_iface_group &&
 +          chown(hapd->conf->ctrl_interface, -1,
 +                hapd->iface->interfaces->ctrl_iface_group) < 0) {
 +              wpa_printf(MSG_ERROR, "chown[ctrl_interface]: %s",
 +                         strerror(errno));
 +              return -1;
 +      }
 +
 +#ifdef ANDROID
 +      /*
 +       * Android is using umask 0077 which would leave the control interface
 +       * directory without group access. This breaks things since Wi-Fi
 +       * framework assumes that this directory can be accessed by other
 +       * applications in the wifi group. Fix this by adding group access even
 +       * if umask value would prevent this.
 +       */
 +      if (chmod(hapd->conf->ctrl_interface, S_IRWXU | S_IRWXG) < 0) {
 +              wpa_printf(MSG_ERROR, "CTRL: Could not chmod directory: %s",
 +                         strerror(errno));
 +              /* Try to continue anyway */
 +      }
 +#endif /* ANDROID */
 +
 +      if (os_strlen(hapd->conf->ctrl_interface) + 1 +
 +          os_strlen(hapd->conf->iface) >= sizeof(addr.sun_path))
 +              goto fail;
 +
 +      s = socket(PF_UNIX, SOCK_DGRAM, 0);
 +      if (s < 0) {
 +              wpa_printf(MSG_ERROR, "socket(PF_UNIX): %s", strerror(errno));
 +              goto fail;
 +      }
 +
 +      os_memset(&addr, 0, sizeof(addr));
 +#ifdef __FreeBSD__
 +      addr.sun_len = sizeof(addr);
 +#endif /* __FreeBSD__ */
 +      addr.sun_family = AF_UNIX;
 +      fname = hostapd_ctrl_iface_path(hapd);
 +      if (fname == NULL)
 +              goto fail;
 +      os_strlcpy(addr.sun_path, fname, sizeof(addr.sun_path));
 +      if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
 +              wpa_printf(MSG_DEBUG, "ctrl_iface bind(PF_UNIX) failed: %s",
 +                         strerror(errno));
 +              if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
 +                      wpa_printf(MSG_DEBUG, "ctrl_iface exists, but does not"
 +                                 " allow connections - assuming it was left"
 +                                 "over from forced program termination");
 +                      if (unlink(fname) < 0) {
 +                              wpa_printf(MSG_ERROR,
 +                                         "Could not unlink existing ctrl_iface socket '%s': %s",
 +                                         fname, strerror(errno));
 +                              goto fail;
 +                      }
 +                      if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) <
 +                          0) {
 +                              wpa_printf(MSG_ERROR,
 +                                         "hostapd-ctrl-iface: bind(PF_UNIX): %s",
 +                                         strerror(errno));
 +                              goto fail;
 +                      }
 +                      wpa_printf(MSG_DEBUG, "Successfully replaced leftover "
 +                                 "ctrl_iface socket '%s'", fname);
 +              } else {
 +                      wpa_printf(MSG_INFO, "ctrl_iface exists and seems to "
 +                                 "be in use - cannot override it");
 +                      wpa_printf(MSG_INFO, "Delete '%s' manually if it is "
 +                                 "not used anymore", fname);
 +                      os_free(fname);
 +                      fname = NULL;
 +                      goto fail;
 +              }
 +      }
 +
 +      if (hapd->conf->ctrl_interface_gid_set &&
 +          chown(fname, -1, hapd->conf->ctrl_interface_gid) < 0) {
 +              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) {
 +              wpa_printf(MSG_ERROR, "chown[ctrl_interface/ifname]: %s",
 +                         strerror(errno));
 +              goto fail;
 +      }
 +
 +      if (chmod(fname, S_IRWXU | S_IRWXG) < 0) {
 +              wpa_printf(MSG_ERROR, "chmod[ctrl_interface/ifname]: %s",
 +                         strerror(errno));
 +              goto fail;
 +      }
 +      os_free(fname);
 +
 +      hapd->ctrl_sock = s;
 +      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);
 +
 +      return 0;
 +
 +fail:
 +      if (s >= 0)
 +              close(s);
 +      if (fname) {
 +              unlink(fname);
 +              os_free(fname);
 +      }
 +      return -1;
 +}
 +
 +
 +void hostapd_ctrl_iface_deinit(struct hostapd_data *hapd)
 +{
 +      struct wpa_ctrl_dst *dst, *prev;
 +
 +      if (hapd->ctrl_sock > -1) {
 +              char *fname;
 +              eloop_unregister_read_sock(hapd->ctrl_sock);
 +              close(hapd->ctrl_sock);
 +              hapd->ctrl_sock = -1;
 +              fname = hostapd_ctrl_iface_path(hapd);
 +              if (fname)
 +                      unlink(fname);
 +              os_free(fname);
 +
 +              if (hapd->conf->ctrl_interface &&
 +                  rmdir(hapd->conf->ctrl_interface) < 0) {
 +                      if (errno == ENOTEMPTY) {
 +                              wpa_printf(MSG_DEBUG, "Control interface "
 +                                         "directory not empty - leaving it "
 +                                         "behind");
 +                      } else {
 +                              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 */
 +}
 +
 +
 +static int hostapd_ctrl_iface_add(struct hapd_interfaces *interfaces,
 +                                char *buf)
 +{
 +      if (hostapd_add_iface(interfaces, buf) < 0) {
 +              wpa_printf(MSG_ERROR, "Adding interface %s failed", buf);
 +              return -1;
 +      }
 +      return 0;
 +}
 +
 +
 +static int hostapd_ctrl_iface_remove(struct hapd_interfaces *interfaces,
 +                                   char *buf)
 +{
 +      if (hostapd_remove_iface(interfaces, buf) < 0) {
 +              wpa_printf(MSG_ERROR, "Removing interface %s failed", buf);
 +              return -1;
 +      }
 +      return 0;
 +}
 +
 +
++static int hostapd_global_ctrl_iface_attach(struct hapd_interfaces *interfaces,
++                                          struct sockaddr_un *from,
++                                          socklen_t fromlen)
++{
++      struct wpa_ctrl_dst *dst;
++
++      dst = os_zalloc(sizeof(*dst));
++      if (dst == NULL)
++              return -1;
++      os_memcpy(&dst->addr, from, sizeof(struct sockaddr_un));
++      dst->addrlen = fromlen;
++      dst->debug_level = MSG_INFO;
++      dst->next = interfaces->global_ctrl_dst;
++      interfaces->global_ctrl_dst = dst;
++      wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor attached (global)",
++                  from->sun_path,
++                  fromlen - offsetof(struct sockaddr_un, sun_path));
++      return 0;
++}
++
++
++static int hostapd_global_ctrl_iface_detach(struct hapd_interfaces *interfaces,
++                                          struct sockaddr_un *from,
++                                          socklen_t fromlen)
++{
++      struct wpa_ctrl_dst *dst, *prev = NULL;
++
++      dst = interfaces->global_ctrl_dst;
++      while (dst) {
++              if (fromlen == dst->addrlen &&
++                  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 (global)",
++                                  from->sun_path,
++                                  fromlen -
++                                  offsetof(struct sockaddr_un, sun_path));
++                      if (prev == NULL)
++                              interfaces->global_ctrl_dst = dst->next;
++                      else
++                              prev->next = dst->next;
++                      os_free(dst);
++                      return 0;
++              }
++              prev = dst;
++              dst = dst->next;
++      }
++      return -1;
++}
++
++
 +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 */
 +}
 +
 +
++#ifdef CONFIG_FST
++
++static int
++hostapd_global_ctrl_iface_fst_attach(struct hapd_interfaces *interfaces,
++                                   const char *cmd)
++{
++      char ifname[IFNAMSIZ + 1];
++      struct fst_iface_cfg cfg;
++      struct hostapd_data *hapd;
++      struct fst_wpa_obj iface_obj;
++
++      if (!fst_parse_attach_command(cmd, ifname, sizeof(ifname), &cfg)) {
++              hapd = hostapd_get_iface(interfaces, ifname);
++              if (hapd) {
++                      if (hapd->iface->fst) {
++                              wpa_printf(MSG_INFO, "FST: Already attached");
++                              return -1;
++                      }
++                      fst_hostapd_fill_iface_obj(hapd, &iface_obj);
++                      hapd->iface->fst = fst_attach(ifname, hapd->own_addr,
++                                                    &iface_obj, &cfg);
++                      if (hapd->iface->fst)
++                              return 0;
++              }
++      }
++
++      return -EINVAL;
++}
++
++
++static int
++hostapd_global_ctrl_iface_fst_detach(struct hapd_interfaces *interfaces,
++                                   const char *cmd)
++{
++      char ifname[IFNAMSIZ + 1];
++      struct hostapd_data * hapd;
++
++      if (!fst_parse_detach_command(cmd, ifname, sizeof(ifname))) {
++              hapd = hostapd_get_iface(interfaces, ifname);
++              if (hapd) {
++                      if (!fst_iface_detach(ifname)) {
++                              hapd->iface->fst = NULL;
++                              hapd->iface->fst_ies = NULL;
++                              return 0;
++                      }
++              }
++      }
++
++      return -EINVAL;
++}
++
++#endif /* CONFIG_FST */
++
++
++static struct hostapd_data *
++hostapd_interfaces_get_hapd(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;
++
++                      hapd = iface->bss[j];
++                      if (os_strcmp(ifname, hapd->conf->iface) == 0)
++                              return hapd;
++              }
++      }
++
++      return NULL;
++}
++
++
++static int hostapd_ctrl_iface_dup_param(struct hostapd_data *src_hapd,
++                                      struct hostapd_data *dst_hapd,
++                                      const char *param)
++{
++      int res;
++      char *value;
++
++      value = os_zalloc(HOSTAPD_CLI_DUP_VALUE_MAX_LEN);
++      if (!value) {
++              wpa_printf(MSG_ERROR,
++                         "DUP: cannot allocate buffer to stringify %s",
++                         param);
++              goto error_return;
++      }
++
++      if (os_strcmp(param, "wpa") == 0) {
++              os_snprintf(value, HOSTAPD_CLI_DUP_VALUE_MAX_LEN, "%d",
++                          src_hapd->conf->wpa);
++      } else if (os_strcmp(param, "wpa_key_mgmt") == 0 &&
++                 src_hapd->conf->wpa_key_mgmt) {
++              res = hostapd_ctrl_iface_get_key_mgmt(
++                      src_hapd, value, HOSTAPD_CLI_DUP_VALUE_MAX_LEN);
++              if (os_snprintf_error(HOSTAPD_CLI_DUP_VALUE_MAX_LEN, res))
++                      goto error_stringify;
++      } else if (os_strcmp(param, "wpa_pairwise") == 0 &&
++                 src_hapd->conf->wpa_pairwise) {
++              res = wpa_write_ciphers(value,
++                                      value + HOSTAPD_CLI_DUP_VALUE_MAX_LEN,
++                                      src_hapd->conf->wpa_pairwise, " ");
++              if (res < 0)
++                      goto error_stringify;
++      } else if (os_strcmp(param, "rsn_pairwise") == 0 &&
++                 src_hapd->conf->rsn_pairwise) {
++              res = wpa_write_ciphers(value,
++                                      value + HOSTAPD_CLI_DUP_VALUE_MAX_LEN,
++                                      src_hapd->conf->rsn_pairwise, " ");
++              if (res < 0)
++                      goto error_stringify;
++      } else if (os_strcmp(param, "wpa_passphrase") == 0 &&
++                 src_hapd->conf->ssid.wpa_passphrase) {
++              os_snprintf(value, HOSTAPD_CLI_DUP_VALUE_MAX_LEN, "%s",
++                          src_hapd->conf->ssid.wpa_passphrase);
++      } else if (os_strcmp(param, "wpa_psk") == 0 &&
++                 src_hapd->conf->ssid.wpa_psk_set) {
++              wpa_snprintf_hex(value, HOSTAPD_CLI_DUP_VALUE_MAX_LEN,
++                      src_hapd->conf->ssid.wpa_psk->psk, PMK_LEN);
++      } else {
++              wpa_printf(MSG_WARNING, "DUP: %s cannot be duplicated", param);
++              goto error_return;
++      }
++
++      res = hostapd_set_iface(dst_hapd->iconf, dst_hapd->conf, param, value);
++      os_free(value);
++      return res;
++
++error_stringify:
++      wpa_printf(MSG_ERROR, "DUP: cannot stringify %s", param);
++error_return:
++      os_free(value);
++      return -1;
++}
++
++
++static int
++hostapd_global_ctrl_iface_dup_network(struct hapd_interfaces *interfaces,
++                                    char *cmd)
++{
++      char *p_start = cmd, *p_end;
++      struct hostapd_data *src_hapd, *dst_hapd;
++
++      /* cmd: "<src ifname> <dst ifname> <variable name> */
++
++      p_end = os_strchr(p_start, ' ');
++      if (!p_end) {
++              wpa_printf(MSG_ERROR, "DUP: no src ifname found in cmd: '%s'",
++                         cmd);
++              return -1;
++      }
++
++      *p_end = '\0';
++      src_hapd = hostapd_interfaces_get_hapd(interfaces, p_start);
++      if (!src_hapd) {
++              wpa_printf(MSG_ERROR, "DUP: no src ifname found: '%s'",
++                         p_start);
++              return -1;
++      }
++
++      p_start = p_end + 1;
++      p_end = os_strchr(p_start, ' ');
++      if (!p_end) {
++              wpa_printf(MSG_ERROR, "DUP: no dst ifname found in cmd: '%s'",
++                         cmd);
++              return -1;
++      }
++
++      *p_end = '\0';
++      dst_hapd = hostapd_interfaces_get_hapd(interfaces, p_start);
++      if (!dst_hapd) {
++              wpa_printf(MSG_ERROR, "DUP: no dst ifname found: '%s'",
++                         p_start);
++              return -1;
++      }
++
++      p_start = p_end + 1;
++      return hostapd_ctrl_iface_dup_param(src_hapd, dst_hapd, p_start);
++}
++
++
++static int hostapd_global_ctrl_iface_ifname(struct hapd_interfaces *interfaces,
++                                          const char *ifname,
++                                          char *buf, char *reply,
++                                          int reply_size,
++                                          struct sockaddr_un *from,
++                                          socklen_t fromlen)
++{
++      struct hostapd_data *hapd;
++
++      hapd = hostapd_interfaces_get_hapd(interfaces, ifname);
++      if (hapd == NULL) {
++              int res;
++
++              res = os_snprintf(reply, reply_size, "FAIL-NO-IFNAME-MATCH\n");
++              if (os_snprintf_error(reply_size, res))
++                      return -1;
++              return res;
++      }
++
++      return hostapd_ctrl_iface_receive_process(hapd, buf, reply,reply_size,
++                                                from, fromlen);
++}
++
++
 +static void hostapd_global_ctrl_iface_receive(int sock, void *eloop_ctx,
 +                                            void *sock_ctx)
 +{
 +      void *interfaces = eloop_ctx;
 +      char buf[256];
 +      int res;
 +      struct sockaddr_un from;
 +      socklen_t fromlen = sizeof(from);
-               os_free(interfaces->global_iface_path);
-               interfaces->global_iface_path = NULL;
++      char *reply;
 +      int reply_len;
++      const int reply_size = 4096;
 +
 +      res = recvfrom(sock, buf, sizeof(buf) - 1, 0,
 +                     (struct sockaddr *) &from, &fromlen);
 +      if (res < 0) {
 +              wpa_printf(MSG_ERROR, "recvfrom(ctrl_iface): %s",
 +                         strerror(errno));
 +              return;
 +      }
 +      buf[res] = '\0';
 +      wpa_printf(MSG_DEBUG, "Global ctrl_iface command: %s", buf);
 +
++      reply = os_malloc(reply_size);
++      if (reply == NULL) {
++              if (sendto(sock, "FAIL\n", 5, 0, (struct sockaddr *) &from,
++                         fromlen) < 0) {
++                      wpa_printf(MSG_DEBUG, "CTRL: sendto failed: %s",
++                                 strerror(errno));
++              }
++              return;
++      }
++
 +      os_memcpy(reply, "OK\n", 3);
 +      reply_len = 3;
 +
++      if (os_strncmp(buf, "IFNAME=", 7) == 0) {
++              char *pos = os_strchr(buf + 7, ' ');
++
++              if (pos) {
++                      *pos++ = '\0';
++                      reply_len = hostapd_global_ctrl_iface_ifname(
++                              interfaces, buf + 7, pos, reply, reply_size,
++                              &from, fromlen);
++                      goto send_reply;
++              }
++      }
++
 +      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;
++      } else if (os_strcmp(buf, "ATTACH") == 0) {
++              if (hostapd_global_ctrl_iface_attach(interfaces, &from,
++                                                   fromlen))
++                      reply_len = -1;
++      } else if (os_strcmp(buf, "DETACH") == 0) {
++              if (hostapd_global_ctrl_iface_detach(interfaces, &from,
++                      fromlen))
++                      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 */
++#ifdef CONFIG_FST
++      } else if (os_strncmp(buf, "FST-ATTACH ", 11) == 0) {
++              if (!hostapd_global_ctrl_iface_fst_attach(interfaces, buf + 11))
++                      reply_len = os_snprintf(reply, reply_size, "OK\n");
++              else
++                      reply_len = -1;
++      } else if (os_strncmp(buf, "FST-DETACH ", 11) == 0) {
++              if (!hostapd_global_ctrl_iface_fst_detach(interfaces, buf + 11))
++                      reply_len = os_snprintf(reply, reply_size, "OK\n");
++              else
++                      reply_len = -1;
++      } else if (os_strncmp(buf, "FST-MANAGER ", 12) == 0) {
++              reply_len = fst_ctrl_iface_receive(buf + 12, reply, reply_size);
++#endif /* CONFIG_FST */
++      } else if (os_strncmp(buf, "DUP_NETWORK ", 12) == 0) {
++              if (!hostapd_global_ctrl_iface_dup_network(interfaces,
++                                                         buf + 12))
++                      reply_len = os_snprintf(reply, reply_size, "OK\n");
++              else
++                      reply_len = -1;
 +      } else {
 +              wpa_printf(MSG_DEBUG, "Unrecognized global ctrl_iface command "
 +                         "ignored");
 +              reply_len = -1;
 +      }
 +
++send_reply:
 +      if (reply_len < 0) {
 +              os_memcpy(reply, "FAIL\n", 5);
 +              reply_len = 5;
 +      }
 +
 +      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);
 +}
 +
 +
 +static char * hostapd_global_ctrl_iface_path(struct hapd_interfaces *interface)
 +{
 +      char *buf;
 +      size_t len;
 +
 +      if (interface->global_iface_path == NULL)
 +              return NULL;
 +
 +      len = os_strlen(interface->global_iface_path) +
 +              os_strlen(interface->global_iface_name) + 2;
 +      buf = os_malloc(len);
 +      if (buf == NULL)
 +              return NULL;
 +
 +      os_snprintf(buf, len, "%s/%s", interface->global_iface_path,
 +                  interface->global_iface_name);
 +      buf[len - 1] = '\0';
 +      return buf;
 +}
 +
 +
 +int hostapd_global_ctrl_iface_init(struct hapd_interfaces *interface)
 +{
 +      struct sockaddr_un addr;
 +      int s = -1;
 +      char *fname = NULL;
 +
 +      if (interface->global_iface_path == NULL) {
 +              wpa_printf(MSG_DEBUG, "ctrl_iface not configured!");
 +              return 0;
 +      }
 +
 +      if (mkdir(interface->global_iface_path, S_IRWXU | S_IRWXG) < 0) {
 +              if (errno == EEXIST) {
 +                      wpa_printf(MSG_DEBUG, "Using existing control "
 +                                 "interface directory.");
 +              } else {
 +                      wpa_printf(MSG_ERROR, "mkdir[ctrl_interface]: %s",
 +                                 strerror(errno));
 +                      goto fail;
 +              }
 +      } else if (interface->ctrl_iface_group &&
 +                 chown(interface->global_iface_path, -1,
 +                       interface->ctrl_iface_group) < 0) {
 +              wpa_printf(MSG_ERROR, "chown[ctrl_interface]: %s",
 +                         strerror(errno));
 +              goto fail;
 +      }
 +
 +      if (os_strlen(interface->global_iface_path) + 1 +
 +          os_strlen(interface->global_iface_name) >= sizeof(addr.sun_path))
 +              goto fail;
 +
 +      s = socket(PF_UNIX, SOCK_DGRAM, 0);
 +      if (s < 0) {
 +              wpa_printf(MSG_ERROR, "socket(PF_UNIX): %s", strerror(errno));
 +              goto fail;
 +      }
 +
 +      os_memset(&addr, 0, sizeof(addr));
 +#ifdef __FreeBSD__
 +      addr.sun_len = sizeof(addr);
 +#endif /* __FreeBSD__ */
 +      addr.sun_family = AF_UNIX;
 +      fname = hostapd_global_ctrl_iface_path(interface);
 +      if (fname == NULL)
 +              goto fail;
 +      os_strlcpy(addr.sun_path, fname, sizeof(addr.sun_path));
 +      if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
 +              wpa_printf(MSG_DEBUG, "ctrl_iface bind(PF_UNIX) failed: %s",
 +                         strerror(errno));
 +              if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
 +                      wpa_printf(MSG_DEBUG, "ctrl_iface exists, but does not"
 +                                 " allow connections - assuming it was left"
 +                                 "over from forced program termination");
 +                      if (unlink(fname) < 0) {
 +                              wpa_printf(MSG_ERROR,
 +                                         "Could not unlink existing ctrl_iface socket '%s': %s",
 +                                         fname, strerror(errno));
 +                              goto fail;
 +                      }
 +                      if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) <
 +                          0) {
 +                              wpa_printf(MSG_ERROR, "bind(PF_UNIX): %s",
 +                                         strerror(errno));
 +                              goto fail;
 +                      }
 +                      wpa_printf(MSG_DEBUG, "Successfully replaced leftover "
 +                                 "ctrl_iface socket '%s'", fname);
 +              } else {
 +                      wpa_printf(MSG_INFO, "ctrl_iface exists and seems to "
 +                                 "be in use - cannot override it");
 +                      wpa_printf(MSG_INFO, "Delete '%s' manually if it is "
 +                                 "not used anymore", fname);
 +                      os_free(fname);
 +                      fname = NULL;
 +                      goto fail;
 +              }
 +      }
 +
 +      if (interface->ctrl_iface_group &&
 +          chown(fname, -1, interface->ctrl_iface_group) < 0) {
 +              wpa_printf(MSG_ERROR, "chown[ctrl_interface]: %s",
 +                         strerror(errno));
 +              goto fail;
 +      }
 +
 +      if (chmod(fname, S_IRWXU | S_IRWXG) < 0) {
 +              wpa_printf(MSG_ERROR, "chmod[ctrl_interface/ifname]: %s",
 +                         strerror(errno));
 +              goto fail;
 +      }
 +      os_free(fname);
 +
 +      interface->global_ctrl_sock = s;
 +      eloop_register_read_sock(s, hostapd_global_ctrl_iface_receive,
 +                               interface, NULL);
 +
 +      return 0;
 +
 +fail:
 +      if (s >= 0)
 +              close(s);
 +      if (fname) {
 +              unlink(fname);
 +              os_free(fname);
 +      }
 +      return -1;
 +}
 +
 +
 +void hostapd_global_ctrl_iface_deinit(struct hapd_interfaces *interfaces)
 +{
 +      char *fname = NULL;
++      struct wpa_ctrl_dst *dst, *prev;
 +
 +      if (interfaces->global_ctrl_sock > -1) {
 +              eloop_unregister_read_sock(interfaces->global_ctrl_sock);
 +              close(interfaces->global_ctrl_sock);
 +              interfaces->global_ctrl_sock = -1;
 +              fname = hostapd_global_ctrl_iface_path(interfaces);
 +              if (fname) {
 +                      unlink(fname);
 +                      os_free(fname);
 +              }
 +
 +              if (interfaces->global_iface_path &&
 +                  rmdir(interfaces->global_iface_path) < 0) {
 +                      if (errno == ENOTEMPTY) {
 +                              wpa_printf(MSG_DEBUG, "Control interface "
 +                                         "directory not empty - leaving it "
 +                                         "behind");
 +                      } else {
 +                              wpa_printf(MSG_ERROR,
 +                                         "rmdir[ctrl_interface=%s]: %s",
 +                                         interfaces->global_iface_path,
 +                                         strerror(errno));
 +                      }
 +              }
-       dst = hapd->ctrl_dst;
-       if (hapd->ctrl_sock < 0 || dst == NULL)
++      }
++
++      os_free(interfaces->global_iface_path);
++      interfaces->global_iface_path = NULL;
++
++      dst = interfaces->global_ctrl_dst;
++      interfaces->global_ctrl_dst = NULL;
++      while (dst) {
++              prev = dst;
++              dst = dst->next;
++              os_free(prev);
 +      }
 +}
 +
 +
 +static void hostapd_ctrl_iface_send(struct hostapd_data *hapd, int level,
++                                  enum wpa_msg_type type,
 +                                  const char *buf, size_t len)
 +{
 +      struct wpa_ctrl_dst *dst, *next;
 +      struct msghdr msg;
 +      int idx;
 +      struct iovec io[2];
 +      char levelstr[10];
++      int s;
 +
-                       if (sendmsg(hapd->ctrl_sock, &msg, 0) < 0) {
++      if (type != WPA_MSG_ONLY_GLOBAL) {
++              s = hapd->ctrl_sock;
++              dst = hapd->ctrl_dst;
++      } else {
++              s = hapd->iface->interfaces->global_ctrl_sock;
++              dst = hapd->iface->interfaces->global_ctrl_dst;
++      }
++
++      if (s < 0 || dst == NULL)
 +              return;
 +
 +      os_snprintf(levelstr, sizeof(levelstr), "<%d>", level);
 +      io[0].iov_base = levelstr;
 +      io[0].iov_len = os_strlen(levelstr);
 +      io[1].iov_base = (char *) buf;
 +      io[1].iov_len = len;
 +      os_memset(&msg, 0, sizeof(msg));
 +      msg.msg_iov = io;
 +      msg.msg_iovlen = 2;
 +
 +      idx = 0;
 +      while (dst) {
 +              next = dst->next;
 +              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 = &dst->addr;
 +                      msg.msg_namelen = dst->addrlen;
-                                       hostapd_ctrl_iface_detach(
-                                               hapd, &dst->addr,
-                                               dst->addrlen);
++                      if (sendmsg(s, &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 > 10 || _errno == ENOENT) {
++                                      if (type != WPA_MSG_ONLY_GLOBAL)
++                                              hostapd_ctrl_iface_detach(
++                                                      hapd, &dst->addr,
++                                                      dst->addrlen);
++                                      else
++                                              hostapd_global_ctrl_iface_detach(
++                                                      hapd->iface->interfaces,
++                                                      &dst->addr,
++                                                      dst->addrlen);
 +                              }
 +                      } else
 +                              dst->errors = 0;
 +              }
 +              idx++;
 +              dst = next;
 +      }
 +}
 +
 +#endif /* CONFIG_NATIVE_WINDOWS */
index 4cde2b563483b8bb0d5fa5721185bad581447bbf,0000000000000000000000000000000000000000..430f7584b16750f46fd865d80d30e41bd0c7a08f
mode 100644,000000..100644
--- /dev/null
@@@ -1,316 -1,0 +1,328 @@@
 +# 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
 +# lines must be commented out complete, if they are not to be included, i.e.,
 +# 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 cass, these lines should use += in order not
 +# to override previous values of the variables.
 +
 +# Driver interface for Host AP driver
 +CONFIG_DRIVER_HOSTAP=y
 +
 +# Driver interface for wired authenticator
 +#CONFIG_DRIVER_WIRED=y
 +
 +# 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
 +#LIBS += -L/usr/local/lib
 +#LIBS_p += -L/usr/local/lib
 +#LIBS_c += -L/usr/local/lib
 +
 +# Driver interface for no driver (e.g., RADIUS server only)
 +#CONFIG_DRIVER_NONE=y
 +
 +# IEEE 802.11F/IAPP
 +CONFIG_IAPP=y
 +
 +# WPA2/IEEE 802.11i RSN pre-authentication
 +CONFIG_RSN_PREAUTH=y
 +
 +# PeerKey handshake for Station to Station Link (IEEE 802.11e DLS)
 +CONFIG_PEERKEY=y
 +
 +# IEEE 802.11w (management frame protection)
 +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
 +
 +# EAP-TLS for the integrated EAP server
 +CONFIG_EAP_TLS=y
 +
 +# EAP-MSCHAPv2 for the integrated EAP server
 +CONFIG_EAP_MSCHAPV2=y
 +
 +# EAP-PEAP for the integrated EAP server
 +CONFIG_EAP_PEAP=y
 +
 +# EAP-GTC for the integrated EAP server
 +CONFIG_EAP_GTC=y
 +
 +# EAP-TTLS for the integrated EAP server
 +CONFIG_EAP_TTLS=y
 +
 +# EAP-SIM for the integrated EAP server
 +#CONFIG_EAP_SIM=y
 +
 +# EAP-AKA for the integrated EAP server
 +#CONFIG_EAP_AKA=y
 +
 +# EAP-AKA' for the integrated EAP server
 +# This requires CONFIG_EAP_AKA to be enabled, too.
 +#CONFIG_EAP_AKA_PRIME=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 for the integrated EAP server
 +#CONFIG_EAP_SAKE=y
 +
 +# 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-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 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
 +
 +# RADIUS authentication server. This provides access to the integrated EAP
 +# server from external hosts using RADIUS.
 +#CONFIG_RADIUS_SERVER=y
 +
 +# Build IPv6 support for RADIUS operations
 +CONFIG_IPV6=y
 +
 +# IEEE Std 802.11r-2008 (Fast BSS Transition)
 +#CONFIG_IEEE80211R=y
 +
 +# Use the hostapd's IEEE 802.11 authentication (ACL), but without
 +# the IEEE 802.11 Management capability (e.g., FreeBSD/net80211)
 +#CONFIG_DRIVER_RADIUS_ACL=y
 +
 +# IEEE 802.11n (High Throughput) support
 +#CONFIG_IEEE80211N=y
 +
 +# Wireless Network Management (IEEE Std 802.11v-2011)
 +# Note: This is experimental and not complete implementation.
 +#CONFIG_WNM=y
 +
 +# IEEE 802.11ac (Very High Throughput) support
 +#CONFIG_IEEE80211AC=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
 +
 +# Add support for writing debug log to a file: -f /tmp/hostapd.log
 +# 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
 +
 +# Remove support for RADIUS
 +#CONFIG_NO_RADIUS=y
 +
 +# Remove support for VLANs
 +#CONFIG_NO_VLAN=y
 +
 +# Enable support for fully dynamic VLANs. This enables hostapd to
 +# automatically create bridge and VLAN interfaces if necessary.
 +#CONFIG_FULL_DYNAMIC_VLAN=y
 +
 +# Use netlink-based kernel API for VLAN operations instead of ioctl()
 +# Note: This requires libnl 3.1 or newer.
 +#CONFIG_VLAN_NETLINK=y
 +
 +# 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
 +
 +# 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
 +
 +# 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
 +
 +# 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
 +
++# 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 TLS implementation
 +# openssl = OpenSSL (default)
 +# gnutls = GnuTLS
 +# internal = Internal TLSv1 implementation (experimental)
 +# none = Empty template
 +#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.
 +#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.
 +#CONFIG_TLSV12=y
 +
 +# If CONFIG_TLS=internal is used, additional library and include paths are
 +# needed for LibTomMath. Alternatively, an integrated, minimal version of
 +# LibTomMath can be used. See beginning of libtommath.c for details on benefits
 +# and drawbacks of this option.
 +#CONFIG_INTERNAL_LIBTOMMATH=y
 +#ifndef CONFIG_INTERNAL_LIBTOMMATH
 +#LTM_PATH=/usr/src/libtommath-0.39
 +#CFLAGS += -I$(LTM_PATH)
 +#LIBS += -L$(LTM_PATH)
 +#LIBS_p += -L$(LTM_PATH)
 +#endif
 +# At the cost of about 4 kB of additional binary size, the internal LibTomMath
 +# can be configured to include faster routines for exptmod, sqr, and div to
 +# speed up DH and RSA calculation considerably
 +#CONFIG_INTERNAL_LIBTOMMATH_FAST=y
 +
 +# 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
 +
++# Enable Fast Session Transfer (FST)
++#CONFIG_FST=y
++
++# Enable CLI commands for FST testing
++#CONFIG_FST_TEST=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.
 +#
 +#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 42d59dba7edea7fca2740bc54259cfdd7b1e76aa,0000000000000000000000000000000000000000..84d0308262e6d5b64fd4c58e388ae8a68cfc8a63
mode 100644,000000..100644
--- /dev/null
@@@ -1,1141 -1,0 +1,1163 @@@
-               "  sqn CHAR(12) NOT NULL"
 +/*
 + * HLR/AuC testing gateway for hostapd EAP-SIM/AKA database/authenticator
 + * 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.
 + *
 + * This is an example implementation of the EAP-SIM/AKA database/authentication
 + * gateway interface to HLR/AuC. It is expected to be replaced with an
 + * implementation of SS7 gateway to GSM/UMTS authentication center (HLR/AuC) or
 + * a local implementation of SIM triplet and AKA authentication data generator.
 + *
 + * hostapd will send SIM/AKA authentication queries over a UNIX domain socket
 + * to and external program, e.g., this hlr_auc_gw. This interface uses simple
 + * text-based format:
 + *
 + * EAP-SIM / GSM triplet query/response:
 + * 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>
 + * AKA-RESP-AUTH <IMSI> <RAND> <AUTN> <IK> <CK> <RES>
 + * AKA-RESP-AUTH <IMSI> FAILURE
 + *
 + * EAP-AKA / UMTS AUTS (re-synchronization):
 + * AKA-AUTS <IMSI> <AUTS> <RAND>
 + *
 + * IMSI and max_chal are sent as an ASCII string,
 + * Kc/SRES/RAND/AUTN/IK/CK/RES/AUTS as hex strings.
 + *
 + * 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.
 + */
 +
 +#include "includes.h"
 +#include <sys/un.h>
 +#ifdef CONFIG_SQLITE
 +#include <sqlite3.h>
 +#endif /* CONFIG_SQLITE */
 +
 +#include "common.h"
 +#include "crypto/milenage.h"
 +#include "crypto/random.h"
 +
 +static const char *default_socket_path = "/tmp/hlr_auc_gw.sock";
 +static const char *socket_path;
 +static int serv_sock = -1;
 +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 {
 +      struct gsm_triplet *next;
 +      char imsi[20];
 +      u8 kc[8];
 +      u8 sres[4];
 +      u8 _rand[16];
 +};
 +
 +static struct gsm_triplet *gsm_db = NULL, *gsm_db_pos = NULL;
 +
 +/* OPc and AMF parameters for Milenage (Example algorithms for AKA). */
 +struct milenage_parameters {
 +      struct milenage_parameters *next;
 +      char imsi[20];
 +      u8 ki[16];
 +      u8 opc[16];
 +      u8 amf[2];
 +      u8 sqn[6];
 +      int set;
++      size_t res_len;
 +};
 +
 +static struct milenage_parameters *milenage_db = NULL;
 +
 +#define EAP_SIM_MAX_CHAL 3
 +
 +#define EAP_AKA_RAND_LEN 16
 +#define EAP_AKA_AUTN_LEN 16
 +#define EAP_AKA_AUTS_LEN 14
++#define EAP_AKA_RES_MIN_LEN 4
 +#define EAP_AKA_RES_MAX_LEN 16
 +#define EAP_AKA_IK_LEN 16
 +#define EAP_AKA_CK_LEN 16
 +
 +
 +#ifdef CONFIG_SQLITE
 +
 +static sqlite3 *sqlite_db = NULL;
 +static struct milenage_parameters db_tmp_milenage;
 +
 +
 +static int db_table_exists(sqlite3 *db, const char *name)
 +{
 +      char cmd[128];
 +      os_snprintf(cmd, sizeof(cmd), "SELECT 1 FROM %s;", name);
 +      return sqlite3_exec(db, cmd, NULL, NULL, NULL) == SQLITE_OK;
 +}
 +
 +
 +static int db_table_create_milenage(sqlite3 *db)
 +{
 +      char *err = NULL;
 +      const char *sql =
 +              "CREATE TABLE milenage("
 +              "  imsi INTEGER PRIMARY KEY NOT NULL,"
 +              "  ki CHAR(32) NOT NULL,"
 +              "  opc CHAR(32) NOT NULL,"
 +              "  amf CHAR(4) NOT NULL,"
-                   "SELECT ki,opc,amf,sqn FROM milenage WHERE imsi=%llu;",
-                   imsi);
++              "  sqn CHAR(12) NOT NULL,"
++              "  res_len INTEGER"
 +              ");";
 +
 +      printf("Adding database table for milenage information\n");
 +      if (sqlite3_exec(db, sql, NULL, NULL, &err) != SQLITE_OK) {
 +              printf("SQLite error: %s\n", err);
 +              sqlite3_free(err);
 +              return -1;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +static sqlite3 * db_open(const char *db_file)
 +{
 +      sqlite3 *db;
 +
 +      if (sqlite3_open(db_file, &db)) {
 +              printf("Failed to open database %s: %s\n",
 +                     db_file, sqlite3_errmsg(db));
 +              sqlite3_close(db);
 +              return NULL;
 +      }
 +
 +      if (!db_table_exists(db, "milenage") &&
 +          db_table_create_milenage(db) < 0) {
 +              sqlite3_close(db);
 +              return NULL;
 +      }
 +
 +      return db;
 +}
 +
 +
 +static int get_milenage_cb(void *ctx, int argc, char *argv[], char *col[])
 +{
 +      struct milenage_parameters *m = ctx;
 +      int i;
 +
 +      m->set = 1;
 +
 +      for (i = 0; i < argc; i++) {
 +              if (os_strcmp(col[i], "ki") == 0 && argv[i] &&
 +                  hexstr2bin(argv[i], m->ki, sizeof(m->ki))) {
 +                      printf("Invalid ki value in database\n");
 +                      return -1;
 +              }
 +
 +              if (os_strcmp(col[i], "opc") == 0 && argv[i] &&
 +                  hexstr2bin(argv[i], m->opc, sizeof(m->opc))) {
 +                      printf("Invalid opcvalue in database\n");
 +                      return -1;
 +              }
 +
 +              if (os_strcmp(col[i], "amf") == 0 && argv[i] &&
 +                  hexstr2bin(argv[i], m->amf, sizeof(m->amf))) {
 +                      printf("Invalid amf value in database\n");
 +                      return -1;
 +              }
 +
 +              if (os_strcmp(col[i], "sqn") == 0 && argv[i] &&
 +                  hexstr2bin(argv[i], m->sqn, sizeof(m->sqn))) {
 +                      printf("Invalid sqn value in database\n");
 +                      return -1;
 +              }
++
++              if (os_strcmp(col[i], "res_len") == 0 && argv[i]) {
++                      m->res_len = atoi(argv[i]);
++              }
 +      }
 +
 +      return 0;
 +}
 +
 +
 +static struct milenage_parameters * db_get_milenage(const char *imsi_txt)
 +{
 +      char cmd[128];
 +      unsigned long long imsi;
 +
 +      os_memset(&db_tmp_milenage, 0, sizeof(db_tmp_milenage));
 +      imsi = atoll(imsi_txt);
 +      os_snprintf(db_tmp_milenage.imsi, sizeof(db_tmp_milenage.imsi),
 +                  "%llu", imsi);
 +      os_snprintf(cmd, sizeof(cmd),
-               /* Parse IMSI Ki OPc AMF SQN */
++                  "SELECT * FROM milenage WHERE imsi=%llu;", imsi);
 +      if (sqlite3_exec(sqlite_db, cmd, get_milenage_cb, &db_tmp_milenage,
 +                       NULL) != SQLITE_OK)
 +              return NULL;
 +
 +      if (!db_tmp_milenage.set)
 +              return NULL;
 +      return &db_tmp_milenage;
 +}
 +
 +
 +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';
 +      os_snprintf(cmd, sizeof(cmd),
 +                  "UPDATE milenage SET sqn='%s' WHERE imsi=%s;",
 +                  val, m->imsi);
 +      if (sqlite3_exec(sqlite_db, cmd, NULL, NULL, NULL) != SQLITE_OK) {
 +              printf("Failed to update SQN in database for IMSI %s\n",
 +                     m->imsi);
 +              return -1;
 +      }
 +      return 0;
 +}
 +
 +#endif /* CONFIG_SQLITE */
 +
 +
 +static int open_socket(const char *path)
 +{
 +      struct sockaddr_un addr;
 +      int s;
 +
 +      s = socket(PF_UNIX, SOCK_DGRAM, 0);
 +      if (s < 0) {
 +              perror("socket(PF_UNIX)");
 +              return -1;
 +      }
 +
 +      memset(&addr, 0, sizeof(addr));
 +      addr.sun_family = AF_UNIX;
 +      os_strlcpy(addr.sun_path, path, sizeof(addr.sun_path));
 +      if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
 +              perror("hlr-auc-gw: bind(PF_UNIX)");
 +              close(s);
 +              return -1;
 +      }
 +
 +      return s;
 +}
 +
 +
 +static int read_gsm_triplets(const char *fname)
 +{
 +      FILE *f;
 +      char buf[200], *pos, *pos2;
 +      struct gsm_triplet *g = NULL;
 +      int line, ret = 0;
 +
 +      if (fname == NULL)
 +              return -1;
 +
 +      f = fopen(fname, "r");
 +      if (f == NULL) {
 +              printf("Could not open GSM tripler data file '%s'\n", fname);
 +              return -1;
 +      }
 +
 +      line = 0;
 +      while (fgets(buf, sizeof(buf), f)) {
 +              line++;
 +
 +              /* Parse IMSI:Kc:SRES:RAND */
 +              buf[sizeof(buf) - 1] = '\0';
 +              if (buf[0] == '#')
 +                      continue;
 +              pos = buf;
 +              while (*pos != '\0' && *pos != '\n')
 +                      pos++;
 +              if (*pos == '\n')
 +                      *pos = '\0';
 +              pos = buf;
 +              if (*pos == '\0')
 +                      continue;
 +
 +              g = os_zalloc(sizeof(*g));
 +              if (g == NULL) {
 +                      ret = -1;
 +                      break;
 +              }
 +
 +              /* IMSI */
 +              pos2 = strchr(pos, ':');
 +              if (pos2 == NULL) {
 +                      printf("%s:%d - Invalid IMSI (%s)\n",
 +                             fname, line, pos);
 +                      ret = -1;
 +                      break;
 +              }
 +              *pos2 = '\0';
 +              if (strlen(pos) >= sizeof(g->imsi)) {
 +                      printf("%s:%d - Too long IMSI (%s)\n",
 +                             fname, line, pos);
 +                      ret = -1;
 +                      break;
 +              }
 +              os_strlcpy(g->imsi, pos, sizeof(g->imsi));
 +              pos = pos2 + 1;
 +
 +              /* Kc */
 +              pos2 = strchr(pos, ':');
 +              if (pos2 == NULL) {
 +                      printf("%s:%d - Invalid Kc (%s)\n", fname, line, pos);
 +                      ret = -1;
 +                      break;
 +              }
 +              *pos2 = '\0';
 +              if (strlen(pos) != 16 || hexstr2bin(pos, g->kc, 8)) {
 +                      printf("%s:%d - Invalid Kc (%s)\n", fname, line, pos);
 +                      ret = -1;
 +                      break;
 +              }
 +              pos = pos2 + 1;
 +
 +              /* SRES */
 +              pos2 = strchr(pos, ':');
 +              if (pos2 == NULL) {
 +                      printf("%s:%d - Invalid SRES (%s)\n", fname, line,
 +                             pos);
 +                      ret = -1;
 +                      break;
 +              }
 +              *pos2 = '\0';
 +              if (strlen(pos) != 8 || hexstr2bin(pos, g->sres, 4)) {
 +                      printf("%s:%d - Invalid SRES (%s)\n", fname, line,
 +                             pos);
 +                      ret = -1;
 +                      break;
 +              }
 +              pos = pos2 + 1;
 +
 +              /* RAND */
 +              pos2 = strchr(pos, ':');
 +              if (pos2)
 +                      *pos2 = '\0';
 +              if (strlen(pos) != 32 || hexstr2bin(pos, g->_rand, 16)) {
 +                      printf("%s:%d - Invalid RAND (%s)\n", fname, line,
 +                             pos);
 +                      ret = -1;
 +                      break;
 +              }
 +              pos = pos2 + 1;
 +
 +              g->next = gsm_db;
 +              gsm_db = g;
 +              g = NULL;
 +      }
 +      os_free(g);
 +
 +      fclose(f);
 +
 +      return ret;
 +}
 +
 +
 +static struct gsm_triplet * get_gsm_triplet(const char *imsi)
 +{
 +      struct gsm_triplet *g = gsm_db_pos;
 +
 +      while (g) {
 +              if (strcmp(g->imsi, imsi) == 0) {
 +                      gsm_db_pos = g->next;
 +                      return g;
 +              }
 +              g = g->next;
 +      }
 +
 +      g = gsm_db;
 +      while (g && g != gsm_db_pos) {
 +              if (strcmp(g->imsi, imsi) == 0) {
 +                      gsm_db_pos = g->next;
 +                      return g;
 +              }
 +              g = g->next;
 +      }
 +
 +      return NULL;
 +}
 +
 +
 +static int read_milenage(const char *fname)
 +{
 +      FILE *f;
 +      char buf[200], *pos, *pos2;
 +      struct milenage_parameters *m = NULL;
 +      int line, ret = 0;
 +
 +      if (fname == NULL)
 +              return -1;
 +
 +      f = fopen(fname, "r");
 +      if (f == NULL) {
 +              printf("Could not open Milenage data file '%s'\n", fname);
 +              return -1;
 +      }
 +
 +      line = 0;
 +      while (fgets(buf, sizeof(buf), f)) {
 +              line++;
 +
-               pos = pos2 + 1;
++              /* Parse IMSI Ki OPc AMF SQN [RES_len] */
 +              buf[sizeof(buf) - 1] = '\0';
 +              if (buf[0] == '#')
 +                      continue;
 +              pos = buf;
 +              while (*pos != '\0' && *pos != '\n')
 +                      pos++;
 +              if (*pos == '\n')
 +                      *pos = '\0';
 +              pos = buf;
 +              if (*pos == '\0')
 +                      continue;
 +
 +              m = os_zalloc(sizeof(*m));
 +              if (m == NULL) {
 +                      ret = -1;
 +                      break;
 +              }
 +
 +              /* IMSI */
 +              pos2 = strchr(pos, ' ');
 +              if (pos2 == NULL) {
 +                      printf("%s:%d - Invalid IMSI (%s)\n",
 +                             fname, line, pos);
 +                      ret = -1;
 +                      break;
 +              }
 +              *pos2 = '\0';
 +              if (strlen(pos) >= sizeof(m->imsi)) {
 +                      printf("%s:%d - Too long IMSI (%s)\n",
 +                             fname, line, pos);
 +                      ret = -1;
 +                      break;
 +              }
 +              os_strlcpy(m->imsi, pos, sizeof(m->imsi));
 +              pos = pos2 + 1;
 +
 +              /* Ki */
 +              pos2 = strchr(pos, ' ');
 +              if (pos2 == NULL) {
 +                      printf("%s:%d - Invalid Ki (%s)\n", fname, line, pos);
 +                      ret = -1;
 +                      break;
 +              }
 +              *pos2 = '\0';
 +              if (strlen(pos) != 32 || hexstr2bin(pos, m->ki, 16)) {
 +                      printf("%s:%d - Invalid Ki (%s)\n", fname, line, pos);
 +                      ret = -1;
 +                      break;
 +              }
 +              pos = pos2 + 1;
 +
 +              /* OPc */
 +              pos2 = strchr(pos, ' ');
 +              if (pos2 == NULL) {
 +                      printf("%s:%d - Invalid OPc (%s)\n", fname, line, pos);
 +                      ret = -1;
 +                      break;
 +              }
 +              *pos2 = '\0';
 +              if (strlen(pos) != 32 || hexstr2bin(pos, m->opc, 16)) {
 +                      printf("%s:%d - Invalid OPc (%s)\n", fname, line, pos);
 +                      ret = -1;
 +                      break;
 +              }
 +              pos = pos2 + 1;
 +
 +              /* AMF */
 +              pos2 = strchr(pos, ' ');
 +              if (pos2 == NULL) {
 +                      printf("%s:%d - Invalid AMF (%s)\n", fname, line, pos);
 +                      ret = -1;
 +                      break;
 +              }
 +              *pos2 = '\0';
 +              if (strlen(pos) != 4 || hexstr2bin(pos, m->amf, 2)) {
 +                      printf("%s:%d - Invalid AMF (%s)\n", fname, line, pos);
 +                      ret = -1;
 +                      break;
 +              }
 +              pos = pos2 + 1;
 +
 +              /* SQN */
 +              pos2 = strchr(pos, ' ');
 +              if (pos2)
 +                      *pos2 = '\0';
 +              if (strlen(pos) != 12 || hexstr2bin(pos, m->sqn, 6)) {
 +                      printf("%s:%d - Invalid SEQ (%s)\n", fname, line, pos);
 +                      ret = -1;
 +                      break;
 +              }
-       char buf[500], *pos;
++
++              if (pos2) {
++                      pos = pos2 + 1;
++                      m->res_len = atoi(pos);
++                      if (m->res_len &&
++                          (m->res_len < EAP_AKA_RES_MIN_LEN ||
++                           m->res_len > EAP_AKA_RES_MAX_LEN)) {
++                              printf("%s:%d - Invalid RES_len (%s)\n",
++                                     fname, line, pos);
++                              ret = -1;
++                              break;
++                      }
++              }
 +
 +              m->next = milenage_db;
 +              milenage_db = m;
 +              m = NULL;
 +      }
 +      os_free(m);
 +
 +      fclose(f);
 +
 +      return ret;
 +}
 +
 +
 +static void update_milenage_file(const char *fname)
 +{
 +      FILE *f, *f2;
-       snprintf(buf, sizeof(buf), "%s.new", fname);
-       f2 = fopen(buf, "w");
++      char name[500], buf[500], *pos;
 +      char *end = buf + sizeof(buf);
 +      struct milenage_parameters *m;
 +      size_t imsi_len;
 +
 +      f = fopen(fname, "r");
 +      if (f == NULL) {
 +              printf("Could not open Milenage data file '%s'\n", fname);
 +              return;
 +      }
 +
-               printf("Could not write Milenage data file '%s'\n", buf);
++      snprintf(name, sizeof(name), "%s.new", fname);
++      f2 = fopen(name, "w");
 +      if (f2 == NULL) {
-       snprintf(buf, sizeof(buf), "%s.bak", fname);
-       if (rename(fname, buf) < 0) {
++              printf("Could not write Milenage data file '%s'\n", name);
 +              fclose(f);
 +              return;
 +      }
 +
 +      while (fgets(buf, sizeof(buf), f)) {
 +              /* IMSI Ki OPc AMF SQN */
 +              buf[sizeof(buf) - 1] = '\0';
 +
 +              pos = strchr(buf, ' ');
 +              if (buf[0] == '#' || pos == NULL || pos - buf >= 20)
 +                      goto no_update;
 +
 +              imsi_len = pos - buf;
 +
 +              for (m = milenage_db; m; m = m->next) {
 +                      if (strncmp(buf, m->imsi, imsi_len) == 0 &&
 +                          m->imsi[imsi_len] == '\0')
 +                              break;
 +              }
 +
 +              if (!m)
 +                      goto no_update;
 +
 +              pos = buf;
 +              pos += snprintf(pos, end - pos, "%s ", m->imsi);
 +              pos += wpa_snprintf_hex(pos, end - pos, m->ki, 16);
 +              *pos++ = ' ';
 +              pos += wpa_snprintf_hex(pos, end - pos, m->opc, 16);
 +              *pos++ = ' ';
 +              pos += wpa_snprintf_hex(pos, end - pos, m->amf, 2);
 +              *pos++ = ' ';
 +              pos += wpa_snprintf_hex(pos, end - pos, m->sqn, 6);
 +              *pos++ = '\n';
 +
 +      no_update:
 +              fprintf(f2, "%s", buf);
 +      }
 +
 +      fclose(f2);
 +      fclose(f);
 +
-       snprintf(buf, sizeof(buf), "%s.new", fname);
-       if (rename(buf, fname) < 0) {
++      snprintf(name, sizeof(name), "%s.bak", fname);
++      if (rename(fname, name) < 0) {
 +              perror("rename");
 +              return;
 +      }
 +
++      snprintf(name, sizeof(name), "%s.new", fname);
++      if (rename(name, fname) < 0) {
 +              perror("rename");
 +              return;
 +      }
 +
 +}
 +
 +
 +static struct milenage_parameters * get_milenage(const char *imsi)
 +{
 +      struct milenage_parameters *m = milenage_db;
 +
 +      while (m) {
 +              if (strcmp(m->imsi, imsi) == 0)
 +                      break;
 +              m = m->next;
 +      }
 +
 +#ifdef CONFIG_SQLITE
 +      if (!m)
 +              m = db_get_milenage(imsi);
 +#endif /* CONFIG_SQLITE */
 +
 +      return m;
 +}
 +
 +
 +static int sim_req_auth(char *imsi, char *resp, size_t resp_len)
 +{
 +      int count, max_chal, ret;
 +      char *pos;
 +      char *rpos, *rend;
 +      struct milenage_parameters *m;
 +      struct gsm_triplet *g;
 +
 +      resp[0] = '\0';
 +
 +      pos = strchr(imsi, ' ');
 +      if (pos) {
 +              *pos++ = '\0';
 +              max_chal = atoi(pos);
 +              if (max_chal < 1 || max_chal > EAP_SIM_MAX_CHAL)
 +                      max_chal = EAP_SIM_MAX_CHAL;
 +      } else
 +              max_chal = EAP_SIM_MAX_CHAL;
 +
 +      rend = resp + resp_len;
 +      rpos = resp;
 +      ret = snprintf(rpos, rend - rpos, "SIM-RESP-AUTH %s", imsi);
 +      if (ret < 0 || ret >= rend - rpos)
 +              return -1;
 +      rpos += ret;
 +
 +      m = get_milenage(imsi);
 +      if (m) {
 +              u8 _rand[16], sres[4], kc[8];
 +              for (count = 0; count < max_chal; count++) {
 +                      if (random_get_bytes(_rand, 16) < 0)
 +                              return -1;
 +                      gsm_milenage(m->opc, m->ki, _rand, sres, kc);
 +                      *rpos++ = ' ';
 +                      rpos += wpa_snprintf_hex(rpos, rend - rpos, kc, 8);
 +                      *rpos++ = ':';
 +                      rpos += wpa_snprintf_hex(rpos, rend - rpos, sres, 4);
 +                      *rpos++ = ':';
 +                      rpos += wpa_snprintf_hex(rpos, rend - rpos, _rand, 16);
 +              }
 +              *rpos = '\0';
 +              return 0;
 +      }
 +
 +      count = 0;
 +      while (count < max_chal && (g = get_gsm_triplet(imsi))) {
 +              if (strcmp(g->imsi, imsi) != 0)
 +                      continue;
 +
 +              if (rpos < rend)
 +                      *rpos++ = ' ';
 +              rpos += wpa_snprintf_hex(rpos, rend - rpos, g->kc, 8);
 +              if (rpos < rend)
 +                      *rpos++ = ':';
 +              rpos += wpa_snprintf_hex(rpos, rend - rpos, g->sres, 4);
 +              if (rpos < rend)
 +                      *rpos++ = ':';
 +              rpos += wpa_snprintf_hex(rpos, rend - rpos, g->_rand, 16);
 +              count++;
 +      }
 +
 +      if (count == 0) {
 +              printf("No GSM triplets found for %s\n", imsi);
 +              ret = snprintf(rpos, rend - rpos, " FAILURE");
 +              if (ret < 0 || ret >= rend - rpos)
 +                      return -1;
 +              rpos += ret;
 +      }
 +
 +      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;
 +}
 +
 +
 +static void inc_sqn(u8 *sqn)
 +{
 +      u64 val, seq, ind;
 +
 +      /*
 +       * SQN = SEQ | IND = SEQ1 | SEQ2 | IND
 +       *
 +       * The mechanism used here is not time-based, so SEQ2 is void and
 +       * SQN = SEQ1 | IND. The length of IND is ind_len bits and the length
 +       * of SEQ1 is 48 - ind_len bits.
 +       */
 +
 +      /* Increment both SEQ and IND by one */
 +      val = ((u64) WPA_GET_BE32(sqn) << 16) | ((u64) WPA_GET_BE16(sqn + 4));
 +      seq = (val >> ind_len) + 1;
 +      ind = (val + 1) & ((1 << ind_len) - 1);
 +      val = (seq << ind_len) | ind;
 +      WPA_PUT_BE32(sqn, val >> 16);
 +      WPA_PUT_BE16(sqn + 4, val & 0xffff);
 +}
 +
 +
 +static int aka_req_auth(char *imsi, char *resp, size_t resp_len)
 +{
 +      /* AKA-RESP-AUTH <IMSI> <RAND> <AUTN> <IK> <CK> <RES> */
 +      char *pos, *end;
 +      u8 _rand[EAP_AKA_RAND_LEN];
 +      u8 autn[EAP_AKA_AUTN_LEN];
 +      u8 ik[EAP_AKA_IK_LEN];
 +      u8 ck[EAP_AKA_CK_LEN];
 +      u8 res[EAP_AKA_RES_MAX_LEN];
 +      size_t res_len;
 +      int ret;
 +      struct milenage_parameters *m;
 +      int failed = 0;
 +
 +      m = get_milenage(imsi);
 +      if (m) {
 +              if (random_get_bytes(_rand, EAP_AKA_RAND_LEN) < 0)
 +                      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;
 +              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);
++              if (m->res_len >= EAP_AKA_RES_MIN_LEN &&
++                  m->res_len <= EAP_AKA_RES_MAX_LEN &&
++                  m->res_len < res_len)
++                      res_len = m->res_len;
 +      } else {
 +              printf("Unknown IMSI: %s\n", imsi);
 +#ifdef AKA_USE_FIXED_TEST_VALUES
 +              printf("Using fixed test values for AKA\n");
 +              memset(_rand, '0', EAP_AKA_RAND_LEN);
 +              memset(autn, '1', EAP_AKA_AUTN_LEN);
 +              memset(ik, '3', EAP_AKA_IK_LEN);
 +              memset(ck, '4', EAP_AKA_CK_LEN);
 +              memset(res, '2', EAP_AKA_RES_MAX_LEN);
 +              res_len = EAP_AKA_RES_MAX_LEN;
 +#else /* AKA_USE_FIXED_TEST_VALUES */
 +              failed = 1;
 +#endif /* AKA_USE_FIXED_TEST_VALUES */
 +      }
 +
 +      pos = resp;
 +      end = resp + resp_len;
 +      ret = snprintf(pos, end - pos, "AKA-RESP-AUTH %s ", imsi);
 +      if (ret < 0 || ret >= end - pos)
 +              return -1;
 +      pos += ret;
 +      if (failed) {
 +              ret = snprintf(pos, end - pos, "FAILURE");
 +              if (ret < 0 || ret >= end - pos)
 +                      return -1;
 +              pos += ret;
 +              return 0;
 +      }
 +      pos += wpa_snprintf_hex(pos, end - pos, _rand, EAP_AKA_RAND_LEN);
 +      *pos++ = ' ';
 +      pos += wpa_snprintf_hex(pos, end - pos, autn, EAP_AKA_AUTN_LEN);
 +      *pos++ = ' ';
 +      pos += wpa_snprintf_hex(pos, end - pos, ik, EAP_AKA_IK_LEN);
 +      *pos++ = ' ';
 +      pos += wpa_snprintf_hex(pos, end - pos, ck, EAP_AKA_CK_LEN);
 +      *pos++ = ' ';
 +      pos += wpa_snprintf_hex(pos, end - pos, res, res_len);
 +
 +      return 0;
 +}
 +
 +
 +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 -1;
 +      *auts++ = '\0';
 +
 +      __rand = strchr(auts, ' ');
 +      if (__rand == NULL)
 +              return -1;
 +      *__rand++ = '\0';
 +
 +      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 -1;
 +      }
 +
 +      m = get_milenage(imsi);
 +      if (m == NULL) {
 +              printf("Unknown IMSI: %s\n", imsi);
 +              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);
 +              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], resp[1000];
 +      struct sockaddr_un from;
 +      socklen_t fromlen;
 +      ssize_t res;
 +
 +      fromlen = sizeof(from);
 +      res = recvfrom(s, buf, sizeof(buf), 0, (struct sockaddr *) &from,
 +                     &fromlen);
 +      if (res < 0) {
 +              perror("recvfrom");
 +              return -1;
 +      }
 +
 +      if (res == 0)
 +              return 0;
 +
 +      if ((size_t) res >= sizeof(buf))
 +              res = sizeof(buf) - 1;
 +      buf[res] = '\0';
 +
 +      printf("Received: %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;
 +}
 +
 +
 +static void cleanup(void)
 +{
 +      struct gsm_triplet *g, *gprev;
 +      struct milenage_parameters *m, *prev;
 +
 +      if (update_milenage && milenage_file && sqn_changes)
 +              update_milenage_file(milenage_file);
 +
 +      g = gsm_db;
 +      while (g) {
 +              gprev = g;
 +              g = g->next;
 +              os_free(gprev);
 +      }
 +
 +      m = milenage_db;
 +      while (m) {
 +              prev = m;
 +              m = m->next;
 +              os_free(prev);
 +      }
 +
 +      if (serv_sock >= 0)
 +              close(serv_sock);
 +      if (socket_path)
 +              unlink(socket_path);
 +
 +#ifdef CONFIG_SQLITE
 +      if (sqlite_db) {
 +              sqlite3_close(sqlite_db);
 +              sqlite_db = NULL;
 +      }
 +#endif /* CONFIG_SQLITE */
 +}
 +
 +
 +static void handle_term(int sig)
 +{
 +      printf("Signal %d - terminate\n", sig);
 +      exit(0);
 +}
 +
 +
 +static void usage(void)
 +{
 +      printf("HLR/AuC testing gateway for hostapd EAP-SIM/AKA "
 +             "database/authenticator\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>] [command]\n"
 +             "\n"
 +             "options:\n"
 +             "  -h = show this usage help\n"
 +             "  -u = update SQN in Milenage file on exit\n"
 +             "  -s<socket path> = path for UNIX domain socket\n"
 +             "                    (default: %s)\n"
 +             "  -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"
 +             "\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);
 +}
 +
 +
 +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;
 +
 +      socket_path = default_socket_path;
 +
 +      for (;;) {
 +              c = getopt(argc, argv, "D:g:hi:m:s:u");
 +              if (c < 0)
 +                      break;
 +              switch (c) {
 +              case 'D':
 +#ifdef CONFIG_SQLITE
 +                      sqlite_db_file = optarg;
 +                      break;
 +#else /* CONFIG_SQLITE */
 +                      printf("No SQLite support included in the build\n");
 +                      return -1;
 +#endif /* CONFIG_SQLITE */
 +              case 'g':
 +                      gsm_triplet_file = optarg;
 +                      break;
 +              case 'h':
 +                      usage();
 +                      return 0;
 +              case 'i':
 +                      ind_len = atoi(optarg);
 +                      if (ind_len < 0 || ind_len > 32) {
 +                              printf("Invalid IND length\n");
 +                              return -1;
 +                      }
 +                      break;
 +              case 'm':
 +                      milenage_file = optarg;
 +                      break;
 +              case 's':
 +                      socket_path = optarg;
 +                      break;
 +              case 'u':
 +                      update_milenage = 1;
 +                      break;
 +              default:
 +                      usage();
 +                      return -1;
 +              }
 +      }
 +
 +      if (!gsm_triplet_file && !milenage_file && !sqlite_db_file) {
 +              usage();
 +              return -1;
 +      }
 +
 +#ifdef CONFIG_SQLITE
 +      if (sqlite_db_file && (sqlite_db = db_open(sqlite_db_file)) == NULL)
 +              return -1;
 +#endif /* CONFIG_SQLITE */
 +
 +      if (gsm_triplet_file && read_gsm_triplets(gsm_triplet_file) < 0)
 +              return -1;
 +
 +      if (milenage_file && read_milenage(milenage_file) < 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);
 +
 +              atexit(cleanup);
 +              signal(SIGTERM, handle_term);
 +              signal(SIGINT, handle_term);
 +
 +              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) {
 +              sqlite3_close(sqlite_db);
 +              sqlite_db = NULL;
 +      }
 +#endif /* CONFIG_SQLITE */
 +
 +      os_program_deinit();
 +
 +      return ret;
 +}
index ecd06d72096d11712a68b3f53eaf6b442af043d2,0000000000000000000000000000000000000000..c156a29aeda0ded03c9471159b179bfcab8639a6
mode 100644,000000..100644
--- /dev/null
@@@ -1,13 -1,0 +1,15 @@@
- # IMSI Ki OPc AMF SQN
 +# 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 [RES_len]
 +232010000000000 90dca4eda45b53cf0f12d7c9c3bc6a89 cb9cccc4b9258e6dca4760379fb82581 61df 000000000000
++# Example using truncated 32-bit RES instead of 64-bit default
++232010000000001 90dca4eda45b53cf0f12d7c9c3bc6a89 cb9cccc4b9258e6dca4760379fb82581 61df 000000000000 4
 +
 +# 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
index 9e81e9e986830a19a18a189cdd9c4fb35e091d5a,0000000000000000000000000000000000000000..a0071f7d82c41eddd3940be6b30c7b1eabd79d76
mode 100644,000000..100644
--- /dev/null
@@@ -1,1800 -1,0 +1,1885 @@@
- # specify band)
 +##### hostapd configuration file ##############################################
 +# Empty lines and lines starting with # are ignored
 +
 +# AP netdevice name (without 'ap' postfix, i.e., wlan0 uses wlan0ap for
 +# management frames with the Host AP driver); wlan0 with many nl80211 drivers
 +interface=wlan0
 +
 +# 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
 +# figure out the bridge interface (assuming sysfs is enabled and mounted to
 +# /sys) and this parameter may not be needed.
 +#
 +# For nl80211, this parameter can be used to request the AP interface to be
 +# added to the bridge automatically (brctl may refuse to do this before hostapd
 +# has been started to change the interface mode). If needed, the bridge
 +# interface is also created.
 +#bridge=br0
 +
 +# 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
 +# background).
 +#
 +# Module bitfield (ORed bitfield of modules that will be logged; -1 = all
 +# modules):
 +# bit 0 (1) = IEEE 802.11
 +# bit 1 (2) = IEEE 802.1X
 +# bit 2 (4) = RADIUS
 +# bit 3 (8) = WPA
 +# bit 4 (16) = driver interface
 +# bit 5 (32) = IAPP
 +# bit 6 (64) = MLME
 +#
 +# Levels (minimum value for logged events):
 +#  0 = verbose debugging
 +#  1 = debugging
 +#  2 = informational messages
 +#  3 = notification
 +#  4 = warning
 +#
 +logger_syslog=-1
 +logger_syslog_level=2
 +logger_stdout=-1
 +logger_stdout_level=2
 +
 +# 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
 +# configuration. The socket file will be named based on the interface name, so
 +# multiple hostapd processes/interfaces can be run at the same time if more
 +# than one interface is used.
 +# /var/run/hostapd is the recommended directory for sockets and by default,
 +# hostapd_cli will use it when trying to connect with hostapd.
 +ctrl_interface=/var/run/hostapd
 +
 +# Access control for the control interface can be configured by setting the
 +# directory to allow only members of a group to use sockets. This way, it is
 +# possible to run hostapd as root (since it needs to change network
 +# configuration and open raw sockets) and still allow GUI/CLI components to be
 +# run as non-root users. However, since the control interface can be used to
 +# change the network configuration, this access needs to be protected in many
 +# cases. By default, hostapd is configured to use gid 0 (root). If you
 +# want to allow non-root users to use the contron interface, add a new group
 +# and change this value to match with that group. Add users that should have
 +# control interface access to this group.
 +#
 +# This variable can be a group name or gid.
 +#ctrl_interface_group=wheel
 +ctrl_interface_group=0
 +
 +
 +##### IEEE 802.11 related configuration #######################################
 +
 +# SSID to be used in IEEE 802.11 management frames
 +ssid=test
 +# Alternative formats for configuring SSID
 +# (double quoted string, hexdump, printf-escaped string)
 +#ssid2="test"
 +#ssid2=74657374
 +#ssid2=P"hello\nthere"
 +
 +# UTF-8 SSID: Whether the SSID is to be interpreted using UTF-8 encoding
 +#utf8_ssid=1
 +
 +# Country code (ISO/IEC 3166-1). Used to set regulatory domain.
 +# Set as needed to indicate country in which device is operating.
 +# This can limit available channels and transmit power.
 +#country_code=US
 +
 +# Enable IEEE 802.11d. This advertises the country_code and the set of allowed
 +# channels and transmit power levels based on the regulatory limits. The
 +# country_code setting must be configured with the correct country for
 +# IEEE 802.11d functions.
 +# (default: 0 = disabled)
 +#ieee80211d=1
 +
 +# Enable IEEE 802.11h. This enables radar detection and DFS support if
 +# available. DFS support is required on outdoor 5 GHz channels in most countries
 +# of the world. This can be used only with ieee80211d=1.
 +# (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
- # Default: not set (allow any enabled channel to be selected)
++# specify band). When using ACS (see channel parameter), a special value "any"
++# can be used to indicate that any support band can be used. This special case
++# is currently supported only with drivers with which offloaded ACS is used.
 +# Default: IEEE 802.11b
 +hw_mode=g
 +
 +# Channel number (IEEE 802.11)
 +# (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.
- #   cwmin: cwMin (1, 3, 7, 15, 31, 63, 127, 255, 511, 1023)
- #   cwmax: cwMax (1, 3, 7, 15, 31, 63, 127, 255, 511, 1023); cwMax >= cwMin
++# Channel list can be provided as range using hyphen ('-') or individual
++# channels can be specified by space (' ') seperated values
++# Default: all channels allowed in selected hw_mode
 +#chanlist=100 104 108 112 116
++#chanlist=1 6 11-13
 +
 +# Beacon interval in kus (1.024 ms) (default: 100; range 15..65535)
 +beacon_int=100
 +
 +# DTIM (delivery traffic information message) period (range 1..255):
 +# number of beacons between DTIMs (1 = every beacon includes DTIM element)
 +# (default: 2)
 +dtim_period=2
 +
 +# Maximum number of stations allowed in station table. New stations will be
 +# rejected after the station table is full. IEEE 802.11 has a limit of 2007
 +# different association IDs, so this number should not be larger than that.
 +# (default: 2007)
 +max_num_sta=255
 +
 +# RTS/CTS threshold; 2347 = disabled (default); range 0..2347
 +# If this field is not included in hostapd.conf, hostapd will not control
 +# RTS threshold and 'iwconfig wlan# rts <val>' can be used to set it.
 +rts_threshold=2347
 +
 +# Fragmentation threshold; 2346 = disabled (default); range 256..2346
 +# If this field is not included in hostapd.conf, hostapd will not control
 +# fragmentation threshold and 'iwconfig wlan# frag <val>' can be used to set
 +# it.
 +fragm_threshold=2346
 +
 +# Rate configuration
 +# Default is to enable all rates supported by the hardware. This configuration
 +# item allows this list be filtered so that only the listed rates will be left
 +# in the list. If the list is empty, all rates are used. This list can have
 +# entries that are not in the list of rates the hardware supports (such entries
 +# are ignored). The entries in this list are in 100 kbps, i.e., 11 Mbps = 110.
 +# If this item is present, at least one rate have to be matching with the rates
 +# hardware supports.
 +# default: use the most common supported rate setting for the selected
 +# hw_mode (i.e., this line can be removed from configuration file in most
 +# cases)
 +#supported_rates=10 20 55 110 60 90 120 180 240 360 480 540
 +
 +# Basic rate set configuration
 +# List of rates (in 100 kbps) that are included in the basic rate set.
 +# If this item is not included, usually reasonable default set is used.
 +#basic_rates=10 20
 +#basic_rates=10 20 55 110
 +#basic_rates=60 120 240
 +
 +# Short Preamble
 +# This parameter can be used to enable optional use of short preamble for
 +# frames sent at 2 Mbps, 5.5 Mbps, and 11 Mbps to improve network performance.
 +# This applies only to IEEE 802.11b-compatible networks and this should only be
 +# enabled if the local hardware supports use of short preamble. If any of the
 +# associated STAs do not support short preamble, use of short preamble will be
 +# disabled (and enabled when such STAs disassociate) dynamically.
 +# 0 = do not allow use of short preamble (default)
 +# 1 = allow use of short preamble
 +#preamble=1
 +
 +# Station MAC address -based authentication
 +# Please note that this kind of access control requires a driver that uses
 +# hostapd to take care of management frame processing and as such, this can be
 +# used with driver=hostap or driver=nl80211, but not with driver=atheros.
 +# 0 = accept unless in deny list
 +# 1 = deny unless in accept list
 +# 2 = use external RADIUS server (accept/deny lists are searched first)
 +macaddr_acl=0
 +
 +# Accept/deny lists are read from separate files (containing list of
 +# MAC addresses, one per line). Use absolute path name to make sure that the
 +# files can be read on SIGHUP configuration reloads.
 +#accept_mac_file=/etc/hostapd.accept
 +#deny_mac_file=/etc/hostapd.deny
 +
 +# IEEE 802.11 specifies two authentication algorithms. hostapd can be
 +# configured to allow both of these or only one. Open system authentication
 +# should be used with IEEE 802.1X.
 +# Bit fields of allowed authentication algorithms:
 +# bit 0 = Open System Authentication
 +# bit 1 = Shared Key Authentication (requires WEP)
 +auth_algs=3
 +
 +# Send empty SSID in beacons and ignore probe request frames that do not
 +# specify full SSID, i.e., require stations to know SSID.
 +# default: disabled (0)
 +# 1 = send empty (length=0) SSID in beacon and ignore probe request for
 +#     broadcast SSID
 +# 2 = clear SSID (ASCII 0), but keep the original length (this may be required
 +#     with some clients that do not support empty SSID) and ignore probe
 +#     requests for broadcast SSID
 +ignore_broadcast_ssid=0
 +
 +# Additional vendor specfic elements for Beacon and Probe Response frames
 +# This parameter can be used to add additional vendor specific element(s) into
 +# the end of the Beacon and Probe Response frames. The format for these
 +# element(s) is a hexdump of the raw information elements (id+len+payload for
 +# one or more elements)
 +#vendor_elements=dd0411223301
 +
 +# TX queue parameters (EDCF / bursting)
 +# tx_queue_<queue name>_<param>
 +# queues: data0, data1, data2, data3, after_beacon, beacon
 +#             (data0 is the highest priority queue)
 +# parameters:
 +#   aifs: AIFS (default 2)
- # note - here cwMin and cmMax are in exponent form. the actual cw value used
- # will be (2^n)-1 where n is the value given here
++#   cwmin: cwMin (1, 3, 7, 15, 31, 63, 127, 255, 511, 1023, 2047, 4095, 8191,
++#        16383, 32767)
++#   cwmax: cwMax (same values as cwMin, cwMax >= cwMin)
 +#   burst: maximum length (in milliseconds with precision of up to 0.1 ms) for
 +#          bursting
 +#
 +# Default WMM parameters (IEEE 802.11 draft; 11-03-0504-03-000e):
 +# These parameters are used by the access point when transmitting frames
 +# to the clients.
 +#
 +# Low priority / AC_BK = background
 +#tx_queue_data3_aifs=7
 +#tx_queue_data3_cwmin=15
 +#tx_queue_data3_cwmax=1023
 +#tx_queue_data3_burst=0
 +# Note: for IEEE 802.11b mode: cWmin=31 cWmax=1023 burst=0
 +#
 +# Normal priority / AC_BE = best effort
 +#tx_queue_data2_aifs=3
 +#tx_queue_data2_cwmin=15
 +#tx_queue_data2_cwmax=63
 +#tx_queue_data2_burst=0
 +# Note: for IEEE 802.11b mode: cWmin=31 cWmax=127 burst=0
 +#
 +# High priority / AC_VI = video
 +#tx_queue_data1_aifs=1
 +#tx_queue_data1_cwmin=7
 +#tx_queue_data1_cwmax=15
 +#tx_queue_data1_burst=3.0
 +# Note: for IEEE 802.11b mode: cWmin=15 cWmax=31 burst=6.0
 +#
 +# Highest priority / AC_VO = voice
 +#tx_queue_data0_aifs=1
 +#tx_queue_data0_cwmin=3
 +#tx_queue_data0_cwmax=7
 +#tx_queue_data0_burst=1.5
 +# Note: for IEEE 802.11b mode: cWmin=7 cWmax=15 burst=3.3
 +
 +# 802.1D Tag (= UP) to AC mappings
 +# WMM specifies following mapping of data frames to different ACs. This mapping
 +# can be configured using Linux QoS/tc and sch_pktpri.o module.
 +# 802.1D Tag  802.1D Designation      Access Category WMM Designation
 +# 1           BK                      AC_BK           Background
 +# 2           -                       AC_BK           Background
 +# 0           BE                      AC_BE           Best Effort
 +# 3           EE                      AC_BE           Best Effort
 +# 4           CL                      AC_VI           Video
 +# 5           VI                      AC_VI           Video
 +# 6           VO                      AC_VO           Voice
 +# 7           NC                      AC_VO           Voice
 +# Data frames with no priority information: AC_BE
 +# Management frames: AC_VO
 +# PS-Poll frames: AC_BE
 +
 +# Default WMM parameters (IEEE 802.11 draft; 11-03-0504-03-000e):
 +# for 802.11a or 802.11g networks
 +# These parameters are sent to WMM clients when they associate.
 +# The parameters will be used by WMM clients for frames transmitted to the
 +# access point.
 +#
 +# note - txop_limit is in units of 32microseconds
 +# note - acm is admission control mandatory flag. 0 = admission control not
 +# required, 1 = mandatory
- # Compressed Steering Number of Beamformer Antennas Supported: [BF-ANTENNA-2]
++# note - Here cwMin and cmMax are in exponent form. The actual cw value used
++# will be (2^n)-1 where n is the value given here. The allowed range for these
++# wmm_ac_??_{cwmin,cwmax} is 0..15 with cwmax >= cwmin.
 +#
 +wmm_enabled=1
 +#
 +# WMM-PS Unscheduled Automatic Power Save Delivery [U-APSD]
 +# Enable this flag if U-APSD supported outside hostapd (eg., Firmware/driver)
 +#uapsd_advertisement_enabled=1
 +#
 +# Low priority / AC_BK = background
 +wmm_ac_bk_cwmin=4
 +wmm_ac_bk_cwmax=10
 +wmm_ac_bk_aifs=7
 +wmm_ac_bk_txop_limit=0
 +wmm_ac_bk_acm=0
 +# Note: for IEEE 802.11b mode: cWmin=5 cWmax=10
 +#
 +# Normal priority / AC_BE = best effort
 +wmm_ac_be_aifs=3
 +wmm_ac_be_cwmin=4
 +wmm_ac_be_cwmax=10
 +wmm_ac_be_txop_limit=0
 +wmm_ac_be_acm=0
 +# Note: for IEEE 802.11b mode: cWmin=5 cWmax=7
 +#
 +# High priority / AC_VI = video
 +wmm_ac_vi_aifs=2
 +wmm_ac_vi_cwmin=3
 +wmm_ac_vi_cwmax=4
 +wmm_ac_vi_txop_limit=94
 +wmm_ac_vi_acm=0
 +# Note: for IEEE 802.11b mode: cWmin=4 cWmax=5 txop_limit=188
 +#
 +# Highest priority / AC_VO = voice
 +wmm_ac_vo_aifs=2
 +wmm_ac_vo_cwmin=2
 +wmm_ac_vo_cwmax=3
 +wmm_ac_vo_txop_limit=47
 +wmm_ac_vo_acm=0
 +# Note: for IEEE 802.11b mode: cWmin=3 cWmax=4 burst=102
 +
 +# Static WEP key configuration
 +#
 +# The key number to use when transmitting.
 +# It must be between 0 and 3, and the corresponding key must be set.
 +# default: not set
 +#wep_default_key=0
 +# The WEP keys to use.
 +# A key may be a quoted string or unquoted hexadecimal digits.
 +# The key length should be 5, 13, or 16 characters, or 10, 26, or 32
 +# digits, depending on whether 40-bit (64-bit), 104-bit (128-bit), or
 +# 128-bit (152-bit) WEP is used.
 +# Only the default key must be supplied; the others are optional.
 +# default: not set
 +#wep_key0=123456789a
 +#wep_key1="vwxyz"
 +#wep_key2=0102030405060708090a0b0c0d
 +#wep_key3=".2.4.6.8.0.23"
 +
 +# Station inactivity limit
 +#
 +# If a station does not send anything in ap_max_inactivity seconds, an
 +# empty data frame is sent to it in order to verify whether it is
 +# still in range. If this frame is not ACKed, the station will be
 +# disassociated and then deauthenticated. This feature is used to
 +# clear station table of old entries when the STAs move out of the
 +# range.
 +#
 +# The station can associate again with the AP if it is still in range;
 +# this inactivity poll is just used as a nicer way of verifying
 +# inactivity; i.e., client will not report broken connection because
 +# disassociation frame is not sent immediately without first polling
 +# the STA with a data frame.
 +# default: 300 (i.e., 5 minutes)
 +#ap_max_inactivity=300
 +#
 +# The inactivity polling can be disabled to disconnect stations based on
 +# inactivity timeout so that idle stations are more likely to be disconnected
 +# even if they are still in range of the AP. This can be done by setting
 +# skip_inactivity_poll to 1 (default 0).
 +#skip_inactivity_poll=0
 +
 +# Disassociate stations based on excessive transmission failures or other
 +# indications of connection loss. This depends on the driver capabilities and
 +# may not be available with all drivers.
 +#disassoc_low_ack=1
 +
 +# Maximum allowed Listen Interval (how many Beacon periods STAs are allowed to
 +# remain asleep). Default: 65535 (no limit apart from field size)
 +#max_listen_interval=100
 +
 +# WDS (4-address frame) mode with per-station virtual interfaces
 +# (only supported with driver=nl80211)
 +# This mode allows associated stations to use 4-address frames to allow layer 2
 +# bridging to be used.
 +#wds_sta=1
 +
 +# If bridge parameter is set, the WDS STA interface will be added to the same
 +# bridge by default. This can be overridden with the wds_bridge parameter to
 +# 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
 +# 0 = disabled (default)
 +# 1 = enabled
 +# Note: You will also need to enable WMM for full HT functionality.
 +#ieee80211n=1
 +
 +# ht_capab: HT capabilities (list of flags)
 +# 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 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
 +#     HT40- and HT40+ use per IEEE 802.11n Annex J:
 +#     freq            HT40-           HT40+
 +#     2.4 GHz         5-13            1-7 (1-9 in Europe/Japan)
 +#     5 GHz           40,48,56,64     36,44,52,60
 +#     (depending on the location, not all of these channels may be available
 +#     for use)
 +#     Please note that 40 MHz channels may switch their primary and secondary
 +#     channels if needed or creation of 40 MHz channel maybe rejected based
 +#     on overlapping BSSes. These changes are done automatically when hostapd
 +#     is setting up the 40 MHz channel.
 +# Spatial Multiplexing (SM) Power Save: [SMPS-STATIC] or [SMPS-DYNAMIC]
 +#     (SMPS disabled if neither is set)
 +# HT-greenfield: [GF] (disabled if not set)
 +# Short GI for 20 MHz: [SHORT-GI-20] (disabled if not set)
 +# Short GI for 40 MHz: [SHORT-GI-40] (disabled if not set)
 +# Tx STBC: [TX-STBC] (disabled if not set)
 +# Rx STBC: [RX-STBC1] (one spatial stream), [RX-STBC12] (one or two spatial
 +#     streams), or [RX-STBC123] (one, two, or three spatial streams); Rx STBC
 +#     disabled if none of these set
 +# HT-delayed Block Ack: [DELAYED-BA] (disabled if not set)
 +# 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)
 +# 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
 +# 0 = disabled (default)
 +# 1 = enabled
 +# Note: You will also need to enable WMM for full VHT functionality.
 +#ieee80211ac=1
 +
 +# vht_capab: VHT capabilities (list of flags)
 +#
 +# vht_max_mpdu_len: [MAX-MPDU-7991] [MAX-MPDU-11454]
 +# Indicates maximum MPDU length
 +# 0 = 3895 octets (default)
 +# 1 = 7991 octets
 +# 2 = 11454 octets
 +# 3 = reserved
 +#
 +# supported_chan_width: [VHT160] [VHT160-80PLUS80]
 +# Indicates supported Channel widths
 +# 0 = 160 MHz & 80+80 channel widths are not supported (default)
 +# 1 = 160 MHz channel width is supported
 +# 2 = 160 MHz & 80+80 channel widths are supported
 +# 3 = reserved
 +#
 +# Rx LDPC coding capability: [RXLDPC]
 +# Indicates support for receiving LDPC coded pkts
 +# 0 = Not supported (default)
 +# 1 = Supported
 +#
 +# Short GI for 80 MHz: [SHORT-GI-80]
 +# Indicates short GI support for reception of packets transmitted with TXVECTOR
 +# params format equal to VHT and CBW = 80Mhz
 +# 0 = Not supported (default)
 +# 1 = Supported
 +#
 +# Short GI for 160 MHz: [SHORT-GI-160]
 +# Indicates short GI support for reception of packets transmitted with TXVECTOR
 +# params format equal to VHT and CBW = 160Mhz
 +# 0 = Not supported (default)
 +# 1 = Supported
 +#
 +# Tx STBC: [TX-STBC-2BY1]
 +# Indicates support for the transmission of at least 2x1 STBC
 +# 0 = Not supported (default)
 +# 1 = Supported
 +#
 +# Rx STBC: [RX-STBC-1] [RX-STBC-12] [RX-STBC-123] [RX-STBC-1234]
 +# Indicates support for the reception of PPDUs using STBC
 +# 0 = Not supported (default)
 +# 1 = support of one spatial stream
 +# 2 = support of one and two spatial streams
 +# 3 = support of one, two and three spatial streams
 +# 4 = support of one, two, three and four spatial streams
 +# 5,6,7 = reserved
 +#
 +# SU Beamformer Capable: [SU-BEAMFORMER]
 +# Indicates support for operation as a single user beamformer
 +# 0 = Not supported (default)
 +# 1 = Supported
 +#
 +# SU Beamformee Capable: [SU-BEAMFORMEE]
 +# Indicates support for operation as a single user beamformee
 +# 0 = Not supported (default)
 +# 1 = Supported
 +#
- # Number of Sounding Dimensions: [SOUNDING-DIMENSION-2]
++# Compressed Steering Number of Beamformer Antennas Supported:
++# [BF-ANTENNA-2] [BF-ANTENNA-3] [BF-ANTENNA-4]
 +#   Beamformee's capability indicating the maximum number of beamformer
 +#   antennas the beamformee can support when sending compressed beamforming
 +#   feedback
 +# If SU beamformer capable, set to maximum value minus 1
 +# else reserved (default)
 +#
- # MU Beamformee Capable: [MU-BEAMFORMEE]
- # Indicates support for operation as an MU beamformee
- # 0 = Not supported or sent by AP (default)
- # 1 = Supported
- #
++# Number of Sounding Dimensions:
++# [SOUNDING-DIMENSION-2] [SOUNDING-DIMENSION-3] [SOUNDING-DIMENSION-4]
 +# Beamformer's capability indicating the maximum value of the NUM_STS parameter
 +# in the TXVECTOR of a VHT NDP
 +# If SU beamformer capable, set to maximum value minus 1
 +# else reserved (default)
 +#
 +# MU Beamformer Capable: [MU-BEAMFORMER]
 +# Indicates support for operation as an MU beamformer
 +# 0 = Not supported or sent by Non-AP STA (default)
 +# 1 = Supported
 +#
- # "openssl dhparam -out /etc/hostapd.dh.pem 1024"
 +# VHT TXOP PS: [VHT-TXOP-PS]
 +# Indicates whether or not the AP supports VHT TXOP Power Save Mode
 +#  or whether or not the STA is in VHT TXOP Power Save mode
 +# 0 = VHT AP doesnt support VHT TXOP PS mode (OR) VHT Sta not in VHT TXOP PS
 +#  mode
 +# 1 = VHT AP supports VHT TXOP PS mode (OR) VHT Sta is in VHT TXOP power save
 +#  mode
 +#
 +# +HTC-VHT Capable: [HTC-VHT]
 +# Indicates whether or not the STA supports receiving a VHT variant HT Control
 +# field.
 +# 0 = Not supported (default)
 +# 1 = supported
 +#
 +# Maximum A-MPDU Length Exponent: [MAX-A-MPDU-LEN-EXP0]..[MAX-A-MPDU-LEN-EXP7]
 +# Indicates the maximum length of A-MPDU pre-EOF padding that the STA can recv
 +# This field is an integer in the range of 0 to 7.
 +# The length defined by this field is equal to
 +# 2 pow(13 + Maximum A-MPDU Length Exponent) -1 octets
 +#
 +# VHT Link Adaptation Capable: [VHT-LINK-ADAPT2] [VHT-LINK-ADAPT3]
 +# Indicates whether or not the STA supports link adaptation using VHT variant
 +# HT Control field
 +# If +HTC-VHTcapable is 1
 +#  0 = (no feedback) if the STA does not provide VHT MFB (default)
 +#  1 = reserved
 +#  2 = (Unsolicited) if the STA provides only unsolicited VHT MFB
 +#  3 = (Both) if the STA can provide VHT MFB in response to VHT MRQ and if the
 +#      STA provides unsolicited VHT MFB
 +# Reserved if +HTC-VHTcapable is 0
 +#
 +# Rx Antenna Pattern Consistency: [RX-ANTENNA-PATTERN]
 +# Indicates the possibility of Rx antenna pattern change
 +# 0 = Rx antenna pattern might change during the lifetime of an association
 +# 1 = Rx antenna pattern does not change during the lifetime of an association
 +#
 +# Tx Antenna Pattern Consistency: [TX-ANTENNA-PATTERN]
 +# Indicates the possibility of Tx antenna pattern change
 +# 0 = Tx antenna pattern might change during the lifetime of an association
 +# 1 = Tx antenna pattern does not change during the lifetime of an association
 +#vht_capab=[SHORT-GI-80][HTC-VHT]
 +#
 +# Require stations to support VHT PHY (reject association if they do not)
 +#require_vht=1
 +
 +# 0 = 20 or 40 MHz operating Channel width
 +# 1 = 80 MHz channel width
 +# 2 = 160 MHz channel width
 +# 3 = 80+80 MHz channel width
 +#vht_oper_chwidth=1
 +#
 +# center freq = 5 GHz + (5 * index)
 +# So index 42 gives center freq 5.210 GHz
 +# which is channel 42 in 5G band
 +#
 +#vht_oper_centr_freq_seg0_idx=42
 +#
 +# center freq = 5 GHz + (5 * index)
 +# So index 159 gives center freq 5.795 GHz
 +# which is channel 159 in 5G band
 +#
 +#vht_oper_centr_freq_seg1_idx=159
 +
 +##### IEEE 802.1X-2004 related configuration ##################################
 +
 +# Require IEEE 802.1X authorization
 +#ieee8021x=1
 +
 +# IEEE 802.1X/EAPOL version
 +# hostapd is implemented based on IEEE Std 802.1X-2004 which defines EAPOL
 +# version 2. However, there are many client implementations that do not handle
 +# the new version number correctly (they seem to drop the frames completely).
 +# In order to make hostapd interoperate with these clients, the version number
 +# can be set to the older version (1) with this configuration value.
 +#eapol_version=2
 +
 +# Optional displayable message sent with EAP Request-Identity. The first \0
 +# in this string will be converted to ASCII-0 (nul). This can be used to
 +# separate network info (comma separated list of attribute=value pairs); see,
 +# e.g., RFC 4284.
 +#eap_message=hello
 +#eap_message=hello\0networkid=netw,nasid=foo,portid=0,NAIRealms=example.com
 +
 +# WEP rekeying (disabled if key lengths are not set or are set to 0)
 +# Key lengths for default/broadcast and individual/unicast keys:
 +# 5 = 40-bit WEP (also known as 64-bit WEP with 40 secret bits)
 +# 13 = 104-bit WEP (also known as 128-bit WEP with 104 secret bits)
 +#wep_key_len_broadcast=5
 +#wep_key_len_unicast=5
 +# Rekeying period in seconds. 0 = do not rekey (i.e., set keys only once)
 +#wep_rekey_period=300
 +
 +# EAPOL-Key index workaround (set bit7) for WinXP Supplicant (needed only if
 +# only broadcast keys are used)
 +eapol_key_index_workaround=0
 +
 +# EAP reauthentication period in seconds (default: 3600 seconds; 0 = disable
 +# reauthentication).
 +#eap_reauth_period=3600
 +
 +# Use PAE group address (01:80:c2:00:00:03) instead of individual target
 +# address when sending EAPOL frames with driver=wired. This is the most common
 +# mechanism used in wired authentication, but it also requires that the port
 +# 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
 +# to process EAP authentication locally without need for an external RADIUS
 +# server. This functionality can be used both as a local authentication server
 +# for IEEE 802.1X/EAPOL and as a RADIUS server for other devices.
 +
 +# Use integrated EAP server instead of external RADIUS authentication
 +# server. This is also needed if hostapd is configured to act as a RADIUS
 +# authentication server.
 +eap_server=0
 +
 +# Path for EAP server user database
 +# If SQLite support is included, this can be set to "sqlite:/path/to/sqlite.db"
 +# to use SQLite database instead of a text file.
 +#eap_user_file=/etc/hostapd.eap_user
 +
 +# CA certificate (PEM or DER file) for EAP-TLS/PEAP/TTLS
 +#ca_cert=/etc/hostapd.ca.pem
 +
 +# Server certificate (PEM or DER file) for EAP-TLS/PEAP/TTLS
 +#server_cert=/etc/hostapd.server.pem
 +
 +# Private key matching with the server certificate for EAP-TLS/PEAP/TTLS
 +# This may point to the same file as server_cert if both certificate and key
 +# are included in a single file. PKCS#12 (PFX) file (.p12/.pfx) can also be
 +# used by commenting out server_cert and specifying the PFX file as the
 +# private_key.
 +#private_key=/etc/hostapd.server.prv
 +
 +# 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.
 +# This can be done by using PEM format for CA certificate and CRL and
 +# concatenating these into one file. Whenever CRL changes, hostapd needs to be
 +# restarted to take the new CRL into use.
 +# 0 = do not verify CRLs (default)
 +# 1 = check the CRL of the user certificate
 +# 2 = check all CRLs in the certificate path
 +#check_crl=1
 +
++# TLS Session Lifetime in seconds
++# This can be used to allow TLS sessions to be cached and resumed with an
++# abbreviated handshake when using EAP-TLS/TTLS/PEAP.
++# (default: 0 = session caching and resumption disabled)
++#tls_session_lifetime=3600
++
 +# Cached OCSP stapling response (DER encoded)
 +# If set, this file is sent as a certificate status response by the EAP server
 +# if the EAP peer requests certificate status in the ClientHello message.
 +# This cache file can be updated, e.g., by running following command
 +# periodically to get an update from the OCSP responder:
 +# openssl ocsp \
 +#     -no_nonce \
 +#     -CAfile /etc/hostapd.ca.pem \
 +#     -issuer /etc/hostapd.ca.pem \
 +#     -cert /etc/hostapd.server.pem \
 +#     -url http://ocsp.example.com:8888/ \
 +#     -respout /tmp/ocsp-cache.der
 +#ocsp_stapling_response=/tmp/ocsp-cache.der
 +
 +# dh_file: File path to DH/DSA parameters file (in PEM format)
 +# This is an optional configuration file for setting parameters for an
 +# ephemeral DH key exchange. In most cases, the default RSA authentication does
 +# not use this configuration. However, it is possible setup RSA to use
 +# ephemeral DH key exchange. In addition, ciphers with DSA keys always use
 +# ephemeral DH keys. This can be used to achieve forward secrecy. If the file
 +# is in DSA parameters format, it will be automatically converted into DH
 +# params. This parameter is required if anonymous EAP-FAST is used.
 +# You can generate DH parameters file with OpenSSL, e.g.,
- # WPS RF Bands (a = 5G, b = 2.4G, g = 2.4G, ag = dual band)
++# "openssl dhparam -out /etc/hostapd.dh.pem 2048"
 +#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
 +
 +# Finite cyclic group for EAP-pwd. Number maps to group of domain parameters
 +# using the IANA repository for IKE (RFC 2409).
 +#pwd_group=19
 +
 +# Configuration data for EAP-SIM database/authentication gateway interface.
 +# This is a text string in implementation specific format. The example
 +# implementation in eap_sim_db.c uses this as the UNIX domain socket name for
 +# the HLR/AuC gateway (e.g., hlr_auc_gw). In this case, the path uses "unix:"
 +# prefix. If hostapd is built with SQLite support (CONFIG_SQLITE=y in .config),
 +# database file can be described with an optional db=<path> parameter.
 +#eap_sim_db=unix:/tmp/hlr_auc_gw.sock
 +#eap_sim_db=unix:/tmp/hlr_auc_gw.sock db=/tmp/hostapd.db
 +
 +# Encryption key for EAP-FAST PAC-Opaque values. This key must be a secret,
 +# random value. It is configured as a 16-octet value in hex format. It can be
 +# generated, e.g., with the following command:
 +# od -tx1 -v -N16 /dev/random | colrm 1 8 | tr -d ' '
 +#pac_opaque_encr_key=000102030405060708090a0b0c0d0e0f
 +
 +# EAP-FAST authority identity (A-ID)
 +# A-ID indicates the identity of the authority that issues PACs. The A-ID
 +# should be unique across all issuing servers. In theory, this is a variable
 +# length field, but due to some existing implementations requiring A-ID to be
 +# 16 octets in length, it is strongly recommended to use that length for the
 +# field to provid interoperability with deployed peer implementations. This
 +# field is configured in hex format.
 +#eap_fast_a_id=101112131415161718191a1b1c1d1e1f
 +
 +# EAP-FAST authority identifier information (A-ID-Info)
 +# This is a user-friendly name for the A-ID. For example, the enterprise name
 +# and server name in a human-readable format. This field is encoded as UTF-8.
 +#eap_fast_a_id_info=test server
 +
 +# Enable/disable different EAP-FAST provisioning modes:
 +#0 = provisioning disabled
 +#1 = only anonymous provisioning allowed
 +#2 = only authenticated provisioning allowed
 +#3 = both provisioning modes allowed (default)
 +#eap_fast_prov=3
 +
 +# EAP-FAST PAC-Key lifetime in seconds (hard limit)
 +#pac_key_lifetime=604800
 +
 +# EAP-FAST PAC-Key refresh time in seconds (soft limit on remaining hard
 +# limit). The server will generate a new PAC-Key when this number of seconds
 +# (or fewer) of the lifetime remains.
 +#pac_key_refresh_time=86400
 +
 +# EAP-SIM and EAP-AKA protected success/failure indication using AT_RESULT_IND
 +# (default: 0 = disabled).
 +#eap_sim_aka_result_ind=1
 +
 +# Trusted Network Connect (TNC)
 +# If enabled, TNC validation will be required before the peer is allowed to
 +# connect. Note: This is only used with EAP-TTLS and EAP-FAST. If any other
 +# 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) #######################
 +
 +# Interface to be used for IAPP broadcast packets
 +#iapp_interface=eth0
 +
 +
 +##### RADIUS client configuration #############################################
 +# for IEEE 802.1X with external Authentication Server, IEEE 802.11
 +# authentication with external ACL for MAC addresses, and accounting
 +
 +# The own IP address of the access point (used as NAS-IP-Address)
 +own_ip_addr=127.0.0.1
 +
 +# Optional NAS-Identifier string for RADIUS messages. When used, this should be
 +# a unique to the NAS within the scope of the RADIUS server. For example, a
 +# fully qualified domain name can be used here.
 +# When using IEEE 802.11r, nas_identifier must be set and must be between 1 and
 +# 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
 +#auth_server_shared_secret=secret
 +
 +# RADIUS accounting server
 +#acct_server_addr=127.0.0.1
 +#acct_server_port=1813
 +#acct_server_shared_secret=secret
 +
 +# Secondary RADIUS servers; to be used if primary one does not reply to
 +# RADIUS packets. These are optional and there can be more than one secondary
 +# server listed.
 +#auth_server_addr=127.0.0.2
 +#auth_server_port=1812
 +#auth_server_shared_secret=secret2
 +#
 +#acct_server_addr=127.0.0.2
 +#acct_server_port=1813
 +#acct_server_shared_secret=secret2
 +
 +# Retry interval for trying to return to the primary RADIUS server (in
 +# seconds). RADIUS client code will automatically try to use the next server
 +# when the current server is not replying to requests. If this interval is set,
 +# primary server will be retried after configured amount of time even if the
 +# currently used secondary server is still working.
 +#radius_retry_primary_interval=600
 +
 +
 +# Interim accounting update interval
 +# If this is set (larger than 0) and acct_server is configured, hostapd will
 +# send interim accounting updates every N seconds. Note: if set, this overrides
 +# possible Acct-Interim-Interval attribute in Access-Accept message. Thus, this
 +# value should not be configured in hostapd.conf, if RADIUS server is used to
 +# control the interim interval.
 +# This value should not be less 600 (10 minutes) and must not be less than
 +# 60 (1 minute).
 +#radius_acct_interim_interval=600
 +
 +# Request Chargeable-User-Identity (RFC 4372)
 +# This parameter can be used to configure hostapd to request CUI from the
 +# RADIUS server by including Chargeable-User-Identity attribute into
 +# Access-Request packets.
 +#radius_request_cui=1
 +
 +# Dynamic VLAN mode; allow RADIUS authentication server to decide which VLAN
 +# is used for the stations. This information is parsed from following RADIUS
 +# attributes based on RFC 3580 and RFC 2868: Tunnel-Type (value 13 = VLAN),
 +# Tunnel-Medium-Type (value 6 = IEEE 802), Tunnel-Private-Group-ID (value
 +# VLANID as a string). Optionally, the local MAC ACL list (accept_mac_file) can
 +# be used to set static client MAC address to VLAN ID mapping.
 +# 0 = disabled (default)
 +# 1 = option; use default interface if RADIUS server does not include VLAN ID
 +# 2 = required; reject authentication if RADIUS server does not include VLAN ID
 +#dynamic_vlan=0
 +
 +# VLAN interface list for dynamic VLAN mode is read from a separate text file.
 +# This list is used to map VLAN ID from the RADIUS server to a network
 +# interface. Each station is bound to one interface in the same way as with
 +# multiple BSSIDs or SSIDs. Each line in this text file is defining a new
 +# interface and the line must include VLAN ID and interface name separated by
 +# white space (space or tab).
 +# If no entries are provided by this file, the station is statically mapped
 +# to <bss-iface>.<vlan-id> interfaces.
 +#vlan_file=/etc/hostapd.vlan
 +
 +# Interface where 802.1q tagged packets should appear when a RADIUS server is
 +# used to determine which VLAN a station is on.  hostapd creates a bridge for
 +# each VLAN.  Then hostapd adds a VLAN interface (associated with the interface
 +# indicated by 'vlan_tagged_interface') and the appropriate wireless interface
 +# to the bridge.
 +#vlan_tagged_interface=eth0
 +
 +# Bridge (prefix) to add the wifi and the tagged interface to. This gets the
 +# VLAN ID appended. It defaults to brvlan%d if no tagged interface is given
 +# and br%s.%d if a tagged interface is given, provided %s = tagged interface
 +# and %d = VLAN ID.
 +#vlan_bridge=brvlan
 +
 +# When hostapd creates a VLAN interface on vlan_tagged_interfaces, it needs
 +# to know how to name it.
 +# 0 = vlan<XXX>, e.g., vlan1
 +# 1 = <vlan_tagged_interface>.<XXX>, e.g. eth0.1
 +#vlan_naming=0
 +
 +# Arbitrary RADIUS attributes can be added into Access-Request and
 +# Accounting-Request packets by specifying the contents of the attributes with
 +# the following configuration parameters. There can be multiple of these to
 +# add multiple attributes. These parameters can also be used to override some
 +# of the attributes added automatically by hostapd.
 +# Format: <attr_id>[:<syntax:value>]
 +# attr_id: RADIUS attribute type (e.g., 26 = Vendor-Specific)
 +# syntax: s = string (UTF-8), d = integer, x = octet string
 +# value: attribute value in format indicated by the syntax
 +# If syntax and value parts are omitted, a null value (single 0x00 octet) is
 +# used.
 +#
 +# Additional Access-Request attributes
 +# radius_auth_req_attr=<attr_id>[:<syntax:value>]
 +# Examples:
 +# Operator-Name = "Operator"
 +#radius_auth_req_attr=126:s:Operator
 +# Service-Type = Framed (2)
 +#radius_auth_req_attr=6:d:2
 +# Connect-Info = "testing" (this overrides the automatically generated value)
 +#radius_auth_req_attr=77:s:testing
 +# Same Connect-Info value set as a hexdump
 +#radius_auth_req_attr=77:x:74657374696e67
 +
 +#
 +# Additional Accounting-Request attributes
 +# radius_acct_req_attr=<attr_id>[:<syntax:value>]
 +# Examples:
 +# Operator-Name = "Operator"
 +#radius_acct_req_attr=126:s:Operator
 +
 +# Dynamic Authorization Extensions (RFC 5176)
 +# This mechanism can be used to allow dynamic changes to user session based on
 +# commands from a RADIUS server (or some other disconnect client that has the
 +# needed session information). For example, Disconnect message can be used to
 +# request an associated station to be disconnected.
 +#
 +# This is disabled by default. Set radius_das_port to non-zero UDP port
 +# number to enable.
 +#radius_das_port=3799
 +#
 +# DAS client (the host that can send Disconnect/CoA requests) and shared secret
 +#radius_das_client=192.168.1.123 shared secret here
 +#
 +# DAS Event-Timestamp time window in seconds
 +#radius_das_time_window=300
 +#
 +# DAS require Event-Timestamp
 +#radius_das_require_event_timestamp=1
 +
 +##### RADIUS authentication server configuration ##############################
 +
 +# hostapd can be used as a RADIUS authentication server for other hosts. This
 +# requires that the integrated EAP server is also enabled and both
 +# authentication services are sharing the same configuration.
 +
 +# File name of the RADIUS clients configuration for the RADIUS server. If this
 +# commented out, RADIUS server is disabled.
 +#radius_server_clients=/etc/hostapd.radius_clients
 +
 +# 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
 +
 +
 +##### WPA/IEEE 802.11i configuration ##########################################
 +
 +# Enable WPA. Setting this variable configures the AP to require WPA (either
 +# WPA-PSK or WPA-RADIUS/EAP based on other configuration). For WPA-PSK, either
 +# wpa_psk or wpa_passphrase must be set and wpa_key_mgmt must include WPA-PSK.
 +# Instead of wpa_psk / wpa_passphrase, wpa_psk_radius might suffice.
 +# For WPA-RADIUS/EAP, ieee8021x must be set (but without dynamic WEP keys),
 +# RADIUS authentication server must be configured, and WPA-EAP must be included
 +# in wpa_key_mgmt.
 +# This field is a bit field that can be used to enable WPA (IEEE 802.11i/D3.0)
 +# and/or WPA2 (full IEEE 802.11i/RSN):
 +# bit0 = WPA
 +# bit1 = IEEE 802.11i/RSN (WPA2) (dot11RSNAEnabled)
 +#wpa=1
 +
 +# WPA pre-shared keys for WPA-PSK. This can be either entered as a 256-bit
 +# secret in hex format (64 hex digits), wpa_psk, or as an ASCII passphrase
 +# (8..63 characters) that will be converted to PSK. This conversion uses SSID
 +# so the PSK changes when ASCII passphrase is used and the SSID is changed.
 +# wpa_psk (dot11RSNAConfigPSKValue)
 +# wpa_passphrase (dot11RSNAConfigPSKPassPhrase)
 +#wpa_psk=0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
 +#wpa_passphrase=secret passphrase
 +
 +# Optionally, WPA PSKs can be read from a separate text file (containing list
 +# of (PSK,MAC address) pairs. This allows more than one PSK to be configured.
 +# Use absolute path name to make sure that the files can be read on SIGHUP
 +# configuration reloads.
 +#wpa_psk_file=/etc/hostapd.wpa_psk
 +
 +# Optionally, WPA passphrase can be received from RADIUS authentication server
 +# This requires macaddr_acl to be set to 2 (RADIUS)
 +# 0 = disabled (default)
 +# 1 = optional; use default passphrase/psk if RADIUS server does not include
 +#     Tunnel-Password
 +# 2 = required; reject authentication if RADIUS server does not include
 +#     Tunnel-Password
 +#wpa_psk_radius=0
 +
 +# Set of accepted key management algorithms (WPA-PSK, WPA-EAP, or both). The
 +# entries are separated with a space. WPA-PSK-SHA256 and WPA-EAP-SHA256 can be
 +# added to enable SHA256-based stronger algorithms.
 +# (dot11RSNAConfigAuthenticationSuitesTable)
 +#wpa_key_mgmt=WPA-PSK WPA-EAP
 +
 +# Set of accepted cipher suites (encryption algorithms) for pairwise keys
 +# (unicast packets). This is a space separated list of algorithms:
 +# CCMP = AES in Counter mode with CBC-MAC [RFC 3610, IEEE 802.11i/D7.0]
 +# TKIP = Temporal Key Integrity Protocol [IEEE 802.11i/D7.0]
 +# Group cipher suite (encryption algorithm for broadcast and multicast frames)
 +# is automatically selected based on this configuration. If only CCMP is
 +# allowed as the pairwise cipher, group cipher will also be CCMP. Otherwise,
 +# TKIP will be used as the group cipher.
 +# (dot11RSNAConfigPairwiseCiphersTable)
 +# Pairwise cipher for WPA (v1) (default: TKIP)
 +#wpa_pairwise=TKIP CCMP
 +# Pairwise cipher for RSN/WPA2 (default: use wpa_pairwise value)
 +#rsn_pairwise=CCMP
 +
 +# Time interval for rekeying GTK (broadcast/multicast encryption keys) in
 +# seconds. (dot11RSNAConfigGroupRekeyTime)
 +#wpa_group_rekey=600
 +
 +# Rekey GTK when any STA that possesses the current GTK is leaving the BSS.
 +# (dot11RSNAConfigGroupRekeyStrict)
 +#wpa_strict_rekey=1
 +
 +# Time interval for rekeying GMK (master key used internally to generate GTKs
 +# (in seconds).
 +#wpa_gmk_rekey=86400
 +
 +# Maximum lifetime for PTK in seconds. This can be used to enforce rekeying of
 +# PTK to mitigate some attacks against TKIP deficiencies.
 +#wpa_ptk_rekey=600
 +
 +# Enable IEEE 802.11i/RSN/WPA2 pre-authentication. This is used to speed up
 +# roaming be pre-authenticating IEEE 802.1X/EAP part of the full RSN
 +# authentication and key handshake before actually associating with a new AP.
 +# (dot11RSNAPreauthenticationEnabled)
 +#rsn_preauth=1
 +#
 +# Space separated list of interfaces from which pre-authentication frames are
 +# accepted (e.g., 'eth0' or 'eth0 wlan0wds0'. This list should include all
 +# interface that are used for connections to other APs. This could include
 +# wired interfaces and WDS links. The normal wireless data interface towards
 +# associated stations (e.g., wlan0) should not be added, since
 +# pre-authentication is only used with APs other than the currently associated
 +# one.
 +#rsn_preauth_interfaces=eth0
 +
 +# peerkey: Whether PeerKey negotiation for direct links (IEEE 802.11e) is
 +# allowed. This is only used with RSN/WPA2.
 +# 0 = disabled (default)
 +# 1 = enabled
 +#peerkey=1
 +
 +# ieee80211w: Whether management frame protection (MFP) is enabled
 +# 0 = disabled (default)
 +# 1 = optional
 +# 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
 +#assoc_sa_query_max_timeout=1000
 +
 +# Association SA Query retry timeout (in TU = 1.024 ms; for MFP)
 +# (time between two subsequent SA Query requests)
 +# dot11AssociationSAQueryRetryTimeout, 1...4294967295
 +#assoc_sa_query_retry_timeout=201
 +
 +# disable_pmksa_caching: Disable PMKSA caching
 +# This parameter can be used to disable caching of PMKSA created through EAP
 +# authentication. RSN preauthentication may still end up using PMKSA caching if
 +# it is enabled (rsn_preauth=1).
 +# 0 = PMKSA caching enabled (default)
 +# 1 = PMKSA caching disabled
 +#disable_pmksa_caching=0
 +
 +# okc: Opportunistic Key Caching (aka Proactive Key Caching)
 +# Allow PMK cache to be shared opportunistically among configured interfaces
 +# and BSSes (i.e., all configurations within a single hostapd process).
 +# 0 = disabled (default)
 +# 1 = enabled
 +#okc=1
 +
 +# SAE threshold for anti-clogging mechanism (dot11RSNASAEAntiCloggingThreshold)
 +# This parameter defines how many open SAE instances can be in progress at the
 +# same time before the anti-clogging mechanism is taken into use.
 +#sae_anti_clogging_threshold=5
 +
 +# Enabled SAE finite cyclic groups
 +# SAE implementation are required to support group 19 (ECC group defined over a
 +# 256-bit prime order field). All groups that are supported by the
 +# implementation are enabled by default. This configuration parameter can be
 +# used to specify a limited set of allowed groups. The group values are listed
 +# in the IANA registry:
 +# http://www.iana.org/assignments/ipsec-registry/ipsec-registry.xml#ipsec-registry-9
 +#sae_groups=19 20 21 25 26
 +
 +##### IEEE 802.11r configuration ##############################################
 +
 +# Mobility Domain identifier (dot11FTMobilityDomainID, MDID)
 +# MDID is used to indicate a group of APs (within an ESS, i.e., sharing the
 +# same SSID) between which a STA can use Fast BSS Transition.
 +# 2-octet identifier as a hex string.
 +#mobility_domain=a1b2
 +
 +# PMK-R0 Key Holder identifier (dot11FTR0KeyHolderID)
 +# 1 to 48 octet identifier.
 +# This is configured with nas_identifier (see RADIUS client section above).
 +
 +# Default lifetime of the PMK-RO in minutes; range 1..65535
 +# (dot11FTR0KeyLifetime)
 +#r0_key_lifetime=10000
 +
 +# PMK-R1 Key Holder identifier (dot11FTR1KeyHolderID)
 +# 6-octet identifier as a hex string.
 +#r1_key_holder=000102030405
 +
 +# Reassociation deadline in time units (TUs / 1.024 ms; range 1000..65535)
 +# (dot11FTReassociationDeadline)
 +#reassociation_deadline=1000
 +
 +# List of R0KHs in the same Mobility Domain
 +# format: <MAC address> <NAS Identifier> <128-bit key as hex string>
 +# This list is used to map R0KH-ID (NAS Identifier) to a destination MAC
 +# address when requesting PMK-R1 key from the R0KH that the STA used during the
 +# Initial Mobility Domain Association.
 +#r0kh=02:01:02:03:04:05 r0kh-1.example.com 000102030405060708090a0b0c0d0e0f
 +#r0kh=02:01:02:03:04:06 r0kh-2.example.com 00112233445566778899aabbccddeeff
 +# And so on.. One line per R0KH.
 +
 +# List of R1KHs in the same Mobility Domain
 +# format: <MAC address> <R1KH-ID> <128-bit key as hex string>
 +# This list is used to map R1KH-ID to a destination MAC address when sending
 +# PMK-R1 key from the R0KH. This is also the list of authorized R1KHs in the MD
 +# that can request PMK-R1 keys.
 +#r1kh=02:01:02:03:04:05 02:11:22:33:44:55 000102030405060708090a0b0c0d0e0f
 +#r1kh=02:01:02:03:04:06 02:11:22:33:44:66 00112233445566778899aabbccddeeff
 +# And so on.. One line per R1KH.
 +
 +# Whether PMK-R1 push is enabled at R0KH
 +# 0 = do not push PMK-R1 to all configured R1KHs (default)
 +# 1 = push PMK-R1 to all configured R1KHs whenever a new PMK-R0 is derived
 +#pmk_r1_push=1
 +
++# Whether to enable FT-over-DS
++# 0 = FT-over-DS disabled
++# 1 = FT-over-DS enabled (default)
++#ft_over_ds=1
++
 +##### Neighbor table ##########################################################
 +# Maximum number of entries kept in AP table (either for neigbor table or for
 +# detecting Overlapping Legacy BSS Condition). The oldest entry will be
 +# removed when adding a new entry that would make the list grow over this
 +# limit. Note! WFA certification for IEEE 802.11g requires that OLBC is
 +# enabled, so this field should not be set to 0 when using IEEE 802.11g.
 +# default: 255
 +#ap_table_max_size=255
 +
 +# Number of seconds of no frames received after which entries may be deleted
 +# from the AP table. Since passive scanning is not usually performed frequently
 +# this should not be set to very small value. In addition, there is no
 +# guarantee that every scan cycle will receive beacon frames from the
 +# neighboring APs.
 +# default: 60
 +#ap_table_expiration_time=3600
 +
++# Maximum number of stations to track on the operating channel
++# This can be used to detect dualband capable stations before they have
++# associated, e.g., to provide guidance on which colocated BSS to use.
++# Default: 0 (disabled)
++#track_sta_max_num=100
++
++# Maximum age of a station tracking entry in seconds
++# Default: 180
++#track_sta_max_age=180
++
++# Do not reply to group-addressed Probe Request from a station that was seen on
++# another radio.
++# Default: Disabled
++#
++# This can be used with enabled track_sta_max_num configuration on another
++# interface controlled by the same hostapd process to restrict Probe Request
++# frame handling from replying to group-addressed Probe Request frames from a
++# station that has been detected to be capable of operating on another band,
++# e.g., to try to reduce likelihood of the station selecting a 2.4 GHz BSS when
++# the AP operates both a 2.4 GHz and 5 GHz BSS concurrently.
++#
++# Note: Enabling this can cause connectivity issues and increase latency for
++# discovering the AP.
++#no_probe_resp_if_seen_on=wlan1
++
++# Reject authentication from a station that was seen on another radio.
++# Default: Disabled
++#
++# This can be used with enabled track_sta_max_num configuration on another
++# interface controlled by the same hostapd process to reject authentication
++# attempts from a station that has been detected to be capable of operating on
++# another band, e.g., to try to reduce likelihood of the station selecting a
++# 2.4 GHz BSS when the AP operates both a 2.4 GHz and 5 GHz BSS concurrently.
++#
++# Note: Enabling this can cause connectivity issues and increase latency for
++# connecting with the AP.
++#no_auth_if_seen_on=wlan1
 +
 +##### Wi-Fi Protected Setup (WPS) #############################################
 +
 +# WPS state
 +# 0 = WPS disabled (default)
 +# 1 = WPS enabled, not configured
 +# 2 = WPS enabled, configured
 +#wps_state=2
 +
 +# Whether to manage this interface independently from other WPS interfaces
 +# By default, a single hostapd process applies WPS operations to all configured
 +# interfaces. This parameter can be used to disable that behavior for a subset
 +# of interfaces. If this is set to non-zero for an interface, WPS commands
 +# issued on that interface do not apply to other interfaces and WPS operations
 +# performed on other interfaces do not affect this interface.
 +#wps_independent=0
 +
 +# AP can be configured into a locked state where new WPS Registrar are not
 +# accepted, but previously authorized Registrars (including the internal one)
 +# can continue to add new Enrollees.
 +#ap_setup_locked=1
 +
 +# Universally Unique IDentifier (UUID; see RFC 4122) of the device
 +# This value is used as the UUID for the internal WPS Registrar. If the AP
 +# is also using UPnP, this value should be set to the device's UPnP UUID.
 +# If not configured, UUID will be generated based on the local MAC address.
 +#uuid=12345678-9abc-def0-1234-56789abcdef0
 +
 +# Note: If wpa_psk_file is set, WPS is used to generate random, per-device PSKs
 +# that will be appended to the wpa_psk_file. If wpa_psk_file is not set, the
 +# default PSK (wpa_psk/wpa_passphrase) will be delivered to Enrollees. Use of
 +# per-device PSKs is recommended as the more secure option (i.e., make sure to
 +# set wpa_psk_file when using WPS with WPA-PSK).
 +
 +# When an Enrollee requests access to the network with PIN method, the Enrollee
 +# PIN will need to be entered for the Registrar. PIN request notifications are
 +# sent to hostapd ctrl_iface monitor. In addition, they can be written to a
 +# text file that could be used, e.g., to populate the AP administration UI with
 +# pending PIN requests. If the following variable is set, the PIN requests will
 +# be written to the configured file.
 +#wps_pin_requests=/var/run/hostapd_wps_pin_requests
 +
 +# Device Name
 +# User-friendly description of device; up to 32 octets encoded in UTF-8
 +#device_name=Wireless AP
 +
 +# Manufacturer
 +# The manufacturer of the device (up to 64 ASCII characters)
 +#manufacturer=Company
 +
 +# Model Name
 +# Model of the device (up to 32 ASCII characters)
 +#model_name=WAP
 +
 +# Model Number
 +# Additional device description (up to 32 ASCII characters)
 +#model_number=123
 +
 +# Serial Number
 +# Serial number of the device (up to 32 characters)
 +#serial_number=12345
 +
 +# Primary Device Type
 +# Used format: <categ>-<OUI>-<subcateg>
 +# categ = Category as an integer value
 +# OUI = OUI and type octet as a 4-octet hex-encoded value; 0050F204 for
 +#       default WPS OUI
 +# subcateg = OUI-specific Sub Category as an integer value
 +# Examples:
 +#   1-0050F204-1 (Computer / PC)
 +#   1-0050F204-2 (Computer / Server)
 +#   5-0050F204-1 (Storage / NAS)
 +#   6-0050F204-1 (Network Infrastructure / AP)
 +#device_type=6-0050F204-1
 +
 +# OS Version
 +# 4-octet operating system version number (hex string)
 +#os_version=01020300
 +
 +# Config Methods
 +# List of the supported 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
 +#config_methods=label virtual_display virtual_push_button keypad
 +
 +# WPS capability discovery workaround for PBC with Windows 7
 +# Windows 7 uses incorrect way of figuring out AP's WPS capabilities by acting
 +# as a Registrar and using M1 from the AP. The config methods attribute in that
 +# message is supposed to indicate only the configuration method supported by
 +# the AP in Enrollee role, i.e., to add an external Registrar. For that case,
 +# PBC shall not be used and as such, the PushButton config method is removed
 +# from M1 by default. If pbc_in_m1=1 is included in the configuration file,
 +# the PushButton config method is left in M1 (if included in config_methods
 +# parameter) to allow Windows 7 to use PBC instead of PIN (e.g., from a label
 +# in the AP).
 +#pbc_in_m1=1
 +
 +# Static access point PIN for initial configuration and adding Registrars
 +# If not set, hostapd will not allow external WPS Registrars to control the
 +# access point. The AP PIN can also be set at runtime with hostapd_cli
 +# wps_ap_pin command. Use of temporary (enabled by user action) and random
 +# AP PIN is much more secure than configuring a static AP PIN here. As such,
 +# use of the ap_pin parameter is not recommended if the AP device has means for
 +# displaying a random PIN.
 +#ap_pin=12345670
 +
 +# Skip building of automatic WPS credential
 +# This can be used to allow the automatically generated Credential attribute to
 +# be replaced with pre-configured Credential(s).
 +#skip_cred_build=1
 +
 +# Additional Credential attribute(s)
 +# This option can be used to add pre-configured Credential attributes into M8
 +# message when acting as a Registrar. If skip_cred_build=1, this data will also
 +# be able to override the Credential attribute that would have otherwise been
 +# automatically generated based on network configuration. This configuration
 +# option points to an external file that much contain the WPS Credential
 +# attribute(s) as binary data.
 +#extra_cred=hostapd.cred
 +
 +# Credential processing
 +#   0 = process received credentials internally (default)
 +#   1 = do not process received credentials; just pass them over ctrl_iface to
 +#     external program(s)
 +#   2 = process received credentials internally and pass them over ctrl_iface
 +#     to external program(s)
 +# Note: With wps_cred_processing=1, skip_cred_build should be set to 1 and
 +# extra_cred be used to provide the Credential data for Enrollees.
 +#
 +# wps_cred_processing=1 will disabled automatic updates of hostapd.conf file
 +# both for Credential processing and for marking AP Setup Locked based on
 +# validation failures of AP PIN. An external program is responsible on updating
 +# the configuration appropriately in this case.
 +#wps_cred_processing=0
 +
 +# AP Settings Attributes for M7
 +# By default, hostapd generates the AP Settings Attributes for M7 based on the
 +# current configuration. It is possible to override this by providing a file
 +# with pre-configured attributes. This is similar to extra_cred file format,
 +# but the AP Settings attributes are not encapsulated in a Credential
 +# attribute.
 +#ap_settings=hostapd.ap_settings
 +
 +# WPS UPnP interface
 +# If set, support for external Registrars is enabled.
 +#upnp_iface=br0
 +
 +# Friendly Name (required for UPnP)
 +# Short description for end use. Should be less than 64 characters.
 +#friendly_name=WPS Access Point
 +
 +# Manufacturer URL (optional for UPnP)
 +#manufacturer_url=http://www.example.com/
 +
 +# Model Description (recommended for UPnP)
 +# Long description for end user. Should be less than 128 characters.
 +#model_description=Wireless Access Point
 +
 +# Model URL (optional for UPnP)
 +#model_url=http://www.example.com/model/
 +
 +# Universal Product Code (optional for UPnP)
 +# 12-digit, all-numeric code that identifies the consumer package.
 +#upc=123456789012
 +
++# WPS RF Bands (a = 5G, b = 2.4G, g = 2.4G, ag = dual band, ad = 60 GHz)
 +# This value should be set according to RF band(s) supported by the AP if
 +# hw_mode is not set. For dual band dual concurrent devices, this needs to be
 +# set to ag to allow both RF bands to be advertized.
 +#wps_rf_bands=ag
 +
 +# NFC password token for WPS
 +# These parameters can be used to configure a fixed NFC password token for the
 +# AP. This can be generated, e.g., with nfc_pw_token from wpa_supplicant. When
 +# these parameters are used, the AP is assumed to be deployed with a NFC tag
 +# that includes the matching NFC password token (e.g., written based on the
 +# NDEF record from nfc_pw_token).
 +#
 +#wps_nfc_dev_pw_id: Device Password ID (16..65535)
 +#wps_nfc_dh_pubkey: Hexdump of DH Public Key
 +#wps_nfc_dh_privkey: Hexdump of DH Private Key
 +#wps_nfc_dev_pw: Hexdump of Device Password
 +
 +##### Wi-Fi Direct (P2P) ######################################################
 +
 +# Enable P2P Device management
 +#manage_p2p=1
 +
 +# Allow cross connection
 +#allow_cross_connection=1
 +
 +#### TDLS (IEEE 802.11z-2010) #################################################
 +
 +# Prohibit use of TDLS in this BSS
 +#tdls_prohibit=1
 +
 +# Prohibit use of TDLS Channel Switching in this BSS
 +#tdls_prohibit_chan_switch=1
 +
 +##### IEEE 802.11v-2011 #######################################################
 +
 +# Time advertisement
 +# 0 = disabled (default)
 +# 2 = UTC time at which the TSF timer is 0
 +#time_advertisement=2
 +
 +# Local time zone as specified in 8.3 of IEEE Std 1003.1-2004:
 +# stdoffset[dst[offset][,start[/time],end[/time]]]
 +#time_zone=EST5
 +
 +# WNM-Sleep Mode (extended sleep mode for stations)
 +# 0 = disabled (default)
 +# 1 = enabled (allow stations to use WNM-Sleep Mode)
 +#wnm_sleep_mode=1
 +
 +# BSS Transition Management
 +# 0 = disabled (default)
 +# 1 = enabled
 +#bss_transition=1
 +
 +# Proxy ARP
 +# 0 = disabled (default)
 +# 1 = enabled
 +#proxy_arp=1
 +
++# IPv6 Neighbor Advertisement multicast-to-unicast conversion
++# This can be used with Proxy ARP to allow multicast NAs to be forwarded to
++# associated STAs using link layer unicast delivery.
++# 0 = disabled (default)
++# 1 = enabled
++#na_mcast_to_ucast=0
++
 +##### IEEE 802.11u-2011 #######################################################
 +
 +# Enable Interworking service
 +#interworking=1
 +
 +# Access Network Type
 +# 0 = Private network
 +# 1 = Private network with guest access
 +# 2 = Chargeable public network
 +# 3 = Free public network
 +# 4 = Personal device network
 +# 5 = Emergency services only network
 +# 14 = Test or experimental
 +# 15 = Wildcard
 +#access_network_type=0
 +
 +# Whether the network provides connectivity to the Internet
 +# 0 = Unspecified
 +# 1 = Network provides connectivity to the Internet
 +#internet=1
 +
 +# Additional Step Required for Access
 +# Note: This is only used with open network, i.e., ASRA shall ne set to 0 if
 +# RSN is used.
 +#asra=0
 +
 +# Emergency services reachable
 +#esr=0
 +
 +# Unauthenticated emergency service accessible
 +#uesa=0
 +
 +# Venue Info (optional)
 +# The available values are defined in IEEE Std 802.11u-2011, 7.3.1.34.
 +# Example values (group,type):
 +# 0,0 = Unspecified
 +# 1,7 = Convention Center
 +# 1,13 = Coffee Shop
 +# 2,0 = Unspecified Business
 +# 7,1  Private Residence
 +#venue_group=7
 +#venue_type=1
 +
 +# Homogeneous ESS identifier (optional; dot11HESSID)
 +# If set, this shall be identifical to one of the BSSIDs in the homogeneous
 +# ESS and this shall be set to the same value across all BSSs in homogeneous
 +# ESS.
 +#hessid=02:03:04:05:06:07
 +
 +# Roaming Consortium List
 +# Arbitrary number of Roaming Consortium OIs can be configured with each line
 +# adding a new OI to the list. The first three entries are available through
 +# Beacon and Probe Response frames. Any additional entry will be available only
 +# through ANQP queries. Each OI is between 3 and 15 octets and is configured as
 +# a hexstring.
 +#roaming_consortium=021122
 +#roaming_consortium=2233445566
 +
 +# Venue Name information
 +# This parameter can be used to configure one or more Venue Name Duples for
 +# Venue Name ANQP information. Each entry has a two or three character language
 +# code (ISO-639) separated by colon from the venue name string.
 +# Note that venue_group and venue_type have to be set for Venue Name
 +# 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
 +# network.
 +# format: <network auth type indicator (1-octet hex str)> [redirect URL]
 +# Network Authentication Type Indicator values:
 +# 00 = Acceptance of terms and conditions
 +# 01 = On-line enrollment supported
 +# 02 = http/https redirection
 +# 03 = DNS redirection
 +#network_auth_type=00
 +#network_auth_type=02http://www.example.com/redirect/me/here/
 +
 +# IP Address Type Availability
 +# format: <1-octet encoded value as hex str>
 +# (ipv4_type & 0x3f) << 2 | (ipv6_type & 0x3)
 +# ipv4_type:
 +# 0 = Address type not available
 +# 1 = Public IPv4 address available
 +# 2 = Port-restricted IPv4 address available
 +# 3 = Single NATed private IPv4 address available
 +# 4 = Double NATed private IPv4 address available
 +# 5 = Port-restricted IPv4 address and single NATed IPv4 address available
 +# 6 = Port-restricted IPv4 address and double NATed IPv4 address available
 +# 7 = Availability of the address type is not known
 +# ipv6_type:
 +# 0 = Address type not available
 +# 1 = Address type available
 +# 2 = Availability of the address type not known
 +#ipaddr_type_availability=14
 +
 +# Domain Name
 +# format: <variable-octet str>[,<variable-octet str>]
 +#domain_name=example.com,another.example.com,yet-another.example.com
 +
 +# 3GPP Cellular Network information
 +# format: <MCC1,MNC1>[;<MCC2,MNC2>][;...]
 +#anqp_3gpp_cell_net=244,91;310,026;234,56
 +
 +# NAI Realm information
 +# One or more realm can be advertised. Each nai_realm line adds a new realm to
 +# the set. These parameters provide information for stations using Interworking
 +# network selection to allow automatic connection to a network based on
 +# credentials.
 +# format: <encoding>,<NAI Realm(s)>[,<EAP Method 1>][,<EAP Method 2>][,...]
 +# encoding:
 +#     0 = Realm formatted in accordance with IETF RFC 4282
 +#     1 = UTF-8 formatted character string that is not formatted in
 +#         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
 +# ID 3 = Inner authentication EAP Method Type
 +# ID 5 = Credential Type
 +#     1 = SIM, 2 = USIM, 3 = NFC Secure Element, 4 = Hardware Token,
 +#     5 = Softoken, 6 = Certificate, 7 = username/password, 9 = Anonymous,
 +#     10 = Vendor Specific
 +#nai_realm=0,example.com;example.net
 +# EAP methods EAP-TLS with certificate and EAP-TTLS/MSCHAPv2 with
 +# 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
 +#hs20=1
 +
 +# Disable Downstream Group-Addressed Forwarding (DGAF)
 +# This can be used to configure a network where no group-addressed frames are
 +# allowed. The AP will not forward any group-address frames to the stations and
 +# random GTKs are issued for each station to prevent associated stations from
 +# 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)
 +# separated by colon from the operator friendly name string.
 +#hs20_oper_friendly_name=eng:Example operator
 +#hs20_oper_friendly_name=fin:Esimerkkioperaattori
 +
 +# Connection Capability
 +# This can be used to advertise what type of IP traffic can be sent through the
 +# hotspot (e.g., due to firewall allowing/blocking protocols/ports).
 +# format: <IP Protocol>:<Port Number>:<Status>
 +# IP Protocol: 1 = ICMP, 6 = TCP, 17 = UDP
 +# Port Number: 0..65535
 +# Status: 0 = Closed, 1 = Open, 2 = Unknown
 +# Each hs20_conn_capab line is added to the list of advertised tuples.
 +#hs20_conn_capab=1:0:2
 +#hs20_conn_capab=6:22:1
 +#hs20_conn_capab=17:5060:0
 +
 +# WAN Metrics
 +# format: <WAN Info>:<DL Speed>:<UL Speed>:<DL Load>:<UL Load>:<LMD>
 +# WAN Info: B0-B1: Link Status, B2: Symmetric Link, B3: At Capabity
 +#    (encoded as two hex digits)
 +#    Link Status: 1 = Link up, 2 = Link down, 3 = Link in test state
 +# Downlink Speed: Estimate of WAN backhaul link current downlink speed in kbps;
 +#     1..4294967295; 0 = unknown
 +# Uplink Speed: Estimate of WAN backhaul link current uplink speed in kbps
 +#     1..4294967295; 0 = unknown
 +# Downlink Load: Current load of downlink WAN connection (scaled to 255 = 100%)
 +# Uplink Load: Current load of uplink WAN connection (scaled to 255 = 100%)
 +# Load Measurement Duration: Duration for measuring downlink/uplink load in
 +# tenths of a second (1..65535); 0 if load cannot be determined
 +#hs20_wan_metrics=01:8000:1000:80:240:3000
 +
 +# Operating Class Indication
 +# List of operating classes the BSSes in this ESS use. The Global operating
 +# classes in Table E-4 of IEEE Std 802.11-2012 Annex E define the values that
 +# can be used in this.
 +# format: hexdump of operating class octets
 +# for example, operating classes 81 (2.4 GHz channels 1-13) and 115 (5 GHz
 +# 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=...
 +
++##### Fast Session Transfer (FST) support #####################################
++#
++# The options in this section are only available when the build configuration
++# option CONFIG_FST is set while compiling hostapd. They allow this interface
++# to be a part of FST setup.
++#
++# FST is the transfer of a session from a channel to another channel, in the
++# same or different frequency bands.
++#
++# For detals, see IEEE Std 802.11ad-2012.
++
++# Identifier of an FST Group the interface belongs to.
++#fst_group_id=bond0
++
++# Interface priority within the FST Group.
++# Announcing a higher priority for an interface means declaring it more
++# preferable for FST switch.
++# fst_priority is in 1..255 range with 1 being the lowest priority.
++#fst_priority=100
++
++# Default LLT value for this interface in milliseconds. The value used in case
++# no value provided during session setup. Default is 50 ms.
++# fst_llt is in 1..4294967 range (due to spec limitation, see 10.32.2.2
++# Transitioning between states).
++#fst_llt=100
++
 +##### TESTING OPTIONS #########################################################
 +#
 +# The options in this section are only available when the build configuration
 +# option CONFIG_TESTING_OPTIONS is set while compiling hostapd. They allow
 +# testing some scenarios that are otherwise difficult to reproduce.
 +#
 +# Ignore probe requests sent to hostapd with the given probability, must be a
 +# floating point number in the range [0, 1).
 +#ignore_probe_probability=0.0
 +#
 +# Ignore authentication frames with the given probability
 +#ignore_auth_probability=0.0
 +#
 +# Ignore association requests with the given probability
 +#ignore_assoc_probability=0.0
 +#
 +# Ignore reassociation requests with the given probability
 +#ignore_reassoc_probability=0.0
 +#
 +# Corrupt Key MIC in GTK rekey EAPOL-Key frames with the given probability
 +#corrupt_gtk_rekey_mic_probability=0.0
 +
 +##### Multiple BSSID support ##################################################
 +#
 +# Above configuration is using the default interface (wlan#, or multi-SSID VLAN
 +# interfaces). Other BSSIDs can be added by using separator 'bss' with
 +# default interface name to be allocated for the data packets of the new BSS.
 +#
 +# hostapd will generate BSSID mask based on the BSSIDs that are
 +# configured. hostapd will verify that dev_addr & MASK == dev_addr. If this is
 +# not the case, the MAC address of the radio must be changed before starting
 +# hostapd (ifconfig wlan0 hw ether <MAC addr>). If a BSSID is configured for
 +# every secondary BSS, this limitation is not applied at hostapd and other
 +# masks may be used if the driver supports them (e.g., swap the locally
 +# administered bit)
 +#
 +# BSSIDs are assigned in order to each BSS, unless an explicit BSSID is
 +# specified using the 'bssid' parameter.
 +# If an explicit BSSID is specified, it must be chosen such that it:
 +# - results in a valid MASK that covers it and the dev_addr
 +# - 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.
 +#
 +#bss=wlan0_0
 +#ssid=test2
 +# most of the above items can be used here (apart from radio interface specific
 +# items, like channel)
 +
 +#bss=wlan0_1
 +#bssid=00:13:10:95:fe:0b
 +# ...
index 3f00cbbf0cc7a9b1604a698866df0b9c39d66514,0000000000000000000000000000000000000000..46c2f37e4601747347c17c7345dc918aed3891f9
mode 100644,000000..100644
--- /dev/null
@@@ -1,1392 -1,0 +1,1461 @@@
- static const char *hostapd_cli_version =
 +/*
 + * hostapd - command line interface for hostapd daemon
 + * 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 "includes.h"
 +#include <dirent.h>
 +
 +#include "common/wpa_ctrl.h"
++#include "common/ieee802_11_defs.h"
 +#include "utils/common.h"
 +#include "utils/eloop.h"
 +#include "utils/edit.h"
 +#include "common/version.h"
 +
 +
- static const char *hostapd_cli_license =
++static const char *const hostapd_cli_version =
 +"hostapd_cli v" VERSION_STR "\n"
 +"Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi> and contributors";
 +
 +
- static const char *hostapd_cli_full_license =
++static const char *const hostapd_cli_license =
 +"This software may be distributed under the terms of the BSD license.\n"
 +"See README for more details.\n";
 +
- static const char *commands_help =
++static const char *const hostapd_cli_full_license =
 +"This software may be distributed under the terms of the BSD license.\n"
 +"\n"
 +"Redistribution and use in source and binary forms, with or without\n"
 +"modification, are permitted provided that the following conditions are\n"
 +"met:\n"
 +"\n"
 +"1. Redistributions of source code must retain the above copyright\n"
 +"   notice, this list of conditions and the following disclaimer.\n"
 +"\n"
 +"2. Redistributions in binary form must reproduce the above copyright\n"
 +"   notice, this list of conditions and the following disclaimer in the\n"
 +"   documentation and/or other materials provided with the distribution.\n"
 +"\n"
 +"3. Neither the name(s) of the above-listed copyright holder(s) nor the\n"
 +"   names of its contributors may be used to endorse or promote products\n"
 +"   derived from this software without specific prior written permission.\n"
 +"\n"
 +"THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n"
 +"\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n"
 +"LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n"
 +"A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n"
 +"OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n"
 +"SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n"
 +"LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n"
 +"DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n"
 +"THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n"
 +"(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n"
 +"OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
 +"\n";
 +
-               "                   [-G<ping interval>] [command..]\n"
++static const char *const commands_help =
 +"Commands:\n"
 +"   mib                  get MIB variables (dot1x, dot11, radius)\n"
 +"   sta <addr>           get MIB variables for one station\n"
 +"   all_sta              get MIB variables for all stations\n"
 +"   new_sta <addr>       add a new station\n"
 +"   deauthenticate <addr>  deauthenticate a station\n"
 +"   disassociate <addr>  disassociate a station\n"
 +#ifdef CONFIG_IEEE80211W
 +"   sa_query <addr>      send SA Query to a station\n"
 +#endif /* CONFIG_IEEE80211W */
 +#ifdef CONFIG_WPS
 +"   wps_pin <uuid> <pin> [timeout] [addr]  add WPS Enrollee PIN\n"
 +"   wps_check_pin <PIN>  verify PIN checksum\n"
 +"   wps_pbc              indicate button pushed to initiate PBC\n"
 +"   wps_cancel           cancel the pending WPS operation\n"
 +#ifdef CONFIG_WPS_NFC
 +"   wps_nfc_tag_read <hexdump>  report read NFC tag with WPS data\n"
 +"   wps_nfc_config_token <WPS/NDEF>  build NFC configuration token\n"
 +"   wps_nfc_token <WPS/NDEF/enable/disable>  manager NFC password token\n"
 +#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"
 +"   interface [ifname]   show interfaces/select interface\n"
 +"   level <debug level>  change debug level\n"
 +"   license              show full hostapd_cli license\n"
 +"   quit                 exit hostapd_cli\n";
 +
 +static struct wpa_ctrl *ctrl_conn;
 +static int hostapd_cli_quit = 0;
 +static int hostapd_cli_attached = 0;
 +
 +#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 const char *client_socket_dir = NULL;
 +
 +static char *ctrl_ifname = NULL;
 +static const char *pid_file = NULL;
 +static const char *action_file = NULL;
 +static int ping_interval = 5;
 +static int interactive = 0;
 +
 +
 +static void usage(void)
 +{
 +      fprintf(stderr, "%s\n", hostapd_cli_version);
 +      fprintf(stderr,
 +              "\n"
 +              "usage: hostapd_cli [-p<path>] [-i<ifname>] [-hvB] "
 +              "[-a<path>] \\\n"
-       ctrl_conn = wpa_ctrl_open(cfile);
++              "                   [-P<pid file>] [-G<ping interval>] [command..]\n"
 +              "\n"
 +              "Options:\n"
 +              "   -h           help (show this usage text)\n"
 +              "   -v           shown version information\n"
 +              "   -p<path>     path to find control sockets (default: "
 +              "/var/run/hostapd)\n"
++              "   -s<dir_path> dir path to open client sockets (default: "
++              CONFIG_CTRL_IFACE_DIR ")\n"
 +              "   -a<file>     run in daemon mode executing the action file "
 +              "based on events\n"
 +              "                from hostapd\n"
 +              "   -B           run a daemon in the background\n"
 +              "   -i<ifname>   Interface to listen on (default: first "
 +              "interface found in the\n"
 +              "                socket path)\n\n"
 +              "%s",
 +              commands_help);
 +}
 +
 +
 +static struct wpa_ctrl * hostapd_cli_open_connection(const char *ifname)
 +{
 +      char *cfile;
 +      int flen;
 +
 +      if (ifname == NULL)
 +              return NULL;
 +
 +      flen = strlen(ctrl_iface_dir) + strlen(ifname) + 2;
 +      cfile = malloc(flen);
 +      if (cfile == NULL)
 +              return NULL;
 +      snprintf(cfile, flen, "%s/%s", ctrl_iface_dir, ifname);
 +
-       char ssid_hex[2 * 32 + 1];
++      if (client_socket_dir && client_socket_dir[0] &&
++          access(client_socket_dir, F_OK) < 0) {
++              perror(client_socket_dir);
++              free(cfile);
++              return NULL;
++      }
++
++      ctrl_conn = wpa_ctrl_open2(cfile, client_socket_dir);
 +      free(cfile);
 +      return ctrl_conn;
 +}
 +
 +
 +static void hostapd_cli_close_connection(void)
 +{
 +      if (ctrl_conn == NULL)
 +              return;
 +
 +      if (hostapd_cli_attached) {
 +              wpa_ctrl_detach(ctrl_conn);
 +              hostapd_cli_attached = 0;
 +      }
 +      wpa_ctrl_close(ctrl_conn);
 +      ctrl_conn = NULL;
 +}
 +
 +
 +static void hostapd_cli_msg_cb(char *msg, size_t len)
 +{
 +      printf("%s\n", msg);
 +}
 +
 +
 +static int _wpa_ctrl_command(struct wpa_ctrl *ctrl, char *cmd, int print)
 +{
 +      char buf[4096];
 +      size_t len;
 +      int ret;
 +
 +      if (ctrl_conn == NULL) {
 +              printf("Not connected to hostapd - command dropped.\n");
 +              return -1;
 +      }
 +      len = sizeof(buf) - 1;
 +      ret = wpa_ctrl_request(ctrl, cmd, strlen(cmd), buf, &len,
 +                             hostapd_cli_msg_cb);
 +      if (ret == -2) {
 +              printf("'%s' command timed out.\n", cmd);
 +              return -2;
 +      } else if (ret < 0) {
 +              printf("'%s' command failed.\n", cmd);
 +              return -1;
 +      }
 +      if (print) {
 +              buf[len] = '\0';
 +              printf("%s", buf);
 +      }
 +      return 0;
 +}
 +
 +
 +static inline int wpa_ctrl_command(struct wpa_ctrl *ctrl, char *cmd)
 +{
 +      return _wpa_ctrl_command(ctrl, cmd, 1);
 +}
 +
 +
 +static int hostapd_cli_cmd_ping(struct wpa_ctrl *ctrl, int argc, char *argv[])
 +{
 +      return wpa_ctrl_command(ctrl, "PING");
 +}
 +
 +
 +static int hostapd_cli_cmd_relog(struct wpa_ctrl *ctrl, int argc, char *argv[])
 +{
 +      return wpa_ctrl_command(ctrl, "RELOG");
 +}
 +
 +
 +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");
 +}
 +
 +
 +static int hostapd_cli_exec(const char *program, const char *arg1,
 +                          const char *arg2)
 +{
 +      char *arg;
 +      size_t len;
 +      int res;
 +
 +      len = os_strlen(arg1) + os_strlen(arg2) + 2;
 +      arg = os_malloc(len);
 +      if (arg == NULL)
 +              return -1;
 +      os_snprintf(arg, len, "%s %s", arg1, arg2);
 +      res = os_exec(program, arg, 1);
 +      os_free(arg);
 +
 +      return res;
 +}
 +
 +
 +static void hostapd_cli_action_process(char *msg, size_t len)
 +{
 +      const char *pos;
 +
 +      pos = msg;
 +      if (*pos == '<') {
 +              pos = os_strchr(pos, '>');
 +              if (pos)
 +                      pos++;
 +              else
 +                      pos = msg;
 +      }
 +
 +      hostapd_cli_exec(action_file, ctrl_ifname, pos);
 +}
 +
 +
 +static int hostapd_cli_cmd_sta(struct wpa_ctrl *ctrl, int argc, char *argv[])
 +{
 +      char buf[64];
 +      if (argc < 1) {
 +              printf("Invalid 'sta' command - at least one argument, STA "
 +                     "address, is required.\n");
 +              return -1;
 +      }
 +      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);
 +}
 +
 +
 +static int hostapd_cli_cmd_new_sta(struct wpa_ctrl *ctrl, int argc,
 +                                 char *argv[])
 +{
 +      char buf[64];
 +      if (argc != 1) {
 +              printf("Invalid 'new_sta' command - exactly one argument, STA "
 +                     "address, is required.\n");
 +              return -1;
 +      }
 +      snprintf(buf, sizeof(buf), "NEW_STA %s", argv[0]);
 +      return wpa_ctrl_command(ctrl, buf);
 +}
 +
 +
 +static int hostapd_cli_cmd_deauthenticate(struct wpa_ctrl *ctrl, int argc,
 +                                        char *argv[])
 +{
 +      char buf[64];
 +      if (argc < 1) {
 +              printf("Invalid 'deauthenticate' command - exactly one "
 +                     "argument, STA address, is required.\n");
 +              return -1;
 +      }
 +      if (argc > 1)
 +              os_snprintf(buf, sizeof(buf), "DEAUTHENTICATE %s %s",
 +                          argv[0], argv[1]);
 +      else
 +              os_snprintf(buf, sizeof(buf), "DEAUTHENTICATE %s", argv[0]);
 +      return wpa_ctrl_command(ctrl, buf);
 +}
 +
 +
 +static int hostapd_cli_cmd_disassociate(struct wpa_ctrl *ctrl, int argc,
 +                                      char *argv[])
 +{
 +      char buf[64];
 +      if (argc < 1) {
 +              printf("Invalid 'disassociate' command - exactly one "
 +                     "argument, STA address, is required.\n");
 +              return -1;
 +      }
 +      if (argc > 1)
 +              os_snprintf(buf, sizeof(buf), "DISASSOCIATE %s %s",
 +                          argv[0], argv[1]);
 +      else
 +              os_snprintf(buf, sizeof(buf), "DISASSOCIATE %s", argv[0]);
 +      return wpa_ctrl_command(ctrl, buf);
 +}
 +
 +
 +#ifdef CONFIG_IEEE80211W
 +static int hostapd_cli_cmd_sa_query(struct wpa_ctrl *ctrl, int argc,
 +                                  char *argv[])
 +{
 +      char buf[64];
 +      if (argc != 1) {
 +              printf("Invalid 'sa_query' command - exactly one argument, "
 +                     "STA address, is required.\n");
 +              return -1;
 +      }
 +      snprintf(buf, sizeof(buf), "SA_QUERY %s", argv[0]);
 +      return wpa_ctrl_command(ctrl, buf);
 +}
 +#endif /* CONFIG_IEEE80211W */
 +
 +
 +#ifdef CONFIG_WPS
 +static int hostapd_cli_cmd_wps_pin(struct wpa_ctrl *ctrl, int argc,
 +                                 char *argv[])
 +{
 +      char buf[256];
 +      if (argc < 2) {
 +              printf("Invalid 'wps_pin' command - at least two arguments, "
 +                     "UUID and PIN, are required.\n");
 +              return -1;
 +      }
 +      if (argc > 3)
 +              snprintf(buf, sizeof(buf), "WPS_PIN %s %s %s %s",
 +                       argv[0], argv[1], argv[2], argv[3]);
 +      else if (argc > 2)
 +              snprintf(buf, sizeof(buf), "WPS_PIN %s %s %s",
 +                       argv[0], argv[1], argv[2]);
 +      else
 +              snprintf(buf, sizeof(buf), "WPS_PIN %s %s", argv[0], argv[1]);
 +      return wpa_ctrl_command(ctrl, buf);
 +}
 +
 +
 +static int hostapd_cli_cmd_wps_check_pin(struct wpa_ctrl *ctrl, int argc,
 +                                       char *argv[])
 +{
 +      char cmd[256];
 +      int res;
 +
 +      if (argc != 1 && argc != 2) {
 +              printf("Invalid WPS_CHECK_PIN command: needs one argument:\n"
 +                     "- PIN to be verified\n");
 +              return -1;
 +      }
 +
 +      if (argc == 2)
 +              res = os_snprintf(cmd, sizeof(cmd), "WPS_CHECK_PIN %s %s",
 +                                argv[0], argv[1]);
 +      else
 +              res = os_snprintf(cmd, sizeof(cmd), "WPS_CHECK_PIN %s",
 +                                argv[0]);
 +      if (os_snprintf_error(sizeof(cmd), res)) {
 +              printf("Too long WPS_CHECK_PIN command.\n");
 +              return -1;
 +      }
 +      return wpa_ctrl_command(ctrl, cmd);
 +}
 +
 +
 +static int hostapd_cli_cmd_wps_pbc(struct wpa_ctrl *ctrl, int argc,
 +                                 char *argv[])
 +{
 +      return wpa_ctrl_command(ctrl, "WPS_PBC");
 +}
 +
 +
 +static int hostapd_cli_cmd_wps_cancel(struct wpa_ctrl *ctrl, int argc,
 +                                    char *argv[])
 +{
 +      return wpa_ctrl_command(ctrl, "WPS_CANCEL");
 +}
 +
 +
 +#ifdef CONFIG_WPS_NFC
 +static int hostapd_cli_cmd_wps_nfc_tag_read(struct wpa_ctrl *ctrl, int argc,
 +                                          char *argv[])
 +{
 +      int ret;
 +      char *buf;
 +      size_t buflen;
 +
 +      if (argc != 1) {
 +              printf("Invalid 'wps_nfc_tag_read' command - one argument "
 +                     "is required.\n");
 +              return -1;
 +      }
 +
 +      buflen = 18 + os_strlen(argv[0]);
 +      buf = os_malloc(buflen);
 +      if (buf == NULL)
 +              return -1;
 +      os_snprintf(buf, buflen, "WPS_NFC_TAG_READ %s", argv[0]);
 +
 +      ret = wpa_ctrl_command(ctrl, buf);
 +      os_free(buf);
 +
 +      return ret;
 +}
 +
 +
 +static int hostapd_cli_cmd_wps_nfc_config_token(struct wpa_ctrl *ctrl,
 +                                              int argc, char *argv[])
 +{
 +      char cmd[64];
 +      int res;
 +
 +      if (argc != 1) {
 +              printf("Invalid 'wps_nfc_config_token' command - one argument "
 +                     "is required.\n");
 +              return -1;
 +      }
 +
 +      res = os_snprintf(cmd, sizeof(cmd), "WPS_NFC_CONFIG_TOKEN %s",
 +                        argv[0]);
 +      if (os_snprintf_error(sizeof(cmd), res)) {
 +              printf("Too long WPS_NFC_CONFIG_TOKEN command.\n");
 +              return -1;
 +      }
 +      return wpa_ctrl_command(ctrl, cmd);
 +}
 +
 +
 +static int hostapd_cli_cmd_wps_nfc_token(struct wpa_ctrl *ctrl,
 +                                       int argc, char *argv[])
 +{
 +      char cmd[64];
 +      int res;
 +
 +      if (argc != 1) {
 +              printf("Invalid 'wps_nfc_token' command - one argument is "
 +                     "required.\n");
 +              return -1;
 +      }
 +
 +      res = os_snprintf(cmd, sizeof(cmd), "WPS_NFC_TOKEN %s", argv[0]);
 +      if (os_snprintf_error(sizeof(cmd), res)) {
 +              printf("Too long WPS_NFC_TOKEN command.\n");
 +              return -1;
 +      }
 +      return wpa_ctrl_command(ctrl, cmd);
 +}
 +
 +
 +static int hostapd_cli_cmd_nfc_get_handover_sel(struct wpa_ctrl *ctrl,
 +                                              int argc, char *argv[])
 +{
 +      char cmd[64];
 +      int res;
 +
 +      if (argc != 2) {
 +              printf("Invalid 'nfc_get_handover_sel' command - two arguments "
 +                     "are required.\n");
 +              return -1;
 +      }
 +
 +      res = os_snprintf(cmd, sizeof(cmd), "NFC_GET_HANDOVER_SEL %s %s",
 +                        argv[0], argv[1]);
 +      if (os_snprintf_error(sizeof(cmd), res)) {
 +              printf("Too long NFC_GET_HANDOVER_SEL command.\n");
 +              return -1;
 +      }
 +      return wpa_ctrl_command(ctrl, cmd);
 +}
 +
 +#endif /* CONFIG_WPS_NFC */
 +
 +
 +static int hostapd_cli_cmd_wps_ap_pin(struct wpa_ctrl *ctrl, int argc,
 +                                    char *argv[])
 +{
 +      char buf[64];
 +      if (argc < 1) {
 +              printf("Invalid 'wps_ap_pin' command - at least one argument "
 +                     "is required.\n");
 +              return -1;
 +      }
 +      if (argc > 2)
 +              snprintf(buf, sizeof(buf), "WPS_AP_PIN %s %s %s",
 +                       argv[0], argv[1], argv[2]);
 +      else if (argc > 1)
 +              snprintf(buf, sizeof(buf), "WPS_AP_PIN %s %s",
 +                       argv[0], argv[1]);
 +      else
 +              snprintf(buf, sizeof(buf), "WPS_AP_PIN %s", argv[0]);
 +      return wpa_ctrl_command(ctrl, buf);
 +}
 +
 +
 +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[])
 +{
 +      char buf[256];
-       for (i = 0; i < 32; i++) {
++      char ssid_hex[2 * SSID_MAX_LEN + 1];
 +      char key_hex[2 * 64 + 1];
 +      int i;
 +
 +      if (argc < 1) {
 +              printf("Invalid 'wps_config' command - at least two arguments "
 +                     "are required.\n");
 +              return -1;
 +      }
 +
 +      ssid_hex[0] = '\0';
- static struct hostapd_cli_cmd hostapd_cli_commands[] = {
++      for (i = 0; i < SSID_MAX_LEN; i++) {
 +              if (argv[0][i] == '\0')
 +                      break;
 +              os_snprintf(&ssid_hex[i * 2], 3, "%02x", argv[0][i]);
 +      }
 +
 +      key_hex[0] = '\0';
 +      if (argc > 3) {
 +              for (i = 0; i < 64; i++) {
 +                      if (argv[3][i] == '\0')
 +                              break;
 +                      os_snprintf(&key_hex[i * 2], 3, "%02x",
 +                                  argv[3][i]);
 +              }
 +      }
 +
 +      if (argc > 3)
 +              snprintf(buf, sizeof(buf), "WPS_CONFIG %s %s %s %s",
 +                       ssid_hex, argv[1], argv[2], key_hex);
 +      else if (argc > 2)
 +              snprintf(buf, sizeof(buf), "WPS_CONFIG %s %s %s",
 +                       ssid_hex, argv[1], argv[2]);
 +      else
 +              snprintf(buf, sizeof(buf), "WPS_CONFIG %s %s",
 +                       ssid_hex, argv[1]);
 +      return wpa_ctrl_command(ctrl, buf);
 +}
 +#endif /* CONFIG_WPS */
 +
 +
 +static int hostapd_cli_cmd_disassoc_imminent(struct wpa_ctrl *ctrl, int argc,
 +                                           char *argv[])
 +{
 +      char buf[300];
 +      int res;
 +
 +      if (argc < 2) {
 +              printf("Invalid 'disassoc_imminent' command - two arguments "
 +                     "(STA addr and Disassociation Timer) are needed\n");
 +              return -1;
 +      }
 +
 +      res = os_snprintf(buf, sizeof(buf), "DISASSOC_IMMINENT %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_ess_disassoc(struct wpa_ctrl *ctrl, int argc,
 +                                      char *argv[])
 +{
 +      char buf[300];
 +      int res;
 +
 +      if (argc < 3) {
 +              printf("Invalid 'ess_disassoc' command - three arguments (STA "
 +                     "addr, disassoc timer, and URL) are needed\n");
 +              return -1;
 +      }
 +
 +      res = os_snprintf(buf, sizeof(buf), "ESS_DISASSOC %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_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);
 +}
 +
 +
 +static int hostapd_cli_cmd_get_config(struct wpa_ctrl *ctrl, int argc,
 +                                    char *argv[])
 +{
 +      return wpa_ctrl_command(ctrl, "GET_CONFIG");
 +}
 +
 +
 +static int wpa_ctrl_command_sta(struct wpa_ctrl *ctrl, char *cmd,
 +                              char *addr, size_t addr_len)
 +{
 +      char buf[4096], *pos;
 +      size_t len;
 +      int ret;
 +
 +      if (ctrl_conn == NULL) {
 +              printf("Not connected to hostapd - command dropped.\n");
 +              return -1;
 +      }
 +      len = sizeof(buf) - 1;
 +      ret = wpa_ctrl_request(ctrl, cmd, strlen(cmd), buf, &len,
 +                             hostapd_cli_msg_cb);
 +      if (ret == -2) {
 +              printf("'%s' command timed out.\n", cmd);
 +              return -2;
 +      } else if (ret < 0) {
 +              printf("'%s' command failed.\n", cmd);
 +              return -1;
 +      }
 +
 +      buf[len] = '\0';
 +      if (memcmp(buf, "FAIL", 4) == 0)
 +              return -1;
 +      printf("%s", buf);
 +
 +      pos = buf;
 +      while (*pos != '\0' && *pos != '\n')
 +              pos++;
 +      *pos = '\0';
 +      os_strlcpy(addr, buf, addr_len);
 +      return 0;
 +}
 +
 +
 +static int hostapd_cli_cmd_all_sta(struct wpa_ctrl *ctrl, int argc,
 +                                 char *argv[])
 +{
 +      char addr[32], cmd[64];
 +
 +      if (wpa_ctrl_command_sta(ctrl, "STA-FIRST", addr, sizeof(addr)))
 +              return 0;
 +      do {
 +              snprintf(cmd, sizeof(cmd), "STA-NEXT %s", addr);
 +      } while (wpa_ctrl_command_sta(ctrl, cmd, addr, sizeof(addr)) == 0);
 +
 +      return -1;
 +}
 +
 +
 +static int hostapd_cli_cmd_help(struct wpa_ctrl *ctrl, int argc, char *argv[])
 +{
 +      printf("%s", commands_help);
 +      return 0;
 +}
 +
 +
 +static int hostapd_cli_cmd_license(struct wpa_ctrl *ctrl, int argc,
 +                                 char *argv[])
 +{
 +      printf("%s\n\n%s\n", hostapd_cli_version, hostapd_cli_full_license);
 +      return 0;
 +}
 +
 +
 +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;
 +      if (interactive)
 +              eloop_terminate();
 +      return 0;
 +}
 +
 +
 +static int hostapd_cli_cmd_level(struct wpa_ctrl *ctrl, int argc, char *argv[])
 +{
 +      char cmd[256];
 +      if (argc != 1) {
 +              printf("Invalid LEVEL command: needs one argument (debug "
 +                     "level)\n");
 +              return 0;
 +      }
 +      snprintf(cmd, sizeof(cmd), "LEVEL %s", argv[0]);
 +      return wpa_ctrl_command(ctrl, cmd);
 +}
 +
 +
 +static void hostapd_cli_list_interfaces(struct wpa_ctrl *ctrl)
 +{
 +      struct dirent *dent;
 +      DIR *dir;
 +
 +      dir = opendir(ctrl_iface_dir);
 +      if (dir == NULL) {
 +              printf("Control interface directory '%s' could not be "
 +                     "openned.\n", ctrl_iface_dir);
 +              return;
 +      }
 +
 +      printf("Available interfaces:\n");
 +      while ((dent = readdir(dir))) {
 +              if (strcmp(dent->d_name, ".") == 0 ||
 +                  strcmp(dent->d_name, "..") == 0)
 +                      continue;
 +              printf("%s\n", dent->d_name);
 +      }
 +      closedir(dir);
 +}
 +
 +
 +static int hostapd_cli_cmd_interface(struct wpa_ctrl *ctrl, int argc,
 +                                   char *argv[])
 +{
 +      if (argc < 1) {
 +              hostapd_cli_list_interfaces(ctrl);
 +              return 0;
 +      }
 +
 +      hostapd_cli_close_connection();
 +      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);
 +              if (wpa_ctrl_attach(ctrl_conn) == 0) {
 +                      hostapd_cli_attached = 1;
 +              } else {
 +                      printf("Warning: Failed to attach to "
 +                             "hostapd.\n");
 +              }
 +      } else {
 +              printf("Could not connect to interface '%s' - re-trying\n",
 +                      ctrl_ifname);
 +      }
 +      return 0;
 +}
 +
 +
 +static int hostapd_cli_cmd_set(struct wpa_ctrl *ctrl, int argc, char *argv[])
 +{
 +      char cmd[256];
 +      int res;
 +
 +      if (argc != 2) {
 +              printf("Invalid SET command: needs two arguments (variable "
 +                     "name and value)\n");
 +              return -1;
 +      }
 +
 +      res = os_snprintf(cmd, sizeof(cmd), "SET %s %s", argv[0], argv[1]);
 +      if (os_snprintf_error(sizeof(cmd), res)) {
 +              printf("Too long SET command.\n");
 +              return -1;
 +      }
 +      return wpa_ctrl_command(ctrl, cmd);
 +}
 +
 +
 +static int hostapd_cli_cmd_get(struct wpa_ctrl *ctrl, int argc, char *argv[])
 +{
 +      char cmd[256];
 +      int res;
 +
 +      if (argc != 1) {
 +              printf("Invalid GET command: needs one argument (variable "
 +                     "name)\n");
 +              return -1;
 +      }
 +
 +      res = os_snprintf(cmd, sizeof(cmd), "GET %s", argv[0]);
 +      if (os_snprintf_error(sizeof(cmd), res)) {
 +              printf("Too long GET command.\n");
 +              return -1;
 +      }
 +      return wpa_ctrl_command(ctrl, cmd);
 +}
 +
 +
++#ifdef CONFIG_FST
++static int hostapd_cli_cmd_fst(struct wpa_ctrl *ctrl, int argc, char *argv[])
++{
++      char cmd[256];
++      int res;
++      int i;
++      int total;
++
++      if (argc <= 0) {
++              printf("FST command: parameters are required.\n");
++              return -1;
++      }
++
++      total = os_snprintf(cmd, sizeof(cmd), "FST-MANAGER");
++
++      for (i = 0; i < argc; i++) {
++              res = os_snprintf(cmd + total, sizeof(cmd) - total, " %s",
++                                argv[i]);
++              if (os_snprintf_error(sizeof(cmd) - total, res)) {
++                      printf("Too long fst command.\n");
++                      return -1;
++              }
++              total += res;
++      }
++      return wpa_ctrl_command(ctrl, cmd);
++}
++#endif /* CONFIG_FST */
++
++
 +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");
 +}
 +
 +
++static int hostapd_cli_cmd_log_level(struct wpa_ctrl *ctrl, int argc,
++                                   char *argv[])
++{
++      char cmd[256];
++      int res;
++
++      res = os_snprintf(cmd, sizeof(cmd), "LOG_LEVEL%s%s%s%s",
++                        argc >= 1 ? " " : "",
++                        argc >= 1 ? argv[0] : "",
++                        argc == 2 ? " " : "",
++                        argc == 2 ? argv[1] : "");
++      if (os_snprintf_error(sizeof(cmd), res)) {
++              printf("Too long option\n");
++              return -1;
++      }
++      return wpa_ctrl_command(ctrl, cmd);
++}
++
++
 +struct hostapd_cli_cmd {
 +      const char *cmd;
 +      int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]);
 +};
 +
-       struct hostapd_cli_cmd *cmd, *match = NULL;
++static const 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 },
 +      { "deauthenticate", hostapd_cli_cmd_deauthenticate },
 +      { "disassociate", hostapd_cli_cmd_disassociate },
 +#ifdef CONFIG_IEEE80211W
 +      { "sa_query", hostapd_cli_cmd_sa_query },
 +#endif /* CONFIG_IEEE80211W */
 +#ifdef CONFIG_WPS
 +      { "wps_pin", hostapd_cli_cmd_wps_pin },
 +      { "wps_check_pin", hostapd_cli_cmd_wps_check_pin },
 +      { "wps_pbc", hostapd_cli_cmd_wps_pbc },
 +      { "wps_cancel", hostapd_cli_cmd_wps_cancel },
 +#ifdef CONFIG_WPS_NFC
 +      { "wps_nfc_tag_read", hostapd_cli_cmd_wps_nfc_tag_read },
 +      { "wps_nfc_config_token", hostapd_cli_cmd_wps_nfc_config_token },
 +      { "wps_nfc_token", hostapd_cli_cmd_wps_nfc_token },
 +      { "nfc_get_handover_sel", hostapd_cli_cmd_nfc_get_handover_sel },
 +#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 },
++#ifdef CONFIG_FST
++      { "fst", hostapd_cli_cmd_fst },
++#endif /* CONFIG_FST */
 +      { "level", hostapd_cli_cmd_level },
 +      { "license", hostapd_cli_cmd_license },
 +      { "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 },
++      { "log_level", hostapd_cli_cmd_log_level },
 +      { NULL, NULL }
 +};
 +
 +
 +static void wpa_request(struct wpa_ctrl *ctrl, int argc, char *argv[])
 +{
-               c = getopt(argc, argv, "a:BhG:i:p:v");
++      const struct hostapd_cli_cmd *cmd, *match = NULL;
 +      int count;
 +
 +      count = 0;
 +      cmd = hostapd_cli_commands;
 +      while (cmd->cmd) {
 +              if (strncasecmp(cmd->cmd, argv[0], strlen(argv[0])) == 0) {
 +                      match = cmd;
 +                      if (os_strcasecmp(cmd->cmd, argv[0]) == 0) {
 +                              /* we have an exact match */
 +                              count = 1;
 +                              break;
 +                      }
 +                      count++;
 +              }
 +              cmd++;
 +      }
 +
 +      if (count > 1) {
 +              printf("Ambiguous command '%s'; possible commands:", argv[0]);
 +              cmd = hostapd_cli_commands;
 +              while (cmd->cmd) {
 +                      if (strncasecmp(cmd->cmd, argv[0], strlen(argv[0])) ==
 +                          0) {
 +                              printf(" %s", cmd->cmd);
 +                      }
 +                      cmd++;
 +              }
 +              printf("\n");
 +      } else if (count == 0) {
 +              printf("Unknown command '%s'\n", argv[0]);
 +      } else {
 +              match->handler(ctrl, argc - 1, &argv[1]);
 +      }
 +}
 +
 +
 +static void hostapd_cli_recv_pending(struct wpa_ctrl *ctrl, int in_read,
 +                                   int action_monitor)
 +{
 +      int first = 1;
 +      if (ctrl_conn == NULL)
 +              return;
 +      while (wpa_ctrl_pending(ctrl)) {
 +              char buf[256];
 +              size_t len = sizeof(buf) - 1;
 +              if (wpa_ctrl_recv(ctrl, buf, &len) == 0) {
 +                      buf[len] = '\0';
 +                      if (action_monitor)
 +                              hostapd_cli_action_process(buf, len);
 +                      else {
 +                              if (in_read && first)
 +                                      printf("\n");
 +                              first = 0;
 +                              printf("%s\n", buf);
 +                      }
 +              } else {
 +                      printf("Could not read pending message.\n");
 +                      break;
 +              }
 +      }
 +}
 +
 +
 +#define max_args 10
 +
 +static int tokenize_cmd(char *cmd, char *argv[])
 +{
 +      char *pos;
 +      int argc = 0;
 +
 +      pos = cmd;
 +      for (;;) {
 +              while (*pos == ' ')
 +                      pos++;
 +              if (*pos == '\0')
 +                      break;
 +              argv[argc] = pos;
 +              argc++;
 +              if (argc == max_args)
 +                      break;
 +              if (*pos == '"') {
 +                      char *pos2 = os_strrchr(pos, '"');
 +                      if (pos2)
 +                              pos = pos2 + 1;
 +              }
 +              while (*pos != '\0' && *pos != ' ')
 +                      pos++;
 +              if (*pos == ' ')
 +                      *pos++ = '\0';
 +      }
 +
 +      return argc;
 +}
 +
 +
 +static void hostapd_cli_ping(void *eloop_ctx, void *timeout_ctx)
 +{
 +      if (ctrl_conn && _wpa_ctrl_command(ctrl_conn, "PING", 0)) {
 +              printf("Connection to hostapd lost - trying to reconnect\n");
 +              hostapd_cli_close_connection();
 +      }
 +      if (!ctrl_conn) {
 +              ctrl_conn = hostapd_cli_open_connection(ctrl_ifname);
 +              if (ctrl_conn) {
 +                      printf("Connection to hostapd re-established\n");
 +                      if (wpa_ctrl_attach(ctrl_conn) == 0) {
 +                              hostapd_cli_attached = 1;
 +                      } else {
 +                              printf("Warning: Failed to attach to "
 +                                     "hostapd.\n");
 +                      }
 +              }
 +      }
 +      if (ctrl_conn)
 +              hostapd_cli_recv_pending(ctrl_conn, 1, 0);
 +      eloop_register_timeout(ping_interval, 0, hostapd_cli_ping, NULL, NULL);
 +}
 +
 +
 +static void hostapd_cli_eloop_terminate(int sig, void *signal_ctx)
 +{
 +      eloop_terminate();
 +}
 +
 +
 +static void hostapd_cli_edit_cmd_cb(void *ctx, char *cmd)
 +{
 +      char *argv[max_args];
 +      int argc;
 +      argc = tokenize_cmd(cmd, argv);
 +      if (argc)
 +              wpa_request(ctrl_conn, argc, argv);
 +}
 +
 +
 +static void hostapd_cli_edit_eof_cb(void *ctx)
 +{
 +      eloop_terminate();
 +}
 +
 +
 +static void hostapd_cli_interactive(void)
 +{
 +      printf("\nInteractive mode\n\n");
 +
 +      eloop_register_signal_terminate(hostapd_cli_eloop_terminate, NULL);
 +      edit_init(hostapd_cli_edit_cmd_cb, hostapd_cli_edit_eof_cb,
 +                NULL, NULL, NULL, NULL);
 +      eloop_register_timeout(ping_interval, 0, hostapd_cli_ping, NULL, NULL);
 +
 +      eloop_run();
 +
 +      edit_deinit(NULL, NULL);
 +      eloop_cancel_timeout(hostapd_cli_ping, NULL, NULL);
 +}
 +
 +
 +static void hostapd_cli_cleanup(void)
 +{
 +      hostapd_cli_close_connection();
 +      if (pid_file)
 +              os_daemonize_terminate(pid_file);
 +
 +      os_program_deinit();
 +}
 +
 +
 +static void hostapd_cli_action(struct wpa_ctrl *ctrl)
 +{
 +      fd_set rfds;
 +      int fd, res;
 +      struct timeval tv;
 +      char buf[256];
 +      size_t len;
 +
 +      fd = wpa_ctrl_get_fd(ctrl);
 +
 +      while (!hostapd_cli_quit) {
 +              FD_ZERO(&rfds);
 +              FD_SET(fd, &rfds);
 +              tv.tv_sec = ping_interval;
 +              tv.tv_usec = 0;
 +              res = select(fd + 1, &rfds, NULL, NULL, &tv);
 +              if (res < 0 && errno != EINTR) {
 +                      perror("select");
 +                      break;
 +              }
 +
 +              if (FD_ISSET(fd, &rfds))
 +                      hostapd_cli_recv_pending(ctrl, 0, 1);
 +              else {
 +                      len = sizeof(buf) - 1;
 +                      if (wpa_ctrl_request(ctrl, "PING", 4, buf, &len,
 +                                           hostapd_cli_action_process) < 0 ||
 +                          len < 4 || os_memcmp(buf, "PONG", 4) != 0) {
 +                              printf("hostapd did not reply to PING "
 +                                     "command - exiting\n");
 +                              break;
 +                      }
 +              }
 +      }
 +}
 +
 +
 +int main(int argc, char *argv[])
 +{
 +      int warning_displayed = 0;
 +      int c;
 +      int daemonize = 0;
 +
 +      if (os_program_init())
 +              return -1;
 +
 +      for (;;) {
++              c = getopt(argc, argv, "a:BhG:i:p:P:s:v");
 +              if (c < 0)
 +                      break;
 +              switch (c) {
 +              case 'a':
 +                      action_file = optarg;
 +                      break;
 +              case 'B':
 +                      daemonize = 1;
 +                      break;
 +              case 'G':
 +                      ping_interval = atoi(optarg);
 +                      break;
 +              case 'h':
 +                      usage();
 +                      return 0;
 +              case 'v':
 +                      printf("%s\n", hostapd_cli_version);
 +                      return 0;
 +              case 'i':
 +                      os_free(ctrl_ifname);
 +                      ctrl_ifname = os_strdup(optarg);
 +                      break;
 +              case 'p':
 +                      ctrl_iface_dir = optarg;
 +                      break;
++              case 'P':
++                      pid_file = optarg;
++                      break;
++              case 's':
++                      client_socket_dir = optarg;
++                      break;
 +              default:
 +                      usage();
 +                      return -1;
 +              }
 +      }
 +
 +      interactive = (argc == optind) && (action_file == NULL);
 +
 +      if (interactive) {
 +              printf("%s\n\n%s\n\n", hostapd_cli_version,
 +                     hostapd_cli_license);
 +      }
 +
 +      if (eloop_init())
 +              return -1;
 +
 +      for (;;) {
 +              if (ctrl_ifname == NULL) {
 +                      struct dirent *dent;
 +                      DIR *dir = opendir(ctrl_iface_dir);
 +                      if (dir) {
 +                              while ((dent = readdir(dir))) {
 +                                      if (os_strcmp(dent->d_name, ".") == 0
 +                                          ||
 +                                          os_strcmp(dent->d_name, "..") == 0)
 +                                              continue;
 +                                      printf("Selected interface '%s'\n",
 +                                             dent->d_name);
 +                                      ctrl_ifname = os_strdup(dent->d_name);
 +                                      break;
 +                              }
 +                              closedir(dir);
 +                      }
 +              }
 +              ctrl_conn = hostapd_cli_open_connection(ctrl_ifname);
 +              if (ctrl_conn) {
 +                      if (warning_displayed)
 +                              printf("Connection established.\n");
 +                      break;
 +              }
 +
 +              if (!interactive) {
 +                      perror("Failed to connect to hostapd - "
 +                             "wpa_ctrl_open");
 +                      return -1;
 +              }
 +
 +              if (!warning_displayed) {
 +                      printf("Could not connect to hostapd - re-trying\n");
 +                      warning_displayed = 1;
 +              }
 +              os_sleep(1, 0);
 +              continue;
 +      }
 +
 +      if (interactive || action_file) {
 +              if (wpa_ctrl_attach(ctrl_conn) == 0) {
 +                      hostapd_cli_attached = 1;
 +              } else {
 +                      printf("Warning: Failed to attach to hostapd.\n");
 +                      if (action_file)
 +                              return -1;
 +              }
 +      }
 +
 +      if (daemonize && os_daemonize(pid_file))
 +              return -1;
 +
 +      if (interactive)
 +              hostapd_cli_interactive();
 +      else if (action_file)
 +              hostapd_cli_action(ctrl_conn);
 +      else
 +              wpa_request(ctrl_conn, argc - optind, &argv[optind]);
 +
 +      os_free(ctrl_ifname);
 +      eloop_destroy();
 +      hostapd_cli_cleanup();
 +      return 0;
 +}
index dd389a8d66a3b12d743d6c4df6df77d30579b3c5,0000000000000000000000000000000000000000..6c7406af447e3e972bd05f6cf33b51e185900677
mode 100644,000000..100644
--- /dev/null
@@@ -1,764 -1,0 +1,805 @@@
-               wpa_printf(MSG_ERROR, "Failed to initilize global context");
 +/*
 + * hostapd / main()
 + * 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.
 + */
 +
 +#include "utils/includes.h"
 +#ifndef CONFIG_NATIVE_WINDOWS
 +#include <syslog.h>
 +#include <grp.h>
 +#endif /* CONFIG_NATIVE_WINDOWS */
 +
 +#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 "drivers/driver.h"
 +#include "eap_server/eap.h"
 +#include "eap_server/tncs.h"
 +#include "ap/hostapd.h"
 +#include "ap/ap_config.h"
 +#include "ap/ap_drv_ops.h"
++#include "fst/fst.h"
 +#include "config_file.h"
 +#include "eap_register.h"
 +#include "ctrl_iface.h"
 +
 +
 +struct hapd_global {
 +      void **drv_priv;
 +      size_t drv_count;
 +};
 +
 +static struct hapd_global global;
 +
 +
 +#ifndef CONFIG_NO_HOSTAPD_LOGGER
 +static void hostapd_logger_cb(void *ctx, const u8 *addr, unsigned int module,
 +                            int level, const char *txt, size_t len)
 +{
 +      struct hostapd_data *hapd = ctx;
 +      char *format, *module_str;
 +      int maxlen;
 +      int conf_syslog_level, conf_stdout_level;
 +      unsigned int conf_syslog, conf_stdout;
 +
 +      maxlen = len + 100;
 +      format = os_malloc(maxlen);
 +      if (!format)
 +              return;
 +
 +      if (hapd && hapd->conf) {
 +              conf_syslog_level = hapd->conf->logger_syslog_level;
 +              conf_stdout_level = hapd->conf->logger_stdout_level;
 +              conf_syslog = hapd->conf->logger_syslog;
 +              conf_stdout = hapd->conf->logger_stdout;
 +      } else {
 +              conf_syslog_level = conf_stdout_level = 0;
 +              conf_syslog = conf_stdout = (unsigned int) -1;
 +      }
 +
 +      switch (module) {
 +      case HOSTAPD_MODULE_IEEE80211:
 +              module_str = "IEEE 802.11";
 +              break;
 +      case HOSTAPD_MODULE_IEEE8021X:
 +              module_str = "IEEE 802.1X";
 +              break;
 +      case HOSTAPD_MODULE_RADIUS:
 +              module_str = "RADIUS";
 +              break;
 +      case HOSTAPD_MODULE_WPA:
 +              module_str = "WPA";
 +              break;
 +      case HOSTAPD_MODULE_DRIVER:
 +              module_str = "DRIVER";
 +              break;
 +      case HOSTAPD_MODULE_IAPP:
 +              module_str = "IAPP";
 +              break;
 +      case HOSTAPD_MODULE_MLME:
 +              module_str = "MLME";
 +              break;
 +      default:
 +              module_str = NULL;
 +              break;
 +      }
 +
 +      if (hapd && hapd->conf && addr)
 +              os_snprintf(format, maxlen, "%s: STA " MACSTR "%s%s: %s",
 +                          hapd->conf->iface, MAC2STR(addr),
 +                          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 ? module_str : "", txt);
 +      else if (addr)
 +              os_snprintf(format, maxlen, "STA " MACSTR "%s%s: %s",
 +                          MAC2STR(addr), module_str ? " " : "",
 +                          module_str ? module_str : "", txt);
 +      else
 +              os_snprintf(format, maxlen, "%s%s%s",
 +                          module_str ? module_str : "",
 +                          module_str ? ": " : "", txt);
 +
 +      if ((conf_stdout & module) && level >= conf_stdout_level) {
 +              wpa_debug_print_timestamp();
 +              wpa_printf(MSG_INFO, "%s", format);
 +      }
 +
 +#ifndef CONFIG_NATIVE_WINDOWS
 +      if ((conf_syslog & module) && level >= conf_syslog_level) {
 +              int priority;
 +              switch (level) {
 +              case HOSTAPD_LEVEL_DEBUG_VERBOSE:
 +              case HOSTAPD_LEVEL_DEBUG:
 +                      priority = LOG_DEBUG;
 +                      break;
 +              case HOSTAPD_LEVEL_INFO:
 +                      priority = LOG_INFO;
 +                      break;
 +              case HOSTAPD_LEVEL_NOTICE:
 +                      priority = LOG_NOTICE;
 +                      break;
 +              case HOSTAPD_LEVEL_WARNING:
 +                      priority = LOG_WARNING;
 +                      break;
 +              default:
 +                      priority = LOG_INFO;
 +                      break;
 +              }
 +              syslog(priority, "%s", format);
 +      }
 +#endif /* CONFIG_NATIVE_WINDOWS */
 +
 +      os_free(format);
 +}
 +#endif /* CONFIG_NO_HOSTAPD_LOGGER */
 +
 +
 +/**
 + * hostapd_driver_init - Preparate driver interface
 + */
 +static int hostapd_driver_init(struct hostapd_iface *iface)
 +{
 +      struct wpa_init_params params;
 +      size_t i;
 +      struct hostapd_data *hapd = iface->bss[0];
 +      struct hostapd_bss_config *conf = hapd->conf;
 +      u8 *b = conf->bssid;
 +      struct wpa_driver_capa capa;
 +
 +      if (hapd->driver == NULL || hapd->driver->hapd_init == NULL) {
 +              wpa_printf(MSG_ERROR, "No hostapd driver wrapper available");
 +              return -1;
 +      }
 +
 +      /* Initialize the driver interface */
 +      if (!(b[0] | b[1] | b[2] | b[3] | b[4] | b[5]))
 +              b = NULL;
 +
 +      os_memset(&params, 0, sizeof(params));
 +      for (i = 0; wpa_drivers[i]; i++) {
 +              if (wpa_drivers[i] != hapd->driver)
 +                      continue;
 +
 +              if (global.drv_priv[i] == NULL &&
 +                  wpa_drivers[i]->global_init) {
 +                      global.drv_priv[i] = wpa_drivers[i]->global_init();
 +                      if (global.drv_priv[i] == NULL) {
 +                              wpa_printf(MSG_ERROR, "Failed to initialize "
 +                                         "driver '%s'",
 +                                         wpa_drivers[i]->name);
 +                              return -1;
 +                      }
 +              }
 +
 +              params.global_priv = global.drv_priv[i];
 +              break;
 +      }
 +      params.bssid = b;
 +      params.ifname = hapd->conf->iface;
 +      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;
 +      params.bridge = os_calloc(hapd->iface->num_bss, sizeof(char *));
 +      if (params.bridge == NULL)
 +              return -1;
 +      for (i = 0; i < hapd->iface->num_bss; i++) {
 +              struct hostapd_data *bss = hapd->iface->bss[i];
 +              if (bss->conf->bridge[0])
 +                      params.bridge[i] = bss->conf->bridge;
 +      }
 +
 +      params.own_addr = hapd->own_addr;
 +
 +      hapd->drv_priv = hapd->driver->hapd_init(hapd, &params);
 +      os_free(params.bridge);
 +      if (hapd->drv_priv == NULL) {
 +              wpa_printf(MSG_ERROR, "%s driver initialization failed.",
 +                         hapd->driver->name);
 +              hapd->driver = NULL;
 +              return -1;
 +      }
 +
 +      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);
 +      }
 +
 +      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)
 +{
 +      struct hostapd_iface *iface;
 +      int k;
 +
 +      wpa_printf(MSG_ERROR, "Configuration file: %s", config_fname);
 +      iface = hostapd_init(interfaces, config_fname);
 +      if (!iface)
 +              return NULL;
 +      iface->interfaces = interfaces;
 +
 +      for (k = 0; k < debug; k++) {
 +              if (iface->bss[0]->conf->logger_stdout_level > 0)
 +                      iface->bss[0]->conf->logger_stdout_level--;
 +      }
 +
 +      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);
 +              hostapd_interface_deinit_free(iface);
 +              return NULL;
 +      }
 +
 +      return iface;
 +}
 +
 +
 +/**
 + * handle_term - SIGINT and SIGTERM handler to terminate hostapd process
 + */
 +static void handle_term(int sig, void *signal_ctx)
 +{
 +      wpa_printf(MSG_DEBUG, "Signal %d received - terminating", sig);
 +      eloop_terminate();
 +}
 +
 +
 +#ifndef CONFIG_NATIVE_WINDOWS
 +
 +static int handle_reload_iface(struct hostapd_iface *iface, void *ctx)
 +{
 +      if (hostapd_reload_config(iface) < 0) {
 +              wpa_printf(MSG_WARNING, "Failed to read new configuration "
 +                         "file - continuing with old.");
 +      }
 +      return 0;
 +}
 +
 +
 +/**
 + * handle_reload - SIGHUP handler to reload configuration
 + */
 +static void handle_reload(int sig, void *signal_ctx)
 +{
 +      struct hapd_interfaces *interfaces = signal_ctx;
 +      wpa_printf(MSG_DEBUG, "Signal %d received - reloading configuration",
 +                 sig);
 +      hostapd_for_each_interface(interfaces, handle_reload_iface, NULL);
 +}
 +
 +
 +static void handle_dump_state(int sig, void *signal_ctx)
 +{
 +      /* Not used anymore - ignore signal */
 +}
 +#endif /* CONFIG_NATIVE_WINDOWS */
 +
 +
 +static int hostapd_global_init(struct hapd_interfaces *interfaces,
 +                             const char *entropy_file)
 +{
 +      int i;
 +
 +      os_memset(&global, 0, sizeof(global));
 +
 +      hostapd_logger_register_cb(hostapd_logger_cb);
 +
 +      if (eap_server_register_methods()) {
 +              wpa_printf(MSG_ERROR, "Failed to register EAP methods");
 +              return -1;
 +      }
 +
 +      if (eloop_init()) {
 +              wpa_printf(MSG_ERROR, "Failed to initialize event loop");
 +              return -1;
 +      }
 +
 +      random_init(entropy_file);
 +
 +#ifndef CONFIG_NATIVE_WINDOWS
 +      eloop_register_signal(SIGHUP, handle_reload, interfaces);
 +      eloop_register_signal(SIGUSR1, handle_dump_state, interfaces);
 +#endif /* CONFIG_NATIVE_WINDOWS */
 +      eloop_register_signal_terminate(handle_term, interfaces);
 +
 +#ifndef CONFIG_NATIVE_WINDOWS
 +      openlog("hostapd", 0, LOG_DAEMON);
 +#endif /* CONFIG_NATIVE_WINDOWS */
 +
 +      for (i = 0; wpa_drivers[i]; i++)
 +              global.drv_count++;
 +      if (global.drv_count == 0) {
 +              wpa_printf(MSG_ERROR, "No drivers enabled");
 +              return -1;
 +      }
 +      global.drv_priv = os_calloc(global.drv_count, sizeof(void *));
 +      if (global.drv_priv == NULL)
 +              return -1;
 +
 +      return 0;
 +}
 +
 +
 +static void hostapd_global_deinit(const char *pid_file)
 +{
 +      int i;
 +
 +      for (i = 0; wpa_drivers[i] && global.drv_priv; i++) {
 +              if (!global.drv_priv[i])
 +                      continue;
 +              wpa_drivers[i]->global_deinit(global.drv_priv[i]);
 +      }
 +      os_free(global.drv_priv);
 +      global.drv_priv = NULL;
 +
 +#ifdef EAP_SERVER_TNC
 +      tncs_global_deinit();
 +#endif /* EAP_SERVER_TNC */
 +
 +      random_deinit();
 +
 +      eloop_destroy();
 +
 +#ifndef CONFIG_NATIVE_WINDOWS
 +      closelog();
 +#endif /* CONFIG_NATIVE_WINDOWS */
 +
 +      eap_server_unregister_methods();
 +
 +      os_daemonize_terminate(pid_file);
 +}
 +
 +
 +static int hostapd_global_run(struct hapd_interfaces *ifaces, int daemonize,
 +                            const char *pid_file)
 +{
 +#ifdef EAP_SERVER_TNC
 +      int tnc = 0;
 +      size_t i, k;
 +
 +      for (i = 0; !tnc && i < ifaces->count; i++) {
 +              for (k = 0; k < ifaces->iface[i]->num_bss; k++) {
 +                      if (ifaces->iface[i]->bss[0]->conf->tnc) {
 +                              tnc++;
 +                              break;
 +                      }
 +              }
 +      }
 +
 +      if (tnc && tncs_global_init() < 0) {
 +              wpa_printf(MSG_ERROR, "Failed to initialize TNCS");
 +              return -1;
 +      }
 +#endif /* EAP_SERVER_TNC */
 +
 +      if (daemonize && os_daemonize(pid_file)) {
 +              wpa_printf(MSG_ERROR, "daemon: %s", strerror(errno));
 +              return -1;
 +      }
 +
 +      eloop_run();
 +
 +      return 0;
 +}
 +
 +
 +static void show_version(void)
 +{
 +      fprintf(stderr,
 +              "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-2015, Jouni Malinen <j@w1.fi> "
 +              "and contributors\n");
 +}
 +
 +
 +static void usage(void)
 +{
 +      show_version();
 +      fprintf(stderr,
 +              "\n"
 +              "usage: hostapd [-hdBKtv] [-P <PID file>] [-e <entropy file>] "
 +              "\\\n"
 +              "         [-g <global ctrl_iface>] [-G <group>] \\\n"
 +              "         <configuration file(s)>\n"
 +              "\n"
 +              "options:\n"
 +              "   -h   show this usage\n"
 +              "   -d   show more debug messages (-dd for even more)\n"
 +              "   -B   run daemon in the background\n"
 +              "   -e   entropy file\n"
 +              "   -g   global control interface path\n"
 +              "   -G   group for control interfaces\n"
 +              "   -P   PID file\n"
 +              "   -K   include key data in debug messages\n"
 +#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");
 +
 +      exit(1);
 +}
 +
 +
 +static const char * hostapd_msg_ifname_cb(void *ctx)
 +{
 +      struct hostapd_data *hapd = ctx;
 +      if (hapd && hapd->iconf && hapd->iconf->bss &&
 +          hapd->iconf->num_bss > 0 && hapd->iconf->bss[0])
 +              return hapd->iconf->bss[0]->iface;
 +      return NULL;
 +}
 +
 +
 +static int hostapd_get_global_ctrl_iface(struct hapd_interfaces *interfaces,
 +                                       const char *path)
 +{
 +      char *pos;
 +      os_free(interfaces->global_iface_path);
 +      interfaces->global_iface_path = os_strdup(path);
 +      if (interfaces->global_iface_path == NULL)
 +              return -1;
 +      pos = os_strrchr(interfaces->global_iface_path, '/');
 +      if (pos == NULL) {
 +              wpa_printf(MSG_ERROR, "No '/' in the global control interface "
 +                         "file");
 +              os_free(interfaces->global_iface_path);
 +              interfaces->global_iface_path = NULL;
 +              return -1;
 +      }
 +
 +      *pos = '\0';
 +      interfaces->global_iface_name = pos + 1;
 +
 +      return 0;
 +}
 +
 +
 +static int hostapd_get_ctrl_iface_group(struct hapd_interfaces *interfaces,
 +                                      const char *group)
 +{
 +#ifndef CONFIG_NATIVE_WINDOWS
 +      struct group *grp;
 +      grp = getgrnam(group);
 +      if (grp == NULL) {
 +              wpa_printf(MSG_ERROR, "Unknown group '%s'", group);
 +              return -1;
 +      }
 +      interfaces->ctrl_iface_group = grp->gr_gid;
 +#endif /* CONFIG_NATIVE_WINDOWS */
 +      return 0;
 +}
 +
 +
 +#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 */
 +
 +
++#ifndef HOSTAPD_CLEANUP_INTERVAL
++#define HOSTAPD_CLEANUP_INTERVAL 10
++#endif /* HOSTAPD_CLEANUP_INTERVAL */
++
++static int hostapd_periodic_call(struct hostapd_iface *iface, void *ctx)
++{
++      hostapd_periodic_iface(iface);
++      return 0;
++}
++
++
++/* Periodic cleanup tasks */
++static void hostapd_periodic(void *eloop_ctx, void *timeout_ctx)
++{
++      struct hapd_interfaces *interfaces = eloop_ctx;
++
++      eloop_register_timeout(HOSTAPD_CLEANUP_INTERVAL, 0,
++                             hostapd_periodic, interfaces, NULL);
++      hostapd_for_each_interface(interfaces, hostapd_periodic_call, NULL);
++}
++
++
 +int main(int argc, char *argv[])
 +{
 +      struct hapd_interfaces interfaces;
 +      int ret = 1;
 +      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;
 +
 +      os_memset(&interfaces, 0, sizeof(interfaces));
 +      interfaces.reload_config = hostapd_reload_config;
 +      interfaces.config_read_cb = hostapd_config_read;
 +      interfaces.for_each_interface = hostapd_for_each_interface;
 +      interfaces.ctrl_iface_init = hostapd_ctrl_iface_init;
 +      interfaces.ctrl_iface_deinit = hostapd_ctrl_iface_deinit;
 +      interfaces.driver_init = hostapd_driver_init;
 +      interfaces.global_iface_path = NULL;
 +      interfaces.global_iface_name = NULL;
 +      interfaces.global_ctrl_sock = -1;
++      interfaces.global_ctrl_dst = NULL;
 +
 +      for (;;) {
 +              c = getopt(argc, argv, "b:Bde:f:hKP:Ttu:vg:G:");
 +              if (c < 0)
 +                      break;
 +              switch (c) {
 +              case 'h':
 +                      usage();
 +                      break;
 +              case 'd':
 +                      debug++;
 +                      if (wpa_debug_level > 0)
 +                              wpa_debug_level--;
 +                      break;
 +              case 'B':
 +                      daemonize++;
 +                      break;
 +              case 'e':
 +                      entropy_file = optarg;
 +                      break;
 +              case 'f':
 +                      log_file = optarg;
 +                      break;
 +              case 'K':
 +                      wpa_debug_show_keys++;
 +                      break;
 +              case 'P':
 +                      os_free(pid_file);
 +                      pid_file = os_rel2abs_path(optarg);
 +                      break;
 +              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);
 +                      break;
 +              case 'g':
 +                      if (hostapd_get_global_ctrl_iface(&interfaces, optarg))
 +                              return -1;
 +                      break;
 +              case 'G':
 +                      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 &&
 +          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 || 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");
 +                      return -1;
 +              }
 +      }
 +
 +      if (hostapd_global_init(&interfaces, entropy_file)) {
++              wpa_printf(MSG_ERROR, "Failed to initialize global context");
 +              return -1;
 +      }
 +
++      eloop_register_timeout(HOSTAPD_CLEANUP_INTERVAL, 0,
++                             hostapd_periodic, &interfaces, NULL);
++
++      if (fst_global_init()) {
++              wpa_printf(MSG_ERROR,
++                         "Failed to initialize global FST context");
++              goto out;
++      }
++
++#if defined(CONFIG_FST) && defined(CONFIG_CTRL_IFACE)
++      if (!fst_global_add_ctrl(fst_ctrl_cli))
++              wpa_printf(MSG_WARNING, "Failed to add CLI FST ctrl");
++#endif /* CONFIG_FST && CONFIG_CTRL_IFACE */
++
 +      /* 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],
 +                                                           debug);
 +              if (!interfaces.iface[i]) {
 +                      wpa_printf(MSG_ERROR, "Failed to initialize interface");
 +                      goto out;
 +              }
 +      }
 +
 +      /* 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)) {
 +              wpa_printf(MSG_ERROR, "Failed to start eloop");
 +              goto out;
 +      }
 +
 +      ret = 0;
 +
 + out:
 +      hostapd_global_ctrl_iface_deinit(&interfaces);
 +      /* Deinitialize all interfaces */
 +      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);
 +
++      eloop_cancel_timeout(hostapd_periodic, &interfaces, NULL);
 +      hostapd_global_deinit(pid_file);
 +      os_free(pid_file);
 +
 +      if (log_file)
 +              wpa_debug_close_file();
 +      wpa_debug_close_linux_tracing();
 +
 +      os_free(bss_config);
 +
++      fst_global_deinit();
++
 +      os_program_deinit();
 +
 +      return ret;
 +}
index ca67b54da2eedee7a8e6149be31c8c5638d0cd81,0000000000000000000000000000000000000000..94cd5f14df1443cc15ba8197ea34357127c4222d
mode 100644,000000..100644
--- /dev/null
@@@ -1,94 -1,0 +1,100 @@@
 +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)
++
++# Allow static/custom linking of libcurl.
++ifdef CUST_CURL_LINKAGE
++LIBS += ${CUST_CURL_LINKAGE}
++else
 +LIBS += -lcurl
++endif
 +
 +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)
index de7f351da244528b3bff647b14c889cc6aadc3c6,0000000000000000000000000000000000000000..0315f7b75ad43da98b8015cb93a63eb383be232a
mode 100644,000000..100644
--- /dev/null
@@@ -1,3227 -1,0 +1,3264 @@@
-               wpa_printf(MSG_INFO, "FQDN '%s' for new PPS MO did not have suffix match with server's dNSName values",
-                          fqdn);
 +/*
 + * 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"
 +
++const char *spp_xsd_fname = "spp.xsd";
++
 +
 +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);
++                      free(fqdn);
 +                      return -1;
 +              }
 +              *pos = '\0'; /* remove trailing slash and PPS node name */
 +      }
 +      wpa_printf(MSG_INFO, "SP FQDN: %s", fqdn);
 +
 +      if (!server_dnsname_suffix_match(ctx, fqdn)) {
-       if (methods & 0x02)
++              wpa_printf(MSG_INFO,
++                         "FQDN '%s' for new PPS MO did not have suffix match with server's dNSName values, count: %d",
++                         fqdn, (int) ctx->server_dnsname_count);
 +              write_result(ctx, "FQDN '%s' for new PPS MO did not have suffix match with server's dNSName values",
 +                           fqdn);
 +              free(fqdn);
 +              return -1;
 +      }
 +
 +      if (!valid_fqdn(fqdn)) {
 +              wpa_printf(MSG_INFO, "Invalid FQDN '%s'", fqdn);
 +              write_result(ctx, "Invalid FQDN '%s'", fqdn);
 +              free(fqdn);
 +              return -1;
 +      }
 +
 +      mkdir("SP", S_IRWXU);
 +      snprintf(fname, fname_len, "SP/%s", fqdn);
 +      if (mkdir(fname, S_IRWXU) < 0) {
 +              if (errno != EEXIST) {
 +                      int err = errno;
 +                      wpa_printf(MSG_INFO, "mkdir(%s) failed: %s",
 +                                 fname, strerror(err));
 +                      free(fqdn);
 +                      return -1;
 +              }
 +      }
 +
 +#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;
-       else if (methods & 0x01)
++      if (methods & 0x02) {
++              wpa_printf(MSG_DEBUG, "Calling cmd_prov from osu_connect");
 +              res = cmd_prov(ctx, url);
-               wpa_printf(MSG_INFO, "Could not any OSU providers from %s",
++      } else if (methods & 0x01) {
++              wpa_printf(MSG_DEBUG,
++                         "Calling cmd_oma_dm_prov from osu_connect");
 +              res = cmd_oma_dm_prov(ctx, url);
++      }
 +
 +      wpa_printf(MSG_INFO, "Remove OSU network connection");
 +      write_summary(ctx, "Remove OSU network connection");
 +      snprintf(buf, sizeof(buf), "REMOVE_NETWORK %d", id);
 +      wpa_command(ifname, buf);
 +
 +      if (res < 0)
 +              return -1;
 +
 +      wpa_printf(MSG_INFO, "Requesting reconnection with updated configuration");
 +      write_summary(ctx, "Requesting reconnection with updated configuration");
 +      if (wpa_command(ctx->ifname, "INTERWORKING_SELECT auto") < 0) {
 +              wpa_printf(MSG_INFO, "Failed to request wpa_supplicant to reconnect");
 +              write_summary(ctx, "Failed to request wpa_supplicant to reconnect");
 +              return -1;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +static int cmd_osu_select(struct hs20_osu_client *ctx, const char *dir,
 +                        int connect, int no_prod_assoc,
 +                        const char *friendly_name)
 +{
 +      char fname[255];
 +      FILE *f;
 +      struct osu_data *osu = NULL, *last = NULL;
 +      size_t osu_count, 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) {
-                       if (last->methods & 0x02)
++              wpa_printf(MSG_INFO, "Could not find any OSU providers from %s",
 +                         fname);
 +              write_result(ctx, "No OSU providers available");
 +              return -1;
 +      }
 +
 +      if (friendly_name) {
 +              for (i = 0; i < osu_count; i++) {
 +                      last = &osu[i];
 +                      for (j = 0; j < last->friendly_name_count; j++) {
 +                              if (os_strcmp(last->friendly_name[j].text,
 +                                            friendly_name) == 0)
 +                                      break;
 +                      }
 +                      if (j < last->friendly_name_count)
 +                              break;
 +              }
 +              if (i == osu_count) {
 +                      wpa_printf(MSG_INFO, "Requested operator friendly name '%s' not found in the list of available providers",
 +                                 friendly_name);
 +                      write_summary(ctx, "Requested operator friendly name '%s' not found in the list of available providers",
 +                                    friendly_name);
 +                      free(osu);
 +                      return -1;
 +              }
 +
 +              wpa_printf(MSG_INFO, "OSU Provider selected based on requested operator friendly name '%s'",
 +                         friendly_name);
 +              write_summary(ctx, "OSU Provider selected based on requested operator friendly name '%s'",
 +                            friendly_name);
 +              ret = i + 1;
 +              goto selected;
 +      }
 +
 +      snprintf(fname, sizeof(fname), "%s/osu-providers.html", dir);
 +      f = fopen(fname, "w");
 +      if (f == NULL) {
 +              wpa_printf(MSG_INFO, "Could not open %s", fname);
 +              free(osu);
 +              return -1;
 +      }
 +
 +      fprintf(f, "<html><head>"
 +              "<meta http-equiv=\"Content-type\" content=\"text/html; "
 +              "charset=utf-8\"<title>Select service operator</title>"
 +              "</head><body><h1>Select service operator</h1>\n");
 +
 +      if (osu_count == 0)
 +              fprintf(f, "No online signup available\n");
 +
 +      for (i = 0; i < osu_count; i++) {
 +              last = &osu[i];
 +#ifdef ANDROID
 +              fprintf(f, "<p>\n"
 +                      "<a href=\"http://localhost:12345/osu/%d\">"
 +                      "<table><tr><td>", (int) i + 1);
 +#else /* ANDROID */
 +              fprintf(f, "<p>\n"
 +                      "<a href=\"osu://%d\">"
 +                      "<table><tr><td>", (int) i + 1);
 +#endif /* ANDROID */
 +              for (j = 0; j < last->icon_count; j++) {
 +                      fprintf(f, "<img src=\"osu-icon-%d.%s\">\n",
 +                              last->icon[j].id,
 +                              strcasecmp(last->icon[j].mime_type,
 +                                         "image/png") == 0 ? "png" : "icon");
 +              }
 +              fprintf(f, "<td>");
 +              for (j = 0; j < last->friendly_name_count; j++) {
 +                      fprintf(f, "<small>[%s]</small> %s<br>\n",
 +                              last->friendly_name[j].lang,
 +                              last->friendly_name[j].text);
 +              }
 +              fprintf(f, "<tr><td colspan=2>");
 +              for (j = 0; j < last->serv_desc_count; j++) {
 +                      fprintf(f, "<small>[%s]</small> %s<br>\n",
 +                              last->serv_desc[j].lang,
 +                              last->serv_desc[j].text);
 +              }
 +              fprintf(f, "</table></a><br><small>BSSID: %s<br>\n"
 +                      "SSID: %s<br>\n",
 +                      last->bssid, last->osu_ssid);
 +              if (last->osu_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) {
-                       else if (last->methods & 0x01)
++                      if (last->methods & 0x02) {
++                              wpa_printf(MSG_DEBUG,
++                                         "Calling cmd_prov from cmd_osu_select");
 +                              ret = cmd_prov(ctx, last->url);
-                       else
++                      } else if (last->methods & 0x01) {
++                              wpa_printf(MSG_DEBUG,
++                                         "Calling cmd_oma_dm_prov from cmd_osu_select");
 +                              ret = cmd_oma_dm_prov(ctx, last->url);
-       if (end && end2 && end2 < end)
++                      } else {
++                              wpa_printf(MSG_DEBUG,
++                                         "No supported OSU provisioning method");
 +                              ret = -1;
++                      }
 +              } else if (connect)
 +                      ret = osu_connect(ctx, last->bssid, last->osu_ssid,
 +                                        last->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, ':');
-       wpa_printf(MSG_INFO, "osu_cert_cb(osu_cert_validation=%d)",
-                  !ctx->no_osu_cert_validation);
++      if ((end && end2 && end2 < end) || (!end && end2))
 +              end = end2;
 +      if (end)
 +              end--;
 +      else {
 +              end = pos;
 +              while (*end)
 +                      end++;
 +              if (end > pos)
 +                      end--;
 +      }
 +
 +      ret = os_malloc(end - pos + 2);
 +      if (ret == NULL)
 +              return NULL;
 +
 +      os_memcpy(ret, pos, end - pos + 1);
 +      ret[end - pos + 1] = '\0';
 +
 +      return ret;
 +}
 +
 +
 +static int osu_cert_cb(void *_ctx, struct http_cert *cert)
 +{
 +      struct hs20_osu_client *ctx = _ctx;
 +      unsigned int i, j;
 +      int found;
 +      char *host = NULL;
 +
-               wpa_printf(MSG_INFO, "Looking for icon file name '%s' match",
-                          name);
++      wpa_printf(MSG_INFO, "osu_cert_cb(osu_cert_validation=%d, url=%s)",
++                 !ctx->no_osu_cert_validation, ctx->server_url);
 +
 +      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, "Comparing to '%s' uri_len=%d name_len=%d",
-                                  logo->uri, (int) uri_len, (int) name_len);
-                       if (uri_len < 1 + name_len)
++              wpa_printf(MSG_INFO,
++                         "[%i] Looking for icon file name '%s' match",
++                         j, name);
 +              for (i = 0; i < cert->num_logo; i++) {
 +                      struct http_logo *logo = &cert->logo[i];
 +                      size_t uri_len = os_strlen(logo->uri);
 +                      char *pos;
 +
-                       if (logo->hash_len != 32)
++                      wpa_printf(MSG_INFO,
++                                 "[%i] Comparing to '%s' uri_len=%d name_len=%d",
++                                 i, logo->uri, (int) uri_len, (int) name_len);
++                      if (uri_len < 1 + name_len) {
++                              wpa_printf(MSG_INFO, "URI Length is too short");
 +                              continue;
++                      }
 +                      pos = &logo->uri[uri_len - name_len - 1];
 +                      if (*pos != '/')
 +                              continue;
 +                      pos++;
 +                      if (os_strcmp(pos, name) == 0) {
 +                              found = 1;
 +                              break;
 +                      }
 +              }
 +
 +              if (!found) {
 +                      wpa_printf(MSG_INFO, "No icon filename match found for '%s'",
 +                                 name);
 +                      write_result(ctx,
 +                                   "No icon filename match found for '%s'",
 +                                   name);
 +                      return -1;
 +              }
 +      }
 +
 +      for (j = 0; !ctx->no_osu_cert_validation && j < ctx->icon_count; j++) {
 +              int found = 0;
 +
 +              for (i = 0; i < cert->num_logo; i++) {
 +                      struct http_logo *logo = &cert->logo[i];
 +
-                       wpa_printf(MSG_INFO, "No icon hash match found");
-                       write_result(ctx, "No icon hash match found");
++                      if (logo->hash_len != 32) {
++                              wpa_printf(MSG_INFO,
++                                         "[%i][%i] Icon hash length invalid (should be 32): %d",
++                                         j, i, (int) logo->hash_len);
 +                              continue;
++                      }
 +                      if (os_memcmp(logo->hash, ctx->icon_hash[j], 32) == 0) {
 +                              found = 1;
 +                              break;
 +                      }
++
++                      wpa_printf(MSG_DEBUG,
++                                 "[%u][%u] Icon hash did not match", j, i);
++                      wpa_hexdump_ascii(MSG_DEBUG, "logo->hash",
++                                        logo->hash, 32);
++                      wpa_hexdump_ascii(MSG_DEBUG, "ctx->icon_hash[j]",
++                                        ctx->icon_hash[j], 32);
 +              }
 +
 +              if (!found) {
-               c = getopt(argc, argv, "df:hi:KNO:qr:s:S:tw:");
++                      wpa_printf(MSG_INFO,
++                                 "No icon hash match (by hash) found");
++                      write_result(ctx,
++                                   "No icon hash match (by hash) found");
 +                      return -1;
 +              }
 +      }
 +
 +      return 0;
 +}
 +
 +
 +static int init_ctx(struct hs20_osu_client *ctx)
 +{
 +      xml_node_t *devinfo, *devid;
 +
 +      os_memset(ctx, 0, sizeof(*ctx));
 +      ctx->ifname = "wlan0";
 +      ctx->xml = xml_node_init_ctx(ctx, NULL);
 +      if (ctx->xml == NULL)
 +              return -1;
 +
 +      devinfo = node_from_file(ctx->xml, "devinfo.xml");
 +      if (!devinfo) {
 +              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"
++             "    [-x<spp.xsd file name>] \\\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:hKNO:qr:s:S:tw:x:");
 +              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 'x':
++                      spp_xsd_fname = 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];
++              wpa_printf(MSG_DEBUG, "Calling cmd_prov from main");
 +              cmd_prov(&ctx, argv[optind + 1]);
 +      } else if (strcmp(argv[optind], "sim_prov") == 0) {
 +              if (argc - optind < 2) {
 +                      usage();
 +                      exit(0);
 +              }
 +              ctx.ca_fname = argv[optind + 2];
 +              cmd_sim_prov(&ctx, argv[optind + 1]);
 +      } else if (strcmp(argv[optind], "dl_osu_ca") == 0) {
 +              if (argc - optind < 2) {
 +                      usage();
 +                      exit(0);
 +              }
 +              cmd_dl_osu_ca(&ctx, argv[optind + 1], argv[optind + 2]);
 +      } else if (strcmp(argv[optind], "dl_polupd_ca") == 0) {
 +              if (argc - optind < 2) {
 +                      usage();
 +                      exit(0);
 +              }
 +              cmd_dl_polupd_ca(&ctx, argv[optind + 1], argv[optind + 2]);
 +      } else if (strcmp(argv[optind], "dl_aaa_ca") == 0) {
 +              if (argc - optind < 2) {
 +                      usage();
 +                      exit(0);
 +              }
 +              cmd_dl_aaa_ca(&ctx, argv[optind + 1], argv[optind + 2]);
 +      } else if (strcmp(argv[optind], "osu_select") == 0) {
 +              if (argc - optind < 2) {
 +                      usage();
 +                      exit(0);
 +              }
 +              ctx.ca_fname = argc > optind + 2 ? argv[optind + 2] : NULL;
 +              cmd_osu_select(&ctx, argv[optind + 1], 2, 1, NULL);
 +      } else if (strcmp(argv[optind], "signup") == 0) {
 +              ctx.ca_fname = argc > optind + 1 ? argv[optind + 1] : NULL;
 +              ret = cmd_signup(&ctx, no_prod_assoc, friendly_name);
 +      } else if (strcmp(argv[optind], "set_pps") == 0) {
 +              if (argc - optind < 2) {
 +                      usage();
 +                      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;
 +}
index 302a05040df6a09354e8908a91970ccf7f88c7e7,0000000000000000000000000000000000000000..c619541ae28673c48c358550b048486d0e6b81dd
mode 100644,000000..100644
--- /dev/null
@@@ -1,995 -1,0 +1,1004 @@@
-       ret = xml_validate(xctx, node, "spp.xsd", &err);
 +/*
 + * Hotspot 2.0 SPP client
 + * Copyright (c) 2012-2014, Qualcomm Atheros, Inc.
 + *
 + * This software may be distributed under the terms of the BSD license.
 + * See README for more details.
 + */
 +
 +#include "includes.h"
 +#include <sys/stat.h>
 +
 +#include "common.h"
 +#include "browser.h"
 +#include "wpa_ctrl.h"
 +#include "wpa_helpers.h"
 +#include "xml-utils.h"
 +#include "http-utils.h"
 +#include "utils/base64.h"
 +#include "crypto/crypto.h"
 +#include "crypto/sha256.h"
 +#include "osu_client.h"
 +
 +
++extern const char *spp_xsd_fname;
++
 +static int hs20_spp_update_response(struct hs20_osu_client *ctx,
 +                                  const char *session_id,
 +                                  const char *spp_status,
 +                                  const char *error_code);
 +static void hs20_policy_update_complete(
 +      struct hs20_osu_client *ctx, const char *pps_fname);
 +
 +
 +static char * get_spp_attr_value(struct xml_node_ctx *ctx, xml_node_t *node,
 +                               char *attr_name)
 +{
 +      return xml_node_get_attr_value_ns(ctx, node, SPP_NS_URI, attr_name);
 +}
 +
 +
 +static int hs20_spp_validate(struct hs20_osu_client *ctx, xml_node_t *node,
 +                           const char *expected_name)
 +{
 +      struct xml_node_ctx *xctx = ctx->xml;
 +      const char *name;
 +      char *err;
 +      int ret;
 +
 +      if (!xml_node_is_element(xctx, node))
 +              return -1;
 +
 +      name = xml_node_get_localname(xctx, node);
 +      if (name == NULL)
 +              return -1;
 +
 +      if (strcmp(expected_name, name) != 0) {
 +              wpa_printf(MSG_INFO, "Unexpected SOAP method name '%s' (expected '%s')",
 +                         name, expected_name);
 +              write_summary(ctx, "Unexpected SOAP method name '%s' (expected '%s')",
 +                            name, expected_name);
 +              return -1;
 +      }
 +
-       if (!fnode)
++      ret = xml_validate(xctx, node, spp_xsd_fname, &err);
 +      if (ret < 0) {
 +              wpa_printf(MSG_INFO, "XML schema validation error(s)\n%s", err);
 +              write_summary(ctx, "SPP XML schema validation failed");
 +              os_free(err);
 +      }
 +      return ret;
 +}
 +
 +
 +static void add_mo_container(struct xml_node_ctx *ctx, xml_namespace_t *ns,
 +                           xml_node_t *parent, const char *urn,
 +                           const char *fname)
 +{
 +      xml_node_t *node;
 +      xml_node_t *fnode, *tnds;
 +      char *str;
 +
++      errno = 0;
 +      fnode = node_from_file(ctx, fname);
-       wpa_printf(MSG_INFO, "Credential provisioning requested");
++      if (!fnode) {
++              wpa_printf(MSG_ERROR,
++                         "Failed to create XML node from file: %s, possible error: %s",
++                         fname, strerror(errno));
 +              return;
++      }
 +      tnds = mo_to_tnds(ctx, fnode, 0, urn, "syncml:dmddf1.2");
 +      xml_node_free(ctx, fnode);
 +      if (!tnds)
 +              return;
 +
 +      str = xml_node_to_str(ctx, tnds);
 +      xml_node_free(ctx, tnds);
 +      if (str == NULL)
 +              return;
 +
 +      node = xml_node_create_text(ctx, parent, ns, "moContainer", str);
 +      if (node)
 +              xml_node_add_attr(ctx, node, ns, "moURN", urn);
 +      os_free(str);
 +}
 +
 +
 +static xml_node_t * build_spp_post_dev_data(struct hs20_osu_client *ctx,
 +                                          xml_namespace_t **ret_ns,
 +                                          const char *session_id,
 +                                          const char *reason)
 +{
 +      xml_namespace_t *ns;
 +      xml_node_t *spp_node;
 +
 +      write_summary(ctx, "Building sppPostDevData requestReason='%s'",
 +                    reason);
 +      spp_node = xml_node_create_root(ctx->xml, SPP_NS_URI, "spp", &ns,
 +                                      "sppPostDevData");
 +      if (spp_node == NULL)
 +              return NULL;
 +      if (ret_ns)
 +              *ret_ns = ns;
 +
 +      xml_node_add_attr(ctx->xml, spp_node, ns, "sppVersion", "1.0");
 +      xml_node_add_attr(ctx->xml, spp_node, NULL, "requestReason", reason);
 +      if (session_id)
 +              xml_node_add_attr(ctx->xml, spp_node, ns, "sessionID",
 +                                session_id);
 +      xml_node_add_attr(ctx->xml, spp_node, NULL, "redirectURI",
 +                        "http://localhost:12345/");
 +
 +      xml_node_create_text(ctx->xml, spp_node, ns, "supportedSPPVersions",
 +                           "1.0");
 +      xml_node_create_text(ctx->xml, spp_node, ns, "supportedMOList",
 +                           URN_HS20_PPS " " URN_OMA_DM_DEVINFO " "
 +                           URN_OMA_DM_DEVDETAIL " " URN_HS20_DEVDETAIL_EXT);
 +
 +      add_mo_container(ctx->xml, ns, spp_node, URN_OMA_DM_DEVINFO,
 +                       "devinfo.xml");
 +      add_mo_container(ctx->xml, ns, spp_node, URN_OMA_DM_DEVDETAIL,
 +                       "devdetail.xml");
 +
 +      return spp_node;
 +}
 +
 +
 +static int process_update_node(struct hs20_osu_client *ctx, xml_node_t *pps,
 +                             xml_node_t *update)
 +{
 +      xml_node_t *node, *parent, *tnds, *unode;
 +      char *str;
 +      const char *name;
 +      char *uri, *pos;
 +      char *cdata, *cdata_end;
 +      size_t fqdn_len;
 +
 +      wpa_printf(MSG_INFO, "Processing updateNode");
 +      debug_dump_node(ctx, "updateNode", update);
 +
 +      uri = get_spp_attr_value(ctx->xml, update, "managementTreeURI");
 +      if (uri == NULL) {
 +              wpa_printf(MSG_INFO, "No managementTreeURI present");
 +              return -1;
 +      }
 +      wpa_printf(MSG_INFO, "managementTreeUri: '%s'", uri);
 +
 +      name = os_strrchr(uri, '/');
 +      if (name == NULL) {
 +              wpa_printf(MSG_INFO, "Unexpected URI");
 +              xml_node_get_attr_value_free(ctx->xml, uri);
 +              return -1;
 +      }
 +      name++;
 +      wpa_printf(MSG_INFO, "Update interior node: '%s'", name);
 +
 +      str = xml_node_get_text(ctx->xml, update);
 +      if (str == NULL) {
 +              wpa_printf(MSG_INFO, "Could not extract MO text");
 +              xml_node_get_attr_value_free(ctx->xml, uri);
 +              return -1;
 +      }
 +      wpa_printf(MSG_DEBUG, "[hs20] nodeContainer text: '%s'", str);
 +      cdata = strstr(str, "<![CDATA[");
 +      cdata_end = strstr(str, "]]>");
 +      if (cdata && cdata_end && cdata_end > cdata &&
 +          cdata < strstr(str, "MgmtTree") &&
 +          cdata_end > strstr(str, "/MgmtTree")) {
 +              char *tmp;
 +              wpa_printf(MSG_DEBUG, "[hs20] Removing extra CDATA container");
 +              tmp = strdup(cdata + 9);
 +              if (tmp) {
 +                      cdata_end = strstr(tmp, "]]>");
 +                      if (cdata_end)
 +                              *cdata_end = '\0';
 +                      wpa_printf(MSG_DEBUG, "[hs20] nodeContainer text with CDATA container removed: '%s'",
 +                                 tmp);
 +                      tnds = xml_node_from_buf(ctx->xml, tmp);
 +                      free(tmp);
 +              } else
 +                      tnds = NULL;
 +      } else
 +              tnds = xml_node_from_buf(ctx->xml, str);
 +      xml_node_get_text_free(ctx->xml, str);
 +      if (tnds == NULL) {
 +              wpa_printf(MSG_INFO, "[hs20] Could not parse nodeContainer text");
 +              xml_node_get_attr_value_free(ctx->xml, uri);
 +              return -1;
 +      }
 +
 +      unode = tnds_to_mo(ctx->xml, tnds);
 +      xml_node_free(ctx->xml, tnds);
 +      if (unode == NULL) {
 +              wpa_printf(MSG_INFO, "[hs20] Could not parse nodeContainer TNDS text");
 +              xml_node_get_attr_value_free(ctx->xml, uri);
 +              return -1;
 +      }
 +
 +      debug_dump_node(ctx, "Parsed TNDS", unode);
 +
 +      if (get_node_uri(ctx->xml, unode, name) == NULL) {
 +              wpa_printf(MSG_INFO, "[hs20] %s node not found", name);
 +              xml_node_free(ctx->xml, unode);
 +              xml_node_get_attr_value_free(ctx->xml, uri);
 +              return -1;
 +      }
 +
 +      if (os_strncasecmp(uri, "./Wi-Fi/", 8) != 0) {
 +              wpa_printf(MSG_INFO, "Do not allow update outside ./Wi-Fi");
 +              xml_node_free(ctx->xml, unode);
 +              xml_node_get_attr_value_free(ctx->xml, uri);
 +              return -1;
 +      }
 +      pos = uri + 8;
 +
 +      if (ctx->fqdn == NULL) {
 +              wpa_printf(MSG_INFO, "FQDN not known");
 +              xml_node_free(ctx->xml, unode);
 +              xml_node_get_attr_value_free(ctx->xml, uri);
 +              return -1;
 +      }
 +      fqdn_len = os_strlen(ctx->fqdn);
 +      if (os_strncasecmp(pos, ctx->fqdn, fqdn_len) != 0 ||
 +          pos[fqdn_len] != '/') {
 +              wpa_printf(MSG_INFO, "Do not allow update outside ./Wi-Fi/%s",
 +                         ctx->fqdn);
 +              xml_node_free(ctx->xml, unode);
 +              xml_node_get_attr_value_free(ctx->xml, uri);
 +              return -1;
 +      }
 +      pos += fqdn_len + 1;
 +
 +      if (os_strncasecmp(pos, "PerProviderSubscription/", 24) != 0) {
 +              wpa_printf(MSG_INFO, "Do not allow update outside ./Wi-Fi/%s/PerProviderSubscription",
 +                         ctx->fqdn);
 +              xml_node_free(ctx->xml, unode);
 +              xml_node_get_attr_value_free(ctx->xml, uri);
 +              return -1;
 +      }
 +      pos += 24;
 +
 +      wpa_printf(MSG_INFO, "Update command for PPS node %s", pos);
 +
 +      node = get_node(ctx->xml, pps, pos);
 +      if (node) {
 +              parent = xml_node_get_parent(ctx->xml, node);
 +              xml_node_detach(ctx->xml, node);
 +              wpa_printf(MSG_INFO, "Replace '%s' node", name);
 +      } else {
 +              char *pos2;
 +              pos2 = os_strrchr(pos, '/');
 +              if (pos2 == NULL) {
 +                      parent = pps;
 +              } else {
 +                      *pos2 = '\0';
 +                      parent = get_node(ctx->xml, pps, pos);
 +              }
 +              if (parent == NULL) {
 +                      wpa_printf(MSG_INFO, "Could not find parent %s", pos);
 +                      xml_node_free(ctx->xml, unode);
 +                      xml_node_get_attr_value_free(ctx->xml, uri);
 +                      return -1;
 +              }
 +              wpa_printf(MSG_INFO, "Add '%s' node", name);
 +      }
 +      xml_node_add_child(ctx->xml, parent, unode);
 +
 +      xml_node_get_attr_value_free(ctx->xml, uri);
 +
 +      return 0;
 +}
 +
 +
 +static int update_pps(struct hs20_osu_client *ctx, xml_node_t *update,
 +                    const char *pps_fname, xml_node_t *pps)
 +{
 +      wpa_printf(MSG_INFO, "Updating PPS based on updateNode element(s)");
 +      xml_node_for_each_sibling(ctx->xml, update) {
 +              xml_node_for_each_check(ctx->xml, update);
 +              if (process_update_node(ctx, pps, update) < 0)
 +                      return -1;
 +      }
 +
 +      return update_pps_file(ctx, pps_fname, pps);
 +}
 +
 +
 +static void hs20_sub_rem_complete(struct hs20_osu_client *ctx,
 +                                const char *pps_fname)
 +{
 +      /*
 +       * Update wpa_supplicant credentials and reconnect using updated
 +       * information.
 +       */
 +      wpa_printf(MSG_INFO, "Updating wpa_supplicant credentials");
 +      cmd_set_pps(ctx, pps_fname);
 +
 +      if (ctx->no_reconnect)
 +              return;
 +
 +      wpa_printf(MSG_INFO, "Requesting reconnection with updated configuration");
 +      if (wpa_command(ctx->ifname, "INTERWORKING_SELECT auto") < 0)
 +              wpa_printf(MSG_ERROR, "Failed to request wpa_supplicant to reconnect");
 +}
 +
 +
 +static xml_node_t * hs20_spp_upload_mo(struct hs20_osu_client *ctx,
 +                                     xml_node_t *cmd,
 +                                     const char *session_id,
 +                                     const char *pps_fname)
 +{
 +      xml_namespace_t *ns;
 +      xml_node_t *node, *ret_node;
 +      char *urn;
 +
 +      urn = get_spp_attr_value(ctx->xml, cmd, "moURN");
 +      if (!urn) {
 +              wpa_printf(MSG_INFO, "No URN included");
 +              return NULL;
 +      }
 +      wpa_printf(MSG_INFO, "Upload MO request - URN=%s", urn);
 +      if (strcasecmp(urn, URN_HS20_PPS) != 0) {
 +              wpa_printf(MSG_INFO, "Unsupported moURN");
 +              xml_node_get_attr_value_free(ctx->xml, urn);
 +              return NULL;
 +      }
 +      xml_node_get_attr_value_free(ctx->xml, urn);
 +
 +      if (!pps_fname) {
 +              wpa_printf(MSG_INFO, "PPS file name no known");
 +              return NULL;
 +      }
 +
 +      node = build_spp_post_dev_data(ctx, &ns, session_id,
 +                                     "MO upload");
 +      if (node == NULL)
 +              return NULL;
 +      add_mo_container(ctx->xml, ns, node, URN_HS20_PPS, pps_fname);
 +
 +      ret_node = soap_send_receive(ctx->http, node);
 +      if (ret_node == NULL)
 +              return NULL;
 +
 +      debug_dump_node(ctx, "Received response to MO upload", ret_node);
 +
 +      if (hs20_spp_validate(ctx, ret_node, "sppPostDevDataResponse") < 0) {
 +              wpa_printf(MSG_INFO, "SPP validation failed");
 +              xml_node_free(ctx->xml, ret_node);
 +              return NULL;
 +      }
 +
 +      return ret_node;
 +}
 +
 +
 +static int hs20_add_mo(struct hs20_osu_client *ctx, xml_node_t *add_mo,
 +                     char *fname, size_t fname_len)
 +{
 +      char *uri, *urn;
 +      int ret;
 +
 +      debug_dump_node(ctx, "Received addMO", add_mo);
 +
 +      urn = get_spp_attr_value(ctx->xml, add_mo, "moURN");
 +      if (urn == NULL) {
 +              wpa_printf(MSG_INFO, "[hs20] No moURN in addMO");
 +              return -1;
 +      }
 +      wpa_printf(MSG_INFO, "addMO - moURN: '%s'", urn);
 +      if (strcasecmp(urn, URN_HS20_PPS) != 0) {
 +              wpa_printf(MSG_INFO, "[hs20] Unsupported MO in addMO");
 +              xml_node_get_attr_value_free(ctx->xml, urn);
 +              return -1;
 +      }
 +      xml_node_get_attr_value_free(ctx->xml, urn);
 +
 +      uri = get_spp_attr_value(ctx->xml, add_mo, "managementTreeURI");
 +      if (uri == NULL) {
 +              wpa_printf(MSG_INFO, "[hs20] No managementTreeURI in addMO");
 +              return -1;
 +      }
 +      wpa_printf(MSG_INFO, "addMO - managementTreeURI: '%s'", uri);
 +
 +      ret = hs20_add_pps_mo(ctx, uri, add_mo, fname, fname_len);
 +      xml_node_get_attr_value_free(ctx->xml, uri);
 +      return ret;
 +}
 +
 +
 +static int process_spp_user_input_response(struct hs20_osu_client *ctx,
 +                                         const char *session_id,
 +                                         xml_node_t *add_mo)
 +{
 +      int ret;
 +      char fname[300];
 +
 +      debug_dump_node(ctx, "addMO", add_mo);
 +
 +      wpa_printf(MSG_INFO, "Subscription registration completed");
 +
 +      if (hs20_add_mo(ctx, add_mo, fname, sizeof(fname)) < 0) {
 +              wpa_printf(MSG_INFO, "Could not add MO");
 +              ret = hs20_spp_update_response(
 +                      ctx, session_id,
 +                      "Error occurred",
 +                      "MO addition or update failed");
 +              return 0;
 +      }
 +
 +      ret = hs20_spp_update_response(ctx, session_id, "OK", NULL);
 +      if (ret == 0)
 +              hs20_sub_rem_complete(ctx, fname);
 +
 +      return 0;
 +}
 +
 +
 +static xml_node_t * hs20_spp_user_input_completed(struct hs20_osu_client *ctx,
 +                                                  const char *session_id)
 +{
 +      xml_node_t *node, *ret_node;
 +
 +      node = build_spp_post_dev_data(ctx, NULL, session_id,
 +                                     "User input completed");
 +      if (node == NULL)
 +              return NULL;
 +
 +      ret_node = soap_send_receive(ctx->http, node);
 +      if (!ret_node) {
 +              if (soap_reinit_client(ctx->http) < 0)
 +                      return NULL;
 +              wpa_printf(MSG_INFO, "Try to finish with re-opened connection");
 +              node = build_spp_post_dev_data(ctx, NULL, session_id,
 +                                             "User input completed");
 +              if (node == NULL)
 +                      return NULL;
 +              ret_node = soap_send_receive(ctx->http, node);
 +              if (ret_node == NULL)
 +                      return NULL;
 +              wpa_printf(MSG_INFO, "Continue with new connection");
 +      }
 +
 +      if (hs20_spp_validate(ctx, ret_node, "sppPostDevDataResponse") < 0) {
 +              wpa_printf(MSG_INFO, "SPP validation failed");
 +              xml_node_free(ctx->xml, ret_node);
 +              return NULL;
 +      }
 +
 +      return ret_node;
 +}
 +
 +
 +static xml_node_t * hs20_spp_get_certificate(struct hs20_osu_client *ctx,
 +                                           xml_node_t *cmd,
 +                                           const char *session_id,
 +                                           const char *pps_fname)
 +{
 +      xml_namespace_t *ns;
 +      xml_node_t *node, *ret_node;
 +      int res;
 +
 +      wpa_printf(MSG_INFO, "Client certificate enrollment");
 +
 +      res = osu_get_certificate(ctx, cmd);
 +      if (res < 0)
 +              wpa_printf(MSG_INFO, "EST simpleEnroll failed");
 +
 +      node = build_spp_post_dev_data(ctx, &ns, session_id,
 +                                     res == 0 ?
 +                                     "Certificate enrollment completed" :
 +                                     "Certificate enrollment failed");
 +      if (node == NULL)
 +              return NULL;
 +
 +      ret_node = soap_send_receive(ctx->http, node);
 +      if (ret_node == NULL)
 +              return NULL;
 +
 +      debug_dump_node(ctx, "Received response to certificate enrollment "
 +                      "completed", ret_node);
 +
 +      if (hs20_spp_validate(ctx, ret_node, "sppPostDevDataResponse") < 0) {
 +              wpa_printf(MSG_INFO, "SPP validation failed");
 +              xml_node_free(ctx->xml, ret_node);
 +              return NULL;
 +      }
 +
 +      return ret_node;
 +}
 +
 +
 +static int hs20_spp_exec(struct hs20_osu_client *ctx, xml_node_t *exec,
 +                       const char *session_id, const char *pps_fname,
 +                       xml_node_t *pps, xml_node_t **ret_node)
 +{
 +      xml_node_t *cmd;
 +      const char *name;
 +      char *uri;
 +      char *id = strdup(session_id);
 +
 +      if (id == NULL)
 +              return -1;
 +
 +      *ret_node = NULL;
 +
 +      debug_dump_node(ctx, "exec", exec);
 +
 +      xml_node_for_each_child(ctx->xml, cmd, exec) {
 +              xml_node_for_each_check(ctx->xml, cmd);
 +              break;
 +      }
 +      if (!cmd) {
 +              wpa_printf(MSG_INFO, "exec command element not found (cmd=%p)",
 +                         cmd);
 +              free(id);
 +              return -1;
 +      }
 +
 +      name = xml_node_get_localname(ctx->xml, cmd);
 +
 +      if (strcasecmp(name, "launchBrowserToURI") == 0) {
 +              int res;
 +              uri = xml_node_get_text(ctx->xml, cmd);
 +              if (!uri) {
 +                      wpa_printf(MSG_INFO, "No URI found");
 +                      free(id);
 +                      return -1;
 +              }
 +              wpa_printf(MSG_INFO, "Launch browser to URI '%s'", uri);
 +              write_summary(ctx, "Launch browser to URI '%s'", uri);
 +              res = hs20_web_browser(uri);
 +              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 - URL: %s ca_fname: %s",
++                 url, ctx->ca_fname ? ctx->ca_fname : "N/A");
 +
 +      os_free(ctx->server_url);
 +      ctx->server_url = os_strdup(url);
 +
 +      if (soap_init_client(ctx->http, url, ctx->ca_fname, NULL, NULL, NULL,
 +                           NULL) < 0)
 +              return -1;
 +      spp_post_dev_data(ctx, SPP_SUBSCRIPTION_REGISTRATION,
 +                        "Subscription registration", NULL, NULL);
 +
 +      return ctx->pps_cred_set ? 0 : -1;
 +}
 +
 +
 +int cmd_sim_prov(struct hs20_osu_client *ctx, const char *url)
 +{
 +      if (url == NULL) {
 +              wpa_printf(MSG_INFO, "Invalid prov command (missing URL)");
 +              return -1;
 +      }
 +
 +      wpa_printf(MSG_INFO, "SIM provisioning requested");
 +
 +      os_free(ctx->server_url);
 +      ctx->server_url = os_strdup(url);
 +
 +      wpa_printf(MSG_INFO, "Wait for IP address before starting SIM provisioning");
 +
 +      if (wait_ip_addr(ctx->ifname, 15) < 0) {
 +              wpa_printf(MSG_INFO, "Could not get IP address for WLAN - try connection anyway");
 +      }
 +
 +      if (soap_init_client(ctx->http, url, ctx->ca_fname, NULL, NULL, NULL,
 +                           NULL) < 0)
 +              return -1;
 +      spp_post_dev_data(ctx, SPP_SUBSCRIPTION_REGISTRATION,
 +                        "Subscription provisioning", NULL, NULL);
 +
 +      return ctx->pps_cred_set ? 0 : -1;
 +}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3a8f90e40ce20c7710ed32631bd6b0cbee246750
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,398 @@@
++This patch adds support for TLS SessionTicket extension (RFC 5077) for
++the parts used by EAP-FAST (RFC 4851).
++
++This is based on the patch from Alexey Kobozev <akobozev@cisco.com>
++(sent to openssl-dev mailing list on Tue, 07 Jun 2005 15:40:58 +0300).
++
++OpenSSL 0.9.8zf does not enable TLS extension support by default, so it
++will need to be enabled by adding enable-tlsext to config script
++command line.
++
++
++diff -upr openssl-0.9.8zf.orig/ssl/s3_clnt.c openssl-0.9.8zf/ssl/s3_clnt.c
++--- openssl-0.9.8zf.orig/ssl/s3_clnt.c        2015-03-19 15:46:46.000000000 +0200
+++++ openssl-0.9.8zf/ssl/s3_clnt.c     2015-03-24 16:19:14.043911769 +0200
++@@ -760,6 +760,23 @@ int ssl3_get_server_hello(SSL *s)
++         goto f_err;
++     }
++ 
+++#ifndef OPENSSL_NO_TLSEXT
+++    /* check if we want to resume the session based on external pre-shared secret */
+++    if (s->version >= TLS1_VERSION && s->tls_session_secret_cb) {
+++        SSL_CIPHER *pref_cipher = NULL;
+++
+++        s->session->master_key_length = sizeof(s->session->master_key);
+++        if (s->tls_session_secret_cb(s, s->session->master_key,
+++                                  &s->session->master_key_length,
+++                                  NULL, &pref_cipher,
+++                                  s->tls_session_secret_cb_arg)) {
+++            s->session->cipher = pref_cipher ?
+++                 pref_cipher : ssl_get_cipher_by_char(s, p + j);
+++         s->s3->flags |= SSL3_FLAGS_CCS_OK;
+++     }
+++    }
+++#endif /* OPENSSL_NO_TLSEXT */
+++
++     if (j != 0 && j == s->session->session_id_length
++         && memcmp(p, s->session->session_id, j) == 0) {
++         if (s->sid_ctx_length != s->session->sid_ctx_length
++@@ -2684,12 +2701,8 @@ int ssl3_check_finished(SSL *s)
++ {
++     int ok;
++     long n;
++-    /*
++-     * If we have no ticket or session ID is non-zero length (a match of a
++-     * non-zero session length would never reach here) it cannot be a resumed
++-     * session.
++-     */
++-    if (!s->session->tlsext_tick || s->session->session_id_length)
+++    /* If we have no ticket it cannot be a resumed session. */
+++    if (!s->session->tlsext_tick)
++         return 1;
++     /*
++      * this function is called when we really expect a Certificate message,
++diff -upr openssl-0.9.8zf.orig/ssl/s3_srvr.c openssl-0.9.8zf/ssl/s3_srvr.c
++--- openssl-0.9.8zf.orig/ssl/s3_srvr.c        2015-03-19 15:46:46.000000000 +0200
+++++ openssl-0.9.8zf/ssl/s3_srvr.c     2015-03-24 16:23:34.567909681 +0200
++@@ -999,6 +999,59 @@ int ssl3_get_client_hello(SSL *s)
++         SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO, SSL_R_CLIENTHELLO_TLSEXT);
++         goto err;
++     }
+++
+++    /* Check if we want to use external pre-shared secret for this
+++     * handshake for not reused session only. We need to generate
+++     * server_random before calling tls_session_secret_cb in order to allow
+++     * SessionTicket processing to use it in key derivation. */
+++    {
+++        unsigned long Time;
+++     unsigned char *pos;
+++     Time = (unsigned long)time(NULL);                       /* Time */
+++     pos = s->s3->server_random;
+++     l2n(Time, pos);
+++     if (RAND_pseudo_bytes(pos, SSL3_RANDOM_SIZE - 4) <= 0) {
+++             al = SSL_AD_INTERNAL_ERROR;
+++             goto f_err;
+++     }
+++    }
+++
+++    if (!s->hit && s->version >= TLS1_VERSION && s->tls_session_secret_cb) {
+++     SSL_CIPHER *pref_cipher = NULL;
+++
+++     s->session->master_key_length = sizeof(s->session->master_key);
+++     if (s->tls_session_secret_cb(s, s->session->master_key,
+++                                  &s->session->master_key_length, 
+++                                  ciphers, &pref_cipher,
+++                                  s->tls_session_secret_cb_arg)) {
+++         s->hit = 1;
+++         s->session->ciphers = ciphers;
+++         s->session->verify_result = X509_V_OK;
+++
+++         ciphers = NULL;
+++
+++         /* check if some cipher was preferred by call back */
+++         pref_cipher = pref_cipher ? pref_cipher :
+++                 ssl3_choose_cipher(s, s->session->ciphers,
+++                                    SSL_get_ciphers(s));
+++         if (pref_cipher == NULL) {
+++             al = SSL_AD_HANDSHAKE_FAILURE;
+++             SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO, SSL_R_NO_SHARED_CIPHER);
+++             goto f_err;
+++         }
+++
+++         s->session->cipher = pref_cipher;
+++
+++         if (s->cipher_list)
+++             sk_SSL_CIPHER_free(s->cipher_list);
+++
+++         if (s->cipher_list_by_id)
+++             sk_SSL_CIPHER_free(s->cipher_list_by_id);
+++
+++         s->cipher_list = sk_SSL_CIPHER_dup(s->session->ciphers);
+++         s->cipher_list_by_id = sk_SSL_CIPHER_dup(s->session->ciphers);
+++     }
+++    }
++ #endif
++     /*
++      * Worst case, we will use the NULL compression, but if we have other
++@@ -1143,15 +1196,21 @@ int ssl3_send_server_hello(SSL *s)
++     unsigned char *buf;
++     unsigned char *p, *d;
++     int i, sl;
++-    unsigned long l, Time;
+++    unsigned long l;
+++#ifdef OPENSSL_NO_TLSEXT
+++    unsigned long Time;
+++#endif
++ 
++     if (s->state == SSL3_ST_SW_SRVR_HELLO_A) {
++         buf = (unsigned char *)s->init_buf->data;
+++#ifdef OPENSSL_NO_TLSEXT
++         p = s->s3->server_random;
+++        /* Generate server_random if it was not needed previously */
++         Time = (unsigned long)time(NULL); /* Time */
++         l2n(Time, p);
++         if (RAND_pseudo_bytes(p, SSL3_RANDOM_SIZE - 4) <= 0)
++             return -1;
+++#endif
++         /* Do the message type and length last */
++         d = p = &(buf[4]);
++ 
++diff -upr openssl-0.9.8zf.orig/ssl/ssl_err.c openssl-0.9.8zf/ssl/ssl_err.c
++--- openssl-0.9.8zf.orig/ssl/ssl_err.c        2015-03-19 15:46:46.000000000 +0200
+++++ openssl-0.9.8zf/ssl/ssl_err.c     2015-03-24 16:35:58.627903717 +0200
++@@ -316,6 +316,7 @@ static ERR_STRING_DATA SSL_str_functs[]
++     {ERR_FUNC(SSL_F_TLS1_ENC), "TLS1_ENC"},
++     {ERR_FUNC(SSL_F_TLS1_SETUP_KEY_BLOCK), "TLS1_SETUP_KEY_BLOCK"},
++     {ERR_FUNC(SSL_F_WRITE_PENDING), "WRITE_PENDING"},
+++    {ERR_FUNC(SSL_F_SSL_SET_SESSION_TICKET_EXT), "SSL_set_session_ticket_ext"},
++     {0, NULL}
++ };
++ 
++diff -upr openssl-0.9.8zf.orig/ssl/ssl.h openssl-0.9.8zf/ssl/ssl.h
++--- openssl-0.9.8zf.orig/ssl/ssl.h    2015-03-19 15:46:46.000000000 +0200
+++++ openssl-0.9.8zf/ssl/ssl.h 2015-03-24 16:25:44.339908641 +0200
++@@ -349,6 +349,7 @@ extern "C" {
++  * function parameters used to prototype callbacks in SSL_CTX.
++  */
++ typedef struct ssl_st *ssl_crock_st;
+++typedef struct tls_session_ticket_ext_st TLS_SESSION_TICKET_EXT;
++ 
++ /* used to hold info on the particular ciphers used */
++ typedef struct ssl_cipher_st {
++@@ -366,6 +367,12 @@ typedef struct ssl_cipher_st {
++ 
++ DECLARE_STACK_OF(SSL_CIPHER)
++ 
+++typedef int (*tls_session_ticket_ext_cb_fn)(SSL *s, const unsigned char *data,
+++                                         int len, void *arg);
+++typedef int (*tls_session_secret_cb_fn)(SSL *s, void *secret, int *secret_len,
+++                                     STACK_OF(SSL_CIPHER) *peer_ciphers,
+++                                     SSL_CIPHER **cipher, void *arg);
+++
++ /* Used to hold functions for SSLv2 or SSLv3/TLSv1 functions */
++ typedef struct ssl_method_st {
++     int version;
++@@ -1116,6 +1123,18 @@ struct ssl_st {
++     int tlsext_ocsp_resplen;
++     /* RFC4507 session ticket expected to be received or sent */
++     int tlsext_ticket_expected;
+++
+++    /* TLS Session Ticket extension override */
+++    TLS_SESSION_TICKET_EXT *tlsext_session_ticket;
+++
+++    /* TLS Session Ticket extension callback */
+++    tls_session_ticket_ext_cb_fn tls_session_ticket_ext_cb;
+++    void *tls_session_ticket_ext_cb_arg;
+++
+++    /* TLS pre-shared secret session resumption */
+++    tls_session_secret_cb_fn tls_session_secret_cb;
+++    void *tls_session_secret_cb_arg;
+++
++     SSL_CTX *initial_ctx;       /* initial ctx, used to store sessions */
++ #  define session_ctx initial_ctx
++ # else
++@@ -1772,6 +1791,17 @@ void *SSL_COMP_get_compression_methods(v
++ int SSL_COMP_add_compression_method(int id, void *cm);
++ # endif
++ 
+++/* TLS extensions functions */
+++int SSL_set_session_ticket_ext(SSL *s, void *ext_data, int ext_len);
+++
+++int SSL_set_session_ticket_ext_cb(SSL *s, tls_session_ticket_ext_cb_fn cb,
+++                               void *arg);
+++
+++/* Pre-shared secret session resumption functions */
+++int SSL_set_session_secret_cb(SSL *s,
+++                           tls_session_secret_cb_fn tls_session_secret_cb,
+++                           void *arg);
+++
++ /* BEGIN ERROR CODES */
++ /*
++  * The following lines are auto generated by the script mkerr.pl. Any changes
++@@ -1977,6 +2007,7 @@ void ERR_load_SSL_strings(void);
++ # define SSL_F_TLS1_ENC                                   210
++ # define SSL_F_TLS1_SETUP_KEY_BLOCK                       211
++ # define SSL_F_WRITE_PENDING                              212
+++#define SSL_F_SSL_SET_SESSION_TICKET_EXT                  213
++ 
++ /* Reason codes. */
++ # define SSL_R_APP_DATA_IN_HANDSHAKE                      100
++diff -upr openssl-0.9.8zf.orig/ssl/ssl_sess.c openssl-0.9.8zf/ssl/ssl_sess.c
++--- openssl-0.9.8zf.orig/ssl/ssl_sess.c       2015-03-19 15:46:46.000000000 +0200
+++++ openssl-0.9.8zf/ssl/ssl_sess.c    2015-03-24 16:28:04.819907515 +0200
++@@ -716,6 +716,61 @@ long SSL_CTX_get_timeout(const SSL_CTX *
++     return (s->session_timeout);
++ }
++ 
+++#ifndef OPENSSL_NO_TLSEXT
+++int SSL_set_session_secret_cb(
+++     SSL *s,
+++     int (*tls_session_secret_cb)(SSL *s, void *secret, int *secret_len,
+++                                  STACK_OF(SSL_CIPHER) *peer_ciphers,
+++                                  SSL_CIPHER **cipher, void *arg), void *arg)
+++{
+++    if (s == NULL)
+++         return 0;
+++    s->tls_session_secret_cb = tls_session_secret_cb;
+++    s->tls_session_secret_cb_arg = arg;
+++    return 1;
+++}
+++
+++int SSL_set_session_ticket_ext_cb(SSL *s, tls_session_ticket_ext_cb_fn cb,
+++                               void *arg)
+++{
+++    if (s == NULL)
+++         return 0;
+++    s->tls_session_ticket_ext_cb = cb;
+++    s->tls_session_ticket_ext_cb_arg = arg;
+++    return 1;
+++}
+++
+++int SSL_set_session_ticket_ext(SSL *s, void *ext_data, int ext_len)
+++{
+++    if (s->version >= TLS1_VERSION) {
+++     if (s->tlsext_session_ticket) {
+++         OPENSSL_free(s->tlsext_session_ticket);
+++         s->tlsext_session_ticket = NULL;
+++     }
+++
+++     s->tlsext_session_ticket = OPENSSL_malloc(
+++             sizeof(TLS_SESSION_TICKET_EXT) + ext_len);
+++     if (!s->tlsext_session_ticket) {
+++         SSLerr(SSL_F_SSL_SET_SESSION_TICKET_EXT, ERR_R_MALLOC_FAILURE);
+++         return 0;
+++     }
+++
+++     if (ext_data) {
+++         s->tlsext_session_ticket->length = ext_len;
+++         s->tlsext_session_ticket->data = s->tlsext_session_ticket + 1;
+++         memcpy(s->tlsext_session_ticket->data, ext_data, ext_len);
+++     } else {
+++             s->tlsext_session_ticket->length = 0;
+++             s->tlsext_session_ticket->data = NULL;
+++     }
+++
+++     return 1;
+++    }
+++
+++    return 0;
+++}
+++#endif /* OPENSSL_NO_TLSEXT */
+++
++ typedef struct timeout_param_st {
++     SSL_CTX *ctx;
++     long time;
++diff -upr openssl-0.9.8zf.orig/ssl/t1_lib.c openssl-0.9.8zf/ssl/t1_lib.c
++--- openssl-0.9.8zf.orig/ssl/t1_lib.c 2015-03-19 15:46:46.000000000 +0200
+++++ openssl-0.9.8zf/ssl/t1_lib.c      2015-03-24 16:32:46.923905254 +0200
++@@ -108,6 +108,11 @@ int tls1_new(SSL *s)
++ 
++ void tls1_free(SSL *s)
++ {
+++#ifndef OPENSSL_NO_TLSEXT
+++    if (s->tlsext_session_ticket) {
+++     OPENSSL_free(s->tlsext_session_ticket);
+++    }
+++#endif
++     ssl3_free(s);
++ }
++ 
++@@ -206,8 +211,20 @@ unsigned char *ssl_add_clienthello_tlsex
++         int ticklen;
++         if (!s->new_session && s->session && s->session->tlsext_tick)
++             ticklen = s->session->tlsext_ticklen;
++-        else
+++     else if (s->session && s->tlsext_session_ticket &&
+++              s->tlsext_session_ticket->data) {
+++         ticklen = s->tlsext_session_ticket->length;
+++         s->session->tlsext_tick = OPENSSL_malloc(ticklen);
+++         if (!s->session->tlsext_tick)
+++             return NULL;
+++         memcpy(s->session->tlsext_tick, s->tlsext_session_ticket->data,
+++                ticklen);
+++         s->session->tlsext_ticklen = ticklen;
+++     } else
++             ticklen = 0;
+++     if (ticklen == 0 && s->tlsext_session_ticket &&
+++         s->tlsext_session_ticket->data == NULL)
+++         goto skip_ext;
++         /*
++          * Check for enough room 2 for extension type, 2 for len rest for
++          * ticket
++@@ -221,6 +238,7 @@ unsigned char *ssl_add_clienthello_tlsex
++             ret += ticklen;
++         }
++     }
+++skip_ext:
++ 
++     if (s->tlsext_status_type == TLSEXT_STATUSTYPE_ocsp &&
++         s->version != DTLS1_VERSION) {
++@@ -560,6 +578,14 @@ int ssl_parse_clienthello_tlsext(SSL *s,
++             if (!ssl_parse_clienthello_renegotiate_ext(s, data, size, al))
++                 return 0;
++             renegotiate_seen = 1;
+++     } else if (type == TLSEXT_TYPE_session_ticket) {
+++         if (s->tls_session_ticket_ext_cb &&
+++             !s->tls_session_ticket_ext_cb(s, data, size,
+++                                           s->tls_session_ticket_ext_cb_arg))
+++         {
+++             *al = TLS1_AD_INTERNAL_ERROR;
+++             return 0;
+++         }
++         } else if (type == TLSEXT_TYPE_status_request &&
++                    s->version != DTLS1_VERSION && s->ctx->tlsext_status_cb) {
++ 
++@@ -710,6 +736,13 @@ int ssl_parse_serverhello_tlsext(SSL *s,
++             }
++             tlsext_servername = 1;
++         } else if (type == TLSEXT_TYPE_session_ticket) {
+++         if (s->tls_session_ticket_ext_cb &&
+++             !s->tls_session_ticket_ext_cb(
+++                     s, data, size,
+++                     s->tls_session_ticket_ext_cb_arg)) {
+++             *al = TLS1_AD_INTERNAL_ERROR;
+++             return 0;
+++         }
++             if ((SSL_get_options(s) & SSL_OP_NO_TICKET)
++                 || (size > 0)) {
++                 *al = TLS1_AD_UNSUPPORTED_EXTENSION;
++@@ -993,6 +1026,14 @@ int tls1_process_ticket(SSL *s, unsigned
++                 s->tlsext_ticket_expected = 1;
++                 return 0;       /* Cache miss */
++             }
+++         if (s->tls_session_secret_cb) {
+++             /* Indicate cache miss here and instead of
+++              * generating the session from ticket now,
+++              * trigger abbreviated handshake based on
+++              * external mechanism to calculate the master
+++              * secret later. */
+++             return 0;
+++         }
++             return tls_decrypt_ticket(s, p, size, session_id, len, ret);
++         }
++         p += size;
++diff -upr openssl-0.9.8zf.orig/ssl/tls1.h openssl-0.9.8zf/ssl/tls1.h
++--- openssl-0.9.8zf.orig/ssl/tls1.h   2015-03-19 15:46:46.000000000 +0200
+++++ openssl-0.9.8zf/ssl/tls1.h        2015-03-24 16:33:31.855904894 +0200
++@@ -460,6 +460,12 @@ SSL_CTX_callback_ctrl(ssl,SSL_CTRL_SET_T
++ #  define TLS_MD_MASTER_SECRET_CONST    "\x6d\x61\x73\x74\x65\x72\x20\x73\x65\x63\x72\x65\x74"
++ # endif
++ 
+++/* TLS extension struct */
+++struct tls_session_ticket_ext_st {
+++    unsigned short length;
+++    void *data;
+++};
+++
++ #ifdef  __cplusplus
++ }
++ #endif
++diff -upr openssl-0.9.8zf.orig/util/ssleay.num openssl-0.9.8zf/util/ssleay.num
++--- openssl-0.9.8zf.orig/util/ssleay.num      2015-03-19 15:47:15.000000000 +0200
+++++ openssl-0.9.8zf/util/ssleay.num   2015-03-24 16:33:51.127904739 +0200
++@@ -242,3 +242,5 @@ SSL_set_SSL_CTX
++ SSL_get_servername                      291  EXIST::FUNCTION:TLSEXT
++ SSL_get_servername_type                 292  EXIST::FUNCTION:TLSEXT
++ SSL_CTX_set_client_cert_engine          293  EXIST::FUNCTION:ENGINE
+++SSL_set_session_ticket_ext           306     EXIST::FUNCTION:TLSEXT
+++SSL_set_session_secret_cb            307     EXIST::FUNCTION:TLSEXT
index 7c55146b2c5c2780d2e1e2d4f0b23c5d72bd258c,0000000000000000000000000000000000000000..a096de4d3e5147da9d1f0ccfc9d7432bbb3b3289
mode 100644,000000..100644
--- /dev/null
@@@ -1,484 -1,0 +1,488 @@@
-       /* Acct-Session-Id should be unique over reboots. If reliable clock is
-        * not available, this could be replaced with reboot counter, etc. */
 +/*
 + * hostapd / RADIUS Accounting
 + * Copyright (c) 2002-2009, 2012, Jouni Malinen <j@w1.fi>
 + *
 + * This software may be distributed under the terms of the BSD license.
 + * See README for more details.
 + */
 +
 +#include "utils/includes.h"
 +
 +#include "utils/common.h"
 +#include "utils/eloop.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"
 +#include "ieee802_1x.h"
 +#include "ap_config.h"
 +#include "sta_info.h"
 +#include "ap_drv_ops.h"
 +#include "accounting.h"
 +
 +
 +/* Default interval in seconds for polling TX/RX octets from the driver if
 + * STA is not using interim accounting. This detects wrap arounds for
 + * input/output octets and updates Acct-{Input,Output}-Gigawords. */
 +#define ACCT_DEFAULT_UPDATE_INTERVAL 300
 +
 +static void accounting_sta_interim(struct hostapd_data *hapd,
 +                                 struct sta_info *sta);
 +
 +
 +static struct radius_msg * accounting_msg(struct hostapd_data *hapd,
 +                                        struct sta_info *sta,
 +                                        int status_type)
 +{
 +      struct radius_msg *msg;
 +      char buf[128];
 +      u8 *val;
 +      size_t len;
 +      int i;
 +      struct wpabuf *b;
 +
 +      msg = radius_msg_new(RADIUS_CODE_ACCOUNTING_REQUEST,
 +                           radius_client_get_id(hapd->radius));
 +      if (msg == NULL) {
 +              wpa_printf(MSG_INFO, "Could not create new RADIUS packet");
 +              return NULL;
 +      }
 +
 +      if (sta) {
 +              radius_msg_make_authenticator(msg, (u8 *) sta, sizeof(*sta));
 +
 +              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));
 +      }
 +
 +      if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_STATUS_TYPE,
 +                                     status_type)) {
 +              wpa_printf(MSG_INFO, "Could not add Acct-Status-Type");
 +              goto fail;
 +      }
 +
 +      if (!hostapd_config_get_radius_attr(hapd->conf->radius_acct_req_attr,
 +                                          RADIUS_ATTR_ACCT_AUTHENTIC) &&
 +          !radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_AUTHENTIC,
 +                                     hapd->conf->ieee802_1x ?
 +                                     RADIUS_ACCT_AUTHENTIC_RADIUS :
 +                                     RADIUS_ACCT_AUTHENTIC_LOCAL)) {
 +              wpa_printf(MSG_INFO, "Could not add Acct-Authentic");
 +              goto fail;
 +      }
 +
 +      if (sta) {
 +              /* Use 802.1X identity if available */
 +              val = ieee802_1x_get_identity(sta->eapol_sm, &len);
 +
 +              /* Use RADIUS ACL identity if 802.1X provides no identity */
 +              if (!val && sta->identity) {
 +                      val = (u8 *) sta->identity;
 +                      len = os_strlen(sta->identity);
 +              }
 +
 +              /* Use STA MAC if neither 802.1X nor RADIUS ACL provided
 +               * identity */
 +              if (!val) {
 +                      os_snprintf(buf, sizeof(buf), RADIUS_ADDR_FORMAT,
 +                                  MAC2STR(sta->addr));
 +                      val = (u8 *) buf;
 +                      len = os_strlen(buf);
 +              }
 +
 +              if (!radius_msg_add_attr(msg, RADIUS_ATTR_USER_NAME, val,
 +                                       len)) {
 +                      wpa_printf(MSG_INFO, "Could not add User-Name");
 +                      goto fail;
 +              }
 +      }
 +
 +      if (add_common_radius_attr(hapd, hapd->conf->radius_acct_req_attr, sta,
 +                                 msg) < 0)
 +              goto fail;
 +
 +      if (sta) {
 +              for (i = 0; ; i++) {
 +                      val = ieee802_1x_get_radius_class(sta->eapol_sm, &len,
 +                                                        i);
 +                      if (val == NULL)
 +                              break;
 +
 +                      if (!radius_msg_add_attr(msg, RADIUS_ATTR_CLASS,
 +                                               val, len)) {
 +                              wpa_printf(MSG_INFO, "Could not add Class");
 +                              goto fail;
 +                      }
 +              }
 +
 +              b = ieee802_1x_get_radius_cui(sta->eapol_sm);
 +              if (b &&
 +                  !radius_msg_add_attr(msg,
 +                                       RADIUS_ATTR_CHARGEABLE_USER_IDENTITY,
 +                                       wpabuf_head(b), wpabuf_len(b))) {
 +                      wpa_printf(MSG_ERROR, "Could not add CUI");
 +                      goto fail;
 +              }
 +
 +              if (!b && sta->radius_cui &&
 +                  !radius_msg_add_attr(msg,
 +                                       RADIUS_ATTR_CHARGEABLE_USER_IDENTITY,
 +                                       (u8 *) sta->radius_cui,
 +                                       os_strlen(sta->radius_cui))) {
 +                      wpa_printf(MSG_ERROR, "Could not add CUI from ACL");
 +                      goto fail;
 +              }
 +      }
 +
 +      return msg;
 +
 + fail:
 +      radius_msg_free(msg);
 +      return NULL;
 +}
 +
 +
 +static int accounting_sta_update_stats(struct hostapd_data *hapd,
 +                                     struct sta_info *sta,
 +                                     struct hostap_sta_driver_data *data)
 +{
 +      if (hostapd_drv_read_sta_data(hapd, data, sta->addr))
 +              return -1;
 +
 +      if (sta->last_rx_bytes > data->rx_bytes)
 +              sta->acct_input_gigawords++;
 +      if (sta->last_tx_bytes > data->tx_bytes)
 +              sta->acct_output_gigawords++;
 +      sta->last_rx_bytes = data->rx_bytes;
 +      sta->last_tx_bytes = data->tx_bytes;
 +
 +      hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
 +                     HOSTAPD_LEVEL_DEBUG, "updated TX/RX stats: "
 +                     "Acct-Input-Octets=%lu Acct-Input-Gigawords=%u "
 +                     "Acct-Output-Octets=%lu Acct-Output-Gigawords=%u",
 +                     sta->last_rx_bytes, sta->acct_input_gigawords,
 +                     sta->last_tx_bytes, sta->acct_output_gigawords);
 +
 +      return 0;
 +}
 +
 +
 +static void accounting_interim_update(void *eloop_ctx, void *timeout_ctx)
 +{
 +      struct hostapd_data *hapd = eloop_ctx;
 +      struct sta_info *sta = timeout_ctx;
 +      int interval;
 +
 +      if (sta->acct_interim_interval) {
 +              accounting_sta_interim(hapd, sta);
 +              interval = sta->acct_interim_interval;
 +      } else {
 +              struct hostap_sta_driver_data data;
 +              accounting_sta_update_stats(hapd, sta, &data);
 +              interval = ACCT_DEFAULT_UPDATE_INTERVAL;
 +      }
 +
 +      eloop_register_timeout(interval, 0, accounting_interim_update,
 +                             hapd, sta);
 +}
 +
 +
 +/**
 + * accounting_sta_start - Start STA accounting
 + * @hapd: hostapd BSS data
 + * @sta: The station
 + */
 +void accounting_sta_start(struct hostapd_data *hapd, struct sta_info *sta)
 +{
 +      struct radius_msg *msg;
 +      int interval;
 +
 +      if (sta->acct_session_started)
 +              return;
 +
 +      hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
 +                     HOSTAPD_LEVEL_INFO,
 +                     "starting accounting session %08X-%08X",
 +                     sta->acct_session_id_hi, sta->acct_session_id_lo);
 +
 +      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);
 +
 +      if (!hapd->conf->radius->acct_server)
 +              return;
 +
 +      if (sta->acct_interim_interval)
 +              interval = sta->acct_interim_interval;
 +      else
 +              interval = ACCT_DEFAULT_UPDATE_INTERVAL;
 +      eloop_register_timeout(interval, 0, accounting_interim_update,
 +                             hapd, sta);
 +
 +      msg = accounting_msg(hapd, sta, RADIUS_ACCT_STATUS_TYPE_START);
 +      if (msg &&
 +          radius_client_send(hapd->radius, msg, RADIUS_ACCT, sta->addr) < 0)
 +              radius_msg_free(msg);
 +
 +      sta->acct_session_started = 1;
 +}
 +
 +
 +static void accounting_sta_report(struct hostapd_data *hapd,
 +                                struct sta_info *sta, int stop)
 +{
 +      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;
 +
 +      if (!hapd->conf->radius->acct_server)
 +              return;
 +
 +      msg = accounting_msg(hapd, sta,
 +                           stop ? RADIUS_ACCT_STATUS_TYPE_STOP :
 +                           RADIUS_ACCT_STATUS_TYPE_INTERIM_UPDATE);
 +      if (!msg) {
 +              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,
 +                                     diff.sec)) {
 +              wpa_printf(MSG_INFO, "Could not add Acct-Session-Time");
 +              goto fail;
 +      }
 +
 +      if (accounting_sta_update_stats(hapd, sta, &data) == 0) {
 +              if (!radius_msg_add_attr_int32(msg,
 +                                             RADIUS_ATTR_ACCT_INPUT_PACKETS,
 +                                             data.rx_packets)) {
 +                      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)) {
 +                      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)) {
 +                      wpa_printf(MSG_INFO, "Could not add Acct-Input-Octets");
 +                      goto fail;
 +              }
 +              gigawords = sta->acct_input_gigawords;
 +#if __WORDSIZE == 64
 +              gigawords += data.rx_bytes >> 32;
 +#endif
 +              if (gigawords &&
 +                  !radius_msg_add_attr_int32(
 +                          msg, RADIUS_ATTR_ACCT_INPUT_GIGAWORDS,
 +                          gigawords)) {
 +                      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)) {
 +                      wpa_printf(MSG_INFO, "Could not add Acct-Output-Octets");
 +                      goto fail;
 +              }
 +              gigawords = sta->acct_output_gigawords;
 +#if __WORDSIZE == 64
 +              gigawords += data.tx_bytes >> 32;
 +#endif
 +              if (gigawords &&
 +                  !radius_msg_add_attr_int32(
 +                          msg, RADIUS_ATTR_ACCT_OUTPUT_GIGAWORDS,
 +                          gigawords)) {
 +                      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)) {
 +              wpa_printf(MSG_INFO, "Could not add Event-Timestamp");
 +              goto fail;
 +      }
 +
 +      if (eloop_terminated())
 +              cause = RADIUS_ACCT_TERMINATE_CAUSE_ADMIN_REBOOT;
 +
 +      if (stop && cause &&
 +          !radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_TERMINATE_CAUSE,
 +                                     cause)) {
 +              wpa_printf(MSG_INFO, "Could not add Acct-Terminate-Cause");
 +              goto fail;
 +      }
 +
 +      if (radius_client_send(hapd->radius, msg,
 +                             stop ? RADIUS_ACCT : RADIUS_ACCT_INTERIM,
 +                             sta->addr) < 0)
 +              goto fail;
 +      return;
 +
 + fail:
 +      radius_msg_free(msg);
 +}
 +
 +
 +/**
 + * accounting_sta_interim - Send a interim STA accounting report
 + * @hapd: hostapd BSS data
 + * @sta: The station
 + */
 +static void accounting_sta_interim(struct hostapd_data *hapd,
 +                                 struct sta_info *sta)
 +{
 +      if (sta->acct_session_started)
 +              accounting_sta_report(hapd, sta, 0);
 +}
 +
 +
 +/**
 + * accounting_sta_stop - Stop STA accounting
 + * @hapd: hostapd BSS data
 + * @sta: The station
 + */
 +void accounting_sta_stop(struct hostapd_data *hapd, struct sta_info *sta)
 +{
 +      if (sta->acct_session_started) {
 +              accounting_sta_report(hapd, sta, 1);
 +              eloop_cancel_timeout(accounting_interim_update, hapd, sta);
 +              hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
 +                             HOSTAPD_LEVEL_INFO,
 +                             "stopped accounting session %08X-%08X",
 +                             sta->acct_session_id_hi,
 +                             sta->acct_session_id_lo);
 +              sta->acct_session_started = 0;
 +      }
 +}
 +
 +
 +void accounting_sta_get_id(struct hostapd_data *hapd,
 +                                struct sta_info *sta)
 +{
 +      sta->acct_session_id_lo = hapd->acct_session_id_lo++;
 +      if (hapd->acct_session_id_lo == 0) {
 +              hapd->acct_session_id_hi++;
 +      }
 +      sta->acct_session_id_hi = hapd->acct_session_id_hi;
 +}
 +
 +
 +/**
 + * accounting_receive - Process the RADIUS frames from Accounting Server
 + * @msg: RADIUS response message
 + * @req: RADIUS request message
 + * @shared_secret: RADIUS shared secret
 + * @shared_secret_len: Length of shared_secret in octets
 + * @data: Context data (struct hostapd_data *)
 + * Returns: Processing status
 + */
 +static RadiusRxResult
 +accounting_receive(struct radius_msg *msg, struct radius_msg *req,
 +                 const u8 *shared_secret, size_t shared_secret_len,
 +                 void *data)
 +{
 +      if (radius_msg_get_hdr(msg)->code != RADIUS_CODE_ACCOUNTING_RESPONSE) {
 +              wpa_printf(MSG_INFO, "Unknown RADIUS message code");
 +              return RADIUS_RX_UNKNOWN;
 +      }
 +
 +      if (radius_msg_verify(msg, shared_secret, shared_secret_len, req, 0)) {
 +              wpa_printf(MSG_INFO, "Incoming RADIUS packet did not have correct Authenticator - dropped");
 +              return RADIUS_RX_INVALID_AUTHENTICATOR;
 +      }
 +
 +      return RADIUS_RX_PROCESSED;
 +}
 +
 +
 +static void accounting_report_state(struct hostapd_data *hapd, int on)
 +{
 +      struct radius_msg *msg;
 +
 +      if (!hapd->conf->radius->acct_server || hapd->radius == NULL)
 +              return;
 +
 +      /* Inform RADIUS server that accounting will start/stop so that the
 +       * server can close old accounting sessions. */
 +      msg = accounting_msg(hapd, NULL,
 +                           on ? RADIUS_ACCT_STATUS_TYPE_ACCOUNTING_ON :
 +                           RADIUS_ACCT_STATUS_TYPE_ACCOUNTING_OFF);
 +      if (!msg)
 +              return;
 +
 +      if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_TERMINATE_CAUSE,
 +                                     RADIUS_ACCT_TERMINATE_CAUSE_NAS_REBOOT))
 +      {
 +              wpa_printf(MSG_INFO, "Could not add Acct-Terminate-Cause");
 +              radius_msg_free(msg);
 +              return;
 +      }
 +
 +      if (radius_client_send(hapd->radius, msg, RADIUS_ACCT, NULL) < 0)
 +              radius_msg_free(msg);
 +}
 +
 +
 +/**
 + * accounting_init: Initialize accounting
 + * @hapd: hostapd BSS data
 + * Returns: 0 on success, -1 on failure
 + */
 +int accounting_init(struct hostapd_data *hapd)
 +{
 +      struct os_time now;
 +
-       hapd->acct_session_id_hi = now.sec;
++      /* Acct-Session-Id should be unique over reboots. Using a random number
++       * is preferred. If that is not available, take the current time. Mix
++       * in microseconds to make this more likely to be unique. */
 +      os_get_time(&now);
-  * accounting_deinit: Deinitilize accounting
++      if (os_get_random((u8 *) &hapd->acct_session_id_hi,
++                        sizeof(hapd->acct_session_id_hi)) < 0)
++              hapd->acct_session_id_hi = now.sec;
++      hapd->acct_session_id_hi ^= now.usec;
 +
 +      if (radius_client_register(hapd->radius, RADIUS_ACCT,
 +                                 accounting_receive, hapd))
 +              return -1;
 +
 +      accounting_report_state(hapd, 1);
 +
 +      return 0;
 +}
 +
 +
 +/**
++ * accounting_deinit: Deinitialize accounting
 + * @hapd: hostapd BSS data
 + */
 +void accounting_deinit(struct hostapd_data *hapd)
 +{
 +      accounting_report_state(hapd, 0);
 +}
index ae7f6c3092899b7ad5258e1f3ed9209b60396267,0000000000000000000000000000000000000000..03d797fe8836a5e63c62d78bb76f5b629f492426
mode 100644,000000..100644
--- /dev/null
@@@ -1,949 -1,0 +1,946 @@@
-       int *entry;
-       if (!iface->conf->chanlist)
 +/*
 + * 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)
 +{
-       for (entry = iface->conf->chanlist; *entry != -1; entry++) {
-               if (*entry == chan->chan)
-                       return 1;
-       }
-       return 0;
++      if (!iface->conf->acs_ch_list.num)
 +              return 1;
 +
++      return freq_range_list_includes(&iface->conf->acs_ch_list, chan->chan);
 +}
 +
 +
 +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;
 +
++              if (!is_in_chanlist(iface, chan))
++                      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;
 +}
index 76011dc07fd4b6199822dba2f9268165ceabcc2b,0000000000000000000000000000000000000000..9a96e50b7385b5dcf6163dedf374992e39fb4b3a
mode 100644,000000..100644
--- /dev/null
@@@ -1,946 -1,0 +1,985 @@@
-       os_free(conf->chanlist);
 +/*
 + * hostapd / Configuration helper functions
 + * 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/includes.h"
 +
 +#include "utils/common.h"
 +#include "crypto/sha1.h"
 +#include "radius/radius_client.h"
 +#include "common/ieee802_11_defs.h"
 +#include "common/eapol_common.h"
 +#include "eap_common/eap_wsc_common.h"
 +#include "eap_server/eap.h"
 +#include "wpa_auth.h"
 +#include "sta_info.h"
 +#include "ap_config.h"
 +
 +
 +static void hostapd_config_free_vlan(struct hostapd_bss_config *bss)
 +{
 +      struct hostapd_vlan *vlan, *prev;
 +
 +      vlan = bss->vlan;
 +      prev = NULL;
 +      while (vlan) {
 +              prev = vlan;
 +              vlan = vlan->next;
 +              os_free(prev);
 +      }
 +
 +      bss->vlan = NULL;
 +}
 +
 +
 +void hostapd_config_defaults_bss(struct hostapd_bss_config *bss)
 +{
 +      bss->logger_syslog_level = HOSTAPD_LEVEL_INFO;
 +      bss->logger_stdout_level = HOSTAPD_LEVEL_INFO;
 +      bss->logger_syslog = (unsigned int) -1;
 +      bss->logger_stdout = (unsigned int) -1;
 +
 +      bss->auth_algs = WPA_AUTH_ALG_OPEN | WPA_AUTH_ALG_SHARED;
 +
 +      bss->wep_rekeying_period = 300;
 +      /* use key0 in individual key and key1 in broadcast key */
 +      bss->broadcast_key_idx_min = 1;
 +      bss->broadcast_key_idx_max = 2;
 +      bss->eap_reauth_period = 3600;
 +
 +      bss->wpa_group_rekey = 600;
 +      bss->wpa_gmk_rekey = 86400;
 +      bss->wpa_key_mgmt = WPA_KEY_MGMT_PSK;
 +      bss->wpa_pairwise = WPA_CIPHER_TKIP;
 +      bss->wpa_group = WPA_CIPHER_TKIP;
 +      bss->rsn_pairwise = 0;
 +
 +      bss->max_num_sta = MAX_STA_COUNT;
 +
 +      bss->dtim_period = 2;
 +
 +      bss->radius_server_auth_port = 1812;
 +      bss->ap_max_inactivity = AP_MAX_INACTIVITY;
 +      bss->eapol_version = EAPOL_VERSION;
 +
 +      bss->max_listen_interval = 65535;
 +
 +      bss->pwd_group = 19; /* ECC: GF(p=256) */
 +
 +#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 */
 +      bss->eap_fast_prov = 3;
 +      bss->pac_key_lifetime = 7 * 24 * 60 * 60;
 +      bss->pac_key_refresh_time = 1 * 24 * 60 * 60;
 +#endif /* EAP_SERVER_FAST */
 +
 +      /* Set to -1 as defaults depends on HT in setup */
 +      bss->wmm_enabled = -1;
 +
 +#ifdef CONFIG_IEEE80211R
 +      bss->ft_over_ds = 1;
 +#endif /* CONFIG_IEEE80211R */
 +
 +      bss->radius_das_time_window = 300;
 +
 +      bss->sae_anti_clogging_threshold = 5;
 +}
 +
 +
 +struct hostapd_config * hostapd_config_defaults(void)
 +{
 +#define ecw2cw(ecw) ((1 << (ecw)) - 1)
 +
 +      struct hostapd_config *conf;
 +      struct hostapd_bss_config *bss;
 +      const int aCWmin = 4, aCWmax = 10;
 +      const struct hostapd_wmm_ac_params ac_bk =
 +              { aCWmin, aCWmax, 7, 0, 0 }; /* background traffic */
 +      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, 3008 / 32, 0 };
 +      const struct hostapd_wmm_ac_params ac_vo = /* voice traffic */
 +              { 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 =
 +              { 3, ecw2cw(aCWmin), 4 * (ecw2cw(aCWmin) + 1) - 1, 0};
 +      const struct hostapd_tx_queue_params txq_vi =
 +              { 1, (ecw2cw(aCWmin) + 1) / 2 - 1, ecw2cw(aCWmin), 30};
 +      const struct hostapd_tx_queue_params txq_vo =
 +              { 1, (ecw2cw(aCWmin) + 1) / 4 - 1,
 +                (ecw2cw(aCWmin) + 1) / 2 - 1, 15};
 +
 +#undef ecw2cw
 +
 +      conf = os_zalloc(sizeof(*conf));
 +      bss = os_zalloc(sizeof(*bss));
 +      if (conf == NULL || bss == NULL) {
 +              wpa_printf(MSG_ERROR, "Failed to allocate memory for "
 +                         "configuration data.");
 +              os_free(conf);
 +              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;
 +      }
 +
 +      hostapd_config_defaults_bss(bss);
 +
 +      conf->num_bss = 1;
 +
 +      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;
 +      conf->wmm_ac_params[2] = ac_vi;
 +      conf->wmm_ac_params[3] = ac_vo;
 +
 +      conf->tx_queue[0] = txq_vo;
 +      conf->tx_queue[1] = txq_vi;
 +      conf->tx_queue[2] = txq_be;
 +      conf->tx_queue[3] = txq_bk;
 +
 +      conf->ht_capab = HT_CAP_INFO_SMPS_DISABLED;
 +
 +      conf->ap_table_max_size = 255;
 +      conf->ap_table_expiration_time = 60;
++      conf->track_sta_max_age = 180;
 +
 +#ifdef CONFIG_TESTING_OPTIONS
 +      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 */
 +
++      conf->acs = 0;
++      conf->acs_ch_list.num = 0;
 +#ifdef CONFIG_ACS
 +      conf->acs_num_scans = 5;
 +#endif /* CONFIG_ACS */
 +
 +      return conf;
 +}
 +
 +
 +int hostapd_mac_comp(const void *a, const void *b)
 +{
 +      return os_memcmp(a, b, sizeof(macaddr));
 +}
 +
 +
 +int hostapd_mac_comp_empty(const void *a)
 +{
 +      macaddr empty = { 0 };
 +      return os_memcmp(a, empty, sizeof(macaddr));
 +}
 +
 +
 +static int hostapd_config_read_wpa_psk(const char *fname,
 +                                     struct hostapd_ssid *ssid)
 +{
 +      FILE *f;
 +      char buf[128], *pos;
 +      int line = 0, ret = 0, len, ok;
 +      u8 addr[ETH_ALEN];
 +      struct hostapd_wpa_psk *psk;
 +
 +      if (!fname)
 +              return 0;
 +
 +      f = fopen(fname, "r");
 +      if (!f) {
 +              wpa_printf(MSG_ERROR, "WPA PSK file '%s' not found.", fname);
 +              return -1;
 +      }
 +
 +      while (fgets(buf, sizeof(buf), f)) {
 +              line++;
 +
 +              if (buf[0] == '#')
 +                      continue;
 +              pos = buf;
 +              while (*pos != '\0') {
 +                      if (*pos == '\n') {
 +                              *pos = '\0';
 +                              break;
 +                      }
 +                      pos++;
 +              }
 +              if (buf[0] == '\0')
 +                      continue;
 +
 +              if (hwaddr_aton(buf, addr)) {
 +                      wpa_printf(MSG_ERROR, "Invalid MAC address '%s' on "
 +                                 "line %d in '%s'", buf, line, fname);
 +                      ret = -1;
 +                      break;
 +              }
 +
 +              psk = os_zalloc(sizeof(*psk));
 +              if (psk == NULL) {
 +                      wpa_printf(MSG_ERROR, "WPA PSK allocation failed");
 +                      ret = -1;
 +                      break;
 +              }
 +              if (is_zero_ether_addr(addr))
 +                      psk->group = 1;
 +              else
 +                      os_memcpy(psk->addr, addr, ETH_ALEN);
 +
 +              pos = buf + 17;
 +              if (*pos == '\0') {
 +                      wpa_printf(MSG_ERROR, "No PSK on line %d in '%s'",
 +                                 line, fname);
 +                      os_free(psk);
 +                      ret = -1;
 +                      break;
 +              }
 +              pos++;
 +
 +              ok = 0;
 +              len = os_strlen(pos);
 +              if (len == 64 && hexstr2bin(pos, psk->psk, PMK_LEN) == 0)
 +                      ok = 1;
 +              else if (len >= 8 && len < 64) {
 +                      pbkdf2_sha1(pos, ssid->ssid, ssid->ssid_len,
 +                                  4096, psk->psk, PMK_LEN);
 +                      ok = 1;
 +              }
 +              if (!ok) {
 +                      wpa_printf(MSG_ERROR, "Invalid PSK '%s' on line %d in "
 +                                 "'%s'", pos, line, fname);
 +                      os_free(psk);
 +                      ret = -1;
 +                      break;
 +              }
 +
 +              psk->next = ssid->wpa_psk;
 +              ssid->wpa_psk = psk;
 +      }
 +
 +      fclose(f);
 +
 +      return ret;
 +}
 +
 +
 +static int hostapd_derive_psk(struct hostapd_ssid *ssid)
 +{
 +      ssid->wpa_psk = os_zalloc(sizeof(struct hostapd_wpa_psk));
 +      if (ssid->wpa_psk == NULL) {
 +              wpa_printf(MSG_ERROR, "Unable to alloc space for PSK");
 +              return -1;
 +      }
 +      wpa_hexdump_ascii(MSG_DEBUG, "SSID",
 +                        (u8 *) ssid->ssid, ssid->ssid_len);
 +      wpa_hexdump_ascii_key(MSG_DEBUG, "PSK (ASCII passphrase)",
 +                            (u8 *) ssid->wpa_passphrase,
 +                            os_strlen(ssid->wpa_passphrase));
 +      pbkdf2_sha1(ssid->wpa_passphrase,
 +                  ssid->ssid, ssid->ssid_len,
 +                  4096, ssid->wpa_psk->psk, PMK_LEN);
 +      wpa_hexdump_key(MSG_DEBUG, "PSK (from passphrase)",
 +                      ssid->wpa_psk->psk, PMK_LEN);
 +      return 0;
 +}
 +
 +
 +int hostapd_setup_wpa_psk(struct hostapd_bss_config *conf)
 +{
 +      struct hostapd_ssid *ssid = &conf->ssid;
 +
 +      if (ssid->wpa_passphrase != NULL) {
 +              if (ssid->wpa_psk != NULL) {
 +                      wpa_printf(MSG_DEBUG, "Using pre-configured WPA PSK "
 +                                 "instead of passphrase");
 +              } else {
 +                      wpa_printf(MSG_DEBUG, "Deriving WPA PSK based on "
 +                                 "passphrase");
 +                      if (hostapd_derive_psk(ssid) < 0)
 +                              return -1;
 +              }
 +              ssid->wpa_psk->group = 1;
 +      }
 +
 +      if (ssid->wpa_psk_file) {
 +              if (hostapd_config_read_wpa_psk(ssid->wpa_psk_file,
 +                                              &conf->ssid))
 +                      return -1;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +static void hostapd_config_free_radius(struct hostapd_radius_server *servers,
 +                                     int num_servers)
 +{
 +      int i;
 +
 +      for (i = 0; i < num_servers; i++) {
 +              os_free(servers[i].shared_secret);
 +      }
 +      os_free(servers);
 +}
 +
 +
 +struct hostapd_radius_attr *
 +hostapd_config_get_radius_attr(struct hostapd_radius_attr *attr, u8 type)
 +{
 +      for (; attr; attr = attr->next) {
 +              if (attr->type == type)
 +                      return attr;
 +      }
 +      return NULL;
 +}
 +
 +
 +static void hostapd_config_free_radius_attr(struct hostapd_radius_attr *attr)
 +{
 +      struct hostapd_radius_attr *prev;
 +
 +      while (attr) {
 +              prev = attr;
 +              attr = attr->next;
 +              wpabuf_free(prev->val);
 +              os_free(prev);
 +      }
 +}
 +
 +
 +void hostapd_config_free_eap_user(struct hostapd_eap_user *user)
 +{
 +      hostapd_config_free_radius_attr(user->accept_attr);
 +      os_free(user->identity);
 +      bin_clear_free(user->password, user->password_len);
 +      os_free(user);
 +}
 +
 +
 +static void hostapd_config_free_wep(struct hostapd_wep_keys *keys)
 +{
 +      int i;
 +      for (i = 0; i < NUM_WEP_KEYS; i++) {
 +              bin_clear_free(keys->key[i], keys->len[i]);
 +              keys->key[i] = NULL;
 +      }
 +}
 +
 +
 +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_eap_user *user, *prev_user;
 +
 +      if (conf == NULL)
 +              return;
 +
 +      hostapd_config_clear_wpa_psk(&conf->ssid.wpa_psk);
 +
 +      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
 +      os_free(conf->ssid.vlan_tagged_interface);
 +#endif /* CONFIG_FULL_DYNAMIC_VLAN */
 +
 +      user = conf->eap_user;
 +      while (user) {
 +              prev_user = user;
 +              user = user->next;
 +              hostapd_config_free_eap_user(prev_user);
 +      }
 +      os_free(conf->eap_user_sqlite);
 +
 +      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);
 +      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);
 +      os_free(conf->ctrl_interface);
 +      os_free(conf->ca_cert);
 +      os_free(conf->server_cert);
 +      os_free(conf->private_key);
 +      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->radius);
 +      os_free(conf->radius_das_shared_secret);
 +      hostapd_config_free_vlan(conf);
 +      os_free(conf->time_zone);
 +
 +#ifdef CONFIG_IEEE80211R
 +      {
 +              struct ft_remote_r0kh *r0kh, *r0kh_prev;
 +              struct ft_remote_r1kh *r1kh, *r1kh_prev;
 +
 +              r0kh = conf->r0kh_list;
 +              conf->r0kh_list = NULL;
 +              while (r0kh) {
 +                      r0kh_prev = r0kh;
 +                      r0kh = r0kh->next;
 +                      os_free(r0kh_prev);
 +              }
 +
 +              r1kh = conf->r1kh_list;
 +              conf->r1kh_list = NULL;
 +              while (r1kh) {
 +                      r1kh_prev = r1kh;
 +                      r1kh = r1kh->next;
 +                      os_free(r1kh_prev);
 +              }
 +      }
 +#endif /* CONFIG_IEEE80211R */
 +
 +#ifdef CONFIG_WPS
 +      os_free(conf->wps_pin_requests);
 +      os_free(conf->device_name);
 +      os_free(conf->manufacturer);
 +      os_free(conf->model_name);
 +      os_free(conf->model_number);
 +      os_free(conf->serial_number);
 +      os_free(conf->config_methods);
 +      os_free(conf->ap_pin);
 +      os_free(conf->extra_cred);
 +      os_free(conf->ap_settings);
 +      os_free(conf->upnp_iface);
 +      os_free(conf->friendly_name);
 +      os_free(conf->manufacturer_url);
 +      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);
 +#endif /* CONFIG_WPS */
 +
 +      os_free(conf->roaming_consortium);
 +      os_free(conf->venue_name);
 +      os_free(conf->nai_realm_data);
 +      os_free(conf->network_auth_type);
 +      os_free(conf->anqp_3gpp_cell_net);
 +      os_free(conf->domain_name);
 +
 +#ifdef CONFIG_RADIUS_TEST
 +      os_free(conf->dump_msk_file);
 +#endif /* CONFIG_RADIUS_TEST */
 +
 +#ifdef CONFIG_HS20
 +      os_free(conf->hs20_oper_friendly_name);
 +      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);
 +
++#ifdef CONFIG_TESTING_OPTIONS
++      wpabuf_free(conf->own_ie_override);
++#endif /* CONFIG_TESTING_OPTIONS */
++
++      os_free(conf->no_probe_resp_if_seen_on);
++      os_free(conf->no_auth_if_seen_on);
++
 +      os_free(conf);
 +}
 +
 +
 +/**
 + * hostapd_config_free - Free hostapd configuration
 + * @conf: Configuration data from hostapd_config_read().
 + */
 +void hostapd_config_free(struct hostapd_config *conf)
 +{
 +      size_t i;
 +
 +      if (conf == NULL)
 +              return;
 +
 +      for (i = 0; i < conf->num_bss; i++)
 +              hostapd_config_free_bss(conf->bss[i]);
 +      os_free(conf->bss);
 +      os_free(conf->supported_rates);
 +      os_free(conf->basic_rates);
-            !(bss->rsn_pairwise & WPA_CIPHER_CCMP))) {
++      os_free(conf->acs_ch_list.range);
 +      os_free(conf->driver_params);
 +#ifdef CONFIG_ACS
 +      os_free(conf->acs_chan_bias);
 +#endif /* CONFIG_ACS */
 +
 +      os_free(conf);
 +}
 +
 +
 +/**
 + * hostapd_maclist_found - Find a MAC address from a list
 + * @list: MAC address list
 + * @num_entries: Number of addresses in the list
 + * @addr: Address to search for
 + * @vlan_id: Buffer for returning VLAN ID or %NULL if not needed
 + * Returns: 1 if address is in the list or 0 if not.
 + *
 + * Perform a binary search for given MAC address from a pre-sorted list.
 + */
 +int hostapd_maclist_found(struct mac_acl_entry *list, int num_entries,
 +                        const u8 *addr, int *vlan_id)
 +{
 +      int start, end, middle, res;
 +
 +      start = 0;
 +      end = num_entries - 1;
 +
 +      while (start <= end) {
 +              middle = (start + end) / 2;
 +              res = os_memcmp(list[middle].addr, addr, ETH_ALEN);
 +              if (res == 0) {
 +                      if (vlan_id)
 +                              *vlan_id = list[middle].vlan_id;
 +                      return 1;
 +              }
 +              if (res < 0)
 +                      start = middle + 1;
 +              else
 +                      end = middle - 1;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +int hostapd_rate_found(int *list, int rate)
 +{
 +      int i;
 +
 +      if (list == NULL)
 +              return 0;
 +
 +      for (i = 0; list[i] >= 0; i++)
 +              if (list[i] == rate)
 +                      return 1;
 +
 +      return 0;
 +}
 +
 +
 +int hostapd_vlan_id_valid(struct hostapd_vlan *vlan, int vlan_id)
 +{
 +      struct hostapd_vlan *v = vlan;
 +      while (v) {
 +              if (v->vlan_id == vlan_id || v->vlan_id == VLAN_ID_WILDCARD)
 +                      return 1;
 +              v = v->next;
 +      }
 +      return 0;
 +}
 +
 +
 +const char * hostapd_get_vlan_id_ifname(struct hostapd_vlan *vlan, int vlan_id)
 +{
 +      struct hostapd_vlan *v = vlan;
 +      while (v) {
 +              if (v->vlan_id == vlan_id)
 +                      return v->ifname;
 +              v = v->next;
 +      }
 +      return NULL;
 +}
 +
 +
 +const u8 * hostapd_get_psk(const struct hostapd_bss_config *conf,
 +                         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 ||
 +                   (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)
 +                      next_ok = 1;
 +      }
 +
 +      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) ||
-                          "WPA2/CCMP forced WPS to be disabled");
++           !(bss->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP)))) {
 +              wpa_printf(MSG_INFO, "WPS: WPA/TKIP configuration without "
-               bss->wpa_group = WPA_CIPHER_NONE;
-               bss->wpa_pairwise = WPA_CIPHER_NONE;
-               bss->rsn_pairwise = WPA_CIPHER_NONE;
-               if (full_config)
++                         "WPA2/CCMP/GCMP 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;
 +}
 +
 +
++static int hostapd_config_check_cw(struct hostapd_config *conf, int queue)
++{
++      int tx_cwmin = conf->tx_queue[queue].cwmin;
++      int tx_cwmax = conf->tx_queue[queue].cwmax;
++      int ac_cwmin = conf->wmm_ac_params[queue].cwmin;
++      int ac_cwmax = conf->wmm_ac_params[queue].cwmax;
++
++      if (tx_cwmin > tx_cwmax) {
++              wpa_printf(MSG_ERROR,
++                         "Invalid TX queue cwMin/cwMax values. cwMin(%d) greater than cwMax(%d)",
++                         tx_cwmin, tx_cwmax);
++              return -1;
++      }
++      if (ac_cwmin > ac_cwmax) {
++              wpa_printf(MSG_ERROR,
++                         "Invalid WMM AC cwMin/cwMax values. cwMin(%d) greater than cwMax(%d)",
++                         ac_cwmin, ac_cwmax);
++              return -1;
++      }
++      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 < NUM_TX_QUEUES; i++) {
++              if (hostapd_config_check_cw(conf, i))
++                      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;
++              if (full_config) {
++                      bss->wpa_group = WPA_CIPHER_NONE;
++                      bss->wpa_pairwise = WPA_CIPHER_NONE;
++                      bss->rsn_pairwise = WPA_CIPHER_NONE;
 +                      bss->wpa_key_mgmt = WPA_KEY_MGMT_NONE;
++              }
 +      }
 +}
index 961d2dd389f8720960eb55aafb938311e5dd454a,0000000000000000000000000000000000000000..de470a969b500f3bcc02dc914281214e3afbe844
mode 100644,000000..100644
--- /dev/null
@@@ -1,679 -1,0 +1,692 @@@
-  * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi>
 +/*
 + * hostapd / Configuration definitions and helpers functions
-       u8 *ies;
-       int ie_len;
++ * 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.
 + */
 +
 +#ifndef HOSTAPD_CONFIG_H
 +#define HOSTAPD_CONFIG_H
 +
 +#include "common/defs.h"
 +#include "ip_addr.h"
 +#include "common/wpa_common.h"
++#include "common/ieee802_11_defs.h"
 +#include "common/ieee802_11_common.h"
 +#include "wps/wps.h"
++#include "fst/fst.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;
- #define HOSTAPD_MAX_SSID_LEN 32
++      u8 *rsn_ie;
++      int rsn_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
 +
 +typedef u8 macaddr[ETH_ALEN];
 +
 +struct mac_acl_entry {
 +      macaddr addr;
 +      int vlan_id;
 +};
 +
 +struct hostapd_radius_servers;
 +struct ft_remote_r0kh;
 +struct ft_remote_r1kh;
 +
-       u8 ssid[HOSTAPD_MAX_SSID_LEN];
 +#define NUM_WEP_KEYS 4
 +struct hostapd_wep_keys {
 +      u8 idx;
 +      u8 *key[NUM_WEP_KEYS];
 +      size_t len[NUM_WEP_KEYS];
 +      int keys_set;
 +      size_t default_len; /* key length used for dynamic key generation */
 +};
 +
 +typedef enum hostap_security_policy {
 +      SECURITY_PLAINTEXT = 0,
 +      SECURITY_STATIC_WEP = 1,
 +      SECURITY_IEEE_802_1X = 2,
 +      SECURITY_WPA_PSK = 3,
 +      SECURITY_WPA = 4,
 +      SECURITY_OSEN = 5
 +} secpolicy;
 +
 +struct hostapd_ssid {
- #define DVLAN_CLEAN_BR        0x1
- #define DVLAN_CLEAN_VLAN      0x2
- #define DVLAN_CLEAN_VLAN_PORT 0x4
++      u8 ssid[SSID_MAX_LEN];
 +      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;
 +
 +      struct hostapd_wpa_psk *wpa_psk;
 +      char *wpa_passphrase;
 +      char *wpa_psk_file;
 +
 +      struct hostapd_wep_keys wep;
 +
 +#define DYNAMIC_VLAN_DISABLED 0
 +#define DYNAMIC_VLAN_OPTIONAL 1
 +#define DYNAMIC_VLAN_REQUIRED 2
 +      int dynamic_vlan;
 +#define DYNAMIC_VLAN_NAMING_WITHOUT_DEVICE 0
 +#define DYNAMIC_VLAN_NAMING_WITH_DEVICE 1
 +#define DYNAMIC_VLAN_NAMING_END 2
 +      int vlan_naming;
 +#ifdef CONFIG_FULL_DYNAMIC_VLAN
 +      char *vlan_tagged_interface;
 +#endif /* CONFIG_FULL_DYNAMIC_VLAN */
 +};
 +
 +
 +#define VLAN_ID_WILDCARD -1
 +
 +struct hostapd_vlan {
 +      struct hostapd_vlan *next;
 +      int vlan_id; /* VLAN ID or -1 (VLAN_ID_WILDCARD) for wildcard entry */
 +      char ifname[IFNAMSIZ + 1];
++      int configured;
 +      int dynamic_vlan;
 +#ifdef CONFIG_FULL_DYNAMIC_VLAN
 +
-       u8 osu_ssid[HOSTAPD_MAX_SSID_LEN];
 +#define DVLAN_CLEAN_WLAN_PORT 0x8
 +      int clean;
 +#endif /* CONFIG_FULL_DYNAMIC_VLAN */
 +};
 +
 +#define PMK_LEN 32
 +struct hostapd_sta_wpa_psk_short {
 +      struct hostapd_sta_wpa_psk_short *next;
 +      u8 psk[PMK_LEN];
 +};
 +
 +struct hostapd_wpa_psk {
 +      struct hostapd_wpa_psk *next;
 +      int group;
 +      u8 psk[PMK_LEN];
 +      u8 addr[ETH_ALEN];
 +      u8 p2p_dev_addr[ETH_ALEN];
 +};
 +
 +struct hostapd_eap_user {
 +      struct hostapd_eap_user *next;
 +      u8 *identity;
 +      size_t identity_len;
 +      struct {
 +              int vendor;
 +              u32 method;
 +      } methods[EAP_MAX_METHODS];
 +      u8 *password;
 +      size_t password_len;
 +      int phase2;
 +      int force_version;
 +      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 {
 +      u8 type;
 +      struct wpabuf *val;
 +      struct hostapd_radius_attr *next;
 +};
 +
 +
 +#define NUM_TX_QUEUES 4
 +
 +struct hostapd_tx_queue_params {
 +      int aifs;
 +      int cwmin;
 +      int cwmax;
 +      int burst; /* maximum burst time in 0.1 ms, i.e., 10 = 1 ms */
 +};
 +
 +
 +#define MAX_ROAMING_CONSORTIUM_LEN 15
 +
 +struct hostapd_roaming_consortium {
 +      u8 len;
 +      u8 oi[MAX_ROAMING_CONSORTIUM_LEN];
 +};
 +
 +struct hostapd_lang_string {
 +      u8 lang[3];
 +      u8 name_len;
 +      u8 name[252];
 +};
 +
 +#define MAX_NAI_REALMS 10
 +#define MAX_NAI_REALMLEN 255
 +#define MAX_NAI_EAP_METHODS 5
 +#define MAX_NAI_AUTH_TYPES 4
 +struct hostapd_nai_realm_data {
 +      u8 encoding;
 +      char realm_buf[MAX_NAI_REALMLEN + 1];
 +      char *realm[MAX_NAI_REALMS];
 +      u8 eap_method_count;
 +      struct hostapd_nai_realm_eap {
 +              u8 eap_method;
 +              u8 num_auths;
 +              u8 auth_id[MAX_NAI_AUTH_TYPES];
 +              u8 auth_val[MAX_NAI_AUTH_TYPES];
 +      } eap_method[MAX_NAI_EAP_METHODS];
 +};
 +
 +/**
 + * struct hostapd_bss_config - Per-BSS configuration
 + */
 +struct hostapd_bss_config {
 +      char iface[IFNAMSIZ + 1];
 +      char bridge[IFNAMSIZ + 1];
 +      char vlan_bridge[IFNAMSIZ + 1];
 +      char wds_bridge[IFNAMSIZ + 1];
 +
 +      enum hostapd_logger_level logger_syslog_level, logger_stdout_level;
 +
 +      unsigned int logger_syslog; /* module bitfield */
 +      unsigned int logger_stdout; /* module bitfield */
 +
 +      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;
 +      int eap_server; /* Use internal EAP server instead of external
 +                       * RADIUS server */
 +      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;
 +      int acct_interim_interval;
 +      int radius_request_cui;
 +      struct hostapd_radius_attr *radius_auth_req_attr;
 +      struct hostapd_radius_attr *radius_acct_req_attr;
 +      int radius_das_port;
 +      unsigned int radius_das_time_window;
 +      int radius_das_require_event_timestamp;
 +      struct hostapd_ip_addr radius_das_client_addr;
 +      u8 *radius_das_shared_secret;
 +      size_t radius_das_shared_secret_len;
 +
 +      struct hostapd_ssid ssid;
 +
 +      char *eap_req_id_text; /* optional displayable message sent with
 +                              * EAP Request-Identity */
 +      size_t eap_req_id_text_len;
 +      int eapol_key_index_workaround;
 +
 +      size_t default_wep_key_len;
 +      int individual_wep_key_len;
 +      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
 +                                      * frames */
 +
 +      enum {
 +              ACCEPT_UNLESS_DENIED = 0,
 +              DENY_UNLESS_ACCEPTED = 1,
 +              USE_EXTERNAL_RADIUS_AUTH = 2
 +      } macaddr_acl;
 +      struct mac_acl_entry *accept_mac;
 +      int num_accept_mac;
 +      struct mac_acl_entry *deny_mac;
 +      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} */
 +
 +      int wpa; /* bitfield of WPA_PROTO_WPA, WPA_PROTO_RSN */
 +      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) */
 +      int assoc_sa_query_retry_timeout;
 +#endif /* CONFIG_IEEE80211W */
 +      enum {
 +              PSK_RADIUS_IGNORED = 0,
 +              PSK_RADIUS_ACCEPTED = 1,
 +              PSK_RADIUS_REQUIRED = 2
 +      } wpa_psk_radius;
 +      int wpa_pairwise;
 +      int wpa_group;
 +      int wpa_group_rekey;
 +      int wpa_strict_rekey;
 +      int wpa_gmk_rekey;
 +      int wpa_ptk_rekey;
 +      int rsn_pairwise;
 +      int rsn_preauth;
 +      char *rsn_preauth_interfaces;
 +      int peerkey;
 +
 +#ifdef CONFIG_IEEE80211R
 +      /* IEEE 802.11r - Fast BSS Transition */
 +      u8 mobility_domain[MOBILITY_DOMAIN_ID_LEN];
 +      u8 r1_key_holder[FT_R1KH_ID_LEN];
 +      u32 r0_key_lifetime;
 +      u32 reassociation_deadline;
 +      struct ft_remote_r0kh *r0kh_list;
 +      struct ft_remote_r1kh *r1kh_list;
 +      int pmk_r1_push;
 +      int ft_over_ds;
 +#endif /* CONFIG_IEEE80211R */
 +
 +      char *ctrl_interface; /* directory for UNIX domain sockets */
 +#ifndef CONFIG_NATIVE_WINDOWS
 +      gid_t ctrl_interface_gid;
 +#endif /* CONFIG_NATIVE_WINDOWS */
 +      int ctrl_interface_gid_set;
 +
 +      char *ca_cert;
 +      char *server_cert;
 +      char *private_key;
 +      char *private_key_passwd;
 +      int check_crl;
++      unsigned int tls_session_lifetime;
 +      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;
 +      char *eap_fast_a_id_info;
 +      int eap_fast_prov;
 +      int pac_key_lifetime;
 +      int pac_key_refresh_time;
 +      int eap_sim_aka_result_ind;
 +      int tnc;
 +      int fragment_size;
 +      u16 pwd_group;
 +
 +      char *radius_server_clients;
 +      int radius_server_auth_port;
 +      int radius_server_acct_port;
 +      int radius_server_ipv6;
 +
 +      int use_pae_group_addr; /* Whether to send EAPOL frames to PAE group
 +                               * address instead of individual address
 +                               * (for driver_wired.c).
 +                               */
 +
 +      int ap_max_inactivity;
 +      int ignore_broadcast_ssid;
 +
 +      int wmm_enabled;
 +      int wmm_uapsd;
 +
 +      struct hostapd_vlan *vlan;
 +
 +      macaddr bssid;
 +
 +      /*
 +       * Maximum listen interval that STAs can use when associating with this
 +       * BSS. If a STA tries to use larger value, the association will be
 +       * denied with status code 51.
 +       */
 +      u16 max_listen_interval;
 +
 +      int disable_pmksa_caching;
 +      int okc; /* Opportunistic Key Caching */
 +
 +      int wps_state;
 +#ifdef CONFIG_WPS
 +      int wps_independent;
 +      int ap_setup_locked;
 +      u8 uuid[16];
 +      char *wps_pin_requests;
 +      char *device_name;
 +      char *manufacturer;
 +      char *model_name;
 +      char *model_number;
 +      char *serial_number;
 +      u8 device_type[WPS_DEV_TYPE_LEN];
 +      char *config_methods;
 +      u8 os_version[4];
 +      char *ap_pin;
 +      int skip_cred_build;
 +      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;
 +      char *friendly_name;
 +      char *manufacturer_url;
 +      char *model_description;
 +      char *model_url;
 +      char *upc;
 +      struct wpabuf *wps_vendor_ext[MAX_WPS_VENDOR_EXTENSIONS];
 +      int wps_nfc_pw_from_config;
 +      int wps_nfc_dev_pw_id;
 +      struct wpabuf *wps_nfc_dh_pubkey;
 +      struct wpabuf *wps_nfc_dh_privkey;
 +      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)
 +#define P2P_GROUP_FORMATION BIT(2)
 +#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;
 +
 +#define TDLS_PROHIBIT BIT(0)
 +#define TDLS_PROHIBIT_CHAN_SWITCH BIT(1)
 +      int tdls;
 +      int disable_11n;
 +      int disable_11ac;
 +
 +      /* IEEE 802.11v */
 +      int time_advertisement;
 +      char *time_zone;
 +      int wnm_sleep_mode;
 +      int bss_transition;
 +
 +      /* IEEE 802.11u - Interworking */
 +      int interworking;
 +      int access_network_type;
 +      int internet;
 +      int asra;
 +      int esr;
 +      int uesa;
 +      int venue_info_set;
 +      u8 venue_group;
 +      u8 venue_type;
 +      u8 hessid[ETH_ALEN];
 +
 +      /* IEEE 802.11u - Roaming Consortium list */
 +      unsigned int roaming_consortium_count;
 +      struct hostapd_roaming_consortium *roaming_consortium;
 +
 +      /* IEEE 802.11u - Venue Name duples */
 +      unsigned int venue_name_count;
 +      struct hostapd_lang_string *venue_name;
 +
 +      /* IEEE 802.11u - Network Authentication Type */
 +      u8 *network_auth_type;
 +      size_t network_auth_type_len;
 +
 +      /* IEEE 802.11u - IP Address Type Availability */
 +      u8 ipaddr_type_availability;
 +      u8 ipaddr_type_configured;
 +
 +      /* IEEE 802.11u - 3GPP Cellular Network */
 +      u8 *anqp_3gpp_cell_net;
 +      size_t anqp_3gpp_cell_net_len;
 +
 +      /* IEEE 802.11u - Domain Name */
 +      u8 *domain_name;
 +      size_t domain_name_len;
 +
 +      unsigned int nai_realm_count;
 +      struct hostapd_nai_realm_data *nai_realm_data;
 +
 +      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;
++      int na_mcast_to_ucast;
 +#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;
 +      u8 *hs20_connection_capability;
 +      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;
-       int *chanlist;
++      u8 osu_ssid[SSID_MAX_LEN];
 +      size_t osu_ssid_len;
 +      struct hs20_osu_provider {
 +              unsigned int friendly_name_count;
 +              struct hostapd_lang_string *friendly_name;
 +              char *server_uri;
 +              int *method_list;
 +              char **icons;
 +              size_t icons_count;
 +              char *osu_nai;
 +              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_*) */
 +
 +#ifdef CONFIG_RADIUS_TEST
 +      char *dump_msk_file;
 +#endif /* CONFIG_RADIUS_TEST */
 +
 +      struct wpabuf *vendor_elements;
 +
 +      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;
++      struct wpabuf *own_ie_override;
 +#endif /* CONFIG_TESTING_OPTIONS */
 +
 +#define MESH_ENABLED BIT(0)
 +      int mesh;
 +
 +      int radio_measurements;
 +
 +      int vendor_vht;
++
++      char *no_probe_resp_if_seen_on;
++      char *no_auth_if_seen_on;
 +};
 +
 +
 +/**
 + * struct hostapd_config - Per-radio interface configuration
 + */
 +struct hostapd_config {
 +      struct hostapd_bss_config **bss, *last_bss;
 +      size_t num_bss;
 +
 +      u16 beacon_int;
 +      int rts_threshold;
 +      int fragm_threshold;
 +      u8 send_probe_response;
 +      u8 channel;
++      u8 acs;
++      struct wpa_freq_range_list acs_ch_list;
 +      enum hostapd_hw_mode hw_mode; /* HOSTAPD_MODE_IEEE80211A, .. */
 +      enum {
 +              LONG_PREAMBLE = 0,
 +              SHORT_PREAMBLE = 1
 +      } preamble;
 +
 +      int *supported_rates;
 +      int *basic_rates;
 +
 +      const struct wpa_driver_ops *driver;
 +      char *driver_params;
 +
 +      int ap_table_max_size;
 +      int ap_table_expiration_time;
 +
++      unsigned int track_sta_max_num;
++      unsigned int track_sta_max_age;
++
 +      char country[3]; /* first two octets: country code as described in
 +                        * ISO/IEC 3166-1. Third octet:
 +                        * ' ' (ascii 32): all environments
 +                        * 'O': Outdoor environemnt only
 +                        * 'I': Indoor environment only
 +                        */
 +
 +      int ieee80211d;
 +
 +      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];
 +
 +      /*
 +       * WMM AC parameters, in same order as 802.1D, i.e.
 +       * 0 = BE (best effort)
 +       * 1 = BK (background)
 +       * 2 = VI (video)
 +       * 3 = VO (voice)
 +       */
 +      struct hostapd_wmm_ac_params wmm_ac_params[4];
 +
 +      int ht_op_mode_fixed;
 +      u16 ht_capab;
 +      int ieee80211n;
 +      int secondary_channel;
++      int no_pri_sec_switch;
 +      int require_ht;
 +      int obss_interval;
 +      u32 vht_capab;
 +      int ieee80211ac;
 +      int require_vht;
 +      u8 vht_oper_chwidth;
 +      u8 vht_oper_centr_freq_seg0_idx;
 +      u8 vht_oper_centr_freq_seg1_idx;
 +
++#ifdef CONFIG_FST
++      struct fst_iface_cfg fst_cfg;
++#endif /* CONFIG_FST */
++
 +#ifdef CONFIG_P2P
 +      u8 p2p_go_ctwindow;
 +#endif /* CONFIG_P2P */
 +
 +#ifdef CONFIG_TESTING_OPTIONS
 +      double ignore_probe_probability;
 +      double ignore_auth_probability;
 +      double ignore_assoc_probability;
 +      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 */
 +};
 +
 +
 +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);
 +const u8 * hostapd_get_psk(const struct hostapd_bss_config *conf,
 +                         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 e16306c4e10656219e35033ed5396508e980e340,0000000000000000000000000000000000000000..6cafcb7493511e0613c31f3eb6cff28818e09fec
mode 100644,000000..100644
--- /dev/null
@@@ -1,727 -1,0 +1,842 @@@
-               params.wpa_pairwise = hapd->conf->wpa_pairwise |
-                       hapd->conf->rsn_pairwise;
 +/*
 + * hostapd - Driver operations
 + * Copyright (c) 2009-2010, Jouni Malinen <j@w1.fi>
 + *
 + * This software may be distributed under the terms of the BSD license.
 + * See README for more details.
 + */
 +
 +#include "utils/includes.h"
 +
 +#include "utils/common.h"
 +#include "common/ieee802_11_defs.h"
 +#include "common/hw_features_common.h"
 +#include "wps/wps.h"
 +#include "p2p/p2p.h"
 +#include "hostapd.h"
 +#include "ieee802_11.h"
 +#include "sta_info.h"
 +#include "ap_config.h"
 +#include "p2p_hostapd.h"
 +#include "hs20.h"
 +#include "ap_drv_ops.h"
 +
 +
 +u32 hostapd_sta_flags_to_drv(u32 flags)
 +{
 +      int res = 0;
 +      if (flags & WLAN_STA_AUTHORIZED)
 +              res |= WPA_STA_AUTHORIZED;
 +      if (flags & WLAN_STA_WMM)
 +              res |= WPA_STA_WMM;
 +      if (flags & WLAN_STA_SHORT_PREAMBLE)
 +              res |= WPA_STA_SHORT_PREAMBLE;
 +      if (flags & WLAN_STA_MFP)
 +              res |= WPA_STA_MFP;
 +      return res;
 +}
 +
 +
 +int hostapd_build_ap_extra_ies(struct hostapd_data *hapd,
 +                             struct wpabuf **beacon_ret,
 +                             struct wpabuf **proberesp_ret,
 +                             struct wpabuf **assocresp_ret)
 +{
 +      struct wpabuf *beacon = NULL, *proberesp = NULL, *assocresp = NULL;
 +      u8 buf[200], *pos;
 +
 +      *beacon_ret = *proberesp_ret = *assocresp_ret = NULL;
 +
 +      pos = buf;
 +      pos = hostapd_eid_time_adv(hapd, pos);
 +      if (pos != buf) {
 +              if (wpabuf_resize(&beacon, pos - buf) != 0)
 +                      goto fail;
 +              wpabuf_put_data(beacon, buf, pos - buf);
 +      }
 +      pos = hostapd_eid_time_zone(hapd, pos);
 +      if (pos != buf) {
 +              if (wpabuf_resize(&proberesp, pos - buf) != 0)
 +                      goto fail;
 +              wpabuf_put_data(proberesp, buf, pos - buf);
 +      }
 +
 +      pos = buf;
 +      pos = hostapd_eid_ext_capab(hapd, pos);
 +      if (pos != buf) {
 +              if (wpabuf_resize(&assocresp, pos - buf) != 0)
 +                      goto fail;
 +              wpabuf_put_data(assocresp, buf, pos - buf);
 +      }
 +      pos = hostapd_eid_interworking(hapd, pos);
 +      pos = hostapd_eid_adv_proto(hapd, pos);
 +      pos = hostapd_eid_roaming_consortium(hapd, pos);
 +      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);
 +      }
 +
++#ifdef CONFIG_FST
++      if (hapd->iface->fst_ies) {
++              size_t add = wpabuf_len(hapd->iface->fst_ies);
++
++              if (wpabuf_resize(&beacon, add) < 0)
++                      goto fail;
++              wpabuf_put_buf(beacon, hapd->iface->fst_ies);
++              if (wpabuf_resize(&proberesp, add) < 0)
++                      goto fail;
++              wpabuf_put_buf(proberesp, hapd->iface->fst_ies);
++              if (wpabuf_resize(&assocresp, add) < 0)
++                      goto fail;
++              wpabuf_put_buf(assocresp, hapd->iface->fst_ies);
++      }
++#endif /* CONFIG_FST */
++
 +      if (hapd->wps_beacon_ie) {
 +              if (wpabuf_resize(&beacon, wpabuf_len(hapd->wps_beacon_ie)) <
 +                  0)
 +                      goto fail;
 +              wpabuf_put_buf(beacon, hapd->wps_beacon_ie);
 +      }
 +
 +      if (hapd->wps_probe_resp_ie) {
 +              if (wpabuf_resize(&proberesp,
 +                                wpabuf_len(hapd->wps_probe_resp_ie)) < 0)
 +                      goto fail;
 +              wpabuf_put_buf(proberesp, hapd->wps_probe_resp_ie);
 +      }
 +
 +#ifdef CONFIG_P2P
 +      if (hapd->p2p_beacon_ie) {
 +              if (wpabuf_resize(&beacon, wpabuf_len(hapd->p2p_beacon_ie)) <
 +                  0)
 +                      goto fail;
 +              wpabuf_put_buf(beacon, hapd->p2p_beacon_ie);
 +      }
 +
 +      if (hapd->p2p_probe_resp_ie) {
 +              if (wpabuf_resize(&proberesp,
 +                                wpabuf_len(hapd->p2p_probe_resp_ie)) < 0)
 +                      goto fail;
 +              wpabuf_put_buf(proberesp, hapd->p2p_probe_resp_ie);
 +      }
 +#endif /* CONFIG_P2P */
 +
 +#ifdef CONFIG_P2P_MANAGER
 +      if (hapd->conf->p2p & P2P_MANAGE) {
 +              if (wpabuf_resize(&beacon, 100) == 0) {
 +                      u8 *start, *p;
 +                      start = wpabuf_put(beacon, 0);
 +                      p = hostapd_eid_p2p_manage(hapd, start);
 +                      wpabuf_put(beacon, p - start);
 +              }
 +
 +              if (wpabuf_resize(&proberesp, 100) == 0) {
 +                      u8 *start, *p;
 +                      start = wpabuf_put(proberesp, 0);
 +                      p = hostapd_eid_p2p_manage(hapd, start);
 +                      wpabuf_put(proberesp, p - start);
 +              }
 +      }
 +#endif /* CONFIG_P2P_MANAGER */
 +
 +#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_WPS */
 +
 +#ifdef CONFIG_P2P_MANAGER
 +      if (hapd->conf->p2p & P2P_MANAGE) {
 +              if (wpabuf_resize(&assocresp, 100) == 0) {
 +                      u8 *start, *p;
 +                      start = wpabuf_put(assocresp, 0);
 +                      p = hostapd_eid_p2p_manage(hapd, start);
 +                      wpabuf_put(assocresp, p - start);
 +              }
 +      }
 +#endif /* CONFIG_P2P_MANAGER */
 +
 +#ifdef CONFIG_WIFI_DISPLAY
 +      if (hapd->p2p_group) {
 +              struct wpabuf *a;
 +              a = p2p_group_assoc_resp_ie(hapd->p2p_group, P2P_SC_SUCCESS);
 +              if (a && wpabuf_resize(&assocresp, wpabuf_len(a)) == 0)
 +                      wpabuf_put_buf(assocresp, a);
 +              wpabuf_free(a);
 +      }
 +#endif /* CONFIG_WIFI_DISPLAY */
 +
 +#ifdef CONFIG_HS20
 +      pos = buf;
 +      pos = hostapd_eid_hs20_indication(hapd, pos);
 +      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);
 +      }
 +
 +      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) {
 +              size_t add = wpabuf_len(hapd->conf->vendor_elements);
 +              if (wpabuf_resize(&beacon, add) == 0)
 +                      wpabuf_put_buf(beacon, hapd->conf->vendor_elements);
 +              if (wpabuf_resize(&proberesp, add) == 0)
 +                      wpabuf_put_buf(proberesp, hapd->conf->vendor_elements);
 +      }
 +
 +      *beacon_ret = beacon;
 +      *proberesp_ret = proberesp;
 +      *assocresp_ret = assocresp;
 +
 +      return 0;
 +
 +fail:
 +      wpabuf_free(beacon);
 +      wpabuf_free(proberesp);
 +      wpabuf_free(assocresp);
 +      return -1;
 +}
 +
 +
 +void hostapd_free_ap_extra_ies(struct hostapd_data *hapd,
 +                             struct wpabuf *beacon,
 +                             struct wpabuf *proberesp,
 +                             struct wpabuf *assocresp)
 +{
 +      wpabuf_free(beacon);
 +      wpabuf_free(proberesp);
 +      wpabuf_free(assocresp);
 +}
 +
 +
++int hostapd_reset_ap_wps_ie(struct hostapd_data *hapd)
++{
++      if (hapd->driver == NULL || hapd->driver->set_ap_wps_ie == NULL)
++              return 0;
++
++      return hapd->driver->set_ap_wps_ie(hapd->drv_priv, NULL, NULL, NULL);
++}
++
++
 +int hostapd_set_ap_wps_ie(struct hostapd_data *hapd)
 +{
 +      struct wpabuf *beacon, *proberesp, *assocresp;
 +      int ret;
 +
 +      if (hapd->driver == NULL || hapd->driver->set_ap_wps_ie == NULL)
 +              return 0;
 +
 +      if (hostapd_build_ap_extra_ies(hapd, &beacon, &proberesp, &assocresp) <
 +          0)
 +              return -1;
 +
 +      ret = hapd->driver->set_ap_wps_ie(hapd->drv_priv, beacon, proberesp,
 +                                        assocresp);
 +
 +      hostapd_free_ap_extra_ies(hapd, beacon, proberesp, assocresp);
 +
 +      return ret;
 +}
 +
 +
 +int hostapd_set_authorized(struct hostapd_data *hapd,
 +                         struct sta_info *sta, int authorized)
 +{
 +      if (authorized) {
 +              return hostapd_sta_set_flags(hapd, sta->addr,
 +                                           hostapd_sta_flags_to_drv(
 +                                                   sta->flags),
 +                                           WPA_STA_AUTHORIZED, ~0);
 +      }
 +
 +      return hostapd_sta_set_flags(hapd, sta->addr,
 +                                   hostapd_sta_flags_to_drv(sta->flags),
 +                                   0, ~WPA_STA_AUTHORIZED);
 +}
 +
 +
 +int hostapd_set_sta_flags(struct hostapd_data *hapd, struct sta_info *sta)
 +{
 +      int set_flags, total_flags, flags_and, flags_or;
 +      total_flags = hostapd_sta_flags_to_drv(sta->flags);
 +      set_flags = WPA_STA_SHORT_PREAMBLE | WPA_STA_WMM | WPA_STA_MFP;
 +      if (((!hapd->conf->ieee802_1x && !hapd->conf->wpa) ||
 +           sta->auth_alg == WLAN_AUTH_FT) &&
 +          sta->flags & WLAN_STA_AUTHORIZED)
 +              set_flags |= WPA_STA_AUTHORIZED;
 +      flags_or = total_flags & set_flags;
 +      flags_and = total_flags | ~set_flags;
 +      return hostapd_sta_set_flags(hapd, sta->addr, total_flags,
 +                                   flags_or, flags_and);
 +}
 +
 +
 +int hostapd_set_drv_ieee8021x(struct hostapd_data *hapd, const char *ifname,
 +                            int enabled)
 +{
 +      struct wpa_bss_params params;
 +      os_memset(&params, 0, sizeof(params));
 +      params.ifname = ifname;
 +      params.enabled = enabled;
 +      if (enabled) {
 +              params.wpa = hapd->conf->wpa;
 +              params.ieee802_1x = hapd->conf->ieee802_1x;
 +              params.wpa_group = hapd->conf->wpa_group;
-       return hapd->driver->send_mlme(hapd->drv_priv, msg, len, noack);
++              if ((hapd->conf->wpa & (WPA_PROTO_WPA | WPA_PROTO_RSN)) ==
++                  (WPA_PROTO_WPA | WPA_PROTO_RSN))
++                      params.wpa_pairwise = hapd->conf->wpa_pairwise |
++                              hapd->conf->rsn_pairwise;
++              else if (hapd->conf->wpa & WPA_PROTO_RSN)
++                      params.wpa_pairwise = hapd->conf->rsn_pairwise;
++              else if (hapd->conf->wpa & WPA_PROTO_WPA)
++                      params.wpa_pairwise = hapd->conf->wpa_pairwise;
 +              params.wpa_key_mgmt = hapd->conf->wpa_key_mgmt;
 +              params.rsn_preauth = hapd->conf->rsn_preauth;
 +#ifdef CONFIG_IEEE80211W
 +              params.ieee80211w = hapd->conf->ieee80211w;
 +#endif /* CONFIG_IEEE80211W */
 +      }
 +      return hostapd_set_ieee8021x(hapd, &params);
 +}
 +
 +
 +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, 0);
 +}
 +
 +
 +int hostapd_vlan_if_remove(struct hostapd_data *hapd, const char *ifname)
 +{
 +      return hostapd_if_remove(hapd, WPA_IF_AP_VLAN, ifname);
 +}
 +
 +
 +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 -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, ifname_wds);
 +}
 +
 +
 +int hostapd_add_sta_node(struct hostapd_data *hapd, const u8 *addr,
 +                       u16 auth_alg)
 +{
 +      if (hapd->driver == NULL || hapd->driver->add_sta_node == NULL)
 +              return 0;
 +      return hapd->driver->add_sta_node(hapd->drv_priv, addr, auth_alg);
 +}
 +
 +
 +int hostapd_sta_auth(struct hostapd_data *hapd, const u8 *addr,
 +                   u16 seq, u16 status, const u8 *ie, size_t len)
 +{
 +      if (hapd->driver == NULL || hapd->driver->sta_auth == NULL)
 +              return 0;
 +      return hapd->driver->sta_auth(hapd->drv_priv, hapd->own_addr, addr,
 +                                    seq, status, ie, len);
 +}
 +
 +
 +int hostapd_sta_assoc(struct hostapd_data *hapd, const u8 *addr,
 +                    int reassoc, u16 status, const u8 *ie, size_t len)
 +{
 +      if (hapd->driver == NULL || hapd->driver->sta_assoc == NULL)
 +              return 0;
 +      return hapd->driver->sta_assoc(hapd->drv_priv, hapd->own_addr, addr,
 +                                     reassoc, status, ie, len);
 +}
 +
 +
 +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, u8 vht_opmode)
 +{
 +      struct hostapd_sta_add_params params;
 +
 +      if (hapd->driver == NULL)
 +              return 0;
 +      if (hapd->driver->sta_add == NULL)
 +              return 0;
 +
 +      os_memset(&params, 0, sizeof(params));
 +      params.addr = addr;
 +      params.aid = aid;
 +      params.capability = capability;
 +      params.supp_rates = supp_rates;
 +      params.supp_rates_len = supp_rates_len;
 +      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);
 +}
 +
 +
 +int hostapd_add_tspec(struct hostapd_data *hapd, const u8 *addr,
 +                    u8 *tspec_ie, size_t tspec_ielen)
 +{
 +      if (hapd->driver == NULL || hapd->driver->add_tspec == NULL)
 +              return 0;
 +      return hapd->driver->add_tspec(hapd->drv_priv, addr, tspec_ie,
 +                                     tspec_ielen);
 +}
 +
 +
 +int hostapd_set_privacy(struct hostapd_data *hapd, int enabled)
 +{
 +      if (hapd->driver == NULL || hapd->driver->set_privacy == NULL)
 +              return 0;
 +      return hapd->driver->set_privacy(hapd->drv_priv, enabled);
 +}
 +
 +
 +int hostapd_set_generic_elem(struct hostapd_data *hapd, const u8 *elem,
 +                           size_t elem_len)
 +{
 +      if (hapd->driver == NULL || hapd->driver->set_generic_elem == NULL)
 +              return 0;
 +      return hapd->driver->set_generic_elem(hapd->drv_priv, elem, elem_len);
 +}
 +
 +
 +int hostapd_get_ssid(struct hostapd_data *hapd, u8 *buf, size_t len)
 +{
 +      if (hapd->driver == NULL || hapd->driver->hapd_get_ssid == NULL)
 +              return 0;
 +      return hapd->driver->hapd_get_ssid(hapd->drv_priv, buf, len);
 +}
 +
 +
 +int hostapd_set_ssid(struct hostapd_data *hapd, const u8 *buf, size_t len)
 +{
 +      if (hapd->driver == NULL || hapd->driver->hapd_set_ssid == NULL)
 +              return 0;
 +      return hapd->driver->hapd_set_ssid(hapd->drv_priv, buf, 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, 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, use_existing);
 +}
 +
 +
 +int hostapd_if_remove(struct hostapd_data *hapd, enum wpa_driver_if_type type,
 +                    const char *ifname)
 +{
 +      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);
 +}
 +
 +
 +int hostapd_set_ieee8021x(struct hostapd_data *hapd,
 +                        struct wpa_bss_params *params)
 +{
 +      if (hapd->driver == NULL || hapd->driver->set_ieee8021x == NULL)
 +              return 0;
 +      return hapd->driver->set_ieee8021x(hapd->drv_priv, params);
 +}
 +
 +
 +int hostapd_get_seqnum(const char *ifname, struct hostapd_data *hapd,
 +                     const u8 *addr, int idx, u8 *seq)
 +{
 +      if (hapd->driver == NULL || hapd->driver->get_seqnum == NULL)
 +              return 0;
 +      return hapd->driver->get_seqnum(ifname, hapd->drv_priv, addr, idx,
 +                                      seq);
 +}
 +
 +
 +int hostapd_flush(struct hostapd_data *hapd)
 +{
 +      if (hapd->driver == NULL || hapd->driver->flush == NULL)
 +              return 0;
 +      return hapd->driver->flush(hapd->drv_priv);
 +}
 +
 +
 +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;
 +
 +      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)
 +              return 0;
 +      return hapd->driver->set_freq(hapd->drv_priv, &data);
 +}
 +
 +int hostapd_set_rts(struct hostapd_data *hapd, int rts)
 +{
 +      if (hapd->driver == NULL || hapd->driver->set_rts == NULL)
 +              return 0;
 +      return hapd->driver->set_rts(hapd->drv_priv, rts);
 +}
 +
 +
 +int hostapd_set_frag(struct hostapd_data *hapd, int frag)
 +{
 +      if (hapd->driver == NULL || hapd->driver->set_frag == NULL)
 +              return 0;
 +      return hapd->driver->set_frag(hapd->drv_priv, frag);
 +}
 +
 +
 +int hostapd_sta_set_flags(struct hostapd_data *hapd, u8 *addr,
 +                        int total_flags, int flags_or, int flags_and)
 +{
 +      if (hapd->driver == NULL || hapd->driver->sta_set_flags == NULL)
 +              return 0;
 +      return hapd->driver->sta_set_flags(hapd->drv_priv, addr, total_flags,
 +                                         flags_or, flags_and);
 +}
 +
 +
 +int hostapd_set_country(struct hostapd_data *hapd, const char *country)
 +{
 +      if (hapd->driver == NULL ||
 +          hapd->driver->set_country == NULL)
 +              return 0;
 +      return hapd->driver->set_country(hapd->drv_priv, country);
 +}
 +
 +
 +int hostapd_set_tx_queue_params(struct hostapd_data *hapd, int queue, int aifs,
 +                              int cw_min, int cw_max, int burst_time)
 +{
 +      if (hapd->driver == NULL || hapd->driver->set_tx_queue_params == NULL)
 +              return 0;
 +      return hapd->driver->set_tx_queue_params(hapd->drv_priv, queue, aifs,
 +                                               cw_min, cw_max, burst_time);
 +}
 +
 +
 +struct hostapd_hw_modes *
 +hostapd_get_hw_feature_data(struct hostapd_data *hapd, u16 *num_modes,
 +                          u16 *flags)
 +{
 +      if (hapd->driver == NULL ||
 +          hapd->driver->get_hw_feature_data == NULL)
 +              return NULL;
 +      return hapd->driver->get_hw_feature_data(hapd->drv_priv, num_modes,
 +                                               flags);
 +}
 +
 +
 +int hostapd_driver_commit(struct hostapd_data *hapd)
 +{
 +      if (hapd->driver == NULL || hapd->driver->commit == NULL)
 +              return 0;
 +      return hapd->driver->commit(hapd->drv_priv);
 +}
 +
 +
 +int hostapd_drv_none(struct hostapd_data *hapd)
 +{
 +      return hapd->driver && os_strcmp(hapd->driver->name, "none") == 0;
 +}
 +
 +
 +int hostapd_driver_scan(struct hostapd_data *hapd,
 +                      struct wpa_driver_scan_params *params)
 +{
 +      if (hapd->driver && hapd->driver->scan2)
 +              return hapd->driver->scan2(hapd->drv_priv, params);
 +      return -1;
 +}
 +
 +
 +struct wpa_scan_results * hostapd_driver_get_scan_results(
 +      struct hostapd_data *hapd)
 +{
 +      if (hapd->driver && hapd->driver->get_scan_results2)
 +              return hapd->driver->get_scan_results2(hapd->drv_priv);
 +      return NULL;
 +}
 +
 +
 +int hostapd_driver_set_noa(struct hostapd_data *hapd, u8 count, int start,
 +                         int duration)
 +{
 +      if (hapd->driver && hapd->driver->set_noa)
 +              return hapd->driver->set_noa(hapd->drv_priv, count, start,
 +                                           duration);
 +      return -1;
 +}
 +
 +
 +int hostapd_drv_set_key(const char *ifname, struct hostapd_data *hapd,
 +                      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)
 +{
 +      if (hapd->driver == NULL || hapd->driver->set_key == NULL)
 +              return 0;
 +      return hapd->driver->set_key(ifname, hapd->drv_priv, alg, addr,
 +                                   key_idx, set_tx, seq, seq_len, key,
 +                                   key_len);
 +}
 +
 +
 +int hostapd_drv_send_mlme(struct hostapd_data *hapd,
 +                        const void *msg, size_t len, int noack)
 +{
 +      if (hapd->driver == NULL || hapd->driver->send_mlme == NULL)
 +              return 0;
-       return hapd->driver->do_acs(hapd->drv_priv, &params);
++      return hapd->driver->send_mlme(hapd->drv_priv, msg, len, noack, 0);
 +}
 +
 +
 +int hostapd_drv_sta_deauth(struct hostapd_data *hapd,
 +                         const u8 *addr, int reason)
 +{
 +      if (hapd->driver == NULL || hapd->driver->sta_deauth == NULL)
 +              return 0;
 +      return hapd->driver->sta_deauth(hapd->drv_priv, hapd->own_addr, addr,
 +                                      reason);
 +}
 +
 +
 +int hostapd_drv_sta_disassoc(struct hostapd_data *hapd,
 +                           const u8 *addr, int reason)
 +{
 +      if (hapd->driver == NULL || hapd->driver->sta_disassoc == NULL)
 +              return 0;
 +      return hapd->driver->sta_disassoc(hapd->drv_priv, hapd->own_addr, addr,
 +                                        reason);
 +}
 +
 +
 +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 -1;
 +      return hapd->driver->wnm_oper(hapd->drv_priv, oper, peer, buf,
 +                                    buf_len);
 +}
 +
 +
 +int hostapd_drv_send_action(struct hostapd_data *hapd, unsigned int freq,
 +                          unsigned int wait, const u8 *dst, const u8 *data,
 +                          size_t len)
 +{
 +      if (hapd->driver == NULL || hapd->driver->send_action == NULL)
 +              return 0;
 +      return hapd->driver->send_action(hapd->drv_priv, freq, wait, dst,
 +                                       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);
 +}
 +
 +
++static void hostapd_get_hw_mode_any_channels(struct hostapd_data *hapd,
++                                           struct hostapd_hw_modes *mode,
++                                           int acs_ch_list_all,
++                                           int **freq_list)
++{
++      int i;
++
++      for (i = 0; i < mode->num_channels; i++) {
++              struct hostapd_channel_data *chan = &mode->channels[i];
++
++              if ((acs_ch_list_all ||
++                   freq_range_list_includes(&hapd->iface->conf->acs_ch_list,
++                                            chan->chan)) &&
++                  !(chan->flag & HOSTAPD_CHAN_DISABLED))
++                      int_array_add_unique(freq_list, chan->freq);
++      }
++}
++
++
 +int hostapd_drv_do_acs(struct hostapd_data *hapd)
 +{
 +      struct drv_acs_params params;
++      int ret, i, acs_ch_list_all = 0;
++      u8 *channels = NULL;
++      unsigned int num_channels = 0;
++      struct hostapd_hw_modes *mode;
++      int *freq_list = NULL;
 +
 +      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;
++
++      /*
++       * If no chanlist config parameter is provided, include all enabled
++       * channels of the selected hw_mode.
++       */
++      if (!hapd->iface->conf->acs_ch_list.num)
++              acs_ch_list_all = 1;
++
++      mode = hapd->iface->current_mode;
++      if (mode) {
++              channels = os_malloc(mode->num_channels);
++              if (channels == NULL)
++                      return -1;
++
++              for (i = 0; i < mode->num_channels; i++) {
++                      struct hostapd_channel_data *chan = &mode->channels[i];
++                      if (!acs_ch_list_all &&
++                          !freq_range_list_includes(
++                                  &hapd->iface->conf->acs_ch_list,
++                                  chan->chan))
++                              continue;
++                      if (!(chan->flag & HOSTAPD_CHAN_DISABLED)) {
++                              channels[num_channels++] = chan->chan;
++                              int_array_add_unique(&freq_list, chan->freq);
++                      }
++              }
++      } else {
++              for (i = 0; i < hapd->iface->num_hw_features; i++) {
++                      mode = &hapd->iface->hw_features[i];
++                      hostapd_get_hw_mode_any_channels(hapd, mode,
++                                                       acs_ch_list_all,
++                                                       &freq_list);
++              }
++      }
++
++      params.ch_list = channels;
++      params.ch_list_len = num_channels;
++      params.freq_list = freq_list;
++
 +      params.ht_enabled = !!(hapd->iface->conf->ieee80211n);
 +      params.ht40_enabled = !!(hapd->iface->conf->ht_capab &
 +                               HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET);
++      params.vht_enabled = !!(hapd->iface->conf->ieee80211ac);
++      params.ch_width = 20;
++      if (hapd->iface->conf->ieee80211n && params.ht40_enabled)
++              params.ch_width = 40;
++
++      /* Note: VHT20 is defined by combination of ht_capab & vht_oper_chwidth
++       */
++      if (hapd->iface->conf->ieee80211ac && params.ht40_enabled) {
++              if (hapd->iface->conf->vht_oper_chwidth == VHT_CHANWIDTH_80MHZ)
++                      params.ch_width = 80;
++              else if (hapd->iface->conf->vht_oper_chwidth ==
++                       VHT_CHANWIDTH_160MHZ ||
++                       hapd->iface->conf->vht_oper_chwidth ==
++                       VHT_CHANWIDTH_80P80MHZ)
++                      params.ch_width = 160;
++      }
++
++      ret = hapd->driver->do_acs(hapd->drv_priv, &params);
++      os_free(channels);
++
++      return ret;
 +}
index 5d07e71f1bf13f77ed1f20c1f58bc753af31b86f,0000000000000000000000000000000000000000..82eaf3f08bb5b2cf117ce159515a943cd2e2ccf8
mode 100644,000000..100644
--- /dev/null
@@@ -1,339 -1,0 +1,340 @@@
 +/*
 + * hostapd - Driver operations
 + * 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.
 + */
 +
 +#ifndef AP_DRV_OPS
 +#define AP_DRV_OPS
 +
 +enum wpa_driver_if_type;
 +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,
 +                             struct wpabuf **beacon,
 +                             struct wpabuf **proberesp,
 +                             struct wpabuf **assocresp);
 +void hostapd_free_ap_extra_ies(struct hostapd_data *hapd, struct wpabuf *beacon,
 +                             struct wpabuf *proberesp,
 +                             struct wpabuf *assocresp);
++int hostapd_reset_ap_wps_ie(struct hostapd_data *hapd);
 +int hostapd_set_ap_wps_ie(struct hostapd_data *hapd);
 +int hostapd_set_authorized(struct hostapd_data *hapd,
 +                         struct sta_info *sta, int authorized);
 +int hostapd_set_sta_flags(struct hostapd_data *hapd, struct sta_info *sta);
 +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, 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, 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);
 +int hostapd_get_ssid(struct hostapd_data *hapd, u8 *buf, size_t len);
 +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, 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,
 +                        struct wpa_bss_params *params);
 +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, 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);
 +int hostapd_set_frag(struct hostapd_data *hapd, int frag);
 +int hostapd_sta_set_flags(struct hostapd_data *hapd, u8 *addr,
 +                        int total_flags, int flags_or, int flags_and);
 +int hostapd_set_country(struct hostapd_data *hapd, const char *country);
 +int hostapd_set_tx_queue_params(struct hostapd_data *hapd, int queue, int aifs,
 +                              int cw_min, int cw_max, int burst_time);
 +struct hostapd_hw_modes *
 +hostapd_get_hw_feature_data(struct hostapd_data *hapd, u16 *num_modes,
 +                          u16 *flags);
 +int hostapd_driver_commit(struct hostapd_data *hapd);
 +int hostapd_drv_none(struct hostapd_data *hapd);
 +int hostapd_driver_scan(struct hostapd_data *hapd,
 +                      struct wpa_driver_scan_params *params);
 +struct wpa_scan_results * hostapd_driver_get_scan_results(
 +      struct hostapd_data *hapd);
 +int hostapd_driver_set_noa(struct hostapd_data *hapd, u8 count, int start,
 +                         int duration);
 +int hostapd_drv_set_key(const char *ifname,
 +                      struct hostapd_data *hapd,
 +                      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);
 +int hostapd_drv_send_mlme(struct hostapd_data *hapd,
 +                        const void *msg, size_t len, int noack);
 +int hostapd_drv_sta_deauth(struct hostapd_data *hapd,
 +                         const u8 *addr, int reason);
 +int hostapd_drv_sta_disassoc(struct hostapd_data *hapd,
 +                           const u8 *addr, int reason);
 +int hostapd_drv_send_action(struct hostapd_data *hapd, unsigned int freq,
 +                          unsigned int wait, const u8 *dst, const u8 *data,
 +                          size_t len);
 +int hostapd_add_sta_node(struct hostapd_data *hapd, const u8 *addr,
 +                       u16 auth_alg);
 +int hostapd_sta_auth(struct hostapd_data *hapd, const u8 *addr,
 +                   u16 seq, u16 status, const u8 *ie, size_t len);
 +int hostapd_sta_assoc(struct hostapd_data *hapd, const u8 *addr,
 +                    int reassoc, u16 status, const u8 *ie, size_t len);
 +int hostapd_add_tspec(struct hostapd_data *hapd, const u8 *addr,
 +                    u8 *tspec_ie, size_t tspec_ielen);
 +int hostapd_start_dfs_cac(struct hostapd_iface *iface,
 +                        enum hostapd_hw_mode mode, int freq,
 +                        int channel, int ht_enabled, int vht_enabled,
 +                        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"
 +
 +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)
 +{
 +      if (hapd->driver == NULL ||
 +          hapd->driver->hapd_set_countermeasures == NULL)
 +              return 0;
 +      return hapd->driver->hapd_set_countermeasures(hapd->drv_priv, enabled);
 +}
 +
 +static inline int hostapd_drv_set_sta_vlan(const char *ifname,
 +                                         struct hostapd_data *hapd,
 +                                         const u8 *addr, int vlan_id)
 +{
 +      if (hapd->driver == NULL || hapd->driver->set_sta_vlan == NULL)
 +              return 0;
 +      return hapd->driver->set_sta_vlan(hapd->drv_priv, addr, ifname,
 +                                        vlan_id);
 +}
 +
 +static inline int hostapd_drv_get_inact_sec(struct hostapd_data *hapd,
 +                                          const u8 *addr)
 +{
 +      if (hapd->driver == NULL || hapd->driver->get_inact_sec == NULL)
 +              return 0;
 +      return hapd->driver->get_inact_sec(hapd->drv_priv, addr);
 +}
 +
 +static inline int hostapd_drv_sta_remove(struct hostapd_data *hapd,
 +                                       const u8 *addr)
 +{
 +      if (hapd->driver == NULL || hapd->driver->sta_remove == NULL)
 +              return 0;
 +      return hapd->driver->sta_remove(hapd->drv_priv, addr);
 +}
 +
 +static inline int hostapd_drv_hapd_send_eapol(struct hostapd_data *hapd,
 +                                            const u8 *addr, const u8 *data,
 +                                            size_t data_len, int encrypt,
 +                                            u32 flags)
 +{
 +      if (hapd->driver == NULL || hapd->driver->hapd_send_eapol == NULL)
 +              return 0;
 +      return hapd->driver->hapd_send_eapol(hapd->drv_priv, addr, data,
 +                                           data_len, encrypt,
 +                                           hapd->own_addr, flags);
 +}
 +
 +static inline int hostapd_drv_read_sta_data(
 +      struct hostapd_data *hapd, struct hostap_sta_driver_data *data,
 +      const u8 *addr)
 +{
 +      if (hapd->driver == NULL || hapd->driver->read_sta_data == NULL)
 +              return -1;
 +      return hapd->driver->read_sta_data(hapd->drv_priv, data, addr);
 +}
 +
 +static inline int hostapd_drv_sta_clear_stats(struct hostapd_data *hapd,
 +                                            const u8 *addr)
 +{
 +      if (hapd->driver == NULL || hapd->driver->sta_clear_stats == NULL)
 +              return 0;
 +      return hapd->driver->sta_clear_stats(hapd->drv_priv, addr);
 +}
 +
 +static inline int hostapd_drv_set_acl(struct hostapd_data *hapd,
 +                                    struct hostapd_acl_params *params)
 +{
 +      if (hapd->driver == NULL || hapd->driver->set_acl == NULL)
 +              return 0;
 +      return hapd->driver->set_acl(hapd->drv_priv, params);
 +}
 +
 +static inline int hostapd_drv_set_ap(struct hostapd_data *hapd,
 +                                   struct wpa_driver_ap_params *params)
 +{
 +      if (hapd->driver == NULL || hapd->driver->set_ap == NULL)
 +              return 0;
 +      return hapd->driver->set_ap(hapd->drv_priv, params);
 +}
 +
 +static inline int hostapd_drv_set_radius_acl_auth(struct hostapd_data *hapd,
 +                                                const u8 *mac, int accepted,
 +                                                u32 session_timeout)
 +{
 +      if (hapd->driver == NULL || hapd->driver->set_radius_acl_auth == NULL)
 +              return 0;
 +      return hapd->driver->set_radius_acl_auth(hapd->drv_priv, mac, accepted,
 +                                               session_timeout);
 +}
 +
 +static inline int hostapd_drv_set_radius_acl_expire(struct hostapd_data *hapd,
 +                                                  const u8 *mac)
 +{
 +      if (hapd->driver == NULL ||
 +          hapd->driver->set_radius_acl_expire == NULL)
 +              return 0;
 +      return hapd->driver->set_radius_acl_expire(hapd->drv_priv, mac);
 +}
 +
 +static inline int hostapd_drv_set_authmode(struct hostapd_data *hapd,
 +                                         int auth_algs)
 +{
 +      if (hapd->driver == NULL || hapd->driver->set_authmode == NULL)
 +              return 0;
 +      return hapd->driver->set_authmode(hapd->drv_priv, auth_algs);
 +}
 +
 +static inline void hostapd_drv_poll_client(struct hostapd_data *hapd,
 +                                         const u8 *own_addr, const u8 *addr,
 +                                         int qos)
 +{
 +      if (hapd->driver == NULL || hapd->driver->poll_client == NULL)
 +              return;
 +      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 04a56a95efd91b903fa2112acb932648b881b660,0000000000000000000000000000000000000000..8bf6ddec8d3756e5d0aef3cb6ed99892677886f5
mode 100644,000000..100644
--- /dev/null
@@@ -1,317 -1,0 +1,312 @@@
-       if (elems->erp_info && elems->erp_info_len == 1)
 +/*
 + * hostapd / AP table
 + * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
 + * Copyright (c) 2003-2004, Instant802 Networks, Inc.
 + * Copyright (c) 2006, Devicescape Software, 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/ieee802_11_defs.h"
 +#include "common/ieee802_11_common.h"
 +#include "hostapd.h"
 +#include "ap_config.h"
 +#include "ieee802_11.h"
 +#include "sta_info.h"
 +#include "beacon.h"
 +#include "ap_list.h"
 +
 +
 +/* AP list is a double linked list with head->prev pointing to the end of the
 + * list and tail->next = NULL. Entries are moved to the head of the list
 + * whenever a beacon has been received from the AP in question. The tail entry
 + * in this link will thus be the least recently used entry. */
 +
 +
 +static int ap_list_beacon_olbc(struct hostapd_iface *iface, struct ap_info *ap)
 +{
 +      int i;
 +
 +      if (iface->current_mode == NULL ||
 +          iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G ||
 +          iface->conf->channel != ap->channel)
 +              return 0;
 +
 +      if (ap->erp != -1 && (ap->erp & ERP_INFO_NON_ERP_PRESENT))
 +              return 1;
 +
 +      for (i = 0; i < WLAN_SUPP_RATES_MAX; i++) {
 +              int rate = (ap->supported_rates[i] & 0x7f) * 5;
 +              if (rate == 60 || rate == 90 || rate > 110)
 +                      return 0;
 +      }
 +
 +      return 1;
 +}
 +
 +
 +static struct ap_info * ap_get_ap(struct hostapd_iface *iface, const u8 *ap)
 +{
 +      struct ap_info *s;
 +
 +      s = iface->ap_hash[STA_HASH(ap)];
 +      while (s != NULL && os_memcmp(s->addr, ap, ETH_ALEN) != 0)
 +              s = s->hnext;
 +      return s;
 +}
 +
 +
 +static void ap_ap_list_add(struct hostapd_iface *iface, struct ap_info *ap)
 +{
 +      if (iface->ap_list) {
 +              ap->prev = iface->ap_list->prev;
 +              iface->ap_list->prev = ap;
 +      } else
 +              ap->prev = ap;
 +      ap->next = iface->ap_list;
 +      iface->ap_list = ap;
 +}
 +
 +
 +static void ap_ap_list_del(struct hostapd_iface *iface, struct ap_info *ap)
 +{
 +      if (iface->ap_list == ap)
 +              iface->ap_list = ap->next;
 +      else
 +              ap->prev->next = ap->next;
 +
 +      if (ap->next)
 +              ap->next->prev = ap->prev;
 +      else if (iface->ap_list)
 +              iface->ap_list->prev = ap->prev;
 +}
 +
 +
 +static void ap_ap_hash_add(struct hostapd_iface *iface, struct ap_info *ap)
 +{
 +      ap->hnext = iface->ap_hash[STA_HASH(ap->addr)];
 +      iface->ap_hash[STA_HASH(ap->addr)] = ap;
 +}
 +
 +
 +static void ap_ap_hash_del(struct hostapd_iface *iface, struct ap_info *ap)
 +{
 +      struct ap_info *s;
 +
 +      s = iface->ap_hash[STA_HASH(ap->addr)];
 +      if (s == NULL) return;
 +      if (os_memcmp(s->addr, ap->addr, ETH_ALEN) == 0) {
 +              iface->ap_hash[STA_HASH(ap->addr)] = s->hnext;
 +              return;
 +      }
 +
 +      while (s->hnext != NULL &&
 +             os_memcmp(s->hnext->addr, ap->addr, ETH_ALEN) != 0)
 +              s = s->hnext;
 +      if (s->hnext != NULL)
 +              s->hnext = s->hnext->hnext;
 +      else
 +              wpa_printf(MSG_INFO, "AP: could not remove AP " MACSTR
 +                         " from hash table",  MAC2STR(ap->addr));
 +}
 +
 +
 +static void ap_free_ap(struct hostapd_iface *iface, struct ap_info *ap)
 +{
 +      ap_ap_hash_del(iface, ap);
 +      ap_ap_list_del(iface, ap);
 +
 +      iface->num_ap--;
 +      os_free(ap);
 +}
 +
 +
 +static void hostapd_free_aps(struct hostapd_iface *iface)
 +{
 +      struct ap_info *ap, *prev;
 +
 +      ap = iface->ap_list;
 +
 +      while (ap) {
 +              prev = ap;
 +              ap = ap->next;
 +              ap_free_ap(iface, prev);
 +      }
 +
 +      iface->ap_list = NULL;
 +}
 +
 +
 +static struct ap_info * ap_ap_add(struct hostapd_iface *iface, const u8 *addr)
 +{
 +      struct ap_info *ap;
 +
 +      ap = os_zalloc(sizeof(struct ap_info));
 +      if (ap == NULL)
 +              return NULL;
 +
 +      /* initialize AP info data */
 +      os_memcpy(ap->addr, addr, ETH_ALEN);
 +      ap_ap_list_add(iface, ap);
 +      iface->num_ap++;
 +      ap_ap_hash_add(iface, ap);
 +
 +      if (iface->num_ap > iface->conf->ap_table_max_size && ap != ap->prev) {
 +              wpa_printf(MSG_DEBUG, "Removing the least recently used AP "
 +                         MACSTR " from AP table", MAC2STR(ap->prev->addr));
 +              ap_free_ap(iface, ap->prev);
 +      }
 +
 +      return ap;
 +}
 +
 +
 +void ap_list_process_beacon(struct hostapd_iface *iface,
 +                          const struct ieee80211_mgmt *mgmt,
 +                          struct ieee802_11_elems *elems,
 +                          struct hostapd_frame_info *fi)
 +{
 +      struct ap_info *ap;
 +      int new_ap = 0;
 +      int set_beacon = 0;
 +
 +      if (iface->conf->ap_table_max_size < 1)
 +              return;
 +
 +      ap = ap_get_ap(iface, mgmt->bssid);
 +      if (!ap) {
 +              ap = ap_ap_add(iface, mgmt->bssid);
 +              if (!ap) {
 +                      wpa_printf(MSG_INFO,
 +                                 "Failed to allocate AP information entry");
 +                      return;
 +              }
 +              new_ap = 1;
 +      }
 +
 +      merge_byte_arrays(ap->supported_rates, WLAN_SUPP_RATES_MAX,
 +                        elems->supp_rates, elems->supp_rates_len,
 +                        elems->ext_supp_rates, elems->ext_supp_rates_len);
 +
-       if (elems->ds_params && elems->ds_params_len == 1)
++      if (elems->erp_info)
 +              ap->erp = elems->erp_info[0];
 +      else
 +              ap->erp = -1;
 +
-       else if (elems->ht_operation && elems->ht_operation_len >= 1)
++      if (elems->ds_params)
 +              ap->channel = elems->ds_params[0];
- static void ap_list_timer(void *eloop_ctx, void *timeout_ctx)
++      else if (elems->ht_operation)
 +              ap->channel = elems->ht_operation[0];
 +      else if (fi)
 +              ap->channel = fi->channel;
 +
 +      if (elems->ht_capabilities)
 +              ap->ht_support = 1;
 +      else
 +              ap->ht_support = 0;
 +
 +      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
 +               * oldest entry is always in the end of the list */
 +              ap_ap_list_del(iface, ap);
 +              ap_ap_list_add(iface, ap);
 +      }
 +
 +      if (!iface->olbc &&
 +          ap_list_beacon_olbc(iface, ap)) {
 +              iface->olbc = 1;
 +              wpa_printf(MSG_DEBUG, "OLBC AP detected: " MACSTR
 +                         " (channel %d) - enable protection",
 +                         MAC2STR(ap->addr), ap->channel);
 +              set_beacon++;
 +      }
 +
 +#ifdef CONFIG_IEEE80211N
 +      if (!iface->olbc_ht && !ap->ht_support &&
 +          (ap->channel == 0 ||
 +           ap->channel == iface->conf->channel ||
 +           ap->channel == iface->conf->channel +
 +           iface->conf->secondary_channel * 4)) {
 +              iface->olbc_ht = 1;
 +              hostapd_ht_operation_update(iface);
 +              wpa_printf(MSG_DEBUG, "OLBC HT AP detected: " MACSTR
 +                         " (channel %d) - enable protection",
 +                         MAC2STR(ap->addr), ap->channel);
 +              set_beacon++;
 +      }
 +#endif /* CONFIG_IEEE80211N */
 +
 +      if (set_beacon)
 +              ieee802_11_update_beacons(iface);
 +}
 +
 +
-       struct hostapd_iface *iface = eloop_ctx;
++void ap_list_timer(struct hostapd_iface *iface)
 +{
-       eloop_register_timeout(10, 0, ap_list_timer, iface, NULL);
 +      struct os_reltime now;
 +      struct ap_info *ap;
 +      int set_beacon = 0;
 +
-       eloop_register_timeout(10, 0, ap_list_timer, iface, NULL);
 +      if (!iface->ap_list)
 +              return;
 +
 +      os_get_reltime(&now);
 +
 +      while (iface->ap_list) {
 +              ap = iface->ap_list->prev;
 +              if (!os_reltime_expired(&now, &ap->last_beacon,
 +                                      iface->conf->ap_table_expiration_time))
 +                      break;
 +
 +              ap_free_ap(iface, ap);
 +      }
 +
 +      if (iface->olbc || iface->olbc_ht) {
 +              int olbc = 0;
 +              int olbc_ht = 0;
 +
 +              ap = iface->ap_list;
 +              while (ap && (olbc == 0 || olbc_ht == 0)) {
 +                      if (ap_list_beacon_olbc(iface, ap))
 +                              olbc = 1;
 +                      if (!ap->ht_support)
 +                              olbc_ht = 1;
 +                      ap = ap->next;
 +              }
 +              if (!olbc && iface->olbc) {
 +                      wpa_printf(MSG_DEBUG, "OLBC not detected anymore");
 +                      iface->olbc = 0;
 +                      set_beacon++;
 +              }
 +#ifdef CONFIG_IEEE80211N
 +              if (!olbc_ht && iface->olbc_ht) {
 +                      wpa_printf(MSG_DEBUG, "OLBC HT not detected anymore");
 +                      iface->olbc_ht = 0;
 +                      hostapd_ht_operation_update(iface);
 +                      set_beacon++;
 +              }
 +#endif /* CONFIG_IEEE80211N */
 +      }
 +
 +      if (set_beacon)
 +              ieee802_11_update_beacons(iface);
 +}
 +
 +
 +int ap_list_init(struct hostapd_iface *iface)
 +{
-       eloop_cancel_timeout(ap_list_timer, iface, NULL);
 +      return 0;
 +}
 +
 +
 +void ap_list_deinit(struct hostapd_iface *iface)
 +{
 +      hostapd_free_aps(iface);
 +}
index 93dc0eda88d33bd19f67c71284a46b1c94109ad6,0000000000000000000000000000000000000000..9e0353cfec95a3da8fdbb3d0506bf4b3f99b1dac
mode 100644,000000..100644
--- /dev/null
@@@ -1,53 -1,0 +1,58 @@@
 +/*
 + * hostapd / AP table
 + * Copyright (c) 2002-2003, Jouni Malinen <j@w1.fi>
 + * Copyright (c) 2003-2004, Instant802 Networks, Inc.
 + * Copyright (c) 2006, Devicescape Software, Inc.
 + *
 + * This software may be distributed under the terms of the BSD license.
 + * See README for more details.
 + */
 +
 +#ifndef AP_LIST_H
 +#define AP_LIST_H
 +
 +struct ap_info {
 +      /* Note: next/prev pointers are updated whenever a new beacon is
 +       * received because these are used to find the least recently used
 +       * entries. */
 +      struct ap_info *next; /* next entry in AP list */
 +      struct ap_info *prev; /* previous entry in AP list */
 +      struct ap_info *hnext; /* next entry in hash table list */
 +      u8 addr[6];
 +      u8 supported_rates[WLAN_SUPP_RATES_MAX];
 +      int erp; /* ERP Info or -1 if ERP info element not present */
 +
 +      int channel;
 +
 +      int ht_support;
 +
 +      struct os_reltime last_beacon;
 +};
 +
 +struct ieee802_11_elems;
 +struct hostapd_frame_info;
 +
 +void ap_list_process_beacon(struct hostapd_iface *iface,
 +                          const struct ieee80211_mgmt *mgmt,
 +                          struct ieee802_11_elems *elems,
 +                          struct hostapd_frame_info *fi);
 +#ifdef NEED_AP_MLME
 +int ap_list_init(struct hostapd_iface *iface);
 +void ap_list_deinit(struct hostapd_iface *iface);
++void ap_list_timer(struct hostapd_iface *iface);
 +#else /* NEED_AP_MLME */
 +static inline int ap_list_init(struct hostapd_iface *iface)
 +{
 +      return 0;
 +}
 +
 +static inline void ap_list_deinit(struct hostapd_iface *iface)
 +{
 +}
++
++static inline void ap_list_timer(struct hostapd_iface *iface)
++{
++}
 +#endif /* NEED_AP_MLME */
 +
 +#endif /* AP_LIST_H */
index bd1778e418651140157e4d39cd22694a00839f53,0000000000000000000000000000000000000000..934dcfc8d6318c4496296d009ad03bb82b5e827f
mode 100644,000000..100644
--- /dev/null
@@@ -1,226 -1,0 +1,236 @@@
-               return -1;
 +/*
 + * Authentication server setup
 + * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
 + *
 + * This software may be distributed under the terms of the BSD license.
 + * See README for more details.
 + */
 +
 +#include "utils/includes.h"
 +
 +#include "utils/common.h"
 +#include "crypto/tls.h"
 +#include "eap_server/eap.h"
 +#include "eap_server/eap_sim_db.h"
 +#include "eapol_auth/eapol_auth_sm.h"
 +#include "radius/radius_server.h"
 +#include "hostapd.h"
 +#include "ap_config.h"
 +#include "sta_info.h"
 +#include "authsrv.h"
 +
 +
 +#if defined(EAP_SERVER_SIM) || defined(EAP_SERVER_AKA)
 +#define EAP_SIM_DB
 +#endif /* EAP_SERVER_SIM || EAP_SERVER_AKA */
 +
 +
 +#ifdef EAP_SIM_DB
 +static int hostapd_sim_db_cb_sta(struct hostapd_data *hapd,
 +                               struct sta_info *sta, void *ctx)
 +{
 +      if (eapol_auth_eap_pending_cb(sta->eapol_sm, ctx) == 0)
 +              return 1;
 +      return 0;
 +}
 +
 +
 +static void hostapd_sim_db_cb(void *ctx, void *session_ctx)
 +{
 +      struct hostapd_data *hapd = ctx;
 +      if (ap_for_each_sta(hapd, hostapd_sim_db_cb_sta, session_ctx) == 0) {
 +#ifdef RADIUS_SERVER
 +              radius_server_eap_pending_cb(hapd->radius_srv, session_ctx);
 +#endif /* RADIUS_SERVER */
 +      }
 +}
 +#endif /* EAP_SIM_DB */
 +
 +
 +#ifdef RADIUS_SERVER
 +
 +static int hostapd_radius_get_eap_user(void *ctx, const u8 *identity,
 +                                     size_t identity_len, int phase2,
 +                                     struct eap_user *user)
 +{
 +      const struct hostapd_eap_user *eap_user;
 +      int i;
++      int rv = -1;
 +
 +      eap_user = hostapd_get_eap_user(ctx, identity, identity_len, phase2);
 +      if (eap_user == NULL)
-                       return -1;
++              goto out;
 +
 +      if (user == NULL)
 +              return 0;
 +
 +      os_memset(user, 0, sizeof(*user));
 +      for (i = 0; i < EAP_MAX_METHODS; i++) {
 +              user->methods[i].vendor = eap_user->methods[i].vendor;
 +              user->methods[i].method = eap_user->methods[i].method;
 +      }
 +
 +      if (eap_user->password) {
 +              user->password = os_malloc(eap_user->password_len);
 +              if (user->password == NULL)
-       return 0;
++                      goto out;
 +              os_memcpy(user->password, eap_user->password,
 +                        eap_user->password_len);
 +              user->password_len = eap_user->password_len;
 +              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;
++      rv = 0;
 +
-               hapd->ssl_ctx = tls_init(NULL);
++out:
++      if (rv)
++              wpa_printf(MSG_DEBUG, "%s: Failed to find user", __func__);
++
++      return rv;
 +}
 +
 +
 +static int hostapd_setup_radius_srv(struct hostapd_data *hapd)
 +{
 +      struct radius_server_conf srv;
 +      struct hostapd_bss_config *conf = hapd->conf;
 +      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;
 +      srv.msg_ctx = hapd->msg_ctx;
 +      srv.pac_opaque_encr_key = conf->pac_opaque_encr_key;
 +      srv.eap_fast_a_id = conf->eap_fast_a_id;
 +      srv.eap_fast_a_id_len = conf->eap_fast_a_id_len;
 +      srv.eap_fast_a_id_info = conf->eap_fast_a_id_info;
 +      srv.eap_fast_prov = conf->eap_fast_prov;
 +      srv.pac_key_lifetime = conf->pac_key_lifetime;
 +      srv.pac_key_refresh_time = conf->pac_key_refresh_time;
 +      srv.eap_sim_aka_result_ind = conf->eap_sim_aka_result_ind;
 +      srv.tnc = conf->tnc;
 +      srv.wps = hapd->wps;
 +      srv.ipv6 = conf->radius_server_ipv6;
 +      srv.get_eap_user = hostapd_radius_get_eap_user;
 +      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;
++      srv.tls_session_lifetime = conf->tls_session_lifetime;
 +
 +      hapd->radius_srv = radius_server_init(&srv);
 +      if (hapd->radius_srv == NULL) {
 +              wpa_printf(MSG_ERROR, "RADIUS server initialization failed.");
 +              return -1;
 +      }
 +
 +      return 0;
 +}
 +
 +#endif /* RADIUS_SERVER */
 +
 +
 +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->private_key || hapd->conf->dh_file)) {
++              struct tls_config conf;
 +              struct tls_connection_params params;
 +
++              os_memset(&conf, 0, sizeof(conf));
++              conf.tls_session_lifetime = hapd->conf->tls_session_lifetime;
++              hapd->ssl_ctx = tls_init(&conf);
 +              if (hapd->ssl_ctx == NULL) {
 +                      wpa_printf(MSG_ERROR, "Failed to initialize TLS");
 +                      authsrv_deinit(hapd);
 +                      return -1;
 +              }
 +
 +              os_memset(&params, 0, sizeof(params));
 +              params.ca_cert = hapd->conf->ca_cert;
 +              params.client_cert = hapd->conf->server_cert;
 +              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;
 +
 +              if (tls_global_set_params(hapd->ssl_ctx, &params)) {
 +                      wpa_printf(MSG_ERROR, "Failed to set TLS parameters");
 +                      authsrv_deinit(hapd);
 +                      return -1;
 +              }
 +
 +              if (tls_global_set_verify(hapd->ssl_ctx,
 +                                        hapd->conf->check_crl)) {
 +                      wpa_printf(MSG_ERROR, "Failed to enable check_crl");
 +                      authsrv_deinit(hapd);
 +                      return -1;
 +              }
 +      }
 +#endif /* EAP_TLS_FUNCS */
 +
 +#ifdef EAP_SIM_DB
 +      if (hapd->conf->eap_sim_db) {
 +              hapd->eap_sim_db_priv =
 +                      eap_sim_db_init(hapd->conf->eap_sim_db,
 +                                      hostapd_sim_db_cb, hapd);
 +              if (hapd->eap_sim_db_priv == NULL) {
 +                      wpa_printf(MSG_ERROR, "Failed to initialize EAP-SIM "
 +                                 "database interface");
 +                      authsrv_deinit(hapd);
 +                      return -1;
 +              }
 +      }
 +#endif /* EAP_SIM_DB */
 +
 +#ifdef RADIUS_SERVER
 +      if (hapd->conf->radius_server_clients &&
 +          hostapd_setup_radius_srv(hapd))
 +              return -1;
 +#endif /* RADIUS_SERVER */
 +
 +      return 0;
 +}
 +
 +
 +void authsrv_deinit(struct hostapd_data *hapd)
 +{
 +#ifdef RADIUS_SERVER
 +      radius_server_deinit(hapd->radius_srv);
 +      hapd->radius_srv = NULL;
 +#endif /* RADIUS_SERVER */
 +
 +#ifdef EAP_TLS_FUNCS
 +      if (hapd->ssl_ctx) {
 +              tls_deinit(hapd->ssl_ctx);
 +              hapd->ssl_ctx = NULL;
 +      }
 +#endif /* EAP_TLS_FUNCS */
 +
 +#ifdef EAP_SIM_DB
 +      if (hapd->eap_sim_db_priv) {
 +              eap_sim_db_deinit(hapd->eap_sim_db_priv);
 +              hapd->eap_sim_db_priv = NULL;
 +      }
 +#endif /* EAP_SIM_DB */
 +}
index e575b65cbf3ace94e186f6be63ef3be8e8e788d9,0000000000000000000000000000000000000000..5fe8fd5660b418f8050550d840b35c2141b04d73
mode 100644,000000..100644
--- /dev/null
@@@ -1,1118 -1,0 +1,1253 @@@
-                                  struct sta_info *sta,
 +/*
 + * hostapd / IEEE 802.11 Management: Beacon and Probe Request/Response
 + * Copyright (c) 2002-2004, Instant802 Networks, Inc.
 + * Copyright (c) 2005-2006, Devicescape Software, Inc.
 + * Copyright (c) 2008-2012, Jouni Malinen <j@w1.fi>
 + *
 + * This software may be distributed under the terms of the BSD license.
 + * See README for more details.
 + */
 +
 +#include "utils/includes.h"
 +
 +#ifndef CONFIG_NATIVE_WINDOWS
 +
 +#include "utils/common.h"
 +#include "common/ieee802_11_defs.h"
 +#include "common/ieee802_11_common.h"
 +#include "common/hw_features_common.h"
 +#include "wps/wps_defs.h"
 +#include "p2p/p2p.h"
 +#include "hostapd.h"
 +#include "ieee802_11.h"
 +#include "wpa_auth.h"
 +#include "wmm.h"
 +#include "ap_config.h"
 +#include "sta_info.h"
 +#include "p2p_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;
 +
 +      if (hapd->iface->current_mode == NULL ||
 +          hapd->iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G)
 +              return 0;
 +
 +      if (hapd->iface->olbc)
 +              erp |= ERP_INFO_USE_PROTECTION;
 +      if (hapd->iface->num_sta_non_erp > 0) {
 +              erp |= ERP_INFO_NON_ERP_PRESENT |
 +                      ERP_INFO_USE_PROTECTION;
 +      }
 +      if (hapd->iface->num_sta_no_short_preamble > 0 ||
 +          hapd->iconf->preamble == LONG_PREAMBLE)
 +              erp |= ERP_INFO_BARKER_PREAMBLE_MODE;
 +
 +      return erp;
 +}
 +
 +
 +static u8 * hostapd_eid_ds_params(struct hostapd_data *hapd, u8 *eid)
 +{
 +      *eid++ = WLAN_EID_DS_PARAMS;
 +      *eid++ = 1;
 +      *eid++ = hapd->iconf->channel;
 +      return eid;
 +}
 +
 +
 +static u8 * hostapd_eid_erp_info(struct hostapd_data *hapd, u8 *eid)
 +{
 +      if (hapd->iface->current_mode == NULL ||
 +          hapd->iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G)
 +              return eid;
 +
 +      /* Set NonERP_present and use_protection bits if there
 +       * are any associated NonERP stations. */
 +      /* TODO: use_protection bit can be set to zero even if
 +       * there are NonERP stations present. This optimization
 +       * might be useful if NonERP stations are "quiet".
 +       * See 802.11g/D6 E-1 for recommended practice.
 +       * In addition, Non ERP present might be set, if AP detects Non ERP
 +       * operation on other APs. */
 +
 +      /* Add ERP Information element */
 +      *eid++ = WLAN_EID_ERP_INFO;
 +      *eid++ = 1;
 +      *eid++ = ieee802_11_erp_info(hapd);
 +
 +      return 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)
 +{
 +      if (end - pos < 3)
 +              return pos;
 +
 +      /* first channel number */
 +      *pos++ = start->chan;
 +      /* number of channels */
 +      *pos++ = (prev->chan - start->chan) / chan_spacing + 1;
 +      /* maximum transmit power level */
 +      *pos++ = start->max_tx_power;
 +
 +      return pos;
 +}
 +
 +
 +static u8 * hostapd_eid_country(struct hostapd_data *hapd, u8 *eid,
 +                              int max_len)
 +{
 +      u8 *pos = eid;
 +      u8 *end = eid + max_len;
 +      int i;
 +      struct hostapd_hw_modes *mode;
 +      struct hostapd_channel_data *start, *prev;
 +      int chan_spacing = 1;
 +
 +      if (!hapd->iconf->ieee80211d || max_len < 6 ||
 +          hapd->iface->current_mode == NULL)
 +              return eid;
 +
 +      *pos++ = WLAN_EID_COUNTRY;
 +      pos++; /* length will be set later */
 +      os_memcpy(pos, hapd->iconf->country, 3); /* e.g., 'US ' */
 +      pos += 3;
 +
 +      mode = hapd->iface->current_mode;
 +      if (mode->mode == HOSTAPD_MODE_IEEE80211A)
 +              chan_spacing = 4;
 +
 +      start = prev = NULL;
 +      for (i = 0; i < mode->num_channels; i++) {
 +              struct hostapd_channel_data *chan = &mode->channels[i];
 +              if (chan->flag & HOSTAPD_CHAN_DISABLED)
 +                      continue;
 +              if (start && prev &&
 +                  prev->chan + chan_spacing == chan->chan &&
 +                  start->max_tx_power == chan->max_tx_power) {
 +                      prev = chan;
 +                      continue; /* can use same entry */
 +              }
 +
 +              if (start && prev) {
 +                      pos = hostapd_eid_country_add(pos, end, chan_spacing,
 +                                                    start, prev);
 +                      start = NULL;
 +              }
 +
 +              /* Start new group */
 +              start = prev = chan;
 +      }
 +
 +      if (start) {
 +              pos = hostapd_eid_country_add(pos, end, chan_spacing,
 +                                            start, prev);
 +      }
 +
 +      if ((pos - eid) & 1) {
 +              if (end - pos < 1)
 +                      return eid;
 +              *pos++ = 0; /* pad for 16-bit alignment */
 +      }
 +
 +      eid[1] = (pos - eid) - 2;
 +
 +      return pos;
 +}
 +
 +
 +static u8 * hostapd_eid_wpa(struct hostapd_data *hapd, u8 *eid, size_t len)
 +{
 +      const u8 *ie;
 +      size_t ielen;
 +
 +      ie = wpa_auth_get_wpa_ie(hapd->wpa_auth, &ielen);
 +      if (ie == NULL || ielen > len)
 +              return eid;
 +
 +      os_memcpy(eid, ie, ielen);
 +      return eid + ielen;
 +}
 +
 +
 +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,
-               host_to_le16(hostapd_own_capab_info(hapd, sta, 1));
 +                                 const struct ieee80211_mgmt *req,
 +                                 int is_p2p, size_t *resp_len)
 +{
 +      struct ieee80211_mgmt *resp;
 +      u8 *pos, *epos;
 +      size_t buflen;
 +
 +#define MAX_PROBERESP_LEN 768
 +      buflen = MAX_PROBERESP_LEN;
 +#ifdef CONFIG_WPS
 +      if (hapd->wps_probe_resp_ie)
 +              buflen += wpabuf_len(hapd->wps_probe_resp_ie);
 +#endif /* CONFIG_WPS */
 +#ifdef CONFIG_P2P
 +      if (hapd->p2p_probe_resp_ie)
 +              buflen += wpabuf_len(hapd->p2p_probe_resp_ie);
 +#endif /* CONFIG_P2P */
++#ifdef CONFIG_FST
++      if (hapd->iface->fst_ies)
++              buflen += wpabuf_len(hapd->iface->fst_ies);
++#endif /* CONFIG_FST */
 +      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;
 +
 +      epos = ((u8 *) resp) + MAX_PROBERESP_LEN;
 +
 +      resp->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
 +                                         WLAN_FC_STYPE_PROBE_RESP);
 +      if (req)
 +              os_memcpy(resp->da, req->sa, ETH_ALEN);
 +      os_memcpy(resp->sa, hapd->own_addr, ETH_ALEN);
 +
 +      os_memcpy(resp->bssid, hapd->own_addr, ETH_ALEN);
 +      resp->u.probe_resp.beacon_int =
 +              host_to_le16(hapd->iconf->beacon_int);
 +
 +      /* hardware or low-level driver will setup seq_ctrl and timestamp */
 +      resp->u.probe_resp.capab_info =
-       struct sta_info *sta = NULL;
++              host_to_le16(hostapd_own_capab_info(hapd));
 +
 +      pos = resp->u.probe_resp.variable;
 +      *pos++ = WLAN_EID_SSID;
 +      *pos++ = hapd->conf->ssid.ssid_len;
 +      os_memcpy(pos, hapd->conf->ssid.ssid, hapd->conf->ssid.ssid_len);
 +      pos += hapd->conf->ssid.ssid_len;
 +
 +      /* Supported rates */
 +      pos = hostapd_eid_supp_rates(hapd, pos);
 +
 +      /* DS Params */
 +      pos = hostapd_eid_ds_params(hapd, pos);
 +
 +      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);
 +
 +      /* Extended supported rates */
 +      pos = hostapd_eid_ext_supp_rates(hapd, pos);
 +
 +      /* 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);
 +#endif /* CONFIG_IEEE80211N */
 +
 +      pos = hostapd_eid_ext_capab(hapd, pos);
 +
 +      pos = hostapd_eid_time_adv(hapd, pos);
 +      pos = hostapd_eid_time_zone(hapd, pos);
 +
 +      pos = hostapd_eid_interworking(hapd, pos);
 +      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_FST
++      if (hapd->iface->fst_ies) {
++              os_memcpy(pos, wpabuf_head(hapd->iface->fst_ies),
++                        wpabuf_len(hapd->iface->fst_ies));
++              pos += wpabuf_len(hapd->iface->fst_ies);
++      }
++#endif /* CONFIG_FST */
++
 +#ifdef CONFIG_IEEE80211AC
 +      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 */
 +      pos = hostapd_eid_wmm(hapd, pos);
 +
 +#ifdef CONFIG_WPS
 +      if (hapd->conf->wps_state && hapd->wps_probe_resp_ie) {
 +              os_memcpy(pos, wpabuf_head(hapd->wps_probe_resp_ie),
 +                        wpabuf_len(hapd->wps_probe_resp_ie));
 +              pos += wpabuf_len(hapd->wps_probe_resp_ie);
 +      }
 +#endif /* CONFIG_WPS */
 +
 +#ifdef CONFIG_P2P
 +      if ((hapd->conf->p2p & P2P_ENABLED) && is_p2p &&
 +          hapd->p2p_probe_resp_ie) {
 +              os_memcpy(pos, wpabuf_head(hapd->p2p_probe_resp_ie),
 +                        wpabuf_len(hapd->p2p_probe_resp_ie));
 +              pos += wpabuf_len(hapd->p2p_probe_resp_ie);
 +      }
 +#endif /* CONFIG_P2P */
 +#ifdef CONFIG_P2P_MANAGER
 +      if ((hapd->conf->p2p & (P2P_MANAGE | P2P_ENABLED | P2P_GROUP_OWNER)) ==
 +          P2P_MANAGE)
 +              pos = hostapd_eid_p2p_manage(hapd, pos);
 +#endif /* CONFIG_P2P_MANAGER */
 +
 +#ifdef CONFIG_HS20
 +      pos = hostapd_eid_hs20_indication(hapd, pos);
 +      pos = hostapd_eid_osen(hapd, pos);
 +#endif /* CONFIG_HS20 */
 +
 +      if (hapd->conf->vendor_elements) {
 +              os_memcpy(pos, wpabuf_head(hapd->conf->vendor_elements),
 +                        wpabuf_len(hapd->conf->vendor_elements));
 +              pos += wpabuf_len(hapd->conf->vendor_elements);
 +      }
 +
 +      *resp_len = pos - (u8 *) resp;
 +      return (u8 *) resp;
 +}
 +
 +
 +enum ssid_match_result {
 +      NO_SSID_MATCH,
 +      EXACT_SSID_MATCH,
 +      WILDCARD_SSID_MATCH
 +};
 +
 +static enum ssid_match_result ssid_match(struct hostapd_data *hapd,
 +                                       const u8 *ssid, size_t ssid_len,
 +                                       const u8 *ssid_list,
 +                                       size_t ssid_list_len)
 +{
 +      const u8 *pos, *end;
 +      int wildcard = 0;
 +
 +      if (ssid_len == 0)
 +              wildcard = 1;
 +      if (ssid_len == hapd->conf->ssid.ssid_len &&
 +          os_memcmp(ssid, hapd->conf->ssid.ssid, ssid_len) == 0)
 +              return EXACT_SSID_MATCH;
 +
 +      if (ssid_list == NULL)
 +              return wildcard ? WILDCARD_SSID_MATCH : NO_SSID_MATCH;
 +
 +      pos = ssid_list;
 +      end = ssid_list + ssid_list_len;
 +      while (pos + 1 <= end) {
 +              if (pos + 2 + pos[1] > end)
 +                      break;
 +              if (pos[1] == 0)
 +                      wildcard = 1;
 +              if (pos[1] == hapd->conf->ssid.ssid_len &&
 +                  os_memcmp(pos + 2, hapd->conf->ssid.ssid, pos[1]) == 0)
 +                      return EXACT_SSID_MATCH;
 +              pos += 2 + pos[1];
 +      }
 +
 +      return wildcard ? WILDCARD_SSID_MATCH : NO_SSID_MATCH;
 +}
 +
 +
++void sta_track_expire(struct hostapd_iface *iface, int force)
++{
++      struct os_reltime now;
++      struct hostapd_sta_info *info;
++
++      if (!iface->num_sta_seen)
++              return;
++
++      os_get_reltime(&now);
++      while ((info = dl_list_first(&iface->sta_seen, struct hostapd_sta_info,
++                                   list))) {
++              if (!force &&
++                  !os_reltime_expired(&now, &info->last_seen,
++                                      iface->conf->track_sta_max_age))
++                      break;
++              force = 0;
++
++              wpa_printf(MSG_MSGDUMP, "%s: Expire STA tracking entry for "
++                         MACSTR, iface->bss[0]->conf->iface,
++                         MAC2STR(info->addr));
++              dl_list_del(&info->list);
++              iface->num_sta_seen--;
++              os_free(info);
++      }
++}
++
++
++static struct hostapd_sta_info * sta_track_get(struct hostapd_iface *iface,
++                                             const u8 *addr)
++{
++      struct hostapd_sta_info *info;
++
++      dl_list_for_each(info, &iface->sta_seen, struct hostapd_sta_info, list)
++              if (os_memcmp(addr, info->addr, ETH_ALEN) == 0)
++                      return info;
++
++      return NULL;
++}
++
++
++void sta_track_add(struct hostapd_iface *iface, const u8 *addr)
++{
++      struct hostapd_sta_info *info;
++
++      info = sta_track_get(iface, addr);
++      if (info) {
++              /* Move the most recent entry to the end of the list */
++              dl_list_del(&info->list);
++              dl_list_add_tail(&iface->sta_seen, &info->list);
++              os_get_reltime(&info->last_seen);
++              return;
++      }
++
++      /* Add a new entry */
++      info = os_zalloc(sizeof(*info));
++      os_memcpy(info->addr, addr, ETH_ALEN);
++      os_get_reltime(&info->last_seen);
++
++      if (iface->num_sta_seen >= iface->conf->track_sta_max_num) {
++              /* Expire oldest entry to make room for a new one */
++              sta_track_expire(iface, 1);
++      }
++
++      wpa_printf(MSG_MSGDUMP, "%s: Add STA tracking entry for "
++                 MACSTR, iface->bss[0]->conf->iface, MAC2STR(addr));
++      dl_list_add_tail(&iface->sta_seen, &info->list);
++      iface->num_sta_seen++;
++}
++
++
++struct hostapd_data *
++sta_track_seen_on(struct hostapd_iface *iface, const u8 *addr,
++                const char *ifname)
++{
++      struct hapd_interfaces *interfaces = iface->interfaces;
++      size_t i, j;
++
++      for (i = 0; i < interfaces->count; i++) {
++              struct hostapd_data *hapd = NULL;
++
++              iface = interfaces->iface[i];
++              for (j = 0; j < iface->num_bss; j++) {
++                      hapd = iface->bss[j];
++                      if (os_strcmp(ifname, hapd->conf->iface) == 0)
++                              break;
++                      hapd = NULL;
++              }
++
++              if (hapd && sta_track_get(iface, addr))
++                      return hapd;
++      }
++
++      return NULL;
++}
++
++
 +void handle_probe_req(struct hostapd_data *hapd,
 +                    const struct ieee80211_mgmt *mgmt, size_t len,
 +                    int ssi_signal)
 +{
 +      u8 *resp;
 +      struct ieee802_11_elems elems;
 +      const u8 *ie;
 +      size_t ie_len;
-       if (elems.ds_params && elems.ds_params_len == 1 &&
 +      size_t i, resp_len;
 +      int noack;
 +      enum ssid_match_result res;
 +
 +      ie = mgmt->u.probe_req.variable;
 +      if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req))
 +              return;
++      if (hapd->iconf->track_sta_max_num)
++              sta_track_add(hapd->iface, mgmt->sa);
 +      ie_len = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req));
 +
 +      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;
 +
 +      if (!hapd->iconf->send_probe_response)
 +              return;
 +
 +      if (ieee802_11_parse_elems(ie, ie_len, &elems, 0) == ParseFailed) {
 +              wpa_printf(MSG_DEBUG, "Could not parse ProbeReq from " MACSTR,
 +                         MAC2STR(mgmt->sa));
 +              return;
 +      }
 +
 +      if ((!elems.ssid || !elems.supp_rates)) {
 +              wpa_printf(MSG_DEBUG, "STA " MACSTR " sent probe request "
 +                         "without SSID or supported rates element",
 +                         MAC2STR(mgmt->sa));
 +              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).
 +       */
-       sta = ap_get_sta(hapd, mgmt->sa);
++      if (elems.ds_params &&
 +          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;
 +              wps = ieee802_11_vendor_ie_concat(ie, ie_len, WPS_DEV_OUI_WFA);
 +              if (wps && !p2p_group_match_dev_type(hapd->p2p_group, wps)) {
 +                      wpa_printf(MSG_MSGDUMP, "P2P: Ignore Probe Request "
 +                                 "due to mismatch with Requested Device "
 +                                 "Type");
 +                      wpabuf_free(wps);
 +                      return;
 +              }
 +              wpabuf_free(wps);
 +      }
 +
 +      if (hapd->p2p && elems.p2p) {
 +              struct wpabuf *p2p;
 +              p2p = ieee802_11_vendor_ie_concat(ie, ie_len, P2P_IE_VENDOR_TYPE);
 +              if (p2p && !p2p_group_match_dev_id(hapd->p2p_group, p2p)) {
 +                      wpa_printf(MSG_MSGDUMP, "P2P: Ignore Probe Request "
 +                                 "due to mismatch with Device ID");
 +                      wpabuf_free(p2p);
 +                      return;
 +              }
 +              wpabuf_free(p2p);
 +      }
 +#endif /* CONFIG_P2P */
 +
 +      if (hapd->conf->ignore_broadcast_ssid && elems.ssid_len == 0 &&
 +          elems.ssid_list_len == 0) {
 +              wpa_printf(MSG_MSGDUMP, "Probe Request from " MACSTR " for "
 +                         "broadcast SSID ignored", MAC2STR(mgmt->sa));
 +              return;
 +      }
 +
-       if (res != NO_SSID_MATCH) {
-               if (sta)
-                       sta->ssid_probe = &hapd->conf->ssid;
-       } else {
 +#ifdef CONFIG_P2P
 +      if ((hapd->conf->p2p & P2P_GROUP_OWNER) &&
 +          elems.ssid_len == P2P_WILDCARD_SSID_LEN &&
 +          os_memcmp(elems.ssid, P2P_WILDCARD_SSID,
 +                    P2P_WILDCARD_SSID_LEN) == 0) {
 +              /* Process P2P Wildcard SSID like Wildcard SSID */
 +              elems.ssid_len = 0;
 +      }
 +#endif /* CONFIG_P2P */
 +
 +      res = ssid_match(hapd, elems.ssid, elems.ssid_len,
 +                       elems.ssid_list, elems.ssid_list_len);
-       resp = hostapd_gen_probe_resp(hapd, sta, mgmt, elems.p2p != NULL,
++      if (res == NO_SSID_MATCH) {
 +              if (!(mgmt->da[0] & 0x01)) {
 +                      wpa_printf(MSG_MSGDUMP, "Probe Request from " MACSTR
 +                                 " for foreign SSID '%s' (DA " MACSTR ")%s",
 +                                 MAC2STR(mgmt->sa),
 +                                 wpa_ssid_txt(elems.ssid, elems.ssid_len),
 +                                 MAC2STR(mgmt->da),
 +                                 elems.ssid_list ? " (SSID list)" : "");
 +              }
 +              return;
 +      }
 +
 +#ifdef CONFIG_INTERWORKING
 +      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) {
 +                      wpa_printf(MSG_MSGDUMP, "Probe Request from " MACSTR
 +                                 " for mismatching ANT %u ignored",
 +                                 MAC2STR(mgmt->sa), ant);
 +                      return;
 +              }
 +      }
 +
 +      if (hapd->conf->interworking && elems.interworking &&
 +          (elems.interworking_len == 7 || elems.interworking_len == 9)) {
 +              const u8 *hessid;
 +              if (elems.interworking_len == 7)
 +                      hessid = elems.interworking + 1;
 +              else
 +                      hessid = elems.interworking + 1 + 2;
 +              if (!is_broadcast_ether_addr(hessid) &&
 +                  os_memcmp(hessid, hapd->conf->hessid, ETH_ALEN) != 0) {
 +                      wpa_printf(MSG_MSGDUMP, "Probe Request from " MACSTR
 +                                 " for mismatching HESSID " MACSTR
 +                                 " ignored",
 +                                 MAC2STR(mgmt->sa), MAC2STR(hessid));
 +                      return;
 +              }
 +      }
 +#endif /* CONFIG_INTERWORKING */
 +
 +#ifdef CONFIG_P2P
 +      if ((hapd->conf->p2p & P2P_GROUP_OWNER) &&
 +          supp_rates_11b_only(&elems)) {
 +              /* Indicates support for 11b rates only */
 +              wpa_printf(MSG_EXCESSIVE, "P2P: Ignore Probe Request from "
 +                         MACSTR " with only 802.11b rates",
 +                         MAC2STR(mgmt->sa));
 +              return;
 +      }
 +#endif /* CONFIG_P2P */
 +
 +      /* TODO: verify that supp_rates contains at least one matching rate
 +       * with AP configuration */
 +
++      if (hapd->conf->no_probe_resp_if_seen_on &&
++          is_multicast_ether_addr(mgmt->da) &&
++          is_multicast_ether_addr(mgmt->bssid) &&
++          sta_track_seen_on(hapd->iface, mgmt->sa,
++                            hapd->conf->no_probe_resp_if_seen_on)) {
++              wpa_printf(MSG_MSGDUMP, "%s: Ignore Probe Request from " MACSTR
++                         " since STA has been seen on %s",
++                         hapd->conf->iface, MAC2STR(mgmt->sa),
++                         hapd->conf->no_probe_resp_if_seen_on);
++              return;
++      }
++
 +#ifdef CONFIG_TESTING_OPTIONS
 +      if (hapd->iconf->ignore_probe_probability > 0.0 &&
 +          drand48() < hapd->iconf->ignore_probe_probability) {
 +              wpa_printf(MSG_INFO,
 +                         "TESTING: ignoring probe request from " MACSTR,
 +                         MAC2STR(mgmt->sa));
 +              return;
 +      }
 +#endif /* CONFIG_TESTING_OPTIONS */
 +
-       return hostapd_gen_probe_resp(hapd, NULL, NULL, 0, resp_len);
++      resp = hostapd_gen_probe_resp(hapd, mgmt, elems.p2p != NULL,
 +                                    &resp_len);
 +      if (resp == NULL)
 +              return;
 +
 +      /*
 +       * If this is a broadcast probe request, apply no ack policy to avoid
 +       * excessive retries.
 +       */
 +      noack = !!(res == WILDCARD_SSID_MATCH &&
 +                 is_broadcast_ether_addr(mgmt->da));
 +
 +      if (hostapd_drv_send_mlme(hapd, resp, resp_len, noack) < 0)
 +              wpa_printf(MSG_INFO, "handle_probe_req: send failed");
 +
 +      os_free(resp);
 +
 +      wpa_printf(MSG_EXCESSIVE, "STA " MACSTR " sent probe request for %s "
 +                 "SSID", MAC2STR(mgmt->sa),
 +                 elems.ssid_len == 0 ? "broadcast" : "our");
 +}
 +
 +
 +static u8 * hostapd_probe_resp_offloads(struct hostapd_data *hapd,
 +                                      size_t *resp_len)
 +{
 +      /* check probe response offloading caps and print warnings */
 +      if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_PROBE_RESP_OFFLOAD))
 +              return NULL;
 +
 +#ifdef CONFIG_WPS
 +      if (hapd->conf->wps_state && hapd->wps_probe_resp_ie &&
 +          (!(hapd->iface->probe_resp_offloads &
 +             (WPA_DRIVER_PROBE_RESP_OFFLOAD_WPS |
 +              WPA_DRIVER_PROBE_RESP_OFFLOAD_WPS2))))
 +              wpa_printf(MSG_WARNING, "Device is trying to offload WPS "
 +                         "Probe Response while not supporting this");
 +#endif /* CONFIG_WPS */
 +
 +#ifdef CONFIG_P2P
 +      if ((hapd->conf->p2p & P2P_ENABLED) && hapd->p2p_probe_resp_ie &&
 +          !(hapd->iface->probe_resp_offloads &
 +            WPA_DRIVER_PROBE_RESP_OFFLOAD_P2P))
 +              wpa_printf(MSG_WARNING, "Device is trying to offload P2P "
 +                         "Probe Response while not supporting this");
 +#endif  /* CONFIG_P2P */
 +
 +      if (hapd->conf->interworking &&
 +          !(hapd->iface->probe_resp_offloads &
 +            WPA_DRIVER_PROBE_RESP_OFFLOAD_INTERWORKING))
 +              wpa_printf(MSG_WARNING, "Device is trying to offload "
 +                         "Interworking Probe Response while not supporting "
 +                         "this");
 +
 +      /* Generate a Probe Response template for the non-P2P case */
-       capab_info = hostapd_own_capab_info(hapd, NULL, 0);
++      return hostapd_gen_probe_resp(hapd, NULL, 0, resp_len);
 +}
 +
 +#endif /* NEED_AP_MLME */
 +
 +
 +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;
 +#ifdef NEED_AP_MLME
 +      u16 capab_info;
 +      u8 *pos, *tailpos;
 +
 +#define BEACON_HEAD_BUF_SIZE 256
 +#define BEACON_TAIL_BUF_SIZE 512
 +      head = os_zalloc(BEACON_HEAD_BUF_SIZE);
 +      tail_len = BEACON_TAIL_BUF_SIZE;
 +#ifdef CONFIG_WPS
 +      if (hapd->conf->wps_state && hapd->wps_beacon_ie)
 +              tail_len += wpabuf_len(hapd->wps_beacon_ie);
 +#endif /* CONFIG_WPS */
 +#ifdef CONFIG_P2P
 +      if (hapd->p2p_beacon_ie)
 +              tail_len += wpabuf_len(hapd->p2p_beacon_ie);
 +#endif /* CONFIG_P2P */
++#ifdef CONFIG_FST
++      if (hapd->iface->fst_ies)
++              tail_len += wpabuf_len(hapd->iface->fst_ies);
++#endif /* CONFIG_FST */
 +      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 -1;
 +      }
 +
 +      head->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
 +                                         WLAN_FC_STYPE_BEACON);
 +      head->duration = host_to_le16(0);
 +      os_memset(head->da, 0xff, ETH_ALEN);
 +
 +      os_memcpy(head->sa, hapd->own_addr, ETH_ALEN);
 +      os_memcpy(head->bssid, hapd->own_addr, ETH_ALEN);
 +      head->u.beacon.beacon_int =
 +              host_to_le16(hapd->iconf->beacon_int);
 +
 +      /* hardware or low-level driver will setup seq_ctrl and timestamp */
-       params->pairwise_ciphers = hapd->conf->wpa_pairwise |
-               hapd->conf->rsn_pairwise;
++      capab_info = hostapd_own_capab_info(hapd);
 +      head->u.beacon.capab_info = host_to_le16(capab_info);
 +      pos = &head->u.beacon.variable[0];
 +
 +      /* SSID */
 +      *pos++ = WLAN_EID_SSID;
 +      if (hapd->conf->ignore_broadcast_ssid == 2) {
 +              /* clear the data, but keep the correct length of the SSID */
 +              *pos++ = hapd->conf->ssid.ssid_len;
 +              os_memset(pos, 0, hapd->conf->ssid.ssid_len);
 +              pos += hapd->conf->ssid.ssid_len;
 +      } else if (hapd->conf->ignore_broadcast_ssid) {
 +              *pos++ = 0; /* empty SSID */
 +      } else {
 +              *pos++ = hapd->conf->ssid.ssid_len;
 +              os_memcpy(pos, hapd->conf->ssid.ssid,
 +                        hapd->conf->ssid.ssid_len);
 +              pos += hapd->conf->ssid.ssid_len;
 +      }
 +
 +      /* Supported rates */
 +      pos = hostapd_eid_supp_rates(hapd, pos);
 +
 +      /* DS Params */
 +      pos = hostapd_eid_ds_params(hapd, pos);
 +
 +      head_len = pos - (u8 *) head;
 +
 +      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);
 +
 +      /* Extended supported rates */
 +      tailpos = hostapd_eid_ext_supp_rates(hapd, tailpos);
 +
 +      /* RSN, MDIE, WPA */
 +      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);
 +#endif /* CONFIG_IEEE80211N */
 +
 +      tailpos = hostapd_eid_ext_capab(hapd, tailpos);
 +
 +      /*
 +       * TODO: Time Advertisement element should only be included in some
 +       * DTIM Beacon frames.
 +       */
 +      tailpos = hostapd_eid_time_adv(hapd, tailpos);
 +
 +      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_FST
++      if (hapd->iface->fst_ies) {
++              os_memcpy(tailpos, wpabuf_head(hapd->iface->fst_ies),
++                        wpabuf_len(hapd->iface->fst_ies));
++              tailpos += wpabuf_len(hapd->iface->fst_ies);
++      }
++#endif /* CONFIG_FST */
++
 +#ifdef CONFIG_IEEE80211AC
 +      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 */
 +      tailpos = hostapd_eid_wmm(hapd, tailpos);
 +
 +#ifdef CONFIG_WPS
 +      if (hapd->conf->wps_state && hapd->wps_beacon_ie) {
 +              os_memcpy(tailpos, wpabuf_head(hapd->wps_beacon_ie),
 +                        wpabuf_len(hapd->wps_beacon_ie));
 +              tailpos += wpabuf_len(hapd->wps_beacon_ie);
 +      }
 +#endif /* CONFIG_WPS */
 +
 +#ifdef CONFIG_P2P
 +      if ((hapd->conf->p2p & P2P_ENABLED) && hapd->p2p_beacon_ie) {
 +              os_memcpy(tailpos, wpabuf_head(hapd->p2p_beacon_ie),
 +                        wpabuf_len(hapd->p2p_beacon_ie));
 +              tailpos += wpabuf_len(hapd->p2p_beacon_ie);
 +      }
 +#endif /* CONFIG_P2P */
 +#ifdef CONFIG_P2P_MANAGER
 +      if ((hapd->conf->p2p & (P2P_MANAGE | P2P_ENABLED | P2P_GROUP_OWNER)) ==
 +          P2P_MANAGE)
 +              tailpos = hostapd_eid_p2p_manage(hapd, tailpos);
 +#endif /* CONFIG_P2P_MANAGER */
 +
 +#ifdef CONFIG_HS20
 +      tailpos = hostapd_eid_hs20_indication(hapd, tailpos);
 +      tailpos = hostapd_eid_osen(hapd, tailpos);
 +#endif /* CONFIG_HS20 */
 +
 +      if (hapd->conf->vendor_elements) {
 +              os_memcpy(tailpos, wpabuf_head(hapd->conf->vendor_elements),
 +                        wpabuf_len(hapd->conf->vendor_elements));
 +              tailpos += wpabuf_len(hapd->conf->vendor_elements);
 +      }
 +
 +      tail_len = tailpos > tail ? tailpos - tail : 0;
 +
 +      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;
++      if ((hapd->conf->wpa & (WPA_PROTO_WPA | WPA_PROTO_RSN)) ==
++          (WPA_PROTO_WPA | WPA_PROTO_RSN))
++              params->pairwise_ciphers = hapd->conf->wpa_pairwise |
++                      hapd->conf->rsn_pairwise;
++      else if (hapd->conf->wpa & WPA_PROTO_RSN)
++              params->pairwise_ciphers = hapd->conf->rsn_pairwise;
++      else if (hapd->conf->wpa & WPA_PROTO_WPA)
++              params->pairwise_ciphers = hapd->conf->wpa_pairwise;
 +      params->group_cipher = hapd->conf->wpa_group;
 +      params->key_mgmt_suites = hapd->conf->wpa_key_mgmt;
 +      params->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;
 +              break;
 +      case 1:
 +              params->hide_ssid = HIDDEN_SSID_ZERO_LEN;
 +              break;
 +      case 2:
 +              params->hide_ssid = HIDDEN_SSID_ZERO_CONTENTS;
 +              break;
 +      }
 +      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) &
 +                              ERP_INFO_USE_PROTECTION);
 +      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 =
 +                      hapd->iface->num_sta_no_short_slot_time > 0 ? 0 : 1;
 +      else
 +              params->short_slot_time = -1;
 +      if (!hapd->iconf->ieee80211n || hapd->conf->disable_11n)
 +              params->ht_opmode = -1;
 +      else
 +              params->ht_opmode = hapd->iface->ht_op_mode;
 +#endif /* NEED_AP_MLME */
 +      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;
 +#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;
 +      if (hapd->conf->osen) {
 +              params->privacy = 1;
 +              params->osen = 1;
 +      }
 +#endif /* CONFIG_HS20 */
 +      return 0;
 +}
 +
 +
 +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;
 +}
 +
 +
 +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;
 +      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 */
 +int ieee802_11_update_beacons(struct hostapd_iface *iface)
 +{
 +      size_t 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 722159a7500c661699e1fb1439e9152a23bbcaa3,0000000000000000000000000000000000000000..d98f42e8157aef2a01f53d16f4ab7d525e217b7c
mode 100644,000000..100644
--- /dev/null
@@@ -1,25 -1,0 +1,30 @@@
 +/*
 + * hostapd / IEEE 802.11 Management: Beacon and Probe Request/Response
 + * Copyright (c) 2002-2004, Instant802 Networks, Inc.
 + * Copyright (c) 2005-2006, Devicescape Software, Inc.
 + *
 + * This software may be distributed under the terms of the BSD license.
 + * See README for more details.
 + */
 +
 +#ifndef BEACON_H
 +#define BEACON_H
 +
 +struct ieee80211_mgmt;
 +
 +void handle_probe_req(struct hostapd_data *hapd,
 +                    const struct ieee80211_mgmt *mgmt, size_t len,
 +                    int ssi_signal);
 +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);
++void sta_track_add(struct hostapd_iface *iface, const u8 *addr);
++void sta_track_expire(struct hostapd_iface *iface, int force);
++struct hostapd_data *
++sta_track_seen_on(struct hostapd_iface *iface, const u8 *addr,
++                const char *ifname);
 +
 +#endif /* BEACON_H */
index 41ab988277bbda83eaae26b84147bd3ec54989ef,0000000000000000000000000000000000000000..c98978f33d05e794259c94c1248a6823cf007c7f
mode 100644,000000..100644
--- /dev/null
@@@ -1,545 -1,0 +1,556 @@@
-       return hostapd_ctrl_iface_sta_mib(hapd, sta, buf, buflen);
 +/*
 + * Control interface for shared AP commands
 + * 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 "utils/includes.h"
 +
 +#include "utils/common.h"
 +#include "common/ieee802_11_defs.h"
 +#include "common/sae.h"
 +#include "eapol_auth/eapol_auth_sm.h"
++#include "fst/fst_ctrl_iface.h"
 +#include "hostapd.h"
 +#include "ieee802_1x.h"
 +#include "wpa_auth.h"
 +#include "ieee802_11.h"
 +#include "sta_info.h"
 +#include "wps_hostapd.h"
 +#include "p2p_hostapd.h"
 +#include "ctrl_iface_ap.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_reltime age;
 +      int ret;
 +
 +      if (!sta->connected_time.sec)
 +              return 0;
 +
 +      os_reltime_age(&sta->connected_time, &age);
 +
 +      ret = os_snprintf(buf, buflen, "connected_time=%u\n",
 +                        (unsigned int) age.sec);
 +      if (os_snprintf_error(buflen, ret))
 +              return 0;
 +      return ret;
 +}
 +
 +
 +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 "?";
 +}
 +
 +
 +static int hostapd_ctrl_iface_sta_mib(struct hostapd_data *hapd,
 +                                    struct sta_info *sta,
 +                                    char *buf, size_t buflen)
 +{
 +      int len, res, ret, i;
 +
 +      if (!sta)
 +              return 0;
 +
 +      len = 0;
 +      ret = os_snprintf(buf + len, buflen - len, MACSTR "\nflags=",
 +                        MAC2STR(sta->addr));
 +      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;
 +
 +      res = ieee802_11_get_mib_sta(hapd, sta, buf + len, buflen - len);
 +      if (res >= 0)
 +              len += res;
 +      res = wpa_get_mib_sta(sta->wpa_sm, buf + len, buflen - len);
 +      if (res >= 0)
 +              len += res;
 +      res = ieee802_1x_get_mib_sta(hapd, sta, buf + len, buflen - len);
 +      if (res >= 0)
 +              len += res;
 +      res = hostapd_wps_get_mib_sta(hapd, sta->addr, buf + len,
 +                                    buflen - len);
 +      if (res >= 0)
 +              len += res;
 +      res = hostapd_p2p_get_mib_sta(hapd, 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 */
 +
++      if (sta->vlan_id > 0) {
++              res = os_snprintf(buf + len, buflen - len, "vlan_id=%d\n",
++                                sta->vlan_id);
++              if (!os_snprintf_error(buflen - len, res))
++                      len += res;
++      }
++
 +      return len;
 +}
 +
 +
 +int hostapd_ctrl_iface_sta_first(struct hostapd_data *hapd,
 +                               char *buf, size_t buflen)
 +{
 +      return hostapd_ctrl_iface_sta_mib(hapd, hapd->sta_list, buf, buflen);
 +}
 +
 +
 +int hostapd_ctrl_iface_sta(struct hostapd_data *hapd, const char *txtaddr,
 +                         char *buf, size_t buflen)
 +{
 +      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 (os_snprintf_error(buflen, ret))
 +                      return 0;
 +              return ret;
 +      }
 +
 +      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;
 +      }
 +
++      ret = hostapd_ctrl_iface_sta_mib(hapd, sta, buf, buflen);
++      ret += fst_ctrl_iface_mb_info(addr, buf + ret, buflen - ret);
++
++      return ret;
 +}
 +
 +
 +int hostapd_ctrl_iface_sta_next(struct hostapd_data *hapd, const char *txtaddr,
 +                              char *buf, size_t buflen)
 +{
 +      u8 addr[ETH_ALEN];
 +      struct sta_info *sta;
 +      int ret;
 +
 +      if (hwaddr_aton(txtaddr, addr) ||
 +          (sta = ap_get_sta(hapd, addr)) == NULL) {
 +              ret = os_snprintf(buf, buflen, "FAIL\n");
 +              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);
 +}
 +
 +
 +#ifdef CONFIG_P2P_MANAGER
 +static int p2p_manager_disconnect(struct hostapd_data *hapd, u16 stype,
 +                                u8 minor_reason_code, const u8 *addr)
 +{
 +      struct ieee80211_mgmt *mgmt;
 +      int ret;
 +      u8 *pos;
 +
 +      if (hapd->driver->send_frame == NULL)
 +              return -1;
 +
 +      mgmt = os_zalloc(sizeof(*mgmt) + 100);
 +      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 (%s))",
 +              MAC2STR(addr), minor_reason_code, stype,
 +              fc2str(mgmt->frame_control));
 +
 +      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);
 +      if (stype == WLAN_FC_STYPE_DEAUTH) {
 +              mgmt->u.deauth.reason_code =
 +                      host_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID);
 +              pos = (u8 *) (&mgmt->u.deauth.reason_code + 1);
 +      } else {
 +              mgmt->u.disassoc.reason_code =
 +                      host_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID);
 +              pos = (u8 *) (&mgmt->u.disassoc.reason_code + 1);
 +      }
 +
 +      *pos++ = WLAN_EID_VENDOR_SPECIFIC;
 +      *pos++ = 4 + 3 + 1;
 +      WPA_PUT_BE32(pos, P2P_IE_VENDOR_TYPE);
 +      pos += 4;
 +
 +      *pos++ = P2P_ATTR_MINOR_REASON_CODE;
 +      WPA_PUT_LE16(pos, 1);
 +      pos += 2;
 +      *pos++ = minor_reason_code;
 +
 +      ret = hapd->driver->send_frame(hapd->drv_priv, (u8 *) mgmt,
 +                                     pos - (u8 *) mgmt, 1);
 +      os_free(mgmt);
 +
 +      return ret < 0 ? -1 : 0;
 +}
 +#endif /* CONFIG_P2P_MANAGER */
 +
 +
 +int hostapd_ctrl_iface_deauthenticate(struct hostapd_data *hapd,
 +                                    const char *txtaddr)
 +{
 +      u8 addr[ETH_ALEN];
 +      struct sta_info *sta;
 +      const char *pos;
 +      u16 reason = WLAN_REASON_PREV_AUTH_NOT_VALID;
 +
 +      wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "CTRL_IFACE DEAUTHENTICATE %s",
 +              txtaddr);
 +
 +      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;
 +              int encrypt;
 +              if (hapd->driver->send_frame == NULL)
 +                      return -1;
 +              pos += 6;
 +              encrypt = atoi(pos);
 +              os_memset(&mgmt, 0, sizeof(mgmt));
 +              mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
 +                                                WLAN_FC_STYPE_DEAUTH);
 +              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(reason);
 +              if (hapd->driver->send_frame(hapd->drv_priv, (u8 *) &mgmt,
 +                                           IEEE80211_HDRLEN +
 +                                           sizeof(mgmt.u.deauth),
 +                                           encrypt) < 0)
 +                      return -1;
 +              return 0;
 +      }
 +
 +#ifdef CONFIG_P2P_MANAGER
 +      pos = os_strstr(txtaddr, " p2p=");
 +      if (pos) {
 +              return p2p_manager_disconnect(hapd, WLAN_FC_STYPE_DEAUTH,
 +                                            atoi(pos + 5), addr);
 +      }
 +#endif /* CONFIG_P2P_MANAGER */
 +
 +      hostapd_drv_sta_deauth(hapd, addr, reason);
 +      sta = ap_get_sta(hapd, addr);
 +      if (sta)
 +              ap_sta_deauthenticate(hapd, sta, reason);
 +      else if (addr[0] == 0xff)
 +              hostapd_free_stas(hapd);
 +
 +      return 0;
 +}
 +
 +
 +int hostapd_ctrl_iface_disassociate(struct hostapd_data *hapd,
 +                                  const char *txtaddr)
 +{
 +      u8 addr[ETH_ALEN];
 +      struct sta_info *sta;
 +      const char *pos;
 +      u16 reason = WLAN_REASON_PREV_AUTH_NOT_VALID;
 +
 +      wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "CTRL_IFACE DISASSOCIATE %s",
 +              txtaddr);
 +
 +      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;
 +              int encrypt;
 +              if (hapd->driver->send_frame == NULL)
 +                      return -1;
 +              pos += 6;
 +              encrypt = atoi(pos);
 +              os_memset(&mgmt, 0, sizeof(mgmt));
 +              mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
 +                                                WLAN_FC_STYPE_DISASSOC);
 +              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(reason);
 +              if (hapd->driver->send_frame(hapd->drv_priv, (u8 *) &mgmt,
 +                                           IEEE80211_HDRLEN +
 +                                           sizeof(mgmt.u.deauth),
 +                                           encrypt) < 0)
 +                      return -1;
 +              return 0;
 +      }
 +
 +#ifdef CONFIG_P2P_MANAGER
 +      pos = os_strstr(txtaddr, " p2p=");
 +      if (pos) {
 +              return p2p_manager_disconnect(hapd, WLAN_FC_STYPE_DISASSOC,
 +                                            atoi(pos + 5), addr);
 +      }
 +#endif /* CONFIG_P2P_MANAGER */
 +
 +      hostapd_drv_sta_disassoc(hapd, addr, reason);
 +      sta = ap_get_sta(hapd, addr);
 +      if (sta)
 +              ap_sta_disassociate(hapd, sta, reason);
 +      else if (addr[0] == 0xff)
 +              hostapd_free_stas(hapd);
 +
 +      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 da6fd4646710177f4812d0f7d126a1adae7c4dbe,0000000000000000000000000000000000000000..715f19b6ac7bd12726af876f94bf13d9a30593d3
mode 100644,000000..100644
--- /dev/null
@@@ -1,1064 -1,0 +1,1072 @@@
-       if (first_chan_idx + num_chans >= mode->num_channels)
 +/*
 + * 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 struct hostapd_channel_data *
++dfs_get_chan_data(struct hostapd_hw_modes *mode, int freq, int first_chan_idx)
++{
++      int i;
++
++      for (i = first_chan_idx; i < mode->num_channels; i++) {
++              if (mode->channels[i].freq == freq)
++                      return &mode->channels[i];
++      }
++
++      return NULL;
++}
++
++
 +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;
 +
-               chan = &mode->channels[first_chan_idx + i];
-               if (first_chan->freq + i * 20 != chan->freq)
++      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++) {
-       int *entry;
-       if (!iface->conf->chanlist)
++              chan = dfs_get_chan_data(mode, first_chan->freq + i * 20,
++                                       first_chan_idx);
++              if (!chan)
 +                      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)
 +{
-       for (entry = iface->conf->chanlist; *entry != -1; entry++) {
-               if (*entry == chan->chan)
-                       return 1;
-       }
-       return 0;
++      if (!iface->conf->acs_ch_list.num)
 +              return 1;
 +
++      return freq_range_list_includes(&iface->conf->acs_ch_list, chan->chan);
 +}
 +
 +
 +/*
 + * 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;
 +}
index a0adc67db430b970a767707eb61591906193cc19,0000000000000000000000000000000000000000..ca8b75c8390605d031bada4d7a60777c67da2939
mode 100644,000000..100644
--- /dev/null
@@@ -1,1262 -1,0 +1,1324 @@@
- #ifdef CONFIG_IEEE80211R
 +/*
 + * hostapd / Callback functions for driver wrappers
 + * 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.
 + */
 +
 +#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"
 +#include "common/ieee802_11_common.h"
 +#include "common/wpa_ctrl.h"
 +#include "crypto/random.h"
 +#include "p2p/p2p.h"
 +#include "wps/wps.h"
++#include "fst/fst.h"
 +#include "wnm_ap.h"
 +#include "hostapd.h"
 +#include "ieee802_11.h"
 +#include "sta_info.h"
 +#include "accounting.h"
 +#include "tkip_countermeasures.h"
 +#include "ieee802_1x.h"
 +#include "wpa_auth.h"
 +#include "wps_hostapd.h"
 +#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,
 +                      const u8 *req_ies, size_t req_ies_len, int reassoc)
 +{
 +      struct sta_info *sta;
 +      int new_assoc, res;
 +      struct ieee802_11_elems elems;
 +      const u8 *ie;
 +      size_t ielen;
- #endif /* CONFIG_IEEE80211R */
++#if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W)
 +      u8 buf[sizeof(struct ieee80211_mgmt) + 1024];
 +      u8 *p = buf;
-               wpa_printf(MSG_DEBUG, "hostapd_notif_assoc: Skip event with "
-                          "no address");
++#endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W */
 +      u16 reason = WLAN_REASON_UNSPECIFIED;
 +      u16 status = WLAN_STATUS_SUCCESS;
 +      const u8 *p2p_dev_addr = NULL;
 +
 +      if (addr == NULL) {
 +              /*
 +               * This could potentially happen with unexpected event from the
 +               * driver wrapper. This was seen at least in one case where the
 +               * driver ended up being set to station mode while hostapd was
 +               * running, so better make sure we stop processing such an
 +               * event here.
 +               */
-               wpa_printf(MSG_DEBUG, "STA did not include WPS/RSN/WPA IE in "
-                          "(Re)AssocReq");
++              wpa_printf(MSG_DEBUG,
++                         "hostapd_notif_assoc: Skip event with no address");
 +              return -1;
 +      }
 +      random_add_randomness(addr, ETH_ALEN);
 +
 +      hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211,
 +                     HOSTAPD_LEVEL_INFO, "associated");
 +
 +      ieee802_11_parse_elems(req_ies, req_ies_len, &elems, 0);
 +      if (elems.wps_ie) {
 +              ie = elems.wps_ie - 2;
 +              ielen = elems.wps_ie_len + 2;
 +              wpa_printf(MSG_DEBUG, "STA included WPS IE in (Re)AssocReq");
 +      } else if (elems.rsn_ie) {
 +              ie = elems.rsn_ie - 2;
 +              ielen = elems.rsn_ie_len + 2;
 +              wpa_printf(MSG_DEBUG, "STA included RSN IE in (Re)AssocReq");
 +      } else if (elems.wpa_ie) {
 +              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;
-           elems.ht_capabilities_len >=
-           sizeof(struct ieee80211_ht_capabilities) &&
++              wpa_printf(MSG_DEBUG,
++                         "STA did not include WPS/RSN/WPA IE in (Re)AssocReq");
 +      }
 +
 +      sta = ap_get_sta(hapd, addr);
 +      if (sta) {
 +              ap_sta_no_session_timeout(hapd, sta);
 +              accounting_sta_stop(hapd, sta);
 +
 +              /*
 +               * Make sure that the previously registered inactivity timer
 +               * will not remove the STA immediately.
 +               */
 +              sta->timeout_next = STA_NULLFUNC;
 +      } else {
 +              sta = ap_sta_add(hapd, addr);
 +              if (sta == NULL) {
 +                      hostapd_drv_sta_disassoc(hapd, addr,
 +                                               WLAN_REASON_DISASSOC_AP_BUSY);
 +                      return -1;
 +              }
 +      }
 +      sta->flags &= ~(WLAN_STA_WPS | WLAN_STA_MAYBE_WPS | WLAN_STA_WPS2);
 +
 +#ifdef CONFIG_P2P
 +      if (elems.p2p) {
 +              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 &&
-                               wpa_printf(MSG_DEBUG, "STA did not include "
-                                          "WPA/RSN IE in (Re)Association "
-                                          "Request - possible WPS use");
 +          (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) {
 +              sta->hs20_ie = wpabuf_alloc_copy(elems.hs20 + 4,
 +                                               elems.hs20_len - 4);
 +      } else
 +              sta->hs20_ie = NULL;
 +#endif /* CONFIG_HS20 */
 +
++#ifdef CONFIG_FST
++      wpabuf_free(sta->mb_ies);
++      if (hapd->iface->fst)
++              sta->mb_ies = mb_ies_by_info(&elems.mb_ies);
++      else
++              sta->mb_ies = NULL;
++#endif /* CONFIG_FST */
++
 +      if (hapd->conf->wpa) {
 +              if (ie == NULL || ielen == 0) {
 +#ifdef CONFIG_WPS
 +                      if (hapd->conf->wps_state) {
-                                       wpa_printf(MSG_DEBUG, "WPS: STA "
-                                                  "supports WPS 2.0");
++                              wpa_printf(MSG_DEBUG,
++                                         "STA did not include WPA/RSN IE in (Re)Association Request - possible WPS use");
 +                              sta->flags |= WLAN_STA_MAYBE_WPS;
 +                              goto skip_wpa_check;
 +                      }
 +#endif /* CONFIG_WPS */
 +
 +                      wpa_printf(MSG_DEBUG, "No WPA/RSN IE from STA");
 +                      return -1;
 +              }
 +#ifdef CONFIG_WPS
 +              if (hapd->conf->wps_state && ie[0] == 0xdd && ie[1] >= 4 &&
 +                  os_memcmp(ie + 2, "\x00\x50\xf2\x04", 4) == 0) {
 +                      struct wpabuf *wps;
++
 +                      sta->flags |= WLAN_STA_WPS;
 +                      wps = ieee802_11_vendor_ie_concat(ie, ielen,
 +                                                        WPS_IE_VENDOR_TYPE);
 +                      if (wps) {
 +                              if (wps_is_20(wps)) {
-                       wpa_printf(MSG_ERROR, "Failed to initialize WPA state "
-                                  "machine");
++                                      wpa_printf(MSG_DEBUG,
++                                                 "WPS: STA supports WPS 2.0");
 +                                      sta->flags |= WLAN_STA_WPS2;
 +                              }
 +                              wpabuf_free(wps);
 +                      }
 +                      goto skip_wpa_check;
 +              }
 +#endif /* CONFIG_WPS */
 +
 +              if (sta->wpa_sm == NULL)
 +                      sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth,
 +                                                      sta->addr,
 +                                                      p2p_dev_addr);
 +              if (sta->wpa_sm == NULL) {
-                       wpa_printf(MSG_DEBUG, "WPA/RSN information element "
-                                  "rejected? (res %u)", res);
++                      wpa_printf(MSG_ERROR,
++                                 "Failed to initialize WPA state machine");
 +                      return -1;
 +              }
 +              res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm,
 +                                        ie, ielen,
 +                                        elems.mdie, elems.mdie_len);
 +              if (res != WPA_IE_OK) {
- #ifdef CONFIG_IEEE80211R
++                      wpa_printf(MSG_DEBUG,
++                                 "WPA/RSN information element rejected? (res %u)",
++                                 res);
 +                      wpa_hexdump(MSG_DEBUG, "IE", ie, ielen);
 +                      if (res == WPA_INVALID_GROUP) {
 +                              reason = WLAN_REASON_GROUP_CIPHER_NOT_VALID;
 +                              status = WLAN_STATUS_GROUP_CIPHER_NOT_VALID;
 +                      } else if (res == WPA_INVALID_PAIRWISE) {
 +                              reason = WLAN_REASON_PAIRWISE_CIPHER_NOT_VALID;
 +                              status = WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID;
 +                      } else if (res == WPA_INVALID_AKMP) {
 +                              reason = WLAN_REASON_AKMP_NOT_VALID;
 +                              status = WLAN_STATUS_AKMP_NOT_VALID;
 +                      }
 +#ifdef CONFIG_IEEE80211W
 +                      else if (res == WPA_MGMT_FRAME_PROTECTION_VIOLATION) {
 +                              reason = WLAN_REASON_INVALID_IE;
 +                              status = WLAN_STATUS_INVALID_IE;
 +                      } else if (res == WPA_INVALID_MGMT_GROUP_CIPHER) {
 +                              reason = WLAN_REASON_GROUP_CIPHER_NOT_VALID;
 +                              status = WLAN_STATUS_GROUP_CIPHER_NOT_VALID;
 +                      }
 +#endif /* CONFIG_IEEE80211W */
 +                      else {
 +                              reason = WLAN_REASON_INVALID_IE;
 +                              status = WLAN_STATUS_INVALID_IE;
 +                      }
 +                      goto fail;
 +              }
 +#ifdef CONFIG_IEEE80211W
 +              if ((sta->flags & WLAN_STA_MFP) && !sta->sa_query_timed_out &&
 +                  sta->sa_query_count > 0)
 +                      ap_check_sa_query_timeout(hapd, sta);
 +              if ((sta->flags & WLAN_STA_MFP) && !sta->sa_query_timed_out &&
 +                  (sta->auth_alg != WLAN_AUTH_FT)) {
 +                      /*
 +                       * STA has already been associated with MFP and SA
 +                       * Query timeout has not been reached. Reject the
 +                       * association attempt temporarily and start SA Query,
 +                       * if one is not pending.
 +                       */
 +
 +                      if (sta->sa_query_count == 0)
 +                              ap_sta_start_sa_query(hapd, sta);
 +
- #endif /* CONFIG_IEEE80211R */
 +                      status = WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY;
 +
 +                      p = hostapd_eid_assoc_comeback_time(hapd, sta, p);
 +
 +                      hostapd_sta_assoc(hapd, addr, reassoc, status, buf,
 +                                        p - buf);
-                               wpa_printf(MSG_DEBUG, "WPS: STA supports "
-                                          "WPS 2.0");
 +                      return 0;
 +              }
 +
 +              if (wpa_auth_uses_mfp(sta->wpa_sm))
 +                      sta->flags |= WLAN_STA_MFP;
 +              else
 +                      sta->flags &= ~WLAN_STA_MFP;
 +#endif /* CONFIG_IEEE80211W */
 +
 +#ifdef CONFIG_IEEE80211R
 +              if (sta->auth_alg == WLAN_AUTH_FT) {
 +                      status = wpa_ft_validate_reassoc(sta->wpa_sm, req_ies,
 +                                                       req_ies_len);
 +                      if (status != WLAN_STATUS_SUCCESS) {
 +                              if (status == WLAN_STATUS_INVALID_PMKID)
 +                                      reason = WLAN_REASON_INVALID_IE;
 +                              if (status == WLAN_STATUS_INVALID_MDIE)
 +                                      reason = WLAN_REASON_INVALID_IE;
 +                              if (status == WLAN_STATUS_INVALID_FTIE)
 +                                      reason = WLAN_REASON_INVALID_IE;
 +                              goto fail;
 +                      }
 +              }
 +#endif /* CONFIG_IEEE80211R */
 +      } else if (hapd->conf->wps_state) {
 +#ifdef CONFIG_WPS
 +              struct wpabuf *wps;
++
 +              if (req_ies)
 +                      wps = ieee802_11_vendor_ie_concat(req_ies, req_ies_len,
 +                                                        WPS_IE_VENDOR_TYPE);
 +              else
 +                      wps = NULL;
 +#ifdef CONFIG_WPS_STRICT
 +              if (wps && wps_validate_assoc_req(wps) < 0) {
 +                      reason = WLAN_REASON_INVALID_IE;
 +                      status = WLAN_STATUS_INVALID_IE;
 +                      wpabuf_free(wps);
 +                      goto fail;
 +              }
 +#endif /* CONFIG_WPS_STRICT */
 +              if (wps) {
 +                      sta->flags |= WLAN_STA_WPS;
 +                      if (wps_is_20(wps)) {
-                       wpa_printf(MSG_WARNING, "Failed to initialize WPA "
-                                  "state machine");
++                              wpa_printf(MSG_DEBUG,
++                                         "WPS: STA supports WPS 2.0");
 +                              sta->flags |= WLAN_STA_WPS2;
 +                      }
 +              } else
 +                      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_DEBUG, "hostapd_notif_disassoc: Skip event "
-                          "with no address");
++                      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:
 +#endif /* CONFIG_WPS */
 +
 +#ifdef CONFIG_IEEE80211R
 +      p = wpa_sm_write_assoc_resp_ies(sta->wpa_sm, buf, sizeof(buf),
 +                                      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) {
 +      }
 +#endif /* CONFIG_IEEE80211R */
 +
 +      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);
 +      else
 +              wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC);
 +
 +      hostapd_new_assoc_sta(hapd, sta, !new_assoc);
 +
 +      ieee802_1x_notify_port_enabled(sta->eapol_sm, 1);
 +
 +#ifdef CONFIG_P2P
 +      if (req_ies) {
 +              p2p_group_notif_assoc(hapd->p2p_group, sta->addr,
 +                                    req_ies, req_ies_len);
 +      }
 +#endif /* CONFIG_P2P */
 +
 +      return 0;
 +
 +fail:
 +#ifdef CONFIG_IEEE80211R
 +      hostapd_sta_assoc(hapd, addr, reassoc, status, buf, p - buf);
 +#endif /* CONFIG_IEEE80211R */
 +      hostapd_drv_sta_disassoc(hapd, sta->addr, reason);
 +      ap_free_sta(hapd, sta);
 +      return -1;
 +}
 +
 +
 +void hostapd_notif_disassoc(struct hostapd_data *hapd, const u8 *addr)
 +{
 +      struct sta_info *sta;
 +
 +      if (addr == NULL) {
 +              /*
 +               * This could potentially happen with unexpected event from the
 +               * driver wrapper. This was seen at least in one case where the
 +               * driver ended up reporting a station mode event while hostapd
 +               * was running, so better make sure we stop processing such an
 +               * event here.
 +               */
-               wpa_printf(MSG_DEBUG, "Disassociation notification for "
-                          "unknown STA " MACSTR, MAC2STR(addr));
++              wpa_printf(MSG_DEBUG,
++                         "hostapd_notif_disassoc: Skip event with no address");
 +              return;
 +      }
 +
 +      hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211,
 +                     HOSTAPD_LEVEL_INFO, "disassociated");
 +
 +      sta = ap_get_sta(hapd, addr);
 +      if (sta == NULL) {
-                      HOSTAPD_LEVEL_INFO, "disconnected due to excessive "
-                      "missing ACKs");
++              wpa_printf(MSG_DEBUG,
++                         "Disassociation notification for unknown STA "
++                         MACSTR, MAC2STR(addr));
 +              return;
 +      }
 +
 +      ap_sta_set_authorized(hapd, sta, 0);
 +      sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC);
 +      wpa_auth_sm_event(sta->wpa_sm, WPA_DISASSOC);
 +      sta->acct_terminate_cause = RADIUS_ACCT_TERMINATE_CAUSE_USER_REQUEST;
 +      ieee802_1x_notify_port_enabled(sta->eapol_sm, 0);
 +      ap_free_sta(hapd, sta);
 +}
 +
 +
 +void hostapd_event_sta_low_ack(struct hostapd_data *hapd, const u8 *addr)
 +{
 +      struct sta_info *sta = ap_get_sta(hapd, addr);
 +
 +      if (!sta || !hapd->conf->disassoc_low_ack)
 +              return;
 +
 +      hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211,
-                              HOSTAPD_LEVEL_WARNING, "driver switched to "
-                              "bad channel!");
++                     HOSTAPD_LEVEL_INFO,
++                     "disconnected due to excessive missing ACKs");
 +      hostapd_drv_sta_disassoc(hapd, addr, WLAN_REASON_DISASSOC_LOW_ACK);
 +      if (sta)
 +              ap_sta_disassociate(hapd, sta, WLAN_REASON_DISASSOC_LOW_ACK);
 +}
 +
 +
 +void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht,
 +                           int offset, int width, int cf1, int cf2)
 +{
 +#ifdef NEED_AP_MLME
 +      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, width=%d (%s), cf1=%d, cf2=%d",
 +                     freq, ht, offset, width, channel_width_to_string(width),
 +                     cf1, cf2);
 +
 +      hapd->iface->freq = freq;
 +
 +      channel = hostapd_hw_get_channel(hapd, freq);
 +      if (!channel) {
 +              hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
-                                        u8 pri_channel, u8 sec_channel)
++                             HOSTAPD_LEVEL_WARNING,
++                             "driver switched to bad channel!");
 +              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 */
 +}
 +
 +
 +void hostapd_event_connect_failed_reason(struct hostapd_data *hapd,
 +                                       const u8 *addr, int reason_code)
 +{
 +      switch (reason_code) {
 +      case MAX_CLIENT_REACHED:
 +              wpa_msg(hapd->msg_ctx, MSG_INFO, AP_REJECTED_MAX_STA MACSTR,
 +                      MAC2STR(addr));
 +              break;
 +      case BLOCKED_CLIENT:
 +              wpa_msg(hapd->msg_ctx, MSG_INFO, AP_REJECTED_BLOCKED_STA MACSTR,
 +                      MAC2STR(addr));
 +              break;
 +      }
 +}
 +
 +
 +#ifdef CONFIG_ACS
 +static void hostapd_acs_channel_selected(struct hostapd_data *hapd,
-       int channel;
-       int ret;
++                                       struct acs_selected_channels *acs_res)
 +{
-       hapd->iface->freq = hostapd_hw_get_freq(hapd, pri_channel);
++      int ret, i;
 +
 +      if (hapd->iconf->channel) {
 +              wpa_printf(MSG_INFO, "ACS: Channel was already set to %d",
 +                         hapd->iconf->channel);
 +              return;
 +      }
 +
-       channel = pri_channel;
-       if (!channel) {
++      if (!hapd->iface->current_mode) {
++              for (i = 0; i < hapd->iface->num_hw_features; i++) {
++                      struct hostapd_hw_modes *mode =
++                              &hapd->iface->hw_features[i];
 +
-       hapd->iconf->channel = channel;
++                      if (mode->mode == acs_res->hw_mode) {
++                              hapd->iface->current_mode = mode;
++                              break;
++                      }
++              }
++              if (!hapd->iface->current_mode) {
++                      hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
++                                     HOSTAPD_LEVEL_WARNING,
++                                     "driver selected to bad hw_mode");
++                      return;
++              }
++      }
++
++      hapd->iface->freq = hostapd_hw_get_freq(hapd, acs_res->pri_channel);
++
++      if (!acs_res->pri_channel) {
 +              hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
 +                             HOSTAPD_LEVEL_WARNING,
 +                             "driver switched to bad channel");
 +              return;
 +      }
 +
-       if (sec_channel == 0)
++      hapd->iconf->channel = acs_res->pri_channel;
++      hapd->iconf->acs = 1;
 +
-       else if (sec_channel < pri_channel)
++      if (acs_res->sec_channel == 0)
 +              hapd->iconf->secondary_channel = 0;
-       else if (sec_channel > pri_channel)
++      else if (acs_res->sec_channel < acs_res->pri_channel)
 +              hapd->iconf->secondary_channel = -1;
-                       wpa_printf(MSG_DEBUG, "FT: Failed to initialize WPA "
-                                  "state machine");
++      else if (acs_res->sec_channel > acs_res->pri_channel)
 +              hapd->iconf->secondary_channel = 1;
 +      else {
 +              wpa_printf(MSG_ERROR, "Invalid secondary channel!");
 +              return;
 +      }
 +
++      if (hapd->iface->conf->ieee80211ac) {
++              /* set defaults for backwards compatibility */
++              hapd->iconf->vht_oper_centr_freq_seg1_idx = 0;
++              hapd->iconf->vht_oper_centr_freq_seg0_idx = 0;
++              hapd->iconf->vht_oper_chwidth = VHT_CHANWIDTH_USE_HT;
++              if (acs_res->ch_width == 80) {
++                      hapd->iconf->vht_oper_centr_freq_seg0_idx =
++                              acs_res->vht_seg0_center_ch;
++                      hapd->iconf->vht_oper_chwidth = VHT_CHANWIDTH_80MHZ;
++              } else if (acs_res->ch_width == 160) {
++                      if (acs_res->vht_seg1_center_ch == 0) {
++                              hapd->iconf->vht_oper_centr_freq_seg0_idx =
++                                      acs_res->vht_seg0_center_ch;
++                              hapd->iconf->vht_oper_chwidth =
++                                      VHT_CHANWIDTH_160MHZ;
++                      } else {
++                              hapd->iconf->vht_oper_centr_freq_seg0_idx =
++                                      acs_res->vht_seg0_center_ch;
++                              hapd->iconf->vht_oper_centr_freq_seg1_idx =
++                                      acs_res->vht_seg1_center_ch;
++                              hapd->iconf->vht_oper_chwidth =
++                                      VHT_CHANWIDTH_80P80MHZ;
++                      }
++              }
++      }
++
 +      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)
 +{
 +      size_t i;
 +      int ret = 0;
 +
 +      if (sa == NULL || ie == NULL)
 +              return -1;
 +
 +      random_add_randomness(sa, ETH_ALEN);
 +      for (i = 0; hapd->probereq_cb && i < hapd->num_probereq_cb; i++) {
 +              if (hapd->probereq_cb[i].cb(hapd->probereq_cb[i].ctx,
 +                                          sa, da, bssid, ie, ie_len,
 +                                          ssi_signal) > 0) {
 +                      ret = 1;
 +                      break;
 +              }
 +      }
 +      return ret;
 +}
 +
 +
 +#ifdef HOSTAPD
 +
 +#ifdef CONFIG_IEEE80211R
 +static void hostapd_notify_auth_ft_finish(void *ctx, const u8 *dst,
 +                                        const u8 *bssid,
 +                                        u16 auth_transaction, u16 status,
 +                                        const u8 *ies, size_t ies_len)
 +{
 +      struct hostapd_data *hapd = ctx;
 +      struct sta_info *sta;
 +
 +      sta = ap_get_sta(hapd, dst);
 +      if (sta == NULL)
 +              return;
 +
 +      hostapd_logger(hapd, dst, HOSTAPD_MODULE_IEEE80211,
 +                     HOSTAPD_LEVEL_DEBUG, "authentication OK (FT)");
 +      sta->flags |= WLAN_STA_AUTH;
 +
 +      hostapd_sta_auth(hapd, dst, auth_transaction, status, ies, ies_len);
 +}
 +#endif /* CONFIG_IEEE80211R */
 +
 +
 +static void hostapd_notif_auth(struct hostapd_data *hapd,
 +                             struct auth_info *rx_auth)
 +{
 +      struct sta_info *sta;
 +      u16 status = WLAN_STATUS_SUCCESS;
 +      u8 resp_ies[2 + WLAN_AUTH_CHALLENGE_LEN];
 +      size_t resp_ies_len = 0;
 +
 +      sta = ap_get_sta(hapd, rx_auth->peer);
 +      if (!sta) {
 +              sta = ap_sta_add(hapd, rx_auth->peer);
 +              if (sta == NULL) {
 +                      status = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
 +                      goto fail;
 +              }
 +      }
 +      sta->flags &= ~WLAN_STA_PREAUTH;
 +      ieee802_1x_notify_pre_auth(sta->eapol_sm, 0);
 +#ifdef CONFIG_IEEE80211R
 +      if (rx_auth->auth_type == WLAN_AUTH_FT && hapd->wpa_auth) {
 +              sta->auth_alg = WLAN_AUTH_FT;
 +              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, "RX_ACTION cat %d action plen %d",
++                      wpa_printf(MSG_DEBUG,
++                                 "FT: Failed to initialize WPA state machine");
 +                      status = WLAN_STATUS_UNSPECIFIED_FAILURE;
 +                      goto fail;
 +              }
 +              wpa_ft_process_auth(sta->wpa_sm, rx_auth->bssid,
 +                                  rx_auth->auth_transaction, rx_auth->ies,
 +                                  rx_auth->ies_len,
 +                                  hostapd_notify_auth_ft_finish, hapd);
 +              return;
 +      }
 +#endif /* CONFIG_IEEE80211R */
 +fail:
 +      hostapd_sta_auth(hapd, rx_auth->peer, rx_auth->auth_transaction + 1,
 +                       status, resp_ies, resp_ies_len);
 +}
 +
 +
 +static void hostapd_action_rx(struct hostapd_data *hapd,
 +                            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 */
 +
-               u16 fc;
-               fc = le_to_host16(hdr->frame_control);
++      wpa_printf(MSG_DEBUG, "RX_ACTION cat %d action plen %d",
 +                 mgmt->u.action.category, (int) plen);
 +
 +      sta = ap_get_sta(hapd, mgmt->sa);
 +      if (sta == NULL) {
 +              wpa_printf(MSG_DEBUG, "%s: station not found", __func__);
 +              return;
 +      }
 +#ifdef CONFIG_IEEE80211R
 +      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 (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 (mgmt->u.action.category == WLAN_ACTION_WNM) {
 +              ieee802_11_rx_wnm_action_ap(hapd, mgmt, drv_mgmt->frame_len);
 +      }
 +#endif /* CONFIG_WNM */
++#ifdef CONFIG_FST
++      if (mgmt->u.action.category == WLAN_ACTION_FST && hapd->iface->fst) {
++              fst_rx_action(hapd->iface->fst, mgmt, drv_mgmt->frame_len);
++              return;
++      }
++#endif /* CONFIG_FST */
++
 +}
 +
 +
 +#ifdef NEED_AP_MLME
 +
 +#define HAPD_BROADCAST ((struct hostapd_data *) -1)
 +
 +static struct hostapd_data * get_hapd_bssid(struct hostapd_iface *iface,
 +                                          const u8 *bssid)
 +{
 +      size_t i;
 +
 +      if (bssid == NULL)
 +              return NULL;
 +      if (bssid[0] == 0xff && bssid[1] == 0xff && bssid[2] == 0xff &&
 +          bssid[3] == 0xff && bssid[4] == 0xff && bssid[5] == 0xff)
 +              return HAPD_BROADCAST;
 +
 +      for (i = 0; i < iface->num_bss; i++) {
 +              if (os_memcmp(bssid, iface->bss[i]->own_addr, ETH_ALEN) == 0)
 +                      return iface->bss[i];
 +      }
 +
 +      return NULL;
 +}
 +
 +
 +static void hostapd_rx_from_unknown_sta(struct hostapd_data *hapd,
 +                                      const u8 *bssid, const u8 *addr,
 +                                      int wds)
 +{
 +      hapd = get_hapd_bssid(hapd->iface, bssid);
 +      if (hapd == NULL || hapd == HAPD_BROADCAST)
 +              return;
 +
 +      ieee802_11_rx_from_unknown(hapd, addr, wds);
 +}
 +
 +
 +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 0;
 +
 +      hapd = get_hapd_bssid(iface, bssid);
 +      if (hapd == NULL) {
-               if ((sta = ap_get_sta(iface->bss[j], src))) {
-                       if (sta->flags & WLAN_STA_ASSOC) {
-                               hapd = iface->bss[j];
-                               break;
-                       }
++              u16 fc = le_to_host16(hdr->frame_control);
 +
 +              /*
 +               * Drop frames to unknown BSSIDs except for Beacon frames which
 +               * could be used to update neighbor information.
 +               */
 +              if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT &&
 +                  WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_BEACON)
 +                      hapd = iface->bss[0];
 +              else
 +                      return 0;
 +      }
 +
 +      os_memset(&fi, 0, sizeof(fi));
 +      fi.datarate = rx_mgmt->datarate;
 +      fi.ssi_signal = rx_mgmt->ssi_signal;
 +
 +      if (hapd == HAPD_BROADCAST) {
 +              size_t i;
++
 +              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
 +              ret = ieee802_11_mgmt(hapd, rx_mgmt->frame, rx_mgmt->frame_len,
 +                                    &fi);
 +
 +      random_add_randomness(&fi, sizeof(fi));
 +
 +      return ret;
 +}
 +
 +
 +static void hostapd_mgmt_tx_cb(struct hostapd_data *hapd, const u8 *buf,
 +                             size_t len, u16 stype, int ok)
 +{
 +      struct ieee80211_hdr *hdr;
++
 +      hdr = (struct ieee80211_hdr *) buf;
 +      hapd = get_hapd_bssid(hapd->iface, get_hdr_bssid(hdr, len));
 +      if (hapd == NULL || hapd == HAPD_BROADCAST)
 +              return;
 +      ieee802_11_mgmt_cb(hapd, buf, len, stype, ok);
 +}
 +
 +#endif /* NEED_AP_MLME */
 +
 +
 +static int hostapd_event_new_sta(struct hostapd_data *hapd, const u8 *addr)
 +{
 +      struct sta_info *sta = ap_get_sta(hapd, addr);
++
 +      if (sta)
 +              return 0;
 +
 +      wpa_printf(MSG_DEBUG, "Data frame from unknown STA " MACSTR
 +                 " - adding a new STA", MAC2STR(addr));
 +      sta = ap_sta_add(hapd, addr);
 +      if (sta) {
 +              hostapd_new_assoc_sta(hapd, sta, 0);
 +      } else {
 +              wpa_printf(MSG_DEBUG, "Failed to add STA entry for " MACSTR,
 +                         MAC2STR(addr));
 +              return -1;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +static void hostapd_event_eapol_rx(struct hostapd_data *hapd, const u8 *src,
 +                                 const u8 *data, size_t data_len)
 +{
 +      struct hostapd_iface *iface = hapd->iface;
 +      struct sta_info *sta;
 +      size_t j;
 +
 +      for (j = 0; j < iface->num_bss; j++) {
-       wpa_printf(MSG_DEBUG, "Single Channel Survey: (freq=%d channel_time=%ld channel_time_busy=%ld)",
++              sta = ap_get_sta(iface->bss[j], src);
++              if (sta && sta->flags & WLAN_STA_ASSOC) {
++                      hapd = iface->bss[j];
++                      break;
 +              }
 +      }
 +
 +      ieee802_1x_receive(hapd, src, data, data_len);
 +}
 +
 +
 +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;
 +
-               hostapd_acs_channel_selected(
-                       hapd, data->acs_selected_channels.pri_channel,
-                       data->acs_selected_channels.sec_channel);
++      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)
 +{
 +      struct hostapd_data *hapd = ctx;
 +#ifndef CONFIG_NO_STDOUT_DEBUG
 +      int level = MSG_DEBUG;
 +
 +      if (event == EVENT_RX_MGMT && data->rx_mgmt.frame &&
 +          data->rx_mgmt.frame_len >= 24) {
 +              const struct ieee80211_hdr *hdr;
 +              u16 fc;
++
 +              hdr = (const struct ieee80211_hdr *) data->rx_mgmt.frame;
 +              fc = le_to_host16(hdr->frame_control);
 +              if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT &&
 +                  WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_BEACON)
 +                      level = MSG_EXCESSIVE;
 +              if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT &&
 +                  WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_PROBE_REQ)
 +                      level = MSG_EXCESSIVE;
 +      }
 +
 +      wpa_dbg(hapd->msg_ctx, level, "Event %s (%d) received",
 +              event_to_string(event), event);
 +#endif /* CONFIG_NO_STDOUT_DEBUG */
 +
 +      switch (event) {
 +      case EVENT_MICHAEL_MIC_FAILURE:
 +              michael_mic_failure(hapd, data->michael_mic_failure.src, 1);
 +              break;
 +      case EVENT_SCAN_RESULTS:
 +              if (hapd->iface->scan_cb)
 +                      hapd->iface->scan_cb(hapd->iface);
 +              break;
 +      case EVENT_WPS_BUTTON_PUSHED:
 +              hostapd_wps_button_pushed(hapd, NULL);
 +              break;
 +#ifdef NEED_AP_MLME
 +      case EVENT_TX_STATUS:
 +              switch (data->tx_status.type) {
 +              case WLAN_FC_TYPE_MGMT:
 +                      hostapd_mgmt_tx_cb(hapd, data->tx_status.data,
 +                                         data->tx_status.data_len,
 +                                         data->tx_status.stype,
 +                                         data->tx_status.ack);
 +                      break;
 +              case WLAN_FC_TYPE_DATA:
 +                      hostapd_tx_status(hapd, data->tx_status.dst,
 +                                        data->tx_status.data,
 +                                        data->tx_status.data_len,
 +                                        data->tx_status.ack);
 +                      break;
 +              }
 +              break;
 +      case EVENT_EAPOL_TX_STATUS:
 +              hostapd_eapol_tx_status(hapd, data->eapol_tx_status.dst,
 +                                      data->eapol_tx_status.data,
 +                                      data->eapol_tx_status.data_len,
 +                                      data->eapol_tx_status.ack);
 +              break;
 +      case EVENT_DRIVER_CLIENT_POLL_OK:
 +              hostapd_client_poll_ok(hapd, data->client_poll.addr);
 +              break;
 +      case EVENT_RX_FROM_UNKNOWN:
 +              hostapd_rx_from_unknown_sta(hapd, data->rx_from_unknown.bssid,
 +                                          data->rx_from_unknown.addr,
 +                                          data->rx_from_unknown.wds);
 +              break;
 +#endif /* NEED_AP_MLME */
 +      case EVENT_RX_MGMT:
 +              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)
 +                      break;
 +              hostapd_probe_req_rx(hapd, 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);
 +              break;
 +      case EVENT_NEW_STA:
 +              hostapd_event_new_sta(hapd, data->new_sta.addr);
 +              break;
 +      case EVENT_EAPOL_RX:
 +              hostapd_event_eapol_rx(hapd, data->eapol_rx.src,
 +                                     data->eapol_rx.data,
 +                                     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,
 +                                  data->assoc_info.reassoc);
 +              break;
 +      case EVENT_DISASSOC:
 +              if (data)
 +                      hostapd_notif_disassoc(hapd, data->disassoc_info.addr);
 +              break;
 +      case EVENT_DEAUTH:
 +              if (data)
 +                      hostapd_notif_disassoc(hapd, data->deauth_info.addr);
 +              break;
 +      case EVENT_STATION_LOW_ACK:
 +              if (!data)
 +                      break;
 +              hostapd_event_sta_low_ack(hapd, data->low_ack.addr);
 +              break;
 +      case EVENT_AUTH:
 +              hostapd_notif_auth(hapd, &data->auth);
 +              break;
 +      case EVENT_CH_SWITCH:
 +              if (!data)
 +                      break;
 +              hostapd_event_ch_switch(hapd, data->ch_switch.freq,
 +                                      data->ch_switch.ht_enabled,
 +                                      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)
 +                      break;
 +              hostapd_event_connect_failed_reason(
 +                      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);
 +              break;
 +#endif /* CONFIG_ACS */
 +      default:
 +              wpa_printf(MSG_DEBUG, "Unknown event %d", event);
 +              break;
 +      }
 +}
 +
 +#endif /* HOSTAPD */
index 559d77f9ef2b8606555214a589c357ad99a51868,0000000000000000000000000000000000000000..082d0f53175e197f518783945c57ebc222543997
mode 100644,000000..100644
--- /dev/null
@@@ -1,274 -1,0 +1,282 @@@
-       if (identity_len >= sizeof(id_str))
 +/*
 + * hostapd / EAP user database
 + * Copyright (c) 2012, Jouni Malinen <j@w1.fi>
 + *
 + * This software may be distributed under the terms of the BSD license.
 + * See README for more details.
 + */
 +
 +#include "includes.h"
 +#ifdef CONFIG_SQLITE
 +#include <sqlite3.h>
 +#endif /* CONFIG_SQLITE */
 +
 +#include "common.h"
 +#include "eap_common/eap_wsc_common.h"
 +#include "eap_server/eap_methods.h"
 +#include "eap_server/eap.h"
 +#include "ap_config.h"
 +#include "hostapd.h"
 +
 +#ifdef CONFIG_SQLITE
 +
 +static void set_user_methods(struct hostapd_eap_user *user, const char *methods)
 +{
 +      char *buf, *start;
 +      int num_methods;
 +
 +      buf = os_strdup(methods);
 +      if (buf == NULL)
 +              return;
 +
 +      os_memset(&user->methods, 0, sizeof(user->methods));
 +      num_methods = 0;
 +      start = buf;
 +      while (*start) {
 +              char *pos3 = os_strchr(start, ',');
 +              if (pos3)
 +                      *pos3++ = '\0';
 +              user->methods[num_methods].method =
 +                      eap_server_get_type(start,
 +                                          &user->methods[num_methods].vendor);
 +              if (user->methods[num_methods].vendor == EAP_VENDOR_IETF &&
 +                  user->methods[num_methods].method == EAP_TYPE_NONE) {
 +                      if (os_strcmp(start, "TTLS-PAP") == 0) {
 +                              user->ttls_auth |= EAP_TTLS_AUTH_PAP;
 +                              goto skip_eap;
 +                      }
 +                      if (os_strcmp(start, "TTLS-CHAP") == 0) {
 +                              user->ttls_auth |= EAP_TTLS_AUTH_CHAP;
 +                              goto skip_eap;
 +                      }
 +                      if (os_strcmp(start, "TTLS-MSCHAP") == 0) {
 +                              user->ttls_auth |= EAP_TTLS_AUTH_MSCHAP;
 +                              goto skip_eap;
 +                      }
 +                      if (os_strcmp(start, "TTLS-MSCHAPV2") == 0) {
 +                              user->ttls_auth |= EAP_TTLS_AUTH_MSCHAPV2;
 +                              goto skip_eap;
 +                      }
 +                      wpa_printf(MSG_INFO, "DB: Unsupported EAP type '%s'",
 +                                 start);
 +                      os_free(buf);
 +                      return;
 +              }
 +
 +              num_methods++;
 +              if (num_methods >= EAP_MAX_METHODS)
 +                      break;
 +      skip_eap:
 +              if (pos3 == NULL)
 +                      break;
 +              start = pos3;
 +      }
 +
 +      os_free(buf);
 +}
 +
 +
 +static int get_user_cb(void *ctx, int argc, char *argv[], char *col[])
 +{
 +      struct hostapd_eap_user *user = ctx;
 +      int i;
 +
 +      for (i = 0; i < argc; i++) {
 +              if (os_strcmp(col[i], "password") == 0 && argv[i]) {
 +                      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;
 +              }
 +      }
 +
 +      return 0;
 +}
 +
 +
 +static int get_wildcard_cb(void *ctx, int argc, char *argv[], char *col[])
 +{
 +      struct hostapd_eap_user *user = ctx;
 +      int i, id = -1, methods = -1;
 +      size_t len;
 +
 +      for (i = 0; i < argc; i++) {
 +              if (os_strcmp(col[i], "identity") == 0 && argv[i])
 +                      id = i;
 +              else if (os_strcmp(col[i], "methods") == 0 && argv[i])
 +                      methods = i;
 +      }
 +
 +      if (id < 0 || methods < 0)
 +              return 0;
 +
 +      len = os_strlen(argv[id]);
 +      if (len <= user->identity_len &&
 +          os_memcmp(argv[id], user->identity, len) == 0 &&
 +          (user->password == NULL || len > user->password_len)) {
 +              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;
 +              set_user_methods(user, argv[methods]);
 +      }
 +
 +      return 0;
 +}
 +
 +
 +static const struct hostapd_eap_user *
 +eap_user_sqlite_get(struct hostapd_data *hapd, const u8 *identity,
 +                  size_t identity_len, int phase2)
 +{
 +      sqlite3 *db;
 +      struct hostapd_eap_user *user = NULL;
 +      char id_str[256], cmd[300];
 +      size_t i;
 +
-               wpa_printf(MSG_DEBUG, "DB: Failed to complete SQL operation");
++      if (identity_len >= sizeof(id_str)) {
++              wpa_printf(MSG_DEBUG, "%s: identity len too big: %d >= %d",
++                         __func__, (int) identity_len,
++                         (int) (sizeof(id_str)));
 +              return NULL;
++      }
 +      os_memcpy(id_str, identity, identity_len);
 +      id_str[identity_len] = '\0';
 +      for (i = 0; i < identity_len; i++) {
 +              if (id_str[i] >= 'a' && id_str[i] <= 'z')
 +                      continue;
 +              if (id_str[i] >= 'A' && id_str[i] <= 'Z')
 +                      continue;
 +              if (id_str[i] >= '0' && id_str[i] <= '9')
 +                      continue;
 +              if (id_str[i] == '-' || id_str[i] == '_' || id_str[i] == '.' ||
 +                  id_str[i] == ',' || id_str[i] == '@' || id_str[i] == '\\' ||
 +                  id_str[i] == '!' || id_str[i] == '#' || id_str[i] == '%' ||
 +                  id_str[i] == '=' || id_str[i] == ' ')
 +                      continue;
 +              wpa_printf(MSG_INFO, "DB: Unsupported character in identity");
 +              return NULL;
 +      }
 +
 +      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);
 +      if (hapd->tmp_eap_user.identity == NULL)
 +              return NULL;
 +      os_memcpy(hapd->tmp_eap_user.identity, identity, identity_len);
 +
 +      if (sqlite3_open(hapd->conf->eap_user_sqlite, &db)) {
 +              wpa_printf(MSG_INFO, "DB: Failed to open database %s: %s",
 +                         hapd->conf->eap_user_sqlite, sqlite3_errmsg(db));
 +              sqlite3_close(db);
 +              return NULL;
 +      }
 +
 +      os_snprintf(cmd, sizeof(cmd),
 +                  "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) {
-                       wpa_printf(MSG_DEBUG, "DB: Failed to complete SQL "
-                                  "operation");
++              wpa_printf(MSG_DEBUG,
++                         "DB: Failed to complete SQL operation: %s  db: %s",
++                         sqlite3_errmsg(db), hapd->conf->eap_user_sqlite);
 +      } else if (hapd->tmp_eap_user.next)
 +              user = &hapd->tmp_eap_user;
 +
 +      if (user == NULL && !phase2) {
 +              os_snprintf(cmd, sizeof(cmd),
 +                          "SELECT identity,methods FROM wildcards;");
 +              wpa_printf(MSG_DEBUG, "DB: %s", cmd);
 +              if (sqlite3_exec(db, cmd, get_wildcard_cb, &hapd->tmp_eap_user,
 +                               NULL) != SQLITE_OK) {
++                      wpa_printf(MSG_DEBUG,
++                                 "DB: Failed to complete SQL operation: %s  db: %s",
++                                 sqlite3_errmsg(db),
++                                 hapd->conf->eap_user_sqlite);
 +              } else if (hapd->tmp_eap_user.next) {
 +                      user = &hapd->tmp_eap_user;
 +                      os_free(user->identity);
 +                      user->identity = user->password;
 +                      user->identity_len = user->password_len;
 +                      user->password = NULL;
 +                      user->password_len = 0;
 +              }
 +      }
 +
 +      sqlite3_close(db);
 +
 +      return user;
 +}
 +
 +#endif /* CONFIG_SQLITE */
 +
 +
 +const struct hostapd_eap_user *
 +hostapd_get_eap_user(struct hostapd_data *hapd, const u8 *identity,
 +                   size_t identity_len, int phase2)
 +{
 +      const struct hostapd_bss_config *conf = hapd->conf;
 +      struct hostapd_eap_user *user = conf->eap_user;
 +
 +#ifdef CONFIG_WPS
 +      if (conf->wps_state && identity_len == WSC_ID_ENROLLEE_LEN &&
 +          os_memcmp(identity, WSC_ID_ENROLLEE, WSC_ID_ENROLLEE_LEN) == 0) {
 +              static struct hostapd_eap_user wsc_enrollee;
 +              os_memset(&wsc_enrollee, 0, sizeof(wsc_enrollee));
 +              wsc_enrollee.methods[0].method = eap_server_get_type(
 +                      "WSC", &wsc_enrollee.methods[0].vendor);
 +              return &wsc_enrollee;
 +      }
 +
 +      if (conf->wps_state && identity_len == WSC_ID_REGISTRAR_LEN &&
 +          os_memcmp(identity, WSC_ID_REGISTRAR, WSC_ID_REGISTRAR_LEN) == 0) {
 +              static struct hostapd_eap_user wsc_registrar;
 +              os_memset(&wsc_registrar, 0, sizeof(wsc_registrar));
 +              wsc_registrar.methods[0].method = eap_server_get_type(
 +                      "WSC", &wsc_registrar.methods[0].vendor);
 +              wsc_registrar.password = (u8 *) conf->ap_pin;
 +              wsc_registrar.password_len = conf->ap_pin ?
 +                      os_strlen(conf->ap_pin) : 0;
 +              return &wsc_registrar;
 +      }
 +#endif /* CONFIG_WPS */
 +
 +      while (user) {
 +              if (!phase2 && user->identity == NULL) {
 +                      /* Wildcard match */
 +                      break;
 +              }
 +
 +              if (user->phase2 == !!phase2 && user->wildcard_prefix &&
 +                  identity_len >= user->identity_len &&
 +                  os_memcmp(user->identity, identity, user->identity_len) ==
 +                  0) {
 +                      /* Wildcard prefix match */
 +                      break;
 +              }
 +
 +              if (user->phase2 == !!phase2 &&
 +                  user->identity_len == identity_len &&
 +                  os_memcmp(user->identity, identity, identity_len) == 0)
 +                      break;
 +              user = user->next;
 +      }
 +
 +#ifdef CONFIG_SQLITE
 +      if (user == NULL && conf->eap_user_sqlite) {
 +              return eap_user_sqlite_get(hapd, identity, identity_len,
 +                                         phase2);
 +      }
 +#endif /* CONFIG_SQLITE */
 +
 +      return user;
 +}
index 3e4e16b4f396a4ca0d04e9e12f79d7c696d03684,0000000000000000000000000000000000000000..c09c17a446963b2537c2fdfbc073febd60659510
mode 100644,000000..100644
--- /dev/null
@@@ -1,2725 -1,0 +1,2962 @@@
-       u8 ssid[HOSTAPD_MAX_SSID_LEN + 1];
 +/*
 + * hostapd / Initialization and configuration
 + * 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/includes.h"
 +
 +#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 "eap_server/tncs.h"
 +#include "eapol_auth/eapol_auth_sm.h"
 +#include "eapol_auth/eapol_auth_sm_i.h"
++#include "fst/fst.h"
 +#include "hostapd.h"
 +#include "authsrv.h"
 +#include "sta_info.h"
 +#include "accounting.h"
 +#include "ap_list.h"
 +#include "beacon.h"
 +#include "iapp.h"
 +#include "ieee802_1x.h"
 +#include "ieee802_11_auth.h"
 +#include "vlan_init.h"
 +#include "wpa_auth.h"
 +#include "wps_hostapd.h"
 +#include "hw_features.h"
 +#include "wpa_auth_glue.h"
 +#include "ap_drv_ops.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);
 +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,
 +                             int (*cb)(struct hostapd_iface *iface,
 +                                       void *ctx), void *ctx)
 +{
 +      size_t i;
 +      int ret;
 +
 +      for (i = 0; i < interfaces->count; i++) {
 +              ret = cb(interfaces->iface[i], ctx);
 +              if (ret)
 +                      return ret;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +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");
 +      }
 +
 +      if (hapd->conf->ieee802_1x || hapd->conf->wpa)
 +              hostapd_set_drv_ieee8021x(hapd, hapd->conf->iface, 1);
 +      else
 +              hostapd_set_drv_ieee8021x(hapd, hapd->conf->iface, 0);
 +
 +      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);
 +      } else if (hapd->conf->wpa) {
 +              const u8 *wpa_ie;
 +              size_t wpa_ie_len;
 +              hostapd_reconfig_wpa(hapd);
 +              wpa_ie = wpa_auth_get_wpa_ie(hapd->wpa_auth, &wpa_ie_len);
 +              if (hostapd_set_generic_elem(hapd, wpa_ie, wpa_ie_len))
 +                      wpa_printf(MSG_ERROR, "Failed to configure WPA IE for "
 +                                 "the kernel driver.");
 +      } else if (hapd->wpa_auth) {
 +              wpa_deinit(hapd->wpa_auth);
 +              hapd->wpa_auth = NULL;
 +              hostapd_set_privacy(hapd, 0);
 +              hostapd_setup_encryption(hapd->conf->iface, hapd);
 +              hostapd_set_generic_elem(hapd, (u8 *) "", 0);
 +      }
 +
 +      ieee802_11_set_beacon(hapd);
 +      hostapd_update_wps(hapd);
 +
 +      if (hapd->conf->ssid.ssid_set &&
 +          hostapd_set_ssid(hapd, hapd->conf->ssid.ssid,
 +                           hapd->conf->ssid.ssid_len)) {
 +              wpa_printf(MSG_ERROR, "Could not set SSID for kernel driver");
 +              /* try to continue */
 +      }
 +      wpa_printf(MSG_DEBUG, "Reconfigured interface %s", hapd->conf->iface);
 +}
 +
 +
 +static void hostapd_clear_old(struct hostapd_iface *iface)
 +{
 +      size_t j;
 +
 +      /*
 +       * Deauthenticate all stations since the new configuration may not
 +       * allow them to use the BSS anymore.
 +       */
 +      for (j = 0; j < iface->num_bss; j++) {
 +              hostapd_flush_old_stations(iface->bss[j],
 +                                         WLAN_REASON_PREV_AUTH_NOT_VALID);
 +              hostapd_broadcast_wep_clear(iface->bss[j]);
 +
 +#ifndef CONFIG_NO_RADIUS
 +              /* TODO: update dynamic data based on changed configuration
 +               * items (e.g., open/close sockets, etc.) */
 +              radius_client_flush(iface->bss[j]->radius, 0);
 +#endif /* CONFIG_NO_RADIUS */
 +      }
 +}
 +
 +
 +int hostapd_reload_config(struct hostapd_iface *iface)
 +{
 +      struct hostapd_data *hapd = iface->bss[0];
 +      struct hostapd_config *newconf, *oldconf;
 +      size_t j;
 +
 +      if (iface->config_fname == NULL) {
 +              /* Only in-memory config in use - assume it has been updated */
 +              hostapd_clear_old(iface);
 +              for (j = 0; j < iface->num_bss; j++)
 +                      hostapd_reload_bss(iface->bss[j]);
 +              return 0;
 +      }
 +
 +      if (iface->interfaces == NULL ||
 +          iface->interfaces->config_read_cb == NULL)
 +              return -1;
 +      newconf = iface->interfaces->config_read_cb(iface->config_fname);
 +      if (newconf == NULL)
 +              return -1;
 +
 +      hostapd_clear_old(iface);
 +
 +      oldconf = hapd->iconf;
 +      iface->conf = newconf;
 +
 +      for (j = 0; j < iface->num_bss; j++) {
 +              hapd = iface->bss[j];
 +              hapd->iconf = newconf;
 +              hapd->iconf->channel = oldconf->channel;
++              hapd->iconf->acs = oldconf->acs;
 +              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);
 +      }
 +
 +      hostapd_config_free(oldconf);
 +
 +
 +      return 0;
 +}
 +
 +
 +static void hostapd_broadcast_key_clear_iface(struct hostapd_data *hapd,
 +                                            char *ifname)
 +{
 +      int i;
 +
 +      for (i = 0; i < NUM_WEP_KEYS; i++) {
 +              if (hostapd_drv_set_key(ifname, hapd, WPA_ALG_NONE, NULL, i,
 +                                      0, NULL, 0, NULL, 0)) {
 +                      wpa_printf(MSG_DEBUG, "Failed to clear default "
 +                                 "encryption keys (ifname=%s keyidx=%d)",
 +                                 ifname, i);
 +              }
 +      }
 +#ifdef CONFIG_IEEE80211W
 +      if (hapd->conf->ieee80211w) {
 +              for (i = NUM_WEP_KEYS; i < NUM_WEP_KEYS + 2; i++) {
 +                      if (hostapd_drv_set_key(ifname, hapd, WPA_ALG_NONE,
 +                                              NULL, i, 0, NULL,
 +                                              0, NULL, 0)) {
 +                              wpa_printf(MSG_DEBUG, "Failed to clear "
 +                                         "default mgmt encryption keys "
 +                                         "(ifname=%s keyidx=%d)", ifname, i);
 +                      }
 +              }
 +      }
 +#endif /* CONFIG_IEEE80211W */
 +}
 +
 +
 +static int hostapd_broadcast_wep_clear(struct hostapd_data *hapd)
 +{
 +      hostapd_broadcast_key_clear_iface(hapd, hapd->conf->iface);
 +      return 0;
 +}
 +
 +
 +static int hostapd_broadcast_wep_set(struct hostapd_data *hapd)
 +{
 +      int errors = 0, idx;
 +      struct hostapd_ssid *ssid = &hapd->conf->ssid;
 +
 +      idx = ssid->wep.idx;
 +      if (ssid->wep.default_len &&
 +          hostapd_drv_set_key(hapd->conf->iface,
 +                              hapd, WPA_ALG_WEP, broadcast_ether_addr, idx,
 +                              1, NULL, 0, ssid->wep.key[idx],
 +                              ssid->wep.len[idx])) {
 +              wpa_printf(MSG_WARNING, "Could not set WEP encryption.");
 +              errors++;
 +      }
 +
 +      return errors;
 +}
 +
 +
 +static void hostapd_free_hapd_data(struct hostapd_data *hapd)
 +{
 +      os_free(hapd->probereq_cb);
 +      hapd->probereq_cb = NULL;
++      hapd->num_probereq_cb = 0;
 +
 +#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);
 +      hostapd_deinit_wpa(hapd);
 +      vlan_deinit(hapd);
 +      hostapd_acl_deinit(hapd);
 +#ifndef CONFIG_NO_RADIUS
 +      radius_client_deinit(hapd->radius);
 +      hapd->radius = NULL;
 +      radius_das_deinit(hapd->radius_das);
 +      hapd->radius_das = NULL;
 +#endif /* CONFIG_NO_RADIUS */
 +
 +      hostapd_deinit_wps(hapd);
 +
 +      authsrv_deinit(hapd);
 +
 +      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;
 +              }
 +      }
 +
 +      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
 +      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 */
 +}
 +
 +
 +/**
 + * hostapd_cleanup - Per-BSS cleanup (deinitialization)
 + * @hapd: Pointer to BSS data
 + *
 + * This function is used to free all per-BSS data structures and resources.
 + * 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);
 +      hostapd_free_hapd_data(hapd);
 +}
 +
 +
++static void sta_track_deinit(struct hostapd_iface *iface)
++{
++      struct hostapd_sta_info *info;
++
++      if (!iface->num_sta_seen)
++              return;
++
++      while ((info = dl_list_first(&iface->sta_seen, struct hostapd_sta_info,
++                                   list))) {
++              dl_list_del(&info->list);
++              iface->num_sta_seen--;
++              os_free(info);
++      }
++}
++
++
 +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);
 +      iface->current_rates = NULL;
 +      os_free(iface->basic_rates);
 +      iface->basic_rates = NULL;
 +      ap_list_deinit(iface);
++      sta_track_deinit(iface);
 +}
 +
 +
 +/**
 + * hostapd_cleanup_iface - Complete per-interface cleanup
 + * @iface: Pointer to interface data
 + *
 + * This function is called after per-BSS data structures are deinitialized
 + * with hostapd_cleanup().
 + */
 +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 && !hapd->iface->driver_ap_teardown) {
 +              hostapd_set_privacy(hapd, 0);
 +              hostapd_broadcast_wep_clear(hapd);
 +      }
 +}
 +
 +
 +static int hostapd_setup_encryption(char *iface, struct hostapd_data *hapd)
 +{
 +      int i;
 +
 +      hostapd_broadcast_wep_set(hapd);
 +
 +      if (hapd->conf->ssid.wep.default_len) {
 +              hostapd_set_privacy(hapd, 1);
 +              return 0;
 +      }
 +
 +      /*
 +       * When IEEE 802.1X is not enabled, the driver may need to know how to
 +       * set authentication algorithms for static WEP.
 +       */
 +      hostapd_drv_set_authmode(hapd, hapd->conf->auth_algs);
 +
 +      for (i = 0; i < 4; i++) {
 +              if (hapd->conf->ssid.wep.key[i] &&
 +                  hostapd_drv_set_key(iface, hapd, WPA_ALG_WEP, NULL, i,
 +                                      i == hapd->conf->ssid.wep.idx, NULL, 0,
 +                                      hapd->conf->ssid.wep.key[i],
 +                                      hapd->conf->ssid.wep.len[i])) {
 +                      wpa_printf(MSG_WARNING, "Could not set WEP "
 +                                 "encryption.");
 +                      return -1;
 +              }
 +              if (hapd->conf->ssid.wep.key[i] &&
 +                  i == hapd->conf->ssid.wep.idx)
 +                      hostapd_set_privacy(hapd, 1);
 +      }
 +
 +      return 0;
 +}
 +
 +
 +static int hostapd_flush_old_stations(struct hostapd_data *hapd, u16 reason)
 +{
 +      int ret = 0;
 +      u8 addr[ETH_ALEN];
 +
 +      if (hostapd_drv_none(hapd) || hapd->drv_priv == NULL)
 +              return 0;
 +
 +      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);
 +      hostapd_drv_sta_deauth(hapd, addr, reason);
 +      hostapd_free_stas(hapd);
 +
 +      return ret;
 +}
 +
 +
 +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
 + * Returns: 0 on success, -1 on failure
 + *
 + * This function is used to validate that the configured BSSIDs are valid.
 + */
 +static int hostapd_validate_bssid_configuration(struct hostapd_iface *iface)
 +{
 +      u8 mask[ETH_ALEN] = { 0 };
 +      struct hostapd_data *hapd = iface->bss[0];
 +      unsigned int i = iface->conf->num_bss, bits = 0, j;
 +      int auto_addr = 0;
 +
 +      if (hostapd_drv_none(hapd))
 +              return 0;
 +
 +      /* Generate BSSID mask that is large enough to cover the BSSIDs. */
 +
 +      /* Determine the bits necessary to cover the number of BSSIDs. */
 +      for (i--; i; i >>= 1)
 +              bits++;
 +
 +      /* 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 (j)
 +                              auto_addr++;
 +                      continue;
 +              }
 +
 +              for (i = 0; i < ETH_ALEN; i++) {
 +                      mask[i] |=
 +                              iface->conf->bss[j]->bssid[i] ^
 +                              hapd->own_addr[i];
 +              }
 +      }
 +
 +      if (!auto_addr)
 +              goto skip_mask_ext;
 +
 +      for (i = 0; i < ETH_ALEN && mask[i] == 0; i++)
 +              ;
 +      j = 0;
 +      if (i < ETH_ALEN) {
 +              j = (5 - i) * 8;
 +
 +              while (mask[i] != 0) {
 +                      mask[i] >>= 1;
 +                      j++;
 +              }
 +      }
 +
 +      if (bits < j)
 +              bits = j;
 +
 +      if (bits > 40) {
 +              wpa_printf(MSG_ERROR, "Too many bits in the BSSID mask (%u)",
 +                         bits);
 +              return -1;
 +      }
 +
 +      os_memset(mask, 0xff, ETH_ALEN);
 +      j = bits / 8;
 +      for (i = 5; i > 5 - j; i--)
 +              mask[i] = 0;
 +      j = bits % 8;
 +      while (j--)
 +              mask[i] <<= 1;
 +
 +skip_mask_ext:
 +      wpa_printf(MSG_DEBUG, "BSS count %lu, BSSID mask " MACSTR " (%d bits)",
 +                 (unsigned long) iface->conf->num_bss, MAC2STR(mask), bits);
 +
 +      if (!auto_addr)
 +              return 0;
 +
 +      for (i = 0; i < ETH_ALEN; i++) {
 +              if ((hapd->own_addr[i] & mask[i]) != hapd->own_addr[i]) {
 +                      wpa_printf(MSG_ERROR, "Invalid BSSID mask " MACSTR
 +                                 " for start address " MACSTR ".",
 +                                 MAC2STR(mask), MAC2STR(hapd->own_addr));
 +                      wpa_printf(MSG_ERROR, "Start address must be the "
 +                                 "first address in the block (i.e., addr "
 +                                 "AND mask == addr).");
 +                      return -1;
 +              }
 +      }
 +
 +      return 0;
 +}
 +
 +
 +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) {
 +                      return 1;
 +              }
 +      }
 +
 +      return 0;
 +}
 +
 +
 +#ifndef CONFIG_NO_RADIUS
 +
 +static int hostapd_das_nas_mismatch(struct hostapd_data *hapd,
 +                                  struct radius_das_attrs *attr)
 +{
 +      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,
 +                                            int *multi)
 +{
 +      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) {
 +              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;
 +
 +              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)
 +                              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 (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 ||
 +                          os_memcmp(wpabuf_head(cui), attr->cui,
 +                                    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 (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 ||
 +                          os_memcmp(identity, attr->user_name, identity_len)
 +                          != 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 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);
 +}
 +
 +
 +static enum radius_das_res
 +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, &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);
 +      ap_sta_deauthenticate(hapd, sta, WLAN_REASON_PREV_AUTH_NOT_VALID);
 +
 +      return RADIUS_DAS_SUCCESS;
 +}
 +
 +#endif /* CONFIG_NO_RADIUS */
 +
 +
 +/**
 + * hostapd_setup_bss - Per-BSS setup (initialization)
 + * @hapd: Pointer to BSS data
 + * @first: Whether this BSS is the first BSS of an interface; -1 = not first,
 + *    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
 + * initialized. Most of the modules that are initialized here will be
 + * deinitialized in hostapd_cleanup().
 + */
 +static int hostapd_setup_bss(struct hostapd_data *hapd, int first)
 +{
 +      struct hostapd_bss_config *conf = hapd->conf;
-                    const char *ctrl_iface)
++      u8 ssid[SSID_MAX_LEN + 1];
 +      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);
 +
 +#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, 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", conf->iface);
 +                              return -1;
 +                      }
 +              }
 +
 +              hapd->interface_added = 1;
 +              if (hostapd_if_add(hapd->iface->bss[0], WPA_IF_AP_BSS,
 +                                 conf->iface, hapd->own_addr, hapd,
 +                                 &hapd->drv_priv, force_ifname, if_addr,
 +                                 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;
 +              }
 +      }
 +
 +      if (conf->wmm_enabled < 0)
 +              conf->wmm_enabled = hapd->iconf->ieee80211n;
 +
 +#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(conf->iface, hapd))
 +              return -1;
 +
 +      /*
 +       * Fetch the SSID from the system and use it or,
 +       * if one was specified in the config file, verify they
 +       * match.
 +       */
 +      ssid_len = hostapd_get_ssid(hapd, ssid, sizeof(ssid));
 +      if (ssid_len < 0) {
 +              wpa_printf(MSG_ERROR, "Could not read SSID from system");
 +              return -1;
 +      }
 +      if (conf->ssid.ssid_set) {
 +              /*
 +               * If SSID is specified in the config file and it differs
 +               * from what is being used then force installation of the
 +               * new SSID.
 +               */
 +              set_ssid = (conf->ssid.ssid_len != (size_t) ssid_len ||
 +                          os_memcmp(conf->ssid.ssid, ssid, ssid_len) != 0);
 +      } else {
 +              /*
 +               * No SSID in the config file; just use the one we got
 +               * from the system.
 +               */
 +              set_ssid = 0;
 +              conf->ssid.ssid_len = ssid_len;
 +              os_memcpy(conf->ssid.ssid, ssid, conf->ssid.ssid_len);
 +      }
 +
 +      if (!hostapd_drv_none(hapd)) {
 +              wpa_printf(MSG_ERROR, "Using interface %s with hwaddr " MACSTR
 +                         " and ssid \"%s\"",
 +                         conf->iface, MAC2STR(hapd->own_addr),
 +                         wpa_ssid_txt(conf->ssid.ssid, conf->ssid.ssid_len));
 +      }
 +
 +      if (hostapd_setup_wpa_psk(conf)) {
 +              wpa_printf(MSG_ERROR, "WPA-PSK setup failed.");
 +              return -1;
 +      }
 +
 +      /* Set SSID for the kernel driver (to be used in beacon and probe
 +       * response frames) */
 +      if (set_ssid && hostapd_set_ssid(hapd, conf->ssid.ssid,
 +                                       conf->ssid.ssid_len)) {
 +              wpa_printf(MSG_ERROR, "Could not set SSID for kernel driver");
 +              return -1;
 +      }
 +
 +      if (wpa_debug_level <= MSG_MSGDUMP)
 +              conf->radius->msg_dumps = 1;
 +#ifndef CONFIG_NO_RADIUS
 +      hapd->radius = radius_client_init(hapd, conf->radius);
 +      if (hapd->radius == NULL) {
 +              wpa_printf(MSG_ERROR, "RADIUS client initialization failed.");
 +              return -1;
 +      }
 +
 +      if (conf->radius_das_port) {
 +              struct radius_das_conf das_conf;
 +              os_memset(&das_conf, 0, sizeof(das_conf));
 +              das_conf.port = conf->radius_das_port;
 +              das_conf.shared_secret = conf->radius_das_shared_secret;
 +              das_conf.shared_secret_len =
 +                      conf->radius_das_shared_secret_len;
 +              das_conf.client_addr = &conf->radius_das_client_addr;
 +              das_conf.time_window = conf->radius_das_time_window;
 +              das_conf.require_event_timestamp =
 +                      conf->radius_das_require_event_timestamp;
 +              das_conf.ctx = hapd;
 +              das_conf.disconnect = hostapd_das_disconnect;
 +              hapd->radius_das = radius_das_init(&das_conf);
 +              if (hapd->radius_das == NULL) {
 +                      wpa_printf(MSG_ERROR, "RADIUS DAS initialization "
 +                                 "failed.");
 +                      return -1;
 +              }
 +      }
 +#endif /* CONFIG_NO_RADIUS */
 +
 +      if (hostapd_acl_init(hapd)) {
 +              wpa_printf(MSG_ERROR, "ACL initialization failed.");
 +              return -1;
 +      }
 +      if (hostapd_init_wps(hapd, conf))
 +              return -1;
 +
 +      if (authsrv_init(hapd) < 0)
 +              return -1;
 +
 +      if (ieee802_1x_init(hapd)) {
 +              wpa_printf(MSG_ERROR, "IEEE 802.1X initialization failed.");
 +              return -1;
 +      }
 +
 +      if ((conf->wpa || conf->osen) && hostapd_setup_wpa(hapd))
 +              return -1;
 +
 +      if (accounting_init(hapd)) {
 +              wpa_printf(MSG_ERROR, "Accounting initialization failed.");
 +              return -1;
 +      }
 +
 +      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;
 +      }
 +
 +#ifdef CONFIG_INTERWORKING
 +      if (gas_serv_init(hapd)) {
 +              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 (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;
 +      }
 +
 +      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;
 +
 +      if (hapd->driver && hapd->driver->set_operstate)
 +              hapd->driver->set_operstate(hapd->drv_priv, 1);
 +
 +      return 0;
 +}
 +
 +
 +static void hostapd_tx_queue_params(struct hostapd_iface *iface)
 +{
 +      struct hostapd_data *hapd = iface->bss[0];
 +      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];
 +
 +              if (hostapd_set_tx_queue_params(hapd, i, p->aifs, p->cwmin,
 +                                              p->cwmax, p->burst)) {
 +                      wpa_printf(MSG_DEBUG, "Failed to set TX queue "
 +                                 "parameters for queue %d.", i);
 +                      /* Continue anyway */
 +              }
 +      }
 +}
 +
 +
 +static int hostapd_set_acl_list(struct hostapd_data *hapd,
 +                              struct mac_acl_entry *mac_acl,
 +                              int n_entries, u8 accept_acl)
 +{
 +      struct hostapd_acl_params *acl_params;
 +      int i, err;
 +
 +      acl_params = os_zalloc(sizeof(*acl_params) +
 +                             (n_entries * sizeof(acl_params->mac_acl[0])));
 +      if (!acl_params)
 +              return -ENOMEM;
 +
 +      for (i = 0; i < n_entries; i++)
 +              os_memcpy(acl_params->mac_acl[i].addr, mac_acl[i].addr,
 +                        ETH_ALEN);
 +
 +      acl_params->acl_policy = accept_acl;
 +      acl_params->num_mac_acl = n_entries;
 +
 +      err = hostapd_drv_set_acl(hapd, acl_params);
 +
 +      os_free(acl_params);
 +
 +      return err;
 +}
 +
 +
 +static void hostapd_set_acl(struct hostapd_data *hapd)
 +{
 +      struct hostapd_config *conf = hapd->iconf;
 +      int err;
 +      u8 accept_acl;
 +
 +      if (hapd->iface->drv_max_acl_mac_addrs == 0)
 +              return;
 +
 +      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[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);
 +}
 +
 +
 +static int setup_interface(struct hostapd_iface *iface)
 +{
 +      struct hostapd_data *hapd = iface->bss[0];
 +      size_t i;
 +
 +      /*
 +       * 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
 +       * driver interface.
 +       */
 +      for (i = 1; i < iface->num_bss; i++) {
 +              iface->bss[i]->driver = hapd->driver;
 +              iface->bss[i]->drv_priv = hapd->drv_priv;
 +      }
 +
 +      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. */
 +      } else {
 +              int ret = hostapd_select_hw_mode(iface);
 +              if (ret < 0) {
 +                      wpa_printf(MSG_ERROR, "Could not select hw_mode and "
 +                                 "channel. (%d)", ret);
 +                      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)
 +                      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;
 +}
 +
 +
++#ifdef CONFIG_FST
++
++static const u8 * fst_hostapd_get_bssid_cb(void *ctx)
++{
++      struct hostapd_data *hapd = ctx;
++
++      return hapd->own_addr;
++}
++
++
++static void fst_hostapd_get_channel_info_cb(void *ctx,
++                                          enum hostapd_hw_mode *hw_mode,
++                                          u8 *channel)
++{
++      struct hostapd_data *hapd = ctx;
++
++      *hw_mode = ieee80211_freq_to_chan(hapd->iface->freq, channel);
++}
++
++
++static void fst_hostapd_set_ies_cb(void *ctx, const struct wpabuf *fst_ies)
++{
++      struct hostapd_data *hapd = ctx;
++
++      if (hapd->iface->fst_ies != fst_ies) {
++              hapd->iface->fst_ies = fst_ies;
++              if (ieee802_11_set_beacon(hapd))
++                      wpa_printf(MSG_WARNING, "FST: Cannot set beacon");
++      }
++}
++
++
++static int fst_hostapd_send_action_cb(void *ctx, const u8 *da,
++                                    struct wpabuf *buf)
++{
++      struct hostapd_data *hapd = ctx;
++
++      return hostapd_drv_send_action(hapd, hapd->iface->freq, 0, da,
++                                     wpabuf_head(buf), wpabuf_len(buf));
++}
++
++
++static const struct wpabuf * fst_hostapd_get_mb_ie_cb(void *ctx, const u8 *addr)
++{
++      struct hostapd_data *hapd = ctx;
++      struct sta_info *sta = ap_get_sta(hapd, addr);
++
++      return sta ? sta->mb_ies : NULL;
++}
++
++
++static void fst_hostapd_update_mb_ie_cb(void *ctx, const u8 *addr,
++                                      const u8 *buf, size_t size)
++{
++      struct hostapd_data *hapd = ctx;
++      struct sta_info *sta = ap_get_sta(hapd, addr);
++
++      if (sta) {
++              struct mb_ies_info info;
++
++              if (!mb_ies_info_by_ies(&info, buf, size)) {
++                      wpabuf_free(sta->mb_ies);
++                      sta->mb_ies = mb_ies_by_info(&info);
++              }
++      }
++}
++
++
++static const u8 * fst_hostapd_get_sta(struct fst_get_peer_ctx **get_ctx,
++                                    Boolean mb_only)
++{
++      struct sta_info *s = (struct sta_info *) *get_ctx;
++
++      if (mb_only) {
++              for (; s && !s->mb_ies; s = s->next)
++                      ;
++      }
++
++      if (s) {
++              *get_ctx = (struct fst_get_peer_ctx *) s->next;
++
++              return s->addr;
++      }
++
++      *get_ctx = NULL;
++      return NULL;
++}
++
++
++static const u8 * fst_hostapd_get_peer_first(void *ctx,
++                                           struct fst_get_peer_ctx **get_ctx,
++                                           Boolean mb_only)
++{
++      struct hostapd_data *hapd = ctx;
++
++      *get_ctx = (struct fst_get_peer_ctx *) hapd->sta_list;
++
++      return fst_hostapd_get_sta(get_ctx, mb_only);
++}
++
++
++static const u8 * fst_hostapd_get_peer_next(void *ctx,
++                                          struct fst_get_peer_ctx **get_ctx,
++                                          Boolean mb_only)
++{
++      return fst_hostapd_get_sta(get_ctx, mb_only);
++}
++
++
++void fst_hostapd_fill_iface_obj(struct hostapd_data *hapd,
++                              struct fst_wpa_obj *iface_obj)
++{
++      iface_obj->ctx = hapd;
++      iface_obj->get_bssid = fst_hostapd_get_bssid_cb;
++      iface_obj->get_channel_info = fst_hostapd_get_channel_info_cb;
++      iface_obj->set_ies = fst_hostapd_set_ies_cb;
++      iface_obj->send_action = fst_hostapd_send_action_cb;
++      iface_obj->get_mb_ie = fst_hostapd_get_mb_ie_cb;
++      iface_obj->update_mb_ie = fst_hostapd_update_mb_ie_cb;
++      iface_obj->get_peer_first = fst_hostapd_get_peer_first;
++      iface_obj->get_peer_next = fst_hostapd_get_peer_next;
++}
++
++#endif /* CONFIG_FST */
++
++
 +/**
 + * 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)
 +              goto fail;
 +
 +      wpa_printf(MSG_DEBUG, "Completing interface initialization");
 +      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(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 (!delay_apply_cfg &&
 +                  hostapd_set_freq(hapd, hapd->iconf->hw_mode, iface->freq,
 +                                   hapd->iconf->channel,
 +                                   hapd->iconf->ieee80211n,
 +                                   hapd->iconf->ieee80211ac,
 +                                   hapd->iconf->secondary_channel,
 +                                   hapd->iconf->vht_oper_chwidth,
 +                                   hapd->iconf->vht_oper_centr_freq_seg0_idx,
 +                                   hapd->iconf->vht_oper_centr_freq_seg1_idx)) {
 +                      wpa_printf(MSG_ERROR, "Could not set channel for "
 +                                 "kernel driver");
 +                      goto fail;
 +              }
 +      }
 +
 +      if (iface->current_mode) {
 +              if (hostapd_prepare_rates(iface, iface->current_mode)) {
 +                      wpa_printf(MSG_ERROR, "Failed to prepare rates "
 +                                 "table.");
 +                      hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
 +                                     HOSTAPD_LEVEL_WARNING,
 +                                     "Failed to prepare rates table.");
 +                      goto fail;
 +              }
 +      }
 +
 +      if (hapd->iconf->rts_threshold > -1 &&
 +          hostapd_set_rts(hapd, hapd->iconf->rts_threshold)) {
 +              wpa_printf(MSG_ERROR, "Could not set RTS threshold for "
 +                         "kernel driver");
 +              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");
 +              goto fail;
 +      }
 +
 +      prev_addr = hapd->own_addr;
 +
 +      for (j = 0; j < iface->num_bss; j++) {
 +              hapd = iface->bss[j];
 +              if (j)
 +                      os_memcpy(hapd->own_addr, prev_addr, ETH_ALEN);
 +              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);
 +
 +      ap_list_init(iface);
++      dl_list_init(&iface->sta_seen);
 +
 +      hostapd_set_acl(hapd);
 +
 +      if (hostapd_driver_commit(hapd) < 0) {
 +              wpa_printf(MSG_ERROR, "%s: Failed to commit driver "
 +                         "configuration", __func__);
 +              goto fail;
 +      }
 +
 +      /*
 +       * WPS UPnP module can be initialized only when the "upnp_iface" is up.
 +       * If "interface" and "upnp_iface" are the same (e.g., non-bridge
 +       * mode), the interface is up only after driver_commit, so initialize
 +       * WPS after driver_commit.
 +       */
 +      for (j = 0; j < iface->num_bss; j++) {
 +              if (hostapd_init_wps_complete(iface->bss[j]))
 +                      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 */
++
++#ifdef CONFIG_FST
++      if (hapd->iconf->fst_cfg.group_id[0]) {
++              struct fst_wpa_obj iface_obj;
++
++              fst_hostapd_fill_iface_obj(hapd, &iface_obj);
++              iface->fst = fst_attach(hapd->conf->iface, hapd->own_addr,
++                                      &iface_obj, &hapd->iconf->fst_cfg);
++              if (!iface->fst) {
++                      wpa_printf(MSG_ERROR, "Could not attach to FST %s",
++                                 hapd->iconf->fst_cfg.group_id);
++                      goto fail;
++              }
++      }
++#endif /* CONFIG_FST */
++
 +      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);
++#ifdef CONFIG_FST
++      if (iface->fst) {
++              fst_detach(iface->fst);
++              iface->fst = NULL;
++      }
++#endif /* CONFIG_FST */
 +      if (iface->interfaces && iface->interfaces->terminate_on_error)
 +              eloop_terminate();
 +      return -1;
 +}
 +
 +
 +/**
 + * hostapd_setup_interface - Setup of an interface
 + * @iface: Pointer to interface data.
 + * Returns: 0 on success, -1 on failure
 + *
 + * Initializes the driver interface, validates the configuration,
 + * 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)
 +{
 +      int ret;
 +
 +      ret = setup_interface(iface);
 +      if (ret) {
 +              wpa_printf(MSG_ERROR, "%s: Unable to setup interface.",
 +                         iface->bss[0]->conf->iface);
 +              return -1;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +/**
 + * hostapd_alloc_bss_data - Allocate and initialize per-BSS data
 + * @hapd_iface: Pointer to interface data
 + * @conf: Pointer to per-interface configuration
 + * @bss: Pointer to per-BSS configuration for this BSS
 + * Returns: Pointer to allocated BSS data
 + *
 + * This function is used to allocate per-BSS data structure. This data will be
 + * freed after hostapd_cleanup() is called for it during interface
 + * deinitialization.
 + */
 +struct hostapd_data *
 +hostapd_alloc_bss_data(struct hostapd_iface *hapd_iface,
 +                     struct hostapd_config *conf,
 +                     struct hostapd_bss_config *bss)
 +{
 +      struct hostapd_data *hapd;
 +
 +      hapd = os_zalloc(sizeof(*hapd));
 +      if (hapd == NULL)
 +              return NULL;
 +
 +      hapd->new_assoc_sta_cb = hostapd_new_assoc_sta;
 +      hapd->iconf = conf;
 +      hapd->conf = bss;
 +      hapd->iface = hapd_iface;
 +      hapd->driver = hapd->iconf->driver;
 +      hapd->ctrl_sock = -1;
 +
 +      return hapd;
 +}
 +
 +
 +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)
 +{
 +      int j;
 +
 +      wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface);
 +      if (iface == NULL)
 +              return;
 +
 +      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;
 +
++#ifdef CONFIG_FST
++      if (iface->fst) {
++              fst_detach(iface->fst);
++              iface->fst = NULL;
++      }
++#endif /* CONFIG_FST */
++
 +      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;
 +      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);
 +}
 +
 +
 +/**
 + * 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);
 +      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);
 +              return -1;
 +      }
 +
 +      wpa_printf(MSG_DEBUG, "Enable interface %s",
 +                 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))
 +              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;
 +}
 +
 +
 +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_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;
 +}
 +
 +
 +int hostapd_disable_iface(struct hostapd_iface *hapd_iface)
 +{
 +      size_t j;
 +      const struct wpa_driver_ops *driver;
 +      void *drv_priv;
 +
 +      if (hapd_iface == NULL)
 +              return -1;
 +
 +      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;
 +
 +      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_bss_deinit_no_free(hapd);
 +              hostapd_free_hapd_data(hapd);
 +      }
 +
 +      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);
 +
 +      wpa_printf(MSG_DEBUG, "Interface %s disabled",
 +                 hapd_iface->bss[0]->conf->iface);
 +      hostapd_set_state(hapd_iface, HAPD_IFACE_DISABLED);
 +      return 0;
 +}
 +
 +
 +static struct hostapd_iface *
 +hostapd_iface_alloc(struct hapd_interfaces *interfaces)
 +{
 +      struct hostapd_iface **iface, *hapd_iface;
 +
 +      iface = os_realloc_array(interfaces->iface, interfaces->count + 1,
 +                               sizeof(struct hostapd_iface *));
 +      if (iface == NULL)
 +              return NULL;
 +      interfaces->iface = iface;
 +      hapd_iface = interfaces->iface[interfaces->count] =
 +              os_zalloc(sizeof(*hapd_iface));
 +      if (hapd_iface == NULL) {
 +              wpa_printf(MSG_ERROR, "%s: Failed to allocate memory for "
 +                         "the interface", __func__);
 +              return NULL;
 +      }
 +      interfaces->count++;
 +      hapd_iface->interfaces = interfaces;
 +
 +      return hapd_iface;
 +}
 +
 +
 +static struct hostapd_config *
 +hostapd_config_alloc(struct hapd_interfaces *interfaces, const char *ifname,
-       } else
-               conf = hostapd_config_alloc(interfaces, buf, ptr);
++                   const char *ctrl_iface, const char *driver)
 +{
 +      struct hostapd_bss_config *bss;
 +      struct hostapd_config *conf;
 +
 +      /* Allocates memory for bss and conf */
 +      conf = hostapd_config_defaults();
 +      if (conf == NULL) {
 +               wpa_printf(MSG_ERROR, "%s: Failed to allocate memory for "
 +                              "configuration", __func__);
 +              return NULL;
 +      }
 +
++      if (driver) {
++              int j;
++
++              for (j = 0; wpa_drivers[j]; j++) {
++                      if (os_strcmp(driver, wpa_drivers[j]->name) == 0) {
++                              conf->driver = wpa_drivers[j];
++                              goto skip;
++                      }
++              }
++
++              wpa_printf(MSG_ERROR,
++                         "Invalid/unknown driver '%s' - registering the default driver",
++                         driver);
++      }
++
 +      conf->driver = wpa_drivers[0];
 +      if (conf->driver == NULL) {
 +              wpa_printf(MSG_ERROR, "No driver wrappers registered!");
 +              hostapd_config_free(conf);
 +              return NULL;
 +      }
 +
++skip:
 +      bss = conf->last_bss = conf->bss[0];
 +
 +      os_strlcpy(bss->iface, ifname, sizeof(bss->iface));
 +      bss->ctrl_interface = os_strdup(ctrl_iface);
 +      if (bss->ctrl_interface == NULL) {
 +              hostapd_config_free(conf);
 +              return NULL;
 +      }
 +
 +      /* Reading configuration file skipped, will be done in SET!
 +       * From reading the configuration till the end has to be done in
 +       * SET
 +       */
 +      return conf;
 +}
 +
 +
 +static int hostapd_data_alloc(struct hostapd_iface *hapd_iface,
 +                            struct hostapd_config *conf)
 +{
 +      size_t i;
 +      struct hostapd_data *hapd;
 +
 +      hapd_iface->bss = os_calloc(conf->num_bss,
 +                                  sizeof(struct hostapd_data *));
 +      if (hapd_iface->bss == 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) {
 +                      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->conf = conf;
 +      hapd_iface->num_bss = conf->num_bss;
 +
 +      return 0;
 +}
 +
 +
 +int hostapd_add_iface(struct hapd_interfaces *interfaces, char *buf)
 +{
 +      struct hostapd_config *conf = NULL;
 +      struct hostapd_iface *hapd_iface = NULL, *new_iface = NULL;
 +      struct hostapd_data *hapd;
 +      char *ptr;
 +      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,
 +                             buf)) {
 +                      wpa_printf(MSG_INFO, "Cannot add interface - it "
 +                                 "already exists");
 +                      return -1;
 +              }
 +      }
 +
 +      hapd_iface = hostapd_iface_alloc(interfaces);
 +      if (hapd_iface == NULL) {
 +              wpa_printf(MSG_ERROR, "%s: Failed to allocate memory "
 +                         "for interface", __func__);
 +              goto fail;
 +      }
 +      new_iface = hapd_iface;
 +
 +      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 {
++              char *driver = os_strchr(ptr, ' ');
++
++              if (driver)
++                      *driver++ = '\0';
++              conf = hostapd_config_alloc(interfaces, buf, ptr, driver);
++      }
++
 +      if (conf == NULL || conf->bss == NULL) {
 +              wpa_printf(MSG_ERROR, "%s: Failed to allocate memory "
 +                         "for configuration", __func__);
 +              goto fail;
 +      }
 +
 +      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 (start_ctrl_iface(hapd_iface) < 0)
 +              goto fail;
 +
 +      wpa_printf(MSG_INFO, "Add interface '%s'",
 +                 hapd_iface->conf->bss[0]->iface);
 +
 +      return 0;
 +
 +fail:
 +      if (conf)
 +              hostapd_config_free(conf);
 +      if (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, 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)) {
 +                      wpa_printf(MSG_INFO, "Remove interface '%s'", buf);
 +                      hapd_iface->driver_ap_teardown =
 +                              !!(hapd_iface->drv_flags &
 +                                 WPA_DRIVER_FLAGS_AP_TEARDOWN_SUPPORT);
 +
 +                      hostapd_interface_deinit_free(hapd_iface);
 +                      k = i;
 +                      while (k < (interfaces->count - 1)) {
 +                              interfaces->iface[k] =
 +                                      interfaces->iface[k + 1];
 +                              k++;
 +                      }
 +                      interfaces->count--;
 +                      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;
 +}
 +
 +
 +/**
 + * hostapd_new_assoc_sta - Notify that a new station associated with the AP
 + * @hapd: Pointer to BSS data
 + * @sta: Pointer to the associated STA data
 + * @reassoc: 1 to indicate this was a re-association; 0 = first association
 + *
 + * This function will be called whenever a station associates with the AP. It
 + * can be called from ieee802_11.c for drivers that export MLME to hostapd and
 + * from drv_callbacks.c based on driver events for drivers that take care of
 + * management frames (IEEE 802.11 authentication and association) internally.
 + */
 +void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta,
 +                         int reassoc)
 +{
 +      if (hapd->tkip_countermeasures) {
 +              hostapd_drv_sta_deauth(hapd, sta->addr,
 +                                     WLAN_REASON_MICHAEL_MIC_FAILURE);
 +              return;
 +      }
 +
 +      hostapd_prune_associations(hapd, sta->addr);
 +
 +      /* IEEE 802.11F (IAPP) */
 +      if (hapd->conf->ieee802_11f)
 +              iapp_new_station(hapd->iapp, sta);
 +
 +#ifdef CONFIG_P2P
 +      if (sta->p2p_ie == NULL && !sta->no_p2p_set) {
 +              sta->no_p2p_set = 1;
 +              hapd->num_sta_no_p2p++;
 +              if (hapd->num_sta_no_p2p == 1)
 +                      hostapd_p2p_non_p2p_sta_connected(hapd);
 +      }
 +#endif /* CONFIG_P2P */
 +
 +      /* Start accounting here, if IEEE 802.1X and WPA are not used.
 +       * IEEE 802.1X/WPA code will start accounting after the station has
 +       * been authorized. */
 +      if (!hapd->conf->ieee802_1x && !hapd->conf->wpa && !hapd->conf->osen) {
 +              ap_sta_set_authorized(hapd, sta, 1);
 +              os_get_reltime(&sta->connected_time);
 +              accounting_sta_start(hapd, sta);
 +      }
 +
 +      /* Start IEEE 802.1X authentication process for new stations */
 +      ieee802_1x_new_station(hapd, sta);
 +      if (reassoc) {
 +              if (sta->auth_alg != WLAN_AUTH_FT &&
 +                  !(sta->flags & (WLAN_STA_WPS | WLAN_STA_MAYBE_WPS)))
 +                      wpa_auth_sm_event(sta->wpa_sm, WPA_REAUTH);
 +      } else
 +              wpa_auth_sta_associated(hapd->wpa_auth, sta->wpa_sm);
 +
 +      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);
 +}
 +
++
++struct hostapd_data * hostapd_get_iface(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 hapd;
++              }
++      }
++
++      return NULL;
++}
++
 +#endif /* NEED_AP_MLME */
++
++
++void hostapd_periodic_iface(struct hostapd_iface *iface)
++{
++      size_t i;
++
++      ap_list_timer(iface);
++
++      for (i = 0; i < iface->num_bss; i++) {
++              struct hostapd_data *hapd = iface->bss[i];
++
++              if (!hapd->started)
++                      continue;
++
++#ifndef CONFIG_NO_RADIUS
++              hostapd_acl_expire(hapd);
++#endif /* CONFIG_NO_RADIUS */
++      }
++}
index 75cc24edb665b7516b825a4ac5a3759682d801cd,0000000000000000000000000000000000000000..dcf51f00f78d38e27ab2a1118710a697aeb999f0
mode 100644,000000..100644
--- /dev/null
@@@ -1,467 -1,0 +1,494 @@@
 +/*
 + * hostapd / Initialization and configuration
 + * 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.
 + */
 +
 +#ifndef HOSTAPD_H
 +#define HOSTAPD_H
 +
 +#include "common/defs.h"
 +#include "utils/list.h"
 +#include "ap_config.h"
 +#include "drivers/driver.h"
 +
 +struct wpa_ctrl_dst;
 +struct radius_server_data;
 +struct upnp_wps_device_sm;
 +struct hostapd_data;
 +struct sta_info;
 +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 hapd_interfaces {
 +      int (*reload_config)(struct hostapd_iface *iface);
 +      struct hostapd_config * (*config_read_cb)(const char *config_fname);
 +      int (*ctrl_iface_init)(struct hostapd_data *hapd);
 +      void (*ctrl_iface_deinit)(struct hostapd_data *hapd);
 +      int (*for_each_interface)(struct hapd_interfaces *interfaces,
 +                                int (*cb)(struct hostapd_iface *iface,
 +                                          void *ctx), void *ctx);
 +      int (*driver_init)(struct hostapd_iface *iface);
 +
 +      size_t count;
 +      int global_ctrl_sock;
++      struct wpa_ctrl_dst *global_ctrl_dst;
 +      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;
 +
 +      size_t terminate_on_error;
++#ifndef CONFIG_NO_VLAN
++      struct dynamic_iface *vlan_priv;
++#endif /* CONFIG_NO_VLAN */
 +};
 +
 +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,
 +                const u8 *ie, size_t ie_len, int ssi_signal);
 +      void *ctx;
 +};
 +
 +#define HOSTAPD_RATE_BASIC 0x00000001
 +
 +struct hostapd_rate_data {
 +      int rate; /* rate in 100 kbps */
 +      int flags; /* HOSTAPD_RATE_ flags */
 +};
 +
 +struct hostapd_frame_info {
 +      u32 channel;
 +      u32 datarate;
 +      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
 + */
 +struct hostapd_data {
 +      struct hostapd_iface *iface;
 +      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];
 +
 +      int num_sta; /* number of entries in sta_list */
 +      struct sta_info *sta_list; /* STA info list head */
 +#define STA_HASH_SIZE 256
 +#define STA_HASH(sta) (sta[5])
 +      struct sta_info *sta_hash[STA_HASH_SIZE];
 +
 +      /*
 +       * Bitfield for indicating which AIDs are allocated. Only AID values
 +       * 1-2007 are used and as such, the bit at index 0 corresponds to AID
 +       * 1.
 +       */
 +#define AID_WORDS ((2008 + 31) / 32)
 +      u32 sta_aid[AID_WORDS];
 +
 +      const struct wpa_driver_ops *driver;
 +      void *drv_priv;
 +
 +      void (*new_assoc_sta_cb)(struct hostapd_data *hapd,
 +                               struct sta_info *sta, int reassoc);
 +
 +      void *msg_ctx; /* ctx for wpa_msg() calls */
 +      void *msg_ctx_parent; /* parent interface ctx for wpa_msg() calls */
 +
 +      struct radius_client_data *radius;
 +      u32 acct_session_id_hi, acct_session_id_lo;
 +      struct radius_das_data *radius_das;
 +
 +      struct iapp_data *iapp;
 +
 +      struct hostapd_cached_radius_acl *acl_cache;
 +      struct hostapd_acl_query_data *acl_queries;
 +
 +      struct wpa_authenticator *wpa_auth;
 +      struct eapol_authenticator *eapol_auth;
 +
 +      struct rsn_preauth_interface *preauth_iface;
 +      struct os_reltime michael_mic_failure;
 +      int michael_mic_failures;
 +      int tkip_countermeasures;
 +
 +      int ctrl_sock;
 +      struct wpa_ctrl_dst *ctrl_dst;
 +
 +      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;
 +
 +      /* Time Advertisement */
 +      u8 time_update_counter;
 +      struct wpabuf *time_adv;
 +
 +#ifdef CONFIG_FULL_DYNAMIC_VLAN
 +      struct full_dynamic_vlan *full_dynamic_vlan;
 +#endif /* CONFIG_FULL_DYNAMIC_VLAN */
 +
 +      struct l2_packet_data *l2;
 +      struct wps_context *wps;
 +
 +      int beacon_set_done;
 +      struct wpabuf *wps_beacon_ie;
 +      struct wpabuf *wps_probe_resp_ie;
 +#ifdef CONFIG_WPS
 +      unsigned int ap_pin_failures;
 +      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;
 +      size_t num_probereq_cb;
 +
 +      void (*public_action_cb)(void *ctx, const u8 *buf, size_t len,
 +                               int freq);
 +      void *public_action_cb_ctx;
 +      void (*public_action_cb2)(void *ctx, const u8 *buf, size_t len,
 +                                int freq);
 +      void *public_action_cb2_ctx;
 +
 +      int (*vendor_action_cb)(void *ctx, const u8 *buf, size_t len,
 +                              int freq);
 +      void *vendor_action_cb_ctx;
 +
 +      void (*wps_reg_success_cb)(void *ctx, const u8 *mac_addr,
 +                                 const u8 *uuid_e);
 +      void *wps_reg_success_cb_ctx;
 +
 +      void (*wps_event_cb)(void *ctx, enum wps_event event,
 +                           union wps_event_data *data);
 +      void *wps_event_cb_ctx;
 +
 +      void (*sta_authorized_cb)(void *ctx, const u8 *mac_addr,
 +                                int authorized, const u8 *p2p_dev_addr);
 +      void *sta_authorized_cb_ctx;
 +
 +      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;
 +      struct wpabuf *p2p_beacon_ie;
 +      struct wpabuf *p2p_probe_resp_ie;
 +
 +      /* Number of non-P2P association stations */
 +      int num_sta_no_p2p;
 +
 +      /* Periodic NoA (used only when no non-P2P clients in the group) */
 +      int noa_enabled;
 +      int noa_start;
 +      int noa_duration;
 +#endif /* CONFIG_P2P */
 +#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;
 +#endif /* CONFIG_SQLITE */
 +
 +#ifdef CONFIG_SAE
 +      /** Key used for generating SAE anti-clogging tokens */
 +      u8 sae_token_key[8];
 +      struct os_reltime last_sae_token_key_update;
++      int dot11RSNASAERetransPeriod; /* msec */
 +#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 */
 +};
 +
 +
++struct hostapd_sta_info {
++      struct dl_list list;
++      u8 addr[ETH_ALEN];
++      struct os_reltime last_seen;
++};
++
 +/**
 + * struct hostapd_iface - hostapd per-interface data structure
 + */
 +struct hostapd_iface {
 +      struct hapd_interfaces *interfaces;
 +      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;
++#ifdef CONFIG_FST
++      struct fst_iface *fst;
++      const struct wpabuf *fst_ies;
++#endif /* CONFIG_FST */
 +
 +      /*
 +       * 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];
 +
 +      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
 +       * struct wpa_driver_capa in driver.h
 +       */
 +      unsigned int probe_resp_offloads;
 +
 +      /* extended capabilities supported by the driver */
 +      const u8 *extended_capa, *extended_capa_mask;
 +      unsigned int extended_capa_len;
 +
 +      unsigned int drv_max_acl_mac_addrs;
 +
 +      struct hostapd_hw_modes *hw_features;
 +      int num_hw_features;
 +      struct hostapd_hw_modes *current_mode;
 +      /* Rates that are currently used (i.e., filtered copy of
 +       * current_mode->channels */
 +      int num_rates;
 +      struct hostapd_rate_data *current_rates;
 +      int *basic_rates;
 +      int freq;
 +
 +      u16 hw_flags;
 +
 +      /* Number of associated Non-ERP stations (i.e., stations using 802.11b
 +       * in 802.11g BSS) */
 +      int num_sta_non_erp;
 +
 +      /* Number of associated stations that do not support Short Slot Time */
 +      int num_sta_no_short_slot_time;
 +
 +      /* Number of associated stations that do not support Short Preamble */
 +      int num_sta_no_short_preamble;
 +
 +      int olbc; /* Overlapping Legacy BSS Condition */
 +
 +      /* Number of HT associated stations that do not support greenfield */
 +      int num_sta_ht_no_gf;
 +
 +      /* Number of associated non-HT stations */
 +      int num_sta_no_ht;
 +
 +      /* 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;
 +
 +      /* 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;
++
++      struct dl_list sta_seen; /* struct hostapd_sta_info */
++      unsigned int num_sta_seen;
 +};
 +
 +/* hostapd.c */
 +int hostapd_for_each_interface(struct hapd_interfaces *interfaces,
 +                             int (*cb)(struct hostapd_iface *iface,
 +                                       void *ctx), void *ctx);
 +int hostapd_reload_config(struct hostapd_iface *iface);
 +struct hostapd_data *
 +hostapd_alloc_bss_data(struct hostapd_iface *hapd_iface,
 +                     struct hostapd_config *conf,
 +                     struct hostapd_bss_config *bss);
 +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);
 +int hostapd_enable_iface(struct hostapd_iface *hapd_iface);
 +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);
++void hostapd_periodic_iface(struct hostapd_iface *iface);
 +
 +/* utils.c */
 +int hostapd_register_probereq_cb(struct hostapd_data *hapd,
 +                               int (*cb)(void *ctx, const u8 *sa,
 +                                         const u8 *da, const u8 *bssid,
 +                                         const u8 *ie, size_t ie_len,
 +                                         int ssi_signal),
 +                               void *ctx);
 +void hostapd_prune_associations(struct hostapd_data *hapd, const u8 *addr);
 +
 +/* drv_callbacks.c (TODO: move to somewhere else?) */
 +int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
 +                      const u8 *ie, size_t ielen, int reassoc);
 +void hostapd_notif_disassoc(struct hostapd_data *hapd, const u8 *addr);
 +void hostapd_event_sta_low_ack(struct hostapd_data *hapd, const u8 *addr);
 +void hostapd_event_connect_failed_reason(struct hostapd_data *hapd,
 +                                       const u8 *addr, int reason_code);
 +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 width, int cf1, int cf2);
 +
 +const struct hostapd_eap_user *
 +hostapd_get_eap_user(struct hostapd_data *hapd, const u8 *identity,
 +                   size_t identity_len, int phase2);
 +
++struct hostapd_data * hostapd_get_iface(struct hapd_interfaces *interfaces,
++                                      const char *ifname);
++
++#ifdef CONFIG_FST
++void fst_hostapd_fill_iface_obj(struct hostapd_data *hapd,
++                              struct fst_wpa_obj *iface_obj);
++#endif /* CONFIG_FST */
++
 +#endif /* HOSTAPD_H */
index 05431d32c205dabafcf49d6f7b5848f9237386cf,0000000000000000000000000000000000000000..fc8786dc311cafd106d08a00ddec53dcbf6e9dc1
mode 100644,000000..100644
--- /dev/null
@@@ -1,943 -1,0 +1,980 @@@
-       if (res == 2)
-               ieee80211n_switch_pri_sec(iface);
 +/*
 + * hostapd / Hardware feature query and different modes
 + * Copyright 2002-2003, Instant802 Networks, Inc.
 + * Copyright 2005-2006, Devicescape Software, Inc.
 + * Copyright (c) 2008-2012, Jouni Malinen <j@w1.fi>
 + *
 + * This software may be distributed under the terms of the BSD license.
 + * See README for more details.
 + */
 +
 +#include "utils/includes.h"
 +
 +#include "utils/common.h"
 +#include "utils/eloop.h"
 +#include "common/ieee802_11_defs.h"
 +#include "common/ieee802_11_common.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"
 +
 +
 +void hostapd_free_hw_features(struct hostapd_hw_modes *hw_features,
 +                            size_t num_hw_features)
 +{
 +      size_t i;
 +
 +      if (hw_features == NULL)
 +              return;
 +
 +      for (i = 0; i < num_hw_features; i++) {
 +              os_free(hw_features[i].channels);
 +              os_free(hw_features[i].rates);
 +      }
 +
 +      os_free(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 i, j;
 +      u16 num_modes, flags;
 +      struct hostapd_hw_modes *modes;
 +
 +      if (hostapd_drv_none(hapd))
 +              return -1;
 +      modes = hostapd_get_hw_feature_data(hapd, &num_modes, &flags);
 +      if (modes == NULL) {
 +              hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
 +                             HOSTAPD_LEVEL_DEBUG,
 +                             "Fetching hardware channel/rate support not "
 +                             "supported.");
 +              return -1;
 +      }
 +
 +      iface->hw_flags = flags;
 +
 +      hostapd_free_hw_features(iface->hw_features, iface->num_hw_features);
 +      iface->hw_features = modes;
 +      iface->num_hw_features = num_modes;
 +
 +      for (i = 0; i < num_modes; i++) {
 +              struct hostapd_hw_modes *feature = &modes[i];
 +              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
 +                       * 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_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%s",
 +                                 feature->mode,
 +                                 feature->channels[j].chan,
 +                                 feature->channels[j].freq,
 +                                 feature->channels[j].max_tx_power,
 +                                 dfs ? dfs_info(&feature->channels[j]) : "");
 +              }
 +      }
 +
 +      return 0;
 +}
 +
 +
 +int hostapd_prepare_rates(struct hostapd_iface *iface,
 +                        struct hostapd_hw_modes *mode)
 +{
 +      int i, num_basic_rates = 0;
 +      int basic_rates_a[] = { 60, 120, 240, -1 };
 +      int basic_rates_b[] = { 10, 20, -1 };
 +      int basic_rates_g[] = { 10, 20, 55, 110, -1 };
 +      int *basic_rates;
 +
 +      if (iface->conf->basic_rates)
 +              basic_rates = iface->conf->basic_rates;
 +      else switch (mode->mode) {
 +      case HOSTAPD_MODE_IEEE80211A:
 +              basic_rates = basic_rates_a;
 +              break;
 +      case HOSTAPD_MODE_IEEE80211B:
 +              basic_rates = basic_rates_b;
 +              break;
 +      case HOSTAPD_MODE_IEEE80211G:
 +              basic_rates = basic_rates_g;
 +              break;
 +      case HOSTAPD_MODE_IEEE80211AD:
 +              return 0; /* No basic rates for 11ad */
 +      default:
 +              return -1;
 +      }
 +
 +      i = 0;
 +      while (basic_rates[i] >= 0)
 +              i++;
 +      if (i)
 +              i++; /* -1 termination */
 +      os_free(iface->basic_rates);
 +      iface->basic_rates = os_malloc(i * sizeof(int));
 +      if (iface->basic_rates)
 +              os_memcpy(iface->basic_rates, basic_rates, i * sizeof(int));
 +
 +      os_free(iface->current_rates);
 +      iface->num_rates = 0;
 +
 +      iface->current_rates =
 +              os_calloc(mode->num_rates, sizeof(struct hostapd_rate_data));
 +      if (!iface->current_rates) {
 +              wpa_printf(MSG_ERROR, "Failed to allocate memory for rate "
 +                         "table.");
 +              return -1;
 +      }
 +
 +      for (i = 0; i < mode->num_rates; i++) {
 +              struct hostapd_rate_data *rate;
 +
 +              if (iface->conf->supported_rates &&
 +                  !hostapd_rate_found(iface->conf->supported_rates,
 +                                      mode->rates[i]))
 +                      continue;
 +
 +              rate = &iface->current_rates[iface->num_rates];
 +              rate->rate = mode->rates[i];
 +              if (hostapd_rate_found(basic_rates, rate->rate)) {
 +                      rate->flags |= HOSTAPD_RATE_BASIC;
 +                      num_basic_rates++;
 +              }
 +              wpa_printf(MSG_DEBUG, "RATE[%d] rate=%d flags=0x%x",
 +                         iface->num_rates, rate->rate, rate->flags);
 +              iface->num_rates++;
 +      }
 +
 +      if ((iface->num_rates == 0 || num_basic_rates == 0) &&
 +          (!iface->conf->ieee80211n || !iface->conf->require_ht)) {
 +              wpa_printf(MSG_ERROR, "No rates remaining in supported/basic "
 +                         "rate sets (%d,%d).",
 +                         iface->num_rates, num_basic_rates);
 +              return -1;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +#ifdef CONFIG_IEEE80211N
 +static int ieee80211n_allowed_ht40_channel_pair(struct hostapd_iface *iface)
 +{
 +      int pri_chan, sec_chan;
 +
 +      if (!iface->conf->secondary_channel)
 +              return 1; /* HT40 not used */
 +
 +      pri_chan = iface->conf->channel;
 +      sec_chan = pri_chan + iface->conf->secondary_channel * 4;
 +
 +      return allowed_ht40_channel_pair(iface->current_mode, pri_chan,
 +                                       sec_chan);
 +}
 +
 +
 +static void ieee80211n_switch_pri_sec(struct hostapd_iface *iface)
 +{
 +      if (iface->conf->secondary_channel > 0) {
 +              iface->conf->channel += 4;
 +              iface->conf->secondary_channel = -1;
 +      } else {
 +              iface->conf->channel -= 4;
 +              iface->conf->secondary_channel = 1;
 +      }
 +}
 +
 +
 +static int ieee80211n_check_40mhz_5g(struct hostapd_iface *iface,
 +                                   struct wpa_scan_results *scan_res)
 +{
 +      int pri_chan, sec_chan;
 +      int res;
 +
 +      pri_chan = iface->conf->channel;
 +      sec_chan = pri_chan + iface->conf->secondary_channel * 4;
 +
 +      res = check_40mhz_5g(iface->current_mode, scan_res, pri_chan, sec_chan);
 +
-       affected_start = (pri_freq + sec_freq) / 2 - 25;
-       affected_end = (pri_freq + sec_freq) / 2 + 25;
++      if (res == 2) {
++              if (iface->conf->no_pri_sec_switch) {
++                      wpa_printf(MSG_DEBUG,
++                                 "Cannot switch PRI/SEC channels due to local constraint");
++              } else {
++                      ieee80211n_switch_pri_sec(iface);
++              }
++      }
 +
 +      return !!res;
 +}
 +
 +
 +static int ieee80211n_check_40mhz_2g4(struct hostapd_iface *iface,
 +                                    struct wpa_scan_results *scan_res)
 +{
 +      int pri_chan, sec_chan;
 +
 +      pri_chan = iface->conf->channel;
 +      sec_chan = pri_chan + iface->conf->secondary_channel * 4;
 +
 +      return check_40mhz_2g4(iface->current_mode, scan_res, pri_chan,
 +                             sec_chan);
 +}
 +
 +
 +static void ieee80211n_check_scan(struct hostapd_iface *iface)
 +{
 +      struct wpa_scan_results *scan_res;
 +      int oper40;
 +      int res;
 +
 +      /* Check list of neighboring BSSes (from scan) to see whether 40 MHz is
 +       * allowed per IEEE Std 802.11-2012, 10.15.3.2 */
 +
 +      iface->scan_cb = NULL;
 +
 +      scan_res = hostapd_driver_get_scan_results(iface->bss[0]);
 +      if (scan_res == NULL) {
 +              hostapd_setup_interface_complete(iface, 1);
 +              return;
 +      }
 +
 +      if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A)
 +              oper40 = ieee80211n_check_40mhz_5g(iface, scan_res);
 +      else
 +              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",
 +                         iface->conf->channel,
 +                         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);
 +}
 +
 +
 +static void ieee80211n_scan_channels_2g4(struct hostapd_iface *iface,
 +                                       struct wpa_driver_scan_params *params)
 +{
 +      /* Scan only the affected frequency range */
 +      int pri_freq, sec_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)
 +              sec_freq = pri_freq + 20;
 +      else
 +              sec_freq = pri_freq - 20;
-       if ((conf & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) &&
++      /*
++       * Note: Need to find the PRI channel also in cases where the affected
++       * channel is the SEC channel of a 40 MHz BSS, so need to include the
++       * scanning coverage here to be 40 MHz from the center frequency.
++       */
++      affected_start = (pri_freq + sec_freq) / 2 - 40;
++      affected_end = (pri_freq + sec_freq) / 2 + 40;
 +      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 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);
 +      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;
 +      }
 +
 +      iface->scan_cb = ieee80211n_check_scan;
 +      return 1;
 +}
 +
 +
 +static int ieee80211n_supported_ht_capab(struct hostapd_iface *iface)
 +{
 +      u16 hw = iface->current_mode->ht_capab;
 +      u16 conf = iface->conf->ht_capab;
 +
 +      if ((conf & HT_CAP_INFO_LDPC_CODING_CAP) &&
 +          !(hw & HT_CAP_INFO_LDPC_CODING_CAP)) {
 +              wpa_printf(MSG_ERROR, "Driver does not support configured "
 +                         "HT capability [LDPC]");
 +              return 0;
 +      }
 +
-               wpa_printf(MSG_ERROR, "Hardware does not support configured "
-                          "mode");
-               hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211,
-                              HOSTAPD_LEVEL_WARNING,
-                              "Hardware does not support configured mode "
-                              "(%d) (hw_mode in hostapd.conf)",
-                              (int) iface->conf->hw_mode);
-               return -2;
++      /*
++       * Driver ACS chosen channel may not be HT40 due to internal driver
++       * restrictions.
++       */
++      if (!iface->conf->acs && (conf & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) &&
 +          !(hw & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) {
 +              wpa_printf(MSG_ERROR, "Driver does not support configured "
 +                         "HT capability [HT40*]");
 +              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) &&
 +          !(hw & HT_CAP_INFO_GREEN_FIELD)) {
 +              wpa_printf(MSG_ERROR, "Driver does not support configured "
 +                         "HT capability [GF]");
 +              return 0;
 +      }
 +
 +      if ((conf & HT_CAP_INFO_SHORT_GI20MHZ) &&
 +          !(hw & HT_CAP_INFO_SHORT_GI20MHZ)) {
 +              wpa_printf(MSG_ERROR, "Driver does not support configured "
 +                         "HT capability [SHORT-GI-20]");
 +              return 0;
 +      }
 +
 +      if ((conf & HT_CAP_INFO_SHORT_GI40MHZ) &&
 +          !(hw & HT_CAP_INFO_SHORT_GI40MHZ)) {
 +              wpa_printf(MSG_ERROR, "Driver does not support configured "
 +                         "HT capability [SHORT-GI-40]");
 +              return 0;
 +      }
 +
 +      if ((conf & HT_CAP_INFO_TX_STBC) && !(hw & HT_CAP_INFO_TX_STBC)) {
 +              wpa_printf(MSG_ERROR, "Driver does not support configured "
 +                         "HT capability [TX-STBC]");
 +              return 0;
 +      }
 +
 +      if ((conf & HT_CAP_INFO_RX_STBC_MASK) >
 +          (hw & HT_CAP_INFO_RX_STBC_MASK)) {
 +              wpa_printf(MSG_ERROR, "Driver does not support configured "
 +                         "HT capability [RX-STBC*]");
 +              return 0;
 +      }
 +
 +      if ((conf & HT_CAP_INFO_DELAYED_BA) &&
 +          !(hw & HT_CAP_INFO_DELAYED_BA)) {
 +              wpa_printf(MSG_ERROR, "Driver does not support configured "
 +                         "HT capability [DELAYED-BA]");
 +              return 0;
 +      }
 +
 +      if ((conf & HT_CAP_INFO_MAX_AMSDU_SIZE) &&
 +          !(hw & HT_CAP_INFO_MAX_AMSDU_SIZE)) {
 +              wpa_printf(MSG_ERROR, "Driver does not support configured "
 +                         "HT capability [MAX-AMSDU-7935]");
 +              return 0;
 +      }
 +
 +      if ((conf & HT_CAP_INFO_DSSS_CCK40MHZ) &&
 +          !(hw & HT_CAP_INFO_DSSS_CCK40MHZ)) {
 +              wpa_printf(MSG_ERROR, "Driver does not support configured "
 +                         "HT capability [DSSS_CCK-40]");
 +              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 "
 +                         "HT capability [LSIG-TXOP-PROT]");
 +              return 0;
 +      }
 +
 +      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 */
 +
 +
 +int hostapd_check_ht_capab(struct hostapd_iface *iface)
 +{
 +#ifdef CONFIG_IEEE80211N
 +      int ret;
 +      if (!iface->conf->ieee80211n)
 +              return 0;
++
++      if (iface->current_mode->mode != HOSTAPD_MODE_IEEE80211B &&
++          iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G &&
++          (iface->conf->ht_capab & HT_CAP_INFO_DSSS_CCK40MHZ)) {
++              wpa_printf(MSG_DEBUG,
++                         "Disable HT capability [DSSS_CCK-40] on 5 GHz band");
++              iface->conf->ht_capab &= ~HT_CAP_INFO_DSSS_CCK40MHZ;
++      }
++
 +      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;
 +      if (!ieee80211n_allowed_ht40_channel_pair(iface))
 +              return -1;
 +#endif /* CONFIG_IEEE80211N */
 +
 +      return 0;
 +}
 +
 +
 +static int hostapd_is_usable_chan(struct hostapd_iface *iface,
 +                                int channel, int primary)
 +{
 +      int i;
 +      struct hostapd_channel_data *chan;
 +
++      if (!iface->current_mode)
++              return 0;
++
 +      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)
 +{
++      if (!iface->current_mode) {
++              hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211,
++                             HOSTAPD_LEVEL_WARNING,
++                             "Hardware does not support configured mode");
++              return;
++      }
 +      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.
 + * Returns: 0 on success, < 0 on failure
 + *
 + * Sets up the hardware mode, channel, rates, and passive scanning
 + * based on the configuration.
 + */
 +int hostapd_select_hw_mode(struct hostapd_iface *iface)
 +{
 +      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];
 +              if (mode->mode == iface->conf->hw_mode) {
 +                      iface->current_mode = mode;
 +                      break;
 +              }
 +      }
 +
 +      if (iface->current_mode == NULL) {
++              if (!(iface->drv_flags & WPA_DRIVER_FLAGS_ACS_OFFLOAD) ||
++                  !(iface->drv_flags & WPA_DRIVER_FLAGS_SUPPORT_HW_MODE_ANY))
++              {
++                      wpa_printf(MSG_ERROR,
++                                 "Hardware does not support configured mode");
++                      hostapd_logger(iface->bss[0], NULL,
++                                     HOSTAPD_MODULE_IEEE80211,
++                                     HOSTAPD_LEVEL_WARNING,
++                                     "Hardware does not support configured mode (%d) (hw_mode in hostapd.conf)",
++                                     (int) iface->conf->hw_mode);
++                      return -2;
++              }
 +      }
 +
 +      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;
 +      }
 +}
 +
 +
 +const char * hostapd_hw_mode_txt(int mode)
 +{
 +      switch (mode) {
 +      case HOSTAPD_MODE_IEEE80211A:
 +              return "IEEE 802.11a";
 +      case HOSTAPD_MODE_IEEE80211B:
 +              return "IEEE 802.11b";
 +      case HOSTAPD_MODE_IEEE80211G:
 +              return "IEEE 802.11g";
 +      case HOSTAPD_MODE_IEEE80211AD:
 +              return "IEEE 802.11ad";
 +      default:
 +              return "UNKNOWN";
 +      }
 +}
 +
 +
 +int hostapd_hw_get_freq(struct hostapd_data *hapd, int chan)
 +{
 +      return hw_get_freq(hapd->iface->current_mode, chan);
 +}
 +
 +
 +int hostapd_hw_get_channel(struct hostapd_data *hapd, int freq)
 +{
 +      return hw_get_chan(hapd->iface->current_mode, freq);
 +}
index 0f67ab8e5f37ff2711fe8efa6c69727a966e0bb8,0000000000000000000000000000000000000000..ca7f22ba205bf208c494aa31bdf0e52cc4a59aba
mode 100644,000000..100644
--- /dev/null
@@@ -1,71 -1,0 +1,76 @@@
 +/*
 + * hostapd / Hardware feature query and different modes
 + * Copyright 2002-2003, Instant802 Networks, Inc.
 + * Copyright 2005-2006, Devicescape Software, Inc.
 + * Copyright (c) 2008-2011, Jouni Malinen <j@w1.fi>
 + *
 + * This software may be distributed under the terms of the BSD license.
 + * See README for more details.
 + */
 +
 +#ifndef HW_FEATURES_H
 +#define HW_FEATURES_H
 +
 +#ifdef NEED_AP_MLME
 +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);
 +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,
 +                       size_t num_hw_features)
 +{
 +}
 +
 +static inline int hostapd_get_hw_features(struct hostapd_iface *iface)
 +{
 +      return -1;
 +}
 +
++static inline int hostapd_acs_completed(struct hostapd_iface *iface, int err)
++{
++      return -1;
++}
++
 +static inline int hostapd_select_hw_mode(struct hostapd_iface *iface)
 +{
 +      return -100;
 +}
 +
 +static inline const char * hostapd_hw_mode_txt(int mode)
 +{
 +      return NULL;
 +}
 +
 +static inline int hostapd_hw_get_freq(struct hostapd_data *hapd, int chan)
 +{
 +      return -1;
 +}
 +
 +static inline int hostapd_check_ht_capab(struct hostapd_iface *iface)
 +{
 +      return 0;
 +}
 +
 +static inline int hostapd_prepare_rates(struct hostapd_iface *iface,
 +                                      struct hostapd_hw_modes *mode)
 +{
 +      return 0;
 +}
 +
 +static inline void hostapd_stop_setup_timers(struct hostapd_iface *iface)
 +{
 +}
 +
 +#endif /* NEED_AP_MLME */
 +
 +#endif /* HW_FEATURES_H */
index 89911b1fdd42fd9bced9ade35be26387c411098a,0000000000000000000000000000000000000000..7bb18c01d1a19268dc15241dbedbe3729913a6bc
mode 100644,000000..100644
--- /dev/null
@@@ -1,2752 -1,0 +1,2844 @@@
- u16 hostapd_own_capab_info(struct hostapd_data *hapd, struct sta_info *sta,
-                          int probe)
 +/*
 + * hostapd / IEEE 802.11 Management
 + * 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/includes.h"
 +
 +#ifndef CONFIG_NATIVE_WINDOWS
 +
 +#include "utils/common.h"
 +#include "utils/eloop.h"
 +#include "crypto/crypto.h"
 +#include "crypto/sha256.h"
 +#include "crypto/random.h"
 +#include "common/ieee802_11_defs.h"
 +#include "common/ieee802_11_common.h"
 +#include "common/wpa_ctrl.h"
 +#include "common/sae.h"
 +#include "radius/radius.h"
 +#include "radius/radius_client.h"
 +#include "p2p/p2p.h"
 +#include "wps/wps.h"
++#include "fst/fst.h"
 +#include "hostapd.h"
 +#include "beacon.h"
 +#include "ieee802_11_auth.h"
 +#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"
 +#include "ap_config.h"
 +#include "ap_mlme.h"
 +#include "p2p_hostapd.h"
 +#include "ap_drv_ops.h"
 +#include "wnm_ap.h"
++#include "hw_features.h"
 +#include "ieee802_11.h"
 +#include "dfs.h"
 +
 +
 +u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid)
 +{
 +      u8 *pos = eid;
 +      int i, num, count;
 +
 +      if (hapd->iface->current_rates == NULL)
 +              return eid;
 +
 +      *pos++ = WLAN_EID_SUPP_RATES;
 +      num = hapd->iface->num_rates;
 +      if (hapd->iconf->ieee80211n && hapd->iconf->require_ht)
 +              num++;
 +      if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht)
 +              num++;
 +      if (num > 8) {
 +              /* rest of the rates are encoded in Extended supported
 +               * rates element */
 +              num = 8;
 +      }
 +
 +      *pos++ = num;
 +      for (i = 0, count = 0; i < hapd->iface->num_rates && count < num;
 +           i++) {
 +              count++;
 +              *pos = hapd->iface->current_rates[i].rate / 5;
 +              if (hapd->iface->current_rates[i].flags & HOSTAPD_RATE_BASIC)
 +                      *pos |= 0x80;
 +              pos++;
 +      }
 +
 +      if (hapd->iconf->ieee80211n && hapd->iconf->require_ht && count < 8) {
 +              count++;
 +              *pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_HT_PHY;
 +      }
 +
 +      if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht && count < 8) {
 +              count++;
 +              *pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_VHT_PHY;
 +      }
 +
 +      return pos;
 +}
 +
 +
 +u8 * hostapd_eid_ext_supp_rates(struct hostapd_data *hapd, u8 *eid)
 +{
 +      u8 *pos = eid;
 +      int i, num, count;
 +
 +      if (hapd->iface->current_rates == NULL)
 +              return eid;
 +
 +      num = hapd->iface->num_rates;
 +      if (hapd->iconf->ieee80211n && hapd->iconf->require_ht)
 +              num++;
 +      if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht)
 +              num++;
 +      if (num <= 8)
 +              return eid;
 +      num -= 8;
 +
 +      *pos++ = WLAN_EID_EXT_SUPP_RATES;
 +      *pos++ = num;
 +      for (i = 0, count = 0; i < hapd->iface->num_rates && count < num + 8;
 +           i++) {
 +              count++;
 +              if (count <= 8)
 +                      continue; /* already in SuppRates IE */
 +              *pos = hapd->iface->current_rates[i].rate / 5;
 +              if (hapd->iface->current_rates[i].flags & HOSTAPD_RATE_BASIC)
 +                      *pos |= 0x80;
 +              pos++;
 +      }
 +
 +      if (hapd->iconf->ieee80211n && hapd->iconf->require_ht) {
 +              count++;
 +              if (count > 8)
 +                      *pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_HT_PHY;
 +      }
 +
 +      if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht) {
 +              count++;
 +              if (count > 8)
 +                      *pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_VHT_PHY;
 +      }
 +
 +      return pos;
 +}
 +
 +
-       if (sta) {
-               int policy, def_klen;
-               if (probe && sta->ssid_probe) {
-                       policy = sta->ssid_probe->security_policy;
-                       def_klen = sta->ssid_probe->wep.default_len;
-               } else {
-                       policy = sta->ssid->security_policy;
-                       def_klen = sta->ssid->wep.default_len;
-               }
-               privacy = policy != SECURITY_PLAINTEXT;
-               if (policy == SECURITY_IEEE_802_1X && def_klen == 0)
-                       privacy = 0;
-       }
++u16 hostapd_own_capab_info(struct hostapd_data *hapd)
 +{
 +      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)
 +              capab |= WLAN_CAPABILITY_SHORT_PREAMBLE;
 +
 +      privacy = hapd->conf->ssid.wep.keys_set;
 +
 +      if (hapd->conf->ieee802_1x &&
 +          (hapd->conf->default_wep_key_len ||
 +           hapd->conf->individual_wep_key_len))
 +              privacy = 1;
 +
 +      if (hapd->conf->wpa)
 +              privacy = 1;
 +
 +#ifdef CONFIG_HS20
 +      if (hapd->conf->osen)
 +              privacy = 1;
 +#endif /* CONFIG_HS20 */
 +
- #define dot11RSNASAERetransPeriod 40  /* msec */
 +      if (privacy)
 +              capab |= WLAN_CAPABILITY_PRIVACY;
 +
 +      if (hapd->iface->current_mode &&
 +          hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G &&
 +          hapd->iface->num_sta_no_short_slot_time == 0)
 +              capab |= WLAN_CAPABILITY_SHORT_SLOT_TIME;
 +
 +      /*
 +       * 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;
 +
 +      return capab;
 +}
 +
 +
++#ifndef CONFIG_NO_RC4
 +static u16 auth_shared_key(struct hostapd_data *hapd, struct sta_info *sta,
 +                         u16 auth_transaction, const u8 *challenge,
 +                         int iswep)
 +{
 +      hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
 +                     HOSTAPD_LEVEL_DEBUG,
 +                     "authentication (shared key, transaction %d)",
 +                     auth_transaction);
 +
 +      if (auth_transaction == 1) {
 +              if (!sta->challenge) {
 +                      /* Generate a pseudo-random challenge */
 +                      u8 key[8];
 +                      struct os_time now;
 +                      int r;
 +                      sta->challenge = os_zalloc(WLAN_AUTH_CHALLENGE_LEN);
 +                      if (sta->challenge == NULL)
 +                              return WLAN_STATUS_UNSPECIFIED_FAILURE;
 +
 +                      os_get_time(&now);
 +                      r = os_random();
 +                      os_memcpy(key, &now.sec, 4);
 +                      os_memcpy(key + 4, &r, 4);
 +                      rc4_skip(key, sizeof(key), 0,
 +                               sta->challenge, WLAN_AUTH_CHALLENGE_LEN);
 +              }
 +              return 0;
 +      }
 +
 +      if (auth_transaction != 3)
 +              return WLAN_STATUS_UNSPECIFIED_FAILURE;
 +
 +      /* Transaction 3 */
 +      if (!iswep || !sta->challenge || !challenge ||
 +          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 "
 +                             "challenge-response");
 +              return WLAN_STATUS_CHALLENGE_FAIL;
 +      }
 +
 +      hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
 +                     HOSTAPD_LEVEL_DEBUG,
 +                     "authentication OK (shared key)");
 +      sta->flags |= WLAN_STA_AUTH;
 +      wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH);
 +      os_free(sta->challenge);
 +      sta->challenge = NULL;
 +
 +      return 0;
 +}
++#endif /* CONFIG_NO_RC4 */
 +
 +
 +static void send_auth_reply(struct hostapd_data *hapd,
 +                          const u8 *dst, const u8 *bssid,
 +                          u16 auth_alg, u16 auth_transaction, u16 resp,
 +                          const u8 *ies, size_t ies_len)
 +{
 +      struct ieee80211_mgmt *reply;
 +      u8 *buf;
 +      size_t rlen;
 +
 +      rlen = IEEE80211_HDRLEN + sizeof(reply->u.auth) + ies_len;
 +      buf = os_zalloc(rlen);
 +      if (buf == NULL)
 +              return;
 +
 +      reply = (struct ieee80211_mgmt *) buf;
 +      reply->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
 +                                          WLAN_FC_STYPE_AUTH);
 +      os_memcpy(reply->da, dst, ETH_ALEN);
 +      os_memcpy(reply->sa, hapd->own_addr, ETH_ALEN);
 +      os_memcpy(reply->bssid, bssid, ETH_ALEN);
 +
 +      reply->u.auth.auth_alg = host_to_le16(auth_alg);
 +      reply->u.auth.auth_transaction = host_to_le16(auth_transaction);
 +      reply->u.auth.status_code = host_to_le16(resp);
 +
 +      if (ies && ies_len)
 +              os_memcpy(reply->u.auth.variable, ies, ies_len);
 +
 +      wpa_printf(MSG_DEBUG, "authentication reply: STA=" MACSTR
 +                 " auth_alg=%d auth_transaction=%d resp=%d (IE len=%lu)",
 +                 MAC2STR(dst), auth_alg, auth_transaction,
 +                 resp, (unsigned long) ies_len);
 +      if (hostapd_drv_send_mlme(hapd, reply, rlen, 0) < 0)
 +              wpa_printf(MSG_INFO, "send_auth_reply: send");
 +
 +      os_free(buf);
 +}
 +
 +
 +#ifdef CONFIG_IEEE80211R
 +static void handle_auth_ft_finish(void *ctx, const u8 *dst, const u8 *bssid,
 +                                u16 auth_transaction, u16 status,
 +                                const u8 *ies, size_t ies_len)
 +{
 +      struct hostapd_data *hapd = ctx;
 +      struct sta_info *sta;
 +
 +      send_auth_reply(hapd, dst, bssid, WLAN_AUTH_FT, auth_transaction,
 +                      status, ies, ies_len);
 +
 +      if (status != WLAN_STATUS_SUCCESS)
 +              return;
 +
 +      sta = ap_get_sta(hapd, dst);
 +      if (sta == NULL)
 +              return;
 +
 +      hostapd_logger(hapd, dst, HOSTAPD_MODULE_IEEE80211,
 +                     HOSTAPD_LEVEL_DEBUG, "authentication OK (FT)");
 +      sta->flags |= WLAN_STA_AUTH;
 +      mlme_authenticate_indication(hapd, sta);
 +}
 +#endif /* CONFIG_IEEE80211R */
 +
 +
 +#ifdef CONFIG_SAE
 +
-               eloop_register_timeout(0, dot11RSNASAERetransPeriod * 1000,
 +#define dot11RSNASAESync 5            /* attempts */
 +
 +
 +static struct wpabuf * auth_build_sae_commit(struct hostapd_data *hapd,
 +                                           struct sta_info *sta, int update)
 +{
 +      struct wpabuf *buf;
 +
 +      if (hapd->conf->ssid.wpa_passphrase == NULL) {
 +              wpa_printf(MSG_DEBUG, "SAE: No password available");
 +              return NULL;
 +      }
 +
 +      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) {
 +              wpa_printf(MSG_DEBUG, "SAE: Could not pick PWE");
 +              return NULL;
 +      }
 +
 +      buf = wpabuf_alloc(SAE_COMMIT_MAX_LEN);
 +      if (buf == NULL)
 +              return NULL;
 +      sae_write_commit(sta->sae, buf, sta->sae->tmp ?
 +                       sta->sae->tmp->anti_clogging_token : NULL);
 +
 +      return buf;
 +}
 +
 +
 +static struct wpabuf * auth_build_sae_confirm(struct hostapd_data *hapd,
 +                                            struct sta_info *sta)
 +{
 +      struct wpabuf *buf;
 +
 +      buf = wpabuf_alloc(SAE_CONFIRM_MAX_LEN);
 +      if (buf == NULL)
 +              return NULL;
 +
 +      sae_write_confirm(sta->sae, buf);
 +
 +      return buf;
 +}
 +
 +
 +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;
 +      unsigned int open = 0;
 +
 +      if (hapd->conf->sae_anti_clogging_threshold == 0)
 +              return 1;
 +
 +      for (sta = hapd->sta_list; sta; sta = sta->next) {
 +              if (!sta->sae)
 +                      continue;
 +              if (sta->sae->state != SAE_COMMITTED &&
 +                  sta->sae->state != SAE_CONFIRMED)
 +                      continue;
 +              open++;
 +              if (open >= hapd->conf->sae_anti_clogging_threshold)
 +                      return 1;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +static int check_sae_token(struct hostapd_data *hapd, const u8 *addr,
 +                         const u8 *token, size_t token_len)
 +{
 +      u8 mac[SHA256_MAC_LEN];
 +
 +      if (token_len != SHA256_MAC_LEN)
 +              return -1;
 +      if (hmac_sha256(hapd->sae_token_key, sizeof(hapd->sae_token_key),
 +                      addr, ETH_ALEN, mac) < 0 ||
 +          os_memcmp_const(token, mac, SHA256_MAC_LEN) != 0)
 +              return -1;
 +
 +      return 0;
 +}
 +
 +
 +static struct wpabuf * auth_build_token_req(struct hostapd_data *hapd,
 +                                          int group, const u8 *addr)
 +{
 +      struct wpabuf *buf;
 +      u8 *token;
 +      struct os_reltime now;
 +
 +      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 = now;
 +      }
 +
 +      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);
 +
 +      return buf;
 +}
 +
 +
 +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,
++              eloop_register_timeout(0,
++                                     hapd->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,
++              eloop_register_timeout(0,
++                                     hapd->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);
-                       ret = auth_sae_send_commit(hapd, sta, bssid, 1);
++      eloop_register_timeout(0, hapd->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++;
 +
-       resp = copy_sta_ht_capab(hapd, sta, elems.ht_capabilities,
-                                elems.ht_capabilities_len);
++                      ret = auth_sae_send_commit(hapd, sta, bssid, 0);
 +                      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,
 +                          u16 auth_transaction, u16 status_code)
 +{
 +      u16 resp = WLAN_STATUS_SUCCESS;
 +      struct wpabuf *data = NULL;
 +
 +      if (!sta->sae) {
 +              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, *pos, *end;
 +              size_t token_len = 0;
 +              hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
 +                             HOSTAPD_LEVEL_DEBUG,
 +                             "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,
 +                                      &token_len, hapd->conf->sae_groups);
++              if (resp == SAE_SILENTLY_DISCARD) {
++                      wpa_printf(MSG_DEBUG,
++                                 "SAE: Drop commit message from " MACSTR " due to reflection attack",
++                                 MAC2STR(sta->addr));
++                      return;
++              }
 +              if (token && check_sae_token(hapd, sta->addr, token, token_len)
 +                  < 0) {
 +                      wpa_printf(MSG_DEBUG, "SAE: Drop commit message with "
 +                                 "incorrect token from " MACSTR,
 +                                 MAC2STR(sta->addr));
 +                      return;
 +              }
 +
 +              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) {
 +              hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
 +                             HOSTAPD_LEVEL_DEBUG,
 +                             "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;
 +                              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 (status=%u)",
 +                             auth_transaction, status_code);
 +              if (status_code != WLAN_STATUS_SUCCESS)
 +                      return;
 +              resp = WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION;
 +      }
 +
 +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 */
 +
 +
 +static void handle_auth(struct hostapd_data *hapd,
 +                      const struct ieee80211_mgmt *mgmt, size_t len)
 +{
 +      u16 auth_alg, auth_transaction, status_code;
 +      u16 resp = WLAN_STATUS_SUCCESS;
 +      struct sta_info *sta = NULL;
 +      int res;
 +      u16 fc;
 +      const u8 *challenge = NULL;
 +      u32 session_timeout, acct_interim_interval;
 +      int vlan_id = 0;
 +      struct hostapd_sta_wpa_psk_short *psk = NULL;
 +      u8 resp_ies[2 + WLAN_AUTH_CHALLENGE_LEN];
 +      size_t resp_ies_len = 0;
 +      char *identity = NULL;
 +      char *radius_cui = NULL;
 +      u16 seq_ctrl;
 +
 +      if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) {
 +              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.0 &&
 +          drand48() < hapd->iconf->ignore_auth_probability) {
 +              wpa_printf(MSG_INFO,
 +                         "TESTING: ignoring auth frame from " MACSTR,
 +                         MAC2STR(mgmt->sa));
 +              return;
 +      }
 +#endif /* CONFIG_TESTING_OPTIONS */
 +
 +      auth_alg = le_to_host16(mgmt->u.auth.auth_alg);
 +      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 &&
 +          mgmt->u.auth.variable[0] == WLAN_EID_CHALLENGE &&
 +          mgmt->u.auth.variable[1] == WLAN_AUTH_CHALLENGE_LEN)
 +              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 "
 +                 "seq_ctrl=0x%x%s",
 +                 MAC2STR(mgmt->sa), auth_alg, auth_transaction,
 +                 status_code, !!(fc & WLAN_FC_ISWEP),
 +                 challenge ? " challenge" : "",
 +                 seq_ctrl, (fc & WLAN_FC_RETRY) ? " retry" : "");
 +
++#ifdef CONFIG_NO_RC4
++      if (auth_alg == WLAN_AUTH_SHARED_KEY) {
++              wpa_printf(MSG_INFO,
++                         "Unsupported authentication algorithm (%d)",
++                         auth_alg);
++              resp = WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG;
++              goto fail;
++      }
++#endif /* CONFIG_NO_RC4 */
++
 +      if (hapd->tkip_countermeasures) {
 +              resp = WLAN_REASON_MICHAEL_MIC_FAILURE;
 +              goto fail;
 +      }
 +
 +      if (!(((hapd->conf->auth_algs & WPA_AUTH_ALG_OPEN) &&
 +             auth_alg == WLAN_AUTH_OPEN) ||
 +#ifdef CONFIG_IEEE80211R
 +            (hapd->conf->wpa && wpa_key_mgmt_ft(hapd->conf->wpa_key_mgmt) &&
 +             auth_alg == WLAN_AUTH_FT) ||
 +#endif /* CONFIG_IEEE80211R */
 +#ifdef CONFIG_SAE
 +            (hapd->conf->wpa && wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt) &&
 +             auth_alg == WLAN_AUTH_SAE) ||
 +#endif /* CONFIG_SAE */
 +            ((hapd->conf->auth_algs & WPA_AUTH_ALG_SHARED) &&
 +             auth_alg == WLAN_AUTH_SHARED_KEY))) {
 +              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))) {
 +              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) {
 +              wpa_printf(MSG_INFO, "Station " MACSTR " not allowed to authenticate",
 +                         MAC2STR(mgmt->sa));
 +              resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
 +              goto fail;
 +      }
 +
++      if (hapd->conf->no_auth_if_seen_on) {
++              struct hostapd_data *other;
++
++              other = sta_track_seen_on(hapd->iface, mgmt->sa,
++                                        hapd->conf->no_auth_if_seen_on);
++              if (other) {
++                      u8 *pos;
++                      u32 info;
++                      u8 op_class, channel, phytype;
++
++                      wpa_printf(MSG_DEBUG, "%s: Reject authentication from "
++                                 MACSTR " since STA has been seen on %s",
++                                 hapd->conf->iface, MAC2STR(mgmt->sa),
++                                 hapd->conf->no_auth_if_seen_on);
++
++                      resp = WLAN_STATUS_REJECTED_WITH_SUGGESTED_BSS_TRANSITION;
++                      pos = &resp_ies[0];
++                      *pos++ = WLAN_EID_NEIGHBOR_REPORT;
++                      *pos++ = 13;
++                      os_memcpy(pos, other->own_addr, ETH_ALEN);
++                      pos += ETH_ALEN;
++                      info = 0; /* TODO: BSSID Information */
++                      WPA_PUT_LE32(pos, info);
++                      pos += 4;
++                      if (other->iconf->hw_mode == HOSTAPD_MODE_IEEE80211AD)
++                              phytype = 8; /* dmg */
++                      else if (other->iconf->ieee80211ac)
++                              phytype = 9; /* vht */
++                      else if (other->iconf->ieee80211n)
++                              phytype = 7; /* ht */
++                      else if (other->iconf->hw_mode ==
++                               HOSTAPD_MODE_IEEE80211A)
++                              phytype = 4; /* ofdm */
++                      else if (other->iconf->hw_mode ==
++                               HOSTAPD_MODE_IEEE80211G)
++                              phytype = 6; /* erp */
++                      else
++                              phytype = 5; /* hrdsss */
++                      if (ieee80211_freq_to_channel_ext(
++                                  hostapd_hw_get_freq(other,
++                                                      other->iconf->channel),
++                                  other->iconf->secondary_channel,
++                                  other->iconf->ieee80211ac,
++                                  &op_class, &channel) == NUM_HOSTAPD_MODES) {
++                              op_class = 0;
++                              channel = other->iconf->channel;
++                      }
++                      *pos++ = op_class;
++                      *pos++ = channel;
++                      *pos++ = phytype;
++                      resp_ies_len = pos - &resp_ies[0];
++                      goto fail;
++              }
++      }
++
 +      res = hostapd_allowed_address(hapd, mgmt->sa, (u8 *) mgmt, len,
 +                                    &session_timeout,
 +                                    &acct_interim_interval, &vlan_id,
 +                                    &psk, &identity, &radius_cui);
 +
 +      if (res == HOSTAPD_ACL_REJECT) {
 +              wpa_printf(MSG_INFO, "Station " MACSTR " not allowed to authenticate",
 +                         MAC2STR(mgmt->sa));
 +              resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
 +              goto fail;
 +      }
 +      if (res == HOSTAPD_ACL_PENDING) {
 +              wpa_printf(MSG_DEBUG, "Authentication frame from " MACSTR
 +                         " waiting for an external authentication",
 +                         MAC2STR(mgmt->sa));
 +              /* Authentication code will re-send the authentication frame
 +               * after it has received (and cached) information from the
 +               * external source. */
 +              return;
 +      }
 +
 +      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)) {
 +                      hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
 +                                     HOSTAPD_LEVEL_INFO, "Invalid VLAN ID "
 +                                     "%d received from RADIUS server",
 +                                     vlan_id);
 +                      resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
 +                      goto fail;
 +              }
 +              sta->vlan_id = vlan_id;
 +              hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
 +                             HOSTAPD_LEVEL_INFO, "VLAN ID %d", sta->vlan_id);
 +      }
 +
 +      hostapd_free_psk_list(sta->psk);
 +      if (hapd->conf->wpa_psk_radius != PSK_RADIUS_IGNORED) {
 +              sta->psk = psk;
 +              psk = NULL;
 +      } else {
 +              sta->psk = NULL;
 +      }
 +
 +      sta->identity = identity;
 +      identity = NULL;
 +      sta->radius_cui = radius_cui;
 +      radius_cui = NULL;
 +
 +      sta->flags &= ~WLAN_STA_PREAUTH;
 +      ieee802_1x_notify_pre_auth(sta->eapol_sm, 0);
 +
 +      if (hapd->conf->acct_interim_interval == 0 && acct_interim_interval)
 +              sta->acct_interim_interval = acct_interim_interval;
 +      if (res == HOSTAPD_ACL_ACCEPT_TIMEOUT)
 +              ap_sta_session_timeout(hapd, sta, session_timeout);
 +      else
 +              ap_sta_no_session_timeout(hapd, sta);
 +
 +      switch (auth_alg) {
 +      case WLAN_AUTH_OPEN:
 +              hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
 +                             HOSTAPD_LEVEL_DEBUG,
 +                             "authentication OK (open system)");
 +              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);
 +              break;
++#ifndef CONFIG_NO_RC4
 +      case WLAN_AUTH_SHARED_KEY:
 +              resp = auth_shared_key(hapd, sta, auth_transaction, challenge,
 +                                     fc & WLAN_FC_ISWEP);
 +              sta->auth_alg = WLAN_AUTH_SHARED_KEY;
 +              mlme_authenticate_indication(hapd, sta);
 +              if (sta->challenge && auth_transaction == 1) {
 +                      resp_ies[0] = WLAN_EID_CHALLENGE;
 +                      resp_ies[1] = WLAN_AUTH_CHALLENGE_LEN;
 +                      os_memcpy(resp_ies + 2, sta->challenge,
 +                                WLAN_AUTH_CHALLENGE_LEN);
 +                      resp_ies_len = 2 + WLAN_AUTH_CHALLENGE_LEN;
 +              }
 +              break;
++#endif /* CONFIG_NO_RC4 */
 +#ifdef CONFIG_IEEE80211R
 +      case WLAN_AUTH_FT:
 +              sta->auth_alg = WLAN_AUTH_FT;
 +              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, "FT: Failed to initialize WPA "
 +                                 "state machine");
 +                      resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
 +                      goto fail;
 +              }
 +              wpa_ft_process_auth(sta->wpa_sm, mgmt->bssid,
 +                                  auth_transaction, mgmt->u.auth.variable,
 +                                  len - IEEE80211_HDRLEN -
 +                                  sizeof(mgmt->u.auth),
 +                                  handle_auth_ft_finish, hapd);
 +              /* handle_auth_ft_finish() callback will complete auth. */
 +              return;
 +#endif /* CONFIG_IEEE80211R */
 +#ifdef CONFIG_SAE
 +      case WLAN_AUTH_SAE:
 +#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 */
 +      }
 +
 + fail:
 +      os_free(identity);
 +      os_free(radius_cui);
 +      hostapd_free_psk_list(psk);
 +
 +      send_auth_reply(hapd, mgmt->sa, mgmt->bssid, auth_alg,
 +                      auth_transaction + 1, resp, resp_ies, resp_ies_len);
 +}
 +
 +
 +static int hostapd_get_aid(struct hostapd_data *hapd, struct sta_info *sta)
 +{
 +      int i, j = 32, aid;
 +
 +      /* get a unique AID */
 +      if (sta->aid > 0) {
 +              wpa_printf(MSG_DEBUG, "  old AID %d", sta->aid);
 +              return 0;
 +      }
 +
 +      for (i = 0; i < AID_WORDS; i++) {
 +              if (hapd->sta_aid[i] == (u32) -1)
 +                      continue;
 +              for (j = 0; j < 32; j++) {
 +                      if (!(hapd->sta_aid[i] & BIT(j)))
 +                              break;
 +              }
 +              if (j < 32)
 +                      break;
 +      }
 +      if (j == 32)
 +              return -1;
 +      aid = i * 32 + j + 1;
 +      if (aid > 2007)
 +              return -1;
 +
 +      sta->aid = aid;
 +      hapd->sta_aid[i] |= BIT(j);
 +      wpa_printf(MSG_DEBUG, "  new AID %d", sta->aid);
 +      return 0;
 +}
 +
 +
 +static u16 check_ssid(struct hostapd_data *hapd, struct sta_info *sta,
 +                    const u8 *ssid_ie, size_t ssid_ie_len)
 +{
 +      if (ssid_ie == NULL)
 +              return WLAN_STATUS_UNSPECIFIED_FAILURE;
 +
 +      if (ssid_ie_len != hapd->conf->ssid.ssid_len ||
 +          os_memcmp(ssid_ie, hapd->conf->ssid.ssid, ssid_ie_len) != 0) {
 +              hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
 +                             HOSTAPD_LEVEL_INFO,
 +                             "Station tried to associate with unknown SSID "
 +                             "'%s'", wpa_ssid_txt(ssid_ie, ssid_ie_len));
 +              return WLAN_STATUS_UNSPECIFIED_FAILURE;
 +      }
 +
 +      return WLAN_STATUS_SUCCESS;
 +}
 +
 +
 +static u16 check_wmm(struct hostapd_data *hapd, struct sta_info *sta,
 +                   const u8 *wmm_ie, size_t wmm_ie_len)
 +{
 +      sta->flags &= ~WLAN_STA_WMM;
 +      sta->qosinfo = 0;
 +      if (wmm_ie && hapd->conf->wmm_enabled) {
 +              struct wmm_information_element *wmm;
 +
 +              if (!hostapd_eid_wmm_valid(hapd, wmm_ie, wmm_ie_len)) {
 +                      hostapd_logger(hapd, sta->addr,
 +                                     HOSTAPD_MODULE_WPA,
 +                                     HOSTAPD_LEVEL_DEBUG,
 +                                     "invalid WMM element in association "
 +                                     "request");
 +                      return WLAN_STATUS_UNSPECIFIED_FAILURE;
 +              }
 +
 +              sta->flags |= WLAN_STA_WMM;
 +              wmm = (struct wmm_information_element *) wmm_ie;
 +              sta->qosinfo = wmm->qos_info;
 +      }
 +      return WLAN_STATUS_SUCCESS;
 +}
 +
 +
 +static u16 copy_supp_rates(struct hostapd_data *hapd, struct sta_info *sta,
 +                         struct ieee802_11_elems *elems)
 +{
 +      if (!elems->supp_rates) {
 +              hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
 +                             HOSTAPD_LEVEL_DEBUG,
 +                             "No supported rates element in AssocReq");
 +              return WLAN_STATUS_UNSPECIFIED_FAILURE;
 +      }
 +
 +      if (elems->supp_rates_len + elems->ext_supp_rates_len >
 +          sizeof(sta->supported_rates)) {
 +              hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
 +                             HOSTAPD_LEVEL_DEBUG,
 +                             "Invalid supported rates element length %d+%d",
 +                             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;
 +}
 +
 +
 +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)
 +{
 +      struct ieee802_11_elems elems;
 +      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,
 +                             HOSTAPD_LEVEL_INFO, "Station sent an invalid "
 +                             "association request");
 +              return WLAN_STATUS_UNSPECIFIED_FAILURE;
 +      }
 +
 +      resp = check_ssid(hapd, sta, elems.ssid, elems.ssid_len);
 +      if (resp != WLAN_STATUS_SUCCESS)
 +              return resp;
 +      resp = check_wmm(hapd, sta, elems.wmm, elems.wmm_len);
 +      if (resp != WLAN_STATUS_SUCCESS)
 +              return resp;
 +      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;
 +#ifdef CONFIG_IEEE80211N
-       resp = copy_sta_vht_capab(hapd, sta, elems.vht_capabilities,
-                                 elems.vht_capabilities_len);
-       if (resp != WLAN_STATUS_SUCCESS)
-               return resp;
++      resp = copy_sta_ht_capab(hapd, sta, elems.ht_capabilities);
 +      if (resp != WLAN_STATUS_SUCCESS)
 +              return resp;
 +      if (hapd->iconf->ieee80211n && hapd->iconf->require_ht &&
 +          !(sta->flags & WLAN_STA_HT)) {
 +              hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
 +                             HOSTAPD_LEVEL_INFO, "Station does not support "
 +                             "mandatory HT PHY - reject association");
 +              return WLAN_STATUS_ASSOC_DENIED_NO_HT;
 +      }
 +#endif /* CONFIG_IEEE80211N */
 +
 +#ifdef CONFIG_IEEE80211AC
-       resp = set_sta_vht_opmode(hapd, sta, elems.vht_opmode_notif);
-       if (resp != WLAN_STATUS_SUCCESS)
-               return resp;
++      if (hapd->iconf->ieee80211ac) {
++              resp = copy_sta_vht_capab(hapd, sta, elems.vht_capabilities);
++              if (resp != WLAN_STATUS_SUCCESS)
++                      return resp;
 +
-               host_to_le16(hostapd_own_capab_info(hapd, sta, 0));
++              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_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;
 +      } else if ((hapd->conf->wpa & WPA_PROTO_WPA) &&
 +                 elems.wpa_ie) {
 +              wpa_ie = elems.wpa_ie;
 +              wpa_ie_len = elems.wpa_ie_len;
 +      } else {
 +              wpa_ie = NULL;
 +              wpa_ie_len = 0;
 +      }
 +
 +#ifdef CONFIG_WPS
 +      sta->flags &= ~(WLAN_STA_WPS | WLAN_STA_MAYBE_WPS | WLAN_STA_WPS2);
 +      if (hapd->conf->wps_state && elems.wps_ie) {
 +              wpa_printf(MSG_DEBUG, "STA included WPS IE in (Re)Association "
 +                         "Request - assume WPS is used");
 +              sta->flags |= WLAN_STA_WPS;
 +              wpabuf_free(sta->wps_ie);
 +              sta->wps_ie = ieee802_11_vendor_ie_concat(ies, ies_len,
 +                                                        WPS_IE_VENDOR_TYPE);
 +              if (sta->wps_ie && wps_is_20(sta->wps_ie)) {
 +                      wpa_printf(MSG_DEBUG, "WPS: STA supports WPS 2.0");
 +                      sta->flags |= WLAN_STA_WPS2;
 +              }
 +              wpa_ie = NULL;
 +              wpa_ie_len = 0;
 +              if (sta->wps_ie && wps_validate_assoc_req(sta->wps_ie) < 0) {
 +                      wpa_printf(MSG_DEBUG, "WPS: Invalid WPS IE in "
 +                                 "(Re)Association Request - reject");
 +                      return WLAN_STATUS_INVALID_IE;
 +              }
 +      } else if (hapd->conf->wps_state && wpa_ie == NULL) {
 +              wpa_printf(MSG_DEBUG, "STA did not include WPA/RSN IE in "
 +                         "(Re)Association Request - possible WPS use");
 +              sta->flags |= WLAN_STA_MAYBE_WPS;
 +      } else
 +#endif /* CONFIG_WPS */
 +      if (hapd->conf->wpa && wpa_ie == NULL) {
 +              hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
 +                             HOSTAPD_LEVEL_INFO,
 +                             "No WPA/RSN IE in association request");
 +              return WLAN_STATUS_INVALID_IE;
 +      }
 +
 +      if (hapd->conf->wpa && wpa_ie) {
 +              int res;
 +              wpa_ie -= 2;
 +              wpa_ie_len += 2;
 +              if (sta->wpa_sm == NULL)
 +                      sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth,
 +                                                      sta->addr,
 +                                                      p2p_dev_addr);
 +              if (sta->wpa_sm == NULL) {
 +                      wpa_printf(MSG_WARNING, "Failed to initialize WPA "
 +                                 "state machine");
 +                      return WLAN_STATUS_UNSPECIFIED_FAILURE;
 +              }
 +              res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm,
 +                                        wpa_ie, wpa_ie_len,
 +                                        elems.mdie, elems.mdie_len);
 +              if (res == WPA_INVALID_GROUP)
 +                      resp = WLAN_STATUS_GROUP_CIPHER_NOT_VALID;
 +              else if (res == WPA_INVALID_PAIRWISE)
 +                      resp = WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID;
 +              else if (res == WPA_INVALID_AKMP)
 +                      resp = WLAN_STATUS_AKMP_NOT_VALID;
 +              else if (res == WPA_ALLOC_FAIL)
 +                      resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
 +#ifdef CONFIG_IEEE80211W
 +              else if (res == WPA_MGMT_FRAME_PROTECTION_VIOLATION)
 +                      resp = WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION;
 +              else if (res == WPA_INVALID_MGMT_GROUP_CIPHER)
 +                      resp = WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION;
 +#endif /* CONFIG_IEEE80211W */
 +              else if (res == WPA_INVALID_MDIE)
 +                      resp = WLAN_STATUS_INVALID_MDIE;
 +              else if (res != WPA_IE_OK)
 +                      resp = WLAN_STATUS_INVALID_IE;
 +              if (resp != WLAN_STATUS_SUCCESS)
 +                      return resp;
 +#ifdef CONFIG_IEEE80211W
 +              if ((sta->flags & WLAN_STA_MFP) && !sta->sa_query_timed_out &&
 +                  sta->sa_query_count > 0)
 +                      ap_check_sa_query_timeout(hapd, sta);
 +              if ((sta->flags & WLAN_STA_MFP) && !sta->sa_query_timed_out &&
 +                  (!reassoc || sta->auth_alg != WLAN_AUTH_FT)) {
 +                      /*
 +                       * STA has already been associated with MFP and SA
 +                       * Query timeout has not been reached. Reject the
 +                       * association attempt temporarily and start SA Query,
 +                       * if one is not pending.
 +                       */
 +
 +                      if (sta->sa_query_count == 0)
 +                              ap_sta_start_sa_query(hapd, sta);
 +
 +                      return WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY;
 +              }
 +
 +              if (wpa_auth_uses_mfp(sta->wpa_sm))
 +                      sta->flags |= WLAN_STA_MFP;
 +              else
 +                      sta->flags &= ~WLAN_STA_MFP;
 +#endif /* CONFIG_IEEE80211W */
 +
 +#ifdef CONFIG_IEEE80211R
 +              if (sta->auth_alg == WLAN_AUTH_FT) {
 +                      if (!reassoc) {
 +                              wpa_printf(MSG_DEBUG, "FT: " MACSTR " tried "
 +                                         "to use association (not "
 +                                         "re-association) with FT auth_alg",
 +                                         MAC2STR(sta->addr));
 +                              return WLAN_STATUS_UNSPECIFIED_FAILURE;
 +                      }
 +
 +                      resp = wpa_ft_validate_reassoc(sta->wpa_sm, ies,
 +                                                     ies_len);
 +                      if (resp != WLAN_STATUS_SUCCESS)
 +                              return resp;
 +              }
 +#endif /* CONFIG_IEEE80211R */
 +
 +#ifdef CONFIG_SAE
 +              if (wpa_auth_uses_sae(sta->wpa_sm) &&
 +                  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);
 +                      return WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG;
 +              }
 +#endif /* CONFIG_SAE */
 +
 +#ifdef CONFIG_IEEE80211N
 +              if ((sta->flags & (WLAN_STA_HT | WLAN_STA_VHT)) &&
 +                  wpa_auth_get_pairwise(sta->wpa_sm) == WPA_CIPHER_TKIP) {
 +                      hostapd_logger(hapd, sta->addr,
 +                                     HOSTAPD_MODULE_IEEE80211,
 +                                     HOSTAPD_LEVEL_INFO,
 +                                     "Station tried to use TKIP with HT "
 +                                     "association");
 +                      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
 +      p2p_group_notif_assoc(hapd->p2p_group, sta->addr, ies, ies_len);
 +#endif /* CONFIG_P2P */
 +
 +#ifdef CONFIG_HS20
 +      wpabuf_free(sta->hs20_ie);
 +      if (elems.hs20 && elems.hs20_len > 4) {
 +              sta->hs20_ie = wpabuf_alloc_copy(elems.hs20 + 4,
 +                                               elems.hs20_len - 4);
 +      } else
 +              sta->hs20_ie = NULL;
 +#endif /* CONFIG_HS20 */
 +
++#ifdef CONFIG_FST
++      wpabuf_free(sta->mb_ies);
++      if (hapd->iface->fst)
++              sta->mb_ies = mb_ies_by_info(&elems.mb_ies);
++      else
++              sta->mb_ies = NULL;
++#endif /* CONFIG_FST */
++
 +      return WLAN_STATUS_SUCCESS;
 +}
 +
 +
 +static void send_deauth(struct hostapd_data *hapd, const u8 *addr,
 +                      u16 reason_code)
 +{
 +      int send_len;
 +      struct ieee80211_mgmt reply;
 +
 +      os_memset(&reply, 0, sizeof(reply));
 +      reply.frame_control =
 +              IEEE80211_FC(WLAN_FC_TYPE_MGMT, WLAN_FC_STYPE_DEAUTH);
 +      os_memcpy(reply.da, addr, ETH_ALEN);
 +      os_memcpy(reply.sa, hapd->own_addr, ETH_ALEN);
 +      os_memcpy(reply.bssid, hapd->own_addr, ETH_ALEN);
 +
 +      send_len = IEEE80211_HDRLEN + sizeof(reply.u.deauth);
 +      reply.u.deauth.reason_code = host_to_le16(reason_code);
 +
 +      if (hostapd_drv_send_mlme(hapd, &reply, send_len, 0) < 0)
 +              wpa_printf(MSG_INFO, "Failed to send deauth: %s",
 +                         strerror(errno));
 +}
 +
 +
 +static void send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta,
 +                          u16 status_code, int reassoc, const u8 *ies,
 +                          size_t ies_len)
 +{
 +      int send_len;
 +      u8 buf[sizeof(struct ieee80211_mgmt) + 1024];
 +      struct ieee80211_mgmt *reply;
 +      u8 *p;
 +
 +      os_memset(buf, 0, sizeof(buf));
 +      reply = (struct ieee80211_mgmt *) buf;
 +      reply->frame_control =
 +              IEEE80211_FC(WLAN_FC_TYPE_MGMT,
 +                           (reassoc ? WLAN_FC_STYPE_REASSOC_RESP :
 +                            WLAN_FC_STYPE_ASSOC_RESP));
 +      os_memcpy(reply->da, sta->addr, ETH_ALEN);
 +      os_memcpy(reply->sa, hapd->own_addr, ETH_ALEN);
 +      os_memcpy(reply->bssid, hapd->own_addr, ETH_ALEN);
 +
 +      send_len = IEEE80211_HDRLEN;
 +      send_len += sizeof(reply->u.assoc_resp);
 +      reply->u.assoc_resp.capab_info =
-               if (mgmt->u.action.u.public_action.action ==
++              host_to_le16(hostapd_own_capab_info(hapd));
 +      reply->u.assoc_resp.status_code = host_to_le16(status_code);
 +      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 */
 +      p = hostapd_eid_ext_supp_rates(hapd, p);
 +
 +#ifdef CONFIG_IEEE80211R
 +      if (status_code == WLAN_STATUS_SUCCESS) {
 +              /* IEEE 802.11r: Mobility Domain Information, Fast BSS
 +               * Transition Information, RSN, [RIC Response] */
 +              p = wpa_sm_write_assoc_resp_ies(sta->wpa_sm, p,
 +                                              buf + sizeof(buf) - p,
 +                                              sta->auth_alg, ies, ies_len);
 +      }
 +#endif /* CONFIG_IEEE80211R */
 +
 +#ifdef CONFIG_IEEE80211W
 +      if (status_code == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY)
 +              p = hostapd_eid_assoc_comeback_time(hapd, sta, p);
 +#endif /* CONFIG_IEEE80211W */
 +
 +#ifdef CONFIG_IEEE80211N
 +      p = hostapd_eid_ht_capabilities(hapd, p);
 +      p = hostapd_eid_ht_operation(hapd, p);
 +#endif /* CONFIG_IEEE80211N */
 +
 +#ifdef CONFIG_IEEE80211AC
 +      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_FST
++      if (hapd->iface->fst_ies) {
++              os_memcpy(p, wpabuf_head(hapd->iface->fst_ies),
++                        wpabuf_len(hapd->iface->fst_ies));
++              p += wpabuf_len(hapd->iface->fst_ies);
++      }
++#endif /* CONFIG_FST */
++
 +#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);
 +
 +#ifdef CONFIG_WPS
 +      if ((sta->flags & WLAN_STA_WPS) ||
 +          ((sta->flags & WLAN_STA_MAYBE_WPS) && hapd->conf->wpa)) {
 +              struct wpabuf *wps = wps_build_assoc_resp_ie();
 +              if (wps) {
 +                      os_memcpy(p, wpabuf_head(wps), wpabuf_len(wps));
 +                      p += wpabuf_len(wps);
 +                      wpabuf_free(wps);
 +              }
 +      }
 +#endif /* CONFIG_WPS */
 +
 +#ifdef CONFIG_P2P
 +      if (sta->p2p_ie) {
 +              struct wpabuf *p2p_resp_ie;
 +              enum p2p_status_code status;
 +              switch (status_code) {
 +              case WLAN_STATUS_SUCCESS:
 +                      status = P2P_SC_SUCCESS;
 +                      break;
 +              case WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA:
 +                      status = P2P_SC_FAIL_LIMIT_REACHED;
 +                      break;
 +              default:
 +                      status = P2P_SC_FAIL_INVALID_PARAMS;
 +                      break;
 +              }
 +              p2p_resp_ie = p2p_group_assoc_resp_ie(hapd->p2p_group, status);
 +              if (p2p_resp_ie) {
 +                      os_memcpy(p, wpabuf_head(p2p_resp_ie),
 +                                wpabuf_len(p2p_resp_ie));
 +                      p += wpabuf_len(p2p_resp_ie);
 +                      wpabuf_free(p2p_resp_ie);
 +              }
 +      }
 +#endif /* CONFIG_P2P */
 +
 +#ifdef CONFIG_P2P_MANAGER
 +      if (hapd->conf->p2p & P2P_MANAGE)
 +              p = hostapd_eid_p2p_manage(hapd, p);
 +#endif /* CONFIG_P2P_MANAGER */
 +
 +      send_len += p - reply->u.assoc_resp.variable;
 +
 +      if (hostapd_drv_send_mlme(hapd, reply, send_len, 0) < 0)
 +              wpa_printf(MSG_INFO, "Failed to send assoc resp: %s",
 +                         strerror(errno));
 +}
 +
 +
 +static void handle_assoc(struct hostapd_data *hapd,
 +                       const struct ieee80211_mgmt *mgmt, size_t len,
 +                       int reassoc)
 +{
 +      u16 capab_info, listen_interval, seq_ctrl, fc;
 +      u16 resp = WLAN_STATUS_SUCCESS;
 +      const u8 *pos;
 +      int left, i;
 +      struct sta_info *sta;
 +
 +      if (len < IEEE80211_HDRLEN + (reassoc ? sizeof(mgmt->u.reassoc_req) :
 +                                    sizeof(mgmt->u.assoc_req))) {
 +              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.0 &&
 +                  drand48() < hapd->iconf->ignore_reassoc_probability) {
 +                      wpa_printf(MSG_INFO,
 +                                 "TESTING: ignoring reassoc request from "
 +                                 MACSTR, MAC2STR(mgmt->sa));
 +                      return;
 +              }
 +      } else {
 +              if (hapd->iconf->ignore_assoc_probability > 0.0 &&
 +                  drand48() < hapd->iconf->ignore_assoc_probability) {
 +                      wpa_printf(MSG_INFO,
 +                                 "TESTING: ignoring assoc request from "
 +                                 MACSTR, MAC2STR(mgmt->sa));
 +                      return;
 +              }
 +      }
 +#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 " seq_ctrl=0x%x%s",
 +                         MAC2STR(mgmt->sa), capab_info, listen_interval,
 +                         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 {
 +              capab_info = le_to_host16(mgmt->u.assoc_req.capab_info);
 +              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 "
 +                         "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;
 +      }
 +
 +      sta = ap_get_sta(hapd, mgmt->sa);
 +#ifdef CONFIG_IEEE80211R
 +      if (sta && sta->auth_alg == WLAN_AUTH_FT &&
 +          (sta->flags & WLAN_STA_AUTH) == 0) {
 +              wpa_printf(MSG_DEBUG, "FT: Allow STA " MACSTR " to associate "
 +                         "prior to authentication since it is using "
 +                         "over-the-DS FT", MAC2STR(mgmt->sa));
 +      } else
 +#endif /* CONFIG_IEEE80211R */
 +      if (sta == NULL || (sta->flags & WLAN_STA_AUTH) == 0) {
 +              hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
 +                             HOSTAPD_LEVEL_INFO, "Station tried to "
 +                             "associate before authentication "
 +                             "(aid=%d flags=0x%x)",
 +                             sta ? sta->aid : -1,
 +                             sta ? sta->flags : 0);
 +              send_deauth(hapd, mgmt->sa,
 +                          WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA);
 +              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;
 +      }
 +
 +      if (listen_interval > hapd->conf->max_listen_interval) {
 +              hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
 +                             HOSTAPD_LEVEL_DEBUG,
 +                             "Too large Listen Interval (%d)",
 +                             listen_interval);
 +              resp = WLAN_STATUS_ASSOC_DENIED_LISTEN_INT_TOO_LARGE;
 +              goto fail;
 +      }
 +
 +      /* followed by SSID and Supported rates; and HT capabilities if 802.11n
 +       * is used */
 +      resp = check_assoc_ies(hapd, sta, pos, left, reassoc);
 +      if (resp != WLAN_STATUS_SUCCESS)
 +              goto fail;
 +
 +      if (hostapd_get_aid(hapd, sta) < 0) {
 +              hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
 +                             HOSTAPD_LEVEL_INFO, "No room for more AIDs");
 +              resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
 +              goto fail;
 +      }
 +
 +      sta->capability = capab_info;
 +      sta->listen_interval = listen_interval;
 +
 +      if (hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G)
 +              sta->flags |= WLAN_STA_NONERP;
 +      for (i = 0; i < sta->supported_rates_len; i++) {
 +              if ((sta->supported_rates[i] & 0x7f) > 22) {
 +                      sta->flags &= ~WLAN_STA_NONERP;
 +                      break;
 +              }
 +      }
 +      if (sta->flags & WLAN_STA_NONERP && !sta->nonerp_set) {
 +              sta->nonerp_set = 1;
 +              hapd->iface->num_sta_non_erp++;
 +              if (hapd->iface->num_sta_non_erp == 1)
 +                      ieee802_11_set_beacons(hapd->iface);
 +      }
 +
 +      if (!(sta->capability & WLAN_CAPABILITY_SHORT_SLOT_TIME) &&
 +          !sta->no_short_slot_time_set) {
 +              sta->no_short_slot_time_set = 1;
 +              hapd->iface->num_sta_no_short_slot_time++;
 +              if (hapd->iface->current_mode->mode ==
 +                  HOSTAPD_MODE_IEEE80211G &&
 +                  hapd->iface->num_sta_no_short_slot_time == 1)
 +                      ieee802_11_set_beacons(hapd->iface);
 +      }
 +
 +      if (sta->capability & WLAN_CAPABILITY_SHORT_PREAMBLE)
 +              sta->flags |= WLAN_STA_SHORT_PREAMBLE;
 +      else
 +              sta->flags &= ~WLAN_STA_SHORT_PREAMBLE;
 +
 +      if (!(sta->capability & WLAN_CAPABILITY_SHORT_PREAMBLE) &&
 +          !sta->no_short_preamble_set) {
 +              sta->no_short_preamble_set = 1;
 +              hapd->iface->num_sta_no_short_preamble++;
 +              if (hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G
 +                  && hapd->iface->num_sta_no_short_preamble == 1)
 +                      ieee802_11_set_beacons(hapd->iface);
 +      }
 +
 +#ifdef CONFIG_IEEE80211N
 +      update_ht_state(hapd, sta);
 +#endif /* CONFIG_IEEE80211N */
 +
 +      hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
 +                     HOSTAPD_LEVEL_DEBUG,
 +                     "association OK (aid %d)", sta->aid);
 +      /* Station will be marked associated, after it acknowledges AssocResp
 +       */
 +      sta->flags |= WLAN_STA_ASSOC_REQ_OK;
 +
 +#ifdef CONFIG_IEEE80211W
 +      if ((sta->flags & WLAN_STA_MFP) && sta->sa_query_timed_out) {
 +              wpa_printf(MSG_DEBUG, "Allowing %sassociation after timed out "
 +                         "SA Query procedure", reassoc ? "re" : "");
 +              /* TODO: Send a protected Disassociate frame to the STA using
 +               * the old key and Reason Code "Previous Authentication no
 +               * longer valid". Make sure this is only sent protected since
 +               * unprotected frame would be received by the STA that is now
 +               * trying to associate.
 +               */
 +      }
 +#endif /* CONFIG_IEEE80211W */
 +
 +      /* Make sure that the previously registered inactivity timer will not
 +       * remove the STA immediately. */
 +      sta->timeout_next = STA_NULLFUNC;
 +
 + fail:
 +      send_assoc_resp(hapd, sta, resp, reassoc, pos, left);
 +}
 +
 +
 +static void handle_disassoc(struct hostapd_data *hapd,
 +                          const struct ieee80211_mgmt *mgmt, size_t len)
 +{
 +      struct sta_info *sta;
 +
 +      if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.disassoc)) {
 +              wpa_printf(MSG_INFO, "handle_disassoc - too short payload (len=%lu)",
 +                         (unsigned long) len);
 +              return;
 +      }
 +
 +      wpa_printf(MSG_DEBUG, "disassocation: STA=" MACSTR " reason_code=%d",
 +                 MAC2STR(mgmt->sa),
 +                 le_to_host16(mgmt->u.disassoc.reason_code));
 +
 +      sta = ap_get_sta(hapd, mgmt->sa);
 +      if (sta == NULL) {
 +              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,
 +                     HOSTAPD_LEVEL_INFO, "disassociated");
 +      sta->acct_terminate_cause = RADIUS_ACCT_TERMINATE_CAUSE_USER_REQUEST;
 +      ieee802_1x_notify_port_enabled(sta->eapol_sm, 0);
 +      /* Stop Accounting and IEEE 802.1X sessions, but leave the STA
 +       * 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 ||
 +          sta->timeout_next == STA_DISASSOC) {
 +              sta->timeout_next = STA_DEAUTH;
 +              eloop_cancel_timeout(ap_handle_timer, hapd, sta);
 +              eloop_register_timeout(AP_DEAUTH_DELAY, 0, ap_handle_timer,
 +                                     hapd, sta);
 +      }
 +
 +      mlme_disassociate_indication(
 +              hapd, sta, le_to_host16(mgmt->u.disassoc.reason_code));
 +}
 +
 +
 +static void handle_deauth(struct hostapd_data *hapd,
 +                        const struct ieee80211_mgmt *mgmt, size_t len)
 +{
 +      struct sta_info *sta;
 +
 +      if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.deauth)) {
 +              wpa_msg(hapd->msg_ctx, MSG_DEBUG, "handle_deauth - too short "
 +                      "payload (len=%lu)", (unsigned long) len);
 +              return;
 +      }
 +
 +      wpa_msg(hapd->msg_ctx, MSG_DEBUG, "deauthentication: STA=" MACSTR
 +              " reason_code=%d",
 +              MAC2STR(mgmt->sa), le_to_host16(mgmt->u.deauth.reason_code));
 +
 +      sta = ap_get_sta(hapd, mgmt->sa);
 +      if (sta == NULL) {
 +              wpa_msg(hapd->msg_ctx, MSG_DEBUG, "Station " MACSTR " trying "
 +                      "to deauthenticate, but it is not authenticated",
 +                      MAC2STR(mgmt->sa));
 +              return;
 +      }
 +
 +      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);
 +      hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
 +                     HOSTAPD_LEVEL_DEBUG, "deauthenticated");
 +      mlme_deauthenticate_indication(
 +              hapd, sta, le_to_host16(mgmt->u.deauth.reason_code));
 +      sta->acct_terminate_cause = RADIUS_ACCT_TERMINATE_CAUSE_USER_REQUEST;
 +      ieee802_1x_notify_port_enabled(sta->eapol_sm, 0);
 +      ap_free_sta(hapd, sta);
 +}
 +
 +
 +static void handle_beacon(struct hostapd_data *hapd,
 +                        const struct ieee80211_mgmt *mgmt, size_t len,
 +                        struct hostapd_frame_info *fi)
 +{
 +      struct ieee802_11_elems elems;
 +
 +      if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.beacon)) {
 +              wpa_printf(MSG_INFO, "handle_beacon - too short payload (len=%lu)",
 +                         (unsigned long) len);
 +              return;
 +      }
 +
 +      (void) ieee802_11_parse_elems(mgmt->u.beacon.variable,
 +                                    len - (IEEE80211_HDRLEN +
 +                                           sizeof(mgmt->u.beacon)), &elems,
 +                                    0);
 +
 +      ap_list_process_beacon(hapd->iface, mgmt, &elems, fi);
 +}
 +
 +
 +#ifdef CONFIG_IEEE80211W
 +
 +static int hostapd_sa_query_action(struct hostapd_data *hapd,
 +                                 const struct ieee80211_mgmt *mgmt,
 +                                 size_t len)
 +{
 +      const u8 *end;
 +
 +      end = mgmt->u.action.u.sa_query_resp.trans_id +
 +              WLAN_SA_QUERY_TR_ID_LEN;
 +      if (((u8 *) mgmt) + len < end) {
 +              wpa_printf(MSG_DEBUG, "IEEE 802.11: Too short SA Query Action "
 +                         "frame (len=%lu)", (unsigned long) len);
 +              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;
 +}
 +
 +
 +static int robust_action_frame(u8 category)
 +{
 +      return category != WLAN_ACTION_PUBLIC &&
 +              category != WLAN_ACTION_HT;
 +}
 +#endif /* CONFIG_IEEE80211W */
 +
 +
 +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);
 +
 +      if (len < IEEE80211_HDRLEN + 1) {
 +              hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
 +                             HOSTAPD_LEVEL_DEBUG,
 +                             "handle_action - too short payload (len=%lu)",
 +                             (unsigned long) len);
 +              return 0;
 +      }
 +
 +      if (mgmt->u.action.category != WLAN_ACTION_PUBLIC &&
 +          (sta == NULL || !(sta->flags & WLAN_STA_ASSOC))) {
 +              wpa_printf(MSG_DEBUG, "IEEE 802.11: Ignored Action "
 +                         "frame (category=%u) from unassociated STA " MACSTR,
 +                         MAC2STR(mgmt->sa), mgmt->u.action.category);
 +              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)) {
 +              hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
 +                             HOSTAPD_LEVEL_DEBUG,
 +                             "Dropped unprotected Robust Action frame from "
 +                             "an MFP STA");
 +              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 (!sta ||
 +                  wpa_ft_action_rx(sta->wpa_sm, (u8 *) &mgmt->u.action,
 +                                   len - IEEE80211_HDRLEN))
 +                      break;
 +              return 1;
 +#endif /* CONFIG_IEEE80211R */
 +      case WLAN_ACTION_WMM:
 +              hostapd_wmm_action(hapd, mgmt, len);
 +              return 1;
 +#ifdef CONFIG_IEEE80211W
 +      case WLAN_ACTION_SA_QUERY:
 +              return hostapd_sa_query_action(hapd, mgmt, len);
 +#endif /* CONFIG_IEEE80211W */
 +#ifdef CONFIG_WNM
 +      case WLAN_ACTION_WNM:
 +              ieee802_11_rx_wnm_action_ap(hapd, mgmt, len);
 +              return 1;
 +#endif /* CONFIG_WNM */
++#ifdef CONFIG_FST
++      case WLAN_ACTION_FST:
++              if (hapd->iface->fst)
++                      fst_rx_action(hapd->iface->fst, mgmt, len);
++              else
++                      wpa_printf(MSG_DEBUG,
++                                 "FST: Ignore FST Action frame - no FST attached");
++              return 1;
++#endif /* CONFIG_FST */
 +      case WLAN_ACTION_PUBLIC:
 +      case WLAN_ACTION_PROTECTED_DUAL:
 +#ifdef CONFIG_IEEE80211N
-       struct hostapd_ssid *ssid = sta->ssid;
++              if (len >= IEEE80211_HDRLEN + 2 &&
++                  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,
 +                                             hapd->iface->freq);
 +              }
 +              if (hapd->public_action_cb2) {
 +                      hapd->public_action_cb2(hapd->public_action_cb2_ctx,
 +                                              (u8 *) mgmt, len,
 +                                              hapd->iface->freq);
 +              }
 +              if (hapd->public_action_cb || hapd->public_action_cb2)
 +                      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 1;
 +              }
 +              break;
 +      }
 +
 +      hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
 +                     HOSTAPD_LEVEL_DEBUG,
 +                     "handle_action - unknown action category %d or invalid "
 +                     "frame",
 +                     mgmt->u.action.category);
 +      if (!(mgmt->da[0] & 0x01) && !(mgmt->u.action.category & 0x80) &&
 +          !(mgmt->sa[0] & 0x01)) {
 +              struct ieee80211_mgmt *resp;
 +
 +              /*
 +               * IEEE 802.11-REVma/D9.0 - 7.3.1.11
 +               * Return the Action frame to the source without change
 +               * except that MSB of the Category set to 1.
 +               */
 +              wpa_printf(MSG_DEBUG, "IEEE 802.11: Return unknown Action "
 +                         "frame back to sender");
 +              resp = os_malloc(len);
 +              if (resp == NULL)
 +                      return 0;
 +              os_memcpy(resp, mgmt, len);
 +              os_memcpy(resp->da, resp->sa, ETH_ALEN);
 +              os_memcpy(resp->sa, hapd->own_addr, ETH_ALEN);
 +              os_memcpy(resp->bssid, hapd->own_addr, ETH_ALEN);
 +              resp->u.action.category |= 0x80;
 +
 +              if (hostapd_drv_send_mlme(hapd, resp, len, 0) < 0) {
 +                      wpa_printf(MSG_ERROR, "IEEE 802.11: Failed to send "
 +                                 "Action frame");
 +              }
 +              os_free(resp);
 +      }
 +
 +      return 1;
 +}
 +
 +
 +/**
 + * ieee802_11_mgmt - process incoming IEEE 802.11 management frames
 + * @hapd: hostapd BSS data structure (the BSS to which the management frame was
 + * sent to)
 + * @buf: management frame data (starting from IEEE 802.11 header)
 + * @len: length of frame data in octets
 + * @fi: meta data about received frame (signal level, etc.)
 + *
 + * Process all incoming IEEE 802.11 management frames. This will be called for
 + * each frame received from the kernel driver through wlan#ap interface. In
 + * addition, it can be called to re-inserted pending frames (e.g., when using
 + * external RADIUS server as an MAC ACL).
 + */
 +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 0;
 +
 +      mgmt = (struct ieee80211_mgmt *) buf;
 +      fc = le_to_host16(mgmt->frame_control);
 +      stype = WLAN_FC_GET_STYPE(fc);
 +
 +      if (stype == WLAN_FC_STYPE_BEACON) {
 +              handle_beacon(hapd, mgmt, len, fi);
 +              return 1;
 +      }
 +
 +      broadcast = mgmt->bssid[0] == 0xff && mgmt->bssid[1] == 0xff &&
 +              mgmt->bssid[2] == 0xff && mgmt->bssid[3] == 0xff &&
 +              mgmt->bssid[4] == 0xff && mgmt->bssid[5] == 0xff;
 +
 +      if (!broadcast &&
 +#ifdef CONFIG_P2P
 +          /* Invitation responses can be sent with the peer MAC as BSSID */
 +          !((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) {
 +              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 1;
 +      }
 +
 +      if (os_memcmp(mgmt->da, hapd->own_addr, ETH_ALEN) != 0) {
 +              hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
 +                             HOSTAPD_LEVEL_DEBUG,
 +                             "MGMT: DA=" MACSTR " not our address",
 +                             MAC2STR(mgmt->da));
 +              return 0;
 +      }
 +
++      if (hapd->iconf->track_sta_max_num)
++              sta_track_add(hapd->iface, mgmt->sa);
++
 +      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");
 +              ret = handle_action(hapd, mgmt, len);
 +              break;
 +      default:
 +              hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
 +                             HOSTAPD_LEVEL_DEBUG,
 +                             "unknown mgmt frame subtype %d", stype);
 +              break;
 +      }
 +
 +      return ret;
 +}
 +
 +
 +static void handle_auth_cb(struct hostapd_data *hapd,
 +                         const struct ieee80211_mgmt *mgmt,
 +                         size_t len, int ok)
 +{
 +      u16 auth_alg, auth_transaction, status_code;
 +      struct sta_info *sta;
 +
 +      if (!ok) {
 +              hostapd_logger(hapd, mgmt->da, HOSTAPD_MODULE_IEEE80211,
 +                             HOSTAPD_LEVEL_NOTICE,
 +                             "did not acknowledge authentication response");
 +              return;
 +      }
 +
 +      if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) {
 +              wpa_printf(MSG_INFO, "handle_auth_cb - too short payload (len=%lu)",
 +                         (unsigned long) len);
 +              return;
 +      }
 +
 +      auth_alg = le_to_host16(mgmt->u.auth.auth_alg);
 +      auth_transaction = le_to_host16(mgmt->u.auth.auth_transaction);
 +      status_code = le_to_host16(mgmt->u.auth.status_code);
 +
 +      sta = ap_get_sta(hapd, mgmt->da);
 +      if (!sta) {
 +              wpa_printf(MSG_INFO, "handle_auth_cb: STA " MACSTR " not found",
 +                         MAC2STR(mgmt->da));
 +              return;
 +      }
 +
 +      if (status_code == WLAN_STATUS_SUCCESS &&
 +          ((auth_alg == WLAN_AUTH_OPEN && auth_transaction == 2) ||
 +           (auth_alg == WLAN_AUTH_SHARED_KEY && auth_transaction == 4))) {
 +              hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
 +                             HOSTAPD_LEVEL_INFO, "authenticated");
 +              sta->flags |= WLAN_STA_AUTH;
 +      }
 +}
 +
 +
 +static void hostapd_set_wds_encryption(struct hostapd_data *hapd,
 +                                     struct sta_info *sta,
 +                                     char *ifname_wds)
 +{
 +      int i;
-               if (ap_sta_bind_vlan(hapd, sta, 0) < 0)
++      struct hostapd_ssid *ssid = &hapd->conf->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)
 +{
 +      u16 status;
 +      struct sta_info *sta;
 +      int new_assoc = 1;
 +      struct ieee80211_ht_capabilities ht_cap;
 +      struct ieee80211_vht_capabilities vht_cap;
 +
 +      if (len < IEEE80211_HDRLEN + (reassoc ? sizeof(mgmt->u.reassoc_resp) :
 +                                    sizeof(mgmt->u.assoc_resp))) {
 +              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) {
 +              wpa_printf(MSG_INFO, "handle_assoc_cb: STA " MACSTR " not found",
 +                         MAC2STR(mgmt->da));
 +              return;
 +      }
 +
 +      if (!ok) {
 +              hostapd_logger(hapd, mgmt->da, HOSTAPD_MODULE_IEEE80211,
 +                             HOSTAPD_LEVEL_DEBUG,
 +                             "did not acknowledge association response");
 +              sta->flags &= ~WLAN_STA_ASSOC_REQ_OK;
 +              return;
 +      }
 +
 +      if (reassoc)
 +              status = le_to_host16(mgmt->u.reassoc_resp.status_code);
 +      else
 +              status = le_to_host16(mgmt->u.assoc_resp.status_code);
 +
 +      if (status != WLAN_STATUS_SUCCESS)
 +              return;
 +
 +      /* Stop previous accounting session, if one is started, and allocate
 +       * new session id for the new session. */
 +      accounting_sta_stop(hapd, sta);
 +
 +      hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
 +                     HOSTAPD_LEVEL_INFO,
 +                     "associated (aid %d)",
 +                     sta->aid);
 +
 +      if (sta->flags & WLAN_STA_ASSOC)
 +              new_assoc = 0;
 +      sta->flags |= WLAN_STA_ASSOC;
 +      sta->flags &= ~WLAN_STA_WNM_SLEEP_MODE;
 +      if ((!hapd->conf->ieee802_1x && !hapd->conf->wpa && !hapd->conf->osen) ||
 +          sta->auth_alg == WLAN_AUTH_FT) {
 +              /*
 +               * Open, static WEP, or FT protocol; no separate authorization
 +               * step.
 +               */
 +              ap_sta_set_authorized(hapd, sta, 1);
 +      }
 +
 +      if (reassoc)
 +              mlme_reassociate_indication(hapd, sta);
 +      else
 +              mlme_associate_indication(hapd, sta);
 +
 +#ifdef CONFIG_IEEE80211W
 +      sta->sa_query_timed_out = 0;
 +#endif /* CONFIG_IEEE80211W */
 +
 +      /*
 +       * Remove the STA entry in order to make sure the STA PS state gets
 +       * cleared and configuration gets updated in case of reassociation back
 +       * to the same AP.
 +       */
 +      hostapd_drv_sta_remove(hapd, sta->addr);
 +
 +#ifdef CONFIG_IEEE80211N
 +      if (sta->flags & WLAN_STA_HT)
 +              hostapd_get_ht_capab(hapd, sta->ht_capabilities, &ht_cap);
 +#endif /* CONFIG_IEEE80211N */
 +#ifdef CONFIG_IEEE80211AC
 +      if (sta->flags & WLAN_STA_VHT)
 +              hostapd_get_vht_capab(hapd, sta->vht_capabilities, &vht_cap);
 +#endif /* CONFIG_IEEE80211AC */
 +
 +      if (hostapd_sta_add(hapd, sta->addr, sta->aid, sta->capability,
 +                          sta->supported_rates, sta->supported_rates_len,
 +                          sta->listen_interval,
 +                          sta->flags & WLAN_STA_HT ? &ht_cap : NULL,
 +                          sta->flags & WLAN_STA_VHT ? &vht_cap : NULL,
 +                          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");
 +
 +              ap_sta_disconnect(hapd, sta, sta->addr,
 +                                WLAN_REASON_DISASSOC_AP_BUSY);
 +
 +              return;
 +      }
 +
 +      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) {
 +              /*
 +               * This STA does not use RADIUS server for EAP authentication,
 +               * so bind it to the selected VLAN interface now, since the
 +               * interface selection is not going to change anymore.
 +               */
-               if (ap_sta_bind_vlan(hapd, sta, 0) < 0)
++              if (ap_sta_bind_vlan(hapd, sta) < 0)
 +                      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)
 +                      return;
 +      }
 +
 +      hostapd_set_sta_flags(hapd, sta);
 +
 +      if (sta->auth_alg == WLAN_AUTH_FT)
 +              wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC_FT);
 +      else
 +              wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC);
 +      hapd->new_assoc_sta_cb(hapd, sta, !new_assoc);
 +
 +      ieee802_1x_notify_port_enabled(sta->eapol_sm, 1);
 +}
 +
 +
 +static void handle_deauth_cb(struct hostapd_data *hapd,
 +                           const struct ieee80211_mgmt *mgmt,
 +                           size_t len, int ok)
 +{
 +      struct sta_info *sta;
 +      if (mgmt->da[0] & 0x01)
 +              return;
 +      sta = ap_get_sta(hapd, mgmt->da);
 +      if (!sta) {
 +              wpa_printf(MSG_DEBUG, "handle_deauth_cb: STA " MACSTR
 +                         " not found", MAC2STR(mgmt->da));
 +              return;
 +      }
 +      if (ok)
 +              wpa_printf(MSG_DEBUG, "STA " MACSTR " acknowledged deauth",
 +                         MAC2STR(sta->addr));
 +      else
 +              wpa_printf(MSG_DEBUG, "STA " MACSTR " did not acknowledge "
 +                         "deauth", MAC2STR(sta->addr));
 +
 +      ap_sta_deauth_cb(hapd, sta);
 +}
 +
 +
 +static void handle_disassoc_cb(struct hostapd_data *hapd,
 +                             const struct ieee80211_mgmt *mgmt,
 +                             size_t len, int ok)
 +{
 +      struct sta_info *sta;
 +      if (mgmt->da[0] & 0x01)
 +              return;
 +      sta = ap_get_sta(hapd, mgmt->da);
 +      if (!sta) {
 +              wpa_printf(MSG_DEBUG, "handle_disassoc_cb: STA " MACSTR
 +                         " not found", MAC2STR(mgmt->da));
 +              return;
 +      }
 +      if (ok)
 +              wpa_printf(MSG_DEBUG, "STA " MACSTR " acknowledged disassoc",
 +                         MAC2STR(sta->addr));
 +      else
 +              wpa_printf(MSG_DEBUG, "STA " MACSTR " did not acknowledge "
 +                         "disassoc", MAC2STR(sta->addr));
 +
 +      ap_sta_disassoc_cb(hapd, sta);
 +}
 +
 +
 +/**
 + * ieee802_11_mgmt_cb - Process management frame TX status callback
 + * @hapd: hostapd BSS data structure (the BSS from which the management frame
 + * was sent from)
 + * @buf: management frame data (starting from IEEE 802.11 header)
 + * @len: length of frame data in octets
 + * @stype: management frame subtype from frame control field
 + * @ok: Whether the frame was ACK'ed
 + */
 +void ieee802_11_mgmt_cb(struct hostapd_data *hapd, const u8 *buf, size_t len,
 +                      u16 stype, int ok)
 +{
 +      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");
 +              handle_auth_cb(hapd, mgmt, len, ok);
 +              break;
 +      case WLAN_FC_STYPE_ASSOC_RESP:
 +              wpa_printf(MSG_DEBUG, "mgmt::assoc_resp cb");
 +              handle_assoc_cb(hapd, mgmt, len, 0, ok);
 +              break;
 +      case WLAN_FC_STYPE_REASSOC_RESP:
 +              wpa_printf(MSG_DEBUG, "mgmt::reassoc_resp cb");
 +              handle_assoc_cb(hapd, mgmt, len, 1, ok);
 +              break;
 +      case WLAN_FC_STYPE_PROBE_RESP:
 +              wpa_printf(MSG_EXCESSIVE, "mgmt::proberesp cb");
 +              break;
 +      case WLAN_FC_STYPE_DEAUTH:
 +              wpa_printf(MSG_DEBUG, "mgmt::deauth cb");
 +              handle_deauth_cb(hapd, mgmt, len, ok);
 +              break;
 +      case WLAN_FC_STYPE_DISASSOC:
 +              wpa_printf(MSG_DEBUG, "mgmt::disassoc cb");
 +              handle_disassoc_cb(hapd, mgmt, len, ok);
 +              break;
 +      case WLAN_FC_STYPE_ACTION:
 +              wpa_printf(MSG_DEBUG, "mgmt::action cb");
 +              break;
 +      default:
 +              wpa_printf(MSG_INFO, "unknown mgmt cb frame subtype %d", stype);
 +              break;
 +      }
 +}
 +
 +
 +int ieee802_11_get_mib(struct hostapd_data *hapd, char *buf, size_t buflen)
 +{
 +      /* TODO */
 +      return 0;
 +}
 +
 +
 +int ieee802_11_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta,
 +                         char *buf, size_t buflen)
 +{
 +      /* TODO */
 +      return 0;
 +}
 +
 +
 +void hostapd_tx_status(struct hostapd_data *hapd, const u8 *addr,
 +                     const u8 *buf, size_t len, int ack)
 +{
 +      struct sta_info *sta;
 +      struct hostapd_iface *iface = hapd->iface;
 +
 +      sta = ap_get_sta(hapd, addr);
 +      if (sta == NULL && iface->num_bss > 1) {
 +              size_t j;
 +              for (j = 0; j < iface->num_bss; j++) {
 +                      hapd = iface->bss[j];
 +                      sta = ap_get_sta(hapd, addr);
 +                      if (sta)
 +                              break;
 +              }
 +      }
 +      if (sta == NULL || !(sta->flags & WLAN_STA_ASSOC))
 +              return;
 +      if (sta->flags & WLAN_STA_PENDING_POLL) {
 +              wpa_printf(MSG_DEBUG, "STA " MACSTR " %s pending "
 +                         "activity poll", MAC2STR(sta->addr),
 +                         ack ? "ACKed" : "did not ACK");
 +              if (ack)
 +                      sta->flags &= ~WLAN_STA_PENDING_POLL;
 +      }
 +
 +      ieee802_1x_tx_status(hapd, sta, buf, len, ack);
 +}
 +
 +
 +void hostapd_eapol_tx_status(struct hostapd_data *hapd, const u8 *dst,
 +                           const u8 *data, size_t len, int ack)
 +{
 +      struct sta_info *sta;
 +      struct hostapd_iface *iface = hapd->iface;
 +
 +      sta = ap_get_sta(hapd, dst);
 +      if (sta == NULL && iface->num_bss > 1) {
 +              size_t j;
 +              for (j = 0; j < iface->num_bss; j++) {
 +                      hapd = iface->bss[j];
 +                      sta = ap_get_sta(hapd, dst);
 +                      if (sta)
 +                              break;
 +              }
 +      }
 +      if (sta == NULL || !(sta->flags & WLAN_STA_ASSOC)) {
 +              wpa_printf(MSG_DEBUG, "Ignore TX status for Data frame to STA "
 +                         MACSTR " that is not currently associated",
 +                         MAC2STR(dst));
 +              return;
 +      }
 +
 +      ieee802_1x_eapol_tx_status(hapd, sta, data, len, ack);
 +}
 +
 +
 +void hostapd_client_poll_ok(struct hostapd_data *hapd, const u8 *addr)
 +{
 +      struct sta_info *sta;
 +      struct hostapd_iface *iface = hapd->iface;
 +
 +      sta = ap_get_sta(hapd, addr);
 +      if (sta == NULL && iface->num_bss > 1) {
 +              size_t j;
 +              for (j = 0; j < iface->num_bss; j++) {
 +                      hapd = iface->bss[j];
 +                      sta = ap_get_sta(hapd, addr);
 +                      if (sta)
 +                              break;
 +              }
 +      }
 +      if (sta == NULL)
 +              return;
 +      if (!(sta->flags & WLAN_STA_PENDING_POLL))
 +              return;
 +
 +      wpa_printf(MSG_DEBUG, "STA " MACSTR " ACKed pending "
 +                 "activity poll", MAC2STR(sta->addr));
 +      sta->flags &= ~WLAN_STA_PENDING_POLL;
 +}
 +
 +
 +void ieee802_11_rx_from_unknown(struct hostapd_data *hapd, const u8 *src,
 +                              int wds)
 +{
 +      struct sta_info *sta;
 +
 +      sta = ap_get_sta(hapd, src);
 +      if (sta && (sta->flags & WLAN_STA_ASSOC)) {
 +              if (!hapd->conf->wds_sta)
 +                      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;
 +                      ret = hostapd_set_wds_sta(hapd, ifname_wds,
 +                                                sta->addr, sta->aid, 1);
 +                      if (!ret)
 +                              hostapd_set_wds_encryption(hapd, sta,
 +                                                         ifname_wds);
 +              }
 +              return;
 +      }
 +
 +      wpa_printf(MSG_DEBUG, "Data/PS-poll frame from not associated STA "
 +                 MACSTR, MAC2STR(src));
 +      if (src[0] & 0x01) {
 +              /* Broadcast bit set in SA?! Ignore the frame silently. */
 +              return;
 +      }
 +
 +      if (sta && (sta->flags & WLAN_STA_ASSOC_REQ_OK)) {
 +              wpa_printf(MSG_DEBUG, "Association Response to the STA has "
 +                         "already been sent, but no TX status yet known - "
 +                         "ignore Class 3 frame issue with " MACSTR,
 +                         MAC2STR(src));
 +              return;
 +      }
 +
 +      if (sta && (sta->flags & WLAN_STA_AUTH))
 +              hostapd_drv_sta_disassoc(
 +                      hapd, src,
 +                      WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA);
 +      else
 +              hostapd_drv_sta_deauth(
 +                      hapd, src,
 +                      WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA);
 +}
 +
 +
 +#endif /* CONFIG_NATIVE_WINDOWS */
index 41c27d906e72a7eb2c96a13c8c75c6f79fbcb1fa,0000000000000000000000000000000000000000..44c1bff364ac495b700a6ac4de97258f56e5c7d1
mode 100644,000000..100644
--- /dev/null
@@@ -1,107 -1,0 +1,107 @@@
- u16 hostapd_own_capab_info(struct hostapd_data *hapd, struct sta_info *sta,
-                          int probe);
 +/*
 + * hostapd / IEEE 802.11 Management
 + * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
 + *
 + * This software may be distributed under the terms of the BSD license.
 + * See README for more details.
 + */
 +
 +#ifndef IEEE802_11_H
 +#define IEEE802_11_H
 +
 +struct hostapd_iface;
 +struct hostapd_data;
 +struct sta_info;
 +struct hostapd_frame_info;
 +struct ieee80211_ht_capabilities;
++struct ieee80211_vht_capabilities;
 +struct ieee80211_mgmt;
 +
 +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 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,
 +                         char *buf, size_t buflen);
 +#else /* NEED_AP_MLME */
 +static inline int ieee802_11_get_mib(struct hostapd_data *hapd, char *buf,
 +                                   size_t buflen)
 +{
 +      return 0;
 +}
 +
 +static inline int ieee802_11_get_mib_sta(struct hostapd_data *hapd,
 +                                       struct sta_info *sta,
 +                                       char *buf, size_t buflen)
 +{
 +      return 0;
 +}
 +#endif /* NEED_AP_MLME */
-                     const u8 *ht_capab, size_t ht_capab_len);
++u16 hostapd_own_capab_info(struct hostapd_data *hapd);
 +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);
 +void hostapd_get_ht_capab(struct hostapd_data *hapd,
 +                        struct ieee80211_ht_capabilities *ht_cap,
 +                        struct ieee80211_ht_capabilities *neg_ht_cap);
 +void hostapd_get_vht_capab(struct hostapd_data *hapd,
 +                         struct ieee80211_vht_capabilities *vht_cap,
 +                         struct ieee80211_vht_capabilities *neg_vht_cap);
 +u16 copy_sta_ht_capab(struct hostapd_data *hapd, struct sta_info *sta,
-                      const u8 *vht_capab, size_t vht_capab_len);
++                    const u8 *ht_capab);
 +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);
 +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,
 +                           const u8 *data, size_t len, int ack);
 +void ieee802_11_rx_from_unknown(struct hostapd_data *hapd, const u8 *src,
 +                              int wds);
 +u8 * hostapd_eid_assoc_comeback_time(struct hostapd_data *hapd,
 +                                   struct sta_info *sta, u8 *eid);
 +void ieee802_11_sa_query_action(struct hostapd_data *hapd,
 +                              const u8 *sa, const u8 action_type,
 +                              const u8 *trans_id);
 +u8 * hostapd_eid_interworking(struct hostapd_data *hapd, u8 *eid);
 +u8 * hostapd_eid_adv_proto(struct hostapd_data *hapd, u8 *eid);
 +u8 * hostapd_eid_roaming_consortium(struct hostapd_data *hapd, u8 *eid);
 +u8 * hostapd_eid_time_adv(struct hostapd_data *hapd, u8 *eid);
 +u8 * hostapd_eid_time_zone(struct hostapd_data *hapd, u8 *eid);
 +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 56c3ce0313d436802c470f1bf9a99742285c1ad4,0000000000000000000000000000000000000000..531a67da412c68a4a1ae354ec637ee20ba3ee676
mode 100644,000000..100644
--- /dev/null
@@@ -1,643 -1,0 +1,648 @@@
-  * @eloop_ctx: struct hostapd_data *
-  * @timeout_ctx: Not used
 +/*
 + * hostapd / IEEE 802.11 authentication (ACL)
 + * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi>
 + *
 + * This software may be distributed under the terms of the BSD license.
 + * See README for more details.
 + *
 + * Access control list for IEEE 802.11 authentication can uses statically
 + * configured ACL from configuration files or an external RADIUS server.
 + * Results from external RADIUS queries are cached to allow faster
 + * authentication frame processing.
 + */
 +
 +#include "utils/includes.h"
 +
 +#include "utils/common.h"
 +#include "utils/eloop.h"
 +#include "crypto/sha1.h"
 +#include "radius/radius.h"
 +#include "radius/radius_client.h"
 +#include "hostapd.h"
 +#include "ap_config.h"
 +#include "ap_drv_ops.h"
 +#include "ieee802_11.h"
 +#include "ieee802_1x.h"
 +#include "ieee802_11_auth.h"
 +
 +#define RADIUS_ACL_TIMEOUT 30
 +
 +
 +struct hostapd_cached_radius_acl {
 +      struct os_reltime timestamp;
 +      macaddr addr;
 +      int accepted; /* HOSTAPD_ACL_* */
 +      struct hostapd_cached_radius_acl *next;
 +      u32 session_timeout;
 +      u32 acct_interim_interval;
 +      int vlan_id;
 +      struct hostapd_sta_wpa_psk_short *psk;
 +      char *identity;
 +      char *radius_cui;
 +};
 +
 +
 +struct hostapd_acl_query_data {
 +      struct os_reltime timestamp;
 +      u8 radius_id;
 +      macaddr addr;
 +      u8 *auth_msg; /* IEEE 802.11 authentication frame from station */
 +      size_t auth_msg_len;
 +      struct hostapd_acl_query_data *next;
 +};
 +
 +
 +#ifndef CONFIG_NO_RADIUS
 +static void hostapd_acl_cache_free_entry(struct hostapd_cached_radius_acl *e)
 +{
 +      os_free(e->identity);
 +      os_free(e->radius_cui);
 +      hostapd_free_psk_list(e->psk);
 +      os_free(e);
 +}
 +
 +
 +static void hostapd_acl_cache_free(struct hostapd_cached_radius_acl *acl_cache)
 +{
 +      struct hostapd_cached_radius_acl *prev;
 +
 +      while (acl_cache) {
 +              prev = acl_cache;
 +              acl_cache = acl_cache->next;
 +              hostapd_acl_cache_free_entry(prev);
 +      }
 +}
 +
 +
 +static void copy_psk_list(struct hostapd_sta_wpa_psk_short **psk,
 +                        struct hostapd_sta_wpa_psk_short *src)
 +{
 +      struct hostapd_sta_wpa_psk_short **copy_to;
 +      struct hostapd_sta_wpa_psk_short *copy_from;
 +
 +      /* Copy PSK linked list */
 +      copy_to = psk;
 +      copy_from = src;
 +      while (copy_from && copy_to) {
 +              *copy_to = os_zalloc(sizeof(struct hostapd_sta_wpa_psk_short));
 +              if (*copy_to == NULL)
 +                      break;
 +              os_memcpy(*copy_to, copy_from,
 +                        sizeof(struct hostapd_sta_wpa_psk_short));
 +              copy_from = copy_from->next;
 +              copy_to = &((*copy_to)->next);
 +      }
 +      if (copy_to)
 +              *copy_to = NULL;
 +}
 +
 +
 +static int hostapd_acl_cache_get(struct hostapd_data *hapd, const u8 *addr,
 +                               u32 *session_timeout,
 +                               u32 *acct_interim_interval, int *vlan_id,
 +                               struct hostapd_sta_wpa_psk_short **psk,
 +                               char **identity, char **radius_cui)
 +{
 +      struct hostapd_cached_radius_acl *entry;
 +      struct os_reltime 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 (os_reltime_expired(&now, &entry->timestamp,
 +                                     RADIUS_ACL_TIMEOUT))
 +                      return -1; /* entry has expired */
 +              if (entry->accepted == HOSTAPD_ACL_ACCEPT_TIMEOUT)
 +                      if (session_timeout)
 +                              *session_timeout = entry->session_timeout;
 +              if (acct_interim_interval)
 +                      *acct_interim_interval =
 +                              entry->acct_interim_interval;
 +              if (vlan_id)
 +                      *vlan_id = entry->vlan_id;
 +              copy_psk_list(psk, entry->psk);
 +              if (identity) {
 +                      if (entry->identity)
 +                              *identity = os_strdup(entry->identity);
 +                      else
 +                              *identity = NULL;
 +              }
 +              if (radius_cui) {
 +                      if (entry->radius_cui)
 +                              *radius_cui = os_strdup(entry->radius_cui);
 +                      else
 +                              *radius_cui = NULL;
 +              }
 +              return entry->accepted;
 +      }
 +
 +      return -1;
 +}
 +#endif /* CONFIG_NO_RADIUS */
 +
 +
 +static void hostapd_acl_query_free(struct hostapd_acl_query_data *query)
 +{
 +      if (query == NULL)
 +              return;
 +      os_free(query->auth_msg);
 +      os_free(query);
 +}
 +
 +
 +#ifndef CONFIG_NO_RADIUS
 +static int hostapd_radius_acl_query(struct hostapd_data *hapd, const u8 *addr,
 +                                  struct hostapd_acl_query_data *query)
 +{
 +      struct radius_msg *msg;
 +      char buf[128];
 +
 +      query->radius_id = radius_client_get_id(hapd->radius);
 +      msg = radius_msg_new(RADIUS_CODE_ACCESS_REQUEST, query->radius_id);
 +      if (msg == NULL)
 +              return -1;
 +
 +      radius_msg_make_authenticator(msg, addr, ETH_ALEN);
 +
 +      os_snprintf(buf, sizeof(buf), RADIUS_ADDR_FORMAT, MAC2STR(addr));
 +      if (!radius_msg_add_attr(msg, RADIUS_ATTR_USER_NAME, (u8 *) buf,
 +                               os_strlen(buf))) {
 +              wpa_printf(MSG_DEBUG, "Could not add User-Name");
 +              goto fail;
 +      }
 +
 +      if (!radius_msg_add_attr_user_password(
 +                  msg, (u8 *) buf, os_strlen(buf),
 +                  hapd->conf->radius->auth_server->shared_secret,
 +                  hapd->conf->radius->auth_server->shared_secret_len)) {
 +              wpa_printf(MSG_DEBUG, "Could not add User-Password");
 +              goto fail;
 +      }
 +
 +      if (add_common_radius_attr(hapd, hapd->conf->radius_auth_req_attr,
 +                                 NULL, msg) < 0)
 +              goto fail;
 +
 +      os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT,
 +                  MAC2STR(addr));
 +      if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLING_STATION_ID,
 +                               (u8 *) buf, os_strlen(buf))) {
 +              wpa_printf(MSG_DEBUG, "Could not add Calling-Station-Id");
 +              goto fail;
 +      }
 +
 +      os_snprintf(buf, sizeof(buf), "CONNECT 11Mbps 802.11b");
 +      if (!radius_msg_add_attr(msg, RADIUS_ATTR_CONNECT_INFO,
 +                               (u8 *) buf, os_strlen(buf))) {
 +              wpa_printf(MSG_DEBUG, "Could not add Connect-Info");
 +              goto fail;
 +      }
 +
 +      if (radius_client_send(hapd->radius, msg, RADIUS_AUTH, addr) < 0)
 +              goto fail;
 +      return 0;
 +
 + fail:
 +      radius_msg_free(msg);
 +      return -1;
 +}
 +#endif /* CONFIG_NO_RADIUS */
 +
 +
 +/**
 + * hostapd_allowed_address - Check whether a specified STA can be authenticated
 + * @hapd: hostapd BSS data
 + * @addr: MAC address of the STA
 + * @msg: Authentication message
 + * @len: Length of msg in octets
 + * @session_timeout: Buffer for returning session timeout (from RADIUS)
 + * @acct_interim_interval: Buffer for returning account interval (from RADIUS)
 + * @vlan_id: Buffer for returning VLAN ID
 + * @psk: Linked list buffer for returning WPA PSK
 + * @identity: Buffer for returning identity (from RADIUS)
 + * @radius_cui: Buffer for returning CUI (from RADIUS)
 + * Returns: HOSTAPD_ACL_ACCEPT, HOSTAPD_ACL_REJECT, or HOSTAPD_ACL_PENDING
 + *
 + * The caller is responsible for freeing the returned *identity and *radius_cui
 + * values with os_free().
 + */
 +int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr,
 +                          const u8 *msg, size_t len, u32 *session_timeout,
 +                          u32 *acct_interim_interval, int *vlan_id,
 +                          struct hostapd_sta_wpa_psk_short **psk,
 +                          char **identity, char **radius_cui)
 +{
 +      if (session_timeout)
 +              *session_timeout = 0;
 +      if (acct_interim_interval)
 +              *acct_interim_interval = 0;
 +      if (vlan_id)
 +              *vlan_id = 0;
 +      if (psk)
 +              *psk = NULL;
 +      if (identity)
 +              *identity = NULL;
 +      if (radius_cui)
 +              *radius_cui = NULL;
 +
 +      if (hostapd_maclist_found(hapd->conf->accept_mac,
 +                                hapd->conf->num_accept_mac, addr, vlan_id))
 +              return HOSTAPD_ACL_ACCEPT;
 +
 +      if (hostapd_maclist_found(hapd->conf->deny_mac,
 +                                hapd->conf->num_deny_mac, addr, vlan_id))
 +              return HOSTAPD_ACL_REJECT;
 +
 +      if (hapd->conf->macaddr_acl == ACCEPT_UNLESS_DENIED)
 +              return HOSTAPD_ACL_ACCEPT;
 +      if (hapd->conf->macaddr_acl == DENY_UNLESS_ACCEPTED)
 +              return HOSTAPD_ACL_REJECT;
 +
 +      if (hapd->conf->macaddr_acl == USE_EXTERNAL_RADIUS_AUTH) {
 +#ifdef CONFIG_NO_RADIUS
 +              return HOSTAPD_ACL_REJECT;
 +#else /* CONFIG_NO_RADIUS */
 +              struct hostapd_acl_query_data *query;
 +
 +              /* Check whether ACL cache has an entry for this station */
 +              int res = hostapd_acl_cache_get(hapd, addr, session_timeout,
 +                                              acct_interim_interval,
 +                                              vlan_id, psk,
 +                                              identity, radius_cui);
 +              if (res == HOSTAPD_ACL_ACCEPT ||
 +                  res == HOSTAPD_ACL_ACCEPT_TIMEOUT)
 +                      return res;
 +              if (res == HOSTAPD_ACL_REJECT)
 +                      return HOSTAPD_ACL_REJECT;
 +
 +              query = hapd->acl_queries;
 +              while (query) {
 +                      if (os_memcmp(query->addr, addr, ETH_ALEN) == 0) {
 +                              /* pending query in RADIUS retransmit queue;
 +                               * do not generate a new one */
 +                              if (identity) {
 +                                      os_free(*identity);
 +                                      *identity = NULL;
 +                              }
 +                              if (radius_cui) {
 +                                      os_free(*radius_cui);
 +                                      *radius_cui = NULL;
 +                              }
 +                              return HOSTAPD_ACL_PENDING;
 +                      }
 +                      query = query->next;
 +              }
 +
 +              if (!hapd->conf->radius->auth_server)
 +                      return HOSTAPD_ACL_REJECT;
 +
 +              /* No entry in the cache - query external RADIUS server */
 +              query = os_zalloc(sizeof(*query));
 +              if (query == NULL) {
 +                      wpa_printf(MSG_ERROR, "malloc for query data failed");
 +                      return HOSTAPD_ACL_REJECT;
 +              }
 +              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 "
 +                                 "for ACL query.");
 +                      hostapd_acl_query_free(query);
 +                      return HOSTAPD_ACL_REJECT;
 +              }
 +
 +              query->auth_msg = os_malloc(len);
 +              if (query->auth_msg == NULL) {
 +                      wpa_printf(MSG_ERROR, "Failed to allocate memory for "
 +                                 "auth frame.");
 +                      hostapd_acl_query_free(query);
 +                      return HOSTAPD_ACL_REJECT;
 +              }
 +              os_memcpy(query->auth_msg, msg, len);
 +              query->auth_msg_len = len;
 +              query->next = hapd->acl_queries;
 +              hapd->acl_queries = query;
 +
 +              /* Queued data will be processed in hostapd_acl_recv_radius()
 +               * when RADIUS server replies to the sent Access-Request. */
 +              return HOSTAPD_ACL_PENDING;
 +#endif /* CONFIG_NO_RADIUS */
 +      }
 +
 +      return HOSTAPD_ACL_REJECT;
 +}
 +
 +
 +#ifndef CONFIG_NO_RADIUS
 +static void hostapd_acl_expire_cache(struct hostapd_data *hapd,
 +                                   struct os_reltime *now)
 +{
 +      struct hostapd_cached_radius_acl *prev, *entry, *tmp;
 +
 +      prev = NULL;
 +      entry = hapd->acl_cache;
 +
 +      while (entry) {
 +              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)
 +                              prev->next = entry->next;
 +                      else
 +                              hapd->acl_cache = entry->next;
 +                      hostapd_drv_set_radius_acl_expire(hapd, entry->addr);
 +                      tmp = entry;
 +                      entry = entry->next;
 +                      hostapd_acl_cache_free_entry(tmp);
 +                      continue;
 +              }
 +
 +              prev = entry;
 +              entry = entry->next;
 +      }
 +}
 +
 +
 +static void hostapd_acl_expire_queries(struct hostapd_data *hapd,
 +                                     struct os_reltime *now)
 +{
 +      struct hostapd_acl_query_data *prev, *entry, *tmp;
 +
 +      prev = NULL;
 +      entry = hapd->acl_queries;
 +
 +      while (entry) {
 +              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)
 +                              prev->next = entry->next;
 +                      else
 +                              hapd->acl_queries = entry->next;
 +
 +                      tmp = entry;
 +                      entry = entry->next;
 +                      hostapd_acl_query_free(tmp);
 +                      continue;
 +              }
 +
 +              prev = entry;
 +              entry = entry->next;
 +      }
 +}
 +
 +
 +/**
 + * hostapd_acl_expire - ACL cache expiration callback
- static void hostapd_acl_expire(void *eloop_ctx, void *timeout_ctx)
++ * @hapd: struct hostapd_data *
 + */
-       struct hostapd_data *hapd = eloop_ctx;
++void hostapd_acl_expire(struct hostapd_data *hapd)
 +{
-       eloop_register_timeout(10, 0, hostapd_acl_expire, hapd, NULL);
 +      struct os_reltime now;
 +
 +      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);
 +}
 +
 +
 +static void decode_tunnel_passwords(struct hostapd_data *hapd,
 +                                  const u8 *shared_secret,
 +                                  size_t shared_secret_len,
 +                                  struct radius_msg *msg,
 +                                  struct radius_msg *req,
 +                                  struct hostapd_cached_radius_acl *cache)
 +{
 +      int passphraselen;
 +      char *passphrase, *strpassphrase;
 +      size_t i;
 +      struct hostapd_sta_wpa_psk_short *psk;
 +
 +      /*
 +       * Decode all tunnel passwords as PSK and save them into a linked list.
 +       */
 +      for (i = 0; ; i++) {
 +              passphrase = radius_msg_get_tunnel_password(
 +                      msg, &passphraselen, shared_secret, shared_secret_len,
 +                      req, i);
 +              /*
 +               * Passphrase is NULL iff there is no i-th Tunnel-Password
 +               * attribute in msg.
 +               */
 +              if (passphrase == NULL)
 +                      break;
 +              /*
 +               * passphrase does not contain the NULL termination.
 +               * Add it here as pbkdf2_sha1() requires it.
 +               */
 +              strpassphrase = os_zalloc(passphraselen + 1);
 +              psk = os_zalloc(sizeof(struct hostapd_sta_wpa_psk_short));
 +              if (strpassphrase && psk) {
 +                      os_memcpy(strpassphrase, passphrase, passphraselen);
 +                      pbkdf2_sha1(strpassphrase,
 +                                  hapd->conf->ssid.ssid,
 +                                  hapd->conf->ssid.ssid_len, 4096,
 +                                  psk->psk, PMK_LEN);
 +                      psk->next = cache->psk;
 +                      cache->psk = psk;
 +                      psk = NULL;
 +              }
 +              os_free(strpassphrase);
 +              os_free(psk);
 +              os_free(passphrase);
 +      }
 +}
 +
 +
 +/**
 + * hostapd_acl_recv_radius - Process incoming RADIUS Authentication messages
 + * @msg: RADIUS response message
 + * @req: RADIUS request message
 + * @shared_secret: RADIUS shared secret
 + * @shared_secret_len: Length of shared_secret in octets
 + * @data: Context data (struct hostapd_data *)
 + * Returns: RADIUS_RX_PROCESSED if RADIUS message was a reply to ACL query (and
 + * was processed here) or RADIUS_RX_UNKNOWN if not.
 + */
 +static RadiusRxResult
 +hostapd_acl_recv_radius(struct radius_msg *msg, struct radius_msg *req,
 +                      const u8 *shared_secret, size_t shared_secret_len,
 +                      void *data)
 +{
 +      struct hostapd_data *hapd = data;
 +      struct hostapd_acl_query_data *query, *prev;
 +      struct hostapd_cached_radius_acl *cache;
 +      struct radius_hdr *hdr = radius_msg_get_hdr(msg);
 +
 +      query = hapd->acl_queries;
 +      prev = NULL;
 +      while (query) {
 +              if (query->radius_id == hdr->identifier)
 +                      break;
 +              prev = query;
 +              query = query->next;
 +      }
 +      if (query == NULL)
 +              return RADIUS_RX_UNKNOWN;
 +
 +      wpa_printf(MSG_DEBUG, "Found matching Access-Request for RADIUS "
 +                 "message (id=%d)", query->radius_id);
 +
 +      if (radius_msg_verify(msg, shared_secret, shared_secret_len, req, 0)) {
 +              wpa_printf(MSG_INFO, "Incoming RADIUS packet did not have "
 +                         "correct authenticator - dropped\n");
 +              return RADIUS_RX_INVALID_AUTHENTICATOR;
 +      }
 +
 +      if (hdr->code != RADIUS_CODE_ACCESS_ACCEPT &&
 +          hdr->code != RADIUS_CODE_ACCESS_REJECT) {
 +              wpa_printf(MSG_DEBUG, "Unknown RADIUS message code %d to ACL "
 +                         "query", hdr->code);
 +              return RADIUS_RX_UNKNOWN;
 +      }
 +
 +      /* Insert Accept/Reject info into ACL cache */
 +      cache = os_zalloc(sizeof(*cache));
 +      if (cache == NULL) {
 +              wpa_printf(MSG_DEBUG, "Failed to add ACL cache entry");
 +              goto done;
 +      }
 +      os_get_reltime(&cache->timestamp);
 +      os_memcpy(cache->addr, query->addr, sizeof(cache->addr));
 +      if (hdr->code == RADIUS_CODE_ACCESS_ACCEPT) {
 +              u8 *buf;
 +              size_t len;
 +
 +              if (radius_msg_get_attr_int32(msg, RADIUS_ATTR_SESSION_TIMEOUT,
 +                                            &cache->session_timeout) == 0)
 +                      cache->accepted = HOSTAPD_ACL_ACCEPT_TIMEOUT;
 +              else
 +                      cache->accepted = HOSTAPD_ACL_ACCEPT;
 +
 +              if (radius_msg_get_attr_int32(
 +                          msg, RADIUS_ATTR_ACCT_INTERIM_INTERVAL,
 +                          &cache->acct_interim_interval) == 0 &&
 +                  cache->acct_interim_interval < 60) {
 +                      wpa_printf(MSG_DEBUG, "Ignored too small "
 +                                 "Acct-Interim-Interval %d for STA " MACSTR,
 +                                 cache->acct_interim_interval,
 +                                 MAC2STR(query->addr));
 +                      cache->acct_interim_interval = 0;
 +              }
 +
 +              cache->vlan_id = radius_msg_get_vlanid(msg);
 +
 +              decode_tunnel_passwords(hapd, shared_secret, shared_secret_len,
 +                                      msg, req, cache);
 +
 +              if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_USER_NAME,
 +                                          &buf, &len, NULL) == 0) {
 +                      cache->identity = os_zalloc(len + 1);
 +                      if (cache->identity)
 +                              os_memcpy(cache->identity, buf, len);
 +              }
 +              if (radius_msg_get_attr_ptr(
 +                          msg, RADIUS_ATTR_CHARGEABLE_USER_IDENTITY,
 +                          &buf, &len, NULL) == 0) {
 +                      cache->radius_cui = os_zalloc(len + 1);
 +                      if (cache->radius_cui)
 +                              os_memcpy(cache->radius_cui, buf, len);
 +              }
 +
 +              if (hapd->conf->wpa_psk_radius == PSK_RADIUS_REQUIRED &&
 +                  !cache->psk)
 +                      cache->accepted = HOSTAPD_ACL_REJECT;
++
++              if (cache->vlan_id &&
++                  !hostapd_vlan_id_valid(hapd->conf->vlan, cache->vlan_id)) {
++                      hostapd_logger(hapd, query->addr,
++                                     HOSTAPD_MODULE_RADIUS,
++                                     HOSTAPD_LEVEL_INFO,
++                                     "Invalid VLAN ID %d received from RADIUS server",
++                                     cache->vlan_id);
++                      cache->vlan_id = 0;
++              }
++              if (hapd->conf->ssid.dynamic_vlan == DYNAMIC_VLAN_REQUIRED &&
++                  !cache->vlan_id)
++                      cache->accepted = HOSTAPD_ACL_REJECT;
 +      } else
 +              cache->accepted = HOSTAPD_ACL_REJECT;
 +      cache->next = hapd->acl_cache;
 +      hapd->acl_cache = cache;
 +
 +#ifdef CONFIG_DRIVER_RADIUS_ACL
 +      hostapd_drv_set_radius_acl_auth(hapd, query->addr, cache->accepted,
 +                                      cache->session_timeout);
 +#else /* CONFIG_DRIVER_RADIUS_ACL */
 +#ifdef NEED_AP_MLME
 +      /* Re-send original authentication frame for 802.11 processing */
 +      wpa_printf(MSG_DEBUG, "Re-sending authentication frame after "
 +                 "successful RADIUS ACL query");
 +      ieee802_11_mgmt(hapd, query->auth_msg, query->auth_msg_len, NULL);
 +#endif /* NEED_AP_MLME */
 +#endif /* CONFIG_DRIVER_RADIUS_ACL */
 +
 + done:
 +      if (prev == NULL)
 +              hapd->acl_queries = query->next;
 +      else
 +              prev->next = query->next;
 +
 +      hostapd_acl_query_free(query);
 +
 +      return RADIUS_RX_PROCESSED;
 +}
 +#endif /* CONFIG_NO_RADIUS */
 +
 +
 +/**
 + * hostapd_acl_init: Initialize IEEE 802.11 ACL
 + * @hapd: hostapd BSS data
 + * Returns: 0 on success, -1 on failure
 + */
 +int hostapd_acl_init(struct hostapd_data *hapd)
 +{
 +#ifndef CONFIG_NO_RADIUS
 +      if (radius_client_register(hapd->radius, RADIUS_AUTH,
 +                                 hostapd_acl_recv_radius, hapd))
 +              return -1;
-       eloop_cancel_timeout(hostapd_acl_expire, hapd, NULL);
 +#endif /* CONFIG_NO_RADIUS */
 +
 +      return 0;
 +}
 +
 +
 +/**
 + * hostapd_acl_deinit - Deinitialize IEEE 802.11 ACL
 + * @hapd: hostapd BSS data
 + */
 +void hostapd_acl_deinit(struct hostapd_data *hapd)
 +{
 +      struct hostapd_acl_query_data *query, *prev;
 +
 +#ifndef CONFIG_NO_RADIUS
 +      hostapd_acl_cache_free(hapd->acl_cache);
 +#endif /* CONFIG_NO_RADIUS */
 +
 +      query = hapd->acl_queries;
 +      while (query) {
 +              prev = query;
 +              query = query->next;
 +              hostapd_acl_query_free(prev);
 +      }
 +}
 +
 +
 +void hostapd_free_psk_list(struct hostapd_sta_wpa_psk_short *psk)
 +{
 +      while (psk) {
 +              struct hostapd_sta_wpa_psk_short *prev = psk;
 +              psk = psk->next;
 +              os_free(prev);
 +      }
 +}
index 2bc1065a226c94a4cb9598f25890a6871d955fb6,0000000000000000000000000000000000000000..b66f244b3ebc4138d6c91d0b90aa2cc0bd534555
mode 100644,000000..100644
--- /dev/null
@@@ -1,28 -1,0 +1,29 @@@
 +/*
 + * hostapd / IEEE 802.11 authentication (ACL)
 + * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
 + *
 + * This software may be distributed under the terms of the BSD license.
 + * See README for more details.
 + */
 +
 +#ifndef IEEE802_11_AUTH_H
 +#define IEEE802_11_AUTH_H
 +
 +enum {
 +      HOSTAPD_ACL_REJECT = 0,
 +      HOSTAPD_ACL_ACCEPT = 1,
 +      HOSTAPD_ACL_PENDING = 2,
 +      HOSTAPD_ACL_ACCEPT_TIMEOUT = 3
 +};
 +
 +int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr,
 +                          const u8 *msg, size_t len, u32 *session_timeout,
 +                          u32 *acct_interim_interval, int *vlan_id,
 +                          struct hostapd_sta_wpa_psk_short **psk,
 +                          char **identity, char **radius_cui);
 +int hostapd_acl_init(struct hostapd_data *hapd);
 +void hostapd_acl_deinit(struct hostapd_data *hapd);
 +void hostapd_free_psk_list(struct hostapd_sta_wpa_psk_short *psk);
++void hostapd_acl_expire(struct hostapd_data *hapd);
 +
 +#endif /* IEEE802_11_AUTH_H */
index 4b0653de95dbdacab5a3f7873c3dd5c52982b6e6,0000000000000000000000000000000000000000..11fde2a26394607e32f9d6f02eb7750474f0a9ff
mode 100644,000000..100644
--- /dev/null
@@@ -1,487 -1,0 +1,490 @@@
-       int is_ht_allowed = 1;
 +/*
 + * hostapd / IEEE 802.11n HT
 + * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
 + * Copyright (c) 2007-2008, Intel Corporation
 + *
 + * 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 "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)
 +{
 +      struct ieee80211_ht_capabilities *cap;
 +      u8 *pos = eid;
 +
 +      if (!hapd->iconf->ieee80211n || !hapd->iface->current_mode ||
 +          hapd->conf->disable_11n)
 +              return eid;
 +
 +      *pos++ = WLAN_EID_HT_CAP;
 +      *pos++ = sizeof(*cap);
 +
 +      cap = (struct ieee80211_ht_capabilities *) pos;
 +      os_memset(cap, 0, sizeof(*cap));
 +      cap->ht_capabilities_info = host_to_le16(hapd->iconf->ht_capab);
 +      cap->a_mpdu_params = hapd->iface->current_mode->a_mpdu_params;
 +      os_memcpy(cap->supported_mcs_set, hapd->iface->current_mode->mcs_set,
 +                16);
 +
 +      /* TODO: ht_extended_capabilities (now fully disabled) */
 +      /* TODO: tx_bf_capability_info (now fully disabled) */
 +      /* TODO: asel_capabilities (now fully disabled) */
 +
 +      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;
 +}
 +
 +
 +u8 * hostapd_eid_ht_operation(struct hostapd_data *hapd, u8 *eid)
 +{
 +      struct ieee80211_ht_operation *oper;
 +      u8 *pos = eid;
 +
 +      if (!hapd->iconf->ieee80211n || hapd->conf->disable_11n)
 +              return eid;
 +
 +      *pos++ = WLAN_EID_HT_OPERATION;
 +      *pos++ = sizeof(*oper);
 +
 +      oper = (struct ieee80211_ht_operation *) pos;
 +      os_memset(oper, 0, sizeof(*oper));
 +
 +      oper->primary_chan = hapd->iconf->channel;
 +      oper->operation_mode = host_to_le16(hapd->iface->ht_op_mode);
 +      if (hapd->iconf->secondary_channel == 1)
 +              oper->ht_param |= HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE |
 +                      HT_INFO_HT_PARAM_STA_CHNL_WIDTH;
 +      if (hapd->iconf->secondary_channel == -1)
 +              oper->ht_param |= HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW |
 +                      HT_INFO_HT_PARAM_STA_CHNL_WIDTH;
 +
 +      pos += sizeof(*oper);
 +
 +      return pos;
 +}
 +
 +
 +/*
 +op_mode
 +Set to 0 (HT pure) under the followign conditions
 +      - all STAs in the BSS are 20/40 MHz HT in 20/40 MHz BSS or
 +      - all STAs in the BSS are 20 MHz HT in 20 MHz BSS
 +Set to 1 (HT non-member protection) if there may be non-HT STAs
 +      in both the primary and the secondary channel
 +Set to 2 if only HT STAs are associated in BSS,
 +      however and at least one 20 MHz HT STA is associated
 +Set to 3 (HT mixed mode) when one or more non-HT STAs are associated
 +*/
 +int hostapd_ht_operation_update(struct hostapd_iface *iface)
 +{
 +      u16 cur_op_mode, new_op_mode;
 +      int op_mode_changes = 0;
 +
 +      if (!iface->conf->ieee80211n || iface->conf->ht_op_mode_fixed)
 +              return 0;
 +
 +      wpa_printf(MSG_DEBUG, "%s current operation mode=0x%X",
 +                 __func__, iface->ht_op_mode);
 +
 +      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_OPER_OP_MODE_NON_GF_HT_STAS_PRESENT;
 +              op_mode_changes++;
 +      } else if ((iface->ht_op_mode &
 +                  HT_OPER_OP_MODE_NON_GF_HT_STAS_PRESENT) &&
 +                 iface->num_sta_ht_no_gf == 0) {
 +              iface->ht_op_mode &= ~HT_OPER_OP_MODE_NON_GF_HT_STAS_PRESENT;
 +              op_mode_changes++;
 +      }
 +
 +      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_OPER_OP_MODE_OBSS_NON_HT_STAS_PRESENT;
 +              op_mode_changes++;
 +      } else if ((iface->ht_op_mode &
 +                  HT_OPER_OP_MODE_OBSS_NON_HT_STAS_PRESENT) &&
 +                 (iface->num_sta_no_ht == 0 && !iface->olbc_ht)) {
 +              iface->ht_op_mode &= ~HT_OPER_OP_MODE_OBSS_NON_HT_STAS_PRESENT;
 +              op_mode_changes++;
 +      }
 +
 +      if (iface->num_sta_no_ht)
 +              new_op_mode = HT_PROT_NON_HT_MIXED;
 +      else if (iface->conf->secondary_channel && iface->num_sta_ht_20mhz)
 +              new_op_mode = HT_PROT_20MHZ_PROTECTION;
 +      else if (iface->olbc_ht)
 +              new_op_mode = HT_PROT_NONMEMBER_PROTECTION;
 +      else
 +              new_op_mode = HT_PROT_NO_PROTECTION;
 +
 +      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_OPER_OP_MODE_HT_PROT_MASK;
 +              iface->ht_op_mode |= new_op_mode;
 +              op_mode_changes++;
 +      }
 +
 +      wpa_printf(MSG_DEBUG, "%s new operation mode=0x%X changes=%d",
 +                 __func__, iface->ht_op_mode, op_mode_changes);
 +
 +      return op_mode_changes;
 +}
 +
 +
 +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;
-               is_ht_allowed = 0;
++      int is_ht40_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;
++              is_ht40_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;
++              is_ht40_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);
-       wpa_printf(MSG_DEBUG, "is_ht_allowed=%d num_sta_ht40_intolerant=%d",
-                  is_ht_allowed, iface->num_sta_ht40_intolerant);
++                      is_ht40_allowed = 0;
 +              }
 +      }
-       if (!is_ht_allowed &&
++      wpa_printf(MSG_DEBUG, "is_ht40_allowed=%d num_sta_ht40_intolerant=%d",
++                 is_ht40_allowed, iface->num_sta_ht40_intolerant);
 +
-                     const u8 *ht_capab, size_t ht_capab_len)
++      if (!is_ht40_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,
-       /* Disable HT caps for STAs associated to no-HT BSSes. */
++                    const u8 *ht_capab)
 +{
-           ht_capab_len < sizeof(struct ieee80211_ht_capabilities) ||
-           hapd->conf->disable_11n) {
++      /*
++       * Disable HT caps for STAs associated to no-HT BSSes, or for stations
++       * that did not specify a valid WMM IE in the (Re)Association Request
++       * frame.
++       */
 +      if (!ht_capab ||
++          !(sta->flags & WLAN_STA_WMM) || hapd->conf->disable_11n) {
 +              sta->flags &= ~WLAN_STA_HT;
 +              os_free(sta->ht_capabilities);
 +              sta->ht_capabilities = NULL;
 +              return WLAN_STATUS_SUCCESS;
 +      }
 +
 +      if (sta->ht_capabilities == NULL) {
 +              sta->ht_capabilities =
 +                      os_zalloc(sizeof(struct ieee80211_ht_capabilities));
 +              if (sta->ht_capabilities == NULL)
 +                      return WLAN_STATUS_UNSPECIFIED_FAILURE;
 +      }
 +
 +      sta->flags |= WLAN_STA_HT;
 +      os_memcpy(sta->ht_capabilities, ht_capab,
 +                sizeof(struct ieee80211_ht_capabilities));
 +
 +      return WLAN_STATUS_SUCCESS;
 +}
 +
 +
 +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;
 +
 +      ht_capab = le_to_host16(sta->ht_capabilities->ht_capabilities_info);
 +      wpa_printf(MSG_DEBUG, "HT: STA " MACSTR " HT Capabilities Info: "
 +                 "0x%04x", MAC2STR(sta->addr), ht_capab);
 +      if ((ht_capab & HT_CAP_INFO_GREEN_FIELD) == 0) {
 +              if (!sta->no_ht_gf_set) {
 +                      sta->no_ht_gf_set = 1;
 +                      hapd->iface->num_sta_ht_no_gf++;
 +              }
 +              wpa_printf(MSG_DEBUG, "%s STA " MACSTR " - no greenfield, num "
 +                         "of non-gf stations %d",
 +                         __func__, MAC2STR(sta->addr),
 +                         hapd->iface->num_sta_ht_no_gf);
 +      }
 +      if ((ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) == 0) {
 +              if (!sta->ht_20mhz_set) {
 +                      sta->ht_20mhz_set = 1;
 +                      hapd->iface->num_sta_ht_20mhz++;
 +              }
 +              wpa_printf(MSG_DEBUG, "%s STA " MACSTR " - 20 MHz HT, num of "
 +                         "20MHz HT STAs %d",
 +                         __func__, MAC2STR(sta->addr),
 +                         hapd->iface->num_sta_ht_20mhz);
 +      }
 +
 +      if (ht_capab & HT_CAP_INFO_40MHZ_INTOLERANT)
 +              ht40_intolerant_add(hapd->iface, sta);
 +}
 +
 +
 +static void update_sta_no_ht(struct hostapd_data *hapd, struct sta_info *sta)
 +{
 +      if (!sta->no_ht_set) {
 +              sta->no_ht_set = 1;
 +              hapd->iface->num_sta_no_ht++;
 +      }
 +      if (hapd->iconf->ieee80211n) {
 +              wpa_printf(MSG_DEBUG, "%s STA " MACSTR " - no HT, num of "
 +                         "non-HT stations %d",
 +                         __func__, MAC2STR(sta->addr),
 +                         hapd->iface->num_sta_no_ht);
 +      }
 +}
 +
 +
 +void update_ht_state(struct hostapd_data *hapd, struct sta_info *sta)
 +{
 +      if ((sta->flags & WLAN_STA_HT) && sta->ht_capabilities)
 +              update_sta_ht(hapd, sta);
 +      else
 +              update_sta_no_ht(hapd, sta);
 +
 +      if (hostapd_ht_operation_update(hapd->iface) > 0)
 +              ieee802_11_set_beacons(hapd->iface);
 +}
 +
 +
 +void hostapd_get_ht_capab(struct hostapd_data *hapd,
 +                        struct ieee80211_ht_capabilities *ht_cap,
 +                        struct ieee80211_ht_capabilities *neg_ht_cap)
 +{
 +      u16 cap;
 +
 +      if (ht_cap == NULL)
 +              return;
 +      os_memcpy(neg_ht_cap, ht_cap, sizeof(*neg_ht_cap));
 +      cap = le_to_host16(neg_ht_cap->ht_capabilities_info);
 +
 +      /*
 +       * Mask out HT features we don't support, but don't overwrite
 +       * non-symmetric features like STBC and SMPS. Just because
 +       * we're not in dynamic SMPS mode the STA might still be.
 +       */
 +      cap &= (hapd->iconf->ht_capab | HT_CAP_INFO_RX_STBC_MASK |
 +              HT_CAP_INFO_TX_STBC | HT_CAP_INFO_SMPS_MASK);
 +
 +      /*
 +       * STBC needs to be handled specially
 +       * 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 (!(hapd->iconf->ht_capab & HT_CAP_INFO_RX_STBC_MASK))
 +              cap &= ~HT_CAP_INFO_TX_STBC;
 +      if (!(hapd->iconf->ht_capab & HT_CAP_INFO_TX_STBC))
 +              cap &= ~HT_CAP_INFO_RX_STBC_MASK;
 +
 +      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 171538ad74a7a2c1379faa739de516367b34276d,0000000000000000000000000000000000000000..5bf1b5d72002a8e0acc6bb5948795ab8c2bc5050
mode 100644,000000..100644
--- /dev/null
@@@ -1,297 -1,0 +1,296 @@@
-                      const u8 *vht_capab, size_t vht_capab_len)
 +/*
 + * hostapd / IEEE 802.11ac VHT
 + * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
 + *
 + * This program is free software; you can redistribute it and/or modify
 + * it under the terms of BSD license
 + *
 + * See README and COPYING for more details.
 + */
 +
 +#include "utils/includes.h"
 +
 +#include "utils/common.h"
 +#include "common/ieee802_11_defs.h"
 +#include "hostapd.h"
 +#include "ap_config.h"
 +#include "sta_info.h"
 +#include "beacon.h"
 +#include "ieee802_11.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 (!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->conf->vht_capab);
 +
 +      /* Supported MCS set comes from hw */
 +      os_memcpy(&cap->vht_supported_mcs_set, mode->vht_mcs_set, 8);
 +
 +      pos += sizeof(*cap);
 +
 +      return pos;
 +}
 +
 +
 +u8 * hostapd_eid_vht_operation(struct hostapd_data *hapd, u8 *eid)
 +{
 +      struct ieee80211_vht_operation *oper;
 +      u8 *pos = eid;
 +
 +      *pos++ = WLAN_EID_VHT_OPERATION;
 +      *pos++ = sizeof(*oper);
 +
 +      oper = (struct ieee80211_vht_operation *) pos;
 +      os_memset(oper, 0, sizeof(*oper));
 +
 +      /*
 +       * center freq = 5 GHz + (5 * index)
 +       * So index 42 gives center freq 5.210 GHz
 +       * which is channel 42 in 5G band
 +       */
 +      oper->vht_op_info_chan_center_freq_seg0_idx =
 +              hapd->iconf->vht_oper_centr_freq_seg0_idx;
 +      oper->vht_op_info_chan_center_freq_seg1_idx =
 +              hapd->iconf->vht_oper_centr_freq_seg1_idx;
 +
 +      oper->vht_op_info_chwidth = hapd->iconf->vht_oper_chwidth;
 +
 +      /* VHT Basic MCS set comes from hw */
 +      /* Hard code 1 stream, MCS0-7 is a min Basic VHT MCS rates */
 +      oper->vht_basic_mcs_set = host_to_le16(0xfffc);
 +      pos += sizeof(*oper);
 +
 +      return pos;
 +}
 +
 +
 +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,
-           vht_capab_len < sizeof(struct ieee80211_vht_capabilities) ||
++                     const u8 *vht_capab)
 +{
 +      /* Disable VHT caps for STAs associated to no-VHT BSSes. */
 +      if (!vht_capab ||
 +          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;
 +              return WLAN_STATUS_SUCCESS;
 +      }
 +
 +      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;
 +      os_memcpy(sta->vht_capabilities, vht_capab,
 +                sizeof(struct ieee80211_vht_capabilities));
 +
 +      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));
 +
 +      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 79dc0f9574880efb2820527532b5f9059c9c3617,0000000000000000000000000000000000000000..0f2d428cf752aa7c826570c06bac82a0aa1677ce
mode 100644,000000..100644
--- /dev/null
@@@ -1,2594 -1,0 +1,2616 @@@
- void ieee802_1x_tx_key(struct hostapd_data *hapd, struct sta_info *sta)
 +/*
 + * hostapd / IEEE 802.1X-2004 Authenticator
 + * Copyright (c) 2002-2012, Jouni Malinen <j@w1.fi>
 + *
 + * This software may be distributed under the terms of the BSD license.
 + * See README for more details.
 + */
 +
 +#include "utils/includes.h"
 +
 +#include "utils/common.h"
 +#include "utils/eloop.h"
 +#include "crypto/md5.h"
 +#include "crypto/crypto.h"
 +#include "crypto/random.h"
 +#include "common/ieee802_11_defs.h"
 +#include "radius/radius.h"
 +#include "radius/radius_client.h"
 +#include "eap_server/eap.h"
 +#include "eap_common/eap_wsc_common.h"
 +#include "eapol_auth/eapol_auth_sm.h"
 +#include "eapol_auth/eapol_auth_sm_i.h"
 +#include "p2p/p2p.h"
 +#include "hostapd.h"
 +#include "accounting.h"
 +#include "sta_info.h"
 +#include "wpa_auth.h"
 +#include "preauth_auth.h"
 +#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,
 +                              int remediation);
 +
 +
 +static void ieee802_1x_send(struct hostapd_data *hapd, struct sta_info *sta,
 +                          u8 type, const u8 *data, size_t datalen)
 +{
 +      u8 *buf;
 +      struct ieee802_1x_hdr *xhdr;
 +      size_t len;
 +      int encrypt = 0;
 +
 +      len = sizeof(*xhdr) + datalen;
 +      buf = os_zalloc(len);
 +      if (buf == NULL) {
 +              wpa_printf(MSG_ERROR, "malloc() failed for "
 +                         "ieee802_1x_send(len=%lu)",
 +                         (unsigned long) len);
 +              return;
 +      }
 +
 +      xhdr = (struct ieee802_1x_hdr *) buf;
 +      xhdr->version = hapd->conf->eapol_version;
 +      xhdr->type = type;
 +      xhdr->length = host_to_be16(datalen);
 +
 +      if (datalen > 0 && data != NULL)
 +              os_memcpy(xhdr + 1, data, datalen);
 +
 +      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 {
 +              hostapd_drv_hapd_send_eapol(
 +                      hapd, sta->addr, buf, len,
 +                      encrypt, hostapd_sta_flags_to_drv(sta->flags));
 +      }
 +
 +      os_free(buf);
 +}
 +
 +
 +void ieee802_1x_set_sta_authorized(struct hostapd_data *hapd,
 +                                 struct sta_info *sta, int authorized)
 +{
 +      int res;
 +
 +      if (sta->flags & WLAN_STA_PREAUTH)
 +              return;
 +
 +      if (authorized) {
 +              ap_sta_set_authorized(hapd, sta, 1);
 +              res = hostapd_set_authorized(hapd, sta, 1);
 +              hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
 +                             HOSTAPD_LEVEL_DEBUG, "authorizing port");
 +      } else {
 +              ap_sta_set_authorized(hapd, sta, 0);
 +              res = hostapd_set_authorized(hapd, sta, 0);
 +              hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
 +                             HOSTAPD_LEVEL_DEBUG, "unauthorizing port");
 +      }
 +
 +      if (res && errno != ENOENT) {
 +              wpa_printf(MSG_DEBUG, "Could not set station " MACSTR
 +                         " flags for kernel driver (errno=%d).",
 +                         MAC2STR(sta->addr), errno);
 +      }
 +
 +      if (authorized) {
 +              os_get_reltime(&sta->connected_time);
 +              accounting_sta_start(hapd, sta);
 +      }
 +}
 +
 +
++#ifndef CONFIG_FIPS
++#ifndef CONFIG_NO_RC4
++
 +static void ieee802_1x_tx_key_one(struct hostapd_data *hapd,
 +                                struct sta_info *sta,
 +                                int idx, int broadcast,
 +                                u8 *key_data, size_t key_len)
 +{
 +      u8 *buf, *ekey;
 +      struct ieee802_1x_hdr *hdr;
 +      struct ieee802_1x_eapol_key *key;
 +      size_t len, ekey_len;
 +      struct eapol_state_machine *sm = sta->eapol_sm;
 +
 +      if (sm == NULL)
 +              return;
 +
 +      len = sizeof(*key) + key_len;
 +      buf = os_zalloc(sizeof(*hdr) + len);
 +      if (buf == NULL)
 +              return;
 +
 +      hdr = (struct ieee802_1x_hdr *) buf;
 +      key = (struct ieee802_1x_eapol_key *) (hdr + 1);
 +      key->type = EAPOL_KEY_TYPE_RC4;
 +      WPA_PUT_BE16(key->key_length, key_len);
 +      wpa_get_ntp_timestamp(key->replay_counter);
 +
 +      if (random_get_bytes(key->key_iv, sizeof(key->key_iv))) {
 +              wpa_printf(MSG_ERROR, "Could not get random numbers");
 +              os_free(buf);
 +              return;
 +      }
 +
 +      key->key_index = idx | (broadcast ? 0 : BIT(7));
 +      if (hapd->conf->eapol_key_index_workaround) {
 +              /* According to some information, WinXP Supplicant seems to
 +               * interpret bit7 as an indication whether the key is to be
 +               * activated, so make it possible to enable workaround that
 +               * sets this bit for all keys. */
 +              key->key_index |= BIT(7);
 +      }
 +
 +      /* Key is encrypted using "Key-IV + MSK[0..31]" as the RC4-key and
 +       * MSK[32..63] is used to sign the message. */
 +      if (sm->eap_if->eapKeyData == NULL || sm->eap_if->eapKeyDataLen < 64) {
 +              wpa_printf(MSG_ERROR, "No eapKeyData available for encrypting "
 +                         "and signing EAPOL-Key");
 +              os_free(buf);
 +              return;
 +      }
 +      os_memcpy((u8 *) (key + 1), key_data, key_len);
 +      ekey_len = sizeof(key->key_iv) + 32;
 +      ekey = os_malloc(ekey_len);
 +      if (ekey == NULL) {
 +              wpa_printf(MSG_ERROR, "Could not encrypt key");
 +              os_free(buf);
 +              return;
 +      }
 +      os_memcpy(ekey, key->key_iv, sizeof(key->key_iv));
 +      os_memcpy(ekey + sizeof(key->key_iv), sm->eap_if->eapKeyData, 32);
 +      rc4_skip(ekey, ekey_len, 0, (u8 *) (key + 1), key_len);
 +      os_free(ekey);
 +
 +      /* This header is needed here for HMAC-MD5, but it will be regenerated
 +       * in ieee802_1x_send() */
 +      hdr->version = hapd->conf->eapol_version;
 +      hdr->type = IEEE802_1X_TYPE_EAPOL_KEY;
 +      hdr->length = host_to_be16(len);
 +      hmac_md5(sm->eap_if->eapKeyData + 32, 32, buf, sizeof(*hdr) + len,
 +               key->key_signature);
 +
 +      wpa_printf(MSG_DEBUG, "IEEE 802.1X: Sending EAPOL-Key to " MACSTR
 +                 " (%s index=%d)", MAC2STR(sm->addr),
 +                 broadcast ? "broadcast" : "unicast", idx);
 +      ieee802_1x_send(hapd, sta, IEEE802_1X_TYPE_EAPOL_KEY, (u8 *) key, len);
 +      if (sta->eapol_sm)
 +              sta->eapol_sm->dot1xAuthEapolFramesTx++;
 +      os_free(buf);
 +}
 +
 +
-       suite = wpa_cipher_to_suite((hapd->conf->wpa & 0x2) ?
++static 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;
 +
 +      if (sm == NULL || !sm->eap_if->eapKeyData)
 +              return;
 +
 +      wpa_printf(MSG_DEBUG, "IEEE 802.1X: Sending EAPOL-Key(s) to " MACSTR,
 +                 MAC2STR(sta->addr));
 +
 +#ifndef CONFIG_NO_VLAN
 +      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,
 +                                    hapd->conf->default_wep_key_len);
 +      }
 +
 +      if (hapd->conf->individual_wep_key_len > 0) {
 +              u8 *ikey;
 +              ikey = os_malloc(hapd->conf->individual_wep_key_len);
 +              if (ikey == NULL ||
 +                  random_get_bytes(ikey, hapd->conf->individual_wep_key_len))
 +              {
 +                      wpa_printf(MSG_ERROR, "Could not generate random "
 +                                 "individual WEP key.");
 +                      os_free(ikey);
 +                      return;
 +              }
 +
 +              wpa_hexdump_key(MSG_DEBUG, "Individual WEP key",
 +                              ikey, hapd->conf->individual_wep_key_len);
 +
 +              ieee802_1x_tx_key_one(hapd, sta, 0, 0, ikey,
 +                                    hapd->conf->individual_wep_key_len);
 +
 +              /* TODO: set encryption in TX callback, i.e., only after STA
 +               * has ACKed EAPOL-Key frame */
 +              if (hostapd_drv_set_key(hapd->conf->iface, hapd, WPA_ALG_WEP,
 +                                      sta->addr, 0, 1, NULL, 0, ikey,
 +                                      hapd->conf->individual_wep_key_len)) {
 +                      wpa_printf(MSG_ERROR, "Could not set individual WEP "
 +                                 "encryption.");
 +              }
 +
 +              os_free(ikey);
 +      }
 +}
 +
++#endif /* CONFIG_NO_RC4 */
++#endif /* CONFIG_FIPS */
++
 +
 +const char *radius_mode_txt(struct hostapd_data *hapd)
 +{
 +      switch (hapd->iface->conf->hw_mode) {
 +      case HOSTAPD_MODE_IEEE80211AD:
 +              return "802.11ad";
 +      case HOSTAPD_MODE_IEEE80211A:
 +              return "802.11a";
 +      case HOSTAPD_MODE_IEEE80211G:
 +              return "802.11g";
 +      case HOSTAPD_MODE_IEEE80211B:
 +      default:
 +              return "802.11b";
 +      }
 +}
 +
 +
 +int radius_sta_rate(struct hostapd_data *hapd, struct sta_info *sta)
 +{
 +      int i;
 +      u8 rate = 0;
 +
 +      for (i = 0; i < sta->supported_rates_len; i++)
 +              if ((sta->supported_rates[i] & 0x7f) > rate)
 +                      rate = sta->supported_rates[i] & 0x7f;
 +
 +      return rate;
 +}
 +
 +
 +#ifndef CONFIG_NO_RADIUS
 +static void ieee802_1x_learn_identity(struct hostapd_data *hapd,
 +                                    struct eapol_state_machine *sm,
 +                                    const u8 *eap, size_t len)
 +{
 +      const u8 *identity;
 +      size_t identity_len;
 +      const struct eap_hdr *hdr = (const struct eap_hdr *) eap;
 +
 +      if (len <= sizeof(struct eap_hdr) ||
 +          (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);
 +      if (identity == NULL)
 +              return;
 +
 +      /* Save station identity for future RADIUS packets */
 +      os_free(sm->identity);
 +      sm->identity = (u8 *) dup_binstr(identity, identity_len);
 +      if (sm->identity == NULL) {
 +              sm->identity_len = 0;
 +              return;
 +      }
 +
 +      sm->identity_len = identity_len;
 +      hostapd_logger(hapd, sm->addr, HOSTAPD_MODULE_IEEE8021X,
 +                     HOSTAPD_LEVEL_DEBUG, "STA identity '%s'", sm->identity);
 +      sm->dot1xAuthEapolRespIdFramesRx++;
 +}
 +
 +
 +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;
 +      }
 +
-       if (hapd->conf->wpa && sta->wpa_sm &&
++      suite = wpa_cipher_to_suite(((hapd->conf->wpa & 0x2) ||
++                                   hapd->conf->osen) ?
 +                                  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,
 +                                    struct radius_msg *msg)
 +{
 +      char buf[128];
 +
 +      if (!hostapd_config_get_radius_attr(req_attr,
 +                                          RADIUS_ATTR_NAS_PORT) &&
 +          !radius_msg_add_attr_int32(msg, RADIUS_ATTR_NAS_PORT, sta->aid)) {
 +              wpa_printf(MSG_ERROR, "Could not add NAS-Port");
 +              return -1;
 +      }
 +
 +      os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT,
 +                  MAC2STR(sta->addr));
 +      buf[sizeof(buf) - 1] = '\0';
 +      if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLING_STATION_ID,
 +                               (u8 *) buf, os_strlen(buf))) {
 +              wpa_printf(MSG_ERROR, "Could not add Calling-Station-Id");
 +              return -1;
 +      }
 +
 +      if (sta->flags & WLAN_STA_PREAUTH) {
 +              os_strlcpy(buf, "IEEE 802.11i Pre-Authentication",
 +                         sizeof(buf));
 +      } else {
 +              os_snprintf(buf, sizeof(buf), "CONNECT %d%sMbps %s",
 +                          radius_sta_rate(hapd, sta) / 2,
 +                          (radius_sta_rate(hapd, sta) & 1) ? ".5" : "",
 +                          radius_mode_txt(hapd));
 +              buf[sizeof(buf) - 1] = '\0';
 +      }
 +      if (!hostapd_config_get_radius_attr(req_attr,
 +                                          RADIUS_ATTR_CONNECT_INFO) &&
 +          !radius_msg_add_attr(msg, RADIUS_ATTR_CONNECT_INFO,
 +                               (u8 *) buf, os_strlen(buf))) {
 +              wpa_printf(MSG_ERROR, "Could not add Connect-Info");
 +              return -1;
 +      }
 +
 +      if (sta->acct_session_id_hi || sta->acct_session_id_lo) {
 +              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))) {
 +                      wpa_printf(MSG_ERROR, "Could not add Acct-Session-Id");
 +                      return -1;
 +              }
 +      }
 +
 +#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 (eap && !radius_msg_add_eap(msg, eap, len)) {
++      if ((hapd->conf->wpa || hapd->conf->osen) && sta->wpa_sm &&
 +          add_common_radius_sta_attr_rsn(hapd, req_attr, sta, msg) < 0)
 +              return -1;
 +
 +      return 0;
 +}
 +
 +
 +int add_common_radius_attr(struct hostapd_data *hapd,
 +                         struct hostapd_radius_attr *req_attr,
 +                         struct sta_info *sta,
 +                         struct radius_msg *msg)
 +{
 +      char buf[128];
 +      struct hostapd_radius_attr *attr;
 +
 +      if (!hostapd_config_get_radius_attr(req_attr,
 +                                          RADIUS_ATTR_NAS_IP_ADDRESS) &&
 +          hapd->conf->own_ip_addr.af == AF_INET &&
 +          !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IP_ADDRESS,
 +                               (u8 *) &hapd->conf->own_ip_addr.u.v4, 4)) {
 +              wpa_printf(MSG_ERROR, "Could not add NAS-IP-Address");
 +              return -1;
 +      }
 +
 +#ifdef CONFIG_IPV6
 +      if (!hostapd_config_get_radius_attr(req_attr,
 +                                          RADIUS_ATTR_NAS_IPV6_ADDRESS) &&
 +          hapd->conf->own_ip_addr.af == AF_INET6 &&
 +          !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IPV6_ADDRESS,
 +                               (u8 *) &hapd->conf->own_ip_addr.u.v6, 16)) {
 +              wpa_printf(MSG_ERROR, "Could not add NAS-IPv6-Address");
 +              return -1;
 +      }
 +#endif /* CONFIG_IPV6 */
 +
 +      if (!hostapd_config_get_radius_attr(req_attr,
 +                                          RADIUS_ATTR_NAS_IDENTIFIER) &&
 +          hapd->conf->nas_identifier &&
 +          !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IDENTIFIER,
 +                               (u8 *) hapd->conf->nas_identifier,
 +                               os_strlen(hapd->conf->nas_identifier))) {
 +              wpa_printf(MSG_ERROR, "Could not add NAS-Identifier");
 +              return -1;
 +      }
 +
 +      os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT ":%s",
 +                  MAC2STR(hapd->own_addr),
 +                  wpa_ssid_txt(hapd->conf->ssid.ssid,
 +                               hapd->conf->ssid.ssid_len));
 +      buf[sizeof(buf) - 1] = '\0';
 +      if (!hostapd_config_get_radius_attr(req_attr,
 +                                          RADIUS_ATTR_CALLED_STATION_ID) &&
 +          !radius_msg_add_attr(msg, RADIUS_ATTR_CALLED_STATION_ID,
 +                               (u8 *) buf, os_strlen(buf))) {
 +              wpa_printf(MSG_ERROR, "Could not add Called-Station-Id");
 +              return -1;
 +      }
 +
 +      if (!hostapd_config_get_radius_attr(req_attr,
 +                                          RADIUS_ATTR_NAS_PORT_TYPE) &&
 +          !radius_msg_add_attr_int32(msg, RADIUS_ATTR_NAS_PORT_TYPE,
 +                                     RADIUS_NAS_PORT_TYPE_IEEE_802_11)) {
 +              wpa_printf(MSG_ERROR, "Could not add NAS-Port-Type");
 +              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;
 +
 +      for (attr = req_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");
 +                      return -1;
 +              }
 +      }
 +
 +      return 0;
 +}
 +
 +
 +static void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd,
 +                                        struct sta_info *sta,
 +                                        const u8 *eap, size_t len)
 +{
 +      struct radius_msg *msg;
 +      struct eapol_state_machine *sm = sta->eapol_sm;
 +
 +      if (sm == NULL)
 +              return;
 +
 +      ieee802_1x_learn_identity(hapd, sm, eap, len);
 +
 +      wpa_printf(MSG_DEBUG, "Encapsulating EAP message into a RADIUS "
 +                 "packet");
 +
 +      sm->radius_identifier = radius_client_get_id(hapd->radius);
 +      msg = radius_msg_new(RADIUS_CODE_ACCESS_REQUEST,
 +                           sm->radius_identifier);
 +      if (msg == NULL) {
 +              wpa_printf(MSG_INFO, "Could not create new RADIUS packet");
 +              return;
 +      }
 +
 +      radius_msg_make_authenticator(msg, (u8 *) sta, sizeof(*sta));
 +
 +      if (sm->identity &&
 +          !radius_msg_add_attr(msg, RADIUS_ATTR_USER_NAME,
 +                               sm->identity, sm->identity_len)) {
 +              wpa_printf(MSG_INFO, "Could not add User-Name");
 +              goto fail;
 +      }
 +
 +      if (add_common_radius_attr(hapd, hapd->conf->radius_auth_req_attr, sta,
 +                                 msg) < 0)
 +              goto fail;
 +
 +      /* TODO: should probably check MTU from driver config; 2304 is max for
 +       * IEEE 802.11, but use 1400 to avoid problems with too large packets
 +       */
 +      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)) {
 +              wpa_printf(MSG_INFO, "Could not add Framed-MTU");
 +              goto fail;
 +      }
 +
-               int old_vlanid;
++      if (!radius_msg_add_eap(msg, eap, len)) {
 +              wpa_printf(MSG_INFO, "Could not add EAP-Message");
 +              goto fail;
 +      }
 +
 +      /* State attribute must be copied if and only if this packet is
 +       * Access-Request reply to the previous Access-Challenge */
 +      if (sm->last_recv_radius &&
 +          radius_msg_get_hdr(sm->last_recv_radius)->code ==
 +          RADIUS_CODE_ACCESS_CHALLENGE) {
 +              int res = radius_msg_copy_attr(msg, sm->last_recv_radius,
 +                                             RADIUS_ATTR_STATE);
 +              if (res < 0) {
 +                      wpa_printf(MSG_INFO, "Could not copy State attribute from previous Access-Challenge");
 +                      goto fail;
 +              }
 +              if (res > 0) {
 +                      wpa_printf(MSG_DEBUG, "Copied RADIUS State Attribute");
 +              }
 +      }
 +
 +      if (hapd->conf->radius_request_cui) {
 +              const u8 *cui;
 +              size_t cui_len;
 +              /* Add previously learned CUI or nul CUI to request CUI */
 +              if (sm->radius_cui) {
 +                      cui = wpabuf_head(sm->radius_cui);
 +                      cui_len = wpabuf_len(sm->radius_cui);
 +              } else {
 +                      cui = (const u8 *) "\0";
 +                      cui_len = 1;
 +              }
 +              if (!radius_msg_add_attr(msg,
 +                                       RADIUS_ATTR_CHARGEABLE_USER_IDENTITY,
 +                                       cui, cui_len)) {
 +                      wpa_printf(MSG_ERROR, "Could not add CUI");
 +                      goto fail;
 +              }
 +      }
 +
 +#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;
 +
 +      return;
 +
 + fail:
 +      radius_msg_free(msg);
 +}
 +#endif /* CONFIG_NO_RADIUS */
 +
 +
 +static void handle_eap_response(struct hostapd_data *hapd,
 +                              struct sta_info *sta, struct eap_hdr *eap,
 +                              size_t len)
 +{
 +      u8 type, *data;
 +      struct eapol_state_machine *sm = sta->eapol_sm;
 +      if (sm == NULL)
 +              return;
 +
 +      data = (u8 *) (eap + 1);
 +
 +      if (len < sizeof(*eap) + 1) {
 +              wpa_printf(MSG_INFO, "handle_eap_response: too short response data");
 +              return;
 +      }
 +
 +      sm->eap_type_supp = 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 Response-%s (%d)",
 +                     eap->code, eap->identifier, be_to_host16(eap->length),
 +                     eap_server_get_name(0, type), type);
 +
 +      sm->dot1xAuthEapolRespFramesRx++;
 +
 +      wpabuf_free(sm->eap_if->eapRespData);
 +      sm->eap_if->eapRespData = wpabuf_alloc_copy(eap, len);
 +      sm->eapolEap = TRUE;
 +}
 +
 +
 +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)
 +{
 +      struct eap_hdr *eap;
 +      u16 eap_len;
 +
 +      if (len < sizeof(*eap)) {
 +              wpa_printf(MSG_INFO, "   too short EAP packet");
 +              return;
 +      }
 +
 +      eap = (struct eap_hdr *) buf;
 +
 +      eap_len = be_to_host16(eap->length);
 +      wpa_printf(MSG_DEBUG, "EAP: code=%d identifier=%d length=%d",
 +                 eap->code, eap->identifier, eap_len);
 +      if (eap_len < sizeof(*eap)) {
 +              wpa_printf(MSG_DEBUG, "   Invalid EAP length");
 +              return;
 +      } else if (eap_len > len) {
 +              wpa_printf(MSG_DEBUG, "   Too short frame to contain this EAP "
 +                         "packet");
 +              return;
 +      } else if (eap_len < len) {
 +              wpa_printf(MSG_DEBUG, "   Ignoring %lu extra bytes after EAP "
 +                         "packet", (unsigned long) len - eap_len);
 +      }
 +
 +      switch (eap->code) {
 +      case EAP_CODE_REQUEST:
 +              wpa_printf(MSG_DEBUG, " (request)");
 +              return;
 +      case EAP_CODE_RESPONSE:
 +              wpa_printf(MSG_DEBUG, " (response)");
 +              handle_eap_response(hapd, sta, eap, eap_len);
 +              break;
 +      case EAP_CODE_SUCCESS:
 +              wpa_printf(MSG_DEBUG, " (success)");
 +              return;
 +      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;
 +      }
 +}
 +
 +
 +static struct eapol_state_machine *
 +ieee802_1x_alloc_eapol_sm(struct hostapd_data *hapd, struct sta_info *sta)
 +{
 +      int flags = 0;
 +      if (sta->flags & WLAN_STA_PREAUTH)
 +              flags |= EAPOL_SM_PREAUTH;
 +      if (sta->wpa_sm) {
 +              flags |= EAPOL_SM_USES_WPA;
 +              if (wpa_auth_sta_get_pmksa(sta->wpa_sm))
 +                      flags |= EAPOL_SM_FROM_PMKSA_CACHE;
 +      }
 +      return eapol_auth_alloc(hapd->eapol_auth, sta->addr, flags,
 +                              sta->wps_ie, sta->p2p_ie, sta,
 +                              sta->identity, sta->radius_cui);
 +}
 +
 +
 +/**
 + * ieee802_1x_receive - Process the EAPOL frames from the Supplicant
 + * @hapd: hostapd BSS data
 + * @sa: Source address (sender of the EAPOL frame)
 + * @buf: EAPOL frame
 + * @len: Length of buf in octets
 + *
 + * This function is called for each incoming EAPOL frame from the interface
 + */
 +void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf,
 +                      size_t len)
 +{
 +      struct sta_info *sta;
 +      struct ieee802_1x_hdr *hdr;
 +      struct ieee802_1x_eapol_key *key;
 +      u16 datalen;
 +      struct rsn_pmksa_cache_entry *pmksa;
 +      int key_mgmt;
 +
 +      if (!hapd->conf->ieee802_1x && !hapd->conf->wpa && !hapd->conf->osen &&
 +          !hapd->conf->wps_state)
 +              return;
 +
 +      wpa_printf(MSG_DEBUG, "IEEE 802.1X: %lu bytes from " MACSTR,
 +                 (unsigned long) len, MAC2STR(sa));
 +      sta = ap_get_sta(hapd, sa);
 +      if (!sta || (!(sta->flags & (WLAN_STA_ASSOC | WLAN_STA_PREAUTH)) &&
 +                   !(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_WIRED))) {
 +              wpa_printf(MSG_DEBUG, "IEEE 802.1X data frame from not "
 +                         "associated/Pre-authenticating STA");
 +              return;
 +      }
 +
 +      if (len < sizeof(*hdr)) {
 +              wpa_printf(MSG_INFO, "   too short IEEE 802.1X packet");
 +              return;
 +      }
 +
 +      hdr = (struct ieee802_1x_hdr *) buf;
 +      datalen = be_to_host16(hdr->length);
 +      wpa_printf(MSG_DEBUG, "   IEEE 802.1X: version=%d type=%d length=%d",
 +                 hdr->version, hdr->type, datalen);
 +
 +      if (len - sizeof(*hdr) < datalen) {
 +              wpa_printf(MSG_INFO, "   frame too short for this IEEE 802.1X packet");
 +              if (sta->eapol_sm)
 +                      sta->eapol_sm->dot1xAuthEapLengthErrorFramesRx++;
 +              return;
 +      }
 +      if (len - sizeof(*hdr) > datalen) {
 +              wpa_printf(MSG_DEBUG, "   ignoring %lu extra octets after "
 +                         "IEEE 802.1X packet",
 +                         (unsigned long) len - sizeof(*hdr) - datalen);
 +      }
 +
 +      if (sta->eapol_sm) {
 +              sta->eapol_sm->dot1xAuthLastEapolFrameVersion = hdr->version;
 +              sta->eapol_sm->dot1xAuthEapolFramesRx++;
 +      }
 +
 +      key = (struct ieee802_1x_eapol_key *) (hdr + 1);
 +      if (datalen >= sizeof(struct ieee802_1x_eapol_key) &&
 +          hdr->type == IEEE802_1X_TYPE_EAPOL_KEY &&
 +          (key->type == EAPOL_KEY_TYPE_WPA ||
 +           key->type == EAPOL_KEY_TYPE_RSN)) {
 +              wpa_receive(hapd->wpa_auth, sta->wpa_sm, (u8 *) hdr,
 +                          sizeof(*hdr) + datalen);
 +              return;
 +      }
 +
 +      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");
 +              return;
 +      }
 +
 +      key_mgmt = wpa_auth_sta_key_mgmt(sta->wpa_sm);
 +      if (key_mgmt != -1 && wpa_key_mgmt_wpa_psk(key_mgmt)) {
 +              wpa_printf(MSG_DEBUG, "IEEE 802.1X: Ignore EAPOL message - "
 +                         "STA is using PSK");
 +              return;
 +      }
 +
 +      if (!sta->eapol_sm) {
 +              sta->eapol_sm = ieee802_1x_alloc_eapol_sm(hapd, sta);
 +              if (!sta->eapol_sm)
 +                      return;
 +
 +#ifdef CONFIG_WPS
 +              if (!hapd->conf->ieee802_1x && hapd->conf->wps_state) {
 +                      u32 wflags = sta->flags & (WLAN_STA_WPS |
 +                                                 WLAN_STA_WPS2 |
 +                                                 WLAN_STA_MAYBE_WPS);
 +                      if (wflags == WLAN_STA_MAYBE_WPS ||
 +                          wflags == (WLAN_STA_WPS | WLAN_STA_MAYBE_WPS)) {
 +                              /*
 +                               * Delay EAPOL frame transmission until a
 +                               * possible WPS STA initiates the handshake
 +                               * with EAPOL-Start. Only allow the wait to be
 +                               * skipped if the STA is known to support WPS
 +                               * 2.0.
 +                               */
 +                              wpa_printf(MSG_DEBUG, "WPS: Do not start "
 +                                         "EAPOL until EAPOL-Start is "
 +                                         "received");
 +                              sta->eapol_sm->flags |= EAPOL_SM_WAIT_START;
 +                      }
 +              }
 +#endif /* CONFIG_WPS */
 +
 +              sta->eapol_sm->eap_if->portEnabled = TRUE;
 +      }
 +
 +      /* since we support version 1, we can ignore version field and proceed
 +       * as specified in version 1 standard [IEEE Std 802.1X-2001, 7.5.5] */
 +      /* TODO: actually, we are not version 1 anymore.. However, Version 2
 +       * does not change frame contents, so should be ok to process frames
 +       * more or less identically. Some changes might be needed for
 +       * verification of fields. */
 +
 +      switch (hdr->type) {
 +      case IEEE802_1X_TYPE_EAP_PACKET:
 +              handle_eap(hapd, sta, (u8 *) (hdr + 1), datalen);
 +              break;
 +
 +      case IEEE802_1X_TYPE_EAPOL_START:
 +              hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
 +                             HOSTAPD_LEVEL_DEBUG, "received EAPOL-Start "
 +                             "from STA");
 +              sta->eapol_sm->flags &= ~EAPOL_SM_WAIT_START;
 +              pmksa = wpa_auth_sta_get_pmksa(sta->wpa_sm);
 +              if (pmksa) {
 +                      hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA,
 +                                     HOSTAPD_LEVEL_DEBUG, "cached PMKSA "
 +                                     "available - ignore it since "
 +                                     "STA sent EAPOL-Start");
 +                      wpa_auth_sta_clear_pmksa(sta->wpa_sm, pmksa);
 +              }
 +              sta->eapol_sm->eapolStart = TRUE;
 +              sta->eapol_sm->dot1xAuthEapolStartFramesRx++;
 +              eap_server_clear_identity(sta->eapol_sm->eap);
 +              wpa_auth_sm_event(sta->wpa_sm, WPA_REAUTH_EAPOL);
 +              break;
 +
 +      case IEEE802_1X_TYPE_EAPOL_LOGOFF:
 +              hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
 +                             HOSTAPD_LEVEL_DEBUG, "received EAPOL-Logoff "
 +                             "from STA");
 +              sta->acct_terminate_cause =
 +                      RADIUS_ACCT_TERMINATE_CAUSE_USER_REQUEST;
 +              accounting_sta_stop(hapd, sta);
 +              sta->eapol_sm->eapolLogoff = TRUE;
 +              sta->eapol_sm->dot1xAuthEapolLogoffFramesRx++;
 +              eap_server_clear_identity(sta->eapol_sm->eap);
 +              break;
 +
 +      case IEEE802_1X_TYPE_EAPOL_KEY:
 +              wpa_printf(MSG_DEBUG, "   EAPOL-Key");
 +              if (!ap_sta_is_authorized(sta)) {
 +                      wpa_printf(MSG_DEBUG, "   Dropped key data from "
 +                                 "unauthorized Supplicant");
 +                      break;
 +              }
 +              break;
 +
 +      case IEEE802_1X_TYPE_EAPOL_ENCAPSULATED_ASF_ALERT:
 +              wpa_printf(MSG_DEBUG, "   EAPOL-Encapsulated-ASF-Alert");
 +              /* TODO: implement support for this; show data */
 +              break;
 +
 +      default:
 +              wpa_printf(MSG_DEBUG, "   unknown IEEE 802.1X packet type");
 +              sta->eapol_sm->dot1xAuthInvalidEapolFramesRx++;
 +              break;
 +      }
 +
 +      eapol_auth_step(sta->eapol_sm);
 +}
 +
 +
 +/**
 + * ieee802_1x_new_station - Start IEEE 802.1X authentication
 + * @hapd: hostapd BSS data
 + * @sta: The station
 + *
 + * This function is called to start IEEE 802.1X authentication when a new
 + * station completes IEEE 802.11 association.
 + */
 +void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta)
 +{
 +      struct rsn_pmksa_cache_entry *pmksa;
 +      int reassoc = 1;
 +      int force_1x = 0;
 +      int key_mgmt;
 +
 +#ifdef CONFIG_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
 +               * authentication in this BSS.
 +               */
 +              force_1x = 1;
 +      }
 +#endif /* CONFIG_WPS */
 +
 +      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");
 +              /*
 +               * Clear any possible EAPOL authenticator state to support
 +               * reassociation change from WPS to PSK.
 +               */
 +              ieee802_1x_free_station(sta);
 +              return;
 +      }
 +
 +      key_mgmt = wpa_auth_sta_key_mgmt(sta->wpa_sm);
 +      if (key_mgmt != -1 && wpa_key_mgmt_wpa_psk(key_mgmt)) {
 +              wpa_printf(MSG_DEBUG, "IEEE 802.1X: Ignore STA - using PSK");
 +              /*
 +               * Clear any possible EAPOL authenticator state to support
 +               * reassociation change from WPA-EAP to PSK.
 +               */
 +              ieee802_1x_free_station(sta);
 +              return;
 +      }
 +
 +      if (sta->eapol_sm == NULL) {
 +              hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
 +                             HOSTAPD_LEVEL_DEBUG, "start authentication");
 +              sta->eapol_sm = ieee802_1x_alloc_eapol_sm(hapd, sta);
 +              if (sta->eapol_sm == NULL) {
 +                      hostapd_logger(hapd, sta->addr,
 +                                     HOSTAPD_MODULE_IEEE8021X,
 +                                     HOSTAPD_LEVEL_INFO,
 +                                     "failed to allocate state machine");
 +                      return;
 +              }
 +              reassoc = 0;
 +      }
 +
 +#ifdef CONFIG_WPS
 +      sta->eapol_sm->flags &= ~EAPOL_SM_WAIT_START;
 +      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
 +               * wait to be skipped if the STA is known to support WPS 2.0.
 +               */
 +              wpa_printf(MSG_DEBUG, "WPS: Do not start EAPOL until "
 +                         "EAPOL-Start is received");
 +              sta->eapol_sm->flags |= EAPOL_SM_WAIT_START;
 +      }
 +#endif /* CONFIG_WPS */
 +
 +      sta->eapol_sm->eap_if->portEnabled = TRUE;
 +
 +#ifdef CONFIG_IEEE80211R
 +      if (sta->auth_alg == WLAN_AUTH_FT) {
 +              hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
 +                             HOSTAPD_LEVEL_DEBUG,
 +                             "PMK from FT - skip IEEE 802.1X/EAP");
 +              /* Setup EAPOL state machines to already authenticated state
 +               * because of existing FT information from R0KH. */
 +              sta->eapol_sm->keyRun = TRUE;
 +              sta->eapol_sm->eap_if->eapKeyAvailable = TRUE;
 +              sta->eapol_sm->auth_pae_state = AUTH_PAE_AUTHENTICATING;
 +              sta->eapol_sm->be_auth_state = BE_AUTH_SUCCESS;
 +              sta->eapol_sm->authSuccess = TRUE;
 +              sta->eapol_sm->authFail = FALSE;
 +              if (sta->eapol_sm->eap)
 +                      eap_sm_notify_cached(sta->eapol_sm->eap);
 +              /* TODO: get vlan_id from R0KH using RRB message */
 +              return;
 +      }
 +#endif /* CONFIG_IEEE80211R */
 +
 +      pmksa = wpa_auth_sta_get_pmksa(sta->wpa_sm);
 +      if (pmksa) {
-               old_vlanid = sta->vlan_id;
 +              hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
 +                             HOSTAPD_LEVEL_DEBUG,
 +                             "PMK from PMKSA cache - skip IEEE 802.1X/EAP");
 +              /* Setup EAPOL state machines to already authenticated state
 +               * because of existing PMKSA information in the cache. */
 +              sta->eapol_sm->keyRun = TRUE;
 +              sta->eapol_sm->eap_if->eapKeyAvailable = TRUE;
 +              sta->eapol_sm->auth_pae_state = AUTH_PAE_AUTHENTICATING;
 +              sta->eapol_sm->be_auth_state = BE_AUTH_SUCCESS;
 +              sta->eapol_sm->authSuccess = TRUE;
 +              sta->eapol_sm->authFail = FALSE;
 +              if (sta->eapol_sm->eap)
 +                      eap_sm_notify_cached(sta->eapol_sm->eap);
-               if (sta->ssid->dynamic_vlan == DYNAMIC_VLAN_DISABLED)
-                       sta->vlan_id = 0;
-               ap_sta_bind_vlan(hapd, sta, old_vlanid);
 +              pmksa_cache_to_eapol_data(pmksa, sta->eapol_sm);
-       u8 *class;
++              ap_sta_bind_vlan(hapd, sta);
 +      } else {
 +              if (reassoc) {
 +                      /*
 +                       * Force EAPOL state machines to start
 +                       * re-authentication without having to wait for the
 +                       * Supplicant to send EAPOL-Start.
 +                       */
 +                      sta->eapol_sm->reAuthenticate = TRUE;
 +              }
 +              eapol_auth_step(sta->eapol_sm);
 +      }
 +}
 +
 +
 +void ieee802_1x_free_station(struct sta_info *sta)
 +{
 +      struct eapol_state_machine *sm = sta->eapol_sm;
 +
 +      if (sm == NULL)
 +              return;
 +
 +      sta->eapol_sm = NULL;
 +
 +#ifndef CONFIG_NO_RADIUS
 +      radius_msg_free(sm->last_recv_radius);
 +      radius_free_class(&sm->radius_class);
 +      wpabuf_free(sm->radius_cui);
 +#endif /* CONFIG_NO_RADIUS */
 +
 +      os_free(sm->identity);
 +      eapol_auth_free(sm);
 +}
 +
 +
 +#ifndef CONFIG_NO_RADIUS
 +static void ieee802_1x_decapsulate_radius(struct hostapd_data *hapd,
 +                                        struct sta_info *sta)
 +{
 +      struct wpabuf *eap;
 +      const struct eap_hdr *hdr;
 +      int eap_type = -1;
 +      char buf[64];
 +      struct radius_msg *msg;
 +      struct eapol_state_machine *sm = sta->eapol_sm;
 +
 +      if (sm == NULL || sm->last_recv_radius == NULL) {
 +              if (sm)
 +                      sm->eap_if->aaaEapNoReq = TRUE;
 +              return;
 +      }
 +
 +      msg = sm->last_recv_radius;
 +
 +      eap = radius_msg_get_eap(msg);
 +      if (eap == NULL) {
 +              /* RFC 3579, Chap. 2.6.3:
 +               * RADIUS server SHOULD NOT send Access-Reject/no EAP-Message
 +               * attribute */
 +              hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
 +                             HOSTAPD_LEVEL_WARNING, "could not extract "
 +                             "EAP-Message from RADIUS message");
 +              sm->eap_if->aaaEapNoReq = TRUE;
 +              return;
 +      }
 +
 +      if (wpabuf_len(eap) < sizeof(*hdr)) {
 +              hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
 +                             HOSTAPD_LEVEL_WARNING, "too short EAP packet "
 +                             "received from authentication server");
 +              wpabuf_free(eap);
 +              sm->eap_if->aaaEapNoReq = TRUE;
 +              return;
 +      }
 +
 +      if (wpabuf_len(eap) > sizeof(*hdr))
 +              eap_type = (wpabuf_head_u8(eap))[sizeof(*hdr)];
 +
 +      hdr = wpabuf_head(eap);
 +      switch (hdr->code) {
 +      case EAP_CODE_REQUEST:
 +              if (eap_type >= 0)
 +                      sm->eap_type_authsrv = eap_type;
 +              os_snprintf(buf, sizeof(buf), "EAP-Request-%s (%d)",
 +                          eap_server_get_name(0, eap_type), eap_type);
 +              break;
 +      case EAP_CODE_RESPONSE:
 +              os_snprintf(buf, sizeof(buf), "EAP Response-%s (%d)",
 +                          eap_server_get_name(0, eap_type), eap_type);
 +              break;
 +      case EAP_CODE_SUCCESS:
 +              os_strlcpy(buf, "EAP Success", sizeof(buf));
 +              break;
 +      case EAP_CODE_FAILURE:
 +              os_strlcpy(buf, "EAP Failure", sizeof(buf));
 +              break;
 +      default:
 +              os_strlcpy(buf, "unknown EAP code", sizeof(buf));
 +              break;
 +      }
 +      buf[sizeof(buf) - 1] = '\0';
 +      hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
 +                     HOSTAPD_LEVEL_DEBUG, "decapsulated EAP packet (code=%d "
 +                     "id=%d len=%d) from RADIUS server: %s",
 +                     hdr->code, hdr->identifier, be_to_host16(hdr->length),
 +                     buf);
 +      sm->eap_if->aaaEapReq = TRUE;
 +
 +      wpabuf_free(sm->eap_if->aaaEapReqData);
 +      sm->eap_if->aaaEapReqData = eap;
 +}
 +
 +
 +static void ieee802_1x_get_keys(struct hostapd_data *hapd,
 +                              struct sta_info *sta, struct radius_msg *msg,
 +                              struct radius_msg *req,
 +                              const u8 *shared_secret,
 +                              size_t shared_secret_len)
 +{
 +      struct radius_ms_mppe_keys *keys;
 +      struct eapol_state_machine *sm = sta->eapol_sm;
 +      if (sm == NULL)
 +              return;
 +
 +      keys = radius_msg_get_ms_keys(msg, req, shared_secret,
 +                                    shared_secret_len);
 +
 +      if (keys && keys->send && keys->recv) {
 +              size_t len = keys->send_len + keys->recv_len;
 +              wpa_hexdump_key(MSG_DEBUG, "MS-MPPE-Send-Key",
 +                              keys->send, keys->send_len);
 +              wpa_hexdump_key(MSG_DEBUG, "MS-MPPE-Recv-Key",
 +                              keys->recv, keys->recv_len);
 +
 +              os_free(sm->eap_if->aaaEapKeyData);
 +              sm->eap_if->aaaEapKeyData = os_malloc(len);
 +              if (sm->eap_if->aaaEapKeyData) {
 +                      os_memcpy(sm->eap_if->aaaEapKeyData, keys->recv,
 +                                keys->recv_len);
 +                      os_memcpy(sm->eap_if->aaaEapKeyData + keys->recv_len,
 +                                keys->send, keys->send_len);
 +                      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) {
 +              os_free(keys->send);
 +              os_free(keys->recv);
 +              os_free(keys);
 +      }
 +}
 +
 +
 +static void ieee802_1x_store_radius_class(struct hostapd_data *hapd,
 +                                        struct sta_info *sta,
 +                                        struct radius_msg *msg)
 +{
-       class = NULL;
++      u8 *attr_class;
 +      size_t class_len;
 +      struct eapol_state_machine *sm = sta->eapol_sm;
 +      int count, i;
 +      struct radius_attr_data *nclass;
 +      size_t nclass_count;
 +
 +      if (!hapd->conf->radius->acct_server || hapd->radius == NULL ||
 +          sm == NULL)
 +              return;
 +
 +      radius_free_class(&sm->radius_class);
 +      count = radius_msg_count_attr(msg, RADIUS_ATTR_CLASS, 1);
 +      if (count <= 0)
 +              return;
 +
 +      nclass = os_calloc(count, sizeof(struct radius_attr_data));
 +      if (nclass == NULL)
 +              return;
 +
 +      nclass_count = 0;
 +
-                                                   &class, &class_len,
-                                                   class) < 0) {
++      attr_class = NULL;
 +      for (i = 0; i < count; i++) {
 +              do {
 +                      if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_CLASS,
-               os_memcpy(nclass[nclass_count].data, class, class_len);
++                                                  &attr_class, &class_len,
++                                                  attr_class) < 0) {
 +                              i = count;
 +                              break;
 +                      }
 +              } while (class_len < 1);
 +
 +              nclass[nclass_count].data = os_malloc(class_len);
 +              if (nclass[nclass_count].data == NULL)
 +                      break;
 +
-       int session_timeout_set, old_vlanid = 0;
++              os_memcpy(nclass[nclass_count].data, attr_class, class_len);
 +              nclass[nclass_count].len = class_len;
 +              nclass_count++;
 +      }
 +
 +      sm->radius_class.attr = nclass;
 +      sm->radius_class.count = nclass_count;
 +      wpa_printf(MSG_DEBUG, "IEEE 802.1X: Stored %lu RADIUS Class "
 +                 "attributes for " MACSTR,
 +                 (unsigned long) sm->radius_class.count,
 +                 MAC2STR(sta->addr));
 +}
 +
 +
 +/* Update sta->identity based on User-Name attribute in Access-Accept */
 +static void ieee802_1x_update_sta_identity(struct hostapd_data *hapd,
 +                                         struct sta_info *sta,
 +                                         struct radius_msg *msg)
 +{
 +      u8 *buf, *identity;
 +      size_t len;
 +      struct eapol_state_machine *sm = sta->eapol_sm;
 +
 +      if (sm == NULL)
 +              return;
 +
 +      if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_USER_NAME, &buf, &len,
 +                                  NULL) < 0)
 +              return;
 +
 +      identity = (u8 *) dup_binstr(buf, len);
 +      if (identity == NULL)
 +              return;
 +
 +      hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
 +                     HOSTAPD_LEVEL_DEBUG, "old identity '%s' updated with "
 +                     "User-Name from Access-Accept '%s'",
 +                     sm->identity ? (char *) sm->identity : "N/A",
 +                     (char *) identity);
 +
 +      os_free(sm->identity);
 +      sm->identity = identity;
 +      sm->identity_len = len;
 +}
 +
 +
 +/* Update CUI based on Chargeable-User-Identity attribute in Access-Accept */
 +static void ieee802_1x_update_sta_cui(struct hostapd_data *hapd,
 +                                    struct sta_info *sta,
 +                                    struct radius_msg *msg)
 +{
 +      struct eapol_state_machine *sm = sta->eapol_sm;
 +      struct wpabuf *cui;
 +      u8 *buf;
 +      size_t len;
 +
 +      if (sm == NULL)
 +              return;
 +
 +      if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_CHARGEABLE_USER_IDENTITY,
 +                                  &buf, &len, NULL) < 0)
 +              return;
 +
 +      cui = wpabuf_alloc_copy(buf, len);
 +      if (cui == NULL)
 +              return;
 +
 +      wpabuf_free(sm->radius_cui);
 +      sm->radius_cui = cui;
 +}
 +
 +
 +#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;
 +};
 +
 +
 +static int ieee802_1x_select_radius_identifier(struct hostapd_data *hapd,
 +                                             struct sta_info *sta,
 +                                             void *ctx)
 +{
 +      struct sta_id_search *id_search = ctx;
 +      struct eapol_state_machine *sm = sta->eapol_sm;
 +
 +      if (sm && sm->radius_identifier >= 0 &&
 +          sm->radius_identifier == id_search->identifier) {
 +              id_search->sm = sm;
 +              return 1;
 +      }
 +      return 0;
 +}
 +
 +
 +static struct eapol_state_machine *
 +ieee802_1x_search_radius_identifier(struct hostapd_data *hapd, u8 identifier)
 +{
 +      struct sta_id_search id_search;
 +      id_search.identifier = identifier;
 +      id_search.sm = NULL;
 +      ap_for_each_sta(hapd, ieee802_1x_select_radius_identifier, &id_search);
 +      return id_search.sm;
 +}
 +
 +
 +/**
 + * ieee802_1x_receive_auth - Process RADIUS frames from Authentication Server
 + * @msg: RADIUS response message
 + * @req: RADIUS request message
 + * @shared_secret: RADIUS shared secret
 + * @shared_secret_len: Length of shared_secret in octets
 + * @data: Context data (struct hostapd_data *)
 + * Returns: Processing status
 + */
 +static RadiusRxResult
 +ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req,
 +                      const u8 *shared_secret, size_t shared_secret_len,
 +                      void *data)
 +{
 +      struct hostapd_data *hapd = data;
 +      struct sta_info *sta;
 +      u32 session_timeout = 0, termination_action, acct_interim_interval;
-               if (sta->ssid->dynamic_vlan == DYNAMIC_VLAN_DISABLED)
-                       sta->vlan_id = 0;
++      int session_timeout_set, vlan_id = 0;
 +      struct eapol_state_machine *sm;
 +      int override_eapReq = 0;
 +      struct radius_hdr *hdr = radius_msg_get_hdr(msg);
 +
 +      sm = ieee802_1x_search_radius_identifier(hapd, hdr->identifier);
 +      if (sm == NULL) {
 +              wpa_printf(MSG_DEBUG, "IEEE 802.1X: Could not find matching "
 +                         "station for this RADIUS message");
 +              return RADIUS_RX_UNKNOWN;
 +      }
 +      sta = sm->sta;
 +
 +      /* RFC 2869, Ch. 5.13: valid Message-Authenticator attribute MUST be
 +       * present when packet contains an EAP-Message attribute */
 +      if (hdr->code == RADIUS_CODE_ACCESS_REJECT &&
 +          radius_msg_get_attr(msg, RADIUS_ATTR_MESSAGE_AUTHENTICATOR, NULL,
 +                              0) < 0 &&
 +          radius_msg_get_attr(msg, RADIUS_ATTR_EAP_MESSAGE, NULL, 0) < 0) {
 +              wpa_printf(MSG_DEBUG, "Allowing RADIUS Access-Reject without "
 +                         "Message-Authenticator since it does not include "
 +                         "EAP-Message");
 +      } else if (radius_msg_verify(msg, shared_secret, shared_secret_len,
 +                                   req, 1)) {
 +              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) {
 +              wpa_printf(MSG_INFO, "Unknown RADIUS message code");
 +              return RADIUS_RX_UNKNOWN;
 +      }
 +
 +      sm->radius_identifier = -1;
 +      wpa_printf(MSG_DEBUG, "RADIUS packet matching with station " MACSTR,
 +                 MAC2STR(sta->addr));
 +
 +      radius_msg_free(sm->last_recv_radius);
 +      sm->last_recv_radius = msg;
 +
 +      session_timeout_set =
 +              !radius_msg_get_attr_int32(msg, RADIUS_ATTR_SESSION_TIMEOUT,
 +                                         &session_timeout);
 +      if (radius_msg_get_attr_int32(msg, RADIUS_ATTR_TERMINATION_ACTION,
 +                                    &termination_action))
 +              termination_action = RADIUS_TERMINATION_ACTION_DEFAULT;
 +
 +      if (hapd->conf->acct_interim_interval == 0 &&
 +          hdr->code == RADIUS_CODE_ACCESS_ACCEPT &&
 +          radius_msg_get_attr_int32(msg, RADIUS_ATTR_ACCT_INTERIM_INTERVAL,
 +                                    &acct_interim_interval) == 0) {
 +              if (acct_interim_interval < 60) {
 +                      hostapd_logger(hapd, sta->addr,
 +                                     HOSTAPD_MODULE_IEEE8021X,
 +                                     HOSTAPD_LEVEL_INFO,
 +                                     "ignored too small "
 +                                     "Acct-Interim-Interval %d",
 +                                     acct_interim_interval);
 +              } else
 +                      sta->acct_interim_interval = acct_interim_interval;
 +      }
 +
 +
 +      switch (hdr->code) {
 +      case RADIUS_CODE_ACCESS_ACCEPT:
-               else {
-                       old_vlanid = sta->vlan_id;
-                       sta->vlan_id = radius_msg_get_vlanid(msg);
-               }
-               if (sta->vlan_id > 0 &&
-                   hostapd_vlan_id_valid(hapd->conf->vlan, sta->vlan_id)) {
++              if (hapd->conf->ssid.dynamic_vlan == DYNAMIC_VLAN_DISABLED)
++                      vlan_id = 0;
 +#ifndef CONFIG_NO_VLAN
-                                      "VLAN ID %d", sta->vlan_id);
-               } else if (sta->ssid->dynamic_vlan == DYNAMIC_VLAN_REQUIRED) {
++              else
++                      vlan_id = radius_msg_get_vlanid(msg);
++              if (vlan_id > 0 &&
++                  hostapd_vlan_id_valid(hapd->conf->vlan, vlan_id)) {
 +                      hostapd_logger(hapd, sta->addr,
 +                                     HOSTAPD_MODULE_RADIUS,
 +                                     HOSTAPD_LEVEL_INFO,
-               if (ap_sta_bind_vlan(hapd, sta, old_vlanid) < 0)
++                                     "VLAN ID %d", vlan_id);
++              } else if (vlan_id > 0) {
++                      sta->eapol_sm->authFail = TRUE;
++                      hostapd_logger(hapd, sta->addr,
++                                     HOSTAPD_MODULE_RADIUS,
++                                     HOSTAPD_LEVEL_INFO,
++                                     "Invalid VLAN ID %d received from RADIUS server",
++                                     vlan_id);
++                      break;
++              } else if (hapd->conf->ssid.dynamic_vlan ==
++                         DYNAMIC_VLAN_REQUIRED) {
 +                      sta->eapol_sm->authFail = TRUE;
 +                      hostapd_logger(hapd, sta->addr,
 +                                     HOSTAPD_MODULE_IEEE8021X,
 +                                     HOSTAPD_LEVEL_INFO, "authentication "
 +                                     "server did not include required VLAN "
 +                                     "ID in Access-Accept");
 +                      break;
 +              }
 +#endif /* CONFIG_NO_VLAN */
 +
-               return -1;
++              sta->vlan_id = vlan_id;
++              if ((sta->flags & WLAN_STA_ASSOC) &&
++                  ap_sta_bind_vlan(hapd, sta) < 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) {
 +                      sm->reAuthPeriod = session_timeout;
 +              } else if (session_timeout_set)
 +                      ap_sta_session_timeout(hapd, sta, session_timeout);
 +
 +              sm->eap_if->aaaSuccess = TRUE;
 +              override_eapReq = 1;
 +              ieee802_1x_get_keys(hapd, sta, msg, req, shared_secret,
 +                                  shared_secret_len);
 +              ieee802_1x_store_radius_class(hapd, sta, msg);
 +              ieee802_1x_update_sta_identity(hapd, sta, msg);
 +              ieee802_1x_update_sta_cui(hapd, sta, msg);
 +              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) {
 +                      hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA,
 +                                     HOSTAPD_LEVEL_DEBUG,
 +                                     "Added PMKSA cache entry");
 +              }
 +              break;
 +      case RADIUS_CODE_ACCESS_REJECT:
 +              sm->eap_if->aaaFail = TRUE;
 +              override_eapReq = 1;
 +              break;
 +      case RADIUS_CODE_ACCESS_CHALLENGE:
 +              sm->eap_if->aaaEapReq = TRUE;
 +              if (session_timeout_set) {
 +                      /* RFC 2869, Ch. 2.3.2; RFC 3580, Ch. 3.17 */
 +                      sm->eap_if->aaaMethodTimeout = session_timeout;
 +                      hostapd_logger(hapd, sm->addr,
 +                                     HOSTAPD_MODULE_IEEE8021X,
 +                                     HOSTAPD_LEVEL_DEBUG,
 +                                     "using EAP timeout of %d seconds (from "
 +                                     "RADIUS)",
 +                                     sm->eap_if->aaaMethodTimeout);
 +              } else {
 +                      /*
 +                       * Use dynamic retransmission behavior per EAP
 +                       * specification.
 +                       */
 +                      sm->eap_if->aaaMethodTimeout = 0;
 +              }
 +              break;
 +      }
 +
 +      ieee802_1x_decapsulate_radius(hapd, sta);
 +      if (override_eapReq)
 +              sm->eap_if->aaaEapReq = FALSE;
 +
 +      eapol_auth_step(sm);
 +
 +      return RADIUS_RX_QUEUED;
 +}
 +#endif /* CONFIG_NO_RADIUS */
 +
 +
 +void ieee802_1x_abort_auth(struct hostapd_data *hapd, struct sta_info *sta)
 +{
 +      struct eapol_state_machine *sm = sta->eapol_sm;
 +      if (sm == NULL)
 +              return;
 +
 +      hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
 +                     HOSTAPD_LEVEL_DEBUG, "aborting authentication");
 +
 +#ifndef CONFIG_NO_RADIUS
 +      radius_msg_free(sm->last_recv_radius);
 +      sm->last_recv_radius = NULL;
 +#endif /* CONFIG_NO_RADIUS */
 +
 +      if (sm->eap_if->eapTimeout) {
 +              /*
 +               * Disconnect the STA since it did not reply to the last EAP
 +               * request and we cannot continue EAP processing (EAP-Failure
 +               * could only be sent if the EAP peer actually replied).
 +               */
 +              wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "EAP Timeout, STA " MACSTR,
 +                      MAC2STR(sta->addr));
 +
 +              sm->eap_if->portEnabled = FALSE;
 +              ap_sta_disconnect(hapd, sta, sta->addr,
 +                                WLAN_REASON_PREV_AUTH_NOT_VALID);
 +      }
 +}
 +
 +
 +static int ieee802_1x_rekey_broadcast(struct hostapd_data *hapd)
 +{
 +      struct eapol_authenticator *eapol = hapd->eapol_auth;
 +
 +      if (hapd->conf->default_wep_key_len < 1)
 +              return 0;
 +
 +      os_free(eapol->default_wep_key);
 +      eapol->default_wep_key = os_malloc(hapd->conf->default_wep_key_len);
 +      if (eapol->default_wep_key == NULL ||
 +          random_get_bytes(eapol->default_wep_key,
 +                           hapd->conf->default_wep_key_len)) {
 +              wpa_printf(MSG_INFO, "Could not generate random WEP key");
 +              os_free(eapol->default_wep_key);
 +              eapol->default_wep_key = NULL;
 +              return -1;
 +      }
 +
 +      wpa_hexdump_key(MSG_DEBUG, "IEEE 802.1X: New default WEP key",
 +                      eapol->default_wep_key,
 +                      hapd->conf->default_wep_key_len);
 +
 +      return 0;
 +}
 +
 +
 +static int ieee802_1x_sta_key_available(struct hostapd_data *hapd,
 +                                      struct sta_info *sta, void *ctx)
 +{
 +      if (sta->eapol_sm) {
 +              sta->eapol_sm->eap_if->eapKeyAvailable = TRUE;
 +              eapol_auth_step(sta->eapol_sm);
 +      }
 +      return 0;
 +}
 +
 +
 +static void ieee802_1x_rekey(void *eloop_ctx, void *timeout_ctx)
 +{
 +      struct hostapd_data *hapd = eloop_ctx;
 +      struct eapol_authenticator *eapol = hapd->eapol_auth;
 +
 +      if (eapol->default_wep_key_idx >= 3)
 +              eapol->default_wep_key_idx =
 +                      hapd->conf->individual_wep_key_len > 0 ? 1 : 0;
 +      else
 +              eapol->default_wep_key_idx++;
 +
 +      wpa_printf(MSG_DEBUG, "IEEE 802.1X: New default WEP key index %d",
 +                 eapol->default_wep_key_idx);
 +                    
 +      if (ieee802_1x_rekey_broadcast(hapd)) {
 +              hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE8021X,
 +                             HOSTAPD_LEVEL_WARNING, "failed to generate a "
 +                             "new broadcast key");
 +              os_free(eapol->default_wep_key);
 +              eapol->default_wep_key = NULL;
 +              return;
 +      }
 +
 +      /* TODO: Could setup key for RX here, but change default TX keyid only
 +       * after new broadcast key has been sent to all stations. */
 +      if (hostapd_drv_set_key(hapd->conf->iface, hapd, WPA_ALG_WEP,
 +                              broadcast_ether_addr,
 +                              eapol->default_wep_key_idx, 1, NULL, 0,
 +                              eapol->default_wep_key,
 +                              hapd->conf->default_wep_key_len)) {
 +              hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE8021X,
 +                             HOSTAPD_LEVEL_WARNING, "failed to configure a "
 +                             "new broadcast key");
 +              os_free(eapol->default_wep_key);
 +              eapol->default_wep_key = NULL;
 +              return;
 +      }
 +
 +      ap_for_each_sta(hapd, ieee802_1x_sta_key_available, NULL);
 +
 +      if (hapd->conf->wep_rekeying_period > 0) {
 +              eloop_register_timeout(hapd->conf->wep_rekeying_period, 0,
 +                                     ieee802_1x_rekey, hapd, NULL);
 +      }
 +}
 +
 +
 +static void ieee802_1x_eapol_send(void *ctx, void *sta_ctx, u8 type,
 +                                const u8 *data, size_t datalen)
 +{
 +#ifdef CONFIG_WPS
 +      struct sta_info *sta = sta_ctx;
 +
 +      if ((sta->flags & (WLAN_STA_WPS | WLAN_STA_MAYBE_WPS)) ==
 +          WLAN_STA_MAYBE_WPS) {
 +              const u8 *identity;
 +              size_t identity_len;
 +              struct eapol_state_machine *sm = sta->eapol_sm;
 +
 +              identity = eap_get_identity(sm->eap, &identity_len);
 +              if (identity &&
 +                  ((identity_len == WSC_ID_ENROLLEE_LEN &&
 +                    os_memcmp(identity, WSC_ID_ENROLLEE,
 +                              WSC_ID_ENROLLEE_LEN) == 0) ||
 +                   (identity_len == WSC_ID_REGISTRAR_LEN &&
 +                    os_memcmp(identity, WSC_ID_REGISTRAR,
 +                              WSC_ID_REGISTRAR_LEN) == 0))) {
 +                      wpa_printf(MSG_DEBUG, "WPS: WLAN_STA_MAYBE_WPS -> "
 +                                 "WLAN_STA_WPS");
 +                      sta->flags |= WLAN_STA_WPS;
 +              }
 +      }
 +#endif /* CONFIG_WPS */
 +
 +      ieee802_1x_send(ctx, sta_ctx, type, data, datalen);
 +}
 +
 +
 +static void ieee802_1x_aaa_send(void *ctx, void *sta_ctx,
 +                              const u8 *data, size_t datalen)
 +{
 +#ifndef CONFIG_NO_RADIUS
 +      struct hostapd_data *hapd = ctx;
 +      struct sta_info *sta = sta_ctx;
 +
 +      ieee802_1x_encapsulate_radius(hapd, sta, data, datalen);
 +#endif /* CONFIG_NO_RADIUS */
 +}
 +
 +
 +static void _ieee802_1x_finished(void *ctx, void *sta_ctx, int success,
 +                               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, remediation);
 +}
 +
 +
 +static int ieee802_1x_get_eap_user(void *ctx, const u8 *identity,
 +                                 size_t identity_len, int phase2,
 +                                 struct eap_user *user)
 +{
 +      struct hostapd_data *hapd = ctx;
 +      const struct hostapd_eap_user *eap_user;
 +      int i;
++      int rv = -1;
 +
 +      eap_user = hostapd_get_eap_user(hapd, identity, identity_len, phase2);
 +      if (eap_user == NULL)
-                       return -1;
++              goto out;
 +
 +      os_memset(user, 0, sizeof(*user));
 +      user->phase2 = phase2;
 +      for (i = 0; i < EAP_MAX_METHODS; i++) {
 +              user->methods[i].vendor = eap_user->methods[i].vendor;
 +              user->methods[i].method = eap_user->methods[i].method;
 +      }
 +
 +      if (eap_user->password) {
 +              user->password = os_malloc(eap_user->password_len);
 +              if (user->password == NULL)
-       return 0;
++                      goto out;
 +              os_memcpy(user->password, eap_user->password,
 +                        eap_user->password_len);
 +              user->password_len = eap_user->password_len;
 +              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;
++      rv = 0;
 +
- static const char * bool_txt(Boolean bool)
++out:
++      if (rv)
++              wpa_printf(MSG_DEBUG, "%s: Failed to find user", __func__);
++
++      return rv;
 +}
 +
 +
 +static int ieee802_1x_sta_entry_alive(void *ctx, const u8 *addr)
 +{
 +      struct hostapd_data *hapd = ctx;
 +      struct sta_info *sta;
 +      sta = ap_get_sta(hapd, addr);
 +      if (sta == NULL || sta->eapol_sm == NULL)
 +              return 0;
 +      return 1;
 +}
 +
 +
 +static void ieee802_1x_logger(void *ctx, const u8 *addr,
 +                            eapol_logger_level level, const char *txt)
 +{
 +#ifndef CONFIG_NO_HOSTAPD_LOGGER
 +      struct hostapd_data *hapd = ctx;
 +      int hlevel;
 +
 +      switch (level) {
 +      case EAPOL_LOGGER_WARNING:
 +              hlevel = HOSTAPD_LEVEL_WARNING;
 +              break;
 +      case EAPOL_LOGGER_INFO:
 +              hlevel = HOSTAPD_LEVEL_INFO;
 +              break;
 +      case EAPOL_LOGGER_DEBUG:
 +      default:
 +              hlevel = HOSTAPD_LEVEL_DEBUG;
 +              break;
 +      }
 +
 +      hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE8021X, hlevel, "%s",
 +                     txt);
 +#endif /* CONFIG_NO_HOSTAPD_LOGGER */
 +}
 +
 +
 +static void ieee802_1x_set_port_authorized(void *ctx, void *sta_ctx,
 +                                         int authorized)
 +{
 +      struct hostapd_data *hapd = ctx;
 +      struct sta_info *sta = sta_ctx;
 +      ieee802_1x_set_sta_authorized(hapd, sta, authorized);
 +}
 +
 +
 +static void _ieee802_1x_abort_auth(void *ctx, void *sta_ctx)
 +{
 +      struct hostapd_data *hapd = ctx;
 +      struct sta_info *sta = sta_ctx;
 +      ieee802_1x_abort_auth(hapd, sta);
 +}
 +
 +
 +static void _ieee802_1x_tx_key(void *ctx, void *sta_ctx)
 +{
++#ifndef CONFIG_FIPS
++#ifndef CONFIG_NO_RC4
 +      struct hostapd_data *hapd = ctx;
 +      struct sta_info *sta = sta_ctx;
 +      ieee802_1x_tx_key(hapd, sta);
++#endif /* CONFIG_NO_RC4 */
++#endif /* CONFIG_FIPS */
 +}
 +
 +
 +static void ieee802_1x_eapol_event(void *ctx, void *sta_ctx,
 +                                 enum eapol_event type)
 +{
 +      /* struct hostapd_data *hapd = ctx; */
 +      struct sta_info *sta = sta_ctx;
 +      switch (type) {
 +      case EAPOL_AUTH_SM_CHANGE:
 +              wpa_auth_sm_notify(sta->wpa_sm);
 +              break;
 +      case EAPOL_AUTH_REAUTHENTICATE:
 +              wpa_auth_sm_event(sta->wpa_sm, WPA_REAUTH_EAPOL);
 +              break;
 +      }
 +}
 +
 +
 +#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;
 +      conf.wpa = hapd->conf->wpa;
 +      conf.individual_wep_key_len = hapd->conf->individual_wep_key_len;
 +      conf.eap_server = hapd->conf->eap_server;
 +      conf.ssl_ctx = hapd->ssl_ctx;
 +      conf.msg_ctx = hapd->msg_ctx;
 +      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.tls_session_lifetime = hapd->conf->tls_session_lifetime;
 +      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;
 +      conf.eap_fast_a_id_info = hapd->conf->eap_fast_a_id_info;
 +      conf.eap_fast_prov = hapd->conf->eap_fast_prov;
 +      conf.pac_key_lifetime = hapd->conf->pac_key_lifetime;
 +      conf.pac_key_refresh_time = hapd->conf->pac_key_refresh_time;
 +      conf.eap_sim_aka_result_ind = hapd->conf->eap_sim_aka_result_ind;
 +      conf.tnc = hapd->conf->tnc;
 +      conf.wps = hapd->wps;
 +      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;
 +      cb.aaa_send = ieee802_1x_aaa_send;
 +      cb.finished = _ieee802_1x_finished;
 +      cb.get_eap_user = ieee802_1x_get_eap_user;
 +      cb.sta_entry_alive = ieee802_1x_sta_entry_alive;
 +      cb.logger = ieee802_1x_logger;
 +      cb.set_port_authorized = ieee802_1x_set_port_authorized;
 +      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)
 +              return -1;
 +
 +      if ((hapd->conf->ieee802_1x || hapd->conf->wpa) &&
 +          hostapd_set_drv_ieee8021x(hapd, hapd->conf->iface, 1))
 +              return -1;
 +
 +#ifndef CONFIG_NO_RADIUS
 +      if (radius_client_register(hapd->radius, RADIUS_AUTH,
 +                                 ieee802_1x_receive_auth, hapd))
 +              return -1;
 +#endif /* CONFIG_NO_RADIUS */
 +
 +      if (hapd->conf->default_wep_key_len) {
 +              for (i = 0; i < 4; i++)
 +                      hostapd_drv_set_key(hapd->conf->iface, hapd,
 +                                          WPA_ALG_NONE, NULL, i, 0, NULL, 0,
 +                                          NULL, 0);
 +
 +              ieee802_1x_rekey(hapd, NULL);
 +
 +              if (hapd->eapol_auth->default_wep_key == NULL)
 +                      return -1;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +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);
 +
 +      if (hapd->driver != NULL &&
 +          (hapd->conf->ieee802_1x || hapd->conf->wpa))
 +              hostapd_set_drv_ieee8021x(hapd, hapd->conf->iface, 0);
 +
 +      eapol_auth_deinit(hapd->eapol_auth);
 +      hapd->eapol_auth = NULL;
 +
 +      ieee802_1x_erp_flush(hapd);
 +}
 +
 +
 +int ieee802_1x_tx_status(struct hostapd_data *hapd, struct sta_info *sta,
 +                       const u8 *buf, size_t len, int ack)
 +{
 +      struct ieee80211_hdr *hdr;
 +      u8 *pos;
 +      const unsigned char rfc1042_hdr[ETH_ALEN] =
 +              { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 };
 +
 +      if (sta == NULL)
 +              return -1;
 +      if (len < sizeof(*hdr) + sizeof(rfc1042_hdr) + 2)
 +              return 0;
 +
 +      hdr = (struct ieee80211_hdr *) buf;
 +      pos = (u8 *) (hdr + 1);
 +      if (os_memcmp(pos, rfc1042_hdr, sizeof(rfc1042_hdr)) != 0)
 +              return 0;
 +      pos += sizeof(rfc1042_hdr);
 +      if (WPA_GET_BE16(pos) != ETH_P_PAE)
 +              return 0;
 +      pos += 2;
 +
 +      return ieee802_1x_eapol_tx_status(hapd, sta, pos, buf + len - pos,
 +                                        ack);
 +}
 +
 +
 +int ieee802_1x_eapol_tx_status(struct hostapd_data *hapd, struct sta_info *sta,
 +                             const u8 *buf, int len, int ack)
 +{
 +      const struct ieee802_1x_hdr *xhdr =
 +              (const struct ieee802_1x_hdr *) buf;
 +      const u8 *pos = buf + sizeof(*xhdr);
 +      struct ieee802_1x_eapol_key *key;
 +
 +      if (len < (int) sizeof(*xhdr))
 +              return 0;
 +      wpa_printf(MSG_DEBUG, "IEEE 802.1X: " MACSTR " TX status - version=%d "
 +                 "type=%d length=%d - ack=%d",
 +                 MAC2STR(sta->addr), xhdr->version, xhdr->type,
 +                 be_to_host16(xhdr->length), ack);
 +
 +      if (xhdr->type != IEEE802_1X_TYPE_EAPOL_KEY)
 +              return 0;
 +
 +      if (pos + sizeof(struct wpa_eapol_key) <= buf + len) {
 +              const struct wpa_eapol_key *wpa;
 +              wpa = (const struct wpa_eapol_key *) pos;
 +              if (wpa->type == EAPOL_KEY_TYPE_RSN ||
 +                  wpa->type == EAPOL_KEY_TYPE_WPA)
 +                      wpa_auth_eapol_key_tx_status(hapd->wpa_auth,
 +                                                   sta->wpa_sm, ack);
 +      }
 +
 +      /* EAPOL EAP-Packet packets are eventually re-sent by either Supplicant
 +       * or Authenticator state machines, but EAPOL-Key packets are not
 +       * retransmitted in case of failure. Try to re-send failed EAPOL-Key
 +       * packets couple of times because otherwise STA keys become
 +       * unsynchronized with AP. */
 +      if (!ack && pos + sizeof(*key) <= buf + len) {
 +              key = (struct ieee802_1x_eapol_key *) pos;
 +              hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
 +                             HOSTAPD_LEVEL_DEBUG, "did not Ack EAPOL-Key "
 +                             "frame (%scast index=%d)",
 +                             key->key_index & BIT(7) ? "uni" : "broad",
 +                             key->key_index & ~BIT(7));
 +              /* TODO: re-send EAPOL-Key couple of times (with short delay
 +               * between them?). If all attempt fail, report error and
 +               * deauthenticate STA so that it will get new keys when
 +               * authenticating again (e.g., after returning in range).
 +               * Separate limit/transmit state needed both for unicast and
 +               * broadcast keys(?) */
 +      }
 +      /* TODO: could move unicast key configuration from ieee802_1x_tx_key()
 +       * to here and change the key only if the EAPOL-Key packet was Acked.
 +       */
 +
 +      return 1;
 +}
 +
 +
 +u8 * ieee802_1x_get_identity(struct eapol_state_machine *sm, size_t *len)
 +{
 +      if (sm == NULL || sm->identity == NULL)
 +              return NULL;
 +
 +      *len = sm->identity_len;
 +      return sm->identity;
 +}
 +
 +
 +u8 * ieee802_1x_get_radius_class(struct eapol_state_machine *sm, size_t *len,
 +                               int idx)
 +{
 +      if (sm == NULL || sm->radius_class.attr == NULL ||
 +          idx >= (int) sm->radius_class.count)
 +              return NULL;
 +
 +      *len = sm->radius_class.attr[idx].len;
 +      return sm->radius_class.attr[idx].data;
 +}
 +
 +
 +struct wpabuf * ieee802_1x_get_radius_cui(struct eapol_state_machine *sm)
 +{
 +      if (sm == NULL)
 +              return NULL;
 +      return sm->radius_cui;
 +}
 +
 +
 +const u8 * ieee802_1x_get_key(struct eapol_state_machine *sm, size_t *len)
 +{
 +      *len = 0;
 +      if (sm == NULL)
 +              return NULL;
 +
 +      *len = sm->eap_if->eapKeyDataLen;
 +      return sm->eap_if->eapKeyData;
 +}
 +
 +
 +void ieee802_1x_notify_port_enabled(struct eapol_state_machine *sm,
 +                                  int enabled)
 +{
 +      if (sm == NULL)
 +              return;
 +      sm->eap_if->portEnabled = enabled ? TRUE : FALSE;
 +      eapol_auth_step(sm);
 +}
 +
 +
 +void ieee802_1x_notify_port_valid(struct eapol_state_machine *sm,
 +                                int valid)
 +{
 +      if (sm == NULL)
 +              return;
 +      sm->portValid = valid ? TRUE : FALSE;
 +      eapol_auth_step(sm);
 +}
 +
 +
 +void ieee802_1x_notify_pre_auth(struct eapol_state_machine *sm, int pre_auth)
 +{
 +      if (sm == NULL)
 +              return;
 +      if (pre_auth)
 +              sm->flags |= EAPOL_SM_PREAUTH;
 +      else
 +              sm->flags &= ~EAPOL_SM_PREAUTH;
 +}
 +
 +
-       return bool ? "TRUE" : "FALSE";
++static const char * bool_txt(Boolean val)
 +{
++      return val ? "TRUE" : "FALSE";
 +}
 +
 +
 +int ieee802_1x_get_mib(struct hostapd_data *hapd, char *buf, size_t buflen)
 +{
 +      /* TODO */
 +      return 0;
 +}
 +
 +
 +int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta,
 +                         char *buf, size_t buflen)
 +{
 +      int len = 0, ret;
 +      struct eapol_state_machine *sm = sta->eapol_sm;
 +      struct os_reltime diff;
 +      const char *name1;
 +      const char *name2;
 +
 +      if (sm == NULL)
 +              return 0;
 +
 +      ret = os_snprintf(buf + len, buflen - len,
 +                        "dot1xPaePortNumber=%d\n"
 +                        "dot1xPaePortProtocolVersion=%d\n"
 +                        "dot1xPaePortCapabilities=1\n"
 +                        "dot1xPaePortInitialize=%d\n"
 +                        "dot1xPaePortReauthenticate=FALSE\n",
 +                        sta->aid,
 +                        EAPOL_VERSION,
 +                        sm->initialize);
 +      if (os_snprintf_error(buflen - len, ret))
 +              return len;
 +      len += ret;
 +
 +      /* dot1xAuthConfigTable */
 +      ret = os_snprintf(buf + len, buflen - len,
 +                        "dot1xAuthPaeState=%d\n"
 +                        "dot1xAuthBackendAuthState=%d\n"
 +                        "dot1xAuthAdminControlledDirections=%d\n"
 +                        "dot1xAuthOperControlledDirections=%d\n"
 +                        "dot1xAuthAuthControlledPortStatus=%d\n"
 +                        "dot1xAuthAuthControlledPortControl=%d\n"
 +                        "dot1xAuthQuietPeriod=%u\n"
 +                        "dot1xAuthServerTimeout=%u\n"
 +                        "dot1xAuthReAuthPeriod=%u\n"
 +                        "dot1xAuthReAuthEnabled=%s\n"
 +                        "dot1xAuthKeyTxEnabled=%s\n",
 +                        sm->auth_pae_state + 1,
 +                        sm->be_auth_state + 1,
 +                        sm->adminControlledDirections,
 +                        sm->operControlledDirections,
 +                        sm->authPortStatus,
 +                        sm->portControl,
 +                        sm->quietPeriod,
 +                        sm->serverTimeout,
 +                        sm->reAuthPeriod,
 +                        bool_txt(sm->reAuthEnabled),
 +                        bool_txt(sm->keyTxEnabled));
 +      if (os_snprintf_error(buflen - len, ret))
 +              return len;
 +      len += ret;
 +
 +      /* dot1xAuthStatsTable */
 +      ret = os_snprintf(buf + len, buflen - len,
 +                        "dot1xAuthEapolFramesRx=%u\n"
 +                        "dot1xAuthEapolFramesTx=%u\n"
 +                        "dot1xAuthEapolStartFramesRx=%u\n"
 +                        "dot1xAuthEapolLogoffFramesRx=%u\n"
 +                        "dot1xAuthEapolRespIdFramesRx=%u\n"
 +                        "dot1xAuthEapolRespFramesRx=%u\n"
 +                        "dot1xAuthEapolReqIdFramesTx=%u\n"
 +                        "dot1xAuthEapolReqFramesTx=%u\n"
 +                        "dot1xAuthInvalidEapolFramesRx=%u\n"
 +                        "dot1xAuthEapLengthErrorFramesRx=%u\n"
 +                        "dot1xAuthLastEapolFrameVersion=%u\n"
 +                        "dot1xAuthLastEapolFrameSource=" MACSTR "\n",
 +                        sm->dot1xAuthEapolFramesRx,
 +                        sm->dot1xAuthEapolFramesTx,
 +                        sm->dot1xAuthEapolStartFramesRx,
 +                        sm->dot1xAuthEapolLogoffFramesRx,
 +                        sm->dot1xAuthEapolRespIdFramesRx,
 +                        sm->dot1xAuthEapolRespFramesRx,
 +                        sm->dot1xAuthEapolReqIdFramesTx,
 +                        sm->dot1xAuthEapolReqFramesTx,
 +                        sm->dot1xAuthInvalidEapolFramesRx,
 +                        sm->dot1xAuthEapLengthErrorFramesRx,
 +                        sm->dot1xAuthLastEapolFrameVersion,
 +                        MAC2STR(sm->addr));
 +      if (os_snprintf_error(buflen - len, ret))
 +              return len;
 +      len += ret;
 +
 +      /* dot1xAuthDiagTable */
 +      ret = os_snprintf(buf + len, buflen - len,
 +                        "dot1xAuthEntersConnecting=%u\n"
 +                        "dot1xAuthEapLogoffsWhileConnecting=%u\n"
 +                        "dot1xAuthEntersAuthenticating=%u\n"
 +                        "dot1xAuthAuthSuccessesWhileAuthenticating=%u\n"
 +                        "dot1xAuthAuthTimeoutsWhileAuthenticating=%u\n"
 +                        "dot1xAuthAuthFailWhileAuthenticating=%u\n"
 +                        "dot1xAuthAuthEapStartsWhileAuthenticating=%u\n"
 +                        "dot1xAuthAuthEapLogoffWhileAuthenticating=%u\n"
 +                        "dot1xAuthAuthReauthsWhileAuthenticated=%u\n"
 +                        "dot1xAuthAuthEapStartsWhileAuthenticated=%u\n"
 +                        "dot1xAuthAuthEapLogoffWhileAuthenticated=%u\n"
 +                        "dot1xAuthBackendResponses=%u\n"
 +                        "dot1xAuthBackendAccessChallenges=%u\n"
 +                        "dot1xAuthBackendOtherRequestsToSupplicant=%u\n"
 +                        "dot1xAuthBackendAuthSuccesses=%u\n"
 +                        "dot1xAuthBackendAuthFails=%u\n",
 +                        sm->authEntersConnecting,
 +                        sm->authEapLogoffsWhileConnecting,
 +                        sm->authEntersAuthenticating,
 +                        sm->authAuthSuccessesWhileAuthenticating,
 +                        sm->authAuthTimeoutsWhileAuthenticating,
 +                        sm->authAuthFailWhileAuthenticating,
 +                        sm->authAuthEapStartsWhileAuthenticating,
 +                        sm->authAuthEapLogoffWhileAuthenticating,
 +                        sm->authAuthReauthsWhileAuthenticated,
 +                        sm->authAuthEapStartsWhileAuthenticated,
 +                        sm->authAuthEapLogoffWhileAuthenticated,
 +                        sm->backendResponses,
 +                        sm->backendAccessChallenges,
 +                        sm->backendOtherRequestsToSupplicant,
 +                        sm->backendAuthSuccesses,
 +                        sm->backendAuthFails);
 +      if (os_snprintf_error(buflen - len, ret))
 +              return len;
 +      len += ret;
 +
 +      /* dot1xAuthSessionStatsTable */
 +      os_reltime_age(&sta->acct_session_start, &diff);
 +      ret = os_snprintf(buf + len, buflen - len,
 +                        /* TODO: dot1xAuthSessionOctetsRx */
 +                        /* TODO: dot1xAuthSessionOctetsTx */
 +                        /* TODO: dot1xAuthSessionFramesRx */
 +                        /* TODO: dot1xAuthSessionFramesTx */
 +                        "dot1xAuthSessionId=%08X-%08X\n"
 +                        "dot1xAuthSessionAuthenticMethod=%d\n"
 +                        "dot1xAuthSessionTime=%u\n"
 +                        "dot1xAuthSessionTerminateCause=999\n"
 +                        "dot1xAuthSessionUserName=%s\n",
 +                        sta->acct_session_id_hi, sta->acct_session_id_lo,
 +                        (wpa_key_mgmt_wpa_ieee8021x(
 +                                 wpa_auth_sta_key_mgmt(sta->wpa_sm))) ?
 +                        1 : 2,
 +                        (unsigned int) diff.sec,
 +                        sm->identity);
 +      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;
 +
 +      return len;
 +}
 +
 +
 +static void ieee802_1x_finished(struct hostapd_data *hapd,
 +                              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 (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)");
 +      }
 +
 +      if (!success) {
 +              /*
 +               * Many devices require deauthentication after WPS provisioning
 +               * and some may not be be able to do that themselves, so
 +               * disconnect the client here. In addition, this may also
 +               * benefit IEEE 802.1X/EAPOL authentication cases, too since
 +               * the EAPOL PAE state machine would remain in HELD state for
 +               * considerable amount of time and some EAP methods, like
 +               * EAP-FAST with anonymous provisioning, may require another
 +               * EAPOL authentication to be started to complete connection.
 +               */
 +              wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "IEEE 802.1X: 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_IEEE_802_1X_AUTH_FAILED);
 +              hostapd_wps_eap_completed(hapd);
 +      }
 +}
index de6e0e75fac3b00606f48bcc6682ca574220f112,0000000000000000000000000000000000000000..14d69556993c4dac944c8683ab174593c623ecc2
mode 100644,000000..100644
--- /dev/null
@@@ -1,62 -1,0 +1,61 @@@
- void ieee802_1x_tx_key(struct hostapd_data *hapd, struct sta_info *sta);
 +/*
 + * hostapd / IEEE 802.1X-2004 Authenticator
 + * Copyright (c) 2002-2012, Jouni Malinen <j@w1.fi>
 + *
 + * This software may be distributed under the terms of the BSD license.
 + * See README for more details.
 + */
 +
 +#ifndef IEEE802_1X_H
 +#define IEEE802_1X_H
 +
 +struct hostapd_data;
 +struct sta_info;
 +struct eapol_state_machine;
 +struct hostapd_config;
 +struct hostapd_bss_config;
 +struct hostapd_radius_attr;
 +struct radius_msg;
 +
 +
 +void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf,
 +                      size_t len);
 +void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta);
 +void ieee802_1x_free_station(struct sta_info *sta);
 +
 +void ieee802_1x_abort_auth(struct hostapd_data *hapd, struct sta_info *sta);
 +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);
 +int ieee802_1x_eapol_tx_status(struct hostapd_data *hapd, struct sta_info *sta,
 +                             const u8 *data, int len, int ack);
 +u8 * ieee802_1x_get_identity(struct eapol_state_machine *sm, size_t *len);
 +u8 * ieee802_1x_get_radius_class(struct eapol_state_machine *sm, size_t *len,
 +                               int idx);
 +struct wpabuf * ieee802_1x_get_radius_cui(struct eapol_state_machine *sm);
 +const u8 * ieee802_1x_get_key(struct eapol_state_machine *sm, size_t *len);
 +void ieee802_1x_notify_port_enabled(struct eapol_state_machine *sm,
 +                                  int enabled);
 +void ieee802_1x_notify_port_valid(struct eapol_state_machine *sm,
 +                                int valid);
 +void ieee802_1x_notify_pre_auth(struct eapol_state_machine *sm, int pre_auth);
 +int ieee802_1x_get_mib(struct hostapd_data *hapd, char *buf, size_t buflen);
 +int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta,
 +                         char *buf, size_t buflen);
 +void hostapd_get_ntp_timestamp(u8 *buf);
 +char *eap_type_text(u8 type);
 +
 +const char *radius_mode_txt(struct hostapd_data *hapd);
 +int radius_sta_rate(struct hostapd_data *hapd, struct sta_info *sta);
 +
 +int add_common_radius_attr(struct hostapd_data *hapd,
 +                         struct hostapd_radius_attr *req_attr,
 +                         struct sta_info *sta,
 +                         struct radius_msg *msg);
 +
 +#endif /* IEEE802_1X_H */
index b0d42dcd82c4912b63c9ccc1e35f659d38176105,0000000000000000000000000000000000000000..4a87721e2ecfce3f65e070c3fd3e22077f3f2cab
mode 100644,000000..100644
--- /dev/null
@@@ -1,171 -1,0 +1,184 @@@
-       struct in6_addr *saddr;
 +/*
 + * 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 ucast_to_stas(struct hostapd_data *hapd, const u8 *buf, size_t len)
++{
++      struct sta_info *sta;
++
++      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);
++      }
++}
++
++
 +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;
-               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)) {
++      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;
 +
-                       if (sta_has_ip6addr(sta, saddr))
++              /*
++               * IPv6 header may not be 32-bit aligned in the buffer, so use
++               * a local copy to avoid unaligned reads.
++               */
++              os_memcpy(&saddr, &msg->ipv6h.ip6_src, sizeof(saddr));
++              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 (inet_ntop(AF_INET6, saddr, addrtxt, sizeof(addrtxt))
-                           == NULL)
++                      if (sta_has_ip6addr(sta, &saddr))
 +                              return;
 +
-                       hostapd_drv_br_delete_ip_neigh(hapd, 6, (u8 *) saddr);
-                       res = hostapd_drv_br_add_ip_neigh(hapd, 6, (u8 *) saddr,
++                      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));
-                       if (sta_ip6addr_add(sta, saddr))
++                      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 (!hapd->conf->disable_dgaf)
-                       return;
-               /* fall through */
++                      if (sta_ip6addr_add(sta, &saddr))
 +                              return;
 +              }
 +              break;
 +      case ROUTER_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);
-               }
++              if (hapd->conf->disable_dgaf)
++                      ucast_to_stas(hapd, buf, len);
++              break;
 +      case NEIGHBOR_ADVERTISEMENT:
++              if (hapd->conf->na_mcast_to_ucast)
++                      ucast_to_stas(hapd, 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);
 +}
index 7e75e1a61e1ae3f7e87657e7521a24a5abf226e5,0000000000000000000000000000000000000000..d64307ccfd0850aa09bd97ec3854f784630ab1d8
mode 100644,000000..100644
--- /dev/null
@@@ -1,1138 -1,0 +1,1169 @@@
-       sta->ssid = &hapd->conf->ssid;
 +/*
 + * hostapd / Station table
 + * 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.
 + */
 +
 +#include "utils/includes.h"
 +
 +#include "utils/common.h"
 +#include "utils/eloop.h"
 +#include "common/ieee802_11_defs.h"
 +#include "common/wpa_ctrl.h"
 +#include "common/sae.h"
 +#include "radius/radius.h"
 +#include "radius/radius_client.h"
 +#include "p2p/p2p.h"
++#include "fst/fst.h"
 +#include "hostapd.h"
 +#include "accounting.h"
 +#include "ieee802_1x.h"
 +#include "ieee802_11.h"
 +#include "ieee802_11_auth.h"
 +#include "wpa_auth.h"
 +#include "preauth_auth.h"
 +#include "ap_config.h"
 +#include "beacon.h"
 +#include "ap_mlme.h"
 +#include "vlan_init.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
 +static void ap_sa_query_timer(void *eloop_ctx, void *timeout_ctx);
 +#endif /* CONFIG_IEEE80211W */
 +static int ap_sta_remove(struct hostapd_data *hapd, struct sta_info *sta);
 +
 +int ap_for_each_sta(struct hostapd_data *hapd,
 +                  int (*cb)(struct hostapd_data *hapd, struct sta_info *sta,
 +                            void *ctx),
 +                  void *ctx)
 +{
 +      struct sta_info *sta;
 +
 +      for (sta = hapd->sta_list; sta; sta = sta->next) {
 +              if (cb(hapd, sta, ctx))
 +                      return 1;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +struct sta_info * ap_get_sta(struct hostapd_data *hapd, const u8 *sta)
 +{
 +      struct sta_info *s;
 +
 +      s = hapd->sta_hash[STA_HASH(sta)];
 +      while (s != NULL && os_memcmp(s->addr, sta, 6) != 0)
 +              s = s->hnext;
 +      return s;
 +}
 +
 +
 +#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;
 +
 +      if (hapd->sta_list == sta) {
 +              hapd->sta_list = sta->next;
 +              return;
 +      }
 +
 +      tmp = hapd->sta_list;
 +      while (tmp != NULL && tmp->next != sta)
 +              tmp = tmp->next;
 +      if (tmp == NULL) {
 +              wpa_printf(MSG_DEBUG, "Could not remove STA " MACSTR " from "
 +                         "list.", MAC2STR(sta->addr));
 +      } else
 +              tmp->next = sta->next;
 +}
 +
 +
 +void ap_sta_hash_add(struct hostapd_data *hapd, struct sta_info *sta)
 +{
 +      sta->hnext = hapd->sta_hash[STA_HASH(sta->addr)];
 +      hapd->sta_hash[STA_HASH(sta->addr)] = sta;
 +}
 +
 +
 +static void ap_sta_hash_del(struct hostapd_data *hapd, struct sta_info *sta)
 +{
 +      struct sta_info *s;
 +
 +      s = hapd->sta_hash[STA_HASH(sta->addr)];
 +      if (s == NULL) return;
 +      if (os_memcmp(s->addr, sta->addr, 6) == 0) {
 +              hapd->sta_hash[STA_HASH(sta->addr)] = s->hnext;
 +              return;
 +      }
 +
 +      while (s->hnext != NULL &&
 +             os_memcmp(s->hnext->addr, sta->addr, ETH_ALEN) != 0)
 +              s = s->hnext;
 +      if (s->hnext != NULL)
 +              s->hnext = s->hnext->hnext;
 +      else
 +              wpa_printf(MSG_DEBUG, "AP: could not remove STA " MACSTR
 +                         " from hash table", MAC2STR(sta->addr));
 +}
 +
 +
 +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;
 +
 +      accounting_sta_stop(hapd, sta);
 +
 +      /* just in case */
 +      ap_sta_set_authorized(hapd, sta, 0);
 +
 +      if (sta->flags & WLAN_STA_WDS)
 +              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 (!hapd->iface->driver_ap_teardown &&
 +          !(sta->flags & WLAN_STA_PREAUTH))
 +              hostapd_drv_sta_remove(hapd, sta->addr);
 +
++#ifndef CONFIG_NO_VLAN
++      if (sta->vlan_id_bound) {
++              /*
++               * Need to remove the STA entry before potentially removing the
++               * VLAN.
++               */
++              if (hapd->iface->driver_ap_teardown &&
++                  !(sta->flags & WLAN_STA_PREAUTH))
++                      hostapd_drv_sta_remove(hapd, sta->addr);
++              vlan_remove_dynamic(hapd, sta->vlan_id_bound);
++      }
++#endif /* CONFIG_NO_VLAN */
++
 +      ap_sta_hash_del(hapd, sta);
 +      ap_sta_list_del(hapd, sta);
 +
 +      if (sta->aid > 0)
 +              hapd->sta_aid[(sta->aid - 1) / 32] &=
 +                      ~BIT((sta->aid - 1) % 32);
 +
 +      hapd->num_sta--;
 +      if (sta->nonerp_set) {
 +              sta->nonerp_set = 0;
 +              hapd->iface->num_sta_non_erp--;
 +              if (hapd->iface->num_sta_non_erp == 0)
 +                      set_beacon++;
 +      }
 +
 +      if (sta->no_short_slot_time_set) {
 +              sta->no_short_slot_time_set = 0;
 +              hapd->iface->num_sta_no_short_slot_time--;
 +              if (hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G
 +                  && hapd->iface->num_sta_no_short_slot_time == 0)
 +                      set_beacon++;
 +      }
 +
 +      if (sta->no_short_preamble_set) {
 +              sta->no_short_preamble_set = 0;
 +              hapd->iface->num_sta_no_short_preamble--;
 +              if (hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G
 +                  && hapd->iface->num_sta_no_short_preamble == 0)
 +                      set_beacon++;
 +      }
 +
 +      if (sta->no_ht_gf_set) {
 +              sta->no_ht_gf_set = 0;
 +              hapd->iface->num_sta_ht_no_gf--;
 +      }
 +
 +      if (sta->no_ht_set) {
 +              sta->no_ht_set = 0;
 +              hapd->iface->num_sta_no_ht--;
 +      }
 +
 +      if (sta->ht_20mhz_set) {
 +              sta->ht_20mhz_set = 0;
 +              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;
 +              hapd->num_sta_no_p2p--;
 +              if (hapd->num_sta_no_p2p == 0)
 +                      hostapd_p2p_non_p2p_sta_disconnected(hapd);
 +      }
 +#endif /* CONFIG_P2P */
 +
 +#if defined(NEED_AP_MLME) && defined(CONFIG_IEEE80211N)
 +      if (hostapd_ht_operation_update(hapd->iface) > 0)
 +              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);
 +
 +      wpa_printf(MSG_DEBUG, "%s: cancel ap_handle_timer for " MACSTR,
 +                 __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
 +      if (hapd->radius)
 +              radius_client_flush_auth(hapd->radius, sta->addr);
 +#endif /* CONFIG_NO_RADIUS */
 +
 +      os_free(sta->challenge);
 +
 +#ifdef CONFIG_IEEE80211W
 +      os_free(sta->sa_query_trans_id);
 +      eloop_cancel_timeout(ap_sa_query_timer, hapd, sta);
 +#endif /* CONFIG_IEEE80211W */
 +
 +#ifdef CONFIG_P2P
 +      p2p_group_notif_disassoc(hapd->p2p_group, sta->addr);
 +#endif /* CONFIG_P2P */
 +
 +#ifdef CONFIG_INTERWORKING
 +      if (sta->gas_dialog) {
 +              int i;
 +              for (i = 0; i < GAS_DIALOG_MAX; i++)
 +                      gas_serv_dialog_clear(&sta->gas_dialog[i]);
 +              os_free(sta->gas_dialog);
 +      }
 +#endif /* CONFIG_INTERWORKING */
 +
 +      wpabuf_free(sta->wps_ie);
 +      wpabuf_free(sta->p2p_ie);
 +      wpabuf_free(sta->hs20_ie);
++#ifdef CONFIG_FST
++      wpabuf_free(sta->mb_ies);
++#endif /* CONFIG_FST */
 +
 +      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);
 +      os_free(sta->sae);
 +#endif /* CONFIG_SAE */
 +
 +      os_free(sta);
 +}
 +
 +
 +void hostapd_free_stas(struct hostapd_data *hapd)
 +{
 +      struct sta_info *sta, *prev;
 +
 +      sta = hapd->sta_list;
 +
 +      while (sta) {
 +              prev = sta;
 +              if (sta->flags & WLAN_STA_AUTH) {
 +                      mlme_deauthenticate_indication(
 +                              hapd, sta, WLAN_REASON_UNSPECIFIED);
 +              }
 +              sta = sta->next;
 +              wpa_printf(MSG_DEBUG, "Removing station " MACSTR,
 +                         MAC2STR(prev->addr));
 +              ap_free_sta(hapd, prev);
 +      }
 +}
 +
 +
 +/**
 + * ap_handle_timer - Per STA timer handler
 + * @eloop_ctx: struct hostapd_data *
 + * @timeout_ctx: struct sta_info *
 + *
 + * This function is called to check station activity and to remove inactive
 + * stations.
 + */
 +void ap_handle_timer(void *eloop_ctx, void *timeout_ctx)
 +{
 +      struct hostapd_data *hapd = eloop_ctx;
 +      struct sta_info *sta = timeout_ctx;
 +      unsigned long next_time = 0;
 +      int reason;
 +
 +      wpa_printf(MSG_DEBUG, "%s: " MACSTR " flags=0x%x timeout_next=%d",
 +                 __func__, MAC2STR(sta->addr), sta->flags,
 +                 sta->timeout_next);
 +      if (sta->timeout_next == STA_REMOVE) {
 +              hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
 +                             HOSTAPD_LEVEL_INFO, "deauthenticated due to "
 +                             "local deauth request");
 +              ap_free_sta(hapd, sta);
 +              return;
 +      }
 +
 +      if ((sta->flags & WLAN_STA_ASSOC) &&
 +          (sta->timeout_next == STA_NULLFUNC ||
 +           sta->timeout_next == STA_DISASSOC)) {
 +              int inactive_sec;
 +              /*
 +               * Add random value to timeout so that we don't end up bouncing
 +               * all stations at the same time if we have lots of associated
 +               * stations that are idle (but keep re-associating).
 +               */
 +              int fuzz = os_random() % 20;
 +              inactive_sec = hostapd_drv_get_inact_sec(hapd, sta->addr);
 +              if (inactive_sec == -1) {
 +                      wpa_msg(hapd->msg_ctx, MSG_DEBUG,
 +                              "Check inactivity: Could not "
 +                              "get station info from kernel driver for "
 +                              MACSTR, MAC2STR(sta->addr));
 +                      /*
 +                       * The driver may not support this functionality.
 +                       * Anyway, try again after the next inactivity timeout,
 +                       * but do not disconnect the station now.
 +                       */
 +                      next_time = hapd->conf->ap_max_inactivity + fuzz;
 +              } 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",
 +                              MAC2STR(sta->addr), inactive_sec);
 +                      sta->timeout_next = STA_NULLFUNC;
 +                      next_time = hapd->conf->ap_max_inactivity + fuzz -
 +                              inactive_sec;
 +              } else {
 +                      wpa_msg(hapd->msg_ctx, MSG_DEBUG,
 +                              "Station " MACSTR " has been "
 +                              "inactive too long: %d sec, max allowed: %d",
 +                              MAC2STR(sta->addr), inactive_sec,
 +                              hapd->conf->ap_max_inactivity);
 +
 +                      if (hapd->conf->skip_inactivity_poll)
 +                              sta->timeout_next = STA_DISASSOC;
 +              }
 +      }
 +
 +      if ((sta->flags & WLAN_STA_ASSOC) &&
 +          sta->timeout_next == STA_DISASSOC &&
 +          !(sta->flags & WLAN_STA_PENDING_POLL) &&
 +          !hapd->conf->skip_inactivity_poll) {
 +              wpa_msg(hapd->msg_ctx, MSG_DEBUG, "Station " MACSTR
 +                      " has ACKed data poll", MAC2STR(sta->addr));
 +              /* data nullfunc frame poll did not produce TX errors; assume
 +               * station ACKed it */
 +              sta->timeout_next = STA_NULLFUNC;
 +              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)",
 +                         __func__, MAC2STR(sta->addr), next_time);
 +              eloop_register_timeout(next_time, 0, ap_handle_timer, hapd,
 +                                     sta);
 +              return;
 +      }
 +
 +      if (sta->timeout_next == STA_NULLFUNC &&
 +          (sta->flags & WLAN_STA_ASSOC)) {
 +              wpa_printf(MSG_DEBUG, "  Polling STA");
 +              sta->flags |= WLAN_STA_PENDING_POLL;
 +              hostapd_drv_poll_client(hapd, hapd->own_addr, sta->addr,
 +                                      sta->flags & WLAN_STA_WMM);
 +      } else if (sta->timeout_next != STA_REMOVE) {
 +              int deauth = sta->timeout_next == STA_DEAUTH;
 +
 +              wpa_dbg(hapd->msg_ctx, MSG_DEBUG,
 +                      "Timeout, sending %s info to STA " MACSTR,
 +                      deauth ? "deauthentication" : "disassociation",
 +                      MAC2STR(sta->addr));
 +
 +              if (deauth) {
 +                      hostapd_drv_sta_deauth(
 +                              hapd, sta->addr,
 +                              WLAN_REASON_PREV_AUTH_NOT_VALID);
 +              } else {
 +                      reason = (sta->timeout_next == STA_DISASSOC) ?
 +                              WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY :
 +                              WLAN_REASON_PREV_AUTH_NOT_VALID;
 +
 +                      hostapd_drv_sta_disassoc(hapd, sta->addr, reason);
 +              }
 +      }
 +
 +      switch (sta->timeout_next) {
 +      case STA_NULLFUNC:
 +              sta->timeout_next = STA_DISASSOC;
 +              wpa_printf(MSG_DEBUG, "%s: register ap_handle_timer timeout "
 +                         "for " MACSTR " (%d seconds - AP_DISASSOC_DELAY)",
 +                         __func__, MAC2STR(sta->addr), AP_DISASSOC_DELAY);
 +              eloop_register_timeout(AP_DISASSOC_DELAY, 0, ap_handle_timer,
 +                                     hapd, sta);
 +              break;
 +      case STA_DISASSOC:
 +      case STA_DISASSOC_FROM_CLI:
 +              ap_sta_set_authorized(hapd, sta, 0);
 +              sta->flags &= ~WLAN_STA_ASSOC;
 +              ieee802_1x_notify_port_enabled(sta->eapol_sm, 0);
 +              if (!sta->acct_terminate_cause)
 +                      sta->acct_terminate_cause =
 +                              RADIUS_ACCT_TERMINATE_CAUSE_IDLE_TIMEOUT;
 +              accounting_sta_stop(hapd, sta);
 +              ieee802_1x_free_station(sta);
 +              hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
 +                             HOSTAPD_LEVEL_INFO, "disassociated due to "
 +                             "inactivity");
 +              reason = (sta->timeout_next == STA_DISASSOC) ?
 +                      WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY :
 +                      WLAN_REASON_PREV_AUTH_NOT_VALID;
 +              sta->timeout_next = STA_DEAUTH;
 +              wpa_printf(MSG_DEBUG, "%s: register ap_handle_timer timeout "
 +                         "for " MACSTR " (%d seconds - AP_DEAUTH_DELAY)",
 +                         __func__, MAC2STR(sta->addr), AP_DEAUTH_DELAY);
 +              eloop_register_timeout(AP_DEAUTH_DELAY, 0, ap_handle_timer,
 +                                     hapd, sta);
 +              mlme_disassociate_indication(hapd, sta, reason);
 +              break;
 +      case STA_DEAUTH:
 +      case STA_REMOVE:
 +              hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
 +                             HOSTAPD_LEVEL_INFO, "deauthenticated due to "
 +                             "inactivity (timer DEAUTH/REMOVE)");
 +              if (!sta->acct_terminate_cause)
 +                      sta->acct_terminate_cause =
 +                              RADIUS_ACCT_TERMINATE_CAUSE_IDLE_TIMEOUT;
 +              mlme_deauthenticate_indication(
 +                      hapd, sta,
 +                      WLAN_REASON_PREV_AUTH_NOT_VALID);
 +              ap_free_sta(hapd, sta);
 +              break;
 +      }
 +}
 +
 +
 +static void ap_handle_session_timer(void *eloop_ctx, void *timeout_ctx)
 +{
 +      struct hostapd_data *hapd = eloop_ctx;
 +      struct sta_info *sta = timeout_ctx;
 +
 +      if (!(sta->flags & WLAN_STA_AUTH)) {
 +              if (sta->flags & WLAN_STA_GAS) {
 +                      wpa_printf(MSG_DEBUG, "GAS: Remove temporary STA "
 +                                 "entry " MACSTR, MAC2STR(sta->addr));
 +                      ap_free_sta(hapd, sta);
 +              }
 +              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,
 +                     HOSTAPD_LEVEL_INFO, "deauthenticated due to "
 +                     "session timeout");
 +      sta->acct_terminate_cause =
 +              RADIUS_ACCT_TERMINATE_CAUSE_SESSION_TIMEOUT;
 +      ap_free_sta(hapd, sta);
 +}
 +
 +
 +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);
 +      }
 +}
 +
 +
 +void ap_sta_session_timeout(struct hostapd_data *hapd, struct sta_info *sta,
 +                          u32 session_timeout)
 +{
 +      hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
 +                     HOSTAPD_LEVEL_DEBUG, "setting session timeout to %d "
 +                     "seconds", session_timeout);
 +      eloop_cancel_timeout(ap_handle_session_timer, hapd, sta);
 +      eloop_register_timeout(session_timeout, 0, ap_handle_session_timer,
 +                             hapd, sta);
 +}
 +
 +
 +void ap_sta_no_session_timeout(struct hostapd_data *hapd, struct sta_info *sta)
 +{
 +      eloop_cancel_timeout(ap_handle_session_timer, hapd, 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;
 +
 +      sta = ap_get_sta(hapd, addr);
 +      if (sta)
 +              return sta;
 +
 +      wpa_printf(MSG_DEBUG, "  New STA");
 +      if (hapd->num_sta >= hapd->conf->max_num_sta) {
 +              /* FIX: might try to remove some old STAs first? */
 +              wpa_printf(MSG_DEBUG, "no more room for new STAs (%d/%d)",
 +                         hapd->num_sta, hapd->conf->max_num_sta);
 +              return NULL;
 +      }
 +
 +      sta = os_zalloc(sizeof(struct sta_info));
 +      if (sta == NULL) {
 +              wpa_printf(MSG_ERROR, "malloc failed");
 +              return NULL;
 +      }
 +      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 */
 +      os_memcpy(sta->addr, addr, ETH_ALEN);
 +      sta->next = hapd->sta_list;
 +      hapd->sta_list = sta;
 +      hapd->num_sta++;
 +      ap_sta_hash_add(hapd, sta);
- int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta,
-                    int old_vlanid)
 +      ap_sta_remove_in_other_bss(hapd, sta);
 +      sta->last_seq_ctrl = WLAN_INVALID_MGMT_SEQ;
 +      dl_list_init(&sta->ip6addr);
 +
 +      return sta;
 +}
 +
 +
 +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) &&
 +          sta->flags & WLAN_STA_ASSOC) {
 +              wpa_printf(MSG_DEBUG, "Could not remove station " MACSTR
 +                         " from kernel driver.", MAC2STR(sta->addr));
 +              return -1;
 +      }
 +      return 0;
 +}
 +
 +
 +static void ap_sta_remove_in_other_bss(struct hostapd_data *hapd,
 +                                     struct sta_info *sta)
 +{
 +      struct hostapd_iface *iface = hapd->iface;
 +      size_t i;
 +
 +      for (i = 0; i < iface->num_bss; i++) {
 +              struct hostapd_data *bss = iface->bss[i];
 +              struct sta_info *sta2;
 +              /* bss should always be set during operation, but it may be
 +               * NULL during reconfiguration. Assume the STA is not
 +               * associated to another BSS in that case to avoid NULL pointer
 +               * dereferences. */
 +              if (bss == hapd || bss == NULL)
 +                      continue;
 +              sta2 = ap_get_sta(bss, sta->addr);
 +              if (!sta2)
 +                      continue;
 +
 +              ap_sta_disconnect(bss, sta2, sta2->addr,
 +                                WLAN_REASON_PREV_AUTH_NOT_VALID);
 +      }
 +}
 +
 +
 +static void ap_sta_disassoc_cb_timeout(void *eloop_ctx, void *timeout_ctx)
 +{
 +      struct hostapd_data *hapd = eloop_ctx;
 +      struct sta_info *sta = timeout_ctx;
 +
 +      ap_sta_remove(hapd, sta);
 +      mlme_disassociate_indication(hapd, sta, sta->disassoc_reason);
 +}
 +
 +
 +void ap_sta_disassociate(struct hostapd_data *hapd, struct sta_info *sta,
 +                       u16 reason)
 +{
 +      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;
 +      wpa_printf(MSG_DEBUG, "%s: reschedule ap_handle_timer timeout "
 +                 "for " MACSTR " (%d seconds - "
 +                 "AP_MAX_INACTIVITY_AFTER_DISASSOC)",
 +                 __func__, MAC2STR(sta->addr),
 +                 AP_MAX_INACTIVITY_AFTER_DISASSOC);
 +      eloop_cancel_timeout(ap_handle_timer, hapd, sta);
 +      eloop_register_timeout(AP_MAX_INACTIVITY_AFTER_DISASSOC, 0,
 +                             ap_handle_timer, hapd, sta);
 +      accounting_sta_stop(hapd, sta);
 +      ieee802_1x_free_station(sta);
 +
 +      sta->disassoc_reason = reason;
 +      sta->flags |= WLAN_STA_PENDING_DISASSOC_CB;
 +      eloop_cancel_timeout(ap_sta_disassoc_cb_timeout, hapd, sta);
 +      eloop_register_timeout(hapd->iface->drv_flags &
 +                             WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS ? 2 : 0, 0,
 +                             ap_sta_disassoc_cb_timeout, hapd, sta);
 +}
 +
 +
 +static void ap_sta_deauth_cb_timeout(void *eloop_ctx, void *timeout_ctx)
 +{
 +      struct hostapd_data *hapd = eloop_ctx;
 +      struct sta_info *sta = timeout_ctx;
 +
 +      ap_sta_remove(hapd, sta);
 +      mlme_deauthenticate_indication(hapd, sta, sta->deauth_reason);
 +}
 +
 +
 +void ap_sta_deauthenticate(struct hostapd_data *hapd, struct sta_info *sta,
 +                         u16 reason)
 +{
 +      wpa_printf(MSG_DEBUG, "%s: deauthenticate STA " MACSTR,
 +                 hapd->conf->iface, MAC2STR(sta->addr));
 +      sta->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 "
 +                 "for " MACSTR " (%d seconds - "
 +                 "AP_MAX_INACTIVITY_AFTER_DEAUTH)",
 +                 __func__, MAC2STR(sta->addr),
 +                 AP_MAX_INACTIVITY_AFTER_DEAUTH);
 +      eloop_cancel_timeout(ap_handle_timer, hapd, sta);
 +      eloop_register_timeout(AP_MAX_INACTIVITY_AFTER_DEAUTH, 0,
 +                             ap_handle_timer, hapd, sta);
 +      accounting_sta_stop(hapd, sta);
 +      ieee802_1x_free_station(sta);
 +
 +      sta->deauth_reason = reason;
 +      sta->flags |= WLAN_STA_PENDING_DEAUTH_CB;
 +      eloop_cancel_timeout(ap_sta_deauth_cb_timeout, hapd, sta);
 +      eloop_register_timeout(hapd->iface->drv_flags &
 +                             WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS ? 2 : 0, 0,
 +                             ap_sta_deauth_cb_timeout, hapd, sta);
 +}
 +
 +
 +#ifdef CONFIG_WPS
 +int ap_sta_wps_cancel(struct hostapd_data *hapd,
 +                    struct sta_info *sta, void *ctx)
 +{
 +      if (sta && (sta->flags & WLAN_STA_WPS)) {
 +              ap_sta_deauthenticate(hapd, sta,
 +                                    WLAN_REASON_PREV_AUTH_NOT_VALID);
 +              wpa_printf(MSG_DEBUG, "WPS: %s: Deauth sta=" MACSTR,
 +                         __func__, MAC2STR(sta->addr));
 +              return 1;
 +      }
 +
 +      return 0;
 +}
 +#endif /* CONFIG_WPS */
 +
 +
-       /*
-        * Do not proceed furthur if the vlan id remains same. We do not want
-        * duplicate dynamic vlan entries.
-        */
-       if (sta->vlan_id == old_vlanid)
-               return 0;
++int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta)
 +{
 +#ifndef CONFIG_NO_VLAN
 +      const char *iface;
 +      struct hostapd_vlan *vlan = NULL;
 +      int ret;
-       if (sta->ssid->vlan[0])
-               iface = sta->ssid->vlan;
++      int old_vlanid = sta->vlan_id_bound;
 +
 +      iface = hapd->conf->iface;
-       if (sta->ssid->dynamic_vlan == DYNAMIC_VLAN_DISABLED)
++      if (hapd->conf->ssid.vlan[0])
++              iface = hapd->conf->ssid.vlan;
 +
-               if (vlan_setup_encryption_dyn(hapd, sta->ssid, iface) != 0) {
++      if (hapd->conf->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)
 +                              break;
 +                      if (vlan->vlan_id == VLAN_ID_WILDCARD)
 +                              wildcard_vlan = vlan;
 +                      vlan = vlan->next;
 +              }
 +              if (!vlan)
 +                      vlan = wildcard_vlan;
 +              if (vlan)
 +                      iface = vlan->ifname;
 +      }
 +
++      /*
++       * Do not increment ref counters if the VLAN ID remains same, but do
++       * not skip hostapd_drv_set_sta_vlan() as hostapd_drv_sta_remove() might
++       * have been called before.
++       */
++      if (sta->vlan_id == old_vlanid)
++              goto skip_counting;
++
 +      if (sta->vlan_id > 0 && vlan == NULL) {
 +              hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
 +                             HOSTAPD_LEVEL_DEBUG, "could not find VLAN for "
 +                             "binding station to (vlan_id=%d)",
 +                             sta->vlan_id);
 +              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) {
 +                      hostapd_logger(hapd, sta->addr,
 +                                     HOSTAPD_MODULE_IEEE80211,
 +                                     HOSTAPD_LEVEL_DEBUG, "could not add "
 +                                     "dynamic VLAN interface for vlan_id=%d",
 +                                     sta->vlan_id);
 +                      ret = -1;
 +                      goto done;
 +              }
 +
 +              iface = vlan->ifname;
-               if (sta->vlan_id > 0) {
++              if (vlan_setup_encryption_dyn(hapd, iface) != 0) {
 +                      hostapd_logger(hapd, sta->addr,
 +                                     HOSTAPD_MODULE_IEEE80211,
 +                                     HOSTAPD_LEVEL_DEBUG, "could not "
 +                                     "configure encryption for dynamic VLAN "
 +                                     "interface for vlan_id=%d",
 +                                     sta->vlan_id);
 +              }
 +
 +              hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
 +                             HOSTAPD_LEVEL_DEBUG, "added new dynamic VLAN "
 +                             "interface '%s'", iface);
 +      } else if (vlan && vlan->vlan_id == sta->vlan_id) {
-               if (vlan_setup_encryption_dyn(hapd, sta->ssid, iface) != 0) {
++              if (vlan->dynamic_vlan > 0) {
 +                      vlan->dynamic_vlan++;
 +                      hostapd_logger(hapd, sta->addr,
 +                                     HOSTAPD_MODULE_IEEE80211,
 +                                     HOSTAPD_LEVEL_DEBUG, "updated existing "
 +                                     "dynamic VLAN interface '%s'", iface);
 +              }
 +
 +              /*
 +               * Update encryption configuration for statically generated
 +               * VLAN interface. This is only used for static WEP
 +               * configuration for the case where hostapd did not yet know
 +               * which keys are to be used when the interface was added.
 +               */
- done:
++              if (vlan_setup_encryption_dyn(hapd, iface) != 0) {
 +                      hostapd_logger(hapd, sta->addr,
 +                                     HOSTAPD_MODULE_IEEE80211,
 +                                     HOSTAPD_LEVEL_DEBUG, "could not "
 +                                     "configure encryption for VLAN "
 +                                     "interface for vlan_id=%d",
 +                                     sta->vlan_id);
 +              }
 +      }
 +
++      /* ref counters have been increased, so mark the station */
++      sta->vlan_id_bound = sta->vlan_id;
++
++skip_counting:
 +      hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
 +                     HOSTAPD_LEVEL_DEBUG, "binding station to interface "
 +                     "'%s'", iface);
 +
 +      if (wpa_auth_sta_set_vlan(sta->wpa_sm, sta->vlan_id) < 0)
 +              wpa_printf(MSG_INFO, "Failed to update VLAN-ID for WPA");
 +
 +      ret = hostapd_drv_set_sta_vlan(iface, hapd, sta->addr, sta->vlan_id);
 +      if (ret < 0) {
 +              hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
 +                             HOSTAPD_LEVEL_DEBUG, "could not bind the STA "
 +                             "entry to vlan_id=%d", sta->vlan_id);
 +      }
 +
-       if (old_vlanid > 0)
 +      /* During 1x reauth, if the vlan id changes, then remove the old id. */
++      if (old_vlanid > 0 && old_vlanid != sta->vlan_id)
 +              vlan_remove_dynamic(hapd, old_vlanid);
++done:
 +
 +      return ret;
 +#else /* CONFIG_NO_VLAN */
 +      return 0;
 +#endif /* CONFIG_NO_VLAN */
 +}
 +
 +
 +#ifdef CONFIG_IEEE80211W
 +
 +int ap_check_sa_query_timeout(struct hostapd_data *hapd, struct sta_info *sta)
 +{
 +      u32 tu;
 +      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,
 +                             HOSTAPD_MODULE_IEEE80211,
 +                             HOSTAPD_LEVEL_DEBUG,
 +                             "association SA Query timed out");
 +              sta->sa_query_timed_out = 1;
 +              os_free(sta->sa_query_trans_id);
 +              sta->sa_query_trans_id = NULL;
 +              sta->sa_query_count = 0;
 +              eloop_cancel_timeout(ap_sa_query_timer, hapd, sta);
 +              return 1;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +static void ap_sa_query_timer(void *eloop_ctx, void *timeout_ctx)
 +{
 +      struct hostapd_data *hapd = eloop_ctx;
 +      struct sta_info *sta = timeout_ctx;
 +      unsigned int timeout, sec, usec;
 +      u8 *trans_id, *nbuf;
 +
 +      if (sta->sa_query_count > 0 &&
 +          ap_check_sa_query_timeout(hapd, sta))
 +              return;
 +
 +      nbuf = os_realloc_array(sta->sa_query_trans_id,
 +                              sta->sa_query_count + 1,
 +                              WLAN_SA_QUERY_TR_ID_LEN);
 +      if (nbuf == NULL)
 +              return;
 +      if (sta->sa_query_count == 0) {
 +              /* Starting a new SA Query procedure */
 +              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++;
 +
 +      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;
 +      usec = (timeout % 1000) * 1024;
 +      eloop_register_timeout(sec, usec, ap_sa_query_timer, hapd, sta);
 +
 +      hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
 +                     HOSTAPD_LEVEL_DEBUG,
 +                     "association SA Query attempt %d", sta->sa_query_count);
 +
 +      ieee802_11_send_sa_query_req(hapd, sta->addr, trans_id);
 +}
 +
 +
 +void ap_sta_start_sa_query(struct hostapd_data *hapd, struct sta_info *sta)
 +{
 +      ap_sa_query_timer(hapd, sta);
 +}
 +
 +
 +void ap_sta_stop_sa_query(struct hostapd_data *hapd, struct sta_info *sta)
 +{
 +      eloop_cancel_timeout(ap_sa_query_timer, hapd, sta);
 +      os_free(sta->sa_query_trans_id);
 +      sta->sa_query_trans_id = NULL;
 +      sta->sa_query_count = 0;
 +}
 +
 +#endif /* CONFIG_IEEE80211W */
 +
 +
 +void ap_sta_set_authorized(struct hostapd_data *hapd, struct sta_info *sta,
 +                         int authorized)
 +{
 +      const u8 *dev_addr = NULL;
 +      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 &&
 +                  p2p_parse_dev_addr_in_p2p_ie(sta->p2p_ie, addr) == 0)
 +                      dev_addr = addr;
 +      } else
 +              dev_addr = p2p_group_get_dev_addr(hapd->p2p_group, sta->addr);
 +
 +      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) {
 +              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%s",
 +                                        buf, ip_addr);
 +      } else {
 +              wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_DISCONNECTED "%s", buf);
 +
 +              if (hapd->msg_ctx_parent &&
 +                  hapd->msg_ctx_parent != hapd->msg_ctx)
 +                      wpa_msg_no_global(hapd->msg_ctx_parent, MSG_INFO,
 +                                        AP_STA_DISCONNECTED "%s", buf);
 +      }
++
++#ifdef CONFIG_FST
++      if (hapd->iface->fst) {
++              if (authorized)
++                      fst_notify_peer_connected(hapd->iface->fst, sta->addr);
++              else
++                      fst_notify_peer_disconnected(hapd->iface->fst,
++                                                   sta->addr);
++      }
++#endif /* CONFIG_FST */
 +}
 +
 +
 +void ap_sta_disconnect(struct hostapd_data *hapd, struct sta_info *sta,
 +                     const u8 *addr, u16 reason)
 +{
 +
 +      if (sta == NULL && addr)
 +              sta = ap_get_sta(hapd, addr);
 +
 +      if (addr)
 +              hostapd_drv_sta_deauth(hapd, addr, reason);
 +
 +      if (sta == NULL)
 +              return;
 +      ap_sta_set_authorized(hapd, sta, 0);
 +      wpa_auth_sm_event(sta->wpa_sm, WPA_DEAUTH);
 +      ieee802_1x_notify_port_enabled(sta->eapol_sm, 0);
 +      sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC);
 +      wpa_printf(MSG_DEBUG, "%s: reschedule ap_handle_timer timeout "
 +                 "for " MACSTR " (%d seconds - "
 +                 "AP_MAX_INACTIVITY_AFTER_DEAUTH)",
 +                 __func__, MAC2STR(sta->addr),
 +                 AP_MAX_INACTIVITY_AFTER_DEAUTH);
 +      eloop_cancel_timeout(ap_handle_timer, hapd, sta);
 +      eloop_register_timeout(AP_MAX_INACTIVITY_AFTER_DEAUTH, 0,
 +                             ap_handle_timer, hapd, sta);
 +      sta->timeout_next = STA_REMOVE;
 +
 +      sta->deauth_reason = reason;
 +      sta->flags |= WLAN_STA_PENDING_DEAUTH_CB;
 +      eloop_cancel_timeout(ap_sta_deauth_cb_timeout, hapd, sta);
 +      eloop_register_timeout(hapd->iface->drv_flags &
 +                             WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS ? 2 : 0, 0,
 +                             ap_sta_deauth_cb_timeout, hapd, sta);
 +}
 +
 +
 +void ap_sta_deauth_cb(struct hostapd_data *hapd, struct sta_info *sta)
 +{
 +      if (!(sta->flags & WLAN_STA_PENDING_DEAUTH_CB)) {
 +              wpa_printf(MSG_DEBUG, "Ignore deauth cb for test frame");
 +              return;
 +      }
 +      sta->flags &= ~WLAN_STA_PENDING_DEAUTH_CB;
 +      eloop_cancel_timeout(ap_sta_deauth_cb_timeout, hapd, sta);
 +      ap_sta_deauth_cb_timeout(hapd, sta);
 +}
 +
 +
 +void ap_sta_disassoc_cb(struct hostapd_data *hapd, struct sta_info *sta)
 +{
 +      if (!(sta->flags & WLAN_STA_PENDING_DISASSOC_CB)) {
 +              wpa_printf(MSG_DEBUG, "Ignore disassoc cb for test frame");
 +              return;
 +      }
 +      sta->flags &= ~WLAN_STA_PENDING_DISASSOC_CB;
 +      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 57551ab17d5ddf3da9356ce7a9f4e4ce9a863197,0000000000000000000000000000000000000000..420d64e5793bf86361b7c5804a6a0aa308bf1dde
mode 100644,000000..100644
--- /dev/null
@@@ -1,241 -1,0 +1,241 @@@
-       struct hostapd_ssid *ssid; /* SSID selection based on (Re)AssocReq */
-       struct hostapd_ssid *ssid_probe; /* SSID selection based on ProbeReq */
-       int vlan_id;
 +/*
 + * hostapd / Station table
 + * Copyright (c) 2002-2011, Jouni Malinen <j@w1.fi>
 + *
 + * This software may be distributed under the terms of the BSD license.
 + * See README for more details.
 + */
 +
 +#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_AUTHORIZED BIT(5)
 +#define WLAN_STA_PENDING_POLL BIT(6) /* pending activity poll not ACKed */
 +#define WLAN_STA_SHORT_PREAMBLE BIT(7)
 +#define WLAN_STA_PREAUTH BIT(8)
 +#define WLAN_STA_WMM BIT(9)
 +#define WLAN_STA_MFP BIT(10)
 +#define WLAN_STA_HT BIT(11)
 +#define WLAN_STA_WPS BIT(12)
 +#define WLAN_STA_MAYBE_WPS BIT(13)
 +#define WLAN_STA_WDS BIT(14)
 +#define WLAN_STA_ASSOC_REQ_OK BIT(15)
 +#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)
 +
 +/* Maximum number of supported rates (from both Supported Rates and Extended
 + * Supported Rates IEs). */
 +#define WLAN_SUPP_RATES_MAX 32
 +
 +
 +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;
 +      u16 listen_interval; /* or beacon_int for APs */
 +      u8 supported_rates[WLAN_SUPP_RATES_MAX];
 +      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;
 +
 +      enum {
 +              STA_NULLFUNC = 0, STA_DISASSOC, STA_DEAUTH, STA_REMOVE,
 +              STA_DISASSOC_FROM_CLI
 +      } timeout_next;
 +
 +      u16 deauth_reason;
 +      u16 disassoc_reason;
 +
 +      /* IEEE 802.1X related data */
 +      struct eapol_state_machine *eapol_sm;
 +
 +      u32 acct_session_id_hi;
 +      u32 acct_session_id_lo;
 +      struct os_reltime acct_session_start;
 +      int acct_session_started;
 +      int acct_terminate_cause; /* Acct-Terminate-Cause */
 +      int acct_interim_interval; /* Acct-Interim-Interval */
 +
 +      unsigned long last_rx_bytes;
 +      unsigned long last_tx_bytes;
 +      u32 acct_input_gigawords; /* Acct-Input-Gigawords */
 +      u32 acct_output_gigawords; /* Acct-Output-Gigawords */
 +
 +      u8 *challenge; /* IEEE 802.11 Shared Key Authentication Challenge */
 +
 +      struct wpa_state_machine *wpa_sm;
 +      struct rsn_preauth_interface *preauth_iface;
 +
- int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta,
-                    int old_vlanid);
++      int vlan_id; /* 0: none, >0: VID */
++      int vlan_id_bound; /* updated by ap_sta_bind_vlan() */
 +       /* PSKs from RADIUS authentication server */
 +      struct hostapd_sta_wpa_psk_short *psk;
 +
 +      char *identity; /* User-Name from RADIUS */
 +      char *radius_cui; /* Chargeable-User-Identity from RADIUS */
 +
 +      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;
 +                           * 0 = no SA Query in progress */
 +      int sa_query_timed_out;
 +      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_reltime sa_query_start;
 +#endif /* CONFIG_IEEE80211W */
 +
 +#ifdef CONFIG_INTERWORKING
 +#define GAS_DIALOG_MAX 8 /* Max concurrent dialog number */
 +      struct gas_dialog_info *gas_dialog;
 +      u8 gas_dialog_next;
 +#endif /* CONFIG_INTERWORKING */
 +
 +      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;
++#ifdef CONFIG_FST
++      struct wpabuf *mb_ies; /* MB IEs from (Re)Association Request */
++#endif /* CONFIG_FST */
 +
 +      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;
 +};
 +
 +
 +/* Default value for maximum station inactivity. After AP_MAX_INACTIVITY has
 + * passed since last received frame from the station, a nullfunc data frame is
 + * sent to the station. If this frame is not acknowledged and no other frames
 + * have been received, the station will be disassociated after
 + * AP_DISASSOC_DELAY seconds. Similarly, the station will be deauthenticated
 + * after AP_DEAUTH_DELAY seconds has passed after disassociation. */
 +#define AP_MAX_INACTIVITY (5 * 60)
 +#define AP_DISASSOC_DELAY (1)
 +#define AP_DEAUTH_DELAY (1)
 +/* Number of seconds to keep STA entry with Authenticated flag after it has
 + * been disassociated. */
 +#define AP_MAX_INACTIVITY_AFTER_DISASSOC (1 * 30)
 +/* Number of seconds to keep STA entry after it has been deauthenticated. */
 +#define AP_MAX_INACTIVITY_AFTER_DEAUTH (1 * 5)
 +
 +
 +struct hostapd_data;
 +
 +int ap_for_each_sta(struct hostapd_data *hapd,
 +                  int (*cb)(struct hostapd_data *hapd, struct sta_info *sta,
 +                            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);
 +void ap_sta_deauthenticate(struct hostapd_data *hapd, struct sta_info *sta,
 +                         u16 reason);
 +#ifdef CONFIG_WPS
 +int ap_sta_wps_cancel(struct hostapd_data *hapd,
 +                    struct sta_info *sta, void *ctx);
 +#endif /* CONFIG_WPS */
++int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta);
 +void ap_sta_start_sa_query(struct hostapd_data *hapd, struct sta_info *sta);
 +void ap_sta_stop_sa_query(struct hostapd_data *hapd, struct sta_info *sta);
 +int ap_check_sa_query_timeout(struct hostapd_data *hapd, struct sta_info *sta);
 +void ap_sta_disconnect(struct hostapd_data *hapd, struct sta_info *sta,
 +                     const u8 *addr, u16 reason);
 +
 +void ap_sta_set_authorized(struct hostapd_data *hapd,
 +                         struct sta_info *sta, int authorized);
 +static inline int ap_sta_is_authorized(struct sta_info *sta)
 +{
 +      return sta->flags & WLAN_STA_AUTHORIZED;
 +}
 +
 +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 931968c84b0e8a5082257ed0c8918e6822f6b4aa,0000000000000000000000000000000000000000..fcb371bec28343c90c478f80cb392588c3b923eb
mode 100644,000000..100644
--- /dev/null
@@@ -1,85 -1,0 +1,96 @@@
 +/*
 + * AP mode helper functions
 + * 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 "common.h"
 +#include "common/ieee802_11_defs.h"
++#include "fst/fst.h"
 +#include "sta_info.h"
 +#include "hostapd.h"
 +
 +
 +int hostapd_register_probereq_cb(struct hostapd_data *hapd,
 +                               int (*cb)(void *ctx, const u8 *sa,
 +                                         const u8 *da, const u8 *bssid,
 +                                         const u8 *ie, size_t ie_len,
 +                                         int ssi_signal),
 +                               void *ctx)
 +{
 +      struct hostapd_probereq_cb *n;
 +
 +      n = os_realloc_array(hapd->probereq_cb, hapd->num_probereq_cb + 1,
 +                           sizeof(struct hostapd_probereq_cb));
 +      if (n == NULL)
 +              return -1;
 +
 +      hapd->probereq_cb = n;
 +      n = &hapd->probereq_cb[hapd->num_probereq_cb];
 +      hapd->num_probereq_cb++;
 +
 +      n->cb = cb;
 +      n->ctx = ctx;
 +
 +      return 0;
 +}
 +
 +
 +struct prune_data {
 +      struct hostapd_data *hapd;
 +      const u8 *addr;
 +};
 +
 +static int prune_associations(struct hostapd_iface *iface, void *ctx)
 +{
 +      struct prune_data *data = ctx;
 +      struct sta_info *osta;
 +      struct hostapd_data *ohapd;
 +      size_t j;
 +
 +      for (j = 0; j < iface->num_bss; j++) {
 +              ohapd = iface->bss[j];
 +              if (ohapd == data->hapd)
 +                      continue;
++#ifdef CONFIG_FST
++              /* Don't prune STAs belong to same FST */
++              if (ohapd->iface->fst &&
++                  data->hapd->iface->fst &&
++                  fst_are_ifaces_aggregated(ohapd->iface->fst,
++                                            data->hapd->iface->fst))
++                      continue;
++#endif /* CONFIG_FST */
 +              osta = ap_get_sta(ohapd, data->addr);
 +              if (!osta)
 +                      continue;
 +
++              wpa_printf(MSG_INFO, "%s: Prune association for " MACSTR,
++                         ohapd->conf->iface, MAC2STR(osta->addr));
 +              ap_sta_disassociate(ohapd, osta, WLAN_REASON_UNSPECIFIED);
 +      }
 +
 +      return 0;
 +}
 +
 +/**
 + * hostapd_prune_associations - Remove extraneous associations
 + * @hapd: Pointer to BSS data for the most recent association
 + * @addr: Associated STA address
 + *
 + * This function looks through all radios and BSS's for previous
 + * (stale) associations of STA. If any are found they are removed.
 + */
 +void hostapd_prune_associations(struct hostapd_data *hapd, const u8 *addr)
 +{
 +      struct prune_data data;
 +      data.hapd = hapd;
 +      data.addr = addr;
 +      if (hapd->iface->interfaces &&
 +          hapd->iface->interfaces->for_each_interface)
 +              hapd->iface->interfaces->for_each_interface(
 +                      hapd->iface->interfaces, prune_associations, &data);
 +}
index dc6501997db03c80550ad5fc2fc3dd63ddb9c3fa,0000000000000000000000000000000000000000..fd1c8ddacee62e89eb9c929d7dbeb1d87f2e8654
mode 100644,000000..100644
--- /dev/null
@@@ -1,974 -1,0 +1,1083 @@@
- #include <net/if.h>
- #include <sys/ioctl.h>
- #include <linux/sockios.h>
- #include <linux/if_vlan.h>
- #include <linux/if_bridge.h>
 +/*
 + * hostapd / VLAN initialization
 + * Copyright 2003, Instant802 Networks, Inc.
 + * Copyright 2005-2006, Devicescape Software, Inc.
 + * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
 + *
 + * This software may be distributed under the terms of the BSD license.
 + * See README for more details.
 + */
 +
 +#include "utils/includes.h"
++#ifdef CONFIG_FULL_DYNAMIC_VLAN
++#include <net/if.h>
++#include <sys/ioctl.h>
++#include <linux/sockios.h>
++#include <linux/if_vlan.h>
++#include <linux/if_bridge.h>
++#endif /* CONFIG_FULL_DYNAMIC_VLAN */
 +
 +#include "utils/common.h"
 +#include "hostapd.h"
 +#include "ap_config.h"
 +#include "ap_drv_ops.h"
 +#include "vlan_init.h"
 +#include "vlan_util.h"
 +
 +
 +#ifdef CONFIG_FULL_DYNAMIC_VLAN
 +
-               if (os_strcmp(ifname, vlan->ifname) == 0) {
 +#include "drivers/priv_netlink.h"
 +#include "utils/eloop.h"
 +
 +
 +struct full_dynamic_vlan {
 +      int s; /* socket on which to listen for new/removed interfaces. */
 +};
 +
++#define DVLAN_CLEAN_BR         0x1
++#define DVLAN_CLEAN_VLAN       0x2
++#define DVLAN_CLEAN_VLAN_PORT  0x4
++
++struct dynamic_iface {
++      char ifname[IFNAMSIZ + 1];
++      int usage;
++      int clean;
++      struct dynamic_iface *next;
++};
++
++
++/* Increment ref counter for ifname and add clean flag.
++ * If not in list, add it only if some flags are given.
++ */
++static void dyn_iface_get(struct hostapd_data *hapd, const char *ifname,
++                        int clean)
++{
++      struct dynamic_iface *next, **dynamic_ifaces;
++      struct hapd_interfaces *interfaces;
++
++      interfaces = hapd->iface->interfaces;
++      dynamic_ifaces = &interfaces->vlan_priv;
++
++      for (next = *dynamic_ifaces; next; next = next->next) {
++              if (os_strcmp(ifname, next->ifname) == 0)
++                      break;
++      }
++
++      if (next) {
++              next->usage++;
++              next->clean |= clean;
++              return;
++      }
++
++      if (!clean)
++              return;
++
++      next = os_zalloc(sizeof(*next));
++      if (!next)
++              return;
++      os_strlcpy(next->ifname, ifname, sizeof(next->ifname));
++      next->usage = 1;
++      next->clean = clean;
++      next->next = *dynamic_ifaces;
++      *dynamic_ifaces = next;
++}
++
++
++/* Decrement reference counter for given ifname.
++ * Return clean flag iff reference counter was decreased to zero, else zero
++ */
++static int dyn_iface_put(struct hostapd_data *hapd, const char *ifname)
++{
++      struct dynamic_iface *next, *prev = NULL, **dynamic_ifaces;
++      struct hapd_interfaces *interfaces;
++      int clean;
++
++      interfaces = hapd->iface->interfaces;
++      dynamic_ifaces = &interfaces->vlan_priv;
++
++      for (next = *dynamic_ifaces; next; next = next->next) {
++              if (os_strcmp(ifname, next->ifname) == 0)
++                      break;
++              prev = next;
++      }
++
++      if (!next)
++              return 0;
++
++      next->usage--;
++      if (next->usage)
++              return 0;
++
++      if (prev)
++              prev->next = next->next;
++      else
++              *dynamic_ifaces = next->next;
++      clean = next->clean;
++      os_free(next);
++
++      return clean;
++}
++
 +
 +static int ifconfig_helper(const char *if_name, int up)
 +{
 +      int fd;
 +      struct ifreq ifr;
 +
 +      if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
 +              wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
 +                         "failed: %s", __func__, strerror(errno));
 +              return -1;
 +      }
 +
 +      os_memset(&ifr, 0, sizeof(ifr));
 +      os_strlcpy(ifr.ifr_name, if_name, IFNAMSIZ);
 +
 +      if (ioctl(fd, SIOCGIFFLAGS, &ifr) != 0) {
 +              wpa_printf(MSG_ERROR, "VLAN: %s: ioctl(SIOCGIFFLAGS) failed "
 +                         "for interface %s: %s",
 +                         __func__, if_name, strerror(errno));
 +              close(fd);
 +              return -1;
 +      }
 +
 +      if (up)
 +              ifr.ifr_flags |= IFF_UP;
 +      else
 +              ifr.ifr_flags &= ~IFF_UP;
 +
 +      if (ioctl(fd, SIOCSIFFLAGS, &ifr) != 0) {
 +              wpa_printf(MSG_ERROR, "VLAN: %s: ioctl(SIOCSIFFLAGS) failed "
 +                         "for interface %s (up=%d): %s",
 +                         __func__, if_name, up, strerror(errno));
 +              close(fd);
 +              return -1;
 +      }
 +
 +      close(fd);
 +      return 0;
 +}
 +
 +
 +static int ifconfig_up(const char *if_name)
 +{
 +      wpa_printf(MSG_DEBUG, "VLAN: Set interface %s up", if_name);
 +      return ifconfig_helper(if_name, 1);
 +}
 +
 +
 +static int ifconfig_down(const char *if_name)
 +{
 +      wpa_printf(MSG_DEBUG, "VLAN: Set interface %s down", if_name);
 +      return ifconfig_helper(if_name, 0);
 +}
 +
 +
 +/*
 + * These are only available in recent linux headers (without the leading
 + * underscore).
 + */
 +#define _GET_VLAN_REALDEV_NAME_CMD    8
 +#define _GET_VLAN_VID_CMD             9
 +
 +/* This value should be 256 ONLY. If it is something else, then hostapd
 + * might crash!, as this value has been hard-coded in 2.4.x kernel
 + * bridging code.
 + */
 +#define MAX_BR_PORTS                  256
 +
 +static int br_delif(const char *br_name, const char *if_name)
 +{
 +      int fd;
 +      struct ifreq ifr;
 +      unsigned long args[2];
 +      int if_index;
 +
 +      wpa_printf(MSG_DEBUG, "VLAN: br_delif(%s, %s)", br_name, if_name);
 +      if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
 +              wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
 +                         "failed: %s", __func__, strerror(errno));
 +              return -1;
 +      }
 +
 +      if_index = if_nametoindex(if_name);
 +
 +      if (if_index == 0) {
 +              wpa_printf(MSG_ERROR, "VLAN: %s: Failure determining "
 +                         "interface index for '%s'",
 +                         __func__, if_name);
 +              close(fd);
 +              return -1;
 +      }
 +
 +      args[0] = BRCTL_DEL_IF;
 +      args[1] = if_index;
 +
 +      os_strlcpy(ifr.ifr_name, br_name, sizeof(ifr.ifr_name));
 +      ifr.ifr_data = (__caddr_t) args;
 +
 +      if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0 && errno != EINVAL) {
 +              /* No error if interface already removed. */
 +              wpa_printf(MSG_ERROR, "VLAN: %s: ioctl[SIOCDEVPRIVATE,"
 +                         "BRCTL_DEL_IF] failed for br_name=%s if_name=%s: "
 +                         "%s", __func__, br_name, if_name, strerror(errno));
 +              close(fd);
 +              return -1;
 +      }
 +
 +      close(fd);
 +      return 0;
 +}
 +
 +
 +/*
 +      Add interface 'if_name' to the bridge 'br_name'
 +
 +      returns -1 on error
 +      returns 1 if the interface is already part of the bridge
 +      returns 0 otherwise
 +*/
 +static int br_addif(const char *br_name, const char *if_name)
 +{
 +      int fd;
 +      struct ifreq ifr;
 +      unsigned long args[2];
 +      int if_index;
 +
 +      wpa_printf(MSG_DEBUG, "VLAN: br_addif(%s, %s)", br_name, if_name);
 +      if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
 +              wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
 +                         "failed: %s", __func__, strerror(errno));
 +              return -1;
 +      }
 +
 +      if_index = if_nametoindex(if_name);
 +
 +      if (if_index == 0) {
 +              wpa_printf(MSG_ERROR, "VLAN: %s: Failure determining "
 +                         "interface index for '%s'",
 +                         __func__, if_name);
 +              close(fd);
 +              return -1;
 +      }
 +
 +      args[0] = BRCTL_ADD_IF;
 +      args[1] = if_index;
 +
 +      os_strlcpy(ifr.ifr_name, br_name, sizeof(ifr.ifr_name));
 +      ifr.ifr_data = (__caddr_t) args;
 +
 +      if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0) {
 +              if (errno == EBUSY) {
 +                      /* The interface is already added. */
 +                      close(fd);
 +                      return 1;
 +              }
 +
 +              wpa_printf(MSG_ERROR, "VLAN: %s: ioctl[SIOCDEVPRIVATE,"
 +                         "BRCTL_ADD_IF] failed for br_name=%s if_name=%s: "
 +                         "%s", __func__, br_name, if_name, strerror(errno));
 +              close(fd);
 +              return -1;
 +      }
 +
 +      close(fd);
 +      return 0;
 +}
 +
 +
 +static int br_delbr(const char *br_name)
 +{
 +      int fd;
 +      unsigned long arg[2];
 +
 +      wpa_printf(MSG_DEBUG, "VLAN: br_delbr(%s)", br_name);
 +      if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
 +              wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
 +                         "failed: %s", __func__, strerror(errno));
 +              return -1;
 +      }
 +
 +      arg[0] = BRCTL_DEL_BRIDGE;
 +      arg[1] = (unsigned long) br_name;
 +
 +      if (ioctl(fd, SIOCGIFBR, arg) < 0 && errno != ENXIO) {
 +              /* No error if bridge already removed. */
 +              wpa_printf(MSG_ERROR, "VLAN: %s: BRCTL_DEL_BRIDGE failed for "
 +                         "%s: %s", __func__, br_name, strerror(errno));
 +              close(fd);
 +              return -1;
 +      }
 +
 +      close(fd);
 +      return 0;
 +}
 +
 +
 +/*
 +      Add a bridge with the name 'br_name'.
 +
 +      returns -1 on error
 +      returns 1 if the bridge already exists
 +      returns 0 otherwise
 +*/
 +static int br_addbr(const char *br_name)
 +{
 +      int fd;
 +      unsigned long arg[4];
 +      struct ifreq ifr;
 +
 +      wpa_printf(MSG_DEBUG, "VLAN: br_addbr(%s)", br_name);
 +      if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
 +              wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
 +                         "failed: %s", __func__, strerror(errno));
 +              return -1;
 +      }
 +
 +      arg[0] = BRCTL_ADD_BRIDGE;
 +      arg[1] = (unsigned long) br_name;
 +
 +      if (ioctl(fd, SIOCGIFBR, arg) < 0) {
 +              if (errno == EEXIST) {
 +                      /* The bridge is already added. */
 +                      close(fd);
 +                      return 1;
 +              } else {
 +                      wpa_printf(MSG_ERROR, "VLAN: %s: BRCTL_ADD_BRIDGE "
 +                                 "failed for %s: %s",
 +                                 __func__, br_name, strerror(errno));
 +                      close(fd);
 +                      return -1;
 +              }
 +      }
 +
 +      /* Decrease forwarding delay to avoid EAPOL timeouts. */
 +      os_memset(&ifr, 0, sizeof(ifr));
 +      os_strlcpy(ifr.ifr_name, br_name, IFNAMSIZ);
 +      arg[0] = BRCTL_SET_BRIDGE_FORWARD_DELAY;
 +      arg[1] = 1;
 +      arg[2] = 0;
 +      arg[3] = 0;
 +      ifr.ifr_data = (char *) &arg;
 +      if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0) {
 +              wpa_printf(MSG_ERROR, "VLAN: %s: "
 +                         "BRCTL_SET_BRIDGE_FORWARD_DELAY (1 sec) failed for "
 +                         "%s: %s", __func__, br_name, strerror(errno));
 +              /* Continue anyway */
 +      }
 +
 +      close(fd);
 +      return 0;
 +}
 +
 +
 +static int br_getnumports(const char *br_name)
 +{
 +      int fd;
 +      int i;
 +      int port_cnt = 0;
 +      unsigned long arg[4];
 +      int ifindices[MAX_BR_PORTS];
 +      struct ifreq ifr;
 +
 +      if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
 +              wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
 +                         "failed: %s", __func__, strerror(errno));
 +              return -1;
 +      }
 +
 +      arg[0] = BRCTL_GET_PORT_LIST;
 +      arg[1] = (unsigned long) ifindices;
 +      arg[2] = MAX_BR_PORTS;
 +      arg[3] = 0;
 +
 +      os_memset(ifindices, 0, sizeof(ifindices));
 +      os_strlcpy(ifr.ifr_name, br_name, sizeof(ifr.ifr_name));
 +      ifr.ifr_data = (__caddr_t) arg;
 +
 +      if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0) {
 +              wpa_printf(MSG_ERROR, "VLAN: %s: BRCTL_GET_PORT_LIST "
 +                         "failed for %s: %s",
 +                         __func__, br_name, strerror(errno));
 +              close(fd);
 +              return -1;
 +      }
 +
 +      for (i = 1; i < MAX_BR_PORTS; i++) {
 +              if (ifindices[i] > 0) {
 +                      port_cnt++;
 +              }
 +      }
 +
 +      close(fd);
 +      return port_cnt;
 +}
 +
 +
 +#ifndef CONFIG_VLAN_NETLINK
 +
 +int vlan_rem(const char *if_name)
 +{
 +      int fd;
 +      struct vlan_ioctl_args if_request;
 +
 +      wpa_printf(MSG_DEBUG, "VLAN: vlan_rem(%s)", if_name);
 +      if ((os_strlen(if_name) + 1) > sizeof(if_request.device1)) {
 +              wpa_printf(MSG_ERROR, "VLAN: Interface name too long: '%s'",
 +                         if_name);
 +              return -1;
 +      }
 +
 +      if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
 +              wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
 +                         "failed: %s", __func__, strerror(errno));
 +              return -1;
 +      }
 +
 +      os_memset(&if_request, 0, sizeof(if_request));
 +
 +      os_strlcpy(if_request.device1, if_name, sizeof(if_request.device1));
 +      if_request.cmd = DEL_VLAN_CMD;
 +
 +      if (ioctl(fd, SIOCSIFVLAN, &if_request) < 0) {
 +              wpa_printf(MSG_ERROR, "VLAN: %s: DEL_VLAN_CMD failed for %s: "
 +                         "%s", __func__, if_name, strerror(errno));
 +              close(fd);
 +              return -1;
 +      }
 +
 +      close(fd);
 +      return 0;
 +}
 +
 +
 +/*
 +      Add a vlan interface with VLAN ID 'vid' and tagged interface
 +      'if_name'.
 +
 +      returns -1 on error
 +      returns 1 if the interface already exists
 +      returns 0 otherwise
 +*/
 +int vlan_add(const char *if_name, int vid, const char *vlan_if_name)
 +{
 +      int fd;
 +      struct vlan_ioctl_args if_request;
 +
 +      wpa_printf(MSG_DEBUG, "VLAN: vlan_add(if_name=%s, vid=%d)",
 +                 if_name, vid);
 +      ifconfig_up(if_name);
 +
 +      if ((os_strlen(if_name) + 1) > sizeof(if_request.device1)) {
 +              wpa_printf(MSG_ERROR, "VLAN: Interface name too long: '%s'",
 +                         if_name);
 +              return -1;
 +      }
 +
 +      if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
 +              wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
 +                         "failed: %s", __func__, strerror(errno));
 +              return -1;
 +      }
 +
 +      os_memset(&if_request, 0, sizeof(if_request));
 +
 +      /* Determine if a suitable vlan device already exists. */
 +
 +      os_snprintf(if_request.device1, sizeof(if_request.device1), "vlan%d",
 +                  vid);
 +
 +      if_request.cmd = _GET_VLAN_VID_CMD;
 +
 +      if (ioctl(fd, SIOCSIFVLAN, &if_request) == 0) {
 +
 +              if (if_request.u.VID == vid) {
 +                      if_request.cmd = _GET_VLAN_REALDEV_NAME_CMD;
 +
 +                      if (ioctl(fd, SIOCSIFVLAN, &if_request) == 0 &&
 +                          os_strncmp(if_request.u.device2, if_name,
 +                                     sizeof(if_request.u.device2)) == 0) {
 +                              close(fd);
 +                              wpa_printf(MSG_DEBUG, "VLAN: vlan_add: "
 +                                         "if_name %s exists already",
 +                                         if_request.device1);
 +                              return 1;
 +                      }
 +              }
 +      }
 +
 +      /* A suitable vlan device does not already exist, add one. */
 +
 +      os_memset(&if_request, 0, sizeof(if_request));
 +      os_strlcpy(if_request.device1, if_name, sizeof(if_request.device1));
 +      if_request.u.VID = vid;
 +      if_request.cmd = ADD_VLAN_CMD;
 +
 +      if (ioctl(fd, SIOCSIFVLAN, &if_request) < 0) {
 +              wpa_printf(MSG_ERROR, "VLAN: %s: ADD_VLAN_CMD failed for %s: "
 +                         "%s",
 +                         __func__, if_request.device1, strerror(errno));
 +              close(fd);
 +              return -1;
 +      }
 +
 +      close(fd);
 +      return 0;
 +}
 +
 +
 +static int vlan_set_name_type(unsigned int name_type)
 +{
 +      int fd;
 +      struct vlan_ioctl_args if_request;
 +
 +      wpa_printf(MSG_DEBUG, "VLAN: vlan_set_name_type(name_type=%u)",
 +                 name_type);
 +      if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
 +              wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
 +                         "failed: %s", __func__, strerror(errno));
 +              return -1;
 +      }
 +
 +      os_memset(&if_request, 0, sizeof(if_request));
 +
 +      if_request.u.name_type = name_type;
 +      if_request.cmd = SET_VLAN_NAME_TYPE_CMD;
 +      if (ioctl(fd, SIOCSIFVLAN, &if_request) < 0) {
 +              wpa_printf(MSG_ERROR, "VLAN: %s: SET_VLAN_NAME_TYPE_CMD "
 +                         "name_type=%u failed: %s",
 +                         __func__, name_type, strerror(errno));
 +              close(fd);
 +              return -1;
 +      }
 +
 +      close(fd);
 +      return 0;
 +}
 +
 +#endif /* CONFIG_VLAN_NETLINK */
 +
 +
 +static void vlan_newlink(char *ifname, struct hostapd_data *hapd)
 +{
 +      char vlan_ifname[IFNAMSIZ];
 +      char br_name[IFNAMSIZ];
 +      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 clean;
 +
 +      wpa_printf(MSG_DEBUG, "VLAN: vlan_newlink(%s)", ifname);
 +
 +      while (vlan) {
-                       if (!br_addbr(br_name))
-                               vlan->clean |= DVLAN_CLEAN_BR;
++              if (os_strcmp(ifname, vlan->ifname) == 0 && !vlan->configured) {
++                      vlan->configured = 1;
 +
 +                      if (hapd->conf->vlan_bridge[0]) {
 +                              os_snprintf(br_name, sizeof(br_name), "%s%d",
 +                                          hapd->conf->vlan_bridge,
 +                                          vlan->vlan_id);
 +                      } else if (tagged_interface) {
 +                              os_snprintf(br_name, sizeof(br_name),
 +                                          "br%s.%d", tagged_interface,
 +                                          vlan->vlan_id);
 +                      } else {
 +                              os_snprintf(br_name, sizeof(br_name),
 +                                          "brvlan%d", vlan->vlan_id);
 +                      }
 +
-                                       vlan->clean |= DVLAN_CLEAN_VLAN;
++                      dyn_iface_get(hapd, br_name,
++                                    br_addbr(br_name) ? 0 : DVLAN_CLEAN_BR);
 +
 +                      ifconfig_up(br_name);
 +
 +                      if (tagged_interface) {
 +                              if (vlan_naming ==
 +                                  DYNAMIC_VLAN_NAMING_WITH_DEVICE)
 +                                      os_snprintf(vlan_ifname,
 +                                                  sizeof(vlan_ifname),
 +                                                  "%s.%d", tagged_interface,
 +                                                  vlan->vlan_id);
 +                              else
 +                                      os_snprintf(vlan_ifname,
 +                                                  sizeof(vlan_ifname),
 +                                                  "vlan%d", vlan->vlan_id);
 +
++                              clean = 0;
 +                              ifconfig_up(tagged_interface);
 +                              if (!vlan_add(tagged_interface, vlan->vlan_id,
 +                                            vlan_ifname))
-                                       vlan->clean |= DVLAN_CLEAN_VLAN_PORT;
++                                      clean |= DVLAN_CLEAN_VLAN;
 +
 +                              if (!br_addif(br_name, vlan_ifname))
-               if (os_strcmp(ifname, vlan->ifname) == 0) {
++                                      clean |= DVLAN_CLEAN_VLAN_PORT;
++
++                              dyn_iface_get(hapd, vlan_ifname, clean);
 +
 +                              ifconfig_up(vlan_ifname);
 +                      }
 +
 +                      if (!br_addif(br_name, ifname))
 +                              vlan->clean |= DVLAN_CLEAN_WLAN_PORT;
 +
 +                      ifconfig_up(ifname);
 +
 +                      break;
 +              }
 +              vlan = vlan->next;
 +      }
 +}
 +
 +
 +static void vlan_dellink(char *ifname, struct hostapd_data *hapd)
 +{
 +      char vlan_ifname[IFNAMSIZ];
 +      char br_name[IFNAMSIZ];
 +      struct hostapd_vlan *first, *prev, *vlan = hapd->conf->vlan;
 +      char *tagged_interface = hapd->conf->ssid.vlan_tagged_interface;
 +      int vlan_naming = hapd->conf->ssid.vlan_naming;
++      int clean;
 +
 +      wpa_printf(MSG_DEBUG, "VLAN: vlan_dellink(%s)", ifname);
 +
 +      first = prev = vlan;
 +
 +      while (vlan) {
-                               if (vlan->clean & DVLAN_CLEAN_VLAN_PORT)
++              if (os_strcmp(ifname, vlan->ifname) == 0 &&
++                  vlan->configured) {
 +                      if (hapd->conf->vlan_bridge[0]) {
 +                              os_snprintf(br_name, sizeof(br_name), "%s%d",
 +                                          hapd->conf->vlan_bridge,
 +                                          vlan->vlan_id);
 +                      } else if (tagged_interface) {
 +                              os_snprintf(br_name, sizeof(br_name),
 +                                          "br%s.%d", tagged_interface,
 +                                          vlan->vlan_id);
 +                      } else {
 +                              os_snprintf(br_name, sizeof(br_name),
 +                                          "brvlan%d", vlan->vlan_id);
 +                      }
 +
 +                      if (vlan->clean & DVLAN_CLEAN_WLAN_PORT)
 +                              br_delif(br_name, vlan->ifname);
 +
 +                      if (tagged_interface) {
 +                              if (vlan_naming ==
 +                                  DYNAMIC_VLAN_NAMING_WITH_DEVICE)
 +                                      os_snprintf(vlan_ifname,
 +                                                  sizeof(vlan_ifname),
 +                                                  "%s.%d", tagged_interface,
 +                                                  vlan->vlan_id);
 +                              else
 +                                      os_snprintf(vlan_ifname,
 +                                                  sizeof(vlan_ifname),
 +                                                  "vlan%d", vlan->vlan_id);
-                               ifconfig_down(vlan_ifname);
++
++                              clean = dyn_iface_put(hapd, vlan_ifname);
++
++                              if (clean & DVLAN_CLEAN_VLAN_PORT)
 +                                      br_delif(br_name, vlan_ifname);
-                               if (vlan->clean & DVLAN_CLEAN_VLAN)
 +
-                       if ((vlan->clean & DVLAN_CLEAN_BR) &&
++                              if (clean & DVLAN_CLEAN_VLAN) {
++                                      ifconfig_down(vlan_ifname);
 +                                      vlan_rem(vlan_ifname);
++                              }
 +                      }
 +
- int vlan_setup_encryption_dyn(struct hostapd_data *hapd,
-                             struct hostapd_ssid *mssid, const char *dyn_vlan)
++                      clean = dyn_iface_put(hapd, br_name);
++                      if ((clean & DVLAN_CLEAN_BR) &&
 +                          br_getnumports(br_name) == 0) {
 +                              ifconfig_down(br_name);
 +                              br_delbr(br_name);
 +                      }
++              }
 +
++              if (os_strcmp(ifname, vlan->ifname) == 0) {
 +                      if (vlan == first) {
 +                              hapd->conf->vlan = vlan->next;
 +                      } else {
 +                              prev->next = vlan->next;
 +                      }
 +                      os_free(vlan);
 +
 +                      break;
 +              }
 +              prev = vlan;
 +              vlan = vlan->next;
 +      }
 +}
 +
 +
 +static void
 +vlan_read_ifnames(struct nlmsghdr *h, size_t len, int del,
 +                struct hostapd_data *hapd)
 +{
 +      struct ifinfomsg *ifi;
 +      int attrlen, nlmsg_len, rta_len;
 +      struct rtattr *attr;
 +      char ifname[IFNAMSIZ + 1];
 +
 +      if (len < sizeof(*ifi))
 +              return;
 +
 +      ifi = NLMSG_DATA(h);
 +
 +      nlmsg_len = NLMSG_ALIGN(sizeof(struct ifinfomsg));
 +
 +      attrlen = h->nlmsg_len - nlmsg_len;
 +      if (attrlen < 0)
 +              return;
 +
 +      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)) {
 +              if (attr->rta_type == IFLA_IFNAME) {
 +                      int n = attr->rta_len - rta_len;
 +                      if (n < 0)
 +                              break;
 +
 +                      if ((size_t) n >= sizeof(ifname))
 +                              n = sizeof(ifname) - 1;
 +                      os_memcpy(ifname, ((char *) attr) + rta_len, n);
 +
 +              }
 +
 +              attr = RTA_NEXT(attr, attrlen);
 +      }
 +
 +      if (!ifname[0])
 +              return;
++      if (del && if_nametoindex(ifname)) {
++               /* interface still exists, race condition ->
++                * iface has just been recreated */
++              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);
 +}
 +
 +
 +static void vlan_event_receive(int sock, void *eloop_ctx, void *sock_ctx)
 +{
 +      char buf[8192];
 +      int left;
 +      struct sockaddr_nl from;
 +      socklen_t fromlen;
 +      struct nlmsghdr *h;
 +      struct hostapd_data *hapd = eloop_ctx;
 +
 +      fromlen = sizeof(from);
 +      left = recvfrom(sock, buf, sizeof(buf), MSG_DONTWAIT,
 +                      (struct sockaddr *) &from, &fromlen);
 +      if (left < 0) {
 +              if (errno != EINTR && errno != EAGAIN)
 +                      wpa_printf(MSG_ERROR, "VLAN: %s: recvfrom failed: %s",
 +                                 __func__, strerror(errno));
 +              return;
 +      }
 +
 +      h = (struct nlmsghdr *) buf;
 +      while (NLMSG_OK(h, left)) {
 +              int len, plen;
 +
 +              len = h->nlmsg_len;
 +              plen = len - sizeof(*h);
 +              if (len > left || plen < 0) {
 +                      wpa_printf(MSG_DEBUG, "VLAN: Malformed netlink "
 +                                 "message: len=%d left=%d plen=%d",
 +                                 len, left, plen);
 +                      break;
 +              }
 +
 +              switch (h->nlmsg_type) {
 +              case RTM_NEWLINK:
 +                      vlan_read_ifnames(h, plen, 0, hapd);
 +                      break;
 +              case RTM_DELLINK:
 +                      vlan_read_ifnames(h, plen, 1, hapd);
 +                      break;
 +              }
 +
 +              h = NLMSG_NEXT(h, left);
 +      }
 +
 +      if (left > 0) {
 +              wpa_printf(MSG_DEBUG, "VLAN: %s: %d extra bytes in the end of "
 +                         "netlink message", __func__, left);
 +      }
 +}
 +
 +
 +static struct full_dynamic_vlan *
 +full_dynamic_vlan_init(struct hostapd_data *hapd)
 +{
 +      struct sockaddr_nl local;
 +      struct full_dynamic_vlan *priv;
 +
 +      priv = os_zalloc(sizeof(*priv));
 +      if (priv == NULL)
 +              return NULL;
 +
 +#ifndef CONFIG_VLAN_NETLINK
 +      vlan_set_name_type(hapd->conf->ssid.vlan_naming ==
 +                         DYNAMIC_VLAN_NAMING_WITH_DEVICE ?
 +                         VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD :
 +                         VLAN_NAME_TYPE_PLUS_VID_NO_PAD);
 +#endif /* CONFIG_VLAN_NETLINK */
 +
 +      priv->s = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
 +      if (priv->s < 0) {
 +              wpa_printf(MSG_ERROR, "VLAN: %s: socket(PF_NETLINK,SOCK_RAW,"
 +                         "NETLINK_ROUTE) failed: %s",
 +                         __func__, strerror(errno));
 +              os_free(priv);
 +              return NULL;
 +      }
 +
 +      os_memset(&local, 0, sizeof(local));
 +      local.nl_family = AF_NETLINK;
 +      local.nl_groups = RTMGRP_LINK;
 +      if (bind(priv->s, (struct sockaddr *) &local, sizeof(local)) < 0) {
 +              wpa_printf(MSG_ERROR, "VLAN: %s: bind(netlink) failed: %s",
 +                         __func__, strerror(errno));
 +              close(priv->s);
 +              os_free(priv);
 +              return NULL;
 +      }
 +
 +      if (eloop_register_read_sock(priv->s, vlan_event_receive, hapd, NULL))
 +      {
 +              close(priv->s);
 +              os_free(priv);
 +              return NULL;
 +      }
 +
 +      return priv;
 +}
 +
 +
 +static void full_dynamic_vlan_deinit(struct full_dynamic_vlan *priv)
 +{
 +      if (priv == NULL)
 +              return;
 +      eloop_unregister_read_sock(priv->s);
 +      close(priv->s);
 +      os_free(priv);
 +}
 +#endif /* CONFIG_FULL_DYNAMIC_VLAN */
 +
 +
-               if (mssid->wep.key[i] &&
++int vlan_setup_encryption_dyn(struct hostapd_data *hapd, const char *dyn_vlan)
 +{
 +        int i;
 +
 +        if (dyn_vlan == NULL)
 +              return 0;
 +
 +      /* Static WEP keys are set here; IEEE 802.1X and WPA uses their own
 +       * functions for setting up dynamic broadcast keys. */
 +      for (i = 0; i < 4; i++) {
-                                       i == mssid->wep.idx, NULL, 0,
-                                       mssid->wep.key[i], mssid->wep.len[i]))
++              if (hapd->conf->ssid.wep.key[i] &&
 +                  hostapd_drv_set_key(dyn_vlan, hapd, WPA_ALG_WEP, NULL, i,
-       wpa_printf(MSG_DEBUG, "VLAN: %s(vlan_id=%d)", __func__, vlan_id);
++                                      i == hapd->conf->ssid.wep.idx, NULL, 0,
++                                      hapd->conf->ssid.wep.key[i],
++                                      hapd->conf->ssid.wep.len[i]))
 +              {
 +                      wpa_printf(MSG_ERROR, "VLAN: Could not set WEP "
 +                                 "encryption for dynamic VLAN");
 +                      return -1;
 +              }
 +      }
 +
 +      return 0;
 +}
 +
 +
 +static int vlan_dynamic_add(struct hostapd_data *hapd,
 +                          struct hostapd_vlan *vlan)
 +{
 +      while (vlan) {
 +              if (vlan->vlan_id != VLAN_ID_WILDCARD) {
 +                      if (hostapd_vlan_if_add(hapd, vlan->ifname)) {
 +                              if (errno != EEXIST) {
 +                                      wpa_printf(MSG_ERROR, "VLAN: Could "
 +                                                 "not add VLAN %s: %s",
 +                                                 vlan->ifname,
 +                                                 strerror(errno));
 +                                      return -1;
 +                              }
 +                      }
 +#ifdef CONFIG_FULL_DYNAMIC_VLAN
 +                      ifconfig_up(vlan->ifname);
 +#endif /* CONFIG_FULL_DYNAMIC_VLAN */
 +              }
 +
 +              vlan = vlan->next;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +static void vlan_dynamic_remove(struct hostapd_data *hapd,
 +                              struct hostapd_vlan *vlan)
 +{
 +      struct hostapd_vlan *next;
 +
 +      while (vlan) {
 +              next = vlan->next;
 +
 +              if (vlan->vlan_id != VLAN_ID_WILDCARD &&
 +                  hostapd_vlan_if_remove(hapd, vlan->ifname)) {
 +                      wpa_printf(MSG_ERROR, "VLAN: Could not remove VLAN "
 +                                 "iface: %s: %s",
 +                                 vlan->ifname, strerror(errno));
 +              }
 +#ifdef CONFIG_FULL_DYNAMIC_VLAN
 +              if (vlan->clean)
 +                      vlan_dellink(vlan->ifname, hapd);
 +#endif /* CONFIG_FULL_DYNAMIC_VLAN */
 +
 +              vlan = next;
 +      }
 +}
 +
 +
 +int vlan_init(struct hostapd_data *hapd)
 +{
 +#ifdef CONFIG_FULL_DYNAMIC_VLAN
 +      hapd->full_dynamic_vlan = full_dynamic_vlan_init(hapd);
 +#endif /* CONFIG_FULL_DYNAMIC_VLAN */
 +
 +      if (hapd->conf->ssid.dynamic_vlan != DYNAMIC_VLAN_DISABLED &&
 +          !hapd->conf->vlan) {
 +              /* dynamic vlans enabled but no (or empty) vlan_file given */
 +              struct hostapd_vlan *vlan;
 +              vlan = os_zalloc(sizeof(*vlan));
 +              if (vlan == NULL) {
 +                      wpa_printf(MSG_ERROR, "Out of memory while assigning "
 +                                 "VLAN interfaces");
 +                      return -1;
 +              }
 +
 +              vlan->vlan_id = VLAN_ID_WILDCARD;
 +              os_snprintf(vlan->ifname, sizeof(vlan->ifname), "%s.#",
 +                          hapd->conf->iface);
 +              vlan->next = hapd->conf->vlan;
 +              hapd->conf->vlan = vlan;
 +      }
 +
 +      if (vlan_dynamic_add(hapd, hapd->conf->vlan))
 +              return -1;
 +
 +        return 0;
 +}
 +
 +
 +void vlan_deinit(struct hostapd_data *hapd)
 +{
 +      vlan_dynamic_remove(hapd, hapd->conf->vlan);
 +
 +#ifdef CONFIG_FULL_DYNAMIC_VLAN
 +      full_dynamic_vlan_deinit(hapd->full_dynamic_vlan);
 +      hapd->full_dynamic_vlan = NULL;
 +#endif /* CONFIG_FULL_DYNAMIC_VLAN */
 +}
 +
 +
 +struct hostapd_vlan * vlan_add_dynamic(struct hostapd_data *hapd,
 +                                     struct hostapd_vlan *vlan,
 +                                     int vlan_id)
 +{
 +      struct hostapd_vlan *n = NULL;
 +      char *ifname, *pos;
 +
 +      if (vlan == NULL || vlan_id <= 0 || vlan_id > MAX_VLAN_ID ||
 +          vlan->vlan_id != VLAN_ID_WILDCARD)
 +              return NULL;
 +
 +      wpa_printf(MSG_DEBUG, "VLAN: %s(vlan_id=%d ifname=%s)",
 +                 __func__, vlan_id, vlan->ifname);
 +      ifname = os_strdup(vlan->ifname);
 +      if (ifname == NULL)
 +              return NULL;
 +      pos = os_strchr(ifname, '#');
 +      if (pos == NULL)
 +              goto free_ifname;
 +      *pos++ = '\0';
 +
 +      n = os_zalloc(sizeof(*n));
 +      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);
 +
 +      if (hostapd_vlan_if_add(hapd, n->ifname)) {
 +              os_free(n);
 +              n = NULL;
 +              goto free_ifname;
 +      }
 +
 +      n->next = hapd->conf->vlan;
 +      hapd->conf->vlan = n;
 +
 +#ifdef CONFIG_FULL_DYNAMIC_VLAN
 +      ifconfig_up(n->ifname);
 +#endif /* CONFIG_FULL_DYNAMIC_VLAN */
 +
 +free_ifname:
 +      os_free(ifname);
 +      return n;
 +}
 +
 +
 +int vlan_remove_dynamic(struct hostapd_data *hapd, int vlan_id)
 +{
 +      struct hostapd_vlan *vlan;
 +
 +      if (vlan_id <= 0 || vlan_id > MAX_VLAN_ID)
 +              return 1;
 +
-       if (vlan->dynamic_vlan == 0)
++      wpa_printf(MSG_DEBUG, "VLAN: %s(ifname=%s vlan_id=%d)",
++                 __func__, hapd->conf->iface, vlan_id);
 +
 +      vlan = hapd->conf->vlan;
 +      while (vlan) {
 +              if (vlan->vlan_id == vlan_id && vlan->dynamic_vlan > 0) {
 +                      vlan->dynamic_vlan--;
 +                      break;
 +              }
 +              vlan = vlan->next;
 +      }
 +
 +      if (vlan == NULL)
 +              return 1;
 +
++      if (vlan->dynamic_vlan == 0) {
 +              hostapd_vlan_if_remove(hapd, vlan->ifname);
++#ifdef CONFIG_FULL_DYNAMIC_VLAN
++              vlan_dellink(vlan->ifname, hapd);
++#endif /* CONFIG_FULL_DYNAMIC_VLAN */
++      }
 +
 +      return 0;
 +}
index 781eaac441be26e392b4c54d54cc50f26ecec9ab,0000000000000000000000000000000000000000..fc39443e5d34b6592e19c6812f44bde47a4cce97
mode 100644,000000..100644
--- /dev/null
@@@ -1,53 -1,0 +1,51 @@@
-                             struct hostapd_ssid *mssid,
 +/*
 + * hostapd / VLAN initialization
 + * Copyright 2003, Instant802 Networks, Inc.
 + * Copyright 2005, Devicescape Software, Inc.
 + *
 + * This software may be distributed under the terms of the BSD license.
 + * See README for more details.
 + */
 +
 +#ifndef VLAN_INIT_H
 +#define VLAN_INIT_H
 +
 +#ifndef CONFIG_NO_VLAN
 +int vlan_init(struct hostapd_data *hapd);
 +void vlan_deinit(struct hostapd_data *hapd);
 +struct hostapd_vlan * vlan_add_dynamic(struct hostapd_data *hapd,
 +                                     struct hostapd_vlan *vlan,
 +                                     int vlan_id);
 +int vlan_remove_dynamic(struct hostapd_data *hapd, int vlan_id);
 +int vlan_setup_encryption_dyn(struct hostapd_data *hapd,
-                                           struct hostapd_ssid *mssid,
 +                            const char *dyn_vlan);
 +#else /* CONFIG_NO_VLAN */
 +static inline int vlan_init(struct hostapd_data *hapd)
 +{
 +      return 0;
 +}
 +
 +static inline void vlan_deinit(struct hostapd_data *hapd)
 +{
 +}
 +
 +static inline struct hostapd_vlan * vlan_add_dynamic(struct hostapd_data *hapd,
 +                                                   struct hostapd_vlan *vlan,
 +                                                   int vlan_id)
 +{
 +      return NULL;
 +}
 +
 +static inline int vlan_remove_dynamic(struct hostapd_data *hapd, int vlan_id)
 +{
 +      return -1;
 +}
 +
 +static inline int vlan_setup_encryption_dyn(struct hostapd_data *hapd,
 +                                          const char *dyn_vlan)
 +{
 +      return -1;
 +}
 +#endif /* CONFIG_NO_VLAN */
 +
 +#endif /* VLAN_INIT_H */
index cc54051b1ecadce21839820d5e9962ba4fc26ce8,0000000000000000000000000000000000000000..d4e0efb9b02420232d861ea753afb959c1b51529
mode 100644,000000..100644
--- /dev/null
@@@ -1,177 -1,0 +1,192 @@@
-       int ret = -1;
 +/*
 + * hostapd / VLAN netlink api
 + * Copyright (c) 2012, Michael Braun <michael-dev@fami-braun.de>
 + *
 + * This software may be distributed under the terms of the BSD license.
 + * See README for more details.
 + */
 +
 +#include "utils/includes.h"
 +#include <sys/ioctl.h>
 +#include <linux/sockios.h>
 +#include <linux/if_vlan.h>
 +#include <netlink/genl/genl.h>
 +#include <netlink/genl/family.h>
 +#include <netlink/genl/ctrl.h>
 +#include <netlink/route/link.h>
 +#include <netlink/route/link/vlan.h>
 +
 +#include "utils/common.h"
 +#include "utils/eloop.h"
 +#include "hostapd.h"
 +#include "vlan_util.h"
 +
 +/*
 + * Add a vlan interface with name 'vlan_if_name', VLAN ID 'vid' and
 + * tagged interface 'if_name'.
 + *
 + * returns -1 on error
 + * returns 1 if the interface already exists
 + * returns 0 otherwise
 +*/
 +int vlan_add(const char *if_name, int vid, const char *vlan_if_name)
 +{
-       if (nl_connect(handle, NETLINK_ROUTE) < 0) {
-               wpa_printf(MSG_ERROR, "VLAN: failed to connect to netlink");
++      int err, ret = -1;
 +      struct nl_sock *handle = NULL;
 +      struct nl_cache *cache = NULL;
 +      struct rtnl_link *rlink = NULL;
 +      int if_idx = 0;
 +
 +      wpa_printf(MSG_DEBUG, "VLAN: vlan_add(if_name=%s, vid=%d, "
 +                 "vlan_if_name=%s)", if_name, vid, vlan_if_name);
 +
 +      if ((os_strlen(if_name) + 1) > IFNAMSIZ) {
 +              wpa_printf(MSG_ERROR, "VLAN: Interface name too long: '%s'",
 +                         if_name);
 +              return -1;
 +      }
 +
 +      if ((os_strlen(vlan_if_name) + 1) > IFNAMSIZ) {
 +              wpa_printf(MSG_ERROR, "VLAN: Interface name too long: '%s'",
 +                         vlan_if_name);
 +              return -1;
 +      }
 +
 +      handle = nl_socket_alloc();
 +      if (!handle) {
 +              wpa_printf(MSG_ERROR, "VLAN: failed to open netlink socket");
 +              goto vlan_add_error;
 +      }
 +
-       if (rtnl_link_alloc_cache(handle, AF_UNSPEC, &cache) < 0) {
++      err = nl_connect(handle, NETLINK_ROUTE);
++      if (err < 0) {
++              wpa_printf(MSG_ERROR, "VLAN: failed to connect to netlink: %s",
++                         nl_geterror(err));
 +              goto vlan_add_error;
 +      }
 +
-               wpa_printf(MSG_ERROR, "VLAN: failed to alloc cache");
++      err = rtnl_link_alloc_cache(handle, AF_UNSPEC, &cache);
++      if (err < 0) {
 +              cache = NULL;
-       if (rtnl_link_set_type(rlink, "vlan") < 0) {
-               wpa_printf(MSG_ERROR, "VLAN: failed to set link type");
++              wpa_printf(MSG_ERROR, "VLAN: failed to alloc cache: %s",
++                         nl_geterror(err));
 +              goto vlan_add_error;
 +      }
 +
 +      if (!(if_idx = rtnl_link_name2i(cache, if_name))) {
 +              /* link does not exist */
 +              wpa_printf(MSG_ERROR, "VLAN: interface %s does not exist",
 +                         if_name);
 +              goto vlan_add_error;
 +      }
 +
 +      if ((rlink = rtnl_link_get_by_name(cache, vlan_if_name))) {
 +              /* link does exist */
 +              rtnl_link_put(rlink);
 +              rlink = NULL;
 +              wpa_printf(MSG_ERROR, "VLAN: interface %s already exists",
 +                         vlan_if_name);
 +              ret = 1;
 +              goto vlan_add_error;
 +      }
 +
 +      rlink = rtnl_link_alloc();
 +      if (!rlink) {
 +              wpa_printf(MSG_ERROR, "VLAN: failed to allocate new link");
 +              goto vlan_add_error;
 +      }
 +
-       if (rtnl_link_vlan_set_id(rlink, vid) < 0) {
-               wpa_printf(MSG_ERROR, "VLAN: failed to set link vlan id");
++      err = rtnl_link_set_type(rlink, "vlan");
++      if (err < 0) {
++              wpa_printf(MSG_ERROR, "VLAN: failed to set link type: %s",
++                         nl_geterror(err));
 +              goto vlan_add_error;
 +      }
 +
 +      rtnl_link_set_link(rlink, if_idx);
 +      rtnl_link_set_name(rlink, vlan_if_name);
 +
-       if (rtnl_link_add(handle, rlink, NLM_F_CREATE) < 0) {
++      err = rtnl_link_vlan_set_id(rlink, vid);
++      if (err < 0) {
++              wpa_printf(MSG_ERROR, "VLAN: failed to set link vlan id: %s",
++                         nl_geterror(err));
 +              goto vlan_add_error;
 +      }
 +
-                          "vlan %d on %s (%d)",
-                          vlan_if_name, vid, if_name, if_idx);
++      err = rtnl_link_add(handle, rlink, NLM_F_CREATE);
++      if (err < 0) {
 +              wpa_printf(MSG_ERROR, "VLAN: failed to create link %s for "
-       int ret = -1;
++                         "vlan %d on %s (%d): %s",
++                         vlan_if_name, vid, if_name, if_idx,
++                         nl_geterror(err));
 +              goto vlan_add_error;
 +      }
 +
 +      ret = 0;
 +
 +vlan_add_error:
 +      if (rlink)
 +              rtnl_link_put(rlink);
 +      if (cache)
 +              nl_cache_free(cache);
 +      if (handle)
 +              nl_socket_free(handle);
 +      return ret;
 +}
 +
 +
 +int vlan_rem(const char *if_name)
 +{
-       if (nl_connect(handle, NETLINK_ROUTE) < 0) {
-               wpa_printf(MSG_ERROR, "VLAN: failed to connect to netlink");
++      int err, ret = -1;
 +      struct nl_sock *handle = NULL;
 +      struct nl_cache *cache = NULL;
 +      struct rtnl_link *rlink = NULL;
 +
 +      wpa_printf(MSG_DEBUG, "VLAN: vlan_rem(if_name=%s)", if_name);
 +
 +      handle = nl_socket_alloc();
 +      if (!handle) {
 +              wpa_printf(MSG_ERROR, "VLAN: failed to open netlink socket");
 +              goto vlan_rem_error;
 +      }
 +
-       if (rtnl_link_alloc_cache(handle, AF_UNSPEC, &cache) < 0) {
++      err = nl_connect(handle, NETLINK_ROUTE);
++      if (err < 0) {
++              wpa_printf(MSG_ERROR, "VLAN: failed to connect to netlink: %s",
++                         nl_geterror(err));
 +              goto vlan_rem_error;
 +      }
 +
-               wpa_printf(MSG_ERROR, "VLAN: failed to alloc cache");
++      err = rtnl_link_alloc_cache(handle, AF_UNSPEC, &cache);
++      if (err < 0) {
 +              cache = NULL;
-       if (rtnl_link_delete(handle, rlink) < 0) {
-               wpa_printf(MSG_ERROR, "VLAN: failed to remove link %s",
-                          if_name);
++              wpa_printf(MSG_ERROR, "VLAN: failed to alloc cache: %s",
++                         nl_geterror(err));
 +              goto vlan_rem_error;
 +      }
 +
 +      if (!(rlink = rtnl_link_get_by_name(cache, if_name))) {
 +              /* link does not exist */
 +              wpa_printf(MSG_ERROR, "VLAN: interface %s does not exists",
 +                         if_name);
 +              goto vlan_rem_error;
 +      }
 +
++      err = rtnl_link_delete(handle, rlink);
++      if (err < 0) {
++              wpa_printf(MSG_ERROR, "VLAN: failed to remove link %s: %s",
++                         if_name, nl_geterror(err));
 +              goto vlan_rem_error;
 +      }
 +
 +      ret = 0;
 +
 +vlan_rem_error:
 +      if (rlink)
 +              rtnl_link_put(rlink);
 +      if (cache)
 +              nl_cache_free(cache);
 +      if (handle)
 +              nl_socket_free(handle);
 +      return ret;
 +}
index 6d4177c2a847c6d0cd909e9fda7706fa0066fe90,0000000000000000000000000000000000000000..314e244bc956a4614803f27a64442619ed0c406a
mode 100644,000000..100644
--- /dev/null
@@@ -1,324 -1,0 +1,327 @@@
 +/*
 + * hostapd / WMM (Wi-Fi Multimedia)
 + * Copyright 2002-2003, Instant802 Networks, Inc.
 + * Copyright 2005-2006, Devicescape Software, Inc.
 + * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
 + *
 + * This software may be distributed under the terms of the BSD license.
 + * See README for more details.
 + */
 +
 +#include "utils/includes.h"
 +
 +#include "utils/common.h"
 +#include "common/ieee802_11_defs.h"
 +#include "common/ieee802_11_common.h"
 +#include "hostapd.h"
 +#include "ieee802_11.h"
 +#include "sta_info.h"
 +#include "ap_config.h"
 +#include "ap_drv_ops.h"
 +#include "wmm.h"
 +
 +
 +/* TODO: maintain separate sequence and fragment numbers for each AC
 + * TODO: IGMP snooping to track which multicasts to forward - and use QOS-DATA
 + * if only WMM stations are receiving a certain group */
 +
 +
 +static inline u8 wmm_aci_aifsn(int aifsn, int acm, int aci)
 +{
 +      u8 ret;
 +      ret = (aifsn << WMM_AC_AIFNS_SHIFT) & WMM_AC_AIFSN_MASK;
 +      if (acm)
 +              ret |= WMM_AC_ACM;
 +      ret |= (aci << WMM_AC_ACI_SHIFT) & WMM_AC_ACI_MASK;
 +      return ret;
 +}
 +
 +
 +static inline u8 wmm_ecw(int ecwmin, int ecwmax)
 +{
 +      return ((ecwmin << WMM_AC_ECWMIN_SHIFT) & WMM_AC_ECWMIN_MASK) |
 +              ((ecwmax << WMM_AC_ECWMAX_SHIFT) & WMM_AC_ECWMAX_MASK);
 +}
 +
 +
 +/*
 + * Add WMM Parameter Element to Beacon, Probe Response, and (Re)Association
 + * Response frames.
 + */
 +u8 * hostapd_eid_wmm(struct hostapd_data *hapd, u8 *eid)
 +{
 +      u8 *pos = eid;
 +      struct wmm_parameter_element *wmm =
 +              (struct wmm_parameter_element *) (pos + 2);
 +      int e;
 +
 +      if (!hapd->conf->wmm_enabled)
 +              return eid;
 +      eid[0] = WLAN_EID_VENDOR_SPECIFIC;
 +      wmm->oui[0] = 0x00;
 +      wmm->oui[1] = 0x50;
 +      wmm->oui[2] = 0xf2;
 +      wmm->oui_type = WMM_OUI_TYPE;
 +      wmm->oui_subtype = WMM_OUI_SUBTYPE_PARAMETER_ELEMENT;
 +      wmm->version = WMM_VERSION;
 +      wmm->qos_info = hapd->parameter_set_count & 0xf;
 +
 +      if (hapd->conf->wmm_uapsd &&
 +          (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_UAPSD))
 +              wmm->qos_info |= 0x80;
 +
 +      wmm->reserved = 0;
 +
 +      /* fill in a parameter set record for each AC */
 +      for (e = 0; e < 4; e++) {
 +              struct wmm_ac_parameter *ac = &wmm->ac[e];
 +              struct hostapd_wmm_ac_params *acp =
 +                      &hapd->iconf->wmm_ac_params[e];
 +
 +              ac->aci_aifsn = wmm_aci_aifsn(acp->aifs,
 +                                            acp->admission_control_mandatory,
 +                                            e);
 +              ac->cw = wmm_ecw(acp->cwmin, acp->cwmax);
 +              ac->txop_limit = host_to_le16(acp->txop_limit);
 +      }
 +
 +      pos = (u8 *) (wmm + 1);
 +      eid[1] = pos - eid - 2; /* element length */
 +
 +      return pos;
 +}
 +
 +
 +/*
 + * This function is called when a station sends an association request with
 + * WMM info element. The function returns 1 on success or 0 on any error in WMM
 + * element. eid does not include Element ID and Length octets.
 + */
 +int hostapd_eid_wmm_valid(struct hostapd_data *hapd, const u8 *eid, size_t len)
 +{
 +      struct wmm_information_element *wmm;
 +
 +      wpa_hexdump(MSG_MSGDUMP, "WMM IE", eid, len);
 +
 +      if (len < sizeof(struct wmm_information_element)) {
 +              wpa_printf(MSG_DEBUG, "Too short WMM IE (len=%lu)",
 +                         (unsigned long) len);
 +              return 0;
 +      }
 +
 +      wmm = (struct wmm_information_element *) eid;
 +      wpa_printf(MSG_DEBUG, "Validating WMM IE: OUI %02x:%02x:%02x  "
 +                 "OUI type %d  OUI sub-type %d  version %d  QoS info 0x%x",
 +                 wmm->oui[0], wmm->oui[1], wmm->oui[2], wmm->oui_type,
 +                 wmm->oui_subtype, wmm->version, wmm->qos_info);
 +      if (wmm->oui_subtype != WMM_OUI_SUBTYPE_INFORMATION_ELEMENT ||
 +          wmm->version != WMM_VERSION) {
 +              wpa_printf(MSG_DEBUG, "Unsupported WMM IE Subtype/Version");
 +              return 0;
 +      }
 +
 +      return 1;
 +}
 +
 +
 +static void wmm_send_action(struct hostapd_data *hapd, const u8 *addr,
 +                          const struct wmm_tspec_element *tspec,
 +                          u8 action_code, u8 dialogue_token, u8 status_code)
 +{
 +      u8 buf[256];
 +      struct ieee80211_mgmt *m = (struct ieee80211_mgmt *) buf;
 +      struct wmm_tspec_element *t = (struct wmm_tspec_element *)
 +              m->u.action.u.wmm_action.variable;
 +      int len;
 +
 +      hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211,
 +                     HOSTAPD_LEVEL_DEBUG,
 +                     "action response - reason %d", status_code);
 +      os_memset(buf, 0, sizeof(buf));
 +      m->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
 +                                      WLAN_FC_STYPE_ACTION);
 +      os_memcpy(m->da, addr, ETH_ALEN);
 +      os_memcpy(m->sa, hapd->own_addr, ETH_ALEN);
 +      os_memcpy(m->bssid, hapd->own_addr, ETH_ALEN);
 +      m->u.action.category = WLAN_ACTION_WMM;
 +      m->u.action.u.wmm_action.action_code = action_code;
 +      m->u.action.u.wmm_action.dialog_token = dialogue_token;
 +      m->u.action.u.wmm_action.status_code = status_code;
 +      os_memcpy(t, tspec, sizeof(struct wmm_tspec_element));
 +      len = ((u8 *) (t + 1)) - buf;
 +
 +      if (hostapd_drv_send_mlme(hapd, m, len, 0) < 0)
 +              wpa_printf(MSG_INFO, "wmm_send_action: send failed");
 +}
 +
 +
 +int wmm_process_tspec(struct wmm_tspec_element *tspec)
 +{
 +      int medium_time, pps, duration;
 +      int up, psb, dir, tid;
 +      u16 val, surplus;
 +
 +      up = (tspec->ts_info[1] >> 3) & 0x07;
 +      psb = (tspec->ts_info[1] >> 2) & 0x01;
 +      dir = (tspec->ts_info[0] >> 5) & 0x03;
 +      tid = (tspec->ts_info[0] >> 1) & 0x0f;
 +      wpa_printf(MSG_DEBUG, "WMM: TS Info: UP=%d PSB=%d Direction=%d TID=%d",
 +                 up, psb, dir, tid);
 +      val = le_to_host16(tspec->nominal_msdu_size);
 +      wpa_printf(MSG_DEBUG, "WMM: Nominal MSDU Size: %d%s",
 +                 val & 0x7fff, val & 0x8000 ? " (fixed)" : "");
 +      wpa_printf(MSG_DEBUG, "WMM: Mean Data Rate: %u bps",
 +                 le_to_host32(tspec->mean_data_rate));
 +      wpa_printf(MSG_DEBUG, "WMM: Minimum PHY Rate: %u bps",
 +                 le_to_host32(tspec->minimum_phy_rate));
 +      val = le_to_host16(tspec->surplus_bandwidth_allowance);
 +      wpa_printf(MSG_DEBUG, "WMM: Surplus Bandwidth Allowance: %u.%04u",
 +                 val >> 13, 10000 * (val & 0x1fff) / 0x2000);
 +
 +      val = le_to_host16(tspec->nominal_msdu_size);
 +      if (val == 0) {
 +              wpa_printf(MSG_DEBUG, "WMM: Invalid Nominal MSDU Size (0)");
 +              return WMM_ADDTS_STATUS_INVALID_PARAMETERS;
 +      }
 +      /* pps = Ceiling((Mean Data Rate / 8) / Nominal MSDU Size) */
 +      pps = ((le_to_host32(tspec->mean_data_rate) / 8) + val - 1) / val;
 +      wpa_printf(MSG_DEBUG, "WMM: Packets-per-second estimate for TSPEC: %d",
 +                 pps);
 +
 +      if (le_to_host32(tspec->minimum_phy_rate) < 1000000) {
 +              wpa_printf(MSG_DEBUG, "WMM: Too small Minimum PHY Rate");
 +              return WMM_ADDTS_STATUS_INVALID_PARAMETERS;
 +      }
 +
 +      duration = (le_to_host16(tspec->nominal_msdu_size) & 0x7fff) * 8 /
 +              (le_to_host32(tspec->minimum_phy_rate) / 1000000) +
 +              50 /* FIX: proper SIFS + ACK duration */;
 +
 +      /* unsigned binary number with an implicit binary point after the
 +       * leftmost 3 bits, i.e., 0x2000 = 1.0 */
 +      surplus = le_to_host16(tspec->surplus_bandwidth_allowance);
 +      if (surplus <= 0x2000) {
 +              wpa_printf(MSG_DEBUG, "WMM: Surplus Bandwidth Allowance not "
 +                         "greater than unity");
 +              return WMM_ADDTS_STATUS_INVALID_PARAMETERS;
 +      }
 +
 +      medium_time = surplus * pps * duration / 0x2000;
 +      wpa_printf(MSG_DEBUG, "WMM: Estimated medium time: %u", medium_time);
 +
 +      /*
 +       * TODO: store list of granted (and still active) TSPECs and check
 +       * whether there is available medium time for this request. For now,
 +       * just refuse requests that would by themselves take very large
 +       * portion of the available bandwidth.
 +       */
 +      if (medium_time > 750000) {
 +              wpa_printf(MSG_DEBUG, "WMM: Refuse TSPEC request for over "
 +                         "75%% of available bandwidth");
 +              return WMM_ADDTS_STATUS_REFUSED;
 +      }
 +
 +      /* Convert to 32 microseconds per second unit */
 +      tspec->medium_time = host_to_le16(medium_time / 32);
 +
 +      return WMM_ADDTS_STATUS_ADMISSION_ACCEPTED;
 +}
 +
 +
 +static void wmm_addts_req(struct hostapd_data *hapd,
 +                        const struct ieee80211_mgmt *mgmt,
 +                        struct wmm_tspec_element *tspec, size_t len)
 +{
 +      const u8 *end = ((const u8 *) mgmt) + len;
 +      int res;
 +
 +      if ((const u8 *) (tspec + 1) > end) {
 +              wpa_printf(MSG_DEBUG, "WMM: TSPEC overflow in ADDTS Request");
 +              return;
 +      }
 +
 +      wpa_printf(MSG_DEBUG, "WMM: ADDTS Request (Dialog Token %d) for TSPEC "
 +                 "from " MACSTR,
 +                 mgmt->u.action.u.wmm_action.dialog_token,
 +                 MAC2STR(mgmt->sa));
 +
 +      res = wmm_process_tspec(tspec);
 +      wpa_printf(MSG_DEBUG, "WMM: ADDTS processing result: %d", res);
 +
 +      wmm_send_action(hapd, mgmt->sa, tspec, WMM_ACTION_CODE_ADDTS_RESP,
 +                      mgmt->u.action.u.wmm_action.dialog_token, res);
 +}
 +
 +
 +void hostapd_wmm_action(struct hostapd_data *hapd,
 +                      const struct ieee80211_mgmt *mgmt, size_t len)
 +{
 +      int action_code;
 +      int left = len - IEEE80211_HDRLEN - 4;
 +      const u8 *pos = ((const u8 *) mgmt) + IEEE80211_HDRLEN + 4;
 +      struct ieee802_11_elems elems;
 +      struct sta_info *sta = ap_get_sta(hapd, mgmt->sa);
 +
 +      /* check that the request comes from a valid station */
 +      if (!sta ||
 +          (sta->flags & (WLAN_STA_ASSOC | WLAN_STA_WMM)) !=
 +          (WLAN_STA_ASSOC | WLAN_STA_WMM)) {
 +              hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
 +                             HOSTAPD_LEVEL_DEBUG,
 +                             "wmm action received is not from associated wmm"
 +                             " station");
 +              /* TODO: respond with action frame refused status code */
 +              return;
 +      }
 +
++      if (left < 0)
++              return; /* not a valid WMM Action frame */
++
 +      /* extract the tspec info element */
 +      if (ieee802_11_parse_elems(pos, left, &elems, 1) == ParseFailed) {
 +              hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
 +                             HOSTAPD_LEVEL_DEBUG,
 +                             "hostapd_wmm_action - could not parse wmm "
 +                             "action");
 +              /* TODO: respond with action frame invalid parameters status
 +               * code */
 +              return;
 +      }
 +
 +      if (!elems.wmm_tspec ||
 +          elems.wmm_tspec_len != (sizeof(struct wmm_tspec_element) - 2)) {
 +              hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
 +                             HOSTAPD_LEVEL_DEBUG,
 +                             "hostapd_wmm_action - missing or wrong length "
 +                             "tspec");
 +              /* TODO: respond with action frame invalid parameters status
 +               * code */
 +              return;
 +      }
 +
 +      /* TODO: check the request is for an AC with ACM set, if not, refuse
 +       * request */
 +
 +      action_code = mgmt->u.action.u.wmm_action.action_code;
 +      switch (action_code) {
 +      case WMM_ACTION_CODE_ADDTS_REQ:
 +              wmm_addts_req(hapd, mgmt, (struct wmm_tspec_element *)
 +                            (elems.wmm_tspec - 2), len);
 +              return;
 +#if 0
 +      /* TODO: needed for client implementation */
 +      case WMM_ACTION_CODE_ADDTS_RESP:
 +              wmm_setup_request(hapd, mgmt, len);
 +              return;
 +      /* TODO: handle station teardown requests */
 +      case WMM_ACTION_CODE_DELTS:
 +              wmm_teardown(hapd, mgmt, len);
 +              return;
 +#endif
 +      }
 +
 +      hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
 +                     HOSTAPD_LEVEL_DEBUG,
 +                     "hostapd_wmm_action - unknown action code %d",
 +                     action_code);
 +}
index 9c5f6094acbb868662c70a68fa88e8eb67012312,0000000000000000000000000000000000000000..2760a3f3a00e508ad1c9d4a5dcde53fead6d015a
mode 100644,000000..100644
--- /dev/null
@@@ -1,3400 -1,0 +1,3500 @@@
-       struct wpa_group *group;
 +/*
 + * IEEE 802.11 RSN / WPA Authenticator
 + * 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"
 +
 +#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"
 +#include "crypto/sha1.h"
 +#include "crypto/sha256.h"
 +#include "crypto/random.h"
 +#include "eapol_auth/eapol_auth_sm.h"
 +#include "ap_config.h"
 +#include "ieee802_11.h"
 +#include "wpa_auth.h"
 +#include "pmksa_cache_auth.h"
 +#include "wpa_auth_i.h"
 +#include "wpa_auth_ie.h"
 +
 +#define STATE_MACHINE_DATA struct wpa_state_machine
 +#define STATE_MACHINE_DEBUG_PREFIX "WPA"
 +#define STATE_MACHINE_ADDR sm->addr
 +
 +
 +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(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);
 +static void wpa_request_new_ptk(struct wpa_state_machine *sm);
 +static int wpa_gtk_update(struct wpa_authenticator *wpa_auth,
 +                        struct wpa_group *group);
 +static int wpa_group_config_group_keys(struct wpa_authenticator *wpa_auth,
 +                                     struct wpa_group *group);
 +static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *snonce,
 +                        const u8 *pmk, struct wpa_ptk *ptk);
++static void wpa_group_free(struct wpa_authenticator *wpa_auth,
++                         struct wpa_group *group);
++static void wpa_group_get(struct wpa_authenticator *wpa_auth,
++                        struct wpa_group *group);
++static void wpa_group_put(struct wpa_authenticator *wpa_auth,
++                        struct wpa_group *group);
 +
 +static const u32 dot11RSNAConfigGroupUpdateCount = 4;
 +static const u32 dot11RSNAConfigPairwiseUpdateCount = 4;
 +static const u32 eapol_key_timeout_first = 100; /* ms */
 +static const u32 eapol_key_timeout_subseq = 1000; /* ms */
 +static const u32 eapol_key_timeout_first_group = 500; /* ms */
 +
 +/* TODO: make these configurable */
 +static const int dot11RSNAConfigPMKLifetime = 43200;
 +static const int dot11RSNAConfigPMKReauthThreshold = 70;
 +static const int dot11RSNAConfigSATimeout = 60;
 +
 +
 +static inline int wpa_auth_mic_failure_report(
 +      struct wpa_authenticator *wpa_auth, const u8 *addr)
 +{
 +      if (wpa_auth->cb.mic_failure_report)
 +              return wpa_auth->cb.mic_failure_report(wpa_auth->cb.ctx, addr);
 +      return 0;
 +}
 +
 +
++static inline void wpa_auth_psk_failure_report(
++      struct wpa_authenticator *wpa_auth, const u8 *addr)
++{
++      if (wpa_auth->cb.psk_failure_report)
++              wpa_auth->cb.psk_failure_report(wpa_auth->cb.ctx, addr);
++}
++
++
 +static inline void wpa_auth_set_eapol(struct wpa_authenticator *wpa_auth,
 +                                    const u8 *addr, wpa_eapol_variable var,
 +                                    int value)
 +{
 +      if (wpa_auth->cb.set_eapol)
 +              wpa_auth->cb.set_eapol(wpa_auth->cb.ctx, addr, var, value);
 +}
 +
 +
 +static inline int wpa_auth_get_eapol(struct wpa_authenticator *wpa_auth,
 +                                   const u8 *addr, wpa_eapol_variable var)
 +{
 +      if (wpa_auth->cb.get_eapol == NULL)
 +              return -1;
 +      return wpa_auth->cb.get_eapol(wpa_auth->cb.ctx, addr, var);
 +}
 +
 +
 +static inline const u8 * wpa_auth_get_psk(struct wpa_authenticator *wpa_auth,
 +                                        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, p2p_dev_addr,
 +                                  prev_psk);
 +}
 +
 +
 +static inline int wpa_auth_get_msk(struct wpa_authenticator *wpa_auth,
 +                                 const u8 *addr, u8 *msk, size_t *len)
 +{
 +      if (wpa_auth->cb.get_msk == NULL)
 +              return -1;
 +      return wpa_auth->cb.get_msk(wpa_auth->cb.ctx, addr, msk, len);
 +}
 +
 +
 +static inline int wpa_auth_set_key(struct wpa_authenticator *wpa_auth,
 +                                 int vlan_id,
 +                                 enum wpa_alg alg, const u8 *addr, int idx,
 +                                 u8 *key, size_t key_len)
 +{
 +      if (wpa_auth->cb.set_key == NULL)
 +              return -1;
 +      return wpa_auth->cb.set_key(wpa_auth->cb.ctx, vlan_id, alg, addr, idx,
 +                                  key, key_len);
 +}
 +
 +
 +static inline int wpa_auth_get_seqnum(struct wpa_authenticator *wpa_auth,
 +                                    const u8 *addr, int idx, u8 *seq)
 +{
 +      if (wpa_auth->cb.get_seqnum == NULL)
 +              return -1;
 +      return wpa_auth->cb.get_seqnum(wpa_auth->cb.ctx, addr, idx, seq);
 +}
 +
 +
 +static inline int
 +wpa_auth_send_eapol(struct wpa_authenticator *wpa_auth, const u8 *addr,
 +                  const u8 *data, size_t data_len, int encrypt)
 +{
 +      if (wpa_auth->cb.send_eapol == NULL)
 +              return -1;
 +      return wpa_auth->cb.send_eapol(wpa_auth->cb.ctx, addr, data, data_len,
 +                                     encrypt);
 +}
 +
 +
 +#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)
 +{
 +      if (wpa_auth->cb.for_each_sta == NULL)
 +              return 0;
 +      return wpa_auth->cb.for_each_sta(wpa_auth->cb.ctx, cb, cb_ctx);
 +}
 +
 +
 +int wpa_auth_for_each_auth(struct wpa_authenticator *wpa_auth,
 +                         int (*cb)(struct wpa_authenticator *a, void *ctx),
 +                         void *cb_ctx)
 +{
 +      if (wpa_auth->cb.for_each_auth == NULL)
 +              return 0;
 +      return wpa_auth->cb.for_each_auth(wpa_auth->cb.ctx, cb, cb_ctx);
 +}
 +
 +
 +void wpa_auth_logger(struct wpa_authenticator *wpa_auth, const u8 *addr,
 +                   logger_level level, const char *txt)
 +{
 +      if (wpa_auth->cb.logger == NULL)
 +              return;
 +      wpa_auth->cb.logger(wpa_auth->cb.ctx, addr, level, txt);
 +}
 +
 +
 +void wpa_auth_vlogger(struct wpa_authenticator *wpa_auth, const u8 *addr,
 +                    logger_level level, const char *fmt, ...)
 +{
 +      char *format;
 +      int maxlen;
 +      va_list ap;
 +
 +      if (wpa_auth->cb.logger == NULL)
 +              return;
 +
 +      maxlen = os_strlen(fmt) + 100;
 +      format = os_malloc(maxlen);
 +      if (!format)
 +              return;
 +
 +      va_start(ap, fmt);
 +      vsnprintf(format, maxlen, fmt, ap);
 +      va_end(ap);
 +
 +      wpa_auth_logger(wpa_auth, addr, level, format);
 +
 +      os_free(format);
 +}
 +
 +
 +static void wpa_sta_disconnect(struct wpa_authenticator *wpa_auth,
 +                             const u8 *addr)
 +{
 +      if (wpa_auth->cb.disconnect == NULL)
 +              return;
 +      wpa_printf(MSG_DEBUG, "wpa_sta_disconnect STA " MACSTR, MAC2STR(addr));
 +      wpa_auth->cb.disconnect(wpa_auth->cb.ctx, addr,
 +                              WLAN_REASON_PREV_AUTH_NOT_VALID);
 +}
 +
 +
 +static int wpa_use_aes_cmac(struct wpa_state_machine *sm)
 +{
 +      int ret = 0;
 +#ifdef CONFIG_IEEE80211R
 +      if (wpa_key_mgmt_ft(sm->wpa_key_mgmt))
 +              ret = 1;
 +#endif /* CONFIG_IEEE80211R */
 +#ifdef CONFIG_IEEE80211W
 +      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;
 +}
 +
 +
 +static void wpa_rekey_gmk(void *eloop_ctx, void *timeout_ctx)
 +{
 +      struct wpa_authenticator *wpa_auth = eloop_ctx;
 +
 +      if (random_get_bytes(wpa_auth->group->GMK, WPA_GMK_LEN)) {
 +              wpa_printf(MSG_ERROR, "Failed to get random data for WPA "
 +                         "initialization.");
 +      } else {
 +              wpa_auth_logger(wpa_auth, NULL, LOGGER_DEBUG, "GMK rekeyd");
 +              wpa_hexdump_key(MSG_DEBUG, "GMK",
 +                              wpa_auth->group->GMK, WPA_GMK_LEN);
 +      }
 +
 +      if (wpa_auth->conf.wpa_gmk_rekey) {
 +              eloop_register_timeout(wpa_auth->conf.wpa_gmk_rekey, 0,
 +                                     wpa_rekey_gmk, wpa_auth, NULL);
 +      }
 +}
 +
 +
 +static void wpa_rekey_gtk(void *eloop_ctx, void *timeout_ctx)
 +{
 +      struct wpa_authenticator *wpa_auth = eloop_ctx;
-       for (group = wpa_auth->group; group; group = group->next) {
++      struct wpa_group *group, *next;
 +
 +      wpa_auth_logger(wpa_auth, NULL, LOGGER_DEBUG, "rekeying GTK");
- int wpa_auth_sm_event(struct wpa_state_machine *sm, wpa_event event)
++      group = wpa_auth->group;
++      while (group) {
++              wpa_group_get(wpa_auth, group);
++
 +              group->GTKReKey = TRUE;
 +              do {
 +                      group->changed = FALSE;
 +                      wpa_group_sm_step(wpa_auth, group);
 +              } while (group->changed);
++
++              next = group->next;
++              wpa_group_put(wpa_auth, group);
++              group = next;
 +      }
 +
 +      if (wpa_auth->conf.wpa_group_rekey) {
 +              eloop_register_timeout(wpa_auth->conf.wpa_group_rekey,
 +                                     0, wpa_rekey_gtk, wpa_auth, NULL);
 +      }
 +}
 +
 +
 +static void wpa_rekey_ptk(void *eloop_ctx, void *timeout_ctx)
 +{
 +      struct wpa_authenticator *wpa_auth = eloop_ctx;
 +      struct wpa_state_machine *sm = timeout_ctx;
 +
 +      wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, "rekeying PTK");
 +      wpa_request_new_ptk(sm);
 +      wpa_sm_step(sm);
 +}
 +
 +
 +static int wpa_auth_pmksa_clear_cb(struct wpa_state_machine *sm, void *ctx)
 +{
 +      if (sm->pmksa == ctx)
 +              sm->pmksa = NULL;
 +      return 0;
 +}
 +
 +
 +static void wpa_auth_pmksa_free_cb(struct rsn_pmksa_cache_entry *entry,
 +                                 void *ctx)
 +{
 +      struct wpa_authenticator *wpa_auth = ctx;
 +      wpa_auth_for_each_sta(wpa_auth, wpa_auth_pmksa_clear_cb, entry);
 +}
 +
 +
 +static int wpa_group_init_gmk_and_counter(struct wpa_authenticator *wpa_auth,
 +                                        struct wpa_group *group)
 +{
 +      u8 buf[ETH_ALEN + 8 + sizeof(unsigned long)];
 +      u8 rkey[32];
 +      unsigned long ptr;
 +
 +      if (random_get_bytes(group->GMK, WPA_GMK_LEN) < 0)
 +              return -1;
 +      wpa_hexdump_key(MSG_DEBUG, "GMK", group->GMK, WPA_GMK_LEN);
 +
 +      /*
 +       * Counter = PRF-256(Random number, "Init Counter",
 +       *                   Local MAC Address || Time)
 +       */
 +      os_memcpy(buf, wpa_auth->addr, ETH_ALEN);
 +      wpa_get_ntp_timestamp(buf + ETH_ALEN);
 +      ptr = (unsigned long) group;
 +      os_memcpy(buf + ETH_ALEN + 8, &ptr, sizeof(ptr));
 +      if (random_get_bytes(rkey, sizeof(rkey)) < 0)
 +              return -1;
 +
 +      if (sha1_prf(rkey, sizeof(rkey), "Init Counter", buf, sizeof(buf),
 +                   group->Counter, WPA_NONCE_LEN) < 0)
 +              return -1;
 +      wpa_hexdump_key(MSG_DEBUG, "Key Counter",
 +                      group->Counter, WPA_NONCE_LEN);
 +
 +      return 0;
 +}
 +
 +
 +static struct wpa_group * wpa_group_init(struct wpa_authenticator *wpa_auth,
 +                                       int vlan_id, int delay_init)
 +{
 +      struct wpa_group *group;
 +
 +      group = os_zalloc(sizeof(struct wpa_group));
 +      if (group == NULL)
 +              return NULL;
 +
 +      group->GTKAuthenticator = TRUE;
 +      group->vlan_id = vlan_id;
 +      group->GTK_len = wpa_cipher_key_len(wpa_auth->conf.wpa_group);
 +
 +      if (random_pool_ready() != 1) {
 +              wpa_printf(MSG_INFO, "WPA: Not enough entropy in random pool "
 +                         "for secure operations - update keys later when "
 +                         "the first station connects");
 +      }
 +
 +      /*
 +       * Set initial GMK/Counter value here. The actual values that will be
 +       * used in negotiations will be set once the first station tries to
 +       * connect. This allows more time for collecting additional randomness
 +       * on embedded devices.
 +       */
 +      if (wpa_group_init_gmk_and_counter(wpa_auth, group) < 0) {
 +              wpa_printf(MSG_ERROR, "Failed to get random data for WPA "
 +                         "initialization.");
 +              os_free(group);
 +              return NULL;
 +      }
 +
 +      group->GInit = TRUE;
 +      if (delay_init) {
 +              wpa_printf(MSG_DEBUG, "WPA: Delay group state machine start "
 +                         "until Beacon frames have been configured");
 +              /* Initialization is completed in wpa_init_keys(). */
 +      } else {
 +              wpa_group_sm_step(wpa_auth, group);
 +              group->GInit = FALSE;
 +              wpa_group_sm_step(wpa_auth, group);
 +      }
 +
 +      return group;
 +}
 +
 +
 +/**
 + * wpa_init - Initialize WPA authenticator
 + * @addr: Authenticator address
 + * @conf: Configuration for WPA authenticator
 + * @cb: Callback functions for WPA authenticator
 + * Returns: Pointer to WPA authenticator data or %NULL on failure
 + */
 +struct wpa_authenticator * wpa_init(const u8 *addr,
 +                                  struct wpa_auth_config *conf,
 +                                  struct wpa_auth_callbacks *cb)
 +{
 +      struct wpa_authenticator *wpa_auth;
 +
 +      wpa_auth = os_zalloc(sizeof(struct wpa_authenticator));
 +      if (wpa_auth == NULL)
 +              return NULL;
 +      os_memcpy(wpa_auth->addr, addr, ETH_ALEN);
 +      os_memcpy(&wpa_auth->conf, conf, sizeof(*conf));
 +      os_memcpy(&wpa_auth->cb, cb, sizeof(*cb));
 +
 +      if (wpa_auth_gen_wpa_ie(wpa_auth)) {
 +              wpa_printf(MSG_ERROR, "Could not generate WPA IE.");
 +              os_free(wpa_auth);
 +              return NULL;
 +      }
 +
 +      wpa_auth->group = wpa_group_init(wpa_auth, 0, 1);
 +      if (wpa_auth->group == NULL) {
 +              os_free(wpa_auth->wpa_ie);
 +              os_free(wpa_auth);
 +              return NULL;
 +      }
 +
 +      wpa_auth->pmksa = pmksa_cache_auth_init(wpa_auth_pmksa_free_cb,
 +                                              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;
 +      }
 +
 +#ifdef CONFIG_IEEE80211R
 +      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);
 +              return NULL;
 +      }
 +#endif /* CONFIG_IEEE80211R */
 +
 +      if (wpa_auth->conf.wpa_gmk_rekey) {
 +              eloop_register_timeout(wpa_auth->conf.wpa_gmk_rekey, 0,
 +                                     wpa_rekey_gmk, wpa_auth, NULL);
 +      }
 +
 +      if (wpa_auth->conf.wpa_group_rekey) {
 +              eloop_register_timeout(wpa_auth->conf.wpa_group_rekey, 0,
 +                                     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;
 +}
 +
 +
 +int wpa_init_keys(struct wpa_authenticator *wpa_auth)
 +{
 +      struct wpa_group *group = wpa_auth->group;
 +
 +      wpa_printf(MSG_DEBUG, "WPA: Start group state machine to set initial "
 +                 "keys");
 +      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;
 +}
 +
 +
 +/**
 + * wpa_deinit - Deinitialize WPA authenticator
 + * @wpa_auth: Pointer to WPA authenticator data from wpa_init()
 + */
 +void wpa_deinit(struct wpa_authenticator *wpa_auth)
 +{
 +      struct wpa_group *group, *prev;
 +
 +      eloop_cancel_timeout(wpa_rekey_gmk, wpa_auth, NULL);
 +      eloop_cancel_timeout(wpa_rekey_gtk, wpa_auth, NULL);
 +
 +#ifdef CONFIG_PEERKEY
 +      while (wpa_auth->stsl_negotiations)
 +              wpa_stsl_remove(wpa_auth, wpa_auth->stsl_negotiations);
 +#endif /* CONFIG_PEERKEY */
 +
 +      pmksa_cache_auth_deinit(wpa_auth->pmksa);
 +
 +#ifdef CONFIG_IEEE80211R
 +      wpa_ft_pmk_cache_deinit(wpa_auth->ft_pmk_cache);
 +      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;
 +      while (group) {
 +              prev = group;
 +              group = group->next;
 +              os_free(prev);
 +      }
 +
 +      os_free(wpa_auth);
 +}
 +
 +
 +/**
 + * wpa_reconfig - Update WPA authenticator configuration
 + * @wpa_auth: Pointer to WPA authenticator data from wpa_init()
 + * @conf: Configuration for WPA authenticator
 + */
 +int wpa_reconfig(struct wpa_authenticator *wpa_auth,
 +               struct wpa_auth_config *conf)
 +{
 +      struct wpa_group *group;
 +      if (wpa_auth == NULL)
 +              return 0;
 +
 +      os_memcpy(&wpa_auth->conf, conf, sizeof(*conf));
 +      if (wpa_auth_gen_wpa_ie(wpa_auth)) {
 +              wpa_printf(MSG_ERROR, "Could not generate WPA IE.");
 +              return -1;
 +      }
 +
 +      /*
 +       * Reinitialize GTK to make sure it is suitable for the new
 +       * configuration.
 +       */
 +      group = wpa_auth->group;
 +      group->GTK_len = wpa_cipher_key_len(wpa_auth->conf.wpa_group);
 +      group->GInit = TRUE;
 +      wpa_group_sm_step(wpa_auth, group);
 +      group->GInit = FALSE;
 +      wpa_group_sm_step(wpa_auth, group);
 +
 +      return 0;
 +}
 +
 +
 +struct wpa_state_machine *
 +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;
++      wpa_group_get(sm->wpa_auth, sm->group);
 +
 +      return sm;
 +}
 +
 +
 +int wpa_auth_sta_associated(struct wpa_authenticator *wpa_auth,
 +                          struct wpa_state_machine *sm)
 +{
 +      if (wpa_auth == NULL || !wpa_auth->conf.wpa || sm == NULL)
 +              return -1;
 +
 +#ifdef CONFIG_IEEE80211R
 +      if (sm->ft_completed) {
 +              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 */
 +
 +      if (sm->started) {
 +              os_memset(&sm->key_replay, 0, sizeof(sm->key_replay));
 +              sm->ReAuthenticationRequest = TRUE;
 +              return wpa_sm_step(sm);
 +      }
 +
 +      wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG,
 +                      "start authentication");
 +      sm->started = 1;
 +
 +      sm->Init = TRUE;
 +      if (wpa_sm_step(sm) == 1)
 +              return 1; /* should not really happen */
 +      sm->Init = FALSE;
 +      sm->AuthenticationRequest = TRUE;
 +      return wpa_sm_step(sm);
 +}
 +
 +
 +void wpa_auth_sta_no_wpa(struct wpa_state_machine *sm)
 +{
 +      /* WPA/RSN was not used - clear WPA state. This is needed if the STA
 +       * reassociates back to the same AP while the previous entry for the
 +       * STA has not yet been removed. */
 +      if (sm == NULL)
 +              return;
 +
 +      sm->wpa_key_mgmt = 0;
 +}
 +
 +
 +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);
++      wpa_group_put(sm->wpa_auth, sm->group);
 +      os_free(sm);
 +}
 +
 +
 +void wpa_auth_sta_deinit(struct wpa_state_machine *sm)
 +{
 +      if (sm == NULL)
 +              return;
 +
 +      if (sm->wpa_auth->conf.wpa_strict_rekey && sm->has_GTK) {
 +              wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
 +                              "strict rekeying - force GTK rekey since STA "
 +                              "is leaving");
 +              eloop_cancel_timeout(wpa_rekey_gtk, sm->wpa_auth, NULL);
 +              eloop_register_timeout(0, 500000, wpa_rekey_gtk, sm->wpa_auth,
 +                                     NULL);
 +      }
 +
 +      eloop_cancel_timeout(wpa_send_eapol_timeout, sm->wpa_auth, sm);
 +      sm->pending_1_of_4_timeout = 0;
 +      eloop_cancel_timeout(wpa_sm_call_step, sm, NULL);
 +      eloop_cancel_timeout(wpa_rekey_ptk, sm->wpa_auth, sm);
 +      if (sm->in_step_loop) {
 +              /* Must not free state machine while wpa_sm_step() is running.
 +               * Freeing will be completed in the end of wpa_sm_step(). */
 +              wpa_printf(MSG_DEBUG, "WPA: Registering pending STA state "
 +                         "machine deinit for " MACSTR, MAC2STR(sm->addr));
 +              sm->pending_deinit = 1;
 +      } else
 +              wpa_free_sta_sm(sm);
 +}
 +
 +
 +static void wpa_request_new_ptk(struct wpa_state_machine *sm)
 +{
 +      if (sm == NULL)
 +              return;
 +
 +      sm->PTKRequest = TRUE;
 +      sm->PTK_valid = 0;
 +}
 +
 +
 +static int wpa_replay_counter_valid(struct wpa_key_replay_counter *ctr,
 +                                  const u8 *replay_counter)
 +{
 +      int i;
 +      for (i = 0; i < RSNA_MAX_EAPOL_RETRIES; i++) {
 +              if (!ctr[i].valid)
 +                      break;
 +              if (os_memcmp(replay_counter, ctr[i].counter,
 +                            WPA_REPLAY_COUNTER_LEN) == 0)
 +                      return 1;
 +      }
 +      return 0;
 +}
 +
 +
 +static void wpa_replay_counter_mark_invalid(struct wpa_key_replay_counter *ctr,
 +                                          const u8 *replay_counter)
 +{
 +      int i;
 +      for (i = 0; i < RSNA_MAX_EAPOL_RETRIES; i++) {
 +              if (ctr[i].valid &&
 +                  (replay_counter == NULL ||
 +                   os_memcmp(replay_counter, ctr[i].counter,
 +                             WPA_REPLAY_COUNTER_LEN) == 0))
 +                      ctr[i].valid = FALSE;
 +      }
 +}
 +
 +
 +#ifdef CONFIG_IEEE80211R
 +static int ft_check_msg_2_of_4(struct wpa_authenticator *wpa_auth,
 +                             struct wpa_state_machine *sm,
 +                             struct wpa_eapol_ie_parse *kde)
 +{
 +      struct wpa_ie_data ie;
 +      struct rsn_mdie *mdie;
 +
 +      if (wpa_parse_wpa_ie_rsn(kde->rsn_ie, kde->rsn_ie_len, &ie) < 0 ||
 +          ie.num_pmkid != 1 || ie.pmkid == NULL) {
 +              wpa_printf(MSG_DEBUG, "FT: No PMKR1Name in "
 +                         "FT 4-way handshake message 2/4");
 +              return -1;
 +      }
 +
 +      os_memcpy(sm->sup_pmk_r1_name, ie.pmkid, PMKID_LEN);
 +      wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name from Supplicant",
 +                  sm->sup_pmk_r1_name, PMKID_LEN);
 +
 +      if (!kde->mdie || !kde->ftie) {
 +              wpa_printf(MSG_DEBUG, "FT: No %s in FT 4-way handshake "
 +                         "message 2/4", kde->mdie ? "FTIE" : "MDIE");
 +              return -1;
 +      }
 +
 +      mdie = (struct rsn_mdie *) (kde->mdie + 2);
 +      if (kde->mdie[1] < sizeof(struct rsn_mdie) ||
 +          os_memcmp(wpa_auth->conf.mobility_domain, mdie->mobility_domain,
 +                    MOBILITY_DOMAIN_ID_LEN) != 0) {
 +              wpa_printf(MSG_DEBUG, "FT: MDIE mismatch");
 +              return -1;
 +      }
 +
 +      if (sm->assoc_resp_ftie &&
 +          (kde->ftie[1] != sm->assoc_resp_ftie[1] ||
 +           os_memcmp(kde->ftie, sm->assoc_resp_ftie,
 +                     2 + sm->assoc_resp_ftie[1]) != 0)) {
 +              wpa_printf(MSG_DEBUG, "FT: FTIE mismatch");
 +              wpa_hexdump(MSG_DEBUG, "FT: FTIE in EAPOL-Key msg 2/4",
 +                          kde->ftie, kde->ftie_len);
 +              wpa_hexdump(MSG_DEBUG, "FT: FTIE in (Re)AssocResp",
 +                          sm->assoc_resp_ftie, 2 + sm->assoc_resp_ftie[1]);
 +              return -1;
 +      }
 +
 +      return 0;
 +}
 +#endif /* CONFIG_IEEE80211R */
 +
 +
 +static int wpa_receive_error_report(struct wpa_authenticator *wpa_auth,
 +                                  struct wpa_state_machine *sm, int group)
 +{
 +      /* Supplicant reported a Michael MIC error */
 +      wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO,
 +                       "received EAPOL-Key Error Request "
 +                       "(STA detected Michael MIC failure (group=%d))",
 +                       group);
 +
 +      if (group && wpa_auth->conf.wpa_group != WPA_CIPHER_TKIP) {
 +              wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
 +                              "ignore Michael MIC failure report since "
 +                              "group cipher is not TKIP");
 +      } else if (!group && sm->pairwise != WPA_CIPHER_TKIP) {
 +              wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
 +                              "ignore Michael MIC failure report since "
 +                              "pairwise cipher is not TKIP");
 +      } else {
 +              if (wpa_auth_mic_failure_report(wpa_auth, sm->addr) > 0)
 +                      return 1; /* STA entry was removed */
 +              sm->dot11RSNAStatsTKIPRemoteMICFailures++;
 +              wpa_auth->dot11RSNAStatsTKIPRemoteMICFailures++;
 +      }
 +
 +      /*
 +       * Error report is not a request for a new key handshake, but since
 +       * Authenticator may do it, let's change the keys now anyway.
 +       */
 +      wpa_request_new_ptk(sm);
 +      return 0;
 +}
 +
 +
 +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, *key_data;
 +      size_t eapol_key_ie_len, keyhdrlen, mic_len;
 +
 +      if (wpa_auth == NULL || !wpa_auth->conf.wpa || sm == NULL)
 +              return;
 +
 +      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);
 +      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) - keyhdrlen) {
 +              wpa_printf(MSG_INFO, "WPA: Invalid EAPOL-Key frame - "
 +                         "key_data overflow (%d > %lu)",
 +                         key_data_length,
 +                         (unsigned long) (data_len - sizeof(*hdr) -
 +                                          keyhdrlen));
 +              return;
 +      }
 +
 +      if (sm->wpa == WPA_VERSION_WPA2) {
 +              if (key->type == EAPOL_KEY_TYPE_WPA) {
 +                      /*
 +                       * Some deployed station implementations seem to send
 +                       * msg 4/4 with incorrect type value in WPA2 mode.
 +                       */
 +                      wpa_printf(MSG_DEBUG, "Workaround: Allow EAPOL-Key "
 +                                 "with unexpected WPA type in RSN mode");
 +              } else if (key->type != EAPOL_KEY_TYPE_RSN) {
 +                      wpa_printf(MSG_DEBUG, "Ignore EAPOL-Key with "
 +                                 "unexpected type %d in RSN mode",
 +                                 key->type);
 +                      return;
 +              }
 +      } else {
 +              if (key->type != EAPOL_KEY_TYPE_WPA) {
 +                      wpa_printf(MSG_DEBUG, "Ignore EAPOL-Key with "
 +                                 "unexpected type %d in WPA mode",
 +                                 key->type);
 +                      return;
 +              }
 +      }
 +
 +      wpa_hexdump(MSG_DEBUG, "WPA: Received Key Nonce", key->key_nonce,
 +                  WPA_NONCE_LEN);
 +      wpa_hexdump(MSG_DEBUG, "WPA: Received Replay Counter",
 +                  key->replay_counter, WPA_REPLAY_COUNTER_LEN);
 +
 +      /* FIX: verify that the EAPOL-Key frame was encrypted if pairwise keys
 +       * are set */
 +
 +      if ((key_info & (WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_REQUEST)) ==
 +          (WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_REQUEST)) {
 +              if (key_info & WPA_KEY_INFO_ERROR) {
 +                      msg = SMK_ERROR;
 +                      msgtxt = "SMK Error";
 +              } else {
 +                      msg = SMK_M1;
 +                      msgtxt = "SMK M1";
 +              }
 +      } else if (key_info & WPA_KEY_INFO_SMK_MESSAGE) {
 +              msg = SMK_M3;
 +              msgtxt = "SMK M3";
 +      } else if (key_info & WPA_KEY_INFO_REQUEST) {
 +              msg = REQUEST;
 +              msgtxt = "Request";
 +      } else if (!(key_info & WPA_KEY_INFO_KEY_TYPE)) {
 +              msg = GROUP_2;
 +              msgtxt = "2/2 Group";
 +      } else if (key_data_length == 0) {
 +              msg = PAIRWISE_4;
 +              msgtxt = "4/4 Pairwise";
 +      } else {
 +              msg = PAIRWISE_2;
 +              msgtxt = "2/4 Pairwise";
 +      }
 +
 +      /* TODO: key_info type validation for PeerKey */
 +      if (msg == REQUEST || msg == PAIRWISE_2 || msg == PAIRWISE_4 ||
 +          msg == GROUP_2) {
 +              u16 ver = key_info & WPA_KEY_INFO_TYPE_MASK;
 +              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,
 +                                              "advertised support for "
 +                                              "AES-128-CMAC, but did not "
 +                                              "use it");
 +                              return;
 +                      }
 +
 +                      if (!wpa_use_aes_cmac(sm) &&
 +                          ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
 +                              wpa_auth_logger(wpa_auth, sm->addr,
 +                                              LOGGER_WARNING,
 +                                              "did not use HMAC-SHA1-AES "
 +                                              "with CCMP/GCMP");
 +                              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) {
 +              if (sm->req_replay_counter_used &&
 +                  os_memcmp(key->replay_counter, sm->req_replay_counter,
 +                            WPA_REPLAY_COUNTER_LEN) <= 0) {
 +                      wpa_auth_logger(wpa_auth, sm->addr, LOGGER_WARNING,
 +                                      "received EAPOL-Key request with "
 +                                      "replayed counter");
 +                      return;
 +              }
 +      }
 +
 +      if (!(key_info & WPA_KEY_INFO_REQUEST) &&
 +          !wpa_replay_counter_valid(sm->key_replay, key->replay_counter)) {
 +              int i;
 +
 +              if (msg == PAIRWISE_2 &&
 +                  wpa_replay_counter_valid(sm->prev_key_replay,
 +                                           key->replay_counter) &&
 +                  sm->wpa_ptk_state == WPA_PTK_PTKINITNEGOTIATING &&
 +                  os_memcmp(sm->SNonce, key->key_nonce, WPA_NONCE_LEN) != 0)
 +              {
 +                      /*
 +                       * Some supplicant implementations (e.g., Windows XP
 +                       * WZC) update SNonce for each EAPOL-Key 2/4. This
 +                       * breaks the workaround on accepting any of the
 +                       * pending requests, so allow the SNonce to be updated
 +                       * even if we have already sent out EAPOL-Key 3/4.
 +                       */
 +                      wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG,
 +                                       "Process SNonce update from STA "
 +                                       "based on retransmitted EAPOL-Key "
 +                                       "1/4");
 +                      sm->update_snonce = 1;
 +                      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;
 +              }
 +
 +              if (msg == PAIRWISE_2 &&
 +                  wpa_replay_counter_valid(sm->prev_key_replay,
 +                                           key->replay_counter) &&
 +                  sm->wpa_ptk_state == WPA_PTK_PTKINITNEGOTIATING) {
 +                      wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG,
 +                                       "ignore retransmitted EAPOL-Key %s - "
 +                                       "SNonce did not change", msgtxt);
 +              } else {
 +                      wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG,
 +                                       "received EAPOL-Key %s with "
 +                                       "unexpected replay counter", msgtxt);
 +              }
 +              for (i = 0; i < RSNA_MAX_EAPOL_RETRIES; i++) {
 +                      if (!sm->key_replay[i].valid)
 +                              break;
 +                      wpa_hexdump(MSG_DEBUG, "pending replay counter",
 +                                  sm->key_replay[i].counter,
 +                                  WPA_REPLAY_COUNTER_LEN);
 +              }
 +              wpa_hexdump(MSG_DEBUG, "received replay counter",
 +                          key->replay_counter, WPA_REPLAY_COUNTER_LEN);
 +              return;
 +      }
 +
 +continue_processing:
 +      switch (msg) {
 +      case PAIRWISE_2:
 +              if (sm->wpa_ptk_state != WPA_PTK_PTKSTART &&
 +                  sm->wpa_ptk_state != WPA_PTK_PTKCALCNEGOTIATING &&
 +                  (!sm->update_snonce ||
 +                   sm->wpa_ptk_state != WPA_PTK_PTKINITNEGOTIATING)) {
 +                      wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO,
 +                                       "received EAPOL-Key msg 2/4 in "
 +                                       "invalid state (%d) - dropped",
 +                                       sm->wpa_ptk_state);
 +                      return;
 +              }
 +              random_add_randomness(key->key_nonce, WPA_NONCE_LEN);
 +              if (sm->group->reject_4way_hs_for_entropy) {
 +                      /*
 +                       * The system did not have enough entropy to generate
 +                       * strong random numbers. Reject the first 4-way
 +                       * handshake(s) and collect some entropy based on the
 +                       * information from it. Once enough entropy is
 +                       * available, the next atempt will trigger GMK/Key
 +                       * Counter update and the station will be allowed to
 +                       * continue.
 +                       */
 +                      wpa_printf(MSG_DEBUG, "WPA: Reject 4-way handshake to "
 +                                 "collect more entropy for random number "
 +                                 "generation");
 +                      random_mark_pool_ready();
 +                      wpa_sta_disconnect(wpa_auth, sm->addr);
 +                      return;
 +              }
 +              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");
 +                      return;
 +              }
 +              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;
 +              }
 +              ft = sm->wpa == WPA_VERSION_WPA2 &&
 +                      wpa_key_mgmt_ft(sm->wpa_key_mgmt);
 +              if (sm->wpa_ie == NULL ||
 +                  wpa_compare_rsn_ie(ft,
 +                                     sm->wpa_ie, sm->wpa_ie_len,
 +                                     eapol_key_ie, eapol_key_ie_len)) {
 +                      wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
 +                                      "WPA IE from (Re)AssocReq did not "
 +                                      "match with msg 2/4");
 +                      if (sm->wpa_ie) {
 +                              wpa_hexdump(MSG_DEBUG, "WPA IE in AssocReq",
 +                                          sm->wpa_ie, sm->wpa_ie_len);
 +                      }
 +                      wpa_hexdump(MSG_DEBUG, "WPA IE in msg 2/4",
 +                                  eapol_key_ie, eapol_key_ie_len);
 +                      /* MLME-DEAUTHENTICATE.request */
 +                      wpa_sta_disconnect(wpa_auth, sm->addr);
 +                      return;
 +              }
 +#ifdef CONFIG_IEEE80211R
 +              if (ft && ft_check_msg_2_of_4(wpa_auth, sm, &kde) < 0) {
 +                      wpa_sta_disconnect(wpa_auth, sm->addr);
 +                      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 ||
 +                  !sm->PTK_valid) {
 +                      wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO,
 +                                       "received EAPOL-Key msg 4/4 in "
 +                                       "invalid state (%d) - dropped",
 +                                       sm->wpa_ptk_state);
 +                      return;
 +              }
 +              break;
 +      case GROUP_2:
 +              if (sm->wpa_ptk_group_state != WPA_PTK_GROUP_REKEYNEGOTIATING
 +                  || !sm->PTK_valid) {
 +                      wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO,
 +                                       "received EAPOL-Key msg 2/2 in "
 +                                       "invalid state (%d) - dropped",
 +                                       sm->wpa_ptk_group_state);
 +                      return;
 +              }
 +              break;
 +#ifdef CONFIG_PEERKEY
 +      case SMK_M1:
 +      case SMK_M3:
 +      case SMK_ERROR:
 +              if (!wpa_auth->conf.peerkey) {
 +                      wpa_printf(MSG_DEBUG, "RSN: SMK M1/M3/Error, but "
 +                                 "PeerKey use disabled - ignoring message");
 +                      return;
 +              }
 +              if (!sm->PTK_valid) {
 +                      wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
 +                                      "received EAPOL-Key msg SMK in "
 +                                      "invalid state - dropped");
 +                      return;
 +              }
 +              break;
 +#else /* CONFIG_PEERKEY */
 +      case SMK_M1:
 +      case SMK_M3:
 +      case SMK_ERROR:
 +              return; /* STSL disabled - ignore SMK messages */
 +#endif /* CONFIG_PEERKEY */
 +      case REQUEST:
 +              break;
 +      }
 +
 +      wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG,
 +                       "received EAPOL-Key frame (%s)", msgtxt);
 +
 +      if (key_info & WPA_KEY_INFO_ACK) {
 +              wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
 +                              "received invalid EAPOL-Key: Key Ack set");
 +              return;
 +      }
 +
 +      if (!(key_info & WPA_KEY_INFO_MIC)) {
 +              wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
 +                              "received invalid EAPOL-Key: Key MIC not set");
 +              return;
 +      }
 +
 +      sm->MICVerified = FALSE;
 +      if (sm->PTK_valid && !sm->update_snonce) {
 +              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;
 +              }
 +              sm->MICVerified = TRUE;
 +              eloop_cancel_timeout(wpa_send_eapol_timeout, wpa_auth, sm);
 +              sm->pending_1_of_4_timeout = 0;
 +      }
 +
 +      if (key_info & WPA_KEY_INFO_REQUEST) {
 +              if (sm->MICVerified) {
 +                      sm->req_replay_counter_used = 1;
 +                      os_memcpy(sm->req_replay_counter, key->replay_counter,
 +                                WPA_REPLAY_COUNTER_LEN);
 +              } else {
 +                      wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
 +                                      "received EAPOL-Key request with "
 +                                      "invalid MIC");
 +                      return;
 +              }
 +
 +              /*
 +               * TODO: should decrypt key data field if encryption was used;
 +               * even though MAC address KDE is not normally encrypted,
 +               * supplicant is allowed to encrypt it.
 +               */
 +              if (msg == SMK_ERROR) {
 +#ifdef CONFIG_PEERKEY
 +                      wpa_smk_error(wpa_auth, sm, key_data, key_data_length);
 +#endif /* CONFIG_PEERKEY */
 +                      return;
 +              } else if (key_info & WPA_KEY_INFO_ERROR) {
 +                      if (wpa_receive_error_report(
 +                                  wpa_auth, sm,
 +                                  !(key_info & WPA_KEY_INFO_KEY_TYPE)) > 0)
 +                              return; /* STA entry was removed */
 +              } else if (key_info & WPA_KEY_INFO_KEY_TYPE) {
 +                      wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
 +                                      "received EAPOL-Key Request for new "
 +                                      "4-Way Handshake");
 +                      wpa_request_new_ptk(sm);
 +#ifdef CONFIG_PEERKEY
 +              } else if (msg == SMK_M1) {
 +                      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(key_data, key_data_length,
 +                                           &kde) == 0 &&
 +                         kde.mac_addr) {
 +              } else {
 +                      wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
 +                                      "received EAPOL-Key Request for GTK "
 +                                      "rekeying");
 +                      eloop_cancel_timeout(wpa_rekey_gtk, wpa_auth, NULL);
 +                      wpa_rekey_gtk(wpa_auth, NULL);
 +              }
 +      } else {
 +              /* Do not allow the same key replay counter to be reused. */
 +              wpa_replay_counter_mark_invalid(sm->key_replay,
 +                                              key->replay_counter);
 +
 +              if (msg == PAIRWISE_2) {
 +                      /*
 +                       * Maintain a copy of the pending EAPOL-Key frames in
 +                       * case the EAPOL-Key frame was retransmitted. This is
 +                       * needed to allow EAPOL-Key msg 2/4 reply to another
 +                       * pending msg 1/4 to update the SNonce to work around
 +                       * unexpected supplicant behavior.
 +                       */
 +                      os_memcpy(sm->prev_key_replay, sm->key_replay,
 +                                sizeof(sm->key_replay));
 +              } else {
 +                      os_memset(sm->prev_key_replay, 0,
 +                                sizeof(sm->prev_key_replay));
 +              }
 +
 +              /*
 +               * Make sure old valid counters are not accepted anymore and
 +               * do not get copied again.
 +               */
 +              wpa_replay_counter_mark_invalid(sm->key_replay, NULL);
 +      }
 +
 +#ifdef CONFIG_PEERKEY
 +      if (msg == SMK_M3) {
 +              wpa_smk_m3(wpa_auth, sm, key, key_data, key_data_length);
 +              return;
 +      }
 +#endif /* CONFIG_PEERKEY */
 +
 +      os_free(sm->last_rx_eapol_key);
 +      sm->last_rx_eapol_key = os_malloc(data_len);
 +      if (sm->last_rx_eapol_key == NULL)
 +              return;
 +      os_memcpy(sm->last_rx_eapol_key, data, data_len);
 +      sm->last_rx_eapol_key_len = data_len;
 +
 +      sm->rx_eapol_key_secure = !!(key_info & WPA_KEY_INFO_SECURE);
 +      sm->EAPOLKeyReceived = TRUE;
 +      sm->EAPOLKeyPairwise = !!(key_info & WPA_KEY_INFO_KEY_TYPE);
 +      sm->EAPOLKeyRequest = !!(key_info & WPA_KEY_INFO_REQUEST);
 +      os_memcpy(sm->SNonce, key->key_nonce, WPA_NONCE_LEN);
 +      wpa_sm_step(sm);
 +}
 +
 +
 +static int wpa_gmk_to_gtk(const u8 *gmk, const char *label, const u8 *addr,
 +                        const u8 *gnonce, u8 *gtk, size_t gtk_len)
 +{
 +      u8 data[ETH_ALEN + WPA_NONCE_LEN + 8 + 16];
 +      u8 *pos;
 +      int ret = 0;
 +
 +      /* GTK = PRF-X(GMK, "Group key expansion",
 +       *      AA || GNonce || Time || random data)
 +       * The example described in the IEEE 802.11 standard uses only AA and
 +       * GNonce as inputs here. Add some more entropy since this derivation
 +       * is done only at the Authenticator and as such, does not need to be
 +       * exactly same.
 +       */
 +      os_memcpy(data, addr, ETH_ALEN);
 +      os_memcpy(data + ETH_ALEN, gnonce, WPA_NONCE_LEN);
 +      pos = data + ETH_ALEN + WPA_NONCE_LEN;
 +      wpa_get_ntp_timestamp(pos);
 +      pos += 8;
 +      if (random_get_bytes(pos, 16) < 0)
 +              ret = -1;
 +
 +#ifdef CONFIG_IEEE80211W
 +      sha256_prf(gmk, WPA_GMK_LEN, label, data, sizeof(data), gtk, gtk_len);
 +#else /* CONFIG_IEEE80211W */
 +      if (sha1_prf(gmk, WPA_GMK_LEN, label, data, sizeof(data), gtk, gtk_len)
 +          < 0)
 +              ret = -1;
 +#endif /* CONFIG_IEEE80211W */
 +
 +      return ret;
 +}
 +
 +
 +static void wpa_send_eapol_timeout(void *eloop_ctx, void *timeout_ctx)
 +{
 +      struct wpa_authenticator *wpa_auth = eloop_ctx;
 +      struct wpa_state_machine *sm = timeout_ctx;
 +
 +      sm->pending_1_of_4_timeout = 0;
 +      wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, "EAPOL-Key timeout");
 +      sm->TimeoutEvt = TRUE;
 +      wpa_sm_step(sm);
 +}
 +
 +
 +void __wpa_send_eapol(struct wpa_authenticator *wpa_auth,
 +                    struct wpa_state_machine *sm, int key_info,
 +                    const u8 *key_rsc, const u8 *nonce,
 +                    const u8 *kde, size_t kde_len,
 +                    int keyidx, int encr, int force_version)
 +{
 +      struct ieee802_1x_hdr *hdr;
 +      struct wpa_eapol_key *key;
 +      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;
 +
 +      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)
 +              version = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES;
 +      else
 +              version = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4;
 +
 +      pairwise = !!(key_info & WPA_KEY_INFO_KEY_TYPE);
 +
 +      wpa_printf(MSG_DEBUG, "WPA: Send EAPOL(version=%d secure=%d mic=%d "
 +                 "ack=%d install=%d pairwise=%d kde_len=%lu keyidx=%d "
 +                 "encr=%d)",
 +                 version,
 +                 (key_info & WPA_KEY_INFO_SECURE) ? 1 : 0,
 +                 (key_info & WPA_KEY_INFO_MIC) ? 1 : 0,
 +                 (key_info & WPA_KEY_INFO_ACK) ? 1 : 0,
 +                 (key_info & WPA_KEY_INFO_INSTALL) ? 1 : 0,
 +                 pairwise, (unsigned long) kde_len, keyidx, encr);
 +
 +      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)
 +                      pad_len = 8 - pad_len;
 +              key_data_len += pad_len + 8;
 +      }
 +
 +      len += key_data_len;
 +
 +      hdr = os_zalloc(len);
 +      if (hdr == NULL)
 +              return;
 +      hdr->version = wpa_auth->conf.eapol_version;
 +      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;
 +      key_info |= version;
 +      if (encr && sm->wpa == WPA_VERSION_WPA2)
 +              key_info |= WPA_KEY_INFO_ENCR_KEY_DATA;
 +      if (sm->wpa != WPA_VERSION_WPA2)
 +              key_info |= keyidx << WPA_KEY_INFO_KEY_INDEX_SHIFT;
 +      WPA_PUT_BE16(key->key_info, key_info);
 +
 +      alg = pairwise ? sm->pairwise : wpa_auth->conf.wpa_group;
 +      WPA_PUT_BE16(key->key_length, wpa_cipher_key_len(alg));
 +      if (key_info & WPA_KEY_INFO_SMK_MESSAGE)
 +              WPA_PUT_BE16(key->key_length, 0);
 +
 +      /* FIX: STSL: what to use as key_replay_counter? */
 +      for (i = RSNA_MAX_EAPOL_RETRIES - 1; i > 0; i--) {
 +              sm->key_replay[i].valid = sm->key_replay[i - 1].valid;
 +              os_memcpy(sm->key_replay[i].counter,
 +                        sm->key_replay[i - 1].counter,
 +                        WPA_REPLAY_COUNTER_LEN);
 +      }
 +      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)
 +              os_memcpy(key->key_nonce, nonce, WPA_NONCE_LEN);
 +
 +      if (key_rsc)
 +              os_memcpy(key->key_rsc, key_rsc, WPA_KEY_RSC_LEN);
 +
 +      if (kde && !encr) {
 +              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) {
 +                      os_free(hdr);
 +                      return;
 +              }
 +              pos = buf;
 +              os_memcpy(pos, kde, kde_len);
 +              pos += kde_len;
 +
 +              if (pad_len)
 +                      *pos++ = 0xdd;
 +
 +              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, sm->PTK.kek_len,
 +                                   (key_data_len - 8) / 8, buf, key_data)) {
 +                              os_free(hdr);
 +                              os_free(buf);
 +                              return;
 +                      }
 +                      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);
++#ifndef CONFIG_NO_RC4
 +              } 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, 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);
++#endif /* CONFIG_NO_RC4 */
 +              } 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 "
 +                                      "frame");
 +                      os_free(hdr);
 +                      return;
 +              }
 +
 +              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.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_mic[0]++;
 +              }
 +#endif /* CONFIG_TESTING_OPTIONS */
 +      }
 +
 +      wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_inc_EapolFramesTx,
 +                         1);
 +      wpa_auth_send_eapol(wpa_auth, sm->addr, (u8 *) hdr, len,
 +                          sm->pairwise_set);
 +      os_free(hdr);
 +}
 +
 +
 +static void wpa_send_eapol(struct wpa_authenticator *wpa_auth,
 +                         struct wpa_state_machine *sm, int key_info,
 +                         const u8 *key_rsc, const u8 *nonce,
 +                         const u8 *kde, size_t kde_len,
 +                         int keyidx, int encr)
 +{
 +      int timeout_ms;
 +      int pairwise = key_info & WPA_KEY_INFO_KEY_TYPE;
 +      int ctr;
 +
 +      if (sm == NULL)
 +              return;
 +
 +      __wpa_send_eapol(wpa_auth, sm, key_info, key_rsc, nonce, kde, kde_len,
 +                       keyidx, encr, 0);
 +
 +      ctr = pairwise ? sm->TimeoutCtr : sm->GTimeoutCtr;
 +      if (ctr == 1 && wpa_auth->conf.tx_status)
 +              timeout_ms = pairwise ? eapol_key_timeout_first :
 +                      eapol_key_timeout_first_group;
 +      else
 +              timeout_ms = eapol_key_timeout_subseq;
 +      if (pairwise && ctr == 1 && !(key_info & WPA_KEY_INFO_MIC))
 +              sm->pending_1_of_4_timeout = 1;
 +      wpa_printf(MSG_DEBUG, "WPA: Use EAPOL-Key timeout of %u ms (retry "
 +                 "counter %d)", timeout_ms, ctr);
 +      eloop_register_timeout(timeout_ms / 1000, (timeout_ms % 1000) * 1000,
 +                             wpa_send_eapol_timeout, wpa_auth, sm);
 +}
 +
 +
 +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[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, 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(key192->key_mic, mic, mic_len);
 +      return ret;
 +}
 +
 +
 +void wpa_remove_ptk(struct wpa_state_machine *sm)
 +{
 +      sm->PTK_valid = FALSE;
 +      os_memset(&sm->PTK, 0, sizeof(sm->PTK));
 +      wpa_auth_set_key(sm->wpa_auth, 0, WPA_ALG_NONE, sm->addr, 0, NULL, 0);
 +      sm->pairwise_set = FALSE;
 +      eloop_cancel_timeout(wpa_rekey_ptk, sm->wpa_auth, sm);
 +}
 +
 +
-       wpa_group_init_gmk_and_counter(wpa_auth, group);
-       wpa_gtk_update(wpa_auth, group);
-       wpa_group_config_group_keys(wpa_auth, group);
++int wpa_auth_sm_event(struct wpa_state_machine *sm, enum wpa_event event)
 +{
 +      int remove_ptk = 1;
 +
 +      if (sm == NULL)
 +              return -1;
 +
 +      wpa_auth_vlogger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
 +                       "event %d notification", 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:
 +      case WPA_DISASSOC:
 +              sm->DeauthenticationRequest = TRUE;
 +              break;
 +      case WPA_REAUTH:
 +      case WPA_REAUTH_EAPOL:
 +              if (!sm->started) {
 +                      /*
 +                       * When using WPS, we may end up here if the STA
 +                       * manages to re-associate without the previous STA
 +                       * entry getting removed. Consequently, we need to make
 +                       * sure that the WPA state machines gets initialized
 +                       * properly at this point.
 +                       */
 +                      wpa_printf(MSG_DEBUG, "WPA state machine had not been "
 +                                 "started - initialize now");
 +                      sm->started = 1;
 +                      sm->Init = TRUE;
 +                      if (wpa_sm_step(sm) == 1)
 +                              return 1; /* should not really happen */
 +                      sm->Init = FALSE;
 +                      sm->AuthenticationRequest = TRUE;
 +                      break;
 +              }
 +              if (sm->GUpdateStationKeys) {
 +                      /*
 +                       * Reauthentication cancels the pending group key
 +                       * update for this STA.
 +                       */
 +                      sm->group->GKeyDoneStations--;
 +                      sm->GUpdateStationKeys = FALSE;
 +                      sm->PtkGroupInit = TRUE;
 +              }
 +              sm->ReAuthenticationRequest = TRUE;
 +              break;
 +      case WPA_ASSOC_FT:
 +#ifdef CONFIG_IEEE80211R
 +              wpa_printf(MSG_DEBUG, "FT: Retry PTK configuration "
 +                         "after association");
 +              wpa_ft_install_ptk(sm);
 +
 +              /* Using FT protocol, not WPA auth state machine */
 +              sm->ft_completed = 1;
 +              return 0;
 +#else /* CONFIG_IEEE80211R */
 +              break;
 +#endif /* CONFIG_IEEE80211R */
 +      }
 +
 +#ifdef CONFIG_IEEE80211R
 +      sm->ft_completed = 0;
 +#endif /* CONFIG_IEEE80211R */
 +
 +#ifdef CONFIG_IEEE80211W
 +      if (sm->mgmt_frame_prot && event == WPA_AUTH)
 +              remove_ptk = 0;
 +#endif /* CONFIG_IEEE80211W */
 +
 +      if (remove_ptk) {
 +              sm->PTK_valid = FALSE;
 +              os_memset(&sm->PTK, 0, sizeof(sm->PTK));
 +
 +              if (event != WPA_REAUTH_EAPOL)
 +                      wpa_remove_ptk(sm);
 +      }
 +
++      if (sm->in_step_loop) {
++              /*
++               * wpa_sm_step() is already running - avoid recursive call to
++               * it by making the existing loop process the new update.
++               */
++              sm->changed = TRUE;
++              return 0;
++      }
 +      return wpa_sm_step(sm);
 +}
 +
 +
 +SM_STATE(WPA_PTK, INITIALIZE)
 +{
 +      SM_ENTRY_MA(WPA_PTK, INITIALIZE, wpa_ptk);
 +      if (sm->Init) {
 +              /* Init flag is not cleared here, so avoid busy
 +               * loop by claiming nothing changed. */
 +              sm->changed = FALSE;
 +      }
 +
 +      sm->keycount = 0;
 +      if (sm->GUpdateStationKeys)
 +              sm->group->GKeyDoneStations--;
 +      sm->GUpdateStationKeys = FALSE;
 +      if (sm->wpa == WPA_VERSION_WPA)
 +              sm->PInitAKeys = FALSE;
 +      if (1 /* Unicast cipher supported AND (ESS OR ((IBSS or WDS) and
 +             * Local AA > Remote AA)) */) {
 +              sm->Pair = TRUE;
 +      }
 +      wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_portEnabled, 0);
 +      wpa_remove_ptk(sm);
 +      wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_portValid, 0);
 +      sm->TimeoutCtr = 0;
 +      if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) {
 +              wpa_auth_set_eapol(sm->wpa_auth, sm->addr,
 +                                 WPA_EAPOL_authorized, 0);
 +      }
 +}
 +
 +
 +SM_STATE(WPA_PTK, DISCONNECT)
 +{
 +      SM_ENTRY_MA(WPA_PTK, DISCONNECT, wpa_ptk);
 +      sm->Disconnect = FALSE;
 +      wpa_sta_disconnect(sm->wpa_auth, sm->addr);
 +}
 +
 +
 +SM_STATE(WPA_PTK, DISCONNECTED)
 +{
 +      SM_ENTRY_MA(WPA_PTK, DISCONNECTED, wpa_ptk);
 +      sm->DeauthenticationRequest = FALSE;
 +}
 +
 +
 +SM_STATE(WPA_PTK, AUTHENTICATION)
 +{
 +      SM_ENTRY_MA(WPA_PTK, AUTHENTICATION, wpa_ptk);
 +      os_memset(&sm->PTK, 0, sizeof(sm->PTK));
 +      sm->PTK_valid = FALSE;
 +      wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_portControl_Auto,
 +                         1);
 +      wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_portEnabled, 1);
 +      sm->AuthenticationRequest = FALSE;
 +}
 +
 +
 +static void wpa_group_ensure_init(struct wpa_authenticator *wpa_auth,
 +                                struct wpa_group *group)
 +{
 +      if (group->first_sta_seen)
 +              return;
 +      /*
 +       * System has run bit further than at the time hostapd was started
 +       * potentially very early during boot up. This provides better chances
 +       * of collecting more randomness on embedded systems. Re-initialize the
 +       * GMK and Counter here to improve their strength if there was not
 +       * enough entropy available immediately after system startup.
 +       */
 +      wpa_printf(MSG_DEBUG, "WPA: Re-initialize GMK/Counter on first "
 +                 "station");
 +      if (random_pool_ready() != 1) {
 +              wpa_printf(MSG_INFO, "WPA: Not enough entropy in random pool "
 +                         "to proceed - reject first 4-way handshake");
 +              group->reject_4way_hs_for_entropy = TRUE;
 +      } else {
 +              group->first_sta_seen = TRUE;
 +              group->reject_4way_hs_for_entropy = FALSE;
 +      }
 +
-       int ok = 0;
++      if (wpa_group_init_gmk_and_counter(wpa_auth, group) < 0 ||
++          wpa_gtk_update(wpa_auth, group) < 0 ||
++          wpa_group_config_group_keys(wpa_auth, group) < 0) {
++              wpa_printf(MSG_INFO, "WPA: GMK/GTK setup failed");
++              group->first_sta_seen = FALSE;
++              group->reject_4way_hs_for_entropy = TRUE;
++      }
 +}
 +
 +
 +SM_STATE(WPA_PTK, AUTHENTICATION2)
 +{
 +      SM_ENTRY_MA(WPA_PTK, AUTHENTICATION2, wpa_ptk);
 +
 +      wpa_group_ensure_init(sm->wpa_auth, sm->group);
 +      sm->ReAuthenticationRequest = FALSE;
 +
 +      /*
 +       * Definition of ANonce selection in IEEE Std 802.11i-2004 is somewhat
 +       * ambiguous. The Authenticator state machine uses a counter that is
 +       * incremented by one for each 4-way handshake. However, the security
 +       * analysis of 4-way handshake points out that unpredictable nonces
 +       * help in preventing precomputation attacks. Instead of the state
 +       * machine definition, use an unpredictable nonce value here to provide
 +       * stronger protection against potential precomputation attacks.
 +       */
 +      if (random_get_bytes(sm->ANonce, WPA_NONCE_LEN)) {
 +              wpa_printf(MSG_ERROR, "WPA: Failed to get random data for "
 +                         "ANonce.");
 +              sm->Disconnect = TRUE;
 +              return;
 +      }
 +      wpa_hexdump(MSG_DEBUG, "WPA: Assign ANonce", sm->ANonce,
 +                  WPA_NONCE_LEN);
 +      /* IEEE 802.11i does not clear TimeoutCtr here, but this is more
 +       * logical place than INITIALIZE since AUTHENTICATION2 can be
 +       * re-entered on ReAuthenticationRequest without going through
 +       * INITIALIZE. */
 +      sm->TimeoutCtr = 0;
 +}
 +
 +
 +SM_STATE(WPA_PTK, INITPMK)
 +{
 +      u8 msk[2 * PMK_LEN];
 +      size_t len = 2 * PMK_LEN;
 +
 +      SM_ENTRY_MA(WPA_PTK, INITPMK, wpa_ptk);
 +#ifdef CONFIG_IEEE80211R
 +      sm->xxkey_len = 0;
 +#endif /* CONFIG_IEEE80211R */
 +      if (sm->pmksa) {
 +              wpa_printf(MSG_DEBUG, "WPA: PMK from PMKSA cache");
 +              os_memcpy(sm->PMK, sm->pmksa->pmk, PMK_LEN);
 +      } else if (wpa_auth_get_msk(sm->wpa_auth, sm->addr, msk, &len) == 0) {
 +              wpa_printf(MSG_DEBUG, "WPA: PMK from EAPOL state machine "
 +                         "(len=%lu)", (unsigned long) len);
 +              os_memcpy(sm->PMK, msk, PMK_LEN);
 +#ifdef CONFIG_IEEE80211R
 +              if (len >= 2 * PMK_LEN) {
 +                      os_memcpy(sm->xxkey, msk + PMK_LEN, PMK_LEN);
 +                      sm->xxkey_len = PMK_LEN;
 +              }
 +#endif /* CONFIG_IEEE80211R */
 +      } else {
 +              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
 +       * will break reauthentication since EAPOL state machines may not be
 +       * get into AUTHENTICATING state that clears keyRun before WPA state
 +       * machine enters AUTHENTICATION2 state and goes immediately to INITPMK
 +       * state and takes PMK from the previously used AAA Key. This will
 +       * eventually fail in 4-Way Handshake because Supplicant uses PMK
 +       * derived from the new AAA Key. Setting keyRun = FALSE here seems to
 +       * be good workaround for this issue. */
 +      wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_keyRun, 0);
 +}
 +
 +
 +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, sm->p2p_dev_addr, NULL);
 +      if (psk) {
 +              os_memcpy(sm->PMK, psk, PMK_LEN);
 +#ifdef CONFIG_IEEE80211R
 +              os_memcpy(sm->xxkey, psk, PMK_LEN);
 +              sm->xxkey_len = PMK_LEN;
 +#endif /* CONFIG_IEEE80211R */
 +      }
 +      sm->req_replay_counter_used = 0;
 +}
 +
 +
 +SM_STATE(WPA_PTK, PTKSTART)
 +{
 +      u8 buf[2 + RSN_SELECTOR_LEN + PMKID_LEN], *pmkid = NULL;
 +      size_t pmkid_len = 0;
 +
 +      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) {
 +              /* No point in sending the EAPOL-Key - we will disconnect
 +               * immediately following this. */
 +              return;
 +      }
 +
 +      wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
 +                      "sending 1/4 msg of 4-Way Handshake");
 +      /*
 +       * TODO: Could add PMKID even with WPA2-PSK, but only if there is only
 +       * one possible PSK for this STA.
 +       */
 +      if (sm->wpa == WPA_VERSION_WPA2 &&
 +          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) {
 +                      os_memcpy(&pmkid[2 + RSN_SELECTOR_LEN],
 +                                sm->pmksa->pmkid, PMKID_LEN);
 +              } 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.
 +                       */
 +                      rsn_pmkid(sm->PMK, PMK_LEN, sm->wpa_auth->addr,
 +                                sm->addr, &pmkid[2 + RSN_SELECTOR_LEN],
 +                                wpa_key_mgmt_sha256(sm->wpa_key_mgmt));
 +              }
 +      }
 +      wpa_send_eapol(sm->wpa_auth, sm,
 +                     WPA_KEY_INFO_ACK | WPA_KEY_INFO_KEY_TYPE, NULL,
 +                     sm->ANonce, pmkid, pmkid_len, 0, 0);
 +}
 +
 +
 +static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *snonce,
 +                        const u8 *pmk, struct wpa_ptk *ptk)
 +{
 +#ifdef CONFIG_IEEE80211R
 +      if (wpa_key_mgmt_ft(sm->wpa_key_mgmt))
 +              return wpa_auth_derive_ptk_ft(sm, pmk, ptk);
 +#endif /* CONFIG_IEEE80211R */
 +
 +      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);
 +}
 +
 +
 +SM_STATE(WPA_PTK, PTKCALCNEGOTIATING)
 +{
 +      struct wpa_ptk PTK;
- static const char * wpa_bool_txt(int bool)
++      int ok = 0, psk_found = 0;
 +      const u8 *pmk = NULL;
 +
 +      SM_ENTRY_MA(WPA_PTK, PTKCALCNEGOTIATING, wpa_ptk);
 +      sm->EAPOLKeyReceived = FALSE;
 +      sm->update_snonce = FALSE;
 +
 +      /* WPA with IEEE 802.1X: use the derived PMK from EAP
 +       * WPA-PSK: iterate through possible PSKs and select the one matching
 +       * the packet */
 +      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;
++                      psk_found = 1;
 +              } else
 +                      pmk = sm->PMK;
 +
 +              wpa_derive_ptk(sm, sm->SNonce, pmk, &PTK);
 +
 +              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;
 +              }
 +
 +              if (!wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt))
 +                      break;
 +      }
 +
 +      if (!ok) {
 +              wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
 +                              "invalid MIC in msg 2/4 of 4-Way Handshake");
++              if (psk_found)
++                      wpa_auth_psk_failure_report(sm->wpa_auth, sm->addr);
 +              return;
 +      }
 +
 +#ifdef CONFIG_IEEE80211R
 +      if (sm->wpa == WPA_VERSION_WPA2 && wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
 +              /*
 +               * Verify that PMKR1Name from EAPOL-Key message 2/4 matches
 +               * with the value we derived.
 +               */
 +              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");
 +                      wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name from "
 +                                  "Supplicant",
 +                                  sm->sup_pmk_r1_name, WPA_PMK_NAME_LEN);
 +                      wpa_hexdump(MSG_DEBUG, "FT: Derived PMKR1Name",
 +                                  sm->pmk_r1_name, WPA_PMK_NAME_LEN);
 +                      return;
 +              }
 +      }
 +#endif /* CONFIG_IEEE80211R */
 +
 +      sm->pending_1_of_4_timeout = 0;
 +      eloop_cancel_timeout(wpa_send_eapol_timeout, sm->wpa_auth, sm);
 +
 +      if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) {
 +              /* PSK may have changed from the previous choice, so update
 +               * state machine data based on whatever PSK was selected here.
 +               */
 +              os_memcpy(sm->PMK, pmk, PMK_LEN);
 +      }
 +
 +      sm->MICVerified = TRUE;
 +
 +      os_memcpy(&sm->PTK, &PTK, sizeof(PTK));
 +      sm->PTK_valid = TRUE;
 +}
 +
 +
 +SM_STATE(WPA_PTK, PTKCALCNEGOTIATING2)
 +{
 +      SM_ENTRY_MA(WPA_PTK, PTKCALCNEGOTIATING2, wpa_ptk);
 +      sm->TimeoutCtr = 0;
 +}
 +
 +
 +#ifdef CONFIG_IEEE80211W
 +
 +static int ieee80211w_kde_len(struct wpa_state_machine *sm)
 +{
 +      if (sm->mgmt_frame_prot) {
 +              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;
 +}
 +
 +
 +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;
 +
 +      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, rsc) < 0)
 +              os_memset(igtk.pn, 0, sizeof(igtk.pn));
 +      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, len) < 0)
 +                      return pos;
 +      }
 +      pos = wpa_add_kde(pos, RSN_KEY_DATA_IGTK,
 +                        (const u8 *) &igtk, WPA_IGTK_KDE_PREFIX_LEN + len,
 +                        NULL, 0);
 +
 +      return pos;
 +}
 +
 +#else /* CONFIG_IEEE80211W */
 +
 +static int ieee80211w_kde_len(struct wpa_state_machine *sm)
 +{
 +      return 0;
 +}
 +
 +
 +static u8 * ieee80211w_kde_add(struct wpa_state_machine *sm, u8 *pos)
 +{
 +      return pos;
 +}
 +
 +#endif /* CONFIG_IEEE80211W */
 +
 +
 +SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
 +{
 +      u8 rsc[WPA_KEY_RSC_LEN], *_rsc, *gtk, *kde, *pos, dummy_gtk[32];
 +      size_t gtk_len, kde_len;
 +      struct wpa_group *gsm = sm->group;
 +      u8 *wpa_ie;
 +      int wpa_ie_len, secure, keyidx, encr = 0;
 +
 +      SM_ENTRY_MA(WPA_PTK, PTKINITNEGOTIATING, wpa_ptk);
 +      sm->TimeoutEvt = FALSE;
 +
 +      sm->TimeoutCtr++;
 +      if (sm->TimeoutCtr > (int) dot11RSNAConfigPairwiseUpdateCount) {
 +              /* No point in sending the EAPOL-Key - we will disconnect
 +               * immediately following this. */
 +              return;
 +      }
 +
 +      /* Send EAPOL(1, 1, 1, Pair, P, RSC, ANonce, MIC(PTK), RSNIE, [MDIE],
 +         GTK[GN], IGTK, [FTIE], [TIE * 2])
 +       */
 +      os_memset(rsc, 0, WPA_KEY_RSC_LEN);
 +      wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN, rsc);
 +      /* If FT is used, wpa_auth->wpa_ie includes both RSNIE and MDIE */
 +      wpa_ie = sm->wpa_auth->wpa_ie;
 +      wpa_ie_len = sm->wpa_auth->wpa_ie_len;
 +      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 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,
 +                      "sending 3/4 msg of 4-Way Handshake");
 +      if (sm->wpa == WPA_VERSION_WPA2) {
 +              /* WPA2 send GTK in the 4-way handshake */
 +              secure = 1;
 +              gtk = gsm->GTK[gsm->GN - 1];
 +              gtk_len = gsm->GTK_len;
 +              if (sm->wpa_auth->conf.disable_gtk) {
 +                      /*
 +                       * Provide unique random GTK to each STA to prevent use
 +                       * of GTK in the BSS.
 +                       */
 +                      if (random_get_bytes(dummy_gtk, gtk_len) < 0)
 +                              return;
 +                      gtk = dummy_gtk;
 +              }
 +              keyidx = gsm->GN;
 +              _rsc = rsc;
 +              encr = 1;
 +      } else {
 +              /* WPA does not include GTK in msg 3/4 */
 +              secure = 0;
 +              gtk = NULL;
 +              gtk_len = 0;
 +              keyidx = 0;
 +              _rsc = NULL;
 +              if (sm->rx_eapol_key_secure) {
 +                      /*
 +                       * It looks like Windows 7 supplicant tries to use
 +                       * Secure bit in msg 2/4 after having reported Michael
 +                       * MIC failure and it then rejects the 4-way handshake
 +                       * if msg 3/4 does not set Secure bit. Work around this
 +                       * by setting the Secure bit here even in the case of
 +                       * WPA if the supplicant used it first.
 +                       */
 +                      wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
 +                                      "STA used Secure bit in WPA msg 2/4 - "
 +                                      "set Secure for 3/4 as workaround");
 +                      secure = 1;
 +              }
 +      }
 +
 +      kde_len = wpa_ie_len + ieee80211w_kde_len(sm);
 +      if (gtk)
 +              kde_len += 2 + RSN_SELECTOR_LEN + 2 + gtk_len;
 +#ifdef CONFIG_IEEE80211R
 +      if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
 +              kde_len += 2 + PMKID_LEN; /* PMKR1Name into RSN IE */
 +              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;
 +
 +      pos = kde;
 +      os_memcpy(pos, wpa_ie, wpa_ie_len);
 +      pos += wpa_ie_len;
 +#ifdef CONFIG_IEEE80211R
 +      if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
 +              int res = wpa_insert_pmkid(kde, pos - kde, sm->pmk_r1_name);
 +              if (res < 0) {
 +                      wpa_printf(MSG_ERROR, "FT: Failed to insert "
 +                                 "PMKR1Name into RSN IE in EAPOL-Key data");
 +                      os_free(kde);
 +                      return;
 +              }
 +              pos += res;
 +      }
 +#endif /* CONFIG_IEEE80211R */
 +      if (gtk) {
 +              u8 hdr[2];
 +              hdr[0] = keyidx & 0x03;
 +              hdr[1] = 0;
 +              pos = wpa_add_kde(pos, RSN_KEY_DATA_GROUPKEY, hdr, 2,
 +                                gtk, gtk_len);
 +      }
 +      pos = ieee80211w_kde_add(sm, pos);
 +
 +#ifdef CONFIG_IEEE80211R
 +      if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
 +              int res;
 +              struct wpa_auth_config *conf;
 +
 +              conf = &sm->wpa_auth->conf;
 +              res = wpa_write_ftie(conf, conf->r0_key_holder,
 +                                   conf->r0_key_holder_len,
 +                                   NULL, NULL, pos, kde + kde_len - pos,
 +                                   NULL, 0);
 +              if (res < 0) {
 +                      wpa_printf(MSG_ERROR, "FT: Failed to insert FTIE "
 +                                 "into EAPOL-Key Key Data");
 +                      os_free(kde);
 +                      return;
 +              }
 +              pos += res;
 +
 +              /* TIE[ReassociationDeadline] (TU) */
 +              *pos++ = WLAN_EID_TIMEOUT_INTERVAL;
 +              *pos++ = 5;
 +              *pos++ = WLAN_TIMEOUT_REASSOC_DEADLINE;
 +              WPA_PUT_LE32(pos, conf->reassociation_deadline);
 +              pos += 4;
 +
 +              /* TIE[KeyLifetime] (seconds) */
 +              *pos++ = WLAN_EID_TIMEOUT_INTERVAL;
 +              *pos++ = 5;
 +              *pos++ = WLAN_TIMEOUT_KEY_LIFETIME;
 +              WPA_PUT_LE32(pos, conf->r0_key_lifetime * 60);
 +              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 |
 +                     WPA_KEY_INFO_ACK | WPA_KEY_INFO_INSTALL |
 +                     WPA_KEY_INFO_KEY_TYPE,
 +                     _rsc, sm->ANonce, kde, pos - kde, keyidx, encr);
 +      os_free(kde);
 +}
 +
 +
 +SM_STATE(WPA_PTK, PTKINITDONE)
 +{
 +      SM_ENTRY_MA(WPA_PTK, PTKINITDONE, wpa_ptk);
 +      sm->EAPOLKeyReceived = FALSE;
 +      if (sm->Pair) {
 +              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.tk, klen)) {
 +                      wpa_sta_disconnect(sm->wpa_auth, sm->addr);
 +                      return;
 +              }
 +              /* FIX: MLME-SetProtection.Request(TA, Tx_Rx) */
 +              sm->pairwise_set = TRUE;
 +
 +              if (sm->wpa_auth->conf.wpa_ptk_rekey) {
 +                      eloop_cancel_timeout(wpa_rekey_ptk, sm->wpa_auth, sm);
 +                      eloop_register_timeout(sm->wpa_auth->conf.
 +                                             wpa_ptk_rekey, 0, wpa_rekey_ptk,
 +                                             sm->wpa_auth, sm);
 +              }
 +
 +              if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) {
 +                      wpa_auth_set_eapol(sm->wpa_auth, sm->addr,
 +                                         WPA_EAPOL_authorized, 1);
 +              }
 +      }
 +
 +      if (0 /* IBSS == TRUE */) {
 +              sm->keycount++;
 +              if (sm->keycount == 2) {
 +                      wpa_auth_set_eapol(sm->wpa_auth, sm->addr,
 +                                         WPA_EAPOL_portValid, 1);
 +              }
 +      } else {
 +              wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_portValid,
 +                                 1);
 +      }
 +      wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_keyAvailable, 0);
 +      wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_keyDone, 1);
 +      if (sm->wpa == WPA_VERSION_WPA)
 +              sm->PInitAKeys = TRUE;
 +      else
 +              sm->has_GTK = TRUE;
 +      wpa_auth_vlogger(sm->wpa_auth, sm->addr, LOGGER_INFO,
 +                       "pairwise key handshake completed (%s)",
 +                       sm->wpa == WPA_VERSION_WPA ? "WPA" : "RSN");
 +
 +#ifdef CONFIG_IEEE80211R
 +      wpa_ft_push_pmk_r1(sm->wpa_auth, sm->addr);
 +#endif /* CONFIG_IEEE80211R */
 +}
 +
 +
 +SM_STEP(WPA_PTK)
 +{
 +      struct wpa_authenticator *wpa_auth = sm->wpa_auth;
 +
 +      if (sm->Init)
 +              SM_ENTER(WPA_PTK, INITIALIZE);
 +      else if (sm->Disconnect
 +               /* || FIX: dot11RSNAConfigSALifetime timeout */) {
 +              wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG,
 +                              "WPA_PTK: sm->Disconnect");
 +              SM_ENTER(WPA_PTK, DISCONNECT);
 +      }
 +      else if (sm->DeauthenticationRequest)
 +              SM_ENTER(WPA_PTK, DISCONNECTED);
 +      else if (sm->AuthenticationRequest)
 +              SM_ENTER(WPA_PTK, AUTHENTICATION);
 +      else if (sm->ReAuthenticationRequest)
 +              SM_ENTER(WPA_PTK, AUTHENTICATION2);
 +      else if (sm->PTKRequest)
 +              SM_ENTER(WPA_PTK, PTKSTART);
 +      else switch (sm->wpa_ptk_state) {
 +      case WPA_PTK_INITIALIZE:
 +              break;
 +      case WPA_PTK_DISCONNECT:
 +              SM_ENTER(WPA_PTK, DISCONNECTED);
 +              break;
 +      case WPA_PTK_DISCONNECTED:
 +              SM_ENTER(WPA_PTK, INITIALIZE);
 +              break;
 +      case WPA_PTK_AUTHENTICATION:
 +              SM_ENTER(WPA_PTK, AUTHENTICATION2);
 +              break;
 +      case WPA_PTK_AUTHENTICATION2:
 +              if (wpa_key_mgmt_wpa_ieee8021x(sm->wpa_key_mgmt) &&
 +                  wpa_auth_get_eapol(sm->wpa_auth, sm->addr,
 +                                     WPA_EAPOL_keyRun) > 0)
 +                      SM_ENTER(WPA_PTK, INITPMK);
 +              else if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)
 +                       /* FIX: && 802.1X::keyRun */)
 +                      SM_ENTER(WPA_PTK, INITPSK);
 +              break;
 +      case WPA_PTK_INITPMK:
 +              if (wpa_auth_get_eapol(sm->wpa_auth, sm->addr,
 +                                     WPA_EAPOL_keyAvailable) > 0)
 +                      SM_ENTER(WPA_PTK, PTKSTART);
 +              else {
 +                      wpa_auth->dot11RSNA4WayHandshakeFailures++;
 +                      wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_INFO,
 +                                      "INITPMK - keyAvailable = false");
 +                      SM_ENTER(WPA_PTK, DISCONNECT);
 +              }
 +              break;
 +      case WPA_PTK_INITPSK:
 +              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,
 +                                      "no PSK configured for the STA");
 +                      wpa_auth->dot11RSNA4WayHandshakeFailures++;
 +                      SM_ENTER(WPA_PTK, DISCONNECT);
 +              }
 +              break;
 +      case WPA_PTK_PTKSTART:
 +              if (sm->EAPOLKeyReceived && !sm->EAPOLKeyRequest &&
 +                  sm->EAPOLKeyPairwise)
 +                      SM_ENTER(WPA_PTK, PTKCALCNEGOTIATING);
 +              else if (sm->TimeoutCtr >
 +                       (int) dot11RSNAConfigPairwiseUpdateCount) {
 +                      wpa_auth->dot11RSNA4WayHandshakeFailures++;
 +                      wpa_auth_vlogger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
 +                                       "PTKSTART: Retry limit %d reached",
 +                                       dot11RSNAConfigPairwiseUpdateCount);
 +                      SM_ENTER(WPA_PTK, DISCONNECT);
 +              } else if (sm->TimeoutEvt)
 +                      SM_ENTER(WPA_PTK, PTKSTART);
 +              break;
 +      case WPA_PTK_PTKCALCNEGOTIATING:
 +              if (sm->MICVerified)
 +                      SM_ENTER(WPA_PTK, PTKCALCNEGOTIATING2);
 +              else if (sm->EAPOLKeyReceived && !sm->EAPOLKeyRequest &&
 +                       sm->EAPOLKeyPairwise)
 +                      SM_ENTER(WPA_PTK, PTKCALCNEGOTIATING);
 +              else if (sm->TimeoutEvt)
 +                      SM_ENTER(WPA_PTK, PTKSTART);
 +              break;
 +      case WPA_PTK_PTKCALCNEGOTIATING2:
 +              SM_ENTER(WPA_PTK, PTKINITNEGOTIATING);
 +              break;
 +      case WPA_PTK_PTKINITNEGOTIATING:
 +              if (sm->update_snonce)
 +                      SM_ENTER(WPA_PTK, PTKCALCNEGOTIATING);
 +              else if (sm->EAPOLKeyReceived && !sm->EAPOLKeyRequest &&
 +                       sm->EAPOLKeyPairwise && sm->MICVerified)
 +                      SM_ENTER(WPA_PTK, PTKINITDONE);
 +              else if (sm->TimeoutCtr >
 +                       (int) dot11RSNAConfigPairwiseUpdateCount) {
 +                      wpa_auth->dot11RSNA4WayHandshakeFailures++;
 +                      wpa_auth_vlogger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
 +                                       "PTKINITNEGOTIATING: Retry limit %d "
 +                                       "reached",
 +                                       dot11RSNAConfigPairwiseUpdateCount);
 +                      SM_ENTER(WPA_PTK, DISCONNECT);
 +              } else if (sm->TimeoutEvt)
 +                      SM_ENTER(WPA_PTK, PTKINITNEGOTIATING);
 +              break;
 +      case WPA_PTK_PTKINITDONE:
 +              break;
 +      }
 +}
 +
 +
 +SM_STATE(WPA_PTK_GROUP, IDLE)
 +{
 +      SM_ENTRY_MA(WPA_PTK_GROUP, IDLE, wpa_ptk_group);
 +      if (sm->Init) {
 +              /* Init flag is not cleared here, so avoid busy
 +               * loop by claiming nothing changed. */
 +              sm->changed = FALSE;
 +      }
 +      sm->GTimeoutCtr = 0;
 +}
 +
 +
 +SM_STATE(WPA_PTK_GROUP, REKEYNEGOTIATING)
 +{
 +      u8 rsc[WPA_KEY_RSC_LEN];
 +      struct wpa_group *gsm = sm->group;
 +      const u8 *kde;
 +      u8 *kde_buf = NULL, *pos, hdr[2];
 +      size_t kde_len;
 +      u8 *gtk, dummy_gtk[32];
 +
 +      SM_ENTRY_MA(WPA_PTK_GROUP, REKEYNEGOTIATING, wpa_ptk_group);
 +
 +      sm->GTimeoutCtr++;
 +      if (sm->GTimeoutCtr > (int) dot11RSNAConfigGroupUpdateCount) {
 +              /* No point in sending the EAPOL-Key - we will disconnect
 +               * immediately following this. */
 +              return;
 +      }
 +
 +      if (sm->wpa == WPA_VERSION_WPA)
 +              sm->PInitAKeys = FALSE;
 +      sm->TimeoutEvt = FALSE;
 +      /* Send EAPOL(1, 1, 1, !Pair, G, RSC, GNonce, MIC(PTK), GTK[GN]) */
 +      os_memset(rsc, 0, WPA_KEY_RSC_LEN);
 +      if (gsm->wpa_group_state == WPA_GROUP_SETKEYSDONE)
 +              wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN, rsc);
 +      wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
 +                      "sending 1/2 msg of Group Key Handshake");
 +
 +      gtk = gsm->GTK[gsm->GN - 1];
 +      if (sm->wpa_auth->conf.disable_gtk) {
 +              /*
 +               * Provide unique random GTK to each STA to prevent use
 +               * of GTK in the BSS.
 +               */
 +              if (random_get_bytes(dummy_gtk, gsm->GTK_len) < 0)
 +                      return;
 +              gtk = dummy_gtk;
 +      }
 +      if (sm->wpa == WPA_VERSION_WPA2) {
 +              kde_len = 2 + RSN_SELECTOR_LEN + 2 + gsm->GTK_len +
 +                      ieee80211w_kde_len(sm);
 +              kde_buf = os_malloc(kde_len);
 +              if (kde_buf == NULL)
 +                      return;
 +
 +              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;
 +              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, kde_len, gsm->GN, 1);
 +
 +      os_free(kde_buf);
 +}
 +
 +
 +SM_STATE(WPA_PTK_GROUP, REKEYESTABLISHED)
 +{
 +      SM_ENTRY_MA(WPA_PTK_GROUP, REKEYESTABLISHED, wpa_ptk_group);
 +      sm->EAPOLKeyReceived = FALSE;
 +      if (sm->GUpdateStationKeys)
 +              sm->group->GKeyDoneStations--;
 +      sm->GUpdateStationKeys = FALSE;
 +      sm->GTimeoutCtr = 0;
 +      /* FIX: MLME.SetProtection.Request(TA, Tx_Rx) */
 +      wpa_auth_vlogger(sm->wpa_auth, sm->addr, LOGGER_INFO,
 +                       "group key handshake completed (%s)",
 +                       sm->wpa == WPA_VERSION_WPA ? "WPA" : "RSN");
 +      sm->has_GTK = TRUE;
 +}
 +
 +
 +SM_STATE(WPA_PTK_GROUP, KEYERROR)
 +{
 +      SM_ENTRY_MA(WPA_PTK_GROUP, KEYERROR, wpa_ptk_group);
 +      if (sm->GUpdateStationKeys)
 +              sm->group->GKeyDoneStations--;
 +      sm->GUpdateStationKeys = FALSE;
 +      sm->Disconnect = TRUE;
 +}
 +
 +
 +SM_STEP(WPA_PTK_GROUP)
 +{
 +      if (sm->Init || sm->PtkGroupInit) {
 +              SM_ENTER(WPA_PTK_GROUP, IDLE);
 +              sm->PtkGroupInit = FALSE;
 +      } else switch (sm->wpa_ptk_group_state) {
 +      case WPA_PTK_GROUP_IDLE:
 +              if (sm->GUpdateStationKeys ||
 +                  (sm->wpa == WPA_VERSION_WPA && sm->PInitAKeys))
 +                      SM_ENTER(WPA_PTK_GROUP, REKEYNEGOTIATING);
 +              break;
 +      case WPA_PTK_GROUP_REKEYNEGOTIATING:
 +              if (sm->EAPOLKeyReceived && !sm->EAPOLKeyRequest &&
 +                  !sm->EAPOLKeyPairwise && sm->MICVerified)
 +                      SM_ENTER(WPA_PTK_GROUP, REKEYESTABLISHED);
 +              else if (sm->GTimeoutCtr >
 +                       (int) dot11RSNAConfigGroupUpdateCount)
 +                      SM_ENTER(WPA_PTK_GROUP, KEYERROR);
 +              else if (sm->TimeoutEvt)
 +                      SM_ENTER(WPA_PTK_GROUP, REKEYNEGOTIATING);
 +              break;
 +      case WPA_PTK_GROUP_KEYERROR:
 +              SM_ENTER(WPA_PTK_GROUP, IDLE);
 +              break;
 +      case WPA_PTK_GROUP_REKEYESTABLISHED:
 +              SM_ENTER(WPA_PTK_GROUP, IDLE);
 +              break;
 +      }
 +}
 +
 +
 +static int wpa_gtk_update(struct wpa_authenticator *wpa_auth,
 +                        struct wpa_group *group)
 +{
 +      int ret = 0;
 +
 +      os_memcpy(group->GNonce, group->Counter, WPA_NONCE_LEN);
 +      inc_byte_array(group->Counter, WPA_NONCE_LEN);
 +      if (wpa_gmk_to_gtk(group->GMK, "Group key expansion",
 +                         wpa_auth->addr, group->GNonce,
 +                         group->GTK[group->GN - 1], group->GTK_len) < 0)
 +              ret = -1;
 +      wpa_hexdump_key(MSG_DEBUG, "GTK",
 +                      group->GTK[group->GN - 1], group->GTK_len);
 +
 +#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], len) < 0)
 +                      ret = -1;
 +              wpa_hexdump_key(MSG_DEBUG, "IGTK",
 +                              group->IGTK[group->GN_igtk - 4], len);
 +      }
 +#endif /* CONFIG_IEEE80211W */
 +
 +      return ret;
 +}
 +
 +
 +static void wpa_group_gtk_init(struct wpa_authenticator *wpa_auth,
 +                             struct wpa_group *group)
 +{
 +      wpa_printf(MSG_DEBUG, "WPA: group state machine entering state "
 +                 "GTK_INIT (VLAN-ID %d)", group->vlan_id);
 +      group->changed = FALSE; /* GInit is not cleared here; avoid loop */
 +      group->wpa_group_state = WPA_GROUP_GTK_INIT;
 +
 +      /* GTK[0..N] = 0 */
 +      os_memset(group->GTK, 0, sizeof(group->GTK));
 +      group->GN = 1;
 +      group->GM = 2;
 +#ifdef CONFIG_IEEE80211W
 +      group->GN_igtk = 4;
 +      group->GM_igtk = 5;
 +#endif /* CONFIG_IEEE80211W */
 +      /* GTK[GN] = CalcGTK() */
 +      wpa_gtk_update(wpa_auth, group);
 +}
 +
 +
 +static int wpa_group_update_sta(struct wpa_state_machine *sm, void *ctx)
 +{
 +      if (ctx != NULL && ctx != sm->group)
 +              return 0;
 +
 +      if (sm->wpa_ptk_state != WPA_PTK_PTKINITDONE) {
 +              wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
 +                              "Not in PTKINITDONE; skip Group Key update");
 +              sm->GUpdateStationKeys = FALSE;
 +              return 0;
 +      }
 +      if (sm->GUpdateStationKeys) {
 +              /*
 +               * This should not really happen, so add a debug log entry.
 +               * Since we clear the GKeyDoneStations before the loop, the
 +               * station needs to be counted here anyway.
 +               */
 +              wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
 +                              "GUpdateStationKeys was already set when "
 +                              "marking station for GTK rekeying");
 +      }
 +
 +      /* Do not rekey GTK/IGTK when STA is in WNM-Sleep Mode */
 +      if (sm->is_wnmsleep)
 +              return 0;
 +
 +      sm->group->GKeyDoneStations++;
 +      sm->GUpdateStationKeys = TRUE;
 +
 +      wpa_sm_step(sm);
 +      return 0;
 +}
 +
 +
 +#ifdef CONFIG_WNM
 +/* update GTK when exiting WNM-Sleep Mode */
 +void wpa_wnmsleep_rekey_gtk(struct wpa_state_machine *sm)
 +{
 +      if (sm == NULL || sm->is_wnmsleep)
 +              return;
 +
 +      wpa_group_update_sta(sm, NULL);
 +}
 +
 +
 +void wpa_set_wnmsleep(struct wpa_state_machine *sm, int flag)
 +{
 +      if (sm)
 +              sm->is_wnmsleep = !!flag;
 +}
 +
 +
 +int wpa_wnmsleep_gtk_subelem(struct wpa_state_machine *sm, u8 *pos)
 +{
 +      struct wpa_group *gsm = sm->group;
 +      u8 *start = pos;
 +
 +      /*
 +       * GTK subelement:
 +       * Sub-elem ID[1] | Length[1] | Key Info[2] | Key Length[1] | RSC[8] |
 +       * Key[5..32]
 +       */
 +      *pos++ = WNM_SLEEP_SUBELEM_GTK;
 +      *pos++ = 11 + gsm->GTK_len;
 +      /* Key ID in B0-B1 of Key Info */
 +      WPA_PUT_LE16(pos, gsm->GN & 0x03);
 +      pos += 2;
 +      *pos++ = gsm->GTK_len;
 +      if (wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN, pos) != 0)
 +              return 0;
 +      pos += 8;
 +      os_memcpy(pos, gsm->GTK[gsm->GN - 1], gsm->GTK_len);
 +      pos += gsm->GTK_len;
 +
 +      wpa_printf(MSG_DEBUG, "WNM: GTK Key ID %u in WNM-Sleep Mode exit",
 +                 gsm->GN);
 +      wpa_hexdump_key(MSG_DEBUG, "WNM: GTK in WNM-Sleep Mode exit",
 +                      gsm->GTK[gsm->GN - 1], gsm->GTK_len);
 +
 +      return pos - start;
 +}
 +
 +
 +#ifdef CONFIG_IEEE80211W
 +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 + 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], 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], len);
 +
 +      return pos - start;
 +}
 +#endif /* CONFIG_IEEE80211W */
 +#endif /* CONFIG_WNM */
 +
 +
 +static void wpa_group_setkeys(struct wpa_authenticator *wpa_auth,
 +                            struct wpa_group *group)
 +{
 +      int tmp;
 +
 +      wpa_printf(MSG_DEBUG, "WPA: group state machine entering state "
 +                 "SETKEYS (VLAN-ID %d)", group->vlan_id);
 +      group->changed = TRUE;
 +      group->wpa_group_state = WPA_GROUP_SETKEYS;
 +      group->GTKReKey = FALSE;
 +      tmp = group->GM;
 +      group->GM = group->GN;
 +      group->GN = tmp;
 +#ifdef CONFIG_IEEE80211W
 +      tmp = group->GM_igtk;
 +      group->GM_igtk = group->GN_igtk;
 +      group->GN_igtk = tmp;
 +#endif /* CONFIG_IEEE80211W */
 +      /* "GKeyDoneStations = GNoStations" is done in more robust way by
 +       * counting the STAs that are marked with GUpdateStationKeys instead of
 +       * including all STAs that could be in not-yet-completed state. */
 +      wpa_gtk_update(wpa_auth, group);
 +
 +      if (group->GKeyDoneStations) {
 +              wpa_printf(MSG_DEBUG, "wpa_group_setkeys: Unexpected "
 +                         "GKeyDoneStations=%d when starting new GTK rekey",
 +                         group->GKeyDoneStations);
 +              group->GKeyDoneStations = 0;
 +      }
 +      wpa_auth_for_each_sta(wpa_auth, wpa_group_update_sta, group);
 +      wpa_printf(MSG_DEBUG, "wpa_group_setkeys: GKeyDoneStations=%d",
 +                 group->GKeyDoneStations);
 +}
 +
 +
 +static int wpa_group_config_group_keys(struct wpa_authenticator *wpa_auth,
 +                                     struct wpa_group *group)
 +{
 +      int ret = 0;
 +
 +      if (wpa_auth_set_key(wpa_auth, group->vlan_id,
 +                           wpa_cipher_to_alg(wpa_auth->conf.wpa_group),
 +                           broadcast_ether_addr, group->GN,
 +                           group->GTK[group->GN - 1], group->GTK_len) < 0)
 +              ret = -1;
 +
 +#ifdef CONFIG_IEEE80211W
 +      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)
 +{
 +      wpa_printf(MSG_DEBUG, "WPA: group state machine entering state "
 +                 "SETKEYSDONE (VLAN-ID %d)", group->vlan_id);
 +      group->changed = TRUE;
 +      group->wpa_group_state = WPA_GROUP_SETKEYSDONE;
 +
 +      if (wpa_group_config_group_keys(wpa_auth, group) < 0) {
 +              wpa_group_fatal_failure(wpa_auth, group);
 +              return -1;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +static void wpa_group_sm_step(struct wpa_authenticator *wpa_auth,
 +                            struct wpa_group *group)
 +{
 +      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);
 +      } else if (group->wpa_group_state == WPA_GROUP_SETKEYSDONE &&
 +                 group->GTKReKey) {
 +              wpa_group_setkeys(wpa_auth, group);
 +      } else if (group->wpa_group_state == WPA_GROUP_SETKEYS) {
 +              if (group->GKeyDoneStations == 0)
 +                      wpa_group_setkeysdone(wpa_auth, group);
 +              else if (group->GTKReKey)
 +                      wpa_group_setkeys(wpa_auth, group);
 +      }
 +}
 +
 +
 +static int wpa_sm_step(struct wpa_state_machine *sm)
 +{
 +      if (sm == NULL)
 +              return 0;
 +
 +      if (sm->in_step_loop) {
 +              /* This should not happen, but if it does, make sure we do not
 +               * end up freeing the state machine too early by exiting the
 +               * recursive call. */
 +              wpa_printf(MSG_ERROR, "WPA: wpa_sm_step() called recursively");
 +              return 0;
 +      }
 +
 +      sm->in_step_loop = 1;
 +      do {
 +              if (sm->pending_deinit)
 +                      break;
 +
 +              sm->changed = FALSE;
 +              sm->wpa_auth->group->changed = FALSE;
 +
 +              SM_STEP_RUN(WPA_PTK);
 +              if (sm->pending_deinit)
 +                      break;
 +              SM_STEP_RUN(WPA_PTK_GROUP);
 +              if (sm->pending_deinit)
 +                      break;
 +              wpa_group_sm_step(sm->wpa_auth, sm->group);
 +      } while (sm->changed || sm->wpa_auth->group->changed);
 +      sm->in_step_loop = 0;
 +
 +      if (sm->pending_deinit) {
 +              wpa_printf(MSG_DEBUG, "WPA: Completing pending STA state "
 +                         "machine deinit for " MACSTR, MAC2STR(sm->addr));
 +              wpa_free_sta_sm(sm);
 +              return 1;
 +      }
 +      return 0;
 +}
 +
 +
 +static void wpa_sm_call_step(void *eloop_ctx, void *timeout_ctx)
 +{
 +      struct wpa_state_machine *sm = eloop_ctx;
 +      wpa_sm_step(sm);
 +}
 +
 +
 +void wpa_auth_sm_notify(struct wpa_state_machine *sm)
 +{
 +      if (sm == NULL)
 +              return;
 +      eloop_register_timeout(0, 0, wpa_sm_call_step, sm, NULL);
 +}
 +
 +
 +void wpa_gtk_rekey(struct wpa_authenticator *wpa_auth)
 +{
 +      int tmp, i;
 +      struct wpa_group *group;
 +
 +      if (wpa_auth == NULL)
 +              return;
 +
 +      group = wpa_auth->group;
 +
 +      for (i = 0; i < 2; i++) {
 +              tmp = group->GM;
 +              group->GM = group->GN;
 +              group->GN = tmp;
 +#ifdef CONFIG_IEEE80211W
 +              tmp = group->GM_igtk;
 +              group->GM_igtk = group->GN_igtk;
 +              group->GN_igtk = tmp;
 +#endif /* CONFIG_IEEE80211W */
 +              wpa_gtk_update(wpa_auth, group);
 +              wpa_group_config_group_keys(wpa_auth, group);
 +      }
 +}
 +
 +
-       return bool ? "TRUE" : "FALSE";
++static const char * wpa_bool_txt(int val)
 +{
++      return val ? "TRUE" : "FALSE";
 +}
 +
 +
 +#define RSN_SUITE "%02x-%02x-%02x-%d"
 +#define RSN_SUITE_ARG(s) \
 +((s) >> 24) & 0xff, ((s) >> 16) & 0xff, ((s) >> 8) & 0xff, (s) & 0xff
 +
 +int wpa_get_mib(struct wpa_authenticator *wpa_auth, char *buf, size_t buflen)
 +{
 +      int len = 0, ret;
 +      char pmkid_txt[PMKID_LEN * 2 + 1];
 +#ifdef CONFIG_RSN_PREAUTH
 +      const int preauth = 1;
 +#else /* CONFIG_RSN_PREAUTH */
 +      const int preauth = 0;
 +#endif /* CONFIG_RSN_PREAUTH */
 +
 +      if (wpa_auth == NULL)
 +              return len;
 +
 +      ret = os_snprintf(buf + len, buflen - len,
 +                        "dot11RSNAOptionImplemented=TRUE\n"
 +                        "dot11RSNAPreauthenticationImplemented=%s\n"
 +                        "dot11RSNAEnabled=%s\n"
 +                        "dot11RSNAPreauthenticationEnabled=%s\n",
 +                        wpa_bool_txt(preauth),
 +                        wpa_bool_txt(wpa_auth->conf.wpa & WPA_PROTO_RSN),
 +                        wpa_bool_txt(wpa_auth->conf.rsn_preauth));
 +      if (os_snprintf_error(buflen - len, ret))
 +              return len;
 +      len += ret;
 +
 +      wpa_snprintf_hex(pmkid_txt, sizeof(pmkid_txt),
 +                       wpa_auth->dot11RSNAPMKIDUsed, PMKID_LEN);
 +
 +      ret = os_snprintf(
 +              buf + len, buflen - len,
 +              "dot11RSNAConfigVersion=%u\n"
 +              "dot11RSNAConfigPairwiseKeysSupported=9999\n"
 +              /* FIX: dot11RSNAConfigGroupCipher */
 +              /* FIX: dot11RSNAConfigGroupRekeyMethod */
 +              /* FIX: dot11RSNAConfigGroupRekeyTime */
 +              /* FIX: dot11RSNAConfigGroupRekeyPackets */
 +              "dot11RSNAConfigGroupRekeyStrict=%u\n"
 +              "dot11RSNAConfigGroupUpdateCount=%u\n"
 +              "dot11RSNAConfigPairwiseUpdateCount=%u\n"
 +              "dot11RSNAConfigGroupCipherSize=%u\n"
 +              "dot11RSNAConfigPMKLifetime=%u\n"
 +              "dot11RSNAConfigPMKReauthThreshold=%u\n"
 +              "dot11RSNAConfigNumberOfPTKSAReplayCounters=0\n"
 +              "dot11RSNAConfigSATimeout=%u\n"
 +              "dot11RSNAAuthenticationSuiteSelected=" RSN_SUITE "\n"
 +              "dot11RSNAPairwiseCipherSelected=" RSN_SUITE "\n"
 +              "dot11RSNAGroupCipherSelected=" RSN_SUITE "\n"
 +              "dot11RSNAPMKIDUsed=%s\n"
 +              "dot11RSNAAuthenticationSuiteRequested=" RSN_SUITE "\n"
 +              "dot11RSNAPairwiseCipherRequested=" RSN_SUITE "\n"
 +              "dot11RSNAGroupCipherRequested=" RSN_SUITE "\n"
 +              "dot11RSNATKIPCounterMeasuresInvoked=%u\n"
 +              "dot11RSNA4WayHandshakeFailures=%u\n"
 +              "dot11RSNAConfigNumberOfGTKSAReplayCounters=0\n",
 +              RSN_VERSION,
 +              !!wpa_auth->conf.wpa_strict_rekey,
 +              dot11RSNAConfigGroupUpdateCount,
 +              dot11RSNAConfigPairwiseUpdateCount,
 +              wpa_cipher_key_len(wpa_auth->conf.wpa_group) * 8,
 +              dot11RSNAConfigPMKLifetime,
 +              dot11RSNAConfigPMKReauthThreshold,
 +              dot11RSNAConfigSATimeout,
 +              RSN_SUITE_ARG(wpa_auth->dot11RSNAAuthenticationSuiteSelected),
 +              RSN_SUITE_ARG(wpa_auth->dot11RSNAPairwiseCipherSelected),
 +              RSN_SUITE_ARG(wpa_auth->dot11RSNAGroupCipherSelected),
 +              pmkid_txt,
 +              RSN_SUITE_ARG(wpa_auth->dot11RSNAAuthenticationSuiteRequested),
 +              RSN_SUITE_ARG(wpa_auth->dot11RSNAPairwiseCipherRequested),
 +              RSN_SUITE_ARG(wpa_auth->dot11RSNAGroupCipherRequested),
 +              wpa_auth->dot11RSNATKIPCounterMeasuresInvoked,
 +              wpa_auth->dot11RSNA4WayHandshakeFailures);
 +      if (os_snprintf_error(buflen - len, ret))
 +              return len;
 +      len += ret;
 +
 +      /* TODO: dot11RSNAConfigPairwiseCiphersTable */
 +      /* TODO: dot11RSNAConfigAuthenticationSuitesTable */
 +
 +      /* Private MIB */
 +      ret = os_snprintf(buf + len, buflen - len, "hostapdWPAGroupState=%d\n",
 +                        wpa_auth->group->wpa_group_state);
 +      if (os_snprintf_error(buflen - len, ret))
 +              return len;
 +      len += ret;
 +
 +      return len;
 +}
 +
 +
 +int wpa_get_mib_sta(struct wpa_state_machine *sm, char *buf, size_t buflen)
 +{
 +      int len = 0, ret;
 +      u32 pairwise = 0;
 +
 +      if (sm == NULL)
 +              return 0;
 +
 +      /* TODO: FF-FF-FF-FF-FF-FF entry for broadcast/multicast stats */
 +
 +      /* dot11RSNAStatsEntry */
 +
 +      pairwise = wpa_cipher_to_suite(sm->wpa == WPA_VERSION_WPA2 ?
 +                                     WPA_PROTO_RSN : WPA_PROTO_WPA,
 +                                     sm->pairwise);
 +      if (pairwise == 0)
 +              return 0;
 +
 +      ret = os_snprintf(
 +              buf + len, buflen - len,
 +              /* TODO: dot11RSNAStatsIndex */
 +              "dot11RSNAStatsSTAAddress=" MACSTR "\n"
 +              "dot11RSNAStatsVersion=1\n"
 +              "dot11RSNAStatsSelectedPairwiseCipher=" RSN_SUITE "\n"
 +              /* TODO: dot11RSNAStatsTKIPICVErrors */
 +              "dot11RSNAStatsTKIPLocalMICFailures=%u\n"
 +              "dot11RSNAStatsTKIPRemoteMICFailures=%u\n"
 +              /* TODO: dot11RSNAStatsCCMPReplays */
 +              /* TODO: dot11RSNAStatsCCMPDecryptErrors */
 +              /* TODO: dot11RSNAStatsTKIPReplays */,
 +              MAC2STR(sm->addr),
 +              RSN_SUITE_ARG(pairwise),
 +              sm->dot11RSNAStatsTKIPLocalMICFailures,
 +              sm->dot11RSNAStatsTKIPRemoteMICFailures);
 +      if (os_snprintf_error(buflen - len, ret))
 +              return len;
 +      len += ret;
 +
 +      /* Private MIB */
 +      ret = os_snprintf(buf + len, buflen - len,
 +                        "hostapdWPAPTKState=%d\n"
 +                        "hostapdWPAPTKGroupState=%d\n",
 +                        sm->wpa_ptk_state,
 +                        sm->wpa_ptk_group_state);
 +      if (os_snprintf_error(buflen - len, ret))
 +              return len;
 +      len += ret;
 +
 +      return len;
 +}
 +
 +
 +void wpa_auth_countermeasures_start(struct wpa_authenticator *wpa_auth)
 +{
 +      if (wpa_auth)
 +              wpa_auth->dot11RSNATKIPCounterMeasuresInvoked++;
 +}
 +
 +
 +int wpa_auth_pairwise_set(struct wpa_state_machine *sm)
 +{
 +      return sm && sm->pairwise_set;
 +}
 +
 +
 +int wpa_auth_get_pairwise(struct wpa_state_machine *sm)
 +{
 +      return sm->pairwise;
 +}
 +
 +
 +int wpa_auth_sta_key_mgmt(struct wpa_state_machine *sm)
 +{
 +      if (sm == NULL)
 +              return -1;
 +      return sm->wpa_key_mgmt;
 +}
 +
 +
 +int wpa_auth_sta_wpa_version(struct wpa_state_machine *sm)
 +{
 +      if (sm == NULL)
 +              return 0;
 +      return sm->wpa;
 +}
 +
 +
 +int wpa_auth_sta_clear_pmksa(struct wpa_state_machine *sm,
 +                           struct rsn_pmksa_cache_entry *entry)
 +{
 +      if (sm == NULL || sm->pmksa != entry)
 +              return -1;
 +      sm->pmksa = NULL;
 +      return 0;
 +}
 +
 +
 +struct rsn_pmksa_cache_entry *
 +wpa_auth_sta_get_pmksa(struct wpa_state_machine *sm)
 +{
 +      return sm ? sm->pmksa : NULL;
 +}
 +
 +
 +void wpa_auth_sta_local_mic_failure_report(struct wpa_state_machine *sm)
 +{
 +      if (sm)
 +              sm->dot11RSNAStatsTKIPLocalMICFailures++;
 +}
 +
 +
 +const u8 * wpa_auth_get_wpa_ie(struct wpa_authenticator *wpa_auth, size_t *len)
 +{
 +      if (wpa_auth == NULL)
 +              return NULL;
 +      *len = wpa_auth->wpa_ie_len;
 +      return wpa_auth->wpa_ie;
 +}
 +
 +
 +int wpa_auth_pmksa_add(struct wpa_state_machine *sm, const u8 *pmk,
 +                     int session_timeout, struct eapol_state_machine *eapol)
 +{
 +      if (sm == NULL || sm->wpa != WPA_VERSION_WPA2 ||
 +          sm->wpa_auth->conf.disable_pmksa_caching)
 +              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;
 +
 +      return -1;
 +}
 +
 +
 +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)
 +{
 +      if (wpa_auth == NULL)
 +              return -1;
 +
 +      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;
 +
 +      return -1;
 +}
 +
 +
 +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)
 +{
 +      struct rsn_pmksa_cache_entry *pmksa;
 +
 +      if (wpa_auth == NULL || wpa_auth->pmksa == NULL)
 +              return;
 +      pmksa = pmksa_cache_auth_get(wpa_auth->pmksa, sta_addr, NULL);
 +      if (pmksa) {
 +              wpa_printf(MSG_DEBUG, "WPA: Remove PMKSA cache entry for "
 +                         MACSTR " based on request", MAC2STR(sta_addr));
 +              pmksa_cache_free_entry(wpa_auth->pmksa, pmksa);
 +      }
 +}
 +
 +
++/*
++ * Remove and free the group from wpa_authenticator. This is triggered by a
++ * callback to make sure nobody is currently iterating the group list while it
++ * gets modified.
++ */
++static void wpa_group_free(struct wpa_authenticator *wpa_auth,
++                         struct wpa_group *group)
++{
++      struct wpa_group *prev = wpa_auth->group;
++
++      wpa_printf(MSG_DEBUG, "WPA: Remove group state machine for VLAN-ID %d",
++                 group->vlan_id);
++
++      while (prev) {
++              if (prev->next == group) {
++                      /* This never frees the special first group as needed */
++                      prev->next = group->next;
++                      os_free(group);
++                      break;
++              }
++              prev = prev->next;
++      }
++
++}
++
++
++/* Increase the reference counter for group */
++static void wpa_group_get(struct wpa_authenticator *wpa_auth,
++                        struct wpa_group *group)
++{
++      /* Skip the special first group */
++      if (wpa_auth->group == group)
++              return;
++
++      group->references++;
++}
++
++
++/* Decrease the reference counter and maybe free the group */
++static void wpa_group_put(struct wpa_authenticator *wpa_auth,
++                        struct wpa_group *group)
++{
++      /* Skip the special first group */
++      if (wpa_auth->group == group)
++              return;
++
++      group->references--;
++      if (group->references)
++              return;
++      wpa_group_free(wpa_auth, group);
++}
++
++
++/*
++ * Add a group that has its references counter set to zero. Caller needs to
++ * call wpa_group_get() on the return value to mark the entry in use.
++ */
 +static struct wpa_group *
 +wpa_auth_add_group(struct wpa_authenticator *wpa_auth, int vlan_id)
 +{
 +      struct wpa_group *group;
 +
 +      if (wpa_auth == NULL || wpa_auth->group == NULL)
 +              return NULL;
 +
 +      wpa_printf(MSG_DEBUG, "WPA: Add group state machine for VLAN-ID %d",
 +                 vlan_id);
 +      group = wpa_group_init(wpa_auth, vlan_id, 0);
 +      if (group == NULL)
 +              return NULL;
 +
 +      group->next = wpa_auth->group->next;
 +      wpa_auth->group->next = group;
 +
 +      return group;
 +}
 +
 +
 +int wpa_auth_sta_set_vlan(struct wpa_state_machine *sm, int vlan_id)
 +{
 +      struct wpa_group *group;
 +
 +      if (sm == NULL || sm->wpa_auth == NULL)
 +              return 0;
 +
 +      group = sm->wpa_auth->group;
 +      while (group) {
 +              if (group->vlan_id == vlan_id)
 +                      break;
 +              group = group->next;
 +      }
 +
 +      if (group == NULL) {
 +              group = wpa_auth_add_group(sm->wpa_auth, vlan_id);
 +              if (group == NULL)
 +                      return -1;
 +      }
 +
 +      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);
 +
++      wpa_group_get(sm->wpa_auth, group);
++      wpa_group_put(sm->wpa_auth, sm->group);
 +      sm->group = group;
++
 +      return 0;
 +}
 +
 +
 +void wpa_auth_eapol_key_tx_status(struct wpa_authenticator *wpa_auth,
 +                                struct wpa_state_machine *sm, int ack)
 +{
 +      if (wpa_auth == NULL || sm == NULL)
 +              return;
 +      wpa_printf(MSG_DEBUG, "WPA: EAPOL-Key TX status for STA " MACSTR
 +                 " ack=%d", MAC2STR(sm->addr), ack);
 +      if (sm->pending_1_of_4_timeout && ack) {
 +              /*
 +               * Some deployed supplicant implementations update their SNonce
 +               * for each EAPOL-Key 2/4 message even within the same 4-way
 +               * handshake and then fail to use the first SNonce when
 +               * deriving the PTK. This results in unsuccessful 4-way
 +               * handshake whenever the relatively short initial timeout is
 +               * reached and EAPOL-Key 1/4 is retransmitted. Try to work
 +               * around this by increasing the timeout now that we know that
 +               * the station has received the frame.
 +               */
 +              int timeout_ms = eapol_key_timeout_subseq;
 +              wpa_printf(MSG_DEBUG, "WPA: Increase initial EAPOL-Key 1/4 "
 +                         "timeout by %u ms because of acknowledged frame",
 +                         timeout_ms);
 +              eloop_cancel_timeout(wpa_send_eapol_timeout, wpa_auth, sm);
 +              eloop_register_timeout(timeout_ms / 1000,
 +                                     (timeout_ms % 1000) * 1000,
 +                                     wpa_send_eapol_timeout, wpa_auth, sm);
 +      }
 +}
 +
 +
 +int wpa_auth_uses_sae(struct wpa_state_machine *sm)
 +{
 +      if (sm == NULL)
 +              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 2788e657435d520a093292540977d4eefaf351a5,0000000000000000000000000000000000000000..fd04f169433af4ed108f213f56c702802e94135e
mode 100644,000000..100644
--- /dev/null
@@@ -1,323 -1,0 +1,328 @@@
-  * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
 +/*
 + * hostapd - IEEE 802.11i-2004 / WPA Authenticator
- #define SSID_LEN 32
-       u8 ssid[SSID_LEN];
++ * 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 WPA_AUTH_H
 +#define WPA_AUTH_H
 +
 +#include "common/defs.h"
 +#include "common/eapol_common.h"
 +#include "common/wpa_common.h"
++#include "common/ieee802_11_defs.h"
++
++#define MAX_OWN_IE_OVERRIDE 256
 +
 +#ifdef _MSC_VER
 +#pragma pack(push, 1)
 +#endif /* _MSC_VER */
 +
 +/* IEEE Std 802.11r-2008, 11A.10.3 - Remote request/response frame definition
 + */
 +struct ft_rrb_frame {
 +      u8 frame_type; /* RSN_REMOTE_FRAME_TYPE_FT_RRB */
 +      u8 packet_type; /* FT_PACKET_REQUEST/FT_PACKET_RESPONSE */
 +      le16 action_length; /* little endian length of action_frame */
 +      u8 ap_address[ETH_ALEN];
 +      /*
 +       * Followed by action_length bytes of FT Action frame (from Category
 +       * field to the end of Action Frame body.
 +       */
 +} STRUCT_PACKED;
 +
 +#define RSN_REMOTE_FRAME_TYPE_FT_RRB 1
 +
 +#define FT_PACKET_REQUEST 0
 +#define FT_PACKET_RESPONSE 1
 +/* Vendor-specific types for R0KH-R1KH protocol; not defined in 802.11r */
 +#define FT_PACKET_R0KH_R1KH_PULL 200
 +#define FT_PACKET_R0KH_R1KH_RESP 201
 +#define FT_PACKET_R0KH_R1KH_PUSH 202
 +
 +#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 */
 +      u8 packet_type; /* FT_PACKET_R0KH_R1KH_PULL */
 +      le16 data_length; /* little endian length of data (44) */
 +      u8 ap_address[ETH_ALEN];
 +
 +      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];
 +      u8 pad[4]; /* 8-octet boundary for AES key wrap */
 +      u8 key_wrap_extra[8];
 +} STRUCT_PACKED;
 +
 +struct ft_r0kh_r1kh_resp_frame {
 +      u8 frame_type; /* RSN_REMOTE_FRAME_TYPE_FT_RRB */
 +      u8 packet_type; /* FT_PACKET_R0KH_R1KH_RESP */
 +      le16 data_length; /* little endian length of data (76) */
 +      u8 ap_address[ETH_ALEN];
 +
 +      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];
 +      u8 pmk_r1_name[WPA_PMK_NAME_LEN];
 +      le16 pairwise;
 +      u8 pad[2]; /* 8-octet boundary for AES key wrap */
 +      u8 key_wrap_extra[8];
 +} STRUCT_PACKED;
 +
 +struct ft_r0kh_r1kh_push_frame {
 +      u8 frame_type; /* RSN_REMOTE_FRAME_TYPE_FT_RRB */
 +      u8 packet_type; /* FT_PACKET_R0KH_R1KH_PUSH */
 +      le16 data_length; /* little endian length of data (88) */
 +      u8 ap_address[ETH_ALEN];
 +
 +      /* Encrypted with AES key-wrap */
 +      u8 timestamp[4]; /* current time in seconds since unix epoch, little
 +                        * endian */
 +      u8 r1kh_id[FT_R1KH_ID_LEN];
 +      u8 s1kh_id[ETH_ALEN];
 +      u8 pmk_r0_name[WPA_PMK_NAME_LEN];
 +      u8 pmk_r1[PMK_LEN];
 +      u8 pmk_r1_name[WPA_PMK_NAME_LEN];
 +      le16 pairwise;
 +      u8 pad[6]; /* 8-octet boundary for AES key wrap */
 +      u8 key_wrap_extra[8];
 +} STRUCT_PACKED;
 +
 +#ifdef _MSC_VER
 +#pragma pack(pop)
 +#endif /* _MSC_VER */
 +
 +
 +/* per STA state machine data */
 +
 +struct wpa_authenticator;
 +struct wpa_state_machine;
 +struct rsn_pmksa_cache_entry;
 +struct eapol_state_machine;
 +
 +
 +struct ft_remote_r0kh {
 +      struct ft_remote_r0kh *next;
 +      u8 addr[ETH_ALEN];
 +      u8 id[FT_R0KH_ID_MAX_LEN];
 +      size_t id_len;
 +      u8 key[16];
 +};
 +
 +
 +struct ft_remote_r1kh {
 +      struct ft_remote_r1kh *next;
 +      u8 addr[ETH_ALEN];
 +      u8 id[FT_R1KH_ID_LEN];
 +      u8 key[16];
 +};
 +
 +
 +struct wpa_auth_config {
 +      int wpa;
 +      int wpa_key_mgmt;
 +      int wpa_pairwise;
 +      int wpa_group;
 +      int wpa_group_rekey;
 +      int wpa_strict_rekey;
 +      int wpa_gmk_rekey;
 +      int wpa_ptk_rekey;
 +      int rsn_pairwise;
 +      int rsn_preauth;
 +      int eapol_version;
 +      int peerkey;
 +      int wmm_enabled;
 +      int wmm_uapsd;
 +      int disable_pmksa_caching;
 +      int okc;
 +      int tx_status;
 +#ifdef CONFIG_IEEE80211W
 +      enum mfp_options ieee80211w;
 +      int group_mgmt_cipher;
 +#endif /* CONFIG_IEEE80211W */
 +#ifdef CONFIG_IEEE80211R
- typedef enum {
++      u8 ssid[SSID_MAX_LEN];
 +      size_t ssid_len;
 +      u8 mobility_domain[MOBILITY_DOMAIN_ID_LEN];
 +      u8 r0_key_holder[FT_R0KH_ID_MAX_LEN];
 +      size_t r0_key_holder_len;
 +      u8 r1_key_holder[FT_R1KH_ID_LEN];
 +      u32 r0_key_lifetime;
 +      u32 reassociation_deadline;
 +      struct ft_remote_r0kh *r0kh_list;
 +      struct ft_remote_r1kh *r1kh_list;
 +      int pmk_r1_push;
 +      int ft_over_ds;
 +#endif /* CONFIG_IEEE80211R */
 +      int disable_gtk;
 +      int ap_mlme;
 +#ifdef CONFIG_TESTING_OPTIONS
 +      double corrupt_gtk_rekey_mic_probability;
++      u8 own_ie_override[MAX_OWN_IE_OVERRIDE];
++      size_t own_ie_override_len;
 +#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 {
 +      LOGGER_DEBUG, LOGGER_INFO, LOGGER_WARNING
 +} logger_level;
 +
 +typedef enum {
 +      WPA_EAPOL_portEnabled, WPA_EAPOL_portValid, WPA_EAPOL_authorized,
 +      WPA_EAPOL_portControl_Auto, WPA_EAPOL_keyRun, WPA_EAPOL_keyAvailable,
 +      WPA_EAPOL_keyDone, WPA_EAPOL_inc_EapolFramesTx
 +} wpa_eapol_variable;
 +
 +struct wpa_auth_callbacks {
 +      void *ctx;
 +      void (*logger)(void *ctx, const u8 *addr, logger_level level,
 +                     const char *txt);
 +      void (*disconnect)(void *ctx, const u8 *addr, u16 reason);
 +      int (*mic_failure_report)(void *ctx, const u8 *addr);
++      void (*psk_failure_report)(void *ctx, const u8 *addr);
 +      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 *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);
 +      int (*get_seqnum)(void *ctx, const u8 *addr, int idx, u8 *seq);
 +      int (*send_eapol)(void *ctx, const u8 *addr, const u8 *data,
 +                        size_t data_len, int encrypt);
 +      int (*for_each_sta)(void *ctx, int (*cb)(struct wpa_state_machine *sm,
 +                                               void *ctx), void *cb_ctx);
 +      int (*for_each_auth)(void *ctx, int (*cb)(struct wpa_authenticator *a,
 +                                                void *ctx), void *cb_ctx);
 +      int (*send_ether)(void *ctx, const u8 *dst, u16 proto, const u8 *data,
 +                        size_t data_len);
 +#ifdef CONFIG_IEEE80211R
 +      struct wpa_state_machine * (*add_sta)(void *ctx, const u8 *sta_addr);
 +      int (*send_ft_action)(void *ctx, const u8 *dst,
 +                            const u8 *data, size_t data_len);
 +      int (*add_tspec)(void *ctx, const u8 *sta_addr, u8 *tspec_ie,
 +                       size_t tspec_ielen);
 +#endif /* CONFIG_IEEE80211R */
 +#ifdef CONFIG_MESH
 +      int (*start_ampe)(void *ctx, const u8 *sta_addr);
 +#endif /* CONFIG_MESH */
 +};
 +
 +struct wpa_authenticator * wpa_init(const u8 *addr,
 +                                  struct wpa_auth_config *conf,
 +                                  struct wpa_auth_callbacks *cb);
 +int wpa_init_keys(struct wpa_authenticator *wpa_auth);
 +void wpa_deinit(struct wpa_authenticator *wpa_auth);
 +int wpa_reconfig(struct wpa_authenticator *wpa_auth,
 +               struct wpa_auth_config *conf);
 +
 +enum {
 +      WPA_IE_OK, WPA_INVALID_IE, WPA_INVALID_GROUP, WPA_INVALID_PAIRWISE,
 +      WPA_INVALID_AKMP, WPA_NOT_ENABLED, WPA_ALLOC_FAIL,
 +      WPA_MGMT_FRAME_PROTECTION_VIOLATION, WPA_INVALID_MGMT_GROUP_CIPHER,
 +      WPA_INVALID_MDIE, WPA_INVALID_PROTO
 +};
 +      
 +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,
 +                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);
 +void wpa_auth_sta_deinit(struct wpa_state_machine *sm);
 +void wpa_receive(struct wpa_authenticator *wpa_auth,
 +               struct wpa_state_machine *sm,
 +               u8 *data, size_t data_len);
- } wpa_event;
++enum wpa_event {
 +      WPA_AUTH, WPA_ASSOC, WPA_DISASSOC, WPA_DEAUTH, WPA_REAUTH,
 +      WPA_REAUTH_EAPOL, WPA_ASSOC_FT
- int wpa_auth_sm_event(struct wpa_state_machine *sm, wpa_event event);
++};
 +void wpa_remove_ptk(struct wpa_state_machine *sm);
++int wpa_auth_sm_event(struct wpa_state_machine *sm, enum wpa_event event);
 +void wpa_auth_sm_notify(struct wpa_state_machine *sm);
 +void wpa_gtk_rekey(struct wpa_authenticator *wpa_auth);
 +int wpa_get_mib(struct wpa_authenticator *wpa_auth, char *buf, size_t buflen);
 +int wpa_get_mib_sta(struct wpa_state_machine *sm, char *buf, size_t buflen);
 +void wpa_auth_countermeasures_start(struct wpa_authenticator *wpa_auth);
 +int wpa_auth_pairwise_set(struct wpa_state_machine *sm);
 +int wpa_auth_get_pairwise(struct wpa_state_machine *sm);
 +int wpa_auth_sta_key_mgmt(struct wpa_state_machine *sm);
 +int wpa_auth_sta_wpa_version(struct wpa_state_machine *sm);
 +int wpa_auth_sta_clear_pmksa(struct wpa_state_machine *sm,
 +                           struct rsn_pmksa_cache_entry *entry);
 +struct rsn_pmksa_cache_entry *
 +wpa_auth_sta_get_pmksa(struct wpa_state_machine *sm);
 +void wpa_auth_sta_local_mic_failure_report(struct wpa_state_machine *sm);
 +const u8 * wpa_auth_get_wpa_ie(struct wpa_authenticator *wpa_auth,
 +                             size_t *len);
 +int wpa_auth_pmksa_add(struct wpa_state_machine *sm, const u8 *pmk,
 +                     int session_timeout, struct eapol_state_machine *eapol);
 +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);
 +void wpa_auth_eapol_key_tx_status(struct wpa_authenticator *wpa_auth,
 +                                struct wpa_state_machine *sm, int ack);
 +
 +#ifdef CONFIG_IEEE80211R
 +u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos,
 +                               size_t max_len, int auth_alg,
 +                               const u8 *req_ies, size_t req_ies_len);
 +void wpa_ft_process_auth(struct wpa_state_machine *sm, const u8 *bssid,
 +                       u16 auth_transaction, const u8 *ies, size_t ies_len,
 +                       void (*cb)(void *ctx, const u8 *dst, const u8 *bssid,
 +                                  u16 auth_transaction, u16 resp,
 +                                  const u8 *ies, size_t ies_len),
 +                       void *ctx);
 +u16 wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies,
 +                          size_t ies_len);
 +int wpa_ft_action_rx(struct wpa_state_machine *sm, const u8 *data, size_t len);
 +int wpa_ft_rrb_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr,
 +                const u8 *data, size_t data_len);
 +void wpa_ft_push_pmk_r1(struct wpa_authenticator *wpa_auth, const u8 *addr);
 +#endif /* CONFIG_IEEE80211R */
 +
 +void wpa_wnmsleep_rekey_gtk(struct wpa_state_machine *sm);
 +void wpa_set_wnmsleep(struct wpa_state_machine *sm, int flag);
 +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 ef3249a3eb9b4a89cfec82d25fcd62b51597395f,0000000000000000000000000000000000000000..eeaffbf635166d20767e38996c951c2bcf245d56
mode 100644,000000..100644
--- /dev/null
@@@ -1,1791 -1,0 +1,1792 @@@
- #ifdef NEED_AP_MLME
-       if (parse.wmm_tspec && sm->wpa_auth->conf.ap_mlme) {
 +/*
 + * hostapd - IEEE 802.11r - Fast BSS Transition
 + * 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"
 +
 +#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"
 +#include "crypto/random.h"
 +#include "ap_config.h"
 +#include "ieee802_11.h"
 +#include "wmm.h"
 +#include "wpa_auth.h"
 +#include "wpa_auth_i.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)
 +{
 +      if (wpa_auth->cb.send_ether == NULL)
 +              return -1;
 +      wpa_printf(MSG_DEBUG, "FT: RRB send to " MACSTR, MAC2STR(dst));
 +      return wpa_auth->cb.send_ether(wpa_auth->cb.ctx, dst, ETH_P_RRB,
 +                                     data, data_len);
 +}
 +
 +
 +static int wpa_ft_action_send(struct wpa_authenticator *wpa_auth,
 +                            const u8 *dst, const u8 *data, size_t data_len)
 +{
 +      if (wpa_auth->cb.send_ft_action == NULL)
 +              return -1;
 +      return wpa_auth->cb.send_ft_action(wpa_auth->cb.ctx, dst,
 +                                         data, data_len);
 +}
 +
 +
 +static struct wpa_state_machine *
 +wpa_ft_add_sta(struct wpa_authenticator *wpa_auth, const u8 *sta_addr)
 +{
 +      if (wpa_auth->cb.add_sta == NULL)
 +              return NULL;
 +      return wpa_auth->cb.add_sta(wpa_auth->cb.ctx, sta_addr);
 +}
 +
 +
 +static int wpa_ft_add_tspec(struct wpa_authenticator *wpa_auth,
 +                          const u8 *sta_addr,
 +                          u8 *tspec_ie, size_t tspec_ielen)
 +{
 +      if (wpa_auth->cb.add_tspec == NULL) {
 +              wpa_printf(MSG_DEBUG, "FT: add_tspec is not initialized");
 +              return -1;
 +      }
 +      return wpa_auth->cb.add_tspec(wpa_auth->cb.ctx, sta_addr, tspec_ie,
 +                                    tspec_ielen);
 +}
 +
 +
 +int wpa_write_mdie(struct wpa_auth_config *conf, u8 *buf, size_t len)
 +{
 +      u8 *pos = buf;
 +      u8 capab;
 +      if (len < 2 + sizeof(struct rsn_mdie))
 +              return -1;
 +
 +      *pos++ = WLAN_EID_MOBILITY_DOMAIN;
 +      *pos++ = MOBILITY_DOMAIN_ID_LEN + 1;
 +      os_memcpy(pos, conf->mobility_domain, MOBILITY_DOMAIN_ID_LEN);
 +      pos += MOBILITY_DOMAIN_ID_LEN;
 +      capab = 0;
 +      if (conf->ft_over_ds)
 +              capab |= RSN_FT_CAPAB_FT_OVER_DS;
 +      *pos++ = capab;
 +
 +      return pos - buf;
 +}
 +
 +
 +int wpa_write_ftie(struct wpa_auth_config *conf, const u8 *r0kh_id,
 +                 size_t r0kh_id_len,
 +                 const u8 *anonce, const u8 *snonce,
 +                 u8 *buf, size_t len, const u8 *subelem,
 +                 size_t subelem_len)
 +{
 +      u8 *pos = buf, *ielen;
 +      struct rsn_ftie *hdr;
 +
 +      if (len < 2 + sizeof(*hdr) + 2 + FT_R1KH_ID_LEN + 2 + r0kh_id_len +
 +          subelem_len)
 +              return -1;
 +
 +      *pos++ = WLAN_EID_FAST_BSS_TRANSITION;
 +      ielen = pos++;
 +
 +      hdr = (struct rsn_ftie *) pos;
 +      os_memset(hdr, 0, sizeof(*hdr));
 +      pos += sizeof(*hdr);
 +      WPA_PUT_LE16(hdr->mic_control, 0);
 +      if (anonce)
 +              os_memcpy(hdr->anonce, anonce, WPA_NONCE_LEN);
 +      if (snonce)
 +              os_memcpy(hdr->snonce, snonce, WPA_NONCE_LEN);
 +
 +      /* Optional Parameters */
 +      *pos++ = FTIE_SUBELEM_R1KH_ID;
 +      *pos++ = FT_R1KH_ID_LEN;
 +      os_memcpy(pos, conf->r1_key_holder, FT_R1KH_ID_LEN);
 +      pos += FT_R1KH_ID_LEN;
 +
 +      if (r0kh_id) {
 +              *pos++ = FTIE_SUBELEM_R0KH_ID;
 +              *pos++ = r0kh_id_len;
 +              os_memcpy(pos, r0kh_id, r0kh_id_len);
 +              pos += r0kh_id_len;
 +      }
 +
 +      if (subelem) {
 +              os_memcpy(pos, subelem, subelem_len);
 +              pos += subelem_len;
 +      }
 +
 +      *ielen = pos - buf - 2;
 +
 +      return pos - buf;
 +}
 +
 +
 +struct wpa_ft_pmk_r0_sa {
 +      struct wpa_ft_pmk_r0_sa *next;
 +      u8 pmk_r0[PMK_LEN];
 +      u8 pmk_r0_name[WPA_PMK_NAME_LEN];
 +      u8 spa[ETH_ALEN];
 +      int pairwise; /* Pairwise cipher suite, WPA_CIPHER_* */
 +      /* TODO: expiration, identity, radius_class, EAP type, VLAN ID */
 +      int pmk_r1_pushed;
 +};
 +
 +struct wpa_ft_pmk_r1_sa {
 +      struct wpa_ft_pmk_r1_sa *next;
 +      u8 pmk_r1[PMK_LEN];
 +      u8 pmk_r1_name[WPA_PMK_NAME_LEN];
 +      u8 spa[ETH_ALEN];
 +      int pairwise; /* Pairwise cipher suite, WPA_CIPHER_* */
 +      /* TODO: expiration, identity, radius_class, EAP type, VLAN ID */
 +};
 +
 +struct wpa_ft_pmk_cache {
 +      struct wpa_ft_pmk_r0_sa *pmk_r0;
 +      struct wpa_ft_pmk_r1_sa *pmk_r1;
 +};
 +
 +struct wpa_ft_pmk_cache * wpa_ft_pmk_cache_init(void)
 +{
 +      struct wpa_ft_pmk_cache *cache;
 +
 +      cache = os_zalloc(sizeof(*cache));
 +
 +      return cache;
 +}
 +
 +
 +void wpa_ft_pmk_cache_deinit(struct wpa_ft_pmk_cache *cache)
 +{
 +      struct wpa_ft_pmk_r0_sa *r0, *r0prev;
 +      struct wpa_ft_pmk_r1_sa *r1, *r1prev;
 +
 +      r0 = cache->pmk_r0;
 +      while (r0) {
 +              r0prev = r0;
 +              r0 = r0->next;
 +              os_memset(r0prev->pmk_r0, 0, PMK_LEN);
 +              os_free(r0prev);
 +      }
 +
 +      r1 = cache->pmk_r1;
 +      while (r1) {
 +              r1prev = r1;
 +              r1 = r1->next;
 +              os_memset(r1prev->pmk_r1, 0, PMK_LEN);
 +              os_free(r1prev);
 +      }
 +
 +      os_free(cache);
 +}
 +
 +
 +static int wpa_ft_store_pmk_r0(struct wpa_authenticator *wpa_auth,
 +                             const u8 *spa, const u8 *pmk_r0,
 +                             const u8 *pmk_r0_name, int pairwise)
 +{
 +      struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache;
 +      struct wpa_ft_pmk_r0_sa *r0;
 +
 +      /* TODO: add expiration and limit on number of entries in cache */
 +
 +      r0 = os_zalloc(sizeof(*r0));
 +      if (r0 == NULL)
 +              return -1;
 +
 +      os_memcpy(r0->pmk_r0, pmk_r0, PMK_LEN);
 +      os_memcpy(r0->pmk_r0_name, pmk_r0_name, WPA_PMK_NAME_LEN);
 +      os_memcpy(r0->spa, spa, ETH_ALEN);
 +      r0->pairwise = pairwise;
 +
 +      r0->next = cache->pmk_r0;
 +      cache->pmk_r0 = r0;
 +
 +      return 0;
 +}
 +
 +
 +static int wpa_ft_fetch_pmk_r0(struct wpa_authenticator *wpa_auth,
 +                             const u8 *spa, const u8 *pmk_r0_name,
 +                             u8 *pmk_r0, int *pairwise)
 +{
 +      struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache;
 +      struct wpa_ft_pmk_r0_sa *r0;
 +
 +      r0 = cache->pmk_r0;
 +      while (r0) {
 +              if (os_memcmp(r0->spa, spa, ETH_ALEN) == 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;
 +                      return 0;
 +              }
 +
 +              r0 = r0->next;
 +      }
 +
 +      return -1;
 +}
 +
 +
 +static int wpa_ft_store_pmk_r1(struct wpa_authenticator *wpa_auth,
 +                             const u8 *spa, const u8 *pmk_r1,
 +                             const u8 *pmk_r1_name, int pairwise)
 +{
 +      struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache;
 +      struct wpa_ft_pmk_r1_sa *r1;
 +
 +      /* TODO: add expiration and limit on number of entries in cache */
 +
 +      r1 = os_zalloc(sizeof(*r1));
 +      if (r1 == NULL)
 +              return -1;
 +
 +      os_memcpy(r1->pmk_r1, pmk_r1, PMK_LEN);
 +      os_memcpy(r1->pmk_r1_name, pmk_r1_name, WPA_PMK_NAME_LEN);
 +      os_memcpy(r1->spa, spa, ETH_ALEN);
 +      r1->pairwise = pairwise;
 +
 +      r1->next = cache->pmk_r1;
 +      cache->pmk_r1 = r1;
 +
 +      return 0;
 +}
 +
 +
 +static int wpa_ft_fetch_pmk_r1(struct wpa_authenticator *wpa_auth,
 +                             const u8 *spa, const u8 *pmk_r1_name,
 +                             u8 *pmk_r1, int *pairwise)
 +{
 +      struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache;
 +      struct wpa_ft_pmk_r1_sa *r1;
 +
 +      r1 = cache->pmk_r1;
 +      while (r1) {
 +              if (os_memcmp(r1->spa, spa, ETH_ALEN) == 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;
 +                      return 0;
 +              }
 +
 +              r1 = r1->next;
 +      }
 +
 +      return -1;
 +}
 +
 +
 +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 = sm->wpa_auth->conf.r0kh_list;
 +      while (r0kh) {
 +              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) {
 +              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));
 +
 +      os_memset(&frame, 0, sizeof(frame));
 +      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, 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, 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, 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, sizeof(r0kh->key),
 +                   (FT_R0KH_R1KH_PULL_DATA_LEN + 7) / 8,
 +                   f.nonce, frame.nonce) < 0)
 +              return -1;
 +
 +      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)
 +{
 +      u8 pmk_r0[PMK_LEN], pmk_r0_name[WPA_PMK_NAME_LEN];
 +      u8 pmk_r1[PMK_LEN];
 +      u8 ptk_name[WPA_PMK_NAME_LEN];
 +      const u8 *mdid = sm->wpa_auth->conf.mobility_domain;
 +      const u8 *r0kh = sm->wpa_auth->conf.r0_key_holder;
 +      size_t r0kh_len = sm->wpa_auth->conf.r0_key_holder_len;
 +      const u8 *r1kh = sm->wpa_auth->conf.r1_key_holder;
 +      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");
 +              return -1;
 +      }
 +
 +      wpa_derive_pmk_r0(sm->xxkey, sm->xxkey_len, ssid, ssid_len, mdid,
 +                        r0kh, r0kh_len, sm->addr, pmk_r0, pmk_r0_name);
 +      wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R0", pmk_r0, PMK_LEN);
 +      wpa_hexdump(MSG_DEBUG, "FT: PMKR0Name", pmk_r0_name, WPA_PMK_NAME_LEN);
 +      wpa_ft_store_pmk_r0(sm->wpa_auth, sm->addr, pmk_r0, pmk_r0_name,
 +                          sm->pairwise);
 +
 +      wpa_derive_pmk_r1(pmk_r0, pmk_r0_name, r1kh, sm->addr,
 +                        pmk_r1, sm->pmk_r1_name);
 +      wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", pmk_r1, PMK_LEN);
 +      wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", sm->pmk_r1_name,
 +                  WPA_PMK_NAME_LEN);
 +      wpa_ft_store_pmk_r1(sm->wpa_auth, sm->addr, pmk_r1, sm->pmk_r1_name,
 +                          sm->pairwise);
 +
 +      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);
 +}
 +
 +
 +static inline int wpa_auth_get_seqnum(struct wpa_authenticator *wpa_auth,
 +                                    const u8 *addr, int idx, u8 *seq)
 +{
 +      if (wpa_auth->cb.get_seqnum == NULL)
 +              return -1;
 +      return wpa_auth->cb.get_seqnum(wpa_auth->cb.ctx, addr, idx, seq);
 +}
 +
 +
 +static u8 * wpa_ft_gtk_subelem(struct wpa_state_machine *sm, size_t *len)
 +{
 +      u8 *subelem;
 +      struct wpa_group *gsm = sm->group;
 +      size_t subelem_len, pad_len;
 +      const u8 *key;
 +      size_t key_len;
 +      u8 keybuf[32];
 +
 +      key_len = gsm->GTK_len;
 +      if (key_len > sizeof(keybuf))
 +              return NULL;
 +
 +      /*
 +       * Pad key for AES Key Wrap if it is not multiple of 8 bytes or is less
 +       * than 16 bytes.
 +       */
 +      pad_len = key_len % 8;
 +      if (pad_len)
 +              pad_len = 8 - pad_len;
 +      if (key_len + pad_len < 16)
 +              pad_len += 8;
 +      if (pad_len && key_len < sizeof(keybuf)) {
 +              os_memcpy(keybuf, gsm->GTK[gsm->GN - 1], key_len);
 +              os_memset(keybuf + key_len, 0, pad_len);
 +              keybuf[key_len] = 0xdd;
 +              key_len += pad_len;
 +              key = keybuf;
 +      } else
 +              key = gsm->GTK[gsm->GN - 1];
 +
 +      /*
 +       * Sub-elem ID[1] | Length[1] | Key Info[2] | Key Length[1] | RSC[8] |
 +       * Key[5..32].
 +       */
 +      subelem_len = 13 + key_len + 8;
 +      subelem = os_zalloc(subelem_len);
 +      if (subelem == NULL)
 +              return NULL;
 +
 +      subelem[0] = FTIE_SUBELEM_GTK;
 +      subelem[1] = 11 + key_len + 8;
 +      /* Key ID in B0-B1 of Key Info */
 +      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, sm->PTK.kek_len, key_len / 8, key,
 +                   subelem + 13)) {
 +              os_free(subelem);
 +              return NULL;
 +      }
 +
 +      *len = subelem_len;
 +      return subelem;
 +}
 +
 +
 +#ifdef CONFIG_IEEE80211W
 +static u8 * wpa_ft_igtk_subelem(struct wpa_state_machine *sm, size_t *len)
 +{
 +      u8 *subelem, *pos;
 +      struct wpa_group *gsm = sm->group;
 +      size_t subelem_len;
 +
 +      /* Sub-elem ID[1] | Length[1] | KeyID[2] | IPN[6] | Key Length[1] |
 +       * Key[16+8] */
 +      subelem_len = 1 + 1 + 2 + 6 + 1 + WPA_IGTK_LEN + 8;
 +      subelem = os_zalloc(subelem_len);
 +      if (subelem == NULL)
 +              return NULL;
 +
 +      pos = subelem;
 +      *pos++ = FTIE_SUBELEM_IGTK;
 +      *pos++ = subelem_len - 2;
 +      WPA_PUT_LE16(pos, gsm->GN_igtk);
 +      pos += 2;
 +      wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_igtk, pos);
 +      pos += 6;
 +      *pos++ = WPA_IGTK_LEN;
 +      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;
 +      }
 +
 +      *len = subelem_len;
 +      return subelem;
 +}
 +#endif /* CONFIG_IEEE80211W */
 +
 +
 +static u8 * wpa_ft_process_rdie(struct wpa_state_machine *sm,
 +                              u8 *pos, u8 *end, u8 id, u8 descr_count,
 +                              const u8 *ies, size_t ies_len)
 +{
 +      struct ieee802_11_elems parse;
 +      struct rsn_rdie *rdie;
 +
 +      wpa_printf(MSG_DEBUG, "FT: Resource Request: id=%d descr_count=%d",
 +                 id, descr_count);
 +      wpa_hexdump(MSG_MSGDUMP, "FT: Resource descriptor IE(s)",
 +                  ies, ies_len);
 +
 +      if (end - pos < (int) sizeof(*rdie)) {
 +              wpa_printf(MSG_ERROR, "FT: Not enough room for response RDIE");
 +              return pos;
 +      }
 +
 +      *pos++ = WLAN_EID_RIC_DATA;
 +      *pos++ = sizeof(*rdie);
 +      rdie = (struct rsn_rdie *) pos;
 +      rdie->id = id;
 +      rdie->descr_count = 0;
 +      rdie->status_code = host_to_le16(WLAN_STATUS_SUCCESS);
 +      pos += sizeof(*rdie);
 +
 +      if (ieee802_11_parse_elems((u8 *) ies, ies_len, &parse, 1) ==
 +          ParseFailed) {
 +              wpa_printf(MSG_DEBUG, "FT: Failed to parse request IEs");
 +              rdie->status_code =
 +                      host_to_le16(WLAN_STATUS_UNSPECIFIED_FAILURE);
 +              return pos;
 +      }
 +
-               int res;
++      if (parse.wmm_tspec) {
 +              struct wmm_tspec_element *tspec;
-               res = wmm_process_tspec(tspec);
 +
 +              if (parse.wmm_tspec_len + 2 < (int) sizeof(*tspec)) {
 +                      wpa_printf(MSG_DEBUG, "FT: Too short WMM TSPEC IE "
 +                                 "(%d)", (int) parse.wmm_tspec_len);
 +                      rdie->status_code =
 +                              host_to_le16(WLAN_STATUS_UNSPECIFIED_FAILURE);
 +                      return pos;
 +              }
 +              if (end - pos < (int) sizeof(*tspec)) {
 +                      wpa_printf(MSG_ERROR, "FT: Not enough room for "
 +                                 "response TSPEC");
 +                      rdie->status_code =
 +                              host_to_le16(WLAN_STATUS_UNSPECIFIED_FAILURE);
 +                      return pos;
 +              }
 +              tspec = (struct wmm_tspec_element *) pos;
 +              os_memcpy(tspec, parse.wmm_tspec - 2, sizeof(*tspec));
-                       pos += sizeof(*tspec);
++      }
++
++#ifdef NEED_AP_MLME
++      if (parse.wmm_tspec && sm->wpa_auth->conf.ap_mlme) {
++              int res;
++
++              res = wmm_process_tspec((struct wmm_tspec_element *) pos);
 +              wpa_printf(MSG_DEBUG, "FT: ADDTS processing result: %d", res);
 +              if (res == WMM_ADDTS_STATUS_INVALID_PARAMETERS)
 +                      rdie->status_code =
 +                              host_to_le16(WLAN_STATUS_INVALID_PARAMETERS);
 +              else if (res == WMM_ADDTS_STATUS_REFUSED)
 +                      rdie->status_code =
 +                              host_to_le16(WLAN_STATUS_REQUEST_DECLINED);
 +              else {
 +                      /* TSPEC accepted; include updated TSPEC in response */
 +                      rdie->descr_count = 1;
-               struct wmm_tspec_element *tspec;
++                      pos += sizeof(struct wmm_tspec_element);
 +              }
 +              return pos;
 +      }
 +#endif /* NEED_AP_MLME */
 +
 +      if (parse.wmm_tspec && !sm->wpa_auth->conf.ap_mlme) {
-               tspec = (struct wmm_tspec_element *) pos;
-               os_memcpy(tspec, parse.wmm_tspec - 2, sizeof(*tspec));
 +              int res;
 +
-                                      sizeof(*tspec));
 +              res = wpa_ft_add_tspec(sm->wpa_auth, sm->addr, pos,
-                               pos += sizeof(*tspec);
++                                     sizeof(struct wmm_tspec_element));
 +              if (res >= 0) {
 +                      if (res)
 +                              rdie->status_code = host_to_le16(res);
 +                      else {
 +                              /* TSPEC accepted; include updated TSPEC in
 +                               * response */
 +                              rdie->descr_count = 1;
++                              pos += sizeof(struct wmm_tspec_element);
 +                      }
 +                      return pos;
 +              }
 +      }
 +
 +      wpa_printf(MSG_DEBUG, "FT: No supported resource requested");
 +      rdie->status_code = host_to_le16(WLAN_STATUS_UNSPECIFIED_FAILURE);
 +      return pos;
 +}
 +
 +
 +static u8 * wpa_ft_process_ric(struct wpa_state_machine *sm, u8 *pos, u8 *end,
 +                             const u8 *ric, size_t ric_len)
 +{
 +      const u8 *rpos, *start;
 +      const struct rsn_rdie *rdie;
 +
 +      wpa_hexdump(MSG_MSGDUMP, "FT: RIC Request", ric, ric_len);
 +
 +      rpos = ric;
 +      while (rpos + sizeof(*rdie) < ric + ric_len) {
 +              if (rpos[0] != WLAN_EID_RIC_DATA || rpos[1] < sizeof(*rdie) ||
 +                  rpos + 2 + rpos[1] > ric + ric_len)
 +                      break;
 +              rdie = (const struct rsn_rdie *) (rpos + 2);
 +              rpos += 2 + rpos[1];
 +              start = rpos;
 +
 +              while (rpos + 2 <= ric + ric_len &&
 +                     rpos + 2 + rpos[1] <= ric + ric_len) {
 +                      if (rpos[0] == WLAN_EID_RIC_DATA)
 +                              break;
 +                      rpos += 2 + rpos[1];
 +              }
 +              pos = wpa_ft_process_rdie(sm, pos, end, rdie->id,
 +                                        rdie->descr_count,
 +                                        start, rpos - start);
 +      }
 +
 +      return pos;
 +}
 +
 +
 +u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos,
 +                               size_t max_len, int auth_alg,
 +                               const u8 *req_ies, size_t req_ies_len)
 +{
 +      u8 *end, *mdie, *ftie, *rsnie = NULL, *r0kh_id, *subelem = NULL;
 +      size_t mdie_len, ftie_len, rsnie_len = 0, r0kh_id_len, subelem_len = 0;
 +      int res;
 +      struct wpa_auth_config *conf;
 +      struct rsn_ftie *_ftie;
 +      struct wpa_ft_ies parse;
 +      u8 *ric_start;
 +      u8 *anonce, *snonce;
 +
 +      if (sm == NULL)
 +              return pos;
 +
 +      conf = &sm->wpa_auth->conf;
 +
 +      if (!wpa_key_mgmt_ft(sm->wpa_key_mgmt))
 +              return pos;
 +
 +      end = pos + max_len;
 +
 +      if (auth_alg == WLAN_AUTH_FT) {
 +              /*
 +               * RSN (only present if this is a Reassociation Response and
 +               * part of a fast BSS transition)
 +               */
 +              res = wpa_write_rsn_ie(conf, pos, end - pos, sm->pmk_r1_name);
 +              if (res < 0)
 +                      return pos;
 +              rsnie = pos;
 +              rsnie_len = res;
 +              pos += res;
 +      }
 +
 +      /* Mobility Domain Information */
 +      res = wpa_write_mdie(conf, pos, end - pos);
 +      if (res < 0)
 +              return pos;
 +      mdie = pos;
 +      mdie_len = res;
 +      pos += res;
 +
 +      /* Fast BSS Transition Information */
 +      if (auth_alg == WLAN_AUTH_FT) {
 +              subelem = wpa_ft_gtk_subelem(sm, &subelem_len);
 +              r0kh_id = sm->r0kh_id;
 +              r0kh_id_len = sm->r0kh_id_len;
 +              anonce = sm->ANonce;
 +              snonce = sm->SNonce;
 +#ifdef CONFIG_IEEE80211W
 +              if (sm->mgmt_frame_prot) {
 +                      u8 *igtk;
 +                      size_t igtk_len;
 +                      u8 *nbuf;
 +                      igtk = wpa_ft_igtk_subelem(sm, &igtk_len);
 +                      if (igtk == NULL) {
 +                              os_free(subelem);
 +                              return pos;
 +                      }
 +                      nbuf = os_realloc(subelem, subelem_len + igtk_len);
 +                      if (nbuf == NULL) {
 +                              os_free(subelem);
 +                              os_free(igtk);
 +                              return pos;
 +                      }
 +                      subelem = nbuf;
 +                      os_memcpy(subelem + subelem_len, igtk, igtk_len);
 +                      subelem_len += igtk_len;
 +                      os_free(igtk);
 +              }
 +#endif /* CONFIG_IEEE80211W */
 +      } else {
 +              r0kh_id = conf->r0_key_holder;
 +              r0kh_id_len = conf->r0_key_holder_len;
 +              anonce = NULL;
 +              snonce = NULL;
 +      }
 +      res = wpa_write_ftie(conf, r0kh_id, r0kh_id_len, anonce, snonce, pos,
 +                           end - pos, subelem, subelem_len);
 +      os_free(subelem);
 +      if (res < 0)
 +              return pos;
 +      ftie = pos;
 +      ftie_len = res;
 +      pos += res;
 +
 +      os_free(sm->assoc_resp_ftie);
 +      sm->assoc_resp_ftie = os_malloc(ftie_len);
 +      if (sm->assoc_resp_ftie)
 +              os_memcpy(sm->assoc_resp_ftie, ftie, ftie_len);
 +
 +      _ftie = (struct rsn_ftie *) (ftie + 2);
 +      if (auth_alg == WLAN_AUTH_FT)
 +              _ftie->mic_control[1] = 3; /* Information element count */
 +
 +      ric_start = pos;
 +      if (wpa_ft_parse_ies(req_ies, req_ies_len, &parse) == 0 && parse.ric) {
 +              pos = wpa_ft_process_ric(sm, pos, end, parse.ric,
 +                                       parse.ric_len);
 +              if (auth_alg == WLAN_AUTH_FT)
 +                      _ftie->mic_control[1] +=
 +                              ieee802_11_ie_count(ric_start,
 +                                                  pos - ric_start);
 +      }
 +      if (ric_start == pos)
 +              ric_start = NULL;
 +
 +      if (auth_alg == WLAN_AUTH_FT &&
 +          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,
 +                     _ftie->mic) < 0)
 +              wpa_printf(MSG_DEBUG, "FT: Failed to calculate MIC");
 +
 +      return pos;
 +}
 +
 +
 +static inline int wpa_auth_set_key(struct wpa_authenticator *wpa_auth,
 +                                 int vlan_id,
 +                                 enum wpa_alg alg, const u8 *addr, int idx,
 +                                 u8 *key, size_t key_len)
 +{
 +      if (wpa_auth->cb.set_key == NULL)
 +              return -1;
 +      return wpa_auth->cb.set_key(wpa_auth->cb.ctx, vlan_id, alg, addr, idx,
 +                                  key, key_len);
 +}
 +
 +
 +void wpa_ft_install_ptk(struct wpa_state_machine *sm)
 +{
 +      enum wpa_alg alg;
 +      int klen;
 +
 +      /* MLME-SETKEYS.request(PTK) */
 +      alg = wpa_cipher_to_alg(sm->pairwise);
 +      klen = wpa_cipher_key_len(sm->pairwise);
 +      if (!wpa_cipher_valid_pairwise(sm->pairwise)) {
 +              wpa_printf(MSG_DEBUG, "FT: Unknown pairwise alg 0x%x - skip "
 +                         "PTK configuration", sm->pairwise);
 +              return;
 +      }
 +
 +      /* FIX: add STA entry to kernel/driver here? The set_key will fail
 +       * most likely without this.. At the moment, STA entry is added only
 +       * after association has been completed. This function will be called
 +       * again after association to get the PTK configured, but that could be
 +       * optimized by adding the STA entry earlier.
 +       */
 +      if (wpa_auth_set_key(sm->wpa_auth, 0, alg, sm->addr, 0,
 +                           sm->PTK.tk, klen))
 +              return;
 +
 +      /* FIX: MLME-SetProtection.Request(TA, Tx_Rx) */
 +      sm->pairwise_set = TRUE;
 +}
 +
 +
 +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)
 +{
 +      struct rsn_mdie *mdie;
 +      struct rsn_ftie *ftie;
 +      u8 pmk_r1[PMK_LEN], pmk_r1_name[WPA_PMK_NAME_LEN];
 +      u8 ptk_name[WPA_PMK_NAME_LEN];
 +      struct wpa_auth_config *conf;
 +      struct wpa_ft_ies parse;
 +      size_t buflen;
 +      int ret;
 +      u8 *pos, *end;
 +      int pairwise;
 +
 +      *resp_ies = NULL;
 +      *resp_ies_len = 0;
 +
 +      sm->pmk_r1_name_valid = 0;
 +      conf = &sm->wpa_auth->conf;
 +
 +      wpa_hexdump(MSG_DEBUG, "FT: Received authentication frame IEs",
 +                  ies, ies_len);
 +
 +      if (wpa_ft_parse_ies(ies, ies_len, &parse) < 0) {
 +              wpa_printf(MSG_DEBUG, "FT: Failed to parse FT IEs");
 +              return WLAN_STATUS_UNSPECIFIED_FAILURE;
 +      }
 +
 +      mdie = (struct rsn_mdie *) parse.mdie;
 +      if (mdie == NULL || parse.mdie_len < sizeof(*mdie) ||
 +          os_memcmp(mdie->mobility_domain,
 +                    sm->wpa_auth->conf.mobility_domain,
 +                    MOBILITY_DOMAIN_ID_LEN) != 0) {
 +              wpa_printf(MSG_DEBUG, "FT: Invalid MDIE");
 +              return WLAN_STATUS_INVALID_MDIE;
 +      }
 +
 +      ftie = (struct rsn_ftie *) parse.ftie;
 +      if (ftie == NULL || parse.ftie_len < sizeof(*ftie)) {
 +              wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
 +              return WLAN_STATUS_INVALID_FTIE;
 +      }
 +
 +      os_memcpy(sm->SNonce, ftie->snonce, WPA_NONCE_LEN);
 +
 +      if (parse.r0kh_id == NULL) {
 +              wpa_printf(MSG_DEBUG, "FT: Invalid FTIE - no R0KH-ID");
 +              return WLAN_STATUS_INVALID_FTIE;
 +      }
 +
 +      wpa_hexdump(MSG_DEBUG, "FT: STA R0KH-ID",
 +                  parse.r0kh_id, parse.r0kh_id_len);
 +      os_memcpy(sm->r0kh_id, parse.r0kh_id, parse.r0kh_id_len);
 +      sm->r0kh_id_len = parse.r0kh_id_len;
 +
 +      if (parse.rsn_pmkid == NULL) {
 +              wpa_printf(MSG_DEBUG, "FT: No PMKID in RSNIE");
 +              return WLAN_STATUS_INVALID_PMKID;
 +      }
 +
 +      wpa_hexdump(MSG_DEBUG, "FT: Requested PMKR0Name",
 +                  parse.rsn_pmkid, WPA_PMK_NAME_LEN);
 +      wpa_derive_pmk_r1_name(parse.rsn_pmkid,
 +                             sm->wpa_auth->conf.r1_key_holder, sm->addr,
 +                             pmk_r1_name);
 +      wpa_hexdump(MSG_DEBUG, "FT: Derived requested PMKR1Name",
 +                  pmk_r1_name, WPA_PMK_NAME_LEN);
 +
 +      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, 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;
 +              }
 +
 +              return -1; /* Status pending */
 +      }
 +
 +      wpa_hexdump_key(MSG_DEBUG, "FT: Selected PMK-R1", pmk_r1, PMK_LEN);
 +      sm->pmk_r1_name_valid = 1;
 +      os_memcpy(sm->pmk_r1_name, pmk_r1_name, WPA_PMK_NAME_LEN);
 +
 +      if (random_get_bytes(sm->ANonce, WPA_NONCE_LEN)) {
 +              wpa_printf(MSG_DEBUG, "FT: Failed to get random data for "
 +                         "ANonce");
 +              return WLAN_STATUS_UNSPECIFIED_FAILURE;
 +      }
 +
 +      wpa_hexdump(MSG_DEBUG, "FT: Received SNonce",
 +                  sm->SNonce, WPA_NONCE_LEN);
 +      wpa_hexdump(MSG_DEBUG, "FT: Generated ANonce",
 +                  sm->ANonce, WPA_NONCE_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) +
 +              2 + FT_R1KH_ID_LEN + 200;
 +      *resp_ies = os_zalloc(buflen);
 +      if (*resp_ies == NULL) {
 +              return WLAN_STATUS_UNSPECIFIED_FAILURE;
 +      }
 +
 +      pos = *resp_ies;
 +      end = *resp_ies + buflen;
 +
 +      ret = wpa_write_rsn_ie(conf, pos, end - pos, parse.rsn_pmkid);
 +      if (ret < 0) {
 +              os_free(*resp_ies);
 +              *resp_ies = NULL;
 +              return WLAN_STATUS_UNSPECIFIED_FAILURE;
 +      }
 +      pos += ret;
 +
 +      ret = wpa_write_mdie(conf, pos, end - pos);
 +      if (ret < 0) {
 +              os_free(*resp_ies);
 +              *resp_ies = NULL;
 +              return WLAN_STATUS_UNSPECIFIED_FAILURE;
 +      }
 +      pos += ret;
 +
 +      ret = wpa_write_ftie(conf, parse.r0kh_id, parse.r0kh_id_len,
 +                           sm->ANonce, sm->SNonce, pos, end - pos, NULL, 0);
 +      if (ret < 0) {
 +              os_free(*resp_ies);
 +              *resp_ies = NULL;
 +              return WLAN_STATUS_UNSPECIFIED_FAILURE;
 +      }
 +      pos += ret;
 +
 +      *resp_ies_len = pos - *resp_ies;
 +
 +      return WLAN_STATUS_SUCCESS;
 +}
 +
 +
 +void wpa_ft_process_auth(struct wpa_state_machine *sm, const u8 *bssid,
 +                       u16 auth_transaction, const u8 *ies, size_t ies_len,
 +                       void (*cb)(void *ctx, const u8 *dst, const u8 *bssid,
 +                                  u16 auth_transaction, u16 status,
 +                                  const u8 *ies, size_t ies_len),
 +                       void *ctx)
 +{
 +      u16 status;
 +      u8 *resp_ies;
 +      size_t resp_ies_len;
 +      int res;
 +
 +      if (sm == NULL) {
 +              wpa_printf(MSG_DEBUG, "FT: Received authentication frame, but "
 +                         "WPA SM not available");
 +              return;
 +      }
 +
 +      wpa_printf(MSG_DEBUG, "FT: Received authentication frame: STA=" MACSTR
 +                 " BSSID=" MACSTR " transaction=%d",
 +                 MAC2STR(sm->addr), MAC2STR(bssid), auth_transaction);
 +      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",
 +                 MAC2STR(sm->addr), auth_transaction + 1, status);
 +      wpa_hexdump(MSG_DEBUG, "FT: Response IEs", resp_ies, resp_ies_len);
 +      cb(ctx, sm->addr, bssid, auth_transaction + 1, status,
 +         resp_ies, resp_ies_len);
 +      os_free(resp_ies);
 +}
 +
 +
 +u16 wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies,
 +                          size_t ies_len)
 +{
 +      struct wpa_ft_ies parse;
 +      struct rsn_mdie *mdie;
 +      struct rsn_ftie *ftie;
 +      u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN];
 +      size_t mic_len = 16;
 +      unsigned int count;
 +
 +      if (sm == NULL)
 +              return WLAN_STATUS_UNSPECIFIED_FAILURE;
 +
 +      wpa_hexdump(MSG_DEBUG, "FT: Reassoc Req IEs", ies, ies_len);
 +
 +      if (wpa_ft_parse_ies(ies, ies_len, &parse) < 0) {
 +              wpa_printf(MSG_DEBUG, "FT: Failed to parse FT IEs");
 +              return WLAN_STATUS_UNSPECIFIED_FAILURE;
 +      }
 +
 +      if (parse.rsn == NULL) {
 +              wpa_printf(MSG_DEBUG, "FT: No RSNIE in Reassoc Req");
 +              return WLAN_STATUS_UNSPECIFIED_FAILURE;
 +      }
 +
 +      if (parse.rsn_pmkid == NULL) {
 +              wpa_printf(MSG_DEBUG, "FT: No PMKID in RSNIE");
 +              return WLAN_STATUS_INVALID_PMKID;
 +      }
 +
 +      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;
 +      }
 +
 +      mdie = (struct rsn_mdie *) parse.mdie;
 +      if (mdie == NULL || parse.mdie_len < sizeof(*mdie) ||
 +          os_memcmp(mdie->mobility_domain,
 +                    sm->wpa_auth->conf.mobility_domain,
 +                    MOBILITY_DOMAIN_ID_LEN) != 0) {
 +              wpa_printf(MSG_DEBUG, "FT: Invalid MDIE");
 +              return WLAN_STATUS_INVALID_MDIE;
 +      }
 +
 +      ftie = (struct rsn_ftie *) parse.ftie;
 +      if (ftie == NULL || parse.ftie_len < sizeof(*ftie)) {
 +              wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
 +              return WLAN_STATUS_INVALID_FTIE;
 +      }
 +
 +      if (os_memcmp(ftie->snonce, sm->SNonce, WPA_NONCE_LEN) != 0) {
 +              wpa_printf(MSG_DEBUG, "FT: SNonce mismatch in FTIE");
 +              wpa_hexdump(MSG_DEBUG, "FT: Received SNonce",
 +                          ftie->snonce, WPA_NONCE_LEN);
 +              wpa_hexdump(MSG_DEBUG, "FT: Expected SNonce",
 +                          sm->SNonce, WPA_NONCE_LEN);
 +              return -1;
 +      }
 +
 +      if (os_memcmp(ftie->anonce, sm->ANonce, WPA_NONCE_LEN) != 0) {
 +              wpa_printf(MSG_DEBUG, "FT: ANonce mismatch in FTIE");
 +              wpa_hexdump(MSG_DEBUG, "FT: Received ANonce",
 +                          ftie->anonce, WPA_NONCE_LEN);
 +              wpa_hexdump(MSG_DEBUG, "FT: Expected ANonce",
 +                          sm->ANonce, WPA_NONCE_LEN);
 +              return -1;
 +      }
 +
 +
 +      if (parse.r0kh_id == NULL) {
 +              wpa_printf(MSG_DEBUG, "FT: No R0KH-ID subelem in FTIE");
 +              return -1;
 +      }
 +
 +      if (parse.r0kh_id_len != sm->r0kh_id_len ||
 +          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",
 +                          parse.r0kh_id, parse.r0kh_id_len);
 +              wpa_hexdump(MSG_DEBUG, "FT: The current R0KH-ID",
 +                          sm->r0kh_id, sm->r0kh_id_len);
 +              return -1;
 +      }
 +
 +      if (parse.r1kh_id == NULL) {
 +              wpa_printf(MSG_DEBUG, "FT: No R1KH-ID subelem in FTIE");
 +              return -1;
 +      }
 +
 +      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",
 +                          parse.r1kh_id, FT_R1KH_ID_LEN);
 +              wpa_hexdump(MSG_DEBUG, "FT: Expected R1KH-ID",
 +                          sm->wpa_auth->conf.r1_key_holder, FT_R1KH_ID_LEN);
 +              return -1;
 +      }
 +
 +      if (parse.rsn_pmkid == NULL ||
 +          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;
 +      }
 +
 +      count = 3;
 +      if (parse.ric)
 +              count += ieee802_11_ie_count(parse.ric, parse.ric_len);
 +      if (ftie->mic_control[1] != count) {
 +              wpa_printf(MSG_DEBUG, "FT: Unexpected IE count in MIC "
 +                         "Control: received %u expected %u",
 +                         ftie->mic_control[1], count);
 +              return -1;
 +      }
 +
 +      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,
 +                     parse.ric, parse.ric_len,
 +                     mic) < 0) {
 +              wpa_printf(MSG_DEBUG, "FT: Failed to calculate MIC");
 +              return WLAN_STATUS_UNSPECIFIED_FAILURE;
 +      }
 +
 +      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, 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",
 +                          parse.ftie - 2, parse.ftie_len + 2);
 +              wpa_hexdump(MSG_MSGDUMP, "FT: RSN",
 +                          parse.rsn - 2, parse.rsn_len + 2);
 +              return WLAN_STATUS_INVALID_FTIE;
 +      }
 +
 +      return WLAN_STATUS_SUCCESS;
 +}
 +
 +
 +int wpa_ft_action_rx(struct wpa_state_machine *sm, const u8 *data, size_t len)
 +{
 +      const u8 *sta_addr, *target_ap;
 +      const u8 *ies;
 +      size_t ies_len;
 +      u8 action;
 +      struct ft_rrb_frame *frame;
 +
 +      if (sm == NULL)
 +              return -1;
 +
 +      /*
 +       * data: Category[1] Action[1] STA_Address[6] Target_AP_Address[6]
 +       * FT Request action frame body[variable]
 +       */
 +
 +      if (len < 14) {
 +              wpa_printf(MSG_DEBUG, "FT: Too short FT Action frame "
 +                         "(len=%lu)", (unsigned long) len);
 +              return -1;
 +      }
 +
 +      action = data[1];
 +      sta_addr = data + 2;
 +      target_ap = data + 8;
 +      ies = data + 14;
 +      ies_len = len - 14;
 +
 +      wpa_printf(MSG_DEBUG, "FT: Received FT Action frame (STA=" MACSTR
 +                 " Target AP=" MACSTR " Action=%d)",
 +                 MAC2STR(sta_addr), MAC2STR(target_ap), action);
 +
 +      if (os_memcmp(sta_addr, sm->addr, ETH_ALEN) != 0) {
 +              wpa_printf(MSG_DEBUG, "FT: Mismatch in FT Action STA address: "
 +                         "STA=" MACSTR " STA-Address=" MACSTR,
 +                         MAC2STR(sm->addr), MAC2STR(sta_addr));
 +              return -1;
 +      }
 +
 +      /*
 +       * Do some sanity checking on the target AP address (not own and not
 +       * broadcast. This could be extended to filter based on a list of known
 +       * APs in the MD (if such a list were configured).
 +       */
 +      if ((target_ap[0] & 0x01) ||
 +          os_memcmp(target_ap, sm->wpa_auth->addr, ETH_ALEN) == 0) {
 +              wpa_printf(MSG_DEBUG, "FT: Invalid Target AP in FT Action "
 +                         "frame");
 +              return -1;
 +      }
 +
 +      wpa_hexdump(MSG_MSGDUMP, "FT: Action frame body", ies, ies_len);
 +
 +      /* RRB - Forward action frame to the target AP */
 +      frame = os_malloc(sizeof(*frame) + len);
 +      if (frame == NULL)
 +              return -1;
 +      frame->frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB;
 +      frame->packet_type = FT_PACKET_REQUEST;
 +      frame->action_length = host_to_le16(len);
 +      os_memcpy(frame->ap_address, sm->wpa_auth->addr, ETH_ALEN);
 +      os_memcpy(frame + 1, data, len);
 +
 +      wpa_ft_rrb_send(sm->wpa_auth, target_ap, (u8 *) frame,
 +                      sizeof(*frame) + len);
 +      os_free(frame);
 +
 +      return 0;
 +}
 +
 +
 +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;
 +      size_t resp_ies_len;
 +      int res;
 +
 +      sm = wpa_ft_add_sta(wpa_auth, sta_addr);
 +      if (sm == NULL) {
 +              wpa_printf(MSG_DEBUG, "FT: Failed to add new STA based on "
 +                         "RRB Request");
 +              return -1;
 +      }
 +
 +      wpa_hexdump(MSG_MSGDUMP, "FT: RRB Request Frame body", body, 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",
 +                 MAC2STR(sm->addr), MAC2STR(current_ap), status);
 +      wpa_hexdump(MSG_DEBUG, "FT: Response IEs", resp_ies, resp_ies_len);
 +
 +      /* RRB - Forward action frame response to the Current AP */
 +
 +      /*
 +       * data: Category[1] Action[1] STA_Address[6] Target_AP_Address[6]
 +       * Status_Code[2] FT Request action frame body[variable]
 +       */
 +      rlen = 2 + 2 * ETH_ALEN + 2 + resp_ies_len;
 +
 +      frame = os_malloc(sizeof(*frame) + rlen);
 +      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);
 +      os_memcpy(frame->ap_address, wpa_auth->addr, ETH_ALEN);
 +      pos = (u8 *) (frame + 1);
 +      *pos++ = WLAN_ACTION_FT;
 +      *pos++ = 2; /* Action: Response */
 +      os_memcpy(pos, sta_addr, ETH_ALEN);
 +      pos += ETH_ALEN;
 +      os_memcpy(pos, wpa_auth->addr, ETH_ALEN);
 +      pos += ETH_ALEN;
 +      WPA_PUT_LE16(pos, status);
 +      pos += 2;
 +      if (resp_ies)
 +              os_memcpy(pos, resp_ies, resp_ies_len);
 +
 +      wpa_ft_rrb_send(wpa_auth, current_ap, (u8 *) frame,
 +                      sizeof(*frame) + rlen);
 +      os_free(frame);
 +
 +      return 0;
 +}
 +
 +
 +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 f;
 +      const u8 *crypt;
 +      u8 *plain;
 +      struct ft_remote_r1kh *r1kh;
 +      struct ft_r0kh_r1kh_resp_frame resp, r;
 +      u8 pmk_r0[PMK_LEN];
 +      int pairwise;
 +
 +      wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 pull");
 +
 +      if (data_len < sizeof(f))
 +              return -1;
 +
 +      r1kh = wpa_auth->conf.r1kh_list;
 +      while (r1kh) {
 +              if (os_memcmp(r1kh->addr, src_addr, ETH_ALEN) == 0)
 +                      break;
 +              r1kh = r1kh->next;
 +      }
 +      if (r1kh == NULL) {
 +              wpa_printf(MSG_DEBUG, "FT: No matching R1KH address found for "
 +                         "PMK-R1 pull source address " MACSTR,
 +                         MAC2STR(src_addr));
 +              return -1;
 +      }
 +
 +      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, 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;
 +      }
 +
 +      wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - nonce",
 +                  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="
 +                 MACSTR, MAC2STR(f.r1kh_id), MAC2STR(f.s1kh_id));
 +
 +      os_memset(&resp, 0, sizeof(resp));
 +      resp.frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB;
 +      resp.packet_type = FT_PACKET_R0KH_R1KH_RESP;
 +      resp.data_length = host_to_le16(FT_R0KH_R1KH_RESP_DATA_LEN);
 +      os_memcpy(resp.ap_address, wpa_auth->addr, ETH_ALEN);
 +
 +      /* aes_wrap() does not support inplace encryption, so use a temporary
 +       * buffer for the data. */
 +      os_memcpy(r.nonce, f.nonce, sizeof(f.nonce));
 +      os_memcpy(r.r1kh_id, f.r1kh_id, FT_R1KH_ID_LEN);
 +      os_memcpy(r.s1kh_id, f.s1kh_id, ETH_ALEN);
 +      if (wpa_ft_fetch_pmk_r0(wpa_auth, f.s1kh_id, f.pmk_r0_name, pmk_r0,
 +                              &pairwise) < 0) {
 +              wpa_printf(MSG_DEBUG, "FT: No matching PMKR0Name found for "
 +                         "PMK-R1 pull");
 +              return -1;
 +      }
 +
 +      wpa_derive_pmk_r1(pmk_r0, f.pmk_r0_name, f.r1kh_id, f.s1kh_id,
 +                        r.pmk_r1, r.pmk_r1_name);
 +      wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", r.pmk_r1, PMK_LEN);
 +      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, 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;
 +      }
 +
 +      os_memset(pmk_r0, 0, PMK_LEN);
 +
 +      wpa_ft_rrb_send(wpa_auth, src_addr, (u8 *) &resp, sizeof(resp));
 +
 +      return 0;
 +}
 +
 +
 +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 f;
 +      const u8 *crypt;
 +      u8 *plain;
 +      struct ft_remote_r0kh *r0kh;
 +      int pairwise, res;
 +
 +      wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 pull response");
 +
 +      if (data_len < sizeof(f))
 +              return -1;
 +
 +      r0kh = wpa_auth->conf.r0kh_list;
 +      while (r0kh) {
 +              if (os_memcmp(r0kh->addr, src_addr, ETH_ALEN) == 0)
 +                      break;
 +              r0kh = r0kh->next;
 +      }
 +      if (r0kh == NULL) {
 +              wpa_printf(MSG_DEBUG, "FT: No matching R0KH address found for "
 +                         "PMK-R0 pull response source address " MACSTR,
 +                         MAC2STR(src_addr));
 +              return -1;
 +      }
 +
 +      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, 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_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;
 +      }
 +
 +      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="
 +                 MACSTR " pairwise=0x%x",
 +                 MAC2STR(f.r1kh_id), MAC2STR(f.s1kh_id), pairwise);
 +      wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1 pull - PMK-R1",
 +                      f.pmk_r1, PMK_LEN);
 +      wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - PMKR1Name",
 +                      f.pmk_r1_name, WPA_PMK_NAME_LEN);
 +
 +      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 res ? 0 : -1;
 +}
 +
 +
 +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 f;
 +      const u8 *crypt;
 +      u8 *plain;
 +      struct ft_remote_r0kh *r0kh;
 +      struct os_time now;
 +      os_time_t tsend;
 +      int pairwise;
 +
 +      wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 push");
 +
 +      if (data_len < sizeof(f))
 +              return -1;
 +
 +      r0kh = wpa_auth->conf.r0kh_list;
 +      while (r0kh) {
 +              if (os_memcmp(r0kh->addr, src_addr, ETH_ALEN) == 0)
 +                      break;
 +              r0kh = r0kh->next;
 +      }
 +      if (r0kh == NULL) {
 +              wpa_printf(MSG_DEBUG, "FT: No matching R0KH address found for "
 +                         "PMK-R0 push source address " MACSTR,
 +                         MAC2STR(src_addr));
 +              return -1;
 +      }
 +
 +      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, 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;
 +      }
 +
 +      os_get_time(&now);
 +      tsend = WPA_GET_LE32(f.timestamp);
 +      if ((now.sec > tsend && now.sec - tsend > 60) ||
 +          (now.sec < tsend && tsend - now.sec > 60)) {
 +              wpa_printf(MSG_DEBUG, "FT: PMK-R1 push did not have a valid "
 +                         "timestamp: sender time %d own time %d\n",
 +                         (int) tsend, (int) now.sec);
 +              return -1;
 +      }
 +
 +      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),
 +                         MAC2STR(wpa_auth->conf.r1_key_holder));
 +              return -1;
 +      }
 +
 +      pairwise = le_to_host16(f.pairwise);
 +      wpa_printf(MSG_DEBUG, "FT: PMK-R1 push - 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 push - PMK-R1",
 +                      f.pmk_r1, PMK_LEN);
 +      wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 push - 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);
 +      os_memset(f.pmk_r1, 0, PMK_LEN);
 +
 +      return 0;
 +}
 +
 +
 +int wpa_ft_rrb_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr,
 +                const u8 *data, size_t data_len)
 +{
 +      struct ft_rrb_frame *frame;
 +      u16 alen;
 +      const u8 *pos, *end, *start;
 +      u8 action;
 +      const u8 *sta_addr, *target_ap_addr;
 +
 +      wpa_printf(MSG_DEBUG, "FT: RRB received frame from remote AP " MACSTR,
 +                 MAC2STR(src_addr));
 +
 +      if (data_len < sizeof(*frame)) {
 +              wpa_printf(MSG_DEBUG, "FT: Too short RRB frame (data_len=%lu)",
 +                         (unsigned long) data_len);
 +              return -1;
 +      }
 +
 +      pos = data;
 +      frame = (struct ft_rrb_frame *) pos;
 +      pos += sizeof(*frame);
 +
 +      alen = le_to_host16(frame->action_length);
 +      wpa_printf(MSG_DEBUG, "FT: RRB frame - frame_type=%d packet_type=%d "
 +                 "action_length=%d ap_address=" MACSTR,
 +                 frame->frame_type, frame->packet_type, alen,
 +                 MAC2STR(frame->ap_address));
 +
 +      if (frame->frame_type != RSN_REMOTE_FRAME_TYPE_FT_RRB) {
 +              /* Discard frame per IEEE Std 802.11r-2008, 11A.10.3 */
 +              wpa_printf(MSG_DEBUG, "FT: RRB discarded frame with "
 +                         "unrecognized type %d", frame->frame_type);
 +              return -1;
 +      }
 +
 +      if (alen > data_len - sizeof(*frame)) {
 +              wpa_printf(MSG_DEBUG, "FT: RRB frame too short for action "
 +                         "frame");
 +              return -1;
 +      }
 +
 +      if (frame->packet_type == FT_PACKET_R0KH_R1KH_PULL)
 +              return wpa_ft_rrb_rx_pull(wpa_auth, src_addr, data, data_len);
 +      if (frame->packet_type == FT_PACKET_R0KH_R1KH_RESP)
 +              return wpa_ft_rrb_rx_resp(wpa_auth, src_addr, data, data_len);
 +      if (frame->packet_type == FT_PACKET_R0KH_R1KH_PUSH)
 +              return wpa_ft_rrb_rx_push(wpa_auth, src_addr, data, data_len);
 +
 +      wpa_hexdump(MSG_MSGDUMP, "FT: RRB - FT Action frame", pos, alen);
 +
 +      if (alen < 1 + 1 + 2 * ETH_ALEN) {
 +              wpa_printf(MSG_DEBUG, "FT: Too short RRB frame (not enough "
 +                         "room for Action Frame body); alen=%lu",
 +                         (unsigned long) alen);
 +              return -1;
 +      }
 +      start = pos;
 +      end = pos + alen;
 +
 +      if (*pos != WLAN_ACTION_FT) {
 +              wpa_printf(MSG_DEBUG, "FT: Unexpected Action frame category "
 +                         "%d", *pos);
 +              return -1;
 +      }
 +
 +      pos++;
 +      action = *pos++;
 +      sta_addr = pos;
 +      pos += ETH_ALEN;
 +      target_ap_addr = pos;
 +      pos += ETH_ALEN;
 +      wpa_printf(MSG_DEBUG, "FT: RRB Action Frame: action=%d sta_addr="
 +                 MACSTR " target_ap_addr=" MACSTR,
 +                 action, MAC2STR(sta_addr), MAC2STR(target_ap_addr));
 +
 +      if (frame->packet_type == FT_PACKET_REQUEST) {
 +              wpa_printf(MSG_DEBUG, "FT: FT Packet Type - Request");
 +
 +              if (action != 1) {
 +                      wpa_printf(MSG_DEBUG, "FT: Unexpected Action %d in "
 +                                 "RRB Request", action);
 +                      return -1;
 +              }
 +
 +              if (os_memcmp(target_ap_addr, wpa_auth->addr, ETH_ALEN) != 0) {
 +                      wpa_printf(MSG_DEBUG, "FT: Target AP address in the "
 +                                 "RRB Request does not match with own "
 +                                 "address");
 +                      return -1;
 +              }
 +
 +              if (wpa_ft_rrb_rx_request(wpa_auth, frame->ap_address,
 +                                        sta_addr, pos, end - pos) < 0)
 +                      return -1;
 +      } else if (frame->packet_type == FT_PACKET_RESPONSE) {
 +              u16 status_code;
 +
 +              if (end - pos < 2) {
 +                      wpa_printf(MSG_DEBUG, "FT: Not enough room for status "
 +                                 "code in RRB Response");
 +                      return -1;
 +              }
 +              status_code = WPA_GET_LE16(pos);
 +              pos += 2;
 +
 +              wpa_printf(MSG_DEBUG, "FT: FT Packet Type - Response "
 +                         "(status_code=%d)", status_code);
 +
 +              if (wpa_ft_action_send(wpa_auth, sta_addr, start, alen) < 0)
 +                      return -1;
 +      } else {
 +              wpa_printf(MSG_DEBUG, "FT: RRB discarded frame with unknown "
 +                         "packet_type %d", frame->packet_type);
 +              return -1;
 +      }
 +
 +      if (end > pos) {
 +              wpa_hexdump(MSG_DEBUG, "FT: Ignore extra data in end",
 +                          pos, end - pos);
 +      }
 +
 +      return 0;
 +}
 +
 +
 +static void wpa_ft_generate_pmk_r1(struct wpa_authenticator *wpa_auth,
 +                                 struct wpa_ft_pmk_r0_sa *pmk_r0,
 +                                 struct ft_remote_r1kh *r1kh,
 +                                 const u8 *s1kh_id, int pairwise)
 +{
 +      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;
 +      frame.packet_type = FT_PACKET_R0KH_R1KH_PUSH;
 +      frame.data_length = host_to_le16(FT_R0KH_R1KH_PUSH_DATA_LEN);
 +      os_memcpy(frame.ap_address, wpa_auth->addr, ETH_ALEN);
 +
 +      /* aes_wrap() does not support inplace encryption, so use a temporary
 +       * buffer for the data. */
 +      os_memcpy(f.r1kh_id, r1kh->id, FT_R1KH_ID_LEN);
 +      os_memcpy(f.s1kh_id, s1kh_id, ETH_ALEN);
 +      os_memcpy(f.pmk_r0_name, pmk_r0->pmk_r0_name, WPA_PMK_NAME_LEN);
 +      wpa_derive_pmk_r1(pmk_r0->pmk_r0, pmk_r0->pmk_r0_name, r1kh->id,
 +                        s1kh_id, f.pmk_r1, f.pmk_r1_name);
 +      wpa_printf(MSG_DEBUG, "FT: R1KH-ID " MACSTR, MAC2STR(r1kh->id));
 +      wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", f.pmk_r1, PMK_LEN);
 +      wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", f.pmk_r1_name,
 +                  WPA_PMK_NAME_LEN);
 +      os_get_time(&now);
 +      WPA_PUT_LE32(f.timestamp, now.sec);
 +      f.pairwise = host_to_le16(pairwise);
 +      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));
 +}
 +
 +
 +void wpa_ft_push_pmk_r1(struct wpa_authenticator *wpa_auth, const u8 *addr)
 +{
 +      struct wpa_ft_pmk_r0_sa *r0;
 +      struct ft_remote_r1kh *r1kh;
 +
 +      if (!wpa_auth->conf.pmk_r1_push)
 +              return;
 +
 +      r0 = wpa_auth->ft_pmk_cache->pmk_r0;
 +      while (r0) {
 +              if (os_memcmp(r0->spa, addr, ETH_ALEN) == 0)
 +                      break;
 +              r0 = r0->next;
 +      }
 +
 +      if (r0 == NULL || r0->pmk_r1_pushed)
 +              return;
 +      r0->pmk_r1_pushed = 1;
 +
 +      wpa_printf(MSG_DEBUG, "FT: Deriving and pushing PMK-R1 keys to R1KHs "
 +                 "for STA " MACSTR, MAC2STR(addr));
 +
 +      r1kh = wpa_auth->conf.r1kh_list;
 +      while (r1kh) {
 +              wpa_ft_generate_pmk_r1(wpa_auth, r0, r1kh, addr, r0->pairwise);
 +              r1kh = r1kh->next;
 +      }
 +}
 +
 +#endif /* CONFIG_IEEE80211R */
index 7f8320708c3963a5693b41c7d28cdba35ac49a3c,0000000000000000000000000000000000000000..f98cc50599e3764c1c1aef578cb457dab14f1d96
mode 100644,000000..100644
--- /dev/null
@@@ -1,677 -1,0 +1,695 @@@
-       if (wconf->ssid_len > SSID_LEN)
-               wconf->ssid_len = SSID_LEN;
 +/*
 + * hostapd / WPA authenticator glue code
 + * Copyright (c) 2002-2012, Jouni Malinen <j@w1.fi>
 + *
 + * This software may be distributed under the terms of the BSD license.
 + * See README for more details.
 + */
 +
 +#include "utils/includes.h"
 +
 +#include "utils/common.h"
 +#include "common/ieee802_11_defs.h"
 +#include "common/sae.h"
++#include "common/wpa_ctrl.h"
 +#include "eapol_auth/eapol_auth_sm.h"
 +#include "eapol_auth/eapol_auth_sm_i.h"
 +#include "eap_server/eap.h"
 +#include "l2_packet/l2_packet.h"
 +#include "hostapd.h"
 +#include "ieee802_1x.h"
 +#include "preauth_auth.h"
 +#include "sta_info.h"
 +#include "tkip_countermeasures.h"
 +#include "ap_drv_ops.h"
 +#include "ap_config.h"
 +#include "wpa_auth.h"
 +#include "wpa_auth_glue.h"
 +
 +
 +static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf,
 +                                struct hostapd_config *iconf,
 +                                struct wpa_auth_config *wconf)
 +{
 +      os_memset(wconf, 0, sizeof(*wconf));
 +      wconf->wpa = conf->wpa;
 +      wconf->wpa_key_mgmt = conf->wpa_key_mgmt;
 +      wconf->wpa_pairwise = conf->wpa_pairwise;
 +      wconf->wpa_group = conf->wpa_group;
 +      wconf->wpa_group_rekey = conf->wpa_group_rekey;
 +      wconf->wpa_strict_rekey = conf->wpa_strict_rekey;
 +      wconf->wpa_gmk_rekey = conf->wpa_gmk_rekey;
 +      wconf->wpa_ptk_rekey = conf->wpa_ptk_rekey;
 +      wconf->rsn_pairwise = conf->rsn_pairwise;
 +      wconf->rsn_preauth = conf->rsn_preauth;
 +      wconf->eapol_version = conf->eapol_version;
 +      wconf->peerkey = conf->peerkey;
 +      wconf->wmm_enabled = conf->wmm_enabled;
 +      wconf->wmm_uapsd = conf->wmm_uapsd;
 +      wconf->disable_pmksa_caching = conf->disable_pmksa_caching;
 +      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;
-       if (!hostapd_drv_none(hapd)) {
++      if (wconf->ssid_len > SSID_MAX_LEN)
++              wconf->ssid_len = SSID_MAX_LEN;
 +      os_memcpy(wconf->ssid, conf->ssid.ssid, wconf->ssid_len);
 +      os_memcpy(wconf->mobility_domain, conf->mobility_domain,
 +                MOBILITY_DOMAIN_ID_LEN);
 +      if (conf->nas_identifier &&
 +          os_strlen(conf->nas_identifier) <= FT_R0KH_ID_MAX_LEN) {
 +              wconf->r0_key_holder_len = os_strlen(conf->nas_identifier);
 +              os_memcpy(wconf->r0_key_holder, conf->nas_identifier,
 +                        wconf->r0_key_holder_len);
 +      }
 +      os_memcpy(wconf->r1_key_holder, conf->r1_key_holder, FT_R1KH_ID_LEN);
 +      wconf->r0_key_lifetime = conf->r0_key_lifetime;
 +      wconf->reassociation_deadline = conf->reassociation_deadline;
 +      wconf->r0kh_list = conf->r0kh_list;
 +      wconf->r1kh_list = conf->r1kh_list;
 +      wconf->pmk_r1_push = conf->pmk_r1_push;
 +      wconf->ft_over_ds = conf->ft_over_ds;
 +#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;
++      if (conf->own_ie_override &&
++          wpabuf_len(conf->own_ie_override) <= MAX_OWN_IE_OVERRIDE) {
++              wconf->own_ie_override_len = wpabuf_len(conf->own_ie_override);
++              os_memcpy(wconf->own_ie_override,
++                        wpabuf_head(conf->own_ie_override),
++                        wconf->own_ie_override_len);
++      }
 +#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 */
 +}
 +
 +
 +static void hostapd_wpa_auth_logger(void *ctx, const u8 *addr,
 +                                  logger_level level, const char *txt)
 +{
 +#ifndef CONFIG_NO_HOSTAPD_LOGGER
 +      struct hostapd_data *hapd = ctx;
 +      int hlevel;
 +
 +      switch (level) {
 +      case LOGGER_WARNING:
 +              hlevel = HOSTAPD_LEVEL_WARNING;
 +              break;
 +      case LOGGER_INFO:
 +              hlevel = HOSTAPD_LEVEL_INFO;
 +              break;
 +      case LOGGER_DEBUG:
 +      default:
 +              hlevel = HOSTAPD_LEVEL_DEBUG;
 +              break;
 +      }
 +
 +      hostapd_logger(hapd, addr, HOSTAPD_MODULE_WPA, hlevel, "%s", txt);
 +#endif /* CONFIG_NO_HOSTAPD_LOGGER */
 +}
 +
 +
 +static void hostapd_wpa_auth_disconnect(void *ctx, const u8 *addr,
 +                                      u16 reason)
 +{
 +      struct hostapd_data *hapd = ctx;
 +      wpa_printf(MSG_DEBUG, "%s: WPA authenticator requests disconnect: "
 +                 "STA " MACSTR " reason %d",
 +                 __func__, MAC2STR(addr), reason);
 +      ap_sta_disconnect(hapd, NULL, addr, reason);
 +}
 +
 +
 +static int hostapd_wpa_auth_mic_failure_report(void *ctx, const u8 *addr)
 +{
 +      struct hostapd_data *hapd = ctx;
 +      return michael_mic_failure(hapd, addr, 0);
 +}
 +
 +
++static void hostapd_wpa_auth_psk_failure_report(void *ctx, const u8 *addr)
++{
++      struct hostapd_data *hapd = ctx;
++      wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_POSSIBLE_PSK_MISMATCH MACSTR,
++              MAC2STR(addr));
++}
++
++
 +static void hostapd_wpa_auth_set_eapol(void *ctx, const u8 *addr,
 +                                     wpa_eapol_variable var, int value)
 +{
 +      struct hostapd_data *hapd = ctx;
 +      struct sta_info *sta = ap_get_sta(hapd, addr);
 +      if (sta == NULL)
 +              return;
 +      switch (var) {
 +      case WPA_EAPOL_portEnabled:
 +              ieee802_1x_notify_port_enabled(sta->eapol_sm, value);
 +              break;
 +      case WPA_EAPOL_portValid:
 +              ieee802_1x_notify_port_valid(sta->eapol_sm, value);
 +              break;
 +      case WPA_EAPOL_authorized:
 +              ieee802_1x_set_sta_authorized(hapd, sta, value);
 +              break;
 +      case WPA_EAPOL_portControl_Auto:
 +              if (sta->eapol_sm)
 +                      sta->eapol_sm->portControl = Auto;
 +              break;
 +      case WPA_EAPOL_keyRun:
 +              if (sta->eapol_sm)
 +                      sta->eapol_sm->keyRun = value ? TRUE : FALSE;
 +              break;
 +      case WPA_EAPOL_keyAvailable:
 +              if (sta->eapol_sm)
 +                      sta->eapol_sm->eap_if->eapKeyAvailable =
 +                              value ? TRUE : FALSE;
 +              break;
 +      case WPA_EAPOL_keyDone:
 +              if (sta->eapol_sm)
 +                      sta->eapol_sm->keyDone = value ? TRUE : FALSE;
 +              break;
 +      case WPA_EAPOL_inc_EapolFramesTx:
 +              if (sta->eapol_sm)
 +                      sta->eapol_sm->dot1xAuthEapolFramesTx++;
 +              break;
 +      }
 +}
 +
 +
 +static int hostapd_wpa_auth_get_eapol(void *ctx, const u8 *addr,
 +                                    wpa_eapol_variable var)
 +{
 +      struct hostapd_data *hapd = ctx;
 +      struct sta_info *sta = ap_get_sta(hapd, addr);
 +      if (sta == NULL || sta->eapol_sm == NULL)
 +              return -1;
 +      switch (var) {
 +      case WPA_EAPOL_keyRun:
 +              return sta->eapol_sm->keyRun;
 +      case WPA_EAPOL_keyAvailable:
 +              return sta->eapol_sm->eap_if->eapKeyAvailable;
 +      default:
 +              return -1;
 +      }
 +}
 +
 +
 +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;
 +      struct sta_info *sta = ap_get_sta(hapd, addr);
 +      const u8 *psk;
 +
 +#ifdef CONFIG_SAE
 +      if (sta && sta->auth_alg == WLAN_AUTH_SAE) {
 +              if (!sta->sae || prev_psk)
 +                      return NULL;
 +              return sta->sae->pmk;
 +      }
 +#endif /* CONFIG_SAE */
 +
 +      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.
 +       * logic list (all hostapd_get_psk; all sta->psk)
 +       */
 +      if (sta && sta->psk && !psk) {
 +              struct hostapd_sta_wpa_psk_short *pos;
 +              psk = sta->psk->psk;
 +              for (pos = sta->psk; pos; pos = pos->next) {
 +                      if (pos->psk == prev_psk) {
 +                              psk = pos->next ? pos->next->psk : NULL;
 +                              break;
 +                      }
 +              }
 +      }
 +      return psk;
 +}
 +
 +
 +static int hostapd_wpa_auth_get_msk(void *ctx, const u8 *addr, u8 *msk,
 +                                  size_t *len)
 +{
 +      struct hostapd_data *hapd = ctx;
 +      const u8 *key;
 +      size_t keylen;
 +      struct sta_info *sta;
 +
 +      sta = ap_get_sta(hapd, addr);
 +      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) {
 +              wpa_printf(MSG_DEBUG, "AUTH_GET_MSK: Key is null, eapol_sm: %p",
 +                         sta->eapol_sm);
 +              return -1;
 +      }
 +
 +      if (keylen > *len)
 +              keylen = *len;
 +      os_memcpy(msk, key, keylen);
 +      *len = keylen;
 +
 +      return 0;
 +}
 +
 +
 +static int hostapd_wpa_auth_set_key(void *ctx, int vlan_id, enum wpa_alg alg,
 +                                  const u8 *addr, int idx, u8 *key,
 +                                  size_t key_len)
 +{
 +      struct hostapd_data *hapd = ctx;
 +      const char *ifname = hapd->conf->iface;
 +
 +      if (vlan_id > 0) {
 +              ifname = hostapd_get_vlan_id_ifname(hapd->conf->vlan, vlan_id);
 +              if (ifname == NULL)
 +                      return -1;
 +      }
 +
 +      return hostapd_drv_set_key(ifname, hapd, alg, addr, idx, 1, NULL, 0,
 +                                 key, key_len);
 +}
 +
 +
 +static int hostapd_wpa_auth_get_seqnum(void *ctx, const u8 *addr, int idx,
 +                                     u8 *seq)
 +{
 +      struct hostapd_data *hapd = ctx;
 +      return hostapd_get_seqnum(hapd->conf->iface, hapd, addr, idx, seq);
 +}
 +
 +
 +static int hostapd_wpa_auth_send_eapol(void *ctx, const u8 *addr,
 +                                     const u8 *data, size_t data_len,
 +                                     int encrypt)
 +{
 +      struct hostapd_data *hapd = ctx;
 +      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);
 +
 +      return hostapd_drv_hapd_send_eapol(hapd, addr, data, data_len,
 +                                         encrypt, flags);
 +}
 +
 +
 +static int hostapd_wpa_auth_for_each_sta(
 +      void *ctx, int (*cb)(struct wpa_state_machine *sm, void *ctx),
 +      void *cb_ctx)
 +{
 +      struct hostapd_data *hapd = ctx;
 +      struct sta_info *sta;
 +
 +      for (sta = hapd->sta_list; sta; sta = sta->next) {
 +              if (sta->wpa_sm && cb(sta->wpa_sm, cb_ctx))
 +                      return 1;
 +      }
 +      return 0;
 +}
 +
 +
 +struct wpa_auth_iface_iter_data {
 +      int (*cb)(struct wpa_authenticator *sm, void *ctx);
 +      void *cb_ctx;
 +};
 +
 +static int wpa_auth_iface_iter(struct hostapd_iface *iface, void *ctx)
 +{
 +      struct wpa_auth_iface_iter_data *data = ctx;
 +      size_t i;
 +      for (i = 0; i < iface->num_bss; i++) {
 +              if (iface->bss[i]->wpa_auth &&
 +                  data->cb(iface->bss[i]->wpa_auth, data->cb_ctx))
 +                      return 1;
 +      }
 +      return 0;
 +}
 +
 +
 +static int hostapd_wpa_auth_for_each_auth(
 +      void *ctx, int (*cb)(struct wpa_authenticator *sm, void *ctx),
 +      void *cb_ctx)
 +{
 +      struct hostapd_data *hapd = ctx;
 +      struct wpa_auth_iface_iter_data data;
 +      if (hapd->iface->interfaces == NULL ||
 +          hapd->iface->interfaces->for_each_interface == NULL)
 +              return -1;
 +      data.cb = cb;
 +      data.cb_ctx = cb_ctx;
 +      return hapd->iface->interfaces->for_each_interface(
 +              hapd->iface->interfaces, wpa_auth_iface_iter, &data);
 +}
 +
 +
 +#ifdef CONFIG_IEEE80211R
 +
 +struct wpa_auth_ft_iface_iter_data {
 +      struct hostapd_data *src_hapd;
 +      const u8 *dst;
 +      const u8 *data;
 +      size_t data_len;
 +};
 +
 +
 +static int hostapd_wpa_auth_ft_iter(struct hostapd_iface *iface, void *ctx)
 +{
 +      struct wpa_auth_ft_iface_iter_data *idata = ctx;
 +      struct hostapd_data *hapd;
 +      size_t j;
 +
 +      for (j = 0; j < iface->num_bss; j++) {
 +              hapd = iface->bss[j];
 +              if (hapd == idata->src_hapd)
 +                      continue;
 +              if (os_memcmp(hapd->own_addr, idata->dst, ETH_ALEN) == 0) {
 +                      wpa_printf(MSG_DEBUG, "FT: Send RRB data directly to "
 +                                 "locally managed BSS " MACSTR "@%s -> "
 +                                 MACSTR "@%s",
 +                                 MAC2STR(idata->src_hapd->own_addr),
 +                                 idata->src_hapd->conf->iface,
 +                                 MAC2STR(hapd->own_addr), hapd->conf->iface);
 +                      wpa_ft_rrb_rx(hapd->wpa_auth,
 +                                    idata->src_hapd->own_addr,
 +                                    idata->data, idata->data_len);
 +                      return 1;
 +              }
 +      }
 +
 +      return 0;
 +}
 +
 +#endif /* CONFIG_IEEE80211R */
 +
 +
 +static int hostapd_wpa_auth_send_ether(void *ctx, const u8 *dst, u16 proto,
 +                                     const u8 *data, size_t data_len)
 +{
 +      struct hostapd_data *hapd = ctx;
 +      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) {
 +              int res;
 +              struct wpa_auth_ft_iface_iter_data idata;
 +              idata.src_hapd = hapd;
 +              idata.dst = dst;
 +              idata.data = data;
 +              idata.data_len = data_len;
 +              res = hapd->iface->interfaces->for_each_interface(
 +                      hapd->iface->interfaces, hostapd_wpa_auth_ft_iter,
 +                      &idata);
 +              if (res == 1)
 +                      return data_len;
 +      }
 +#endif /* CONFIG_IEEE80211R */
 +
 +      if (hapd->driver && hapd->driver->send_ether)
 +              return hapd->driver->send_ether(hapd->drv_priv, dst,
 +                                              hapd->own_addr, proto,
 +                                              data, data_len);
 +      if (hapd->l2 == NULL)
 +              return -1;
 +
 +      buf = os_malloc(sizeof(*buf) + data_len);
 +      if (buf == NULL)
 +              return -1;
 +      os_memcpy(buf->h_dest, dst, ETH_ALEN);
 +      os_memcpy(buf->h_source, hapd->own_addr, ETH_ALEN);
 +      buf->h_proto = host_to_be16(proto);
 +      os_memcpy(buf + 1, data, data_len);
 +      ret = l2_packet_send(hapd->l2, dst, proto, (u8 *) buf,
 +                           sizeof(*buf) + data_len);
 +      os_free(buf);
 +      return ret;
 +}
 +
 +
 +#ifdef CONFIG_IEEE80211R
 +
 +static int hostapd_wpa_auth_send_ft_action(void *ctx, const u8 *dst,
 +                                         const u8 *data, size_t data_len)
 +{
 +      struct hostapd_data *hapd = ctx;
 +      int res;
 +      struct ieee80211_mgmt *m;
 +      size_t mlen;
 +      struct sta_info *sta;
 +
 +      sta = ap_get_sta(hapd, dst);
 +      if (sta == NULL || sta->wpa_sm == NULL)
 +              return -1;
 +
 +      m = os_zalloc(sizeof(*m) + data_len);
 +      if (m == NULL)
 +              return -1;
 +      mlen = ((u8 *) &m->u - (u8 *) m) + data_len;
 +      m->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
 +                                      WLAN_FC_STYPE_ACTION);
 +      os_memcpy(m->da, dst, ETH_ALEN);
 +      os_memcpy(m->sa, hapd->own_addr, ETH_ALEN);
 +      os_memcpy(m->bssid, hapd->own_addr, ETH_ALEN);
 +      os_memcpy(&m->u, data, data_len);
 +
 +      res = hostapd_drv_send_mlme(hapd, (u8 *) m, mlen, 0);
 +      os_free(m);
 +      return res;
 +}
 +
 +
 +static struct wpa_state_machine *
 +hostapd_wpa_auth_add_sta(void *ctx, const u8 *sta_addr)
 +{
 +      struct hostapd_data *hapd = ctx;
 +      struct sta_info *sta;
 +
 +      if (hostapd_add_sta_node(hapd, sta_addr, WLAN_AUTH_FT) < 0)
 +              return NULL;
 +
 +      sta = ap_sta_add(hapd, sta_addr);
 +      if (sta == NULL)
 +              return NULL;
 +      if (sta->wpa_sm) {
 +              sta->auth_alg = WLAN_AUTH_FT;
 +              return sta->wpa_sm;
 +      }
 +
 +      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;
 +      }
 +      sta->auth_alg = WLAN_AUTH_FT;
 +
 +      return sta->wpa_sm;
 +}
 +
 +
 +static void hostapd_rrb_receive(void *ctx, const u8 *src_addr, const u8 *buf,
 +                              size_t len)
 +{
 +      struct hostapd_data *hapd = ctx;
 +      struct l2_ethhdr *ethhdr;
 +      if (len < sizeof(*ethhdr))
 +              return;
 +      ethhdr = (struct l2_ethhdr *) buf;
 +      wpa_printf(MSG_DEBUG, "FT: RRB received packet " MACSTR " -> "
 +                 MACSTR, MAC2STR(ethhdr->h_source), MAC2STR(ethhdr->h_dest));
 +      wpa_ft_rrb_rx(hapd->wpa_auth, ethhdr->h_source, buf + sizeof(*ethhdr),
 +                    len - sizeof(*ethhdr));
 +}
 +
 +
 +static int hostapd_wpa_auth_add_tspec(void *ctx, const u8 *sta_addr,
 +                                    u8 *tspec_ie, size_t tspec_ielen)
 +{
 +      struct hostapd_data *hapd = ctx;
 +      return hostapd_add_tspec(hapd, sta_addr, tspec_ie, tspec_ielen);
 +}
 +
 +#endif /* CONFIG_IEEE80211R */
 +
 +
 +int hostapd_setup_wpa(struct hostapd_data *hapd)
 +{
 +      struct wpa_auth_config _conf;
 +      struct wpa_auth_callbacks cb;
 +      const u8 *wpa_ie;
 +      size_t wpa_ie_len;
 +
 +      hostapd_wpa_auth_conf(hapd->conf, hapd->iconf, &_conf);
 +      if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_EAPOL_TX_STATUS)
 +              _conf.tx_status = 1;
 +      if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_MLME)
 +              _conf.ap_mlme = 1;
 +      os_memset(&cb, 0, sizeof(cb));
 +      cb.ctx = hapd;
 +      cb.logger = hostapd_wpa_auth_logger;
 +      cb.disconnect = hostapd_wpa_auth_disconnect;
 +      cb.mic_failure_report = hostapd_wpa_auth_mic_failure_report;
++      cb.psk_failure_report = hostapd_wpa_auth_psk_failure_report;
 +      cb.set_eapol = hostapd_wpa_auth_set_eapol;
 +      cb.get_eapol = hostapd_wpa_auth_get_eapol;
 +      cb.get_psk = hostapd_wpa_auth_get_psk;
 +      cb.get_msk = hostapd_wpa_auth_get_msk;
 +      cb.set_key = hostapd_wpa_auth_set_key;
 +      cb.get_seqnum = hostapd_wpa_auth_get_seqnum;
 +      cb.send_eapol = hostapd_wpa_auth_send_eapol;
 +      cb.for_each_sta = hostapd_wpa_auth_for_each_sta;
 +      cb.for_each_auth = hostapd_wpa_auth_for_each_auth;
 +      cb.send_ether = hostapd_wpa_auth_send_ether;
 +#ifdef CONFIG_IEEE80211R
 +      cb.send_ft_action = hostapd_wpa_auth_send_ft_action;
 +      cb.add_sta = hostapd_wpa_auth_add_sta;
 +      cb.add_tspec = hostapd_wpa_auth_add_tspec;
 +#endif /* CONFIG_IEEE80211R */
 +      hapd->wpa_auth = wpa_init(hapd->own_addr, &_conf, &cb);
 +      if (hapd->wpa_auth == NULL) {
 +              wpa_printf(MSG_ERROR, "WPA initialization failed.");
 +              return -1;
 +      }
 +
 +      if (hostapd_set_privacy(hapd, 1)) {
 +              wpa_printf(MSG_ERROR, "Could not set PrivacyInvoked "
 +                         "for interface %s", hapd->conf->iface);
 +              return -1;
 +      }
 +
 +      wpa_ie = wpa_auth_get_wpa_ie(hapd->wpa_auth, &wpa_ie_len);
 +      if (hostapd_set_generic_elem(hapd, wpa_ie, wpa_ie_len)) {
 +              wpa_printf(MSG_ERROR, "Failed to configure WPA IE for "
 +                         "the kernel driver.");
 +              return -1;
 +      }
 +
 +      if (rsn_preauth_iface_init(hapd)) {
 +              wpa_printf(MSG_ERROR, "Initialization of RSN "
 +                         "pre-authentication failed.");
 +              return -1;
 +      }
 +
 +#ifdef CONFIG_IEEE80211R
++      if (!hostapd_drv_none(hapd) && hapd->conf->ft_over_ds &&
++          wpa_key_mgmt_ft(hapd->conf->wpa_key_mgmt)) {
 +              hapd->l2 = l2_packet_init(hapd->conf->bridge[0] ?
 +                                        hapd->conf->bridge :
 +                                        hapd->conf->iface, NULL, ETH_P_RRB,
 +                                        hostapd_rrb_receive, hapd, 1);
 +              if (hapd->l2 == NULL &&
 +                  (hapd->driver == NULL ||
 +                   hapd->driver->send_ether == NULL)) {
 +                      wpa_printf(MSG_ERROR, "Failed to open l2_packet "
 +                                 "interface");
 +                      return -1;
 +              }
 +      }
 +#endif /* CONFIG_IEEE80211R */
 +
 +      return 0;
 +
 +}
 +
 +
 +void hostapd_reconfig_wpa(struct hostapd_data *hapd)
 +{
 +      struct wpa_auth_config wpa_auth_conf;
 +      hostapd_wpa_auth_conf(hapd->conf, hapd->iconf, &wpa_auth_conf);
 +      wpa_reconfig(hapd->wpa_auth, &wpa_auth_conf);
 +}
 +
 +
 +void hostapd_deinit_wpa(struct hostapd_data *hapd)
 +{
 +      ieee80211_tkip_countermeasures_deinit(hapd);
 +      rsn_preauth_iface_deinit(hapd);
 +      if (hapd->wpa_auth) {
 +              wpa_deinit(hapd->wpa_auth);
 +              hapd->wpa_auth = NULL;
 +
 +              if (hostapd_set_privacy(hapd, 0)) {
 +                      wpa_printf(MSG_DEBUG, "Could not disable "
 +                                 "PrivacyInvoked for interface %s",
 +                                 hapd->conf->iface);
 +              }
 +
 +              if (hostapd_set_generic_elem(hapd, (u8 *) "", 0)) {
 +                      wpa_printf(MSG_DEBUG, "Could not remove generic "
 +                                 "information element from interface %s",
 +                                 hapd->conf->iface);
 +              }
 +      }
 +      ieee802_1x_deinit(hapd);
 +
 +#ifdef CONFIG_IEEE80211R
 +      l2_packet_deinit(hapd->l2);
 +      hapd->l2 = NULL;
 +#endif /* CONFIG_IEEE80211R */
 +}
index 7b2cd3ea8ed445cb74df0662efff61a3282172d0,0000000000000000000000000000000000000000..57b098f2ed72a5e5399964503f9def59ea8da811
mode 100644,000000..100644
--- /dev/null
@@@ -1,257 -1,0 +1,259 @@@
 +/*
 + * hostapd - IEEE 802.11i-2004 / WPA Authenticator: Internal definitions
 + * 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 WPA_AUTH_I_H
 +#define WPA_AUTH_I_H
 +
 +/* max(dot11RSNAConfigGroupUpdateCount,dot11RSNAConfigPairwiseUpdateCount) */
 +#define RSNA_MAX_EAPOL_RETRIES 4
 +
 +struct wpa_group;
 +
 +struct wpa_stsl_negotiation {
 +      struct wpa_stsl_negotiation *next;
 +      u8 initiator[ETH_ALEN];
 +      u8 peer[ETH_ALEN];
 +};
 +
 +
 +struct wpa_state_machine {
 +      struct wpa_authenticator *wpa_auth;
 +      struct wpa_group *group;
 +
 +      u8 addr[ETH_ALEN];
 +      u8 p2p_dev_addr[ETH_ALEN];
 +
 +      enum {
 +              WPA_PTK_INITIALIZE, WPA_PTK_DISCONNECT, WPA_PTK_DISCONNECTED,
 +              WPA_PTK_AUTHENTICATION, WPA_PTK_AUTHENTICATION2,
 +              WPA_PTK_INITPMK, WPA_PTK_INITPSK, WPA_PTK_PTKSTART,
 +              WPA_PTK_PTKCALCNEGOTIATING, WPA_PTK_PTKCALCNEGOTIATING2,
 +              WPA_PTK_PTKINITNEGOTIATING, WPA_PTK_PTKINITDONE
 +      } wpa_ptk_state;
 +
 +      enum {
 +              WPA_PTK_GROUP_IDLE = 0,
 +              WPA_PTK_GROUP_REKEYNEGOTIATING,
 +              WPA_PTK_GROUP_REKEYESTABLISHED,
 +              WPA_PTK_GROUP_KEYERROR
 +      } wpa_ptk_group_state;
 +
 +      Boolean Init;
 +      Boolean DeauthenticationRequest;
 +      Boolean AuthenticationRequest;
 +      Boolean ReAuthenticationRequest;
 +      Boolean Disconnect;
 +      int TimeoutCtr;
 +      int GTimeoutCtr;
 +      Boolean TimeoutEvt;
 +      Boolean EAPOLKeyReceived;
 +      Boolean EAPOLKeyPairwise;
 +      Boolean EAPOLKeyRequest;
 +      Boolean MICVerified;
 +      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;
 +      Boolean pairwise_set;
 +      int keycount;
 +      Boolean Pair;
 +      struct wpa_key_replay_counter {
 +              u8 counter[WPA_REPLAY_COUNTER_LEN];
 +              Boolean valid;
 +      } key_replay[RSNA_MAX_EAPOL_RETRIES],
 +              prev_key_replay[RSNA_MAX_EAPOL_RETRIES];
 +      Boolean PInitAKeys; /* WPA only, not in IEEE 802.11i */
 +      Boolean PTKRequest; /* not in IEEE 802.11i state machine */
 +      Boolean has_GTK;
 +      Boolean PtkGroupInit; /* init request for PTK Group state machine */
 +
 +      u8 *last_rx_eapol_key; /* starting from IEEE 802.1X header */
 +      size_t last_rx_eapol_key_len;
 +
 +      unsigned int changed:1;
 +      unsigned int in_step_loop:1;
 +      unsigned int pending_deinit:1;
 +      unsigned int started:1;
 +      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;
 +#endif /* CONFIG_IEEE80211R */
 +      unsigned int is_wnmsleep:1;
 +
 +      u8 req_replay_counter[WPA_REPLAY_COUNTER_LEN];
 +      int req_replay_counter_used;
 +
 +      u8 *wpa_ie;
 +      size_t wpa_ie_len;
 +
 +      enum {
 +              WPA_VERSION_NO_WPA = 0 /* WPA not used */,
 +              WPA_VERSION_WPA = 1 /* WPA / IEEE 802.11i/D3.0 */,
 +              WPA_VERSION_WPA2 = 2 /* WPA2 / IEEE 802.11i */
 +      } wpa;
 +      int pairwise; /* Pairwise cipher suite, WPA_CIPHER_* */
 +      int wpa_key_mgmt; /* the selected WPA_KEY_MGMT_* */
 +      struct rsn_pmksa_cache_entry *pmksa;
 +
 +      u32 dot11RSNAStatsTKIPLocalMICFailures;
 +      u32 dot11RSNAStatsTKIPRemoteMICFailures;
 +
 +#ifdef CONFIG_IEEE80211R
 +      u8 xxkey[PMK_LEN]; /* PSK or the second 256 bits of MSK */
 +      size_t xxkey_len;
 +      u8 pmk_r1_name[WPA_PMK_NAME_LEN]; /* PMKR1Name derived from FT Auth
 +                                         * Request */
 +      u8 r0kh_id[FT_R0KH_ID_MAX_LEN]; /* R0KH-ID from FT Auth Request */
 +      size_t r0kh_id_len;
 +      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 */
 +};
 +
 +
 +/* per group key state machine data */
 +struct wpa_group {
 +      struct wpa_group *next;
 +      int vlan_id;
 +
 +      Boolean GInit;
 +      int GKeyDoneStations;
 +      Boolean GTKReKey;
 +      int GTK_len;
 +      int GN, GM;
 +      Boolean GTKAuthenticator;
 +      u8 Counter[WPA_NONCE_LEN];
 +
 +      enum {
 +              WPA_GROUP_GTK_INIT = 0,
 +              WPA_GROUP_SETKEYS, WPA_GROUP_SETKEYSDONE,
 +              WPA_GROUP_FATAL_FAILURE
 +      } wpa_group_state;
 +
 +      u8 GMK[WPA_GMK_LEN];
 +      u8 GTK[2][WPA_GTK_MAX_LEN];
 +      u8 GNonce[WPA_NONCE_LEN];
 +      Boolean changed;
 +      Boolean first_sta_seen;
 +      Boolean reject_4way_hs_for_entropy;
 +#ifdef CONFIG_IEEE80211W
 +      u8 IGTK[2][WPA_IGTK_MAX_LEN];
 +      int GN_igtk, GM_igtk;
 +#endif /* CONFIG_IEEE80211W */
++      /* Number of references except those in struct wpa_group->next */
++      unsigned int references;
 +};
 +
 +
 +struct wpa_ft_pmk_cache;
 +
 +/* per authenticator data */
 +struct wpa_authenticator {
 +      struct wpa_group *group;
 +
 +      unsigned int dot11RSNAStatsTKIPRemoteMICFailures;
 +      u32 dot11RSNAAuthenticationSuiteSelected;
 +      u32 dot11RSNAPairwiseCipherSelected;
 +      u32 dot11RSNAGroupCipherSelected;
 +      u8 dot11RSNAPMKIDUsed[PMKID_LEN];
 +      u32 dot11RSNAAuthenticationSuiteRequested; /* FIX: update */
 +      u32 dot11RSNAPairwiseCipherRequested; /* FIX: update */
 +      u32 dot11RSNAGroupCipherRequested; /* FIX: update */
 +      unsigned int dot11RSNATKIPCounterMeasuresInvoked;
 +      unsigned int dot11RSNA4WayHandshakeFailures;
 +
 +      struct wpa_stsl_negotiation *stsl_negotiations;
 +
 +      struct wpa_auth_config conf;
 +      struct wpa_auth_callbacks cb;
 +
 +      u8 *wpa_ie;
 +      size_t wpa_ie_len;
 +
 +      u8 addr[ETH_ALEN];
 +
 +      struct rsn_pmksa_cache *pmksa;
 +      struct wpa_ft_pmk_cache *ft_pmk_cache;
 +
 +#ifdef CONFIG_P2P
 +      struct bitfield *ip_pool;
 +#endif /* CONFIG_P2P */
 +};
 +
 +
 +int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len,
 +                   const u8 *pmkid);
 +void wpa_auth_logger(struct wpa_authenticator *wpa_auth, const u8 *addr,
 +                   logger_level level, const char *txt);
 +void wpa_auth_vlogger(struct wpa_authenticator *wpa_auth, const u8 *addr,
 +                    logger_level level, const char *fmt, ...);
 +void __wpa_send_eapol(struct wpa_authenticator *wpa_auth,
 +                    struct wpa_state_machine *sm, int key_info,
 +                    const u8 *key_rsc, const u8 *nonce,
 +                    const u8 *kde, size_t kde_len,
 +                    int keyidx, int encr, int force_version);
 +int wpa_auth_for_each_sta(struct wpa_authenticator *wpa_auth,
 +                        int (*cb)(struct wpa_state_machine *sm, void *ctx),
 +                        void *cb_ctx);
 +int wpa_auth_for_each_auth(struct wpa_authenticator *wpa_auth,
 +                         int (*cb)(struct wpa_authenticator *a, void *ctx),
 +                         void *cb_ctx);
 +
 +#ifdef CONFIG_PEERKEY
 +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,
 +                 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,
 +              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,
 +              const u8 *key_data, size_t key_data_len);
 +#endif /* CONFIG_PEERKEY */
 +
 +#ifdef CONFIG_IEEE80211R
 +int wpa_write_mdie(struct wpa_auth_config *conf, u8 *buf, size_t len);
 +int wpa_write_ftie(struct wpa_auth_config *conf, const u8 *r0kh_id,
 +                 size_t r0kh_id_len,
 +                 const u8 *anonce, const u8 *snonce,
 +                 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);
 +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);
 +#endif /* CONFIG_IEEE80211R */
 +
 +#endif /* WPA_AUTH_I_H */
index f2872970affee61d3637b4b930a94624b03e497c,0000000000000000000000000000000000000000..eafb828b8d60e4cc0c9c8033c2a84fa92f6e8d0f
mode 100644,000000..100644
--- /dev/null
@@@ -1,919 -1,0 +1,937 @@@
-       if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) {
 +/*
 + * hostapd - WPA/RSN IE and KDE definitions
 + * 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"
 +
 +#include "utils/common.h"
 +#include "common/ieee802_11_defs.h"
 +#include "eapol_auth/eapol_auth_sm.h"
 +#include "ap_config.h"
 +#include "ieee802_11.h"
 +#include "wpa_auth.h"
 +#include "pmksa_cache_auth.h"
 +#include "wpa_auth_ie.h"
 +#include "wpa_auth_i.h"
 +
 +
 +#ifdef CONFIG_RSN_TESTING
 +int rsn_testing = 0;
 +#endif /* CONFIG_RSN_TESTING */
 +
 +
 +static int wpa_write_wpa_ie(struct wpa_auth_config *conf, u8 *buf, size_t len)
 +{
 +      struct wpa_ie_hdr *hdr;
 +      int num_suites;
 +      u8 *pos, *count;
 +      u32 suite;
 +
 +      hdr = (struct wpa_ie_hdr *) buf;
 +      hdr->elem_id = WLAN_EID_VENDOR_SPECIFIC;
 +      RSN_SELECTOR_PUT(hdr->oui, WPA_OUI_TYPE);
 +      WPA_PUT_LE16(hdr->version, WPA_VERSION);
 +      pos = (u8 *) (hdr + 1);
 +
 +      suite = wpa_cipher_to_suite(WPA_PROTO_WPA, conf->wpa_group);
 +      if (suite == 0) {
 +              wpa_printf(MSG_DEBUG, "Invalid group cipher (%d).",
 +                         conf->wpa_group);
 +              return -1;
 +      }
 +      RSN_SELECTOR_PUT(pos, suite);
 +      pos += WPA_SELECTOR_LEN;
 +
 +      count = pos;
 +      pos += 2;
 +
 +      num_suites = wpa_cipher_put_suites(pos, conf->wpa_pairwise);
 +      if (num_suites == 0) {
 +              wpa_printf(MSG_DEBUG, "Invalid pairwise cipher (%d).",
 +                         conf->wpa_pairwise);
 +              return -1;
 +      }
 +      pos += num_suites * WPA_SELECTOR_LEN;
 +      WPA_PUT_LE16(count, num_suites);
 +
 +      num_suites = 0;
 +      count = pos;
 +      pos += 2;
 +
 +      if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X) {
 +              RSN_SELECTOR_PUT(pos, WPA_AUTH_KEY_MGMT_UNSPEC_802_1X);
 +              pos += WPA_SELECTOR_LEN;
 +              num_suites++;
 +      }
 +      if (conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK) {
 +              RSN_SELECTOR_PUT(pos, WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X);
 +              pos += WPA_SELECTOR_LEN;
 +              num_suites++;
 +      }
 +
 +      if (num_suites == 0) {
 +              wpa_printf(MSG_DEBUG, "Invalid key management type (%d).",
 +                         conf->wpa_key_mgmt);
 +              return -1;
 +      }
 +      WPA_PUT_LE16(count, num_suites);
 +
 +      /* WPA Capabilities; use defaults, so no need to include it */
 +
 +      hdr->len = (pos - buf) - 2;
 +
 +      return pos - buf;
 +}
 +
 +
 +int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len,
 +                   const u8 *pmkid)
 +{
 +      struct rsn_ie_hdr *hdr;
 +      int num_suites, res;
 +      u8 *pos, *count;
 +      u16 capab;
 +      u32 suite;
 +
 +      hdr = (struct rsn_ie_hdr *) buf;
 +      hdr->elem_id = WLAN_EID_RSN;
 +      WPA_PUT_LE16(hdr->version, RSN_VERSION);
 +      pos = (u8 *) (hdr + 1);
 +
 +      suite = wpa_cipher_to_suite(WPA_PROTO_RSN, conf->wpa_group);
 +      if (suite == 0) {
 +              wpa_printf(MSG_DEBUG, "Invalid group cipher (%d).",
 +                         conf->wpa_group);
 +              return -1;
 +      }
 +      RSN_SELECTOR_PUT(pos, suite);
 +      pos += RSN_SELECTOR_LEN;
 +
 +      num_suites = 0;
 +      count = pos;
 +      pos += 2;
 +
 +#ifdef CONFIG_RSN_TESTING
 +      if (rsn_testing) {
 +              RSN_SELECTOR_PUT(pos, RSN_SELECTOR(0x12, 0x34, 0x56, 1));
 +              pos += RSN_SELECTOR_LEN;
 +              num_suites++;
 +      }
 +#endif /* CONFIG_RSN_TESTING */
 +
 +      res = rsn_cipher_put_suites(pos, conf->rsn_pairwise);
 +      num_suites += res;
 +      pos += res * RSN_SELECTOR_LEN;
 +
 +#ifdef CONFIG_RSN_TESTING
 +      if (rsn_testing) {
 +              RSN_SELECTOR_PUT(pos, RSN_SELECTOR(0x12, 0x34, 0x56, 2));
 +              pos += RSN_SELECTOR_LEN;
 +              num_suites++;
 +      }
 +#endif /* CONFIG_RSN_TESTING */
 +
 +      if (num_suites == 0) {
 +              wpa_printf(MSG_DEBUG, "Invalid pairwise cipher (%d).",
 +                         conf->rsn_pairwise);
 +              return -1;
 +      }
 +      WPA_PUT_LE16(count, num_suites);
 +
 +      num_suites = 0;
 +      count = pos;
 +      pos += 2;
 +
 +#ifdef CONFIG_RSN_TESTING
 +      if (rsn_testing) {
 +              RSN_SELECTOR_PUT(pos, RSN_SELECTOR(0x12, 0x34, 0x56, 1));
 +              pos += RSN_SELECTOR_LEN;
 +              num_suites++;
 +      }
 +#endif /* CONFIG_RSN_TESTING */
 +
 +      if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X) {
 +              RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_UNSPEC_802_1X);
 +              pos += RSN_SELECTOR_LEN;
 +              num_suites++;
 +      }
 +      if (conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK) {
 +              RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X);
 +              pos += RSN_SELECTOR_LEN;
 +              num_suites++;
 +      }
 +#ifdef CONFIG_IEEE80211R
 +      if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) {
 +              RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_802_1X);
 +              pos += RSN_SELECTOR_LEN;
 +              num_suites++;
 +      }
 +      if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_PSK) {
 +              RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_PSK);
 +              pos += RSN_SELECTOR_LEN;
 +              num_suites++;
 +      }
 +#endif /* CONFIG_IEEE80211R */
 +#ifdef CONFIG_IEEE80211W
 +      if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) {
 +              RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SHA256);
 +              pos += RSN_SELECTOR_LEN;
 +              num_suites++;
 +      }
 +      if (conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK_SHA256) {
 +              RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_PSK_SHA256);
 +              pos += RSN_SELECTOR_LEN;
 +              num_suites++;
 +      }
 +#endif /* CONFIG_IEEE80211W */
 +#ifdef CONFIG_SAE
 +      if (conf->wpa_key_mgmt & WPA_KEY_MGMT_SAE) {
 +              RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_SAE);
 +              pos += RSN_SELECTOR_LEN;
 +              num_suites++;
 +      }
 +      if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_SAE) {
 +              RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_SAE);
 +              pos += RSN_SELECTOR_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) {
 +              RSN_SELECTOR_PUT(pos, RSN_SELECTOR(0x12, 0x34, 0x56, 2));
 +              pos += RSN_SELECTOR_LEN;
 +              num_suites++;
 +      }
 +#endif /* CONFIG_RSN_TESTING */
 +
 +      if (num_suites == 0) {
 +              wpa_printf(MSG_DEBUG, "Invalid key management type (%d).",
 +                         conf->wpa_key_mgmt);
 +              return -1;
 +      }
 +      WPA_PUT_LE16(count, num_suites);
 +
 +      /* RSN Capabilities */
 +      capab = 0;
 +      if (conf->rsn_preauth)
 +              capab |= WPA_CAPABILITY_PREAUTH;
 +      if (conf->peerkey)
 +              capab |= WPA_CAPABILITY_PEERKEY_ENABLED;
 +      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 */
 +#ifdef CONFIG_RSN_TESTING
 +      if (rsn_testing)
 +              capab |= BIT(8) | BIT(14) | BIT(15);
 +#endif /* CONFIG_RSN_TESTING */
 +      WPA_PUT_LE16(pos, capab);
 +      pos += 2;
 +
 +      if (pmkid) {
 +              if (pos + 2 + PMKID_LEN > buf + len)
 +                      return -1;
 +              /* PMKID Count */
 +              WPA_PUT_LE16(pos, 1);
 +              pos += 2;
 +              os_memcpy(pos, pmkid, PMKID_LEN);
 +              pos += PMKID_LEN;
 +      }
 +
 +#ifdef CONFIG_IEEE80211W
++      if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION &&
++          conf->group_mgmt_cipher != WPA_CIPHER_AES_128_CMAC) {
 +              if (pos + 2 + 4 > buf + len)
 +                      return -1;
 +              if (pmkid == NULL) {
 +                      /* PMKID Count */
 +                      WPA_PUT_LE16(pos, 0);
 +                      pos += 2;
 +              }
 +
 +              /* Management Group Cipher Suite */
 +              switch (conf->group_mgmt_cipher) {
 +              case WPA_CIPHER_AES_128_CMAC:
 +                      RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_AES_128_CMAC);
 +                      break;
 +              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 */
 +
 +#ifdef CONFIG_RSN_TESTING
 +      if (rsn_testing) {
 +              /*
 +               * Fill in any defined fields and add extra data to the end of
 +               * the element.
 +               */
 +              int pmkid_count_set = pmkid != NULL;
 +              if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION)
 +                      pmkid_count_set = 1;
 +              /* PMKID Count */
 +              WPA_PUT_LE16(pos, 0);
 +              pos += 2;
 +              if (conf->ieee80211w == NO_MGMT_FRAME_PROTECTION) {
 +                      /* Management Group Cipher Suite */
 +                      RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_AES_128_CMAC);
 +                      pos += RSN_SELECTOR_LEN;
 +              }
 +
 +              os_memset(pos, 0x12, 17);
 +              pos += 17;
 +      }
 +#endif /* CONFIG_RSN_TESTING */
 +
 +      hdr->len = (pos - buf) - 2;
 +
 +      return pos - buf;
 +}
 +
 +
 +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];
 +      int res;
 +
++#ifdef CONFIG_TESTING_OPTIONS
++      if (wpa_auth->conf.own_ie_override_len) {
++              wpa_hexdump(MSG_DEBUG, "WPA: Forced own IE(s) for testing",
++                          wpa_auth->conf.own_ie_override,
++                          wpa_auth->conf.own_ie_override_len);
++              os_free(wpa_auth->wpa_ie);
++              wpa_auth->wpa_ie =
++                      os_malloc(wpa_auth->conf.own_ie_override_len);
++              if (wpa_auth->wpa_ie == NULL)
++                      return -1;
++              os_memcpy(wpa_auth->wpa_ie, wpa_auth->conf.own_ie_override,
++                        wpa_auth->conf.own_ie_override_len);
++              wpa_auth->wpa_ie_len = wpa_auth->conf.own_ie_override_len;
++              return 0;
++      }
++#endif /* CONFIG_TESTING_OPTIONS */
++
 +      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);
 +              if (res < 0)
 +                      return res;
 +              pos += res;
 +      }
 +#ifdef CONFIG_IEEE80211R
 +      if (wpa_key_mgmt_ft(wpa_auth->conf.wpa_key_mgmt)) {
 +              res = wpa_write_mdie(&wpa_auth->conf, pos,
 +                                   buf + sizeof(buf) - pos);
 +              if (res < 0)
 +                      return res;
 +              pos += res;
 +      }
 +#endif /* CONFIG_IEEE80211R */
 +      if (wpa_auth->conf.wpa & WPA_PROTO_WPA) {
 +              res = wpa_write_wpa_ie(&wpa_auth->conf,
 +                                     pos, buf + sizeof(buf) - pos);
 +              if (res < 0)
 +                      return res;
 +              pos += res;
 +      }
 +
 +      os_free(wpa_auth->wpa_ie);
 +      wpa_auth->wpa_ie = os_malloc(pos - buf);
 +      if (wpa_auth->wpa_ie == NULL)
 +              return -1;
 +      os_memcpy(wpa_auth->wpa_ie, buf, pos - buf);
 +      wpa_auth->wpa_ie_len = pos - buf;
 +
 +      return 0;
 +}
 +
 +
 +u8 * wpa_add_kde(u8 *pos, u32 kde, const u8 *data, size_t data_len,
 +               const u8 *data2, size_t data2_len)
 +{
 +      *pos++ = WLAN_EID_VENDOR_SPECIFIC;
 +      *pos++ = RSN_SELECTOR_LEN + data_len + data2_len;
 +      RSN_SELECTOR_PUT(pos, kde);
 +      pos += RSN_SELECTOR_LEN;
 +      os_memcpy(pos, data, data_len);
 +      pos += data_len;
 +      if (data2) {
 +              os_memcpy(pos, data2, data2_len);
 +              pos += data2_len;
 +      }
 +      return pos;
 +}
 +
 +
 +struct wpa_auth_okc_iter_data {
 +      struct rsn_pmksa_cache_entry *pmksa;
 +      const u8 *aa;
 +      const u8 *spa;
 +      const u8 *pmkid;
 +};
 +
 +
 +static int wpa_auth_okc_iter(struct wpa_authenticator *a, void *ctx)
 +{
 +      struct wpa_auth_okc_iter_data *data = ctx;
 +      data->pmksa = pmksa_cache_get_okc(a->pmksa, data->aa, data->spa,
 +                                        data->pmkid);
 +      if (data->pmksa)
 +              return 1;
 +      return 0;
 +}
 +
 +
 +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)
 +{
 +      struct wpa_ie_data data;
 +      int ciphers, key_mgmt, res, version;
 +      u32 selector;
 +      size_t i;
 +      const u8 *pmkid = NULL;
 +
 +      if (wpa_auth == NULL || sm == NULL)
 +              return WPA_NOT_ENABLED;
 +
 +      if (wpa_ie == NULL || wpa_ie_len < 1)
 +              return WPA_INVALID_IE;
 +
 +      if (wpa_ie[0] == WLAN_EID_RSN)
 +              version = WPA_PROTO_RSN;
 +      else
 +              version = WPA_PROTO_WPA;
 +
 +      if (!(wpa_auth->conf.wpa & version)) {
 +              wpa_printf(MSG_DEBUG, "Invalid WPA proto (%d) from " MACSTR,
 +                         version, MAC2STR(sm->addr));
 +              return WPA_INVALID_PROTO;
 +      }
 +
 +      if (version == WPA_PROTO_RSN) {
 +              res = wpa_parse_wpa_ie_rsn(wpa_ie, wpa_ie_len, &data);
 +
 +              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;
 +              else if (data.key_mgmt & WPA_KEY_MGMT_FT_PSK)
 +                      selector = RSN_AUTH_KEY_MGMT_FT_PSK;
 +#endif /* CONFIG_IEEE80211R */
 +#ifdef CONFIG_IEEE80211W
 +              else if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256)
 +                      selector = RSN_AUTH_KEY_MGMT_802_1X_SHA256;
 +              else if (data.key_mgmt & WPA_KEY_MGMT_PSK_SHA256)
 +                      selector = RSN_AUTH_KEY_MGMT_PSK_SHA256;
 +#endif /* CONFIG_IEEE80211W */
 +#ifdef CONFIG_SAE
 +              else if (data.key_mgmt & WPA_KEY_MGMT_SAE)
 +                      selector = RSN_AUTH_KEY_MGMT_SAE;
 +              else if (data.key_mgmt & WPA_KEY_MGMT_FT_SAE)
 +                      selector = RSN_AUTH_KEY_MGMT_FT_SAE;
 +#endif /* CONFIG_SAE */
 +              else if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X)
 +                      selector = RSN_AUTH_KEY_MGMT_UNSPEC_802_1X;
 +              else if (data.key_mgmt & WPA_KEY_MGMT_PSK)
 +                      selector = RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X;
 +              wpa_auth->dot11RSNAAuthenticationSuiteSelected = selector;
 +
 +              selector = wpa_cipher_to_suite(WPA_PROTO_RSN,
 +                                             data.pairwise_cipher);
 +              if (!selector)
 +                      selector = RSN_CIPHER_SUITE_CCMP;
 +              wpa_auth->dot11RSNAPairwiseCipherSelected = selector;
 +
 +              selector = wpa_cipher_to_suite(WPA_PROTO_RSN,
 +                                             data.group_cipher);
 +              if (!selector)
 +                      selector = RSN_CIPHER_SUITE_CCMP;
 +              wpa_auth->dot11RSNAGroupCipherSelected = selector;
 +      } else {
 +              res = wpa_parse_wpa_ie_wpa(wpa_ie, wpa_ie_len, &data);
 +
 +              selector = WPA_AUTH_KEY_MGMT_UNSPEC_802_1X;
 +              if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X)
 +                      selector = WPA_AUTH_KEY_MGMT_UNSPEC_802_1X;
 +              else if (data.key_mgmt & WPA_KEY_MGMT_PSK)
 +                      selector = WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X;
 +              wpa_auth->dot11RSNAAuthenticationSuiteSelected = selector;
 +
 +              selector = wpa_cipher_to_suite(WPA_PROTO_WPA,
 +                                             data.pairwise_cipher);
 +              if (!selector)
 +                      selector = RSN_CIPHER_SUITE_TKIP;
 +              wpa_auth->dot11RSNAPairwiseCipherSelected = selector;
 +
 +              selector = wpa_cipher_to_suite(WPA_PROTO_WPA,
 +                                             data.group_cipher);
 +              if (!selector)
 +                      selector = WPA_CIPHER_SUITE_TKIP;
 +              wpa_auth->dot11RSNAGroupCipherSelected = selector;
 +      }
 +      if (res) {
 +              wpa_printf(MSG_DEBUG, "Failed to parse WPA/RSN IE from "
 +                         MACSTR " (res=%d)", MAC2STR(sm->addr), res);
 +              wpa_hexdump(MSG_DEBUG, "WPA/RSN IE", wpa_ie, wpa_ie_len);
 +              return WPA_INVALID_IE;
 +      }
 +
 +      if (data.group_cipher != wpa_auth->conf.wpa_group) {
 +              wpa_printf(MSG_DEBUG, "Invalid WPA group cipher (0x%x) from "
 +                         MACSTR, data.group_cipher, MAC2STR(sm->addr));
 +              return WPA_INVALID_GROUP;
 +      }
 +
 +      key_mgmt = data.key_mgmt & wpa_auth->conf.wpa_key_mgmt;
 +      if (!key_mgmt) {
 +              wpa_printf(MSG_DEBUG, "Invalid WPA key mgmt (0x%x) from "
 +                         MACSTR, data.key_mgmt, MAC2STR(sm->addr));
 +              return WPA_INVALID_AKMP;
 +      }
 +      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;
 +      else if (key_mgmt & WPA_KEY_MGMT_FT_PSK)
 +              sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_PSK;
 +#endif /* CONFIG_IEEE80211R */
 +#ifdef CONFIG_IEEE80211W
 +      else if (key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256)
 +              sm->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X_SHA256;
 +      else if (key_mgmt & WPA_KEY_MGMT_PSK_SHA256)
 +              sm->wpa_key_mgmt = WPA_KEY_MGMT_PSK_SHA256;
 +#endif /* CONFIG_IEEE80211W */
 +#ifdef CONFIG_SAE
 +      else if (key_mgmt & WPA_KEY_MGMT_SAE)
 +              sm->wpa_key_mgmt = WPA_KEY_MGMT_SAE;
 +      else if (key_mgmt & WPA_KEY_MGMT_FT_SAE)
 +              sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_SAE;
 +#endif /* CONFIG_SAE */
 +      else if (key_mgmt & WPA_KEY_MGMT_IEEE8021X)
 +              sm->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X;
 +      else
 +              sm->wpa_key_mgmt = WPA_KEY_MGMT_PSK;
 +
 +      if (version == WPA_PROTO_RSN)
 +              ciphers = data.pairwise_cipher & wpa_auth->conf.rsn_pairwise;
 +      else
 +              ciphers = data.pairwise_cipher & wpa_auth->conf.wpa_pairwise;
 +      if (!ciphers) {
 +              wpa_printf(MSG_DEBUG, "Invalid %s pairwise cipher (0x%x) "
 +                         "from " MACSTR,
 +                         version == WPA_PROTO_RSN ? "RSN" : "WPA",
 +                         data.pairwise_cipher, MAC2STR(sm->addr));
 +              return WPA_INVALID_PAIRWISE;
 +      }
 +
 +#ifdef CONFIG_IEEE80211W
 +      if (wpa_auth->conf.ieee80211w == MGMT_FRAME_PROTECTION_REQUIRED) {
 +              if (!(data.capabilities & WPA_CAPABILITY_MFPC)) {
 +                      wpa_printf(MSG_DEBUG, "Management frame protection "
 +                                 "required, but client did not enable it");
 +                      return WPA_MGMT_FRAME_PROTECTION_VIOLATION;
 +              }
 +
 +              if (ciphers & WPA_CIPHER_TKIP) {
 +                      wpa_printf(MSG_DEBUG, "Management frame protection "
 +                                 "cannot use TKIP");
 +                      return WPA_MGMT_FRAME_PROTECTION_VIOLATION;
 +              }
 +
 +              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;
 +              }
 +      }
 +
 +      if (wpa_auth->conf.ieee80211w == NO_MGMT_FRAME_PROTECTION ||
 +          !(data.capabilities & WPA_CAPABILITY_MFPC))
 +              sm->mgmt_frame_prot = 0;
 +      else
 +              sm->mgmt_frame_prot = 1;
 +#endif /* CONFIG_IEEE80211W */
 +
 +#ifdef CONFIG_IEEE80211R
 +      if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
 +              if (mdie == NULL || mdie_len < MOBILITY_DOMAIN_ID_LEN + 1) {
 +                      wpa_printf(MSG_DEBUG, "RSN: Trying to use FT, but "
 +                                 "MDIE not included");
 +                      return WPA_INVALID_MDIE;
 +              }
 +              if (os_memcmp(mdie, wpa_auth->conf.mobility_domain,
 +                            MOBILITY_DOMAIN_ID_LEN) != 0) {
 +                      wpa_hexdump(MSG_DEBUG, "RSN: Attempted to use unknown "
 +                                  "MDIE", mdie, MOBILITY_DOMAIN_ID_LEN);
 +                      return WPA_INVALID_MDIE;
 +              }
 +      }
 +#endif /* CONFIG_IEEE80211R */
 +
 +      sm->pairwise = wpa_pick_pairwise_cipher(ciphers, 0);
 +      if (sm->pairwise < 0)
 +              return WPA_INVALID_PAIRWISE;
 +
 +      /* TODO: clear WPA/WPA2 state if STA changes from one to another */
 +      if (wpa_ie[0] == WLAN_EID_RSN)
 +              sm->wpa = WPA_VERSION_WPA2;
 +      else
 +              sm->wpa = WPA_VERSION_WPA;
 +
 +      sm->pmksa = NULL;
 +      for (i = 0; i < data.num_pmkid; i++) {
 +              wpa_hexdump(MSG_DEBUG, "RSN IE: STA PMKID",
 +                          &data.pmkid[i * PMKID_LEN], PMKID_LEN);
 +              sm->pmksa = pmksa_cache_auth_get(wpa_auth->pmksa, sm->addr,
 +                                               &data.pmkid[i * PMKID_LEN]);
 +              if (sm->pmksa) {
 +                      pmkid = sm->pmksa->pmkid;
 +                      break;
 +              }
 +      }
 +      for (i = 0; sm->pmksa == NULL && wpa_auth->conf.okc &&
 +                   i < data.num_pmkid; i++) {
 +              struct wpa_auth_okc_iter_data idata;
 +              idata.pmksa = NULL;
 +              idata.aa = wpa_auth->addr;
 +              idata.spa = sm->addr;
 +              idata.pmkid = &data.pmkid[i * PMKID_LEN];
 +              wpa_auth_for_each_auth(wpa_auth, wpa_auth_okc_iter, &idata);
 +              if (idata.pmksa) {
 +                      wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG,
 +                                       "OKC match for PMKID");
 +                      sm->pmksa = pmksa_cache_add_okc(wpa_auth->pmksa,
 +                                                      idata.pmksa,
 +                                                      wpa_auth->addr,
 +                                                      idata.pmkid);
 +                      pmkid = idata.pmkid;
 +                      break;
 +              }
 +      }
 +      if (sm->pmksa && pmkid) {
 +              wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG,
 +                               "PMKID found from PMKSA cache "
 +                               "eap_type=%d vlan_id=%d",
 +                               sm->pmksa->eap_type_authsrv,
 +                               sm->pmksa->vlan_id);
 +              os_memcpy(wpa_auth->dot11RSNAPMKIDUsed, pmkid, PMKID_LEN);
 +      }
 +
 +      if (sm->wpa_ie == NULL || sm->wpa_ie_len < wpa_ie_len) {
 +              os_free(sm->wpa_ie);
 +              sm->wpa_ie = os_malloc(wpa_ie_len);
 +              if (sm->wpa_ie == NULL)
 +                      return WPA_ALLOC_FAIL;
 +      }
 +      os_memcpy(sm->wpa_ie, wpa_ie, wpa_ie_len);
 +      sm->wpa_ie_len = wpa_ie_len;
 +
 +      return WPA_IE_OK;
 +}
 +
 +
 +#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
 + * @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_generic(const u8 *pos, const u8 *end,
 +                           struct wpa_eapol_ie_parse *ie)
 +{
 +      if (pos[1] == 0)
 +              return 1;
 +
 +      if (pos[1] >= 6 &&
 +          RSN_SELECTOR_GET(pos + 2) == WPA_OUI_TYPE &&
 +          pos[2 + WPA_SELECTOR_LEN] == 1 &&
 +          pos[2 + WPA_SELECTOR_LEN + 1] == 0) {
 +              ie->wpa_ie = pos;
 +              ie->wpa_ie_len = pos[1] + 2;
 +              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) {
 +              ie->pmkid = pos + 2 + RSN_SELECTOR_LEN;
 +              return 0;
 +      }
 +
 +      if (pos[1] > RSN_SELECTOR_LEN + 2 &&
 +          RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_GROUPKEY) {
 +              ie->gtk = pos + 2 + RSN_SELECTOR_LEN;
 +              ie->gtk_len = pos[1] - RSN_SELECTOR_LEN;
 +              return 0;
 +      }
 +
 +      if (pos[1] > RSN_SELECTOR_LEN + 2 &&
 +          RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_MAC_ADDR) {
 +              ie->mac_addr = pos + 2 + RSN_SELECTOR_LEN;
 +              ie->mac_addr_len = pos[1] - RSN_SELECTOR_LEN;
 +              return 0;
 +      }
 +
 +#ifdef CONFIG_PEERKEY
 +      if (pos[1] > RSN_SELECTOR_LEN + 2 &&
 +          RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_SMK) {
 +              ie->smk = pos + 2 + RSN_SELECTOR_LEN;
 +              ie->smk_len = pos[1] - RSN_SELECTOR_LEN;
 +              return 0;
 +      }
 +
 +      if (pos[1] > RSN_SELECTOR_LEN + 2 &&
 +          RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_NONCE) {
 +              ie->nonce = pos + 2 + RSN_SELECTOR_LEN;
 +              ie->nonce_len = pos[1] - RSN_SELECTOR_LEN;
 +              return 0;
 +      }
 +
 +      if (pos[1] > RSN_SELECTOR_LEN + 2 &&
 +          RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_LIFETIME) {
 +              ie->lifetime = pos + 2 + RSN_SELECTOR_LEN;
 +              ie->lifetime_len = pos[1] - RSN_SELECTOR_LEN;
 +              return 0;
 +      }
 +
 +      if (pos[1] > RSN_SELECTOR_LEN + 2 &&
 +          RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_ERROR) {
 +              ie->error = pos + 2 + RSN_SELECTOR_LEN;
 +              ie->error_len = pos[1] - RSN_SELECTOR_LEN;
 +              return 0;
 +      }
 +#endif /* CONFIG_PEERKEY */
 +
 +#ifdef CONFIG_IEEE80211W
 +      if (pos[1] > RSN_SELECTOR_LEN + 2 &&
 +          RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_IGTK) {
 +              ie->igtk = pos + 2 + RSN_SELECTOR_LEN;
 +              ie->igtk_len = pos[1] - RSN_SELECTOR_LEN;
 +              return 0;
 +      }
 +#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;
 +}
 +
 +
 +/**
 + * wpa_parse_kde_ies - Parse EAPOL-Key Key Data IEs
 + * @buf: Pointer to the Key Data buffer
 + * @len: Key Data Length
 + * @ie: Pointer to parsed IE data
 + * Returns: 0 on success, -1 on failure
 + */
 +int wpa_parse_kde_ies(const u8 *buf, size_t len, struct wpa_eapol_ie_parse *ie)
 +{
 +      const u8 *pos, *end;
 +      int ret = 0;
 +
 +      os_memset(ie, 0, sizeof(*ie));
 +      for (pos = buf, end = pos + len; pos + 1 < end; pos += 2 + pos[1]) {
 +              if (pos[0] == 0xdd &&
 +                  ((pos == buf + len - 1) || pos[1] == 0)) {
 +                      /* Ignore padding */
 +                      break;
 +              }
 +              if (pos + 2 + pos[1] > end) {
 +                      wpa_printf(MSG_DEBUG, "WPA: EAPOL-Key Key Data "
 +                                 "underflow (ie=%d len=%d pos=%d)",
 +                                 pos[0], pos[1], (int) (pos - buf));
 +                      wpa_hexdump_key(MSG_DEBUG, "WPA: Key Data",
 +                                      buf, len);
 +                      ret = -1;
 +                      break;
 +              }
 +              if (*pos == WLAN_EID_RSN) {
 +                      ie->rsn_ie = pos;
 +                      ie->rsn_ie_len = pos[1] + 2;
 +#ifdef CONFIG_IEEE80211R
 +              } else if (*pos == WLAN_EID_MOBILITY_DOMAIN) {
 +                      ie->mdie = pos;
 +                      ie->mdie_len = pos[1] + 2;
 +              } else if (*pos == WLAN_EID_FAST_BSS_TRANSITION) {
 +                      ie->ftie = pos;
 +                      ie->ftie_len = pos[1] + 2;
 +#endif /* CONFIG_IEEE80211R */
 +              } else if (*pos == WLAN_EID_VENDOR_SPECIFIC) {
 +                      ret = wpa_parse_generic(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]);
 +              }
 +      }
 +
 +      return ret;
 +}
 +
 +
 +int wpa_auth_uses_mfp(struct wpa_state_machine *sm)
 +{
 +      return sm ? sm->mgmt_frame_prot : 0;
 +}
index b0e8b0bfcac34f77ab89148342b16d3acfe7bdfe,0000000000000000000000000000000000000000..cde31e60e03b70d574b7b989d8b8b1eacf1f1be1
mode 100644,000000..100644
--- /dev/null
@@@ -1,1961 -1,0 +1,2014 @@@
-       if (cred->ssid_len <= HOSTAPD_MAX_SSID_LEN) {
 +/*
 + * hostapd / WPS integration
 + * Copyright (c) 2008-2012, Jouni Malinen <j@w1.fi>
 + *
 + * This software may be distributed under the terms of the BSD license.
 + * See README for more details.
 + */
 +
 +#include "utils/includes.h"
 +
 +#include "utils/common.h"
 +#include "utils/eloop.h"
 +#include "utils/uuid.h"
 +#include "common/wpa_ctrl.h"
 +#include "common/ieee802_11_defs.h"
 +#include "common/ieee802_11_common.h"
 +#include "eapol_auth/eapol_auth_sm.h"
 +#include "eapol_auth/eapol_auth_sm_i.h"
 +#include "wps/wps.h"
 +#include "wps/wps_defs.h"
 +#include "wps/wps_dev_attr.h"
 +#include "wps/wps_attr_parse.h"
 +#include "hostapd.h"
 +#include "ap_config.h"
 +#include "ap_drv_ops.h"
 +#include "beacon.h"
 +#include "sta_info.h"
 +#include "wps_hostapd.h"
 +
 +
 +#ifdef CONFIG_WPS_UPNP
 +#include "wps/wps_upnp.h"
 +static int hostapd_wps_upnp_init(struct hostapd_data *hapd,
 +                               struct wps_context *wps);
 +static void hostapd_wps_upnp_deinit(struct hostapd_data *hapd);
 +#endif /* CONFIG_WPS_UPNP */
 +
 +static int hostapd_wps_probe_req_rx(void *ctx, const u8 *addr, const u8 *da,
 +                                  const u8 *bssid,
 +                                  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 {
 +      int (*func)(struct hostapd_data *h, void *ctx);
 +      void *ctx;
 +      struct hostapd_data *calling_hapd;
 +};
 +
 +
 +static int wps_for_each(struct hostapd_iface *iface, void *ctx)
 +{
 +      struct wps_for_each_data *data = ctx;
 +      size_t j;
 +
 +      if (iface == NULL)
 +              return 0;
 +      for (j = 0; j < iface->num_bss; j++) {
 +              struct hostapd_data *hapd = iface->bss[j];
 +              int ret;
 +
 +              if (hapd != data->calling_hapd &&
 +                  (hapd->conf->wps_independent ||
 +                   data->calling_hapd->conf->wps_independent))
 +                      continue;
 +
 +              ret = data->func(hapd, data->ctx);
 +              if (ret)
 +                      return ret;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +static int hostapd_wps_for_each(struct hostapd_data *hapd,
 +                              int (*func)(struct hostapd_data *h, void *ctx),
 +                              void *ctx)
 +{
 +      struct hostapd_iface *iface = hapd->iface;
 +      struct wps_for_each_data data;
 +      data.func = func;
 +      data.ctx = ctx;
 +      data.calling_hapd = hapd;
 +      if (iface->interfaces == NULL ||
 +          iface->interfaces->for_each_interface == NULL)
 +              return wps_for_each(iface, &data);
 +      return iface->interfaces->for_each_interface(iface->interfaces,
 +                                                   wps_for_each, &data);
 +}
 +
 +
 +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;
 +
 +      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) {
 +              wpa_printf(MSG_DEBUG, "Unexpected PSK length %lu",
 +                         (unsigned long) psk_len);
 +              return -1;
 +      }
 +
 +      /* Add the new PSK to runtime PSK list */
 +      p = os_zalloc(sizeof(*p));
 +      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;
 +
 +      if (ssid->wpa_psk_file) {
 +              FILE *f;
 +              char hex[PMK_LEN * 2 + 1];
 +              /* Add the new PSK to PSK list file */
 +              f = fopen(ssid->wpa_psk_file, "a");
 +              if (f == NULL) {
 +                      wpa_printf(MSG_DEBUG, "Failed to add the PSK to "
 +                                 "'%s'", ssid->wpa_psk_file);
 +                      return -1;
 +              }
 +
 +              wpa_snprintf_hex(hex, sizeof(hex), psk, psk_len);
 +              fprintf(f, MACSTR " %s\n", MAC2STR(mac_addr), hex);
 +              fclose(f);
 +      }
 +
 +      return 0;
 +}
 +
 +
 +static int hostapd_wps_set_ie_cb(void *ctx, struct wpabuf *beacon_ie,
 +                               struct wpabuf *probe_resp_ie)
 +{
 +      struct hostapd_data *hapd = ctx;
 +      wpabuf_free(hapd->wps_beacon_ie);
 +      hapd->wps_beacon_ie = beacon_ie;
 +      wpabuf_free(hapd->wps_probe_resp_ie);
 +      hapd->wps_probe_resp_ie = probe_resp_ie;
 +      if (hapd->beacon_set_done)
 +              ieee802_11_set_beacon(hapd);
 +      return hostapd_set_ap_wps_ie(hapd);
 +}
 +
 +
 +static void hostapd_wps_pin_needed_cb(void *ctx, const u8 *uuid_e,
 +                                    const struct wps_device_data *dev)
 +{
 +      struct hostapd_data *hapd = ctx;
 +      char uuid[40], txt[400];
 +      int len;
 +      char devtype[WPS_DEV_TYPE_BUFSIZE];
 +      if (uuid_bin2str(uuid_e, uuid, sizeof(uuid)))
 +              return;
 +      wpa_printf(MSG_DEBUG, "WPS: PIN needed for E-UUID %s", uuid);
 +      len = os_snprintf(txt, sizeof(txt), WPS_EVENT_PIN_NEEDED
 +                        "%s " MACSTR " [%s|%s|%s|%s|%s|%s]",
 +                        uuid, MAC2STR(dev->mac_addr), dev->device_name,
 +                        dev->manufacturer, dev->model_name,
 +                        dev->model_number, dev->serial_number,
 +                        wps_dev_type_bin2str(dev->pri_dev_type, devtype,
 +                                             sizeof(devtype)));
 +      if (!os_snprintf_error(sizeof(txt), len))
 +              wpa_msg(hapd->msg_ctx, MSG_INFO, "%s", txt);
 +
 +      if (hapd->conf->wps_pin_requests) {
 +              FILE *f;
 +              struct os_time t;
 +              f = fopen(hapd->conf->wps_pin_requests, "a");
 +              if (f == NULL)
 +                      return;
 +              os_get_time(&t);
 +              fprintf(f, "%ld\t%s\t" MACSTR "\t%s\t%s\t%s\t%s\t%s"
 +                      "\t%s\n",
 +                      t.sec, uuid, MAC2STR(dev->mac_addr), dev->device_name,
 +                      dev->manufacturer, dev->model_name, dev->model_number,
 +                      dev->serial_number,
 +                      wps_dev_type_bin2str(dev->pri_dev_type, devtype,
 +                                           sizeof(devtype)));
 +              fclose(f);
 +      }
 +}
 +
 +
 +struct wps_stop_reg_data {
 +      struct hostapd_data *current_hapd;
 +      const u8 *uuid_e;
 +      const u8 *dev_pw;
 +      size_t dev_pw_len;
 +};
 +
 +static int wps_stop_registrar(struct hostapd_data *hapd, void *ctx)
 +{
 +      struct wps_stop_reg_data *data = ctx;
 +      if (hapd != data->current_hapd && hapd->wps != NULL)
 +              wps_registrar_complete(hapd->wps->registrar, data->uuid_e,
 +                                     data->dev_pw, data->dev_pw_len);
 +      return 0;
 +}
 +
 +
 +static void hostapd_wps_reg_success_cb(void *ctx, const u8 *mac_addr,
 +                                     const u8 *uuid_e, const u8 *dev_pw,
 +                                     size_t dev_pw_len)
 +{
 +      struct hostapd_data *hapd = ctx;
 +      char uuid[40];
 +      struct wps_stop_reg_data data;
 +      if (uuid_bin2str(uuid_e, uuid, sizeof(uuid)))
 +              return;
 +      wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_REG_SUCCESS MACSTR " %s",
 +              MAC2STR(mac_addr), uuid);
 +      if (hapd->wps_reg_success_cb)
 +              hapd->wps_reg_success_cb(hapd->wps_reg_success_cb_ctx,
 +                                       mac_addr, uuid_e);
 +      data.current_hapd = hapd;
 +      data.uuid_e = uuid_e;
 +      data.dev_pw = dev_pw;
 +      data.dev_pw_len = dev_pw_len;
 +      hostapd_wps_for_each(hapd, wps_stop_registrar, &data);
 +}
 +
 +
 +static void hostapd_wps_enrollee_seen_cb(void *ctx, const u8 *addr,
 +                                       const u8 *uuid_e,
 +                                       const u8 *pri_dev_type,
 +                                       u16 config_methods,
 +                                       u16 dev_password_id, u8 request_type,
 +                                       const char *dev_name)
 +{
 +      struct hostapd_data *hapd = ctx;
 +      char uuid[40];
 +      char devtype[WPS_DEV_TYPE_BUFSIZE];
 +      if (uuid_bin2str(uuid_e, uuid, sizeof(uuid)))
 +              return;
 +      if (dev_name == NULL)
 +              dev_name = "";
 +      wpa_msg_ctrl(hapd->msg_ctx, MSG_INFO, WPS_EVENT_ENROLLEE_SEEN MACSTR
 +                   " %s %s 0x%x %u %u [%s]",
 +                   MAC2STR(addr), uuid,
 +                   wps_dev_type_bin2str(pri_dev_type, devtype,
 +                                        sizeof(devtype)),
 +                   config_methods, dev_password_id, request_type, dev_name);
 +}
 +
 +
 +static int str_starts(const char *str, const char *start)
 +{
 +      return os_strncmp(str, start, os_strlen(start)) == 0;
 +}
 +
 +
 +static void wps_reload_config(void *eloop_data, void *user_ctx)
 +{
 +      struct hostapd_iface *iface = eloop_data;
 +
 +      wpa_printf(MSG_DEBUG, "WPS: Reload configuration data");
 +      if (iface->interfaces == NULL ||
 +          iface->interfaces->reload_config(iface) < 0) {
 +              wpa_printf(MSG_WARNING, "WPS: Failed to reload the updated "
 +                         "configuration");
 +      }
 +}
 +
 +
 +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)
 +{
 +      size_t blen = attr_len * 2 + 1;
 +      char *buf = os_malloc(blen);
 +      if (buf) {
 +              wpa_snprintf_hex(buf, blen, attr, attr_len);
 +              wpa_msg(hapd->msg_ctx, MSG_INFO,
 +                      WPS_EVENT_NEW_AP_SETTINGS "%s", buf);
 +              os_free(buf);
 +      }
 +}
 +
 +
 +static int hapd_wps_reconfig_in_memory(struct hostapd_data *hapd,
 +                                     const struct wps_credential *cred)
 +{
 +      struct hostapd_bss_config *bss = hapd->conf;
 +
 +      wpa_printf(MSG_DEBUG, "WPS: Updating in-memory configuration");
 +
 +      bss->wps_state = 2;
-               if (cred->encr_type & WPS_ENCR_AES)
-                       bss->wpa_pairwise |= WPA_CIPHER_CCMP;
++      if (cred->ssid_len <= SSID_MAX_LEN) {
 +              os_memcpy(bss->ssid.ssid, cred->ssid, cred->ssid_len);
 +              bss->ssid.ssid_len = cred->ssid_len;
 +              bss->ssid.ssid_set = 1;
 +      }
 +
 +      if ((cred->auth_type & (WPS_AUTH_WPA2 | WPS_AUTH_WPA2PSK)) &&
 +          (cred->auth_type & (WPS_AUTH_WPA | WPS_AUTH_WPAPSK)))
 +              bss->wpa = 3;
 +      else if (cred->auth_type & (WPS_AUTH_WPA2 | WPS_AUTH_WPA2PSK))
 +              bss->wpa = 2;
 +      else if (cred->auth_type & (WPS_AUTH_WPA | WPS_AUTH_WPAPSK))
 +              bss->wpa = 1;
 +      else
 +              bss->wpa = 0;
 +
 +      if (bss->wpa) {
 +              if (cred->auth_type & (WPS_AUTH_WPA2 | WPS_AUTH_WPA))
 +                      bss->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X;
 +              if (cred->auth_type & (WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK))
 +                      bss->wpa_key_mgmt = WPA_KEY_MGMT_PSK;
 +
 +              bss->wpa_pairwise = 0;
-                       fprintf(nconf, "CCMP");
++              if (cred->encr_type & WPS_ENCR_AES) {
++                      if (hapd->iconf->hw_mode == HOSTAPD_MODE_IEEE80211AD)
++                              bss->wpa_pairwise |= WPA_CIPHER_GCMP;
++                      else
++                              bss->wpa_pairwise |= WPA_CIPHER_CCMP;
++              }
 +              if (cred->encr_type & WPS_ENCR_TKIP)
 +                      bss->wpa_pairwise |= WPA_CIPHER_TKIP;
 +              bss->rsn_pairwise = bss->wpa_pairwise;
 +              bss->wpa_group = wpa_select_ap_group_cipher(bss->wpa,
 +                                                          bss->wpa_pairwise,
 +                                                          bss->rsn_pairwise);
 +
 +              if (cred->key_len >= 8 && cred->key_len < 64) {
 +                      os_free(bss->ssid.wpa_passphrase);
 +                      bss->ssid.wpa_passphrase = os_zalloc(cred->key_len + 1);
 +                      if (bss->ssid.wpa_passphrase)
 +                              os_memcpy(bss->ssid.wpa_passphrase, cred->key,
 +                                        cred->key_len);
 +                      hostapd_config_clear_wpa_psk(&bss->ssid.wpa_psk);
 +              } else if (cred->key_len == 64) {
 +                      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 &&
 +                          hexstr2bin((const char *) cred->key,
 +                                     bss->ssid.wpa_psk->psk, PMK_LEN) == 0) {
 +                              bss->ssid.wpa_psk->group = 1;
 +                              os_free(bss->ssid.wpa_passphrase);
 +                              bss->ssid.wpa_passphrase = NULL;
 +                      }
 +              }
 +              bss->auth_algs = 1;
 +      } else {
 +              /*
 +               * 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
 +       * EAP-WSC to be finished.
 +       */
 +      eloop_register_timeout(0, 100000, wps_reload_config, hapd->iface,
 +                             NULL);
 +
 +      return 0;
 +}
 +
 +
 +static int hapd_wps_cred_cb(struct hostapd_data *hapd, void *ctx)
 +{
 +      const struct wps_credential *cred = ctx;
 +      FILE *oconf, *nconf;
 +      size_t len, i;
 +      char *tmp_fname;
 +      char buf[1024];
 +      int multi_bss;
 +      int wpa;
 +
 +      if (hapd->wps == NULL)
 +              return 0;
 +
 +      wpa_hexdump_key(MSG_DEBUG, "WPS: Received Credential attribute",
 +                      cred->cred_attr, cred->cred_attr_len);
 +
 +      wpa_printf(MSG_DEBUG, "WPS: Received new AP Settings");
 +      wpa_hexdump_ascii(MSG_DEBUG, "WPS: SSID", cred->ssid, cred->ssid_len);
 +      wpa_printf(MSG_DEBUG, "WPS: Authentication Type 0x%x",
 +                 cred->auth_type);
 +      wpa_printf(MSG_DEBUG, "WPS: Encryption Type 0x%x", cred->encr_type);
 +      wpa_printf(MSG_DEBUG, "WPS: Network Key Index %d", cred->key_idx);
 +      wpa_hexdump_key(MSG_DEBUG, "WPS: Network Key",
 +                      cred->key, cred->key_len);
 +      wpa_printf(MSG_DEBUG, "WPS: MAC Address " MACSTR,
 +                 MAC2STR(cred->mac_addr));
 +
 +      if ((hapd->conf->wps_cred_processing == 1 ||
 +           hapd->conf->wps_cred_processing == 2) && cred->cred_attr) {
 +              hapd_new_ap_event(hapd, cred->cred_attr, cred->cred_attr_len);
 +      } else if (hapd->conf->wps_cred_processing == 1 ||
 +                 hapd->conf->wps_cred_processing == 2) {
 +              struct wpabuf *attr;
 +              attr = wpabuf_alloc(200);
 +              if (attr && wps_build_credential_wrap(attr, cred) == 0)
 +                      hapd_new_ap_event(hapd, wpabuf_head_u8(attr),
 +                                        wpabuf_len(attr));
 +              wpabuf_free(attr);
 +      } else
 +              wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_NEW_AP_SETTINGS);
 +
 +      if (hapd->conf->wps_cred_processing == 1)
 +              return 0;
 +
 +      os_memcpy(hapd->wps->ssid, cred->ssid, cred->ssid_len);
 +      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;
 +              hapd->wps->network_key_len = 0;
++      } else if ((cred->auth_type & (WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK)) &&
++                 (cred->key_len < 8 || cred->key_len > 2 * PMK_LEN)) {
++              wpa_printf(MSG_INFO, "WPS: Invalid key length %lu for WPA/WPA2",
++                         (unsigned long) cred->key_len);
++              return -1;
 +      } else {
 +              if (hapd->wps->network_key == NULL ||
 +                  hapd->wps->network_key_len < cred->key_len) {
 +                      hapd->wps->network_key_len = 0;
 +                      os_free(hapd->wps->network_key);
 +                      hapd->wps->network_key = os_malloc(cred->key_len);
 +                      if (hapd->wps->network_key == NULL)
 +                              return -1;
 +              }
 +              hapd->wps->network_key_len = cred->key_len;
 +              os_memcpy(hapd->wps->network_key, cred->key, cred->key_len);
 +      }
 +      hapd->wps->wps_state = WPS_STATE_CONFIGURED;
 +
 +      if (hapd->iface->config_fname == NULL)
 +              return hapd_wps_reconfig_in_memory(hapd, cred);
 +      len = os_strlen(hapd->iface->config_fname) + 5;
 +      tmp_fname = os_malloc(len);
 +      if (tmp_fname == NULL)
 +              return -1;
 +      os_snprintf(tmp_fname, len, "%s-new", hapd->iface->config_fname);
 +
 +      oconf = fopen(hapd->iface->config_fname, "r");
 +      if (oconf == NULL) {
 +              wpa_printf(MSG_WARNING, "WPS: Could not open current "
 +                         "configuration file");
 +              os_free(tmp_fname);
 +              return -1;
 +      }
 +
 +      nconf = fopen(tmp_fname, "w");
 +      if (nconf == NULL) {
 +              wpa_printf(MSG_WARNING, "WPS: Could not write updated "
 +                         "configuration file");
 +              os_free(tmp_fname);
 +              fclose(oconf);
 +              return -1;
 +      }
 +
 +      fprintf(nconf, "# WPS configuration - START\n");
 +
 +      fprintf(nconf, "wps_state=2\n");
 +
 +      if (is_hex(cred->ssid, cred->ssid_len)) {
 +              fprintf(nconf, "ssid2=");
 +              for (i = 0; i < cred->ssid_len; i++)
 +                      fprintf(nconf, "%02x", cred->ssid[i]);
 +              fprintf(nconf, "\n");
 +      } else {
 +              fprintf(nconf, "ssid=");
 +              for (i = 0; i < cred->ssid_len; i++)
 +                      fputc(cred->ssid[i], nconf);
 +              fprintf(nconf, "\n");
 +      }
 +
 +      if ((cred->auth_type & (WPS_AUTH_WPA2 | WPS_AUTH_WPA2PSK)) &&
 +          (cred->auth_type & (WPS_AUTH_WPA | WPS_AUTH_WPAPSK)))
 +              wpa = 3;
 +      else if (cred->auth_type & (WPS_AUTH_WPA2 | WPS_AUTH_WPA2PSK))
 +              wpa = 2;
 +      else if (cred->auth_type & (WPS_AUTH_WPA | WPS_AUTH_WPAPSK))
 +              wpa = 1;
 +      else
 +              wpa = 0;
 +
 +      if (wpa) {
 +              char *prefix;
 +              fprintf(nconf, "wpa=%d\n", wpa);
 +
 +              fprintf(nconf, "wpa_key_mgmt=");
 +              prefix = "";
 +              if (cred->auth_type & (WPS_AUTH_WPA2 | WPS_AUTH_WPA)) {
 +                      fprintf(nconf, "WPA-EAP");
 +                      prefix = " ";
 +              }
 +              if (cred->auth_type & (WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK))
 +                      fprintf(nconf, "%sWPA-PSK", prefix);
 +              fprintf(nconf, "\n");
 +
 +              fprintf(nconf, "wpa_pairwise=");
 +              prefix = "";
 +              if (cred->encr_type & WPS_ENCR_AES) {
-               WPS_RF_50GHZ : WPS_RF_24GHZ; /* FIX: dualband AP */
++                      if (hapd->iconf->hw_mode == HOSTAPD_MODE_IEEE80211AD)
++                              fprintf(nconf, "GCMP");
++                      else
++                              fprintf(nconf, "CCMP");
++
 +                      prefix = " ";
 +              }
 +              if (cred->encr_type & WPS_ENCR_TKIP) {
 +                      fprintf(nconf, "%sTKIP", prefix);
 +              }
 +              fprintf(nconf, "\n");
 +
 +              if (cred->key_len >= 8 && cred->key_len < 64) {
 +                      fprintf(nconf, "wpa_passphrase=");
 +                      for (i = 0; i < cred->key_len; i++)
 +                              fputc(cred->key[i], nconf);
 +                      fprintf(nconf, "\n");
 +              } else if (cred->key_len == 64) {
 +                      fprintf(nconf, "wpa_psk=");
 +                      for (i = 0; i < cred->key_len; i++)
 +                              fputc(cred->key[i], nconf);
 +                      fprintf(nconf, "\n");
 +              } else {
 +                      wpa_printf(MSG_WARNING, "WPS: Invalid key length %lu "
 +                                 "for WPA/WPA2",
 +                                 (unsigned long) cred->key_len);
 +              }
 +
 +              fprintf(nconf, "auth_algs=1\n");
 +      } else {
 +              /*
 +               * 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");
 +
 +      multi_bss = 0;
 +      while (fgets(buf, sizeof(buf), oconf)) {
 +              if (os_strncmp(buf, "bss=", 4) == 0)
 +                      multi_bss = 1;
 +              if (!multi_bss &&
 +                  (str_starts(buf, "ssid=") ||
 +                   str_starts(buf, "ssid2=") ||
 +                   str_starts(buf, "auth_algs=") ||
 +                   str_starts(buf, "wep_default_key=") ||
 +                   str_starts(buf, "wep_key") ||
 +                   str_starts(buf, "wps_state=") ||
 +                   str_starts(buf, "wpa=") ||
 +                   str_starts(buf, "wpa_psk=") ||
 +                   str_starts(buf, "wpa_pairwise=") ||
 +                   str_starts(buf, "rsn_pairwise=") ||
 +                   str_starts(buf, "wpa_key_mgmt=") ||
 +                   str_starts(buf, "wpa_passphrase="))) {
 +                      fprintf(nconf, "#WPS# %s", buf);
 +              } else
 +                      fprintf(nconf, "%s", buf);
 +      }
 +
 +      fclose(nconf);
 +      fclose(oconf);
 +
 +      if (rename(tmp_fname, hapd->iface->config_fname) < 0) {
 +              wpa_printf(MSG_WARNING, "WPS: Failed to rename the updated "
 +                         "configuration file: %s", strerror(errno));
 +              os_free(tmp_fname);
 +              return -1;
 +      }
 +
 +      os_free(tmp_fname);
 +
 +      /* Schedule configuration reload after short period of time to allow
 +       * EAP-WSC to be finished.
 +       */
 +      eloop_register_timeout(0, 100000, wps_reload_config, hapd->iface,
 +                             NULL);
 +
 +      wpa_printf(MSG_DEBUG, "WPS: AP configuration updated");
 +
 +      return 0;
 +}
 +
 +
 +static int hostapd_wps_cred_cb(void *ctx, const struct wps_credential *cred)
 +{
 +      struct hostapd_data *hapd = ctx;
 +      return hostapd_wps_for_each(hapd, hapd_wps_cred_cb, (void *) cred);
 +}
 +
 +
 +static void hostapd_wps_reenable_ap_pin(void *eloop_data, void *user_ctx)
 +{
 +      struct hostapd_data *hapd = eloop_data;
 +
 +      if (hapd->conf->ap_setup_locked)
 +              return;
 +      if (hapd->ap_pin_failures_consecutive >= 10)
 +              return;
 +
 +      wpa_printf(MSG_DEBUG, "WPS: Re-enable AP PIN");
 +      wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_AP_SETUP_UNLOCKED);
 +      hapd->wps->ap_setup_locked = 0;
 +      wps_registrar_update_ie(hapd->wps->registrar);
 +}
 +
 +
 +static int wps_pwd_auth_fail(struct hostapd_data *hapd, void *ctx)
 +{
 +      struct wps_event_pwd_auth_fail *data = ctx;
 +
 +      if (!data->enrollee || hapd->conf->ap_pin == NULL || hapd->wps == NULL)
 +              return 0;
 +
 +      /*
 +       * Registrar failed to prove its knowledge of the AP PIN. Lock AP setup
 +       * for some time if this happens multiple times to slow down brute
 +       * force attacks.
 +       */
 +      hapd->ap_pin_failures++;
 +      hapd->ap_pin_failures_consecutive++;
 +      wpa_printf(MSG_DEBUG, "WPS: AP PIN authentication failure number %u "
 +                 "(%u consecutive)",
 +                 hapd->ap_pin_failures, hapd->ap_pin_failures_consecutive);
 +      if (hapd->ap_pin_failures < 3)
 +              return 0;
 +
 +      wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_AP_SETUP_LOCKED);
 +      hapd->wps->ap_setup_locked = 1;
 +
 +      wps_registrar_update_ie(hapd->wps->registrar);
 +
 +      if (!hapd->conf->ap_setup_locked &&
 +          hapd->ap_pin_failures_consecutive >= 10) {
 +              /*
 +               * In indefinite lockdown - disable automatic AP PIN
 +               * reenablement.
 +               */
 +              eloop_cancel_timeout(hostapd_wps_reenable_ap_pin, hapd, NULL);
 +              wpa_printf(MSG_DEBUG, "WPS: AP PIN disabled indefinitely");
 +      } else if (!hapd->conf->ap_setup_locked) {
 +              if (hapd->ap_pin_lockout_time == 0)
 +                      hapd->ap_pin_lockout_time = 60;
 +              else if (hapd->ap_pin_lockout_time < 365 * 24 * 60 * 60 &&
 +                       (hapd->ap_pin_failures % 3) == 0)
 +                      hapd->ap_pin_lockout_time *= 2;
 +
 +              wpa_printf(MSG_DEBUG, "WPS: Disable AP PIN for %u seconds",
 +                         hapd->ap_pin_lockout_time);
 +              eloop_cancel_timeout(hostapd_wps_reenable_ap_pin, hapd, NULL);
 +              eloop_register_timeout(hapd->ap_pin_lockout_time, 0,
 +                                     hostapd_wps_reenable_ap_pin, hapd,
 +                                     NULL);
 +      }
 +
 +      return 0;
 +}
 +
 +
 +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);
 +}
 +
 +
 +static int wps_ap_pin_success(struct hostapd_data *hapd, void *ctx)
 +{
 +      if (hapd->conf->ap_pin == NULL || hapd->wps == NULL)
 +              return 0;
 +
 +      if (hapd->ap_pin_failures_consecutive == 0)
 +              return 0;
 +
 +      wpa_printf(MSG_DEBUG, "WPS: Clear consecutive AP PIN failure counter "
 +                 "- total validation failures %u (%u consecutive)",
 +                 hapd->ap_pin_failures, hapd->ap_pin_failures_consecutive);
 +      hapd->ap_pin_failures_consecutive = 0;
 +
 +      return 0;
 +}
 +
 +
 +static void hostapd_wps_ap_pin_success(struct hostapd_data *hapd)
 +{
 +      hostapd_wps_for_each(hapd, wps_ap_pin_success, NULL);
 +}
 +
 +
 +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_ei_str(fail->error_indication));
 +      } else {
 +              wpa_msg(hapd->msg_ctx, MSG_INFO,
 +                      WPS_EVENT_FAIL "msg=%d config_error=%d",
 +                      fail->msg, fail->config_error);
 +      }
 +}
 +
 +
 +static void hostapd_wps_event_cb(void *ctx, enum wps_event event,
 +                               union wps_event_data *data)
 +{
 +      struct hostapd_data *hapd = ctx;
 +
 +      switch (event) {
 +      case WPS_EV_M2D:
 +              wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_M2D);
 +              break;
 +      case WPS_EV_FAIL:
 +              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:
 +              break;
 +      case WPS_EV_ER_ENROLLEE_ADD:
 +              break;
 +      case WPS_EV_ER_ENROLLEE_REMOVE:
 +              break;
 +      case WPS_EV_ER_AP_SETTINGS:
 +              break;
 +      case WPS_EV_ER_SET_SELECTED_REGISTRAR:
 +              break;
 +      case WPS_EV_AP_PIN_SUCCESS:
 +              hostapd_wps_ap_pin_success(hapd);
 +              break;
 +      }
 +      if (hapd->wps_event_cb)
 +              hapd->wps_event_cb(hapd->wps_event_cb_ctx, event, data);
 +}
 +
 +
 +static int hostapd_wps_rf_band_cb(void *ctx)
 +{
 +      struct hostapd_data *hapd = ctx;
 +
 +      return hapd->iconf->hw_mode == HOSTAPD_MODE_IEEE80211A ?
-       if (deinit_only)
++              WPS_RF_50GHZ :
++              hapd->iconf->hw_mode == HOSTAPD_MODE_IEEE80211AD ?
++              WPS_RF_60GHZ : 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;
 +
 +      wpabuf_free(hapd->wps_probe_resp_ie);
 +      hapd->wps_probe_resp_ie = NULL;
 +
-                       WPS_RF_50GHZ : WPS_RF_24GHZ; /* FIX: dualband AP */
++      if (deinit_only) {
++              hostapd_reset_ap_wps_ie(hapd);
 +              return;
++      }
 +
 +      hostapd_set_ap_wps_ie(hapd);
 +}
 +
 +
 +static int get_uuid_cb(struct hostapd_iface *iface, void *ctx)
 +{
 +      const u8 **uuid = ctx;
 +      size_t j;
 +
 +      if (iface == NULL)
 +              return 0;
 +      for (j = 0; j < iface->num_bss; j++) {
 +              struct hostapd_data *hapd = iface->bss[j];
 +              if (hapd->wps && !hapd->conf->wps_independent &&
 +                  !is_nil_uuid(hapd->wps->uuid)) {
 +                      *uuid = hapd->wps->uuid;
 +                      return 1;
 +              }
 +      }
 +
 +      return 0;
 +}
 +
 +
 +static const u8 * get_own_uuid(struct hostapd_iface *iface)
 +{
 +      const u8 *uuid;
 +      if (iface->interfaces == NULL ||
 +          iface->interfaces->for_each_interface == NULL)
 +              return NULL;
 +      uuid = NULL;
 +      iface->interfaces->for_each_interface(iface->interfaces, get_uuid_cb,
 +                                            &uuid);
 +      return uuid;
 +}
 +
 +
 +static int count_interface_cb(struct hostapd_iface *iface, void *ctx)
 +{
 +      int *count= ctx;
 +      (*count)++;
 +      return 0;
 +}
 +
 +
 +static int interface_count(struct hostapd_iface *iface)
 +{
 +      int count = 0;
 +      if (iface->interfaces == NULL ||
 +          iface->interfaces->for_each_interface == NULL)
 +              return 0;
 +      iface->interfaces->for_each_interface(iface->interfaces,
 +                                            count_interface_cb, &count);
 +      return count;
 +}
 +
 +
 +static int hostapd_wps_set_vendor_ext(struct hostapd_data *hapd,
 +                                    struct wps_context *wps)
 +{
 +      int i;
 +
 +      for (i = 0; i < MAX_WPS_VENDOR_EXTENSIONS; i++) {
 +              wpabuf_free(wps->dev.vendor_ext[i]);
 +              wps->dev.vendor_ext[i] = NULL;
 +
 +              if (hapd->conf->wps_vendor_ext[i] == NULL)
 +                      continue;
 +
 +              wps->dev.vendor_ext[i] =
 +                      wpabuf_dup(hapd->conf->wps_vendor_ext[i]);
 +              if (wps->dev.vendor_ext[i] == NULL) {
 +                      while (--i >= 0)
 +                              wpabuf_free(wps->dev.vendor_ext[i]);
 +                      return -1;
 +              }
 +      }
 +
 +      return 0;
 +}
 +
 +
 +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)
 +{
 +      struct wps_context *wps;
 +      struct wps_registrar_config cfg;
 +
 +      if (conf->wps_state == 0) {
 +              hostapd_wps_clear_ies(hapd, 0);
 +              return 0;
 +      }
 +
 +      wps = os_zalloc(sizeof(*wps));
 +      if (wps == NULL)
 +              return -1;
 +
 +      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));
 +      wps->wps_state = hapd->conf->wps_state;
 +      wps->ap_setup_locked = hapd->conf->ap_setup_locked;
 +      if (is_nil_uuid(hapd->conf->uuid)) {
 +              const u8 *uuid;
 +              uuid = get_own_uuid(hapd->iface);
 +              if (uuid && !conf->wps_independent) {
 +                      os_memcpy(wps->uuid, uuid, UUID_LEN);
 +                      wpa_hexdump(MSG_DEBUG, "WPS: Clone UUID from another "
 +                                  "interface", wps->uuid, UUID_LEN);
 +              } else {
 +                      uuid_gen_mac_addr(hapd->own_addr, wps->uuid);
 +                      wpa_hexdump(MSG_DEBUG, "WPS: UUID based on MAC "
 +                                  "address", wps->uuid, UUID_LEN);
 +              }
 +      } else {
 +              os_memcpy(wps->uuid, hapd->conf->uuid, UUID_LEN);
 +              wpa_hexdump(MSG_DEBUG, "WPS: Use configured UUID",
 +                          wps->uuid, UUID_LEN);
 +      }
 +      wps->ssid_len = hapd->conf->ssid.ssid_len;
 +      os_memcpy(wps->ssid, hapd->conf->ssid.ssid, wps->ssid_len);
 +      wps->ap = 1;
 +      os_memcpy(wps->dev.mac_addr, hapd->own_addr, ETH_ALEN);
 +      wps->dev.device_name = hapd->conf->device_name ?
 +              os_strdup(hapd->conf->device_name) : NULL;
 +      wps->dev.manufacturer = hapd->conf->manufacturer ?
 +              os_strdup(hapd->conf->manufacturer) : NULL;
 +      wps->dev.model_name = hapd->conf->model_name ?
 +              os_strdup(hapd->conf->model_name) : NULL;
 +      wps->dev.model_number = hapd->conf->model_number ?
 +              os_strdup(hapd->conf->model_number) : NULL;
 +      wps->dev.serial_number = hapd->conf->serial_number ?
 +              os_strdup(hapd->conf->serial_number) : NULL;
 +      wps->config_methods =
 +              wps_config_methods_str2bin(hapd->conf->config_methods);
 +      if ((wps->config_methods &
 +           (WPS_CONFIG_DISPLAY | WPS_CONFIG_VIRT_DISPLAY |
 +            WPS_CONFIG_PHY_DISPLAY)) == WPS_CONFIG_DISPLAY) {
 +              wpa_printf(MSG_INFO, "WPS: Converting display to "
 +                         "virtual_display for WPS 2.0 compliance");
 +              wps->config_methods |= WPS_CONFIG_VIRT_DISPLAY;
 +      }
 +      if ((wps->config_methods &
 +           (WPS_CONFIG_PUSHBUTTON | WPS_CONFIG_VIRT_PUSHBUTTON |
 +            WPS_CONFIG_PHY_PUSHBUTTON)) == WPS_CONFIG_PUSHBUTTON) {
 +              wpa_printf(MSG_INFO, "WPS: Converting push_button to "
 +                         "virtual_push_button for WPS 2.0 compliance");
 +              wps->config_methods |= WPS_CONFIG_VIRT_PUSHBUTTON;
 +      }
 +      os_memcpy(wps->dev.pri_dev_type, hapd->conf->device_type,
 +                WPS_DEV_TYPE_LEN);
 +
 +      if (hostapd_wps_set_vendor_ext(hapd, wps) < 0)
 +              goto fail;
 +
 +      wps->dev.os_version = WPA_GET_BE32(hapd->conf->os_version);
 +
 +      if (conf->wps_rf_bands) {
 +              wps->dev.rf_bands = conf->wps_rf_bands;
 +      } else {
 +              wps->dev.rf_bands =
 +                      hapd->iconf->hw_mode == HOSTAPD_MODE_IEEE80211A ?
-       const u8 *p2p_dev_addr = ctx;
-       if (hapd->wps == NULL)
-               return -1;
-       return wps_registrar_button_pushed(hapd->wps->registrar, p2p_dev_addr);
++                      WPS_RF_50GHZ :
++                      hapd->iconf->hw_mode == HOSTAPD_MODE_IEEE80211AD ?
++                      WPS_RF_60GHZ : WPS_RF_24GHZ; /* FIX: dualband AP */
 +      }
 +
 +      if (conf->wpa & WPA_PROTO_RSN) {
 +              if (conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK)
 +                      wps->auth_types |= WPS_AUTH_WPA2PSK;
 +              if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X)
 +                      wps->auth_types |= WPS_AUTH_WPA2;
 +
 +              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;
 +      }
 +
 +      if (conf->wpa & WPA_PROTO_WPA) {
 +              if (conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK)
 +                      wps->auth_types |= WPS_AUTH_WPAPSK;
 +              if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X)
 +                      wps->auth_types |= WPS_AUTH_WPA;
 +
 +              if (conf->wpa_pairwise & WPA_CIPHER_CCMP)
 +                      wps->encr_types |= WPS_ENCR_AES;
 +              if (conf->wpa_pairwise & WPA_CIPHER_TKIP)
 +                      wps->encr_types |= WPS_ENCR_TKIP;
 +      }
 +
 +      if (conf->ssid.security_policy == SECURITY_PLAINTEXT) {
 +              wps->encr_types |= WPS_ENCR_NONE;
 +              wps->auth_types |= WPS_AUTH_OPEN;
 +      }
 +
 +      if (conf->ssid.wpa_psk_file) {
 +              /* Use per-device PSKs */
 +      } else if (conf->ssid.wpa_passphrase) {
 +              wps->network_key = (u8 *) os_strdup(conf->ssid.wpa_passphrase);
 +              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)
 +                      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)
 +                      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];
 +      }
 +
 +      if (conf->ssid.wpa_psk) {
 +              os_memcpy(wps->psk, conf->ssid.wpa_psk->psk, PMK_LEN);
 +              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;
 +              wps->encr_types = WPS_ENCR_AES | WPS_ENCR_TKIP;
 +      }
 +
 +      wps->ap_settings = conf->ap_settings;
 +      wps->ap_settings_len = conf->ap_settings_len;
 +
 +      cfg.new_psk_cb = hostapd_wps_new_psk_cb;
 +      cfg.set_ie_cb = hostapd_wps_set_ie_cb;
 +      cfg.pin_needed_cb = hostapd_wps_pin_needed_cb;
 +      cfg.reg_success_cb = hostapd_wps_reg_success_cb;
 +      cfg.enrollee_seen_cb = hostapd_wps_enrollee_seen_cb;
 +      cfg.cb_ctx = hapd;
 +      cfg.skip_cred_build = conf->skip_cred_build;
 +      cfg.extra_cred = conf->extra_cred;
 +      cfg.extra_cred_len = conf->extra_cred_len;
 +      cfg.disable_auto_conf = (hapd->conf->wps_cred_processing == 1) &&
 +              conf->skip_cred_build;
 +      if (conf->ssid.security_policy == SECURITY_STATIC_WEP)
 +              cfg.static_wep_only = 1;
 +      cfg.dualband = interface_count(hapd->iface) > 1;
 +      if ((wps->dev.rf_bands & (WPS_RF_50GHZ | WPS_RF_24GHZ)) ==
 +          (WPS_RF_50GHZ | WPS_RF_24GHZ))
 +              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");
 +              goto fail;
 +      }
 +
 +#ifdef CONFIG_WPS_UPNP
 +      wps->friendly_name = hapd->conf->friendly_name;
 +      wps->manufacturer_url = hapd->conf->manufacturer_url;
 +      wps->model_description = hapd->conf->model_description;
 +      wps->model_url = hapd->conf->model_url;
 +      wps->upc = hapd->conf->upc;
 +#endif /* CONFIG_WPS_UPNP */
 +
 +      hostapd_register_probereq_cb(hapd, hostapd_wps_probe_req_rx, hapd);
 +
 +      hapd->wps = wps;
 +
 +      return 0;
 +
 +fail:
 +      hostapd_free_wps(wps);
 +      return -1;
 +}
 +
 +
 +int hostapd_init_wps_complete(struct hostapd_data *hapd)
 +{
 +      struct wps_context *wps = hapd->wps;
 +
 +      if (wps == NULL)
 +              return 0;
 +
 +#ifdef CONFIG_WPS_UPNP
 +      if (hostapd_wps_upnp_init(hapd, wps) < 0) {
 +              wpa_printf(MSG_ERROR, "Failed to initialize WPS UPnP");
 +              wps_registrar_deinit(wps->registrar);
 +              hostapd_free_wps(wps);
 +              hapd->wps = NULL;
 +              return -1;
 +      }
 +#endif /* CONFIG_WPS_UPNP */
 +
 +      return 0;
 +}
 +
 +
 +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;
 +      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_NFC */
 +}
 +
 +
 +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);
 +      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);
 +      wps_free_pending_msgs(hapd->wps->upnp_msgs);
 +      hostapd_free_wps(hapd->wps);
 +      hapd->wps = NULL;
 +      hostapd_wps_clear_ies(hapd, 1);
 +}
 +
 +
 +void hostapd_update_wps(struct hostapd_data *hapd)
 +{
 +      if (hapd->wps == NULL)
 +              return;
 +
 +#ifdef CONFIG_WPS_UPNP
 +      hapd->wps->friendly_name = hapd->conf->friendly_name;
 +      hapd->wps->manufacturer_url = hapd->conf->manufacturer_url;
 +      hapd->wps->model_description = hapd->conf->model_description;
 +      hapd->wps->model_url = hapd->conf->model_url;
 +      hapd->wps->upc = hapd->conf->upc;
 +#endif /* CONFIG_WPS_UPNP */
 +
 +      hostapd_wps_set_vendor_ext(hapd, hapd->wps);
 +
 +      if (hapd->conf->wps_state)
 +              wps_registrar_update_ie(hapd->wps->registrar);
 +      else
 +              hostapd_deinit_wps(hapd);
 +}
 +
 +
 +struct wps_add_pin_data {
 +      const u8 *addr;
 +      const u8 *uuid;
 +      const u8 *pin;
 +      size_t pin_len;
 +      int timeout;
 +      int added;
 +};
 +
 +
 +static int wps_add_pin(struct hostapd_data *hapd, void *ctx)
 +{
 +      struct wps_add_pin_data *data = ctx;
 +      int ret;
 +
 +      if (hapd->wps == NULL)
 +              return 0;
 +      ret = wps_registrar_add_pin(hapd->wps->registrar, data->addr,
 +                                  data->uuid, data->pin, data->pin_len,
 +                                  data->timeout);
 +      if (ret == 0)
 +              data->added++;
 +      return ret;
 +}
 +
 +
 +int hostapd_wps_add_pin(struct hostapd_data *hapd, const u8 *addr,
 +                      const char *uuid, const char *pin, int timeout)
 +{
 +      u8 u[UUID_LEN];
 +      struct wps_add_pin_data data;
 +
 +      data.addr = addr;
 +      data.uuid = u;
 +      data.pin = (const u8 *) pin;
 +      data.pin_len = os_strlen(pin);
 +      data.timeout = timeout;
 +      data.added = 0;
 +
 +      if (os_strcmp(uuid, "any") == 0)
 +              data.uuid = NULL;
 +      else {
 +              if (uuid_str2bin(uuid, u))
 +                      return -1;
 +              data.uuid = u;
 +      }
 +      if (hostapd_wps_for_each(hapd, wps_add_pin, &data) < 0)
 +              return -1;
 +      return data.added ? 0 : -1;
 +}
 +
 +
++struct wps_button_pushed_ctx {
++      const u8 *p2p_dev_addr;
++      unsigned int count;
++};
++
 +static int wps_button_pushed(struct hostapd_data *hapd, void *ctx)
 +{
-       return hostapd_wps_for_each(hapd, wps_button_pushed,
-                                   (void *) p2p_dev_addr);
++      struct wps_button_pushed_ctx *data = ctx;
++
++      if (hapd->wps) {
++              data->count++;
++              return wps_registrar_button_pushed(hapd->wps->registrar,
++                                                 data->p2p_dev_addr);
++      }
++
++      return 0;
 +}
 +
 +
 +int hostapd_wps_button_pushed(struct hostapd_data *hapd,
 +                            const u8 *p2p_dev_addr)
 +{
-       if (hapd->wps == NULL)
-               return -1;
++      struct wps_button_pushed_ctx ctx;
++      int ret;
++
++      os_memset(&ctx, 0, sizeof(ctx));
++      ctx.p2p_dev_addr = p2p_dev_addr;
++      ret = hostapd_wps_for_each(hapd, wps_button_pushed, &ctx);
++      if (ret == 0 && !ctx.count)
++              ret = -1;
++      return ret;
 +}
 +
 +
++struct wps_cancel_ctx {
++      unsigned int count;
++};
++
 +static int wps_cancel(struct hostapd_data *hapd, void *ctx)
 +{
-       wps_registrar_wps_cancel(hapd->wps->registrar);
-       ap_for_each_sta(hapd, ap_sta_wps_cancel, NULL);
++      struct wps_cancel_ctx *data = ctx;
 +
-       return hostapd_wps_for_each(hapd, wps_cancel, NULL);
++      if (hapd->wps) {
++              data->count++;
++              wps_registrar_wps_cancel(hapd->wps->registrar);
++              ap_for_each_sta(hapd, ap_sta_wps_cancel, NULL);
++      }
 +
 +      return 0;
 +}
 +
 +
 +int hostapd_wps_cancel(struct hostapd_data *hapd)
 +{
++      struct wps_cancel_ctx ctx;
++      int ret;
++
++      os_memset(&ctx, 0, sizeof(ctx));
++      ret = hostapd_wps_for_each(hapd, wps_cancel, &ctx);
++      if (ret == 0 && !ctx.count)
++              ret = -1;
++      return ret;
 +}
 +
 +
 +static int hostapd_wps_probe_req_rx(void *ctx, const u8 *addr, const u8 *da,
 +                                  const u8 *bssid,
 +                                  const u8 *ie, size_t ie_len,
 +                                  int ssi_signal)
 +{
 +      struct hostapd_data *hapd = ctx;
 +      struct wpabuf *wps_ie;
 +      struct ieee802_11_elems elems;
 +
 +      if (hapd->wps == NULL)
 +              return 0;
 +
 +      if (ieee802_11_parse_elems(ie, ie_len, &elems, 0) == ParseFailed) {
 +              wpa_printf(MSG_DEBUG, "WPS: Could not parse ProbeReq from "
 +                         MACSTR, MAC2STR(addr));
 +              return 0;
 +      }
 +
 +      if (elems.ssid && elems.ssid_len > 0 &&
 +          (elems.ssid_len != hapd->conf->ssid.ssid_len ||
 +           os_memcmp(elems.ssid, hapd->conf->ssid.ssid, elems.ssid_len) !=
 +           0))
 +              return 0; /* Not for us */
 +
 +      wps_ie = ieee802_11_vendor_ie_concat(ie, ie_len, WPS_DEV_OUI_WFA);
 +      if (wps_ie == NULL)
 +              return 0;
 +      if (wps_validate_probe_req(wps_ie, addr) < 0) {
 +              wpabuf_free(wps_ie);
 +              return 0;
 +      }
 +
 +      if (wpabuf_len(wps_ie) > 0) {
 +              int p2p_wildcard = 0;
 +#ifdef CONFIG_P2P
 +              if (elems.ssid && elems.ssid_len == P2P_WILDCARD_SSID_LEN &&
 +                  os_memcmp(elems.ssid, P2P_WILDCARD_SSID,
 +                            P2P_WILDCARD_SSID_LEN) == 0)
 +                      p2p_wildcard = 1;
 +#endif /* CONFIG_P2P */
 +              wps_registrar_probe_req_rx(hapd->wps->registrar, addr, wps_ie,
 +                                         p2p_wildcard);
 +#ifdef CONFIG_WPS_UPNP
 +              /* FIX: what exactly should be included in the WLANEvent?
 +               * WPS attributes? Full ProbeReq frame? */
 +              if (!p2p_wildcard)
 +                      upnp_wps_device_send_wlan_event(
 +                              hapd->wps_upnp, addr,
 +                              UPNP_WPS_WLANEVENT_TYPE_PROBE, wps_ie);
 +#endif /* CONFIG_WPS_UPNP */
 +      }
 +
 +      wpabuf_free(wps_ie);
 +
 +      return 0;
 +}
 +
 +
 +#ifdef CONFIG_WPS_UPNP
 +
 +static int hostapd_rx_req_put_wlan_response(
 +      void *priv, enum upnp_wps_wlanevent_type ev_type,
 +      const u8 *mac_addr, const struct wpabuf *msg,
 +      enum wps_msg_type msg_type)
 +{
 +      struct hostapd_data *hapd = priv;
 +      struct sta_info *sta;
 +      struct upnp_pending_message *p;
 +
 +      wpa_printf(MSG_DEBUG, "WPS UPnP: PutWLANResponse ev_type=%d mac_addr="
 +                 MACSTR, ev_type, MAC2STR(mac_addr));
 +      wpa_hexdump(MSG_MSGDUMP, "WPS UPnP: PutWLANResponse NewMessage",
 +                  wpabuf_head(msg), wpabuf_len(msg));
 +      if (ev_type != UPNP_WPS_WLANEVENT_TYPE_EAP) {
 +              wpa_printf(MSG_DEBUG, "WPS UPnP: Ignored unexpected "
 +                         "PutWLANResponse WLANEventType %d", ev_type);
 +              return -1;
 +      }
 +
 +      /*
 +       * EAP response to ongoing to WPS Registration. Send it to EAP-WSC
 +       * server implementation for delivery to the peer.
 +       */
 +
 +      sta = ap_get_sta(hapd, mac_addr);
 +#ifndef CONFIG_WPS_STRICT
 +      if (!sta) {
 +              /*
 +               * Workaround - Intel wsccmd uses bogus NewWLANEventMAC:
 +               * Pick STA that is in an ongoing WPS registration without
 +               * checking the MAC address.
 +               */
 +              wpa_printf(MSG_DEBUG, "WPS UPnP: No matching STA found based "
 +                         "on NewWLANEventMAC; try wildcard match");
 +              for (sta = hapd->sta_list; sta; sta = sta->next) {
 +                      if (sta->eapol_sm && (sta->flags & WLAN_STA_WPS))
 +                              break;
 +              }
 +      }
 +#endif /* CONFIG_WPS_STRICT */
 +
 +      if (!sta || !(sta->flags & WLAN_STA_WPS)) {
 +              wpa_printf(MSG_DEBUG, "WPS UPnP: No matching STA found");
 +              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;
 +      os_memcpy(p->addr, sta->addr, ETH_ALEN);
 +      p->msg = wpabuf_dup(msg);
 +      p->type = msg_type;
 +      p->next = hapd->wps->upnp_msgs;
 +      hapd->wps->upnp_msgs = p;
 +
 +      return eapol_auth_eap_pending_cb(sta->eapol_sm, sta->eapol_sm->eap);
 +}
 +
 +
 +static int hostapd_wps_upnp_init(struct hostapd_data *hapd,
 +                               struct wps_context *wps)
 +{
 +      struct upnp_wps_device_ctx *ctx;
 +
 +      if (!hapd->conf->upnp_iface)
 +              return 0;
 +      ctx = os_zalloc(sizeof(*ctx));
 +      if (ctx == NULL)
 +              return -1;
 +
 +      ctx->rx_req_put_wlan_response = hostapd_rx_req_put_wlan_response;
 +      if (hapd->conf->ap_pin)
 +              ctx->ap_pin = os_strdup(hapd->conf->ap_pin);
 +
 +      hapd->wps_upnp = upnp_wps_device_init(ctx, wps, hapd,
 +                                            hapd->conf->upnp_iface);
 +      if (hapd->wps_upnp == NULL)
 +              return -1;
 +      wps->wps_upnp = hapd->wps_upnp;
 +
 +      return 0;
 +}
 +
 +
 +static void hostapd_wps_upnp_deinit(struct hostapd_data *hapd)
 +{
 +      upnp_wps_device_deinit(hapd->wps_upnp, hapd);
 +}
 +
 +#endif /* CONFIG_WPS_UPNP */
 +
 +
 +int hostapd_wps_get_mib_sta(struct hostapd_data *hapd, const u8 *addr,
 +                          char *buf, size_t buflen)
 +{
 +      if (hapd->wps == NULL)
 +              return 0;
 +      return wps_registrar_get_info(hapd->wps->registrar, addr, buf, buflen);
 +}
 +
 +
 +static void hostapd_wps_ap_pin_timeout(void *eloop_data, void *user_ctx)
 +{
 +      struct hostapd_data *hapd = eloop_data;
 +      wpa_printf(MSG_DEBUG, "WPS: AP PIN timed out");
 +      hostapd_wps_ap_pin_disable(hapd);
 +      wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_AP_PIN_DISABLED);
 +}
 +
 +
 +static void hostapd_wps_ap_pin_enable(struct hostapd_data *hapd, int timeout)
 +{
 +      wpa_printf(MSG_DEBUG, "WPS: Enabling AP PIN (timeout=%d)", timeout);
 +      hapd->ap_pin_failures = 0;
 +      hapd->ap_pin_failures_consecutive = 0;
 +      hapd->conf->ap_setup_locked = 0;
 +      if (hapd->wps->ap_setup_locked) {
 +              wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_AP_SETUP_UNLOCKED);
 +              hapd->wps->ap_setup_locked = 0;
 +              wps_registrar_update_ie(hapd->wps->registrar);
 +      }
 +      eloop_cancel_timeout(hostapd_wps_ap_pin_timeout, hapd, NULL);
 +      if (timeout > 0)
 +              eloop_register_timeout(timeout, 0,
 +                                     hostapd_wps_ap_pin_timeout, hapd, NULL);
 +}
 +
 +
 +static int wps_ap_pin_disable(struct hostapd_data *hapd, void *ctx)
 +{
 +      os_free(hapd->conf->ap_pin);
 +      hapd->conf->ap_pin = NULL;
 +#ifdef CONFIG_WPS_UPNP
 +      upnp_wps_set_ap_pin(hapd->wps_upnp, NULL);
 +#endif /* CONFIG_WPS_UPNP */
 +      eloop_cancel_timeout(hostapd_wps_ap_pin_timeout, hapd, NULL);
 +      return 0;
 +}
 +
 +
 +void hostapd_wps_ap_pin_disable(struct hostapd_data *hapd)
 +{
 +      wpa_printf(MSG_DEBUG, "WPS: Disabling AP PIN");
 +      hostapd_wps_for_each(hapd, wps_ap_pin_disable, NULL);
 +}
 +
 +
 +struct wps_ap_pin_data {
 +      char pin_txt[9];
 +      int timeout;
 +};
 +
 +
 +static int wps_ap_pin_set(struct hostapd_data *hapd, void *ctx)
 +{
 +      struct wps_ap_pin_data *data = ctx;
++
++      if (!hapd->wps)
++              return 0;
++
 +      os_free(hapd->conf->ap_pin);
 +      hapd->conf->ap_pin = os_strdup(data->pin_txt);
 +#ifdef CONFIG_WPS_UPNP
 +      upnp_wps_set_ap_pin(hapd->wps_upnp, data->pin_txt);
 +#endif /* CONFIG_WPS_UPNP */
 +      hostapd_wps_ap_pin_enable(hapd, data->timeout);
 +      return 0;
 +}
 +
 +
 +const char * hostapd_wps_ap_pin_random(struct hostapd_data *hapd, int timeout)
 +{
 +      unsigned int pin;
 +      struct wps_ap_pin_data data;
 +
 +      pin = wps_generate_pin();
 +      os_snprintf(data.pin_txt, sizeof(data.pin_txt), "%08u", pin);
 +      data.timeout = timeout;
 +      hostapd_wps_for_each(hapd, wps_ap_pin_set, &data);
 +      return hapd->conf->ap_pin;
 +}
 +
 +
 +const char * hostapd_wps_ap_pin_get(struct hostapd_data *hapd)
 +{
 +      return hapd->conf->ap_pin;
 +}
 +
 +
 +int hostapd_wps_ap_pin_set(struct hostapd_data *hapd, const char *pin,
 +                         int timeout)
 +{
 +      struct wps_ap_pin_data data;
 +      int ret;
 +
 +      ret = os_snprintf(data.pin_txt, sizeof(data.pin_txt), "%s", pin);
 +      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);
 +}
 +
 +
 +static int wps_update_ie(struct hostapd_data *hapd, void *ctx)
 +{
 +      if (hapd->wps)
 +              wps_registrar_update_ie(hapd->wps->registrar);
 +      return 0;
 +}
 +
 +
 +void hostapd_wps_update_ie(struct hostapd_data *hapd)
 +{
 +      hostapd_wps_for_each(hapd, wps_update_ie, NULL);
 +}
 +
 +
 +int hostapd_wps_config_ap(struct hostapd_data *hapd, const char *ssid,
 +                        const char *auth, const char *encr, const char *key)
 +{
 +      struct wps_credential cred;
 +      size_t len;
 +
 +      os_memset(&cred, 0, sizeof(cred));
 +
 +      len = os_strlen(ssid);
 +      if ((len & 1) || len > 2 * sizeof(cred.ssid) ||
 +          hexstr2bin(ssid, cred.ssid, len / 2))
 +              return -1;
 +      cred.ssid_len = len / 2;
 +
 +      if (os_strncmp(auth, "OPEN", 4) == 0)
 +              cred.auth_type = WPS_AUTH_OPEN;
 +      else if (os_strncmp(auth, "WPAPSK", 6) == 0)
 +              cred.auth_type = WPS_AUTH_WPAPSK;
 +      else if (os_strncmp(auth, "WPA2PSK", 7) == 0)
 +              cred.auth_type = WPS_AUTH_WPA2PSK;
 +      else
 +              return -1;
 +
 +      if (encr) {
 +              if (os_strncmp(encr, "NONE", 4) == 0)
 +                      cred.encr_type = WPS_ENCR_NONE;
 +              else if (os_strncmp(encr, "TKIP", 4) == 0)
 +                      cred.encr_type = WPS_ENCR_TKIP;
 +              else if (os_strncmp(encr, "CCMP", 4) == 0)
 +                      cred.encr_type = WPS_ENCR_AES;
 +              else
 +                      return -1;
 +      } else
 +              cred.encr_type = WPS_ENCR_NONE;
 +
 +      if (key) {
 +              len = os_strlen(key);
 +              if ((len & 1) || len > 2 * sizeof(cred.key) ||
 +                  hexstr2bin(key, cred.key, len / 2))
 +                      return -1;
 +              cred.key_len = len / 2;
 +      }
 +
 +      return wps_registrar_config_ap(hapd->wps->registrar, &cred);
 +}
 +
 +
 +#ifdef CONFIG_WPS_NFC
 +
 +struct wps_nfc_password_token_data {
 +      const u8 *oob_dev_pw;
 +      size_t oob_dev_pw_len;
 +      int added;
 +};
 +
 +
 +static int wps_add_nfc_password_token(struct hostapd_data *hapd, void *ctx)
 +{
 +      struct wps_nfc_password_token_data *data = ctx;
 +      int ret;
 +
 +      if (hapd->wps == NULL)
 +              return 0;
 +      ret = wps_registrar_add_nfc_password_token(hapd->wps->registrar,
 +                                                 data->oob_dev_pw,
 +                                                 data->oob_dev_pw_len);
 +      if (ret == 0)
 +              data->added++;
 +      return ret;
 +}
 +
 +
 +static int hostapd_wps_add_nfc_password_token(struct hostapd_data *hapd,
 +                                            struct wps_parse_attr *attr)
 +{
 +      struct wps_nfc_password_token_data data;
 +
 +      data.oob_dev_pw = attr->oob_dev_password;
 +      data.oob_dev_pw_len = attr->oob_dev_password_len;
 +      data.added = 0;
 +      if (hostapd_wps_for_each(hapd, wps_add_nfc_password_token, &data) < 0)
 +              return -1;
 +      return data.added ? 0 : -1;
 +}
 +
 +
 +static int hostapd_wps_nfc_tag_process(struct hostapd_data *hapd,
 +                                     const struct wpabuf *wps)
 +{
 +      struct wps_parse_attr attr;
 +
 +      wpa_hexdump_buf(MSG_DEBUG, "WPS: Received NFC tag payload", wps);
 +
 +      if (wps_parse_msg(wps, &attr)) {
 +              wpa_printf(MSG_DEBUG, "WPS: Ignore invalid data from NFC tag");
 +              return -1;
 +      }
 +
 +      if (attr.oob_dev_password)
 +              return hostapd_wps_add_nfc_password_token(hapd, &attr);
 +
 +      wpa_printf(MSG_DEBUG, "WPS: Ignore unrecognized NFC tag");
 +      return -1;
 +}
 +
 +
 +int hostapd_wps_nfc_tag_read(struct hostapd_data *hapd,
 +                           const struct wpabuf *data)
 +{
 +      const struct wpabuf *wps = data;
 +      struct wpabuf *tmp = NULL;
 +      int ret;
 +
 +      if (wpabuf_len(data) < 4)
 +              return -1;
 +
 +      if (*wpabuf_head_u8(data) != 0x10) {
 +              /* Assume this contains full NDEF record */
 +              tmp = ndef_parse_wifi(data);
 +              if (tmp == NULL) {
 +                      wpa_printf(MSG_DEBUG, "WPS: Could not parse NDEF");
 +                      return -1;
 +              }
 +              wps = tmp;
 +      }
 +
 +      ret = hostapd_wps_nfc_tag_process(hapd, wps);
 +      wpabuf_free(tmp);
 +      return ret;
 +}
 +
 +
 +struct wpabuf * hostapd_wps_nfc_config_token(struct hostapd_data *hapd,
 +                                           int ndef)
 +{
 +      struct wpabuf *ret;
 +
 +      if (hapd->wps == NULL)
 +              return NULL;
 +
 +      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);
 +              wpabuf_free(ret);
 +              if (tmp == NULL)
 +                      return NULL;
 +              ret = tmp;
 +      }
 +
 +      return ret;
 +}
 +
 +
 +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;
 +
 +      /*
 +       * 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(hapd->wps->registrar,
 +                                           attr.oob_dev_password,
 +                                           DEV_PW_NFC_CONNECTION_HANDOVER,
 +                                           NULL, 0, 1);
 +
 +out:
 +      wpabuf_free(wps);
 +      return ret;
 +}
 +
 +
 +struct wpabuf * hostapd_wps_nfc_token_gen(struct hostapd_data *hapd, int ndef)
 +{
 +      if (hapd->conf->wps_nfc_pw_from_config) {
 +              return wps_nfc_token_build(ndef,
 +                                         hapd->conf->wps_nfc_dev_pw_id,
 +                                         hapd->conf->wps_nfc_dh_pubkey,
 +                                         hapd->conf->wps_nfc_dev_pw);
 +      }
 +
 +      return wps_nfc_token_gen(ndef, &hapd->conf->wps_nfc_dev_pw_id,
 +                               &hapd->conf->wps_nfc_dh_pubkey,
 +                               &hapd->conf->wps_nfc_dh_privkey,
 +                               &hapd->conf->wps_nfc_dev_pw);
 +}
 +
 +
 +int hostapd_wps_nfc_token_enable(struct hostapd_data *hapd)
 +{
 +      struct wps_context *wps = hapd->wps;
 +      struct wpabuf *pw;
 +
 +      if (wps == NULL)
 +              return -1;
 +
 +      if (!hapd->conf->wps_nfc_dh_pubkey ||
 +          !hapd->conf->wps_nfc_dh_privkey ||
 +          !hapd->conf->wps_nfc_dev_pw ||
 +          !hapd->conf->wps_nfc_dev_pw_id)
 +              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);
 +      pw = hapd->conf->wps_nfc_dev_pw;
 +      wps->ap_nfc_dev_pw = wpabuf_alloc(
 +              wpabuf_len(pw) * 2 + 1);
 +      if (wps->ap_nfc_dev_pw) {
 +              wpa_snprintf_hex_uppercase(
 +                      (char *) wpabuf_put(wps->ap_nfc_dev_pw,
 +                                          wpabuf_len(pw) * 2),
 +                      wpabuf_len(pw) * 2 + 1,
 +                      wpabuf_head(pw), wpabuf_len(pw));
 +      }
 +
 +      if (!wps->ap_nfc_dh_pubkey || !wps->ap_nfc_dh_privkey ||
 +          !wps->ap_nfc_dev_pw) {
 +              hostapd_wps_nfc_clear(wps);
 +              return -1;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +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);
 +}
 +
 +#endif /* CONFIG_WPS_NFC */
index 8f77015ef57fb940ddd951661aa98a11ce5b3832,0000000000000000000000000000000000000000..aef9a53c46cf90476592feee5d836ca82ae68379
mode 100644,000000..100644
--- /dev/null
@@@ -1,123 -1,0 +1,131 @@@
 +/*
 + * 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;
 +      }
 +
++#ifdef CONFIG_IPV6
++      if (hostapd_drv_br_set_net_param(hapd, DRV_BR_MULTICAST_SNOOPING, 1)) {
++              wpa_printf(MSG_DEBUG,
++                         "x_snoop: Failed to enable multicast snooping on the bridge");
++              return -1;
++      }
++#endif /* CONFIG_IPV6 */
++
 +      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);
 +}
index 56b11220c9ed431cbf9fb673455ca647a9598fe1,0000000000000000000000000000000000000000..d69448bd3800c66c2fe79b1fd0a2ca40279fbf74
mode 100644,000000..100644
--- /dev/null
@@@ -1,172 -1,0 +1,207 @@@
-  * Copyright (c) 2014, Jouni Malinen <j@w1.fi>
 +/*
 + * common 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 "ieee802_11_common.h"
++#include "ieee802_11_defs.h"
++#include "gas.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 },
++      { (u8 *) "\x03\x00\x2a\x00\x36\x00\x37\x00\x38\x00\x2d\x00\x3d\x00\xbf\x00\xc0\x00",
++        18, ParseOK, 9 },
++      { (u8 *) "\x8b\x00", 2, ParseOK, 1 },
++      { (u8 *) "\xdd\x04\x00\x90\x4c\x04", 6, ParseUnknown, 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;
 +}
 +
 +
++static int gas_tests(void)
++{
++      struct wpabuf *buf;
++
++      wpa_printf(MSG_INFO, "gas tests");
++      gas_anqp_set_len(NULL);
++
++      buf = wpabuf_alloc(1);
++      if (buf == NULL)
++              return -1;
++      gas_anqp_set_len(buf);
++      wpabuf_free(buf);
++
++      buf = wpabuf_alloc(20);
++      if (buf == NULL)
++              return -1;
++      wpabuf_put_u8(buf, WLAN_ACTION_PUBLIC);
++      wpabuf_put_u8(buf, WLAN_PA_GAS_INITIAL_REQ);
++      wpabuf_put_u8(buf, 0);
++      wpabuf_put_be32(buf, 0);
++      wpabuf_put_u8(buf, 0);
++      gas_anqp_set_len(buf);
++      wpabuf_free(buf);
++
++      return 0;
++}
++
++
 +int common_module_tests(void)
 +{
 +      int ret = 0;
 +
 +      wpa_printf(MSG_INFO, "common module tests");
 +
 +      if (ieee802_11_parse_tests() < 0 ||
++          gas_tests() < 0 ||
 +          rsn_ie_parse_tests() < 0)
 +              ret = -1;
 +
 +      return ret;
 +}
index b5f4f801eda216bef16454bc6fa5013617ac9c81,0000000000000000000000000000000000000000..6aea3751a2bc9529cc4909a0f16653184573e0f7
mode 100644,000000..100644
--- /dev/null
@@@ -1,329 -1,0 +1,337 @@@
-        * This stat eis entered if the network interface is disabled, e.g.,
 +/*
 + * WPA Supplicant - Common definitions
 + * 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 DEFS_H
 +#define DEFS_H
 +
 +#ifdef FALSE
 +#undef FALSE
 +#endif
 +#ifdef TRUE
 +#undef TRUE
 +#endif
 +typedef enum { FALSE = 0, TRUE = 1 } Boolean;
 +
 +
 +#define WPA_CIPHER_NONE BIT(0)
 +#define WPA_CIPHER_WEP40 BIT(1)
 +#define WPA_CIPHER_WEP104 BIT(2)
 +#define WPA_CIPHER_TKIP BIT(3)
 +#define WPA_CIPHER_CCMP BIT(4)
 +#define WPA_CIPHER_AES_128_CMAC BIT(5)
 +#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)
 +#define WPA_KEY_MGMT_NONE BIT(2)
 +#define WPA_KEY_MGMT_IEEE8021X_NO_WPA BIT(3)
 +#define WPA_KEY_MGMT_WPA_NONE BIT(4)
 +#define WPA_KEY_MGMT_FT_IEEE8021X BIT(5)
 +#define WPA_KEY_MGMT_FT_PSK BIT(6)
 +#define WPA_KEY_MGMT_IEEE8021X_SHA256 BIT(7)
 +#define WPA_KEY_MGMT_PSK_SHA256 BIT(8)
 +#define WPA_KEY_MGMT_WPS BIT(9)
 +#define WPA_KEY_MGMT_SAE BIT(10)
 +#define WPA_KEY_MGMT_FT_SAE BIT(11)
 +#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_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)
 +{
 +      return !!(akm & (WPA_KEY_MGMT_PSK |
 +                       WPA_KEY_MGMT_FT_PSK |
 +                       WPA_KEY_MGMT_PSK_SHA256 |
 +                       WPA_KEY_MGMT_SAE |
 +                       WPA_KEY_MGMT_FT_SAE));
 +}
 +
 +static inline int wpa_key_mgmt_ft(int akm)
 +{
 +      return !!(akm & (WPA_KEY_MGMT_FT_PSK |
 +                       WPA_KEY_MGMT_FT_IEEE8021X |
 +                       WPA_KEY_MGMT_FT_SAE));
 +}
 +
 +static inline int wpa_key_mgmt_sae(int akm)
 +{
 +      return !!(akm & (WPA_KEY_MGMT_SAE |
 +                       WPA_KEY_MGMT_FT_SAE));
 +}
 +
 +static inline int wpa_key_mgmt_sha256(int akm)
 +{
 +      return !!(akm & (WPA_KEY_MGMT_PSK_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_sae(akm);
 +}
 +
 +static inline int wpa_key_mgmt_wpa_any(int akm)
 +{
 +      return wpa_key_mgmt_wpa(akm) || (akm & WPA_KEY_MGMT_WPA_NONE);
 +}
 +
 +static inline int wpa_key_mgmt_cckm(int akm)
 +{
 +      return akm == WPA_KEY_MGMT_CCKM;
 +}
 +
 +
 +#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)
 +#define WPA_AUTH_ALG_LEAP BIT(2)
 +#define WPA_AUTH_ALG_FT BIT(3)
 +#define WPA_AUTH_ALG_SAE BIT(4)
 +
 +
 +enum wpa_alg {
 +      WPA_ALG_NONE,
 +      WPA_ALG_WEP,
 +      WPA_ALG_TKIP,
 +      WPA_ALG_CCMP,
 +      WPA_ALG_IGTK,
 +      WPA_ALG_PMK,
 +      WPA_ALG_GCMP,
 +      WPA_ALG_SMS4,
 +      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
 +};
 +
 +/**
 + * enum wpa_states - wpa_supplicant state
 + *
 + * These enumeration values are used to indicate the current wpa_supplicant
 + * state (wpa_s->wpa_state). The current state can be retrieved with
 + * wpa_supplicant_get_state() function and the state can be changed by calling
 + * wpa_supplicant_set_state(). In WPA state machine (wpa.c and preauth.c), the
 + * wrapper functions wpa_sm_get_state() and wpa_sm_set_state() should be used
 + * to access the state variable.
 + */
 +enum wpa_states {
 +      /**
 +       * WPA_DISCONNECTED - Disconnected state
 +       *
 +       * This state indicates that client is not associated, but is likely to
 +       * start looking for an access point. This state is entered when a
 +       * connection is lost.
 +       */
 +      WPA_DISCONNECTED,
 +
 +      /**
 +       * WPA_INTERFACE_DISABLED - Interface disabled
 +       *
++       * This state is entered if the network interface is disabled, e.g.,
 +       * due to rfkill. wpa_supplicant refuses any new operations that would
 +       * use the radio until the interface has been enabled.
 +       */
 +      WPA_INTERFACE_DISABLED,
 +
 +      /**
 +       * WPA_INACTIVE - Inactive state (wpa_supplicant disabled)
 +       *
 +       * This state is entered if there are no enabled networks in the
 +       * configuration. wpa_supplicant is not trying to associate with a new
 +       * network and external interaction (e.g., ctrl_iface call to add or
 +       * enable a network) is needed to start association.
 +       */
 +      WPA_INACTIVE,
 +
 +      /**
 +       * WPA_SCANNING - Scanning for a network
 +       *
 +       * This state is entered when wpa_supplicant starts scanning for a
 +       * network.
 +       */
 +      WPA_SCANNING,
 +
 +      /**
 +       * WPA_AUTHENTICATING - Trying to authenticate with a BSS/SSID
 +       *
 +       * This state is entered when wpa_supplicant has found a suitable BSS
 +       * to authenticate with and the driver is configured to try to
 +       * authenticate with this BSS. This state is used only with drivers
 +       * that use wpa_supplicant as the SME.
 +       */
 +      WPA_AUTHENTICATING,
 +
 +      /**
 +       * WPA_ASSOCIATING - Trying to associate with a BSS/SSID
 +       *
 +       * This state is entered when wpa_supplicant has found a suitable BSS
 +       * to associate with and the driver is configured to try to associate
 +       * with this BSS in ap_scan=1 mode. When using ap_scan=2 mode, this
 +       * state is entered when the driver is configured to try to associate
 +       * with a network using the configured SSID and security policy.
 +       */
 +      WPA_ASSOCIATING,
 +
 +      /**
 +       * WPA_ASSOCIATED - Association completed
 +       *
 +       * This state is entered when the driver reports that association has
 +       * been successfully completed with an AP. If IEEE 802.1X is used
 +       * (with or without WPA/WPA2), wpa_supplicant remains in this state
 +       * until the IEEE 802.1X/EAPOL authentication has been completed.
 +       */
 +      WPA_ASSOCIATED,
 +
 +      /**
 +       * WPA_4WAY_HANDSHAKE - WPA 4-Way Key Handshake in progress
 +       *
 +       * This state is entered when WPA/WPA2 4-Way Handshake is started. In
 +       * case of WPA-PSK, this happens when receiving the first EAPOL-Key
 +       * frame after association. In case of WPA-EAP, this state is entered
 +       * when the IEEE 802.1X/EAPOL authentication has been completed.
 +       */
 +      WPA_4WAY_HANDSHAKE,
 +
 +      /**
 +       * WPA_GROUP_HANDSHAKE - WPA Group Key Handshake in progress
 +       *
 +       * This state is entered when 4-Way Key Handshake has been completed
 +       * (i.e., when the supplicant sends out message 4/4) and when Group
 +       * Key rekeying is started by the AP (i.e., when supplicant receives
 +       * message 1/2).
 +       */
 +      WPA_GROUP_HANDSHAKE,
 +
 +      /**
 +       * WPA_COMPLETED - All authentication completed
 +       *
 +       * This state is entered when the full authentication process is
 +       * completed. In case of WPA2, this happens when the 4-Way Handshake is
 +       * successfully completed. With WPA, this state is entered after the
 +       * Group Key Handshake; with IEEE 802.1X (non-WPA) connection is
 +       * completed after dynamic keys are received (or if not used, after
 +       * the EAP authentication has been completed). With static WEP keys and
 +       * plaintext connections, this state is entered when an association
 +       * has been completed.
 +       *
 +       * This state indicates that the supplicant has completed its
 +       * processing for the association phase and that data connection is
 +       * fully configured.
 +       */
 +      WPA_COMPLETED
 +};
 +
 +#define MLME_SETPROTECTION_PROTECT_TYPE_NONE 0
 +#define MLME_SETPROTECTION_PROTECT_TYPE_RX 1
 +#define MLME_SETPROTECTION_PROTECT_TYPE_TX 2
 +#define MLME_SETPROTECTION_PROTECT_TYPE_RX_TX 3
 +
 +#define MLME_SETPROTECTION_KEY_TYPE_GROUP 0
 +#define MLME_SETPROTECTION_KEY_TYPE_PAIRWISE 1
 +
 +
 +/**
 + * enum mfp_options - Management frame protection (IEEE 802.11w) options
 + */
 +enum mfp_options {
 +      NO_MGMT_FRAME_PROTECTION = 0,
 +      MGMT_FRAME_PROTECTION_OPTIONAL = 1,
 +      MGMT_FRAME_PROTECTION_REQUIRED = 2,
 +};
 +#define MGMT_FRAME_PROTECTION_DEFAULT 3
 +
 +/**
 + * enum hostapd_hw_mode - Hardware mode
 + */
 +enum hostapd_hw_mode {
 +      HOSTAPD_MODE_IEEE80211B,
 +      HOSTAPD_MODE_IEEE80211G,
 +      HOSTAPD_MODE_IEEE80211A,
 +      HOSTAPD_MODE_IEEE80211AD,
++      HOSTAPD_MODE_IEEE80211ANY,
 +      NUM_HOSTAPD_MODES
 +};
 +
 +/**
 + * enum wpa_ctrl_req_type - Control interface request types
 + */
 +enum wpa_ctrl_req_type {
 +      WPA_CTRL_REQ_UNKNOWN,
 +      WPA_CTRL_REQ_EAP_IDENTITY,
 +      WPA_CTRL_REQ_EAP_PASSWORD,
 +      WPA_CTRL_REQ_EAP_NEW_PASSWORD,
 +      WPA_CTRL_REQ_EAP_PIN,
 +      WPA_CTRL_REQ_EAP_OTP,
 +      WPA_CTRL_REQ_EAP_PASSPHRASE,
 +      WPA_CTRL_REQ_SIM,
++      WPA_CTRL_REQ_PSK_PASSPHRASE,
 +      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,
 +};
 +
++enum set_band {
++      WPA_SETBAND_AUTO,
++      WPA_SETBAND_5G,
++      WPA_SETBAND_2G
++};
++
 +#endif /* DEFS_H */
index e8babb52a9c56dc4175fad7539a3103e57bd8105,0000000000000000000000000000000000000000..9c37ea63ca87825cce8312cc3535460508c69ffe
mode 100644,000000..100644
--- /dev/null
@@@ -1,438 -1,0 +1,455 @@@
-       int allowed[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157,
-                         184, 192 };
 +/*
 + * 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;
-       if (elems.ht_operation &&
-           elems.ht_operation_len >= sizeof(*oper)) {
++      int allowed[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 140,
++                        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 (!mode || !scan_res || !pri_chan || !sec_chan)
-               return 0;
-       if (pri_chan == sec_chan)
++      if (elems.ht_operation) {
 +              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;
 +
- int check_20mhz_bss(struct wpa_scan_res *bss, int pri_freq, int start, int end)
++      if (!mode || !scan_res || !pri_chan || !sec_chan ||
++          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;
 +}
 +
 +
-       if (elems.ht_operation &&
-           elems.ht_operation_len >= sizeof(*oper)) {
++static 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 (!mode || !scan_res || !pri_chan || !sec_chan)
-               return 0;
-       if (pri_chan == sec_chan)
++      if (elems.ht_operation) {
 +              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 (elems.ht_capabilities &&
-                   elems.ht_capabilities_len >=
-                   sizeof(struct ieee80211_ht_capabilities)) {
++      if (!mode || !scan_res || !pri_chan || !sec_chan ||
++          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);
-       int tmp;
++              if (elems.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)
 +{
-               if (center_segment1)
-                       return -1;
-               if (center_segment0 != 0 &&
-                   5000 + center_segment0 * 5 != data->center_freq1 &&
-                   2407 + center_segment0 * 5 != data->center_freq1)
 +      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 (vht_oper_chwidth == 1 && center_segment1)
++              if (center_segment1 ||
++                  (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 == 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;
++              if ((vht_oper_chwidth == 1 && center_segment1) ||
++                  (vht_oper_chwidth == 3 && !center_segment1) ||
++                  !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)
++              if (!center_segment0) {
++                      if (channel <= 48)
++                              center_segment0 = 42;
++                      else if (channel <= 64)
++                              center_segment0 = 58;
++                      else if (channel <= 112)
++                              center_segment0 = 106;
++                      else if (channel <= 128)
++                              center_segment0 = 122;
++                      else if (channel <= 144)
++                              center_segment0 = 138;
++                      else if (channel <= 161)
++                              center_segment0 = 155;
++                      data->center_freq1 = 5000 + center_segment0 * 5;
++              } else {
++                      /*
++                       * Note: HT/VHT config and params are coupled. Check if
++                       * HT40 channel band is in VHT80 Pri channel band
++                       * configuration.
++                       */
++                      if (center_segment0 == channel + 6 ||
++                          center_segment0 == channel + 2 ||
++                          center_segment0 == channel - 2 ||
++                          center_segment0 == channel - 6)
++                              data->center_freq1 = 5000 + center_segment0 * 5;
++                      else
++                              return -1;
++              }
 +              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;
-               data->center_freq1 = 5000 + center_segment0 * 5;
++              /*
++               * Note: HT/VHT config and params are coupled. Check if
++               * HT40 channel band is in VHT160 channel band configuration.
++               */
++              if (center_segment0 == channel + 14 ||
++                  center_segment0 == channel + 10 ||
++                  center_segment0 == channel + 6 ||
++                  center_segment0 == channel + 2 ||
++                  center_segment0 == channel - 2 ||
++                  center_segment0 == channel - 6 ||
++                  center_segment0 == channel - 10 ||
++                  center_segment0 == channel - 14)
++                      data->center_freq1 = 5000 + center_segment0 * 5;
++              else
 +                      return -1;
 +              break;
 +      }
 +
 +      return 0;
 +}
index 7f43d00c5b275d3b3b92b2a78b13424476e084e0,0000000000000000000000000000000000000000..7360b4e3efede5721ff9fcaf453277efd713e8e4
mode 100644,000000..100644
--- /dev/null
@@@ -1,40 -1,0 +1,39 @@@
- int check_20mhz_bss(struct wpa_scan_res *bss, int pri_freq, int start, int end);
 +/*
 + * 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_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 aca0b73223bb8b3ff2b3c41a0de31f761802d26b,0000000000000000000000000000000000000000..d07a316a79298bfd69d0724fa4b734fe0995024f
mode 100644,000000..100644
--- /dev/null
@@@ -1,923 -1,0 +1,1147 @@@
-  * Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi>
 +/*
 + * IEEE 802.11 Common routines
-                       elems->ds_params_len = elen;
++ * 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.
 + */
 +
 +#include "includes.h"
 +
 +#include "common.h"
 +#include "defs.h"
++#include "wpa_common.h"
++#include "qca-vendor.h"
 +#include "ieee802_11_defs.h"
 +#include "ieee802_11_common.h"
 +
 +
 +static int ieee802_11_parse_vendor_specific(const u8 *pos, size_t elen,
 +                                          struct ieee802_11_elems *elems,
 +                                          int show_errors)
 +{
 +      unsigned int oui;
 +
 +      /* first 3 bytes in vendor specific information element are the IEEE
 +       * OUI of the vendor. The following byte is used a vendor specific
 +       * sub-type. */
 +      if (elen < 4) {
 +              if (show_errors) {
 +                      wpa_printf(MSG_MSGDUMP, "short vendor specific "
 +                                 "information element ignored (len=%lu)",
 +                                 (unsigned long) elen);
 +              }
 +              return -1;
 +      }
 +
 +      oui = WPA_GET_BE24(pos);
 +      switch (oui) {
 +      case OUI_MICROSOFT:
 +              /* Microsoft/Wi-Fi information elements are further typed and
 +               * subtyped */
 +              switch (pos[3]) {
 +              case 1:
 +                      /* Microsoft OUI (00:50:F2) with OUI Type 1:
 +                       * real WPA information element */
 +                      elems->wpa_ie = pos;
 +                      elems->wpa_ie_len = elen;
 +                      break;
 +              case WMM_OUI_TYPE:
 +                      /* WMM information element */
 +                      if (elen < 5) {
 +                              wpa_printf(MSG_MSGDUMP, "short WMM "
 +                                         "information element ignored "
 +                                         "(len=%lu)",
 +                                         (unsigned long) elen);
 +                              return -1;
 +                      }
 +                      switch (pos[4]) {
 +                      case WMM_OUI_SUBTYPE_INFORMATION_ELEMENT:
 +                      case WMM_OUI_SUBTYPE_PARAMETER_ELEMENT:
 +                              /*
 +                               * Share same pointer since only one of these
 +                               * is used and they start with same data.
 +                               * Length field can be used to distinguish the
 +                               * IEs.
 +                               */
 +                              elems->wmm = pos;
 +                              elems->wmm_len = elen;
 +                              break;
 +                      case WMM_OUI_SUBTYPE_TSPEC_ELEMENT:
 +                              elems->wmm_tspec = pos;
 +                              elems->wmm_tspec_len = elen;
 +                              break;
 +                      default:
 +                              wpa_printf(MSG_EXCESSIVE, "unknown WMM "
 +                                         "information element ignored "
 +                                         "(subtype=%d len=%lu)",
 +                                         pos[4], (unsigned long) elen);
 +                              return -1;
 +                      }
 +                      break;
 +              case 4:
 +                      /* Wi-Fi Protected Setup (WPS) IE */
 +                      elems->wps_ie = pos;
 +                      elems->wps_ie_len = elen;
 +                      break;
 +              default:
 +                      wpa_printf(MSG_EXCESSIVE, "Unknown Microsoft "
 +                                 "information element ignored "
 +                                 "(type=%d len=%lu)",
 +                                 pos[3], (unsigned long) elen);
 +                      return -1;
 +              }
 +              break;
 +
 +      case OUI_WFA:
 +              switch (pos[3]) {
 +              case P2P_OUI_TYPE:
 +                      /* Wi-Fi Alliance - P2P IE */
 +                      elems->p2p = pos;
 +                      elems->p2p_len = elen;
 +                      break;
 +              case WFD_OUI_TYPE:
 +                      /* Wi-Fi Alliance - WFD IE */
 +                      elems->wfd = pos;
 +                      elems->wfd_len = elen;
 +                      break;
 +              case HS20_INDICATION_OUI_TYPE:
 +                      /* Hotspot 2.0 */
 +                      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)",
 +                                 pos[3], (unsigned long) elen);
 +                      return -1;
 +              }
 +              break;
 +
 +      case OUI_BROADCOM:
 +              switch (pos[3]) {
 +              case VENDOR_HT_CAPAB_OUI_TYPE:
 +                      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 "
 +                                 "(type=%d len=%lu)",
 +                                 pos[3], (unsigned long) elen);
 +                      return -1;
 +              }
 +              break;
 +
++      case OUI_QCA:
++              switch (pos[3]) {
++              case QCA_VENDOR_ELEM_P2P_PREF_CHAN_LIST:
++                      elems->pref_freq_list = pos;
++                      elems->pref_freq_list_len = elen;
++                      break;
++              default:
++                      wpa_printf(MSG_EXCESSIVE,
++                                 "Unknown QCA information element ignored (type=%d len=%lu)",
++                                 pos[3], (unsigned long) elen);
++                      return -1;
++              }
++              break;
++
 +      default:
 +              wpa_printf(MSG_EXCESSIVE, "unknown vendor specific "
 +                         "information element ignored (vendor OUI "
 +                         "%02x:%02x:%02x len=%lu)",
 +                         pos[0], pos[1], pos[2], (unsigned long) elen);
 +              return -1;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +/**
 + * ieee802_11_parse_elems - Parse information elements in management frames
 + * @start: Pointer to the start of IEs
 + * @len: Length of IE buffer in octets
 + * @elems: Data structure for parsed elements
 + * @show_errors: Whether to show parsing errors in debug log
 + * Returns: Parsing result
 + */
 +ParseRes ieee802_11_parse_elems(const u8 *start, size_t len,
 +                              struct ieee802_11_elems *elems,
 +                              int show_errors)
 +{
 +      size_t left = len;
 +      const u8 *pos = start;
 +      int unknown = 0;
 +
 +      os_memset(elems, 0, sizeof(*elems));
 +
 +      while (left >= 2) {
 +              u8 id, elen;
 +
 +              id = *pos++;
 +              elen = *pos++;
 +              left -= 2;
 +
 +              if (elen > left) {
 +                      if (show_errors) {
 +                              wpa_printf(MSG_DEBUG, "IEEE 802.11 element "
 +                                         "parse failed (id=%d elen=%d "
 +                                         "left=%lu)",
 +                                         id, elen, (unsigned long) left);
 +                              wpa_hexdump(MSG_MSGDUMP, "IEs", start, len);
 +                      }
 +                      return ParseFailed;
 +              }
 +
 +              switch (id) {
 +              case WLAN_EID_SSID:
++                      if (elen > SSID_MAX_LEN) {
++                              wpa_printf(MSG_DEBUG,
++                                         "Ignored too long SSID element (elen=%u)",
++                                         elen);
++                              break;
++                      }
 +                      elems->ssid = pos;
 +                      elems->ssid_len = elen;
 +                      break;
 +              case WLAN_EID_SUPP_RATES:
 +                      elems->supp_rates = pos;
 +                      elems->supp_rates_len = elen;
 +                      break;
 +              case WLAN_EID_DS_PARAMS:
++                      if (elen < 1)
++                              break;
 +                      elems->ds_params = pos;
-                       elems->erp_info_len = elen;
 +                      break;
 +              case WLAN_EID_CF_PARAMS:
 +              case WLAN_EID_TIM:
 +                      break;
 +              case WLAN_EID_CHALLENGE:
 +                      elems->challenge = pos;
 +                      elems->challenge_len = elen;
 +                      break;
 +              case WLAN_EID_ERP_INFO:
++                      if (elen < 1)
++                              break;
 +                      elems->erp_info = pos;
-                       elems->timeout_int_len = elen;
 +                      break;
 +              case WLAN_EID_EXT_SUPP_RATES:
 +                      elems->ext_supp_rates = pos;
 +                      elems->ext_supp_rates_len = elen;
 +                      break;
 +              case WLAN_EID_VENDOR_SPECIFIC:
 +                      if (ieee802_11_parse_vendor_specific(pos, elen,
 +                                                           elems,
 +                                                           show_errors))
 +                              unknown++;
 +                      break;
 +              case WLAN_EID_RSN:
 +                      elems->rsn_ie = pos;
 +                      elems->rsn_ie_len = elen;
 +                      break;
 +              case WLAN_EID_PWR_CAPABILITY:
 +                      break;
 +              case WLAN_EID_SUPPORTED_CHANNELS:
 +                      elems->supp_channels = pos;
 +                      elems->supp_channels_len = elen;
 +                      break;
 +              case WLAN_EID_MOBILITY_DOMAIN:
++                      if (elen < sizeof(struct rsn_mdie))
++                              break;
 +                      elems->mdie = pos;
 +                      elems->mdie_len = elen;
 +                      break;
 +              case WLAN_EID_FAST_BSS_TRANSITION:
++                      if (elen < sizeof(struct rsn_ftie))
++                              break;
 +                      elems->ftie = pos;
 +                      elems->ftie_len = elen;
 +                      break;
 +              case WLAN_EID_TIMEOUT_INTERVAL:
++                      if (elen != 5)
++                              break;
 +                      elems->timeout_int = pos;
-                       elems->ht_capabilities_len = elen;
 +                      break;
 +              case WLAN_EID_HT_CAP:
++                      if (elen < sizeof(struct ieee80211_ht_capabilities))
++                              break;
 +                      elems->ht_capabilities = pos;
-                       elems->ht_operation_len = elen;
 +                      break;
 +              case WLAN_EID_HT_OPERATION:
++                      if (elen < sizeof(struct ieee80211_ht_operation))
++                              break;
 +                      elems->ht_operation = pos;
-                       elems->vht_capabilities_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:
++                      if (elen < sizeof(struct ieee80211_vht_capabilities))
++                              break;
 +                      elems->vht_capabilities = pos;
-                       elems->vht_operation_len = elen;
 +                      break;
 +              case WLAN_EID_VHT_OPERATION:
++                      if (elen < sizeof(struct ieee80211_vht_operation))
++                              break;
 +                      elems->vht_operation = pos;
-               if (v < 0 || v > 12) {
 +                      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;
 +                      elems->link_id = pos;
 +                      break;
 +              case WLAN_EID_INTERWORKING:
 +                      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;
 +                      break;
 +              case WLAN_EID_BSS_MAX_IDLE_PERIOD:
 +                      if (elen < 3)
 +                              break;
 +                      elems->bss_max_idle_period = pos;
 +                      break;
 +              case WLAN_EID_SSID_LIST:
 +                      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;
++              case WLAN_EID_MULTI_BAND:
++                      if (elems->mb_ies.nof_ies >= MAX_NOF_MB_IES_SUPPORTED) {
++                              wpa_printf(MSG_MSGDUMP,
++                                         "IEEE 802.11 element parse ignored MB IE (id=%d elen=%d)",
++                                         id, elen);
++                              break;
++                      }
++
++                      elems->mb_ies.ies[elems->mb_ies.nof_ies].ie = pos;
++                      elems->mb_ies.ies[elems->mb_ies.nof_ies].ie_len = elen;
++                      elems->mb_ies.nof_ies++;
++                      break;
 +              default:
 +                      unknown++;
 +                      if (!show_errors)
 +                              break;
 +                      wpa_printf(MSG_MSGDUMP, "IEEE 802.11 element parse "
 +                                 "ignored unknown element (id=%d elen=%d)",
 +                                 id, elen);
 +                      break;
 +              }
 +
 +              left -= elen;
 +              pos += elen;
 +      }
 +
 +      if (left)
 +              return ParseFailed;
 +
 +      return unknown ? ParseUnknown : ParseOK;
 +}
 +
 +
 +int ieee802_11_ie_count(const u8 *ies, size_t ies_len)
 +{
 +      int count = 0;
 +      const u8 *pos, *end;
 +
 +      if (ies == NULL)
 +              return 0;
 +
 +      pos = ies;
 +      end = ies + ies_len;
 +
 +      while (pos + 2 <= end) {
 +              if (pos + 2 + pos[1] > end)
 +                      break;
 +              count++;
 +              pos += 2 + pos[1];
 +      }
 +
 +      return count;
 +}
 +
 +
 +struct wpabuf * ieee802_11_vendor_ie_concat(const u8 *ies, size_t ies_len,
 +                                          u32 oui_type)
 +{
 +      struct wpabuf *buf;
 +      const u8 *end, *pos, *ie;
 +
 +      pos = ies;
 +      end = ies + ies_len;
 +      ie = NULL;
 +
 +      while (pos + 1 < end) {
 +              if (pos + 2 + pos[1] > end)
 +                      return NULL;
 +              if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
 +                  WPA_GET_BE32(&pos[2]) == oui_type) {
 +                      ie = pos;
 +                      break;
 +              }
 +              pos += 2 + pos[1];
 +      }
 +
 +      if (ie == NULL)
 +              return NULL; /* No specified vendor IE found */
 +
 +      buf = wpabuf_alloc(ies_len);
 +      if (buf == NULL)
 +              return NULL;
 +
 +      /*
 +       * There may be multiple vendor IEs in the message, so need to
 +       * concatenate their data fields.
 +       */
 +      while (pos + 1 < end) {
 +              if (pos + 2 + pos[1] > end)
 +                      break;
 +              if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
 +                  WPA_GET_BE32(&pos[2]) == oui_type)
 +                      wpabuf_put_data(buf, pos + 6, pos[1] - 4);
 +              pos += 2 + pos[1];
 +      }
 +
 +      return buf;
 +}
 +
 +
 +const u8 * get_hdr_bssid(const struct ieee80211_hdr *hdr, size_t len)
 +{
 +      u16 fc, type, stype;
 +
 +      /*
 +       * PS-Poll frames are 16 bytes. All other frames are
 +       * 24 bytes or longer.
 +       */
 +      if (len < 16)
 +              return NULL;
 +
 +      fc = le_to_host16(hdr->frame_control);
 +      type = WLAN_FC_GET_TYPE(fc);
 +      stype = WLAN_FC_GET_STYPE(fc);
 +
 +      switch (type) {
 +      case WLAN_FC_TYPE_DATA:
 +              if (len < 24)
 +                      return NULL;
 +              switch (fc & (WLAN_FC_FROMDS | WLAN_FC_TODS)) {
 +              case WLAN_FC_FROMDS | WLAN_FC_TODS:
 +              case WLAN_FC_TODS:
 +                      return hdr->addr1;
 +              case WLAN_FC_FROMDS:
 +                      return hdr->addr2;
 +              default:
 +                      return NULL;
 +              }
 +      case WLAN_FC_TYPE_CTRL:
 +              if (stype != WLAN_FC_STYPE_PSPOLL)
 +                      return NULL;
 +              return hdr->addr1;
 +      case WLAN_FC_TYPE_MGMT:
 +              return hdr->addr3;
 +      default:
 +              return NULL;
 +      }
 +}
 +
 +
 +int hostapd_config_wmm_ac(struct hostapd_wmm_ac_params wmm_ac_params[],
 +                        const char *name, const char *val)
 +{
 +      int num, v;
 +      const char *pos;
 +      struct hostapd_wmm_ac_params *ac;
 +
 +      /* skip 'wme_ac_' or 'wmm_ac_' prefix */
 +      pos = name + 7;
 +      if (os_strncmp(pos, "be_", 3) == 0) {
 +              num = 0;
 +              pos += 3;
 +      } else if (os_strncmp(pos, "bk_", 3) == 0) {
 +              num = 1;
 +              pos += 3;
 +      } else if (os_strncmp(pos, "vi_", 3) == 0) {
 +              num = 2;
 +              pos += 3;
 +      } else if (os_strncmp(pos, "vo_", 3) == 0) {
 +              num = 3;
 +              pos += 3;
 +      } else {
 +              wpa_printf(MSG_ERROR, "Unknown WMM name '%s'", pos);
 +              return -1;
 +      }
 +
 +      ac = &wmm_ac_params[num];
 +
 +      if (os_strcmp(pos, "aifs") == 0) {
 +              v = atoi(val);
 +              if (v < 1 || v > 255) {
 +                      wpa_printf(MSG_ERROR, "Invalid AIFS value %d", v);
 +                      return -1;
 +              }
 +              ac->aifs = v;
 +      } else if (os_strcmp(pos, "cwmin") == 0) {
 +              v = atoi(val);
-               if (v < 0 || v > 12) {
++              if (v < 0 || v > 15) {
 +                      wpa_printf(MSG_ERROR, "Invalid cwMin value %d", v);
 +                      return -1;
 +              }
 +              ac->cwmin = v;
 +      } else if (os_strcmp(pos, "cwmax") == 0) {
 +              v = atoi(val);
-       enum hostapd_hw_mode mode = NUM_HOSTAPD_MODES;
++              if (v < 0 || v > 15) {
 +                      wpa_printf(MSG_ERROR, "Invalid cwMax value %d", v);
 +                      return -1;
 +              }
 +              ac->cwmax = v;
 +      } else if (os_strcmp(pos, "txop_limit") == 0) {
 +              v = atoi(val);
 +              if (v < 0 || v > 0xffff) {
 +                      wpa_printf(MSG_ERROR, "Invalid txop value %d", v);
 +                      return -1;
 +              }
 +              ac->txop_limit = v;
 +      } else if (os_strcmp(pos, "acm") == 0) {
 +              v = atoi(val);
 +              if (v < 0 || v > 1) {
 +                      wpa_printf(MSG_ERROR, "Invalid acm value %d", v);
 +                      return -1;
 +              }
 +              ac->admission_control_mandatory = v;
 +      } else {
 +              wpa_printf(MSG_ERROR, "Unknown wmm_ac_ field '%s'", pos);
 +              return -1;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +enum hostapd_hw_mode ieee80211_freq_to_chan(int freq, u8 *channel)
 +{
-               mode = HOSTAPD_MODE_IEEE80211G;
++      u8 op_class;
++
++      return ieee80211_freq_to_channel_ext(freq, 0, 0, &op_class, channel);
++}
++
++
++/**
++ * ieee80211_freq_to_channel_ext - Convert frequency into channel info
++ * for HT40 and VHT. DFS channels are not covered.
++ * @freq: Frequency (MHz) to convert
++ * @sec_channel: 0 = non-HT40, 1 = sec. channel above, -1 = sec. channel below
++ * @vht: 0 - non-VHT, 1 - 80 MHz
++ * @op_class: Buffer for returning operating class
++ * @channel: Buffer for returning channel number
++ * Returns: hw_mode on success, NUM_HOSTAPD_MODES on failure
++ */
++enum hostapd_hw_mode ieee80211_freq_to_channel_ext(unsigned int freq,
++                                                 int sec_channel, int vht,
++                                                 u8 *op_class, u8 *channel)
++{
++      /* TODO: more operating classes */
++
++      if (sec_channel > 1 || sec_channel < -1)
++              return NUM_HOSTAPD_MODES;
 +
 +      if (freq >= 2412 && freq <= 2472) {
-       } else if (freq == 2484) {
-               mode = HOSTAPD_MODE_IEEE80211B;
++              if ((freq - 2407) % 5)
++                      return NUM_HOSTAPD_MODES;
++
++              if (vht)
++                      return NUM_HOSTAPD_MODES;
++
++              /* 2.407 GHz, channels 1..13 */
++              if (sec_channel == 1)
++                      *op_class = 83;
++              else if (sec_channel == -1)
++                      *op_class = 84;
++              else
++                      *op_class = 81;
++
 +              *channel = (freq - 2407) / 5;
-       } else if (freq >= 4900 && freq < 5000) {
-               mode = HOSTAPD_MODE_IEEE80211A;
++
++              return HOSTAPD_MODE_IEEE80211G;
++      }
++
++      if (freq == 2484) {
++              if (sec_channel || vht)
++                      return NUM_HOSTAPD_MODES;
++
++              *op_class = 82; /* channel 14 */
 +              *channel = 14;
-       } else if (freq >= 5000 && freq < 5900) {
-               mode = HOSTAPD_MODE_IEEE80211A;
++
++              return HOSTAPD_MODE_IEEE80211B;
++      }
++
++      if (freq >= 4900 && freq < 5000) {
++              if ((freq - 4000) % 5)
++                      return NUM_HOSTAPD_MODES;
 +              *channel = (freq - 4000) / 5;
-       } else if (freq >= 56160 + 2160 * 1 && freq <= 56160 + 2160 * 4) {
-               mode = HOSTAPD_MODE_IEEE80211AD;
++              *op_class = 0; /* TODO */
++              return HOSTAPD_MODE_IEEE80211A;
++      }
++
++      /* 5 GHz, channels 36..48 */
++      if (freq >= 5180 && freq <= 5240) {
++              if ((freq - 5000) % 5)
++                      return NUM_HOSTAPD_MODES;
++
++              if (sec_channel == 1)
++                      *op_class = 116;
++              else if (sec_channel == -1)
++                      *op_class = 117;
++              else if (vht)
++                      *op_class = 128;
++              else
++                      *op_class = 115;
++
++              *channel = (freq - 5000) / 5;
++
++              return HOSTAPD_MODE_IEEE80211A;
++      }
++
++      /* 5 GHz, channels 149..161 */
++      if (freq >= 5745 && freq <= 5805) {
++              if ((freq - 5000) % 5)
++                      return NUM_HOSTAPD_MODES;
++
++              if (sec_channel == 1)
++                      *op_class = 126;
++              else if (sec_channel == -1)
++                      *op_class = 127;
++              else if (vht)
++                      *op_class = 128;
++              else
++                      *op_class = 124;
++
 +              *channel = (freq - 5000) / 5;
-       return mode;
++
++              return HOSTAPD_MODE_IEEE80211A;
++      }
++
++      /* 5 GHz, channels 149..169 */
++      if (freq >= 5745 && freq <= 5845) {
++              if ((freq - 5000) % 5)
++                      return NUM_HOSTAPD_MODES;
++
++              *op_class = 125;
++
++              *channel = (freq - 5000) / 5;
++
++              return HOSTAPD_MODE_IEEE80211A;
++      }
++
++      if (freq >= 5000 && freq < 5900) {
++              if ((freq - 5000) % 5)
++                      return NUM_HOSTAPD_MODES;
++              *channel = (freq - 5000) / 5;
++              *op_class = 0; /* TODO */
++              return HOSTAPD_MODE_IEEE80211A;
++      }
++
++      /* 56.16 GHz, channel 1..4 */
++      if (freq >= 56160 + 2160 * 1 && freq <= 56160 + 2160 * 4) {
++              if (sec_channel || vht)
++                      return NUM_HOSTAPD_MODES;
++
 +              *channel = (freq - 56160) / 2160;
++              *op_class = 180;
++
++              return HOSTAPD_MODE_IEEE80211AD;
 +      }
 +
- static const char *us_op_class_cc[] = {
++      return NUM_HOSTAPD_MODES;
 +}
 +
 +
- static const char *eu_op_class_cc[] = {
++static const char *const us_op_class_cc[] = {
 +      "US", "CA", NULL
 +};
 +
- static const char *jp_op_class_cc[] = {
++static const char *const 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 *cn_op_class_cc[] = {
-       "CN", "CA", NULL
++static const char *const jp_op_class_cc[] = {
 +      "JP", NULL
 +};
 +
- static int country_match(const char *cc[], const char *country)
++static const char *const cn_op_class_cc[] = {
++      "CN", NULL
 +};
 +
 +
-       case 125: /* channels 149,153,157,161,165,169 */
++static int country_match(const char *const cc[], const char *const 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 5: /* channels 149,153,157,161,165 */
++              if (chan < 149 || chan > 165)
++                      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 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 125: /* channels 149,153,157,161,165,169 */
++              if (chan < 149 || chan > 169)
++                      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;
 +}
 +
 +
 +int supp_rates_11b_only(struct ieee802_11_elems *elems)
 +{
 +      int num_11b = 0, num_others = 0;
 +      int i;
 +
 +      if (elems->supp_rates == NULL && elems->ext_supp_rates == NULL)
 +              return 0;
 +
 +      for (i = 0; elems->supp_rates && i < elems->supp_rates_len; i++) {
 +              if (is_11b(elems->supp_rates[i]))
 +                      num_11b++;
 +              else
 +                      num_others++;
 +      }
 +
 +      for (i = 0; elems->ext_supp_rates && i < elems->ext_supp_rates_len;
 +           i++) {
 +              if (is_11b(elems->ext_supp_rates[i]))
 +                      num_11b++;
 +              else
 +                      num_others++;
 +      }
 +
 +      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
 +}
++
++
++int mb_ies_info_by_ies(struct mb_ies_info *info, const u8 *ies_buf,
++                     size_t ies_len)
++{
++      os_memset(info, 0, sizeof(*info));
++
++      while (ies_buf && ies_len >= 2 &&
++             info->nof_ies < MAX_NOF_MB_IES_SUPPORTED) {
++              size_t len = 2 + ies_buf[1];
++
++              if (len > ies_len) {
++                      wpa_hexdump(MSG_DEBUG, "Truncated IEs",
++                                  ies_buf, ies_len);
++                      return -1;
++              }
++
++              if (ies_buf[0] == WLAN_EID_MULTI_BAND) {
++                      wpa_printf(MSG_DEBUG, "MB IE of %zu bytes found", len);
++                      info->ies[info->nof_ies].ie = ies_buf + 2;
++                      info->ies[info->nof_ies].ie_len = ies_buf[1];
++                      info->nof_ies++;
++              }
++
++              ies_len -= len;
++              ies_buf += len;
++      }
++
++      return 0;
++}
++
++
++struct wpabuf * mb_ies_by_info(struct mb_ies_info *info)
++{
++      struct wpabuf *mb_ies = NULL;
++
++      WPA_ASSERT(info != NULL);
++
++      if (info->nof_ies) {
++              u8 i;
++              size_t mb_ies_size = 0;
++
++              for (i = 0; i < info->nof_ies; i++)
++                      mb_ies_size += 2 + info->ies[i].ie_len;
++
++              mb_ies = wpabuf_alloc(mb_ies_size);
++              if (mb_ies) {
++                      for (i = 0; i < info->nof_ies; i++) {
++                              wpabuf_put_u8(mb_ies, WLAN_EID_MULTI_BAND);
++                              wpabuf_put_u8(mb_ies, info->ies[i].ie_len);
++                              wpabuf_put_data(mb_ies,
++                                              info->ies[i].ie,
++                                              info->ies[i].ie_len);
++                      }
++              }
++      }
++
++      return mb_ies;
++}
index 7f0b296d2b00a340863c3afcb568cb47dc8edb76,0000000000000000000000000000000000000000..55ce0223d923530f4b35b5294688e62bc27c0fad
mode 100644,000000..100644
--- /dev/null
@@@ -1,116 -1,0 +1,128 @@@
-       u8 ds_params_len;
 +/*
 + * IEEE 802.11 Common routines
 + * Copyright (c) 2002-2012, Jouni Malinen <j@w1.fi>
 + *
 + * This software may be distributed under the terms of the BSD license.
 + * See README for more details.
 + */
 +
 +#ifndef IEEE802_11_COMMON_H
 +#define IEEE802_11_COMMON_H
 +
++#define MAX_NOF_MB_IES_SUPPORTED 5
++
++struct mb_ies_info {
++      struct {
++              const u8 *ie;
++              u8 ie_len;
++      } ies[MAX_NOF_MB_IES_SUPPORTED];
++      u8 nof_ies;
++};
++
 +/* Parsed Information Elements */
 +struct ieee802_11_elems {
 +      const u8 *ssid;
 +      const u8 *supp_rates;
 +      const u8 *ds_params;
 +      const u8 *challenge;
 +      const u8 *erp_info;
 +      const u8 *ext_supp_rates;
 +      const u8 *wpa_ie;
 +      const u8 *rsn_ie;
 +      const u8 *wmm; /* WMM Information or Parameter Element */
 +      const u8 *wmm_tspec;
 +      const u8 *wps_ie;
 +      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;
++      const u8 *pref_freq_list;
 +
 +      u8 ssid_len;
 +      u8 supp_rates_len;
-       u8 erp_info_len;
 +      u8 challenge_len;
-       u8 timeout_int_len;
-       u8 ht_capabilities_len;
-       u8 ht_operation_len;
 +      u8 ext_supp_rates_len;
 +      u8 wpa_ie_len;
 +      u8 rsn_ie_len;
 +      u8 wmm_len; /* 7 = WMM Information; 24 = WMM Parameter */
 +      u8 wmm_tspec_len;
 +      u8 wps_ie_len;
 +      u8 supp_channels_len;
 +      u8 mdie_len;
 +      u8 ftie_len;
-       u8 vht_capabilities_len;
-       u8 vht_operation_len;
 +      u8 mesh_config_len;
 +      u8 mesh_id_len;
 +      u8 peer_mgmt_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;
++      u8 pref_freq_list_len;
++      struct mb_ies_info mb_ies;
 +};
 +
 +typedef enum { ParseOK = 0, ParseUnknown = 1, ParseFailed = -1 } ParseRes;
 +
 +ParseRes ieee802_11_parse_elems(const u8 *start, size_t len,
 +                              struct ieee802_11_elems *elems,
 +                              int show_errors);
 +int ieee802_11_ie_count(const u8 *ies, size_t ies_len);
 +struct wpabuf * ieee802_11_vendor_ie_concat(const u8 *ies, size_t ies_len,
 +                                          u32 oui_type);
 +struct ieee80211_hdr;
 +const u8 * get_hdr_bssid(const struct ieee80211_hdr *hdr, size_t len);
 +
 +struct hostapd_wmm_ac_params {
 +      int cwmin;
 +      int cwmax;
 +      int aifs;
 +      int txop_limit; /* in units of 32us */
 +      int admission_control_mandatory;
 +};
 +
 +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);
++enum hostapd_hw_mode ieee80211_freq_to_channel_ext(unsigned int freq,
++                                                 int sec_channel, int vht,
++                                                 u8 *op_class, u8 *channel);
 +int ieee80211_is_dfs(int freq);
 +
 +int supp_rates_11b_only(struct ieee802_11_elems *elems);
++int mb_ies_info_by_ies(struct mb_ies_info *info, const u8 *ies_buf,
++                     size_t ies_len);
++struct wpabuf * mb_ies_by_info(struct mb_ies_info *info);
 +
 +const char * fc2str(u16 fc);
 +#endif /* IEEE802_11_COMMON_H */
index 2e51935b8e3bdc9ef23c6f158d0a6c082b9cab02,0000000000000000000000000000000000000000..44530ce3cee6ada5fc2c3b4fb3ffbc25b0ccbc48
mode 100644,000000..100644
--- /dev/null
@@@ -1,1357 -1,0 +1,1436 @@@
-                       u8 variable[0];
 +/*
 + * IEEE 802.11 Frame type definitions
 + * 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.
 + * See README for more details.
 + */
 +
 +#ifndef IEEE802_11_DEFS_H
 +#define IEEE802_11_DEFS_H
 +
++#include <utils/common.h>
++
 +/* IEEE 802.11 defines */
 +
 +#define WLAN_FC_PVER          0x0003
 +#define WLAN_FC_TODS          0x0100
 +#define WLAN_FC_FROMDS                0x0200
 +#define WLAN_FC_MOREFRAG      0x0400
 +#define WLAN_FC_RETRY         0x0800
 +#define WLAN_FC_PWRMGT                0x1000
 +#define WLAN_FC_MOREDATA      0x2000
 +#define WLAN_FC_ISWEP         0x4000
 +#define WLAN_FC_ORDER         0x8000
 +
 +#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_FC_TYPE_MGMT             0
 +#define WLAN_FC_TYPE_CTRL             1
 +#define WLAN_FC_TYPE_DATA             2
 +
 +/* management */
 +#define WLAN_FC_STYPE_ASSOC_REQ               0
 +#define WLAN_FC_STYPE_ASSOC_RESP      1
 +#define WLAN_FC_STYPE_REASSOC_REQ     2
 +#define WLAN_FC_STYPE_REASSOC_RESP    3
 +#define WLAN_FC_STYPE_PROBE_REQ               4
 +#define WLAN_FC_STYPE_PROBE_RESP      5
 +#define WLAN_FC_STYPE_BEACON          8
 +#define WLAN_FC_STYPE_ATIM            9
 +#define WLAN_FC_STYPE_DISASSOC                10
 +#define WLAN_FC_STYPE_AUTH            11
 +#define WLAN_FC_STYPE_DEAUTH          12
 +#define WLAN_FC_STYPE_ACTION          13
 +
 +/* control */
 +#define WLAN_FC_STYPE_PSPOLL          10
 +#define WLAN_FC_STYPE_RTS             11
 +#define WLAN_FC_STYPE_CTS             12
 +#define WLAN_FC_STYPE_ACK             13
 +#define WLAN_FC_STYPE_CFEND           14
 +#define WLAN_FC_STYPE_CFENDACK                15
 +
 +/* data */
 +#define WLAN_FC_STYPE_DATA            0
 +#define WLAN_FC_STYPE_DATA_CFACK      1
 +#define WLAN_FC_STYPE_DATA_CFPOLL     2
 +#define WLAN_FC_STYPE_DATA_CFACKPOLL  3
 +#define WLAN_FC_STYPE_NULLFUNC                4
 +#define WLAN_FC_STYPE_CFACK           5
 +#define WLAN_FC_STYPE_CFPOLL          6
 +#define WLAN_FC_STYPE_CFACKPOLL               7
 +#define WLAN_FC_STYPE_QOS_DATA                8
 +#define WLAN_FC_STYPE_QOS_DATA_CFACK  9
 +#define WLAN_FC_STYPE_QOS_DATA_CFPOLL 10
 +#define WLAN_FC_STYPE_QOS_DATA_CFACKPOLL      11
 +#define WLAN_FC_STYPE_QOS_NULL                12
 +#define WLAN_FC_STYPE_QOS_CFPOLL      14
 +#define WLAN_FC_STYPE_QOS_CFACKPOLL   15
 +
 +/* Authentication algorithms */
 +#define WLAN_AUTH_OPEN                        0
 +#define WLAN_AUTH_SHARED_KEY          1
 +#define WLAN_AUTH_FT                  2
 +#define WLAN_AUTH_SAE                 3
 +#define WLAN_AUTH_LEAP                        128
 +
 +#define WLAN_AUTH_CHALLENGE_LEN 128
 +
 +#define WLAN_CAPABILITY_ESS BIT(0)
 +#define WLAN_CAPABILITY_IBSS BIT(1)
 +#define WLAN_CAPABILITY_CF_POLLABLE BIT(2)
 +#define WLAN_CAPABILITY_CF_POLL_REQUEST BIT(3)
 +#define WLAN_CAPABILITY_PRIVACY BIT(4)
 +#define WLAN_CAPABILITY_SHORT_PREAMBLE BIT(5)
 +#define WLAN_CAPABILITY_PBCC BIT(6)
 +#define WLAN_CAPABILITY_CHANNEL_AGILITY BIT(7)
 +#define WLAN_CAPABILITY_SPECTRUM_MGMT BIT(8)
 +#define WLAN_CAPABILITY_SHORT_SLOT_TIME BIT(10)
 +#define WLAN_CAPABILITY_DSSS_OFDM BIT(13)
 +
 +/* Status codes (IEEE 802.11-2007, 7.3.1.9, Table 7-23) */
 +#define WLAN_STATUS_SUCCESS 0
 +#define WLAN_STATUS_UNSPECIFIED_FAILURE 1
 +#define WLAN_STATUS_TDLS_WAKEUP_ALTERNATE 2
 +#define WLAN_STATUS_TDLS_WAKEUP_REJECT 3
 +#define WLAN_STATUS_SECURITY_DISABLED 5
 +#define WLAN_STATUS_UNACCEPTABLE_LIFETIME 6
 +#define WLAN_STATUS_NOT_IN_SAME_BSS 7
 +#define WLAN_STATUS_CAPS_UNSUPPORTED 10
 +#define WLAN_STATUS_REASSOC_NO_ASSOC 11
 +#define WLAN_STATUS_ASSOC_DENIED_UNSPEC 12
 +#define WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG 13
 +#define WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION 14
 +#define WLAN_STATUS_CHALLENGE_FAIL 15
 +#define WLAN_STATUS_AUTH_TIMEOUT 16
 +#define WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA 17
 +#define WLAN_STATUS_ASSOC_DENIED_RATES 18
 +/* IEEE 802.11b */
 +#define WLAN_STATUS_ASSOC_DENIED_NOSHORT 19
 +#define WLAN_STATUS_ASSOC_DENIED_NOPBCC 20
 +#define WLAN_STATUS_ASSOC_DENIED_NOAGILITY 21
 +/* IEEE 802.11h */
 +#define WLAN_STATUS_SPEC_MGMT_REQUIRED 22
 +#define WLAN_STATUS_PWR_CAPABILITY_NOT_VALID 23
 +#define WLAN_STATUS_SUPPORTED_CHANNEL_NOT_VALID 24
 +/* IEEE 802.11g */
 +#define WLAN_STATUS_ASSOC_DENIED_NO_SHORT_SLOT_TIME 25
 +#define WLAN_STATUS_ASSOC_DENIED_NO_DSSS_OFDM 26
 +#define WLAN_STATUS_ASSOC_DENIED_NO_HT 27
 +#define WLAN_STATUS_R0KH_UNREACHABLE 28
 +#define WLAN_STATUS_ASSOC_DENIED_NO_PCO 29
 +/* IEEE 802.11w */
 +#define WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY 30
 +#define WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION 31
 +#define WLAN_STATUS_UNSPECIFIED_QOS_FAILURE 32
 +#define WLAN_STATUS_REQUEST_DECLINED 37
 +#define WLAN_STATUS_INVALID_PARAMETERS 38
 +/* IEEE 802.11i */
 +#define WLAN_STATUS_INVALID_IE 40
 +#define WLAN_STATUS_GROUP_CIPHER_NOT_VALID 41
 +#define WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID 42
 +#define WLAN_STATUS_AKMP_NOT_VALID 43
 +#define WLAN_STATUS_UNSUPPORTED_RSN_IE_VERSION 44
 +#define WLAN_STATUS_INVALID_RSN_IE_CAPAB 45
 +#define WLAN_STATUS_CIPHER_REJECTED_PER_POLICY 46
 +#define WLAN_STATUS_TS_NOT_CREATED 47
 +#define WLAN_STATUS_DIRECT_LINK_NOT_ALLOWED 48
 +#define WLAN_STATUS_DEST_STA_NOT_PRESENT 49
 +#define WLAN_STATUS_DEST_STA_NOT_QOS_STA 50
 +#define WLAN_STATUS_ASSOC_DENIED_LISTEN_INT_TOO_LARGE 51
 +/* IEEE 802.11r */
 +#define WLAN_STATUS_INVALID_FT_ACTION_FRAME_COUNT 52
 +#define WLAN_STATUS_INVALID_PMKID 53
 +#define WLAN_STATUS_INVALID_MDIE 54
 +#define WLAN_STATUS_INVALID_FTIE 55
 +#define WLAN_STATUS_GAS_ADV_PROTO_NOT_SUPPORTED 59
 +#define WLAN_STATUS_NO_OUTSTANDING_GAS_REQ 60
 +#define WLAN_STATUS_GAS_RESP_NOT_RECEIVED 61
 +#define WLAN_STATUS_STA_TIMED_OUT_WAITING_FOR_GAS_RESP 62
 +#define WLAN_STATUS_GAS_RESP_LARGER_THAN_LIMIT 63
 +#define WLAN_STATUS_REQ_REFUSED_HOME 64
 +#define WLAN_STATUS_ADV_SRV_UNREACHABLE 65
 +#define WLAN_STATUS_REQ_REFUSED_SSPN 67
 +#define WLAN_STATUS_REQ_REFUSED_UNAUTH_ACCESS 68
 +#define WLAN_STATUS_INVALID_RSNIE 72
 +#define WLAN_STATUS_ANTI_CLOGGING_TOKEN_REQ 76
 +#define WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED 77
 +#define WLAN_STATUS_TRANSMISSION_FAILURE 79
++#define WLAN_STATUS_REJECTED_WITH_SUGGESTED_BSS_TRANSITION 82
++#define WLAN_STATUS_PENDING_ADMITTING_FST_SESSION 86
 +#define WLAN_STATUS_QUERY_RESP_OUTSTANDING 95
++#define WLAN_STATUS_DENIED_WITH_SUGGESTED_BAND_AND_CHANNEL 99
 +#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_PREV_AUTH_NOT_VALID 2
 +#define WLAN_REASON_DEAUTH_LEAVING 3
 +#define WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY 4
 +#define WLAN_REASON_DISASSOC_AP_BUSY 5
 +#define WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA 6
 +#define WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA 7
 +#define WLAN_REASON_DISASSOC_STA_HAS_LEFT 8
 +#define WLAN_REASON_STA_REQ_ASSOC_WITHOUT_AUTH 9
 +/* IEEE 802.11h */
 +#define WLAN_REASON_PWR_CAPABILITY_NOT_VALID 10
 +#define WLAN_REASON_SUPPORTED_CHANNEL_NOT_VALID 11
 +/* IEEE 802.11i */
 +#define WLAN_REASON_INVALID_IE 13
 +#define WLAN_REASON_MICHAEL_MIC_FAILURE 14
 +#define WLAN_REASON_4WAY_HANDSHAKE_TIMEOUT 15
 +#define WLAN_REASON_GROUP_KEY_UPDATE_TIMEOUT 16
 +#define WLAN_REASON_IE_IN_4WAY_DIFFERS 17
 +#define WLAN_REASON_GROUP_CIPHER_NOT_VALID 18
 +#define WLAN_REASON_PAIRWISE_CIPHER_NOT_VALID 19
 +#define WLAN_REASON_AKMP_NOT_VALID 20
 +#define WLAN_REASON_UNSUPPORTED_RSN_IE_VERSION 21
 +#define WLAN_REASON_INVALID_RSN_IE_CAPAB 22
 +#define WLAN_REASON_IEEE_802_1X_AUTH_FAILED 23
 +#define WLAN_REASON_CIPHER_SUITE_REJECTED 24
 +#define WLAN_REASON_TDLS_TEARDOWN_UNREACHABLE 25
 +#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_SSID 0
 +#define WLAN_EID_SUPP_RATES 1
 +#define WLAN_EID_FH_PARAMS 2
 +#define WLAN_EID_DS_PARAMS 3
 +#define WLAN_EID_CF_PARAMS 4
 +#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_PWR_CAPABILITY 33
 +#define WLAN_EID_TPC_REQUEST 34
 +#define WLAN_EID_TPC_REPORT 35
 +#define WLAN_EID_SUPPORTED_CHANNELS 36
 +#define WLAN_EID_CHANNEL_SWITCH 37
 +#define WLAN_EID_MEASURE_REQUEST 38
 +#define WLAN_EID_MEASURE_REPORT 39
 +#define WLAN_EID_QUITE 40
 +#define WLAN_EID_IBSS_DFS 41
 +/* EIDs defined by IEEE 802.11h - END */
 +#define WLAN_EID_ERP_INFO 42
 +#define WLAN_EID_HT_CAP 45
 +#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_MMIE 76
 +#define WLAN_EID_SSID_LIST 84
 +#define WLAN_EID_BSS_MAX_IDLE_PERIOD 90
 +#define WLAN_EID_TFS_REQ 91
 +#define WLAN_EID_TFS_RESP 92
 +#define WLAN_EID_WNMSLEEP 93
 +#define WLAN_EID_TIME_ZONE 98
 +#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_MULTI_BAND 158
++#define WLAN_EID_SESSION_TRANSITION 164
 +#define WLAN_EID_VHT_CAP 191
 +#define WLAN_EID_VHT_OPERATION 192
 +#define WLAN_EID_VHT_EXTENDED_BSS_LOAD 193
 +#define WLAN_EID_VHT_WIDE_BW_CHSWITCH  194
 +#define WLAN_EID_VHT_TRANSMIT_POWER_ENVELOPE 195
 +#define WLAN_EID_VHT_CHANNEL_SWITCH_WRAPPER 196
 +#define WLAN_EID_VHT_AID 197
 +#define WLAN_EID_VHT_QUIET_CHANNEL 198
 +#define WLAN_EID_VHT_OPERATING_MODE_NOTIFICATION 199
 +#define WLAN_EID_VENDOR_SPECIFIC 221
 +
 +
 +/* Action frame categories (IEEE 802.11-2007, 7.3.1.11, Table 7-24) */
 +#define WLAN_ACTION_SPECTRUM_MGMT 0
 +#define WLAN_ACTION_QOS 1
 +#define WLAN_ACTION_DLS 2
 +#define WLAN_ACTION_BLOCK_ACK 3
 +#define WLAN_ACTION_PUBLIC 4
 +#define WLAN_ACTION_RADIO_MEASUREMENT 5
 +#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_FST 18
 +#define WLAN_ACTION_VENDOR_SPECIFIC 127
 +
 +/* Public action codes */
 +#define WLAN_PA_20_40_BSS_COEX 0
 +#define WLAN_PA_VENDOR_SPECIFIC 9
 +#define WLAN_PA_GAS_INITIAL_REQ 10
 +#define WLAN_PA_GAS_INITIAL_RESP 11
 +#define WLAN_PA_GAS_COMEBACK_REQ 12
 +#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_SA_QUERY_TR_ID_LEN 2
 +
 +/* TDLS action codes */
 +#define WLAN_TDLS_SETUP_REQUEST 0
 +#define WLAN_TDLS_SETUP_RESPONSE 1
 +#define WLAN_TDLS_SETUP_CONFIRM 2
 +#define WLAN_TDLS_TEARDOWN 3
 +#define WLAN_TDLS_PEER_TRAFFIC_INDICATION 4
 +#define WLAN_TDLS_CHANNEL_SWITCH_REQUEST 5
 +#define WLAN_TDLS_CHANNEL_SWITCH_RESPONSE 6
 +#define WLAN_TDLS_PEER_PSM_REQUEST 7
 +#define WLAN_TDLS_PEER_PSM_RESPONSE 8
 +#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
 +#define WLAN_TIMEOUT_ASSOC_COMEBACK 3
 +
 +/* Interworking element (IEEE 802.11u) - Access Network Options */
 +#define INTERWORKING_ANO_ACCESS_NETWORK_MASK 0x0f
 +#define INTERWORKING_ANO_INTERNET 0x10
 +#define INTERWORKING_ANO_ASRA 0x20
 +#define INTERWORKING_ANO_ESR 0x40
 +#define INTERWORKING_ANO_UESA 0x80
 +
 +#define INTERWORKING_ANT_PRIVATE 0
 +#define INTERWORKING_ANT_PRIVATE_WITH_GUEST 1
 +#define INTERWORKING_ANT_CHARGEABLE_PUBLIC 2
 +#define INTERWORKING_ANT_FREE_PUBLIC 3
 +#define INTERWORKING_ANT_PERSONAL_DEVICE 4
 +#define INTERWORKING_ANT_EMERGENCY_SERVICES 5
 +#define INTERWORKING_ANT_TEST 6
 +#define INTERWORKING_ANT_WILDCARD 15
 +
 +/* Advertisement Protocol ID definitions (IEEE Std 802.11u-2011) */
 +enum adv_proto_id {
 +      ACCESS_NETWORK_QUERY_PROTOCOL = 0,
 +      MIH_INFO_SERVICE = 1,
 +      MIH_CMD_AND_EVENT_DISCOVERY = 2,
 +      EMERGENCY_ALERT_SYSTEM = 3,
 +      ADV_PROTO_VENDOR_SPECIFIC = 221
 +};
 +
 +/* Access Network Query Protocol info ID definitions (IEEE Std 802.11u-2011) */
 +enum anqp_info_id {
 +      ANQP_QUERY_LIST = 256,
 +      ANQP_CAPABILITY_LIST = 257,
 +      ANQP_VENUE_NAME = 258,
 +      ANQP_EMERGENCY_CALL_NUMBER = 259,
 +      ANQP_NETWORK_AUTH_TYPE = 260,
 +      ANQP_ROAMING_CONSORTIUM = 261,
 +      ANQP_IP_ADDR_TYPE_AVAILABILITY = 262,
 +      ANQP_NAI_REALM = 263,
 +      ANQP_3GPP_CELLULAR_NETWORK = 264,
 +      ANQP_AP_GEOSPATIAL_LOCATION = 265,
 +      ANQP_AP_CIVIC_LOCATION = 266,
 +      ANQP_AP_LOCATION_PUBLIC_URI = 267,
 +      ANQP_DOMAIN_NAME = 268,
 +      ANQP_EMERGENCY_ALERT_URI = 269,
 +      ANQP_EMERGENCY_NAI = 271,
 +      ANQP_VENDOR_SPECIFIC = 56797
 +};
 +
 +/* NAI Realm list - EAP Method subfield - Authentication Parameter ID */
 +enum nai_realm_eap_auth_param {
 +      NAI_REALM_EAP_AUTH_EXPANDED_EAP_METHOD = 1,
 +      NAI_REALM_EAP_AUTH_NON_EAP_INNER_AUTH = 2,
 +      NAI_REALM_EAP_AUTH_INNER_AUTH_EAP_METHOD = 3,
 +      NAI_REALM_EAP_AUTH_EXPANDED_INNER_EAP_METHOD = 4,
 +      NAI_REALM_EAP_AUTH_CRED_TYPE = 5,
 +      NAI_REALM_EAP_AUTH_TUNNELED_CRED_TYPE = 6,
 +      NAI_REALM_EAP_AUTH_VENDOR_SPECIFIC = 221
 +};
 +
 +enum nai_realm_eap_auth_inner_non_eap {
 +      NAI_REALM_INNER_NON_EAP_PAP = 1,
 +      NAI_REALM_INNER_NON_EAP_CHAP = 2,
 +      NAI_REALM_INNER_NON_EAP_MSCHAP = 3,
 +      NAI_REALM_INNER_NON_EAP_MSCHAPV2 = 4
 +};
 +
 +enum nai_realm_eap_cred_type {
 +      NAI_REALM_CRED_TYPE_SIM = 1,
 +      NAI_REALM_CRED_TYPE_USIM = 2,
 +      NAI_REALM_CRED_TYPE_NFC_SECURE_ELEMENT = 3,
 +      NAI_REALM_CRED_TYPE_HARDWARE_TOKEN = 4,
 +      NAI_REALM_CRED_TYPE_SOFTOKEN = 5,
 +      NAI_REALM_CRED_TYPE_CERTIFICATE = 6,
 +      NAI_REALM_CRED_TYPE_USERNAME_PASSWORD = 7,
 +      NAI_REALM_CRED_TYPE_NONE = 8,
 +      NAI_REALM_CRED_TYPE_ANONYMOUS = 9,
 +      NAI_REALM_CRED_TYPE_VENDOR_SPECIFIC = 10
 +};
 +
 +#ifdef _MSC_VER
 +#pragma pack(push, 1)
 +#endif /* _MSC_VER */
 +
 +struct ieee80211_hdr {
 +      le16 frame_control;
 +      le16 duration_id;
 +      u8 addr1[6];
 +      u8 addr2[6];
 +      u8 addr3[6];
 +      le16 seq_ctrl;
 +      /* followed by 'u8 addr4[6];' if ToDS and FromDS is set in data frame
 +       */
 +} STRUCT_PACKED;
 +
 +#define IEEE80211_DA_FROMDS addr1
 +#define IEEE80211_BSSID_FROMDS addr2
 +#define IEEE80211_SA_FROMDS addr3
 +
 +#define IEEE80211_HDRLEN (sizeof(struct ieee80211_hdr))
 +
 +#define IEEE80211_FC(type, stype) host_to_le16((type << 2) | (stype << 4))
 +
 +struct ieee80211_mgmt {
 +      le16 frame_control;
 +      le16 duration;
 +      u8 da[6];
 +      u8 sa[6];
 +      u8 bssid[6];
 +      le16 seq_ctrl;
 +      union {
 +              struct {
 +                      le16 auth_alg;
 +                      le16 auth_transaction;
 +                      le16 status_code;
 +                      /* possibly followed by Challenge text */
-                       u8 variable[0];
++                      u8 variable[];
 +              } STRUCT_PACKED auth;
 +              struct {
 +                      le16 reason_code;
-                       u8 variable[0];
++                      u8 variable[];
 +              } STRUCT_PACKED deauth;
 +              struct {
 +                      le16 capab_info;
 +                      le16 listen_interval;
 +                      /* followed by SSID and Supported rates */
-                       u8 variable[0];
++                      u8 variable[];
 +              } STRUCT_PACKED assoc_req;
 +              struct {
 +                      le16 capab_info;
 +                      le16 status_code;
 +                      le16 aid;
 +                      /* followed by Supported rates */
-                       u8 variable[0];
++                      u8 variable[];
 +              } STRUCT_PACKED assoc_resp, reassoc_resp;
 +              struct {
 +                      le16 capab_info;
 +                      le16 listen_interval;
 +                      u8 current_ap[6];
 +                      /* followed by SSID and Supported rates */
-                       u8 variable[0];
++                      u8 variable[];
 +              } STRUCT_PACKED reassoc_req;
 +              struct {
 +                      le16 reason_code;
-                       u8 variable[0];
++                      u8 variable[];
 +              } STRUCT_PACKED disassoc;
 +              struct {
 +                      u8 timestamp[8];
 +                      le16 beacon_int;
 +                      le16 capab_info;
 +                      /* followed by some of SSID, Supported rates,
 +                       * FH Params, DS Params, CF Params, IBSS Params, TIM */
-                       u8 variable[0];
++                      u8 variable[];
 +              } STRUCT_PACKED beacon;
 +              struct {
 +                      /* only variable items: SSID, Supported rates */
 +                      u8 variable[0];
 +              } STRUCT_PACKED probe_req;
 +              struct {
 +                      u8 timestamp[8];
 +                      le16 beacon_int;
 +                      le16 capab_info;
 +                      /* followed by some of SSID, Supported rates,
 +                       * FH Params, DS Params, CF Params, IBSS Params */
-                                       u8 variable[0];
++                      u8 variable[];
 +              } STRUCT_PACKED probe_resp;
 +              struct {
 +                      u8 category;
 +                      union {
 +                              struct {
 +                                      u8 action_code;
 +                                      u8 dialog_token;
 +                                      u8 status_code;
-                                       u8 variable[0]; /* FT Request */
++                                      u8 variable[];
 +                              } STRUCT_PACKED wmm_action;
 +                              struct{
 +                                      u8 action_code;
 +                                      u8 element_id;
 +                                      u8 length;
 +                                      u8 switch_mode;
 +                                      u8 new_chan;
 +                                      u8 switch_count;
 +                              } STRUCT_PACKED chan_switch;
 +                              struct {
 +                                      u8 action;
 +                                      u8 sta_addr[ETH_ALEN];
 +                                      u8 target_ap_addr[ETH_ALEN];
-                                       u8 variable[0]; /* FT Request */
++                                      u8 variable[]; /* FT Request */
 +                              } STRUCT_PACKED ft_action_req;
 +                              struct {
 +                                      u8 action;
 +                                      u8 sta_addr[ETH_ALEN];
 +                                      u8 target_ap_addr[ETH_ALEN];
 +                                      le16 status_code;
-                                       u8 variable[0];
++                                      u8 variable[]; /* FT Request */
 +                              } STRUCT_PACKED ft_action_resp;
 +                              struct {
 +                                      u8 action;
 +                                      u8 trans_id[WLAN_SA_QUERY_TR_ID_LEN];
 +                              } STRUCT_PACKED sa_query_req;
 +                              struct {
 +                                      u8 action; /* */
 +                                      u8 trans_id[WLAN_SA_QUERY_TR_ID_LEN];
 +                              } STRUCT_PACKED sa_query_resp;
 +                              struct {
 +                                      u8 action;
 +                                      u8 dialogtoken;
-                                       u8 variable[0];
++                                      u8 variable[];
 +                              } STRUCT_PACKED wnm_sleep_req;
 +                              struct {
 +                                      u8 action;
 +                                      u8 dialogtoken;
 +                                      le16 keydata_len;
-                                       u8 variable[0];
++                                      u8 variable[];
 +                              } STRUCT_PACKED wnm_sleep_resp;
 +                              struct {
 +                                      u8 action;
-                                       u8 variable[0];
++                                      u8 variable[];
 +                              } STRUCT_PACKED public_action;
 +                              struct {
 +                                      u8 action; /* 9 */
 +                                      u8 oui[3];
 +                                      /* Vendor-specific content */
-                                       u8 variable[0];
++                                      u8 variable[];
 +                              } STRUCT_PACKED vs_public_action;
 +                              struct {
 +                                      u8 action; /* 7 */
 +                                      u8 dialog_token;
 +                                      u8 req_mode;
 +                                      le16 disassoc_timer;
 +                                      u8 validity_interval;
 +                                      /* BSS Termination Duration (optional),
 +                                       * Session Information URL (optional),
 +                                       * BSS Transition Candidate List
 +                                       * Entries */
-                                       u8 variable[0];
++                                      u8 variable[];
 +                              } STRUCT_PACKED bss_tm_req;
 +                              struct {
 +                                      u8 action; /* 8 */
 +                                      u8 dialog_token;
 +                                      u8 status_code;
 +                                      u8 bss_termination_delay;
 +                                      /* Target BSSID (optional),
 +                                       * BSS Transition Candidate List
 +                                       * Entries (optional) */
-                                       u8 variable[0];
++                                      u8 variable[];
 +                              } STRUCT_PACKED bss_tm_resp;
 +                              struct {
 +                                      u8 action; /* 6 */
 +                                      u8 dialog_token;
 +                                      u8 query_reason;
 +                                      /* BSS Transition Candidate List
 +                                       * Entries (optional) */
-                                       u8 variable[0];
++                                      u8 variable[];
 +                              } STRUCT_PACKED bss_tm_query;
 +                              struct {
 +                                      u8 action; /* 15 */
++                                      u8 variable[];
 +                              } STRUCT_PACKED slf_prot_action;
++                              struct {
++                                      u8 action;
++                                      u8 variable[];
++                              } STRUCT_PACKED fst_action;
 +                      } u;
 +              } STRUCT_PACKED action;
 +      } u;
 +} STRUCT_PACKED;
 +
 +
 +/* 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; /* 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;
 +      u8 asel_capabilities;
 +} STRUCT_PACKED;
 +
 +
 +/* HT Operation element */
 +struct ieee80211_ht_operation {
 +      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;
 +
 +
 +struct ieee80211_vht_capabilities {
 +      le32 vht_capabilities_info;
 +      struct {
 +              le16 rx_map;
 +              le16 rx_highest;
 +              le16 tx_map;
 +              le16 tx_highest;
 +      } vht_supported_mcs_set;
 +} STRUCT_PACKED;
 +
 +struct ieee80211_vht_operation {
 +      u8 vht_op_info_chwidth;
 +      u8 vht_op_info_chan_center_freq_seg0_idx;
 +      u8 vht_op_info_chan_center_freq_seg1_idx;
 +      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 */
 +
 +#define ERP_INFO_NON_ERP_PRESENT BIT(0)
 +#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)))
 +#define HT_CAP_INFO_SMPS_STATIC                       ((u16) 0)
 +#define HT_CAP_INFO_SMPS_DYNAMIC              ((u16) BIT(2))
 +#define HT_CAP_INFO_SMPS_DISABLED             ((u16) (BIT(2) | BIT(3)))
 +#define HT_CAP_INFO_GREEN_FIELD                       ((u16) BIT(4))
 +#define HT_CAP_INFO_SHORT_GI20MHZ             ((u16) BIT(5))
 +#define HT_CAP_INFO_SHORT_GI40MHZ             ((u16) BIT(6))
 +#define HT_CAP_INFO_TX_STBC                   ((u16) BIT(7))
 +#define HT_CAP_INFO_RX_STBC_MASK              ((u16) (BIT(8) | BIT(9)))
 +#define HT_CAP_INFO_RX_STBC_1                 ((u16) BIT(8))
 +#define HT_CAP_INFO_RX_STBC_12                        ((u16) BIT(9))
 +#define HT_CAP_INFO_RX_STBC_123                       ((u16) (BIT(8) | BIT(9)))
 +#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))
 +/* 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_SUPPORT           ((u16) BIT(10))
 +#define EXT_HT_CAP_INFO_RD_RESPONDER          ((u16) BIT(11))
 +/* 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_STA_CHNL_WIDTH                       ((u8) BIT(2))
 +#define HT_INFO_HT_PARAM_RIFS_MODE                    ((u8) BIT(3))
 +/* 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
 +
 +/* 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))
 +#define VHT_CAP_TXSTBC                              ((u32) BIT(7))
 +#define VHT_CAP_RXSTBC_1                            ((u32) BIT(8))
 +#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_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_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
 +#define VHT_CHANWIDTH_160MHZ  2
 +#define VHT_CHANWIDTH_80P80MHZ        3
 +
 +#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
 +#define WMM_OUI_SUBTYPE_PARAMETER_ELEMENT 1
 +#define WMM_OUI_SUBTYPE_TSPEC_ELEMENT 2
 +#define WMM_VERSION 1
 +
 +#define WMM_ACTION_CODE_ADDTS_REQ 0
 +#define WMM_ACTION_CODE_ADDTS_RESP 1
 +#define WMM_ACTION_CODE_DELTS 2
 +
 +#define WMM_ADDTS_STATUS_ADMISSION_ACCEPTED 0
 +#define WMM_ADDTS_STATUS_INVALID_PARAMETERS 1
 +/* 2 - Reserved */
 +#define WMM_ADDTS_STATUS_REFUSED 3
 +/* 4-255 - Reserved */
 +
 +/* WMM TSPEC Direction Field Values */
 +#define WMM_TSPEC_DIRECTION_UPLINK 0
 +#define WMM_TSPEC_DIRECTION_DOWNLINK 1
 +/* 2 - Reserved */
 +#define WMM_TSPEC_DIRECTION_BI_DIRECTIONAL 3
 +
 +/*
 + * WMM Information Element (used in (Re)Association Request frames; may also be
 + * used in Beacon frames)
 + */
 +struct wmm_information_element {
 +      /* Element ID: 221 (0xdd); Length: 7 */
 +      /* required fields for WMM version 1 */
 +      u8 oui[3]; /* 00:50:f2 */
 +      u8 oui_type; /* 2 */
 +      u8 oui_subtype; /* 0 */
 +      u8 version; /* 1 for WMM version 1.0 */
 +      u8 qos_info; /* AP/STA specific QoS info */
 +
 +} 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
 +
 +#define WMM_AC_AIFSN_MASK 0x0f
 +#define WMM_AC_AIFNS_SHIFT 0
 +#define WMM_AC_ACM 0x10
 +#define WMM_AC_ACI_MASK 0x60
 +#define WMM_AC_ACI_SHIFT 5
 +
 +#define WMM_AC_ECWMIN_MASK 0x0f
 +#define WMM_AC_ECWMIN_SHIFT 0
 +#define WMM_AC_ECWMAX_MASK 0xf0
 +#define WMM_AC_ECWMAX_SHIFT 4
 +
 +struct wmm_ac_parameter {
 +      u8 aci_aifsn; /* AIFSN, ACM, ACI */
 +      u8 cw; /* ECWmin, ECWmax (CW = 2^ECW - 1) */
 +      le16 txop_limit;
 +}  STRUCT_PACKED;
 +
 +/*
 + * WMM Parameter Element (used in Beacon, Probe Response, and (Re)Association
 + * Response frmaes)
 + */
 +struct wmm_parameter_element {
 +      /* Element ID: 221 (0xdd); Length: 24 */
 +      /* required fields for WMM version 1 */
 +      u8 oui[3]; /* 00:50:f2 */
 +      u8 oui_type; /* 2 */
 +      u8 oui_subtype; /* 1 */
 +      u8 version; /* 1 for WMM version 1.0 */
 +      u8 qos_info; /* AP/STA specific QoS info */
 +      u8 reserved; /* 0 */
 +      struct wmm_ac_parameter ac[4]; /* AC_BE, AC_BK, AC_VI, AC_VO */
 +
 +} STRUCT_PACKED;
 +
 +/* WMM TSPEC Element */
 +struct wmm_tspec_element {
 +      u8 eid; /* 221 = 0xdd */
 +      u8 length; /* 6 + 55 = 61 */
 +      u8 oui[3]; /* 00:50:f2 */
 +      u8 oui_type; /* 2 */
 +      u8 oui_subtype; /* 2 */
 +      u8 version; /* 1 */
 +      /* WMM TSPEC body (55 octets): */
 +      u8 ts_info[3];
 +      le16 nominal_msdu_size;
 +      le16 maximum_msdu_size;
 +      le32 minimum_service_interval;
 +      le32 maximum_service_interval;
 +      le32 inactivity_interval;
 +      le32 suspension_interval;
 +      le32 service_start_time;
 +      le32 minimum_data_rate;
 +      le32 mean_data_rate;
 +      le32 peak_data_rate;
 +      le32 maximum_burst_size;
 +      le32 delay_bound;
 +      le32 minimum_phy_rate;
 +      le16 surplus_bandwidth_allowance;
 +      le16 medium_time;
 +} STRUCT_PACKED;
 +
 +
 +/* Access Categories / ACI to AC coding */
 +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_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
 +#define HS20_STYPE_WAN_METRICS 4
 +#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) */
 +
 +#define P2P_OUI_TYPE 9
 +
 +enum p2p_attr_id {
 +      P2P_ATTR_STATUS = 0,
 +      P2P_ATTR_MINOR_REASON_CODE = 1,
 +      P2P_ATTR_CAPABILITY = 2,
 +      P2P_ATTR_DEVICE_ID = 3,
 +      P2P_ATTR_GROUP_OWNER_INTENT = 4,
 +      P2P_ATTR_CONFIGURATION_TIMEOUT = 5,
 +      P2P_ATTR_LISTEN_CHANNEL = 6,
 +      P2P_ATTR_GROUP_BSSID = 7,
 +      P2P_ATTR_EXT_LISTEN_TIMING = 8,
 +      P2P_ATTR_INTENDED_INTERFACE_ADDR = 9,
 +      P2P_ATTR_MANAGEABILITY = 10,
 +      P2P_ATTR_CHANNEL_LIST = 11,
 +      P2P_ATTR_NOTICE_OF_ABSENCE = 12,
 +      P2P_ATTR_DEVICE_INFO = 13,
 +      P2P_ATTR_GROUP_INFO = 14,
 +      P2P_ATTR_GROUP_ID = 15,
 +      P2P_ATTR_INTERFACE = 16,
 +      P2P_ATTR_OPERATING_CHANNEL = 17,
 +      P2P_ATTR_INVITATION_FLAGS = 18,
 +      P2P_ATTR_OOB_GO_NEG_CHANNEL = 19,
 +      P2P_ATTR_SERVICE_HASH = 21,
 +      P2P_ATTR_SESSION_INFORMATION_DATA = 22,
 +      P2P_ATTR_CONNECTION_CAPABILITY = 23,
 +      P2P_ATTR_ADVERTISEMENT_ID = 24,
 +      P2P_ATTR_ADVERTISED_SERVICE = 25,
 +      P2P_ATTR_SESSION_ID = 26,
 +      P2P_ATTR_FEATURE_CAPABILITY = 27,
 +      P2P_ATTR_PERSISTENT_GROUP = 28,
 +      P2P_ATTR_VENDOR_SPECIFIC = 221
 +};
 +
 +#define P2P_MAX_GO_INTENT 15
 +
 +/* P2P Capability - Device Capability bitmap */
 +#define P2P_DEV_CAPAB_SERVICE_DISCOVERY BIT(0)
 +#define P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY BIT(1)
 +#define P2P_DEV_CAPAB_CONCURRENT_OPER BIT(2)
 +#define P2P_DEV_CAPAB_INFRA_MANAGED BIT(3)
 +#define P2P_DEV_CAPAB_DEVICE_LIMIT BIT(4)
 +#define P2P_DEV_CAPAB_INVITATION_PROCEDURE BIT(5)
 +
 +/* P2P Capability - Group Capability bitmap */
 +#define P2P_GROUP_CAPAB_GROUP_OWNER BIT(0)
 +#define P2P_GROUP_CAPAB_PERSISTENT_GROUP BIT(1)
 +#define P2P_GROUP_CAPAB_GROUP_LIMIT BIT(2)
 +#define P2P_GROUP_CAPAB_INTRA_BSS_DIST BIT(3)
 +#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)
 +
++/* P2PS Coordination Protocol Transport Bitmap */
++#define P2PS_FEATURE_CAPAB_UDP_TRANSPORT BIT(0)
++#define P2PS_FEATURE_CAPAB_MAC_TRANSPORT BIT(1)
++
++struct p2ps_feature_capab {
++      u8 cpt;
++      u8 reserved;
++} STRUCT_PACKED;
++
 +/* Invitation Flags */
 +#define P2P_INVITATION_FLAGS_TYPE BIT(0)
 +
 +/* P2P Manageability */
 +#define P2P_MAN_DEVICE_MANAGEMENT BIT(0)
 +#define P2P_MAN_CROSS_CONNECTION_PERMITTED BIT(1)
 +#define P2P_MAN_COEXISTENCE_OPTIONAL BIT(2)
 +
 +enum p2p_status_code {
 +      P2P_SC_SUCCESS = 0,
 +      P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE = 1,
 +      P2P_SC_FAIL_INCOMPATIBLE_PARAMS = 2,
 +      P2P_SC_FAIL_LIMIT_REACHED = 3,
 +      P2P_SC_FAIL_INVALID_PARAMS = 4,
 +      P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE = 5,
 +      P2P_SC_FAIL_PREV_PROTOCOL_ERROR = 6,
 +      P2P_SC_FAIL_NO_COMMON_CHANNELS = 7,
 +      P2P_SC_FAIL_UNKNOWN_GROUP = 8,
 +      P2P_SC_FAIL_BOTH_GO_INTENT_15 = 9,
 +      P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD = 10,
 +      P2P_SC_FAIL_REJECTED_BY_USER = 11,
 +      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-"
 +#define P2P_WILDCARD_SSID_LEN 7
 +
 +/* P2P action frames */
 +enum p2p_act_frame_type {
 +      P2P_NOA = 0,
 +      P2P_PRESENCE_REQ = 1,
 +      P2P_PRESENCE_RESP = 2,
 +      P2P_GO_DISC_REQ = 3
 +};
 +
 +/* P2P public action frames */
 +enum p2p_action_frame_type {
 +      P2P_GO_NEG_REQ = 0,
 +      P2P_GO_NEG_RESP = 1,
 +      P2P_GO_NEG_CONF = 2,
 +      P2P_INVITATION_REQ = 3,
 +      P2P_INVITATION_RESP = 4,
 +      P2P_DEV_DISC_REQ = 5,
 +      P2P_DEV_DISC_RESP = 6,
 +      P2P_PROV_DISC_REQ = 7,
 +      P2P_PROV_DISC_RESP = 8
 +};
 +
 +enum p2p_service_protocol_type {
 +      P2P_SERV_ALL_SERVICES = 0,
 +      P2P_SERV_BONJOUR = 1,
 +      P2P_SERV_UPNP = 2,
 +      P2P_SERV_WS_DISCOVERY = 3,
 +      P2P_SERV_WIFI_DISPLAY = 4,
 +      P2P_SERV_P2PS = 11,
 +      P2P_SERV_VENDOR_SPECIFIC = 255
 +};
 +
 +enum p2p_sd_status {
 +      P2P_SD_SUCCESS = 0,
 +      P2P_SD_PROTO_NOT_AVAILABLE = 1,
 +      P2P_SD_REQUESTED_INFO_NOT_AVAILABLE = 2,
 +      P2P_SD_BAD_REQUEST = 3
 +};
 +
 +
 +enum wifi_display_subelem {
 +      WFD_SUBELEM_DEVICE_INFO = 0,
 +      WFD_SUBELEM_ASSOCIATED_BSSID = 1,
 +      WFD_SUBELEM_AUDIO_FORMATS = 2,
 +      WFD_SUBELEM_VIDEO_FORMATS = 3,
 +      WFD_SUBELEM_3D_VIDEO_FORMATS = 4,
 +      WFD_SUBELEM_CONTENT_PROTECTION = 5,
 +      WFD_SUBELEM_COUPLED_SINK = 6,
 +      WFD_SUBELEM_EXT_CAPAB = 7,
 +      WFD_SUBELEM_LOCAL_IP_ADDRESS = 8,
 +      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 */
 +
 +/* cipher suite selectors */
 +#define WLAN_CIPHER_SUITE_USE_GROUP   0x000FAC00
 +#define WLAN_CIPHER_SUITE_WEP40               0x000FAC01
 +#define WLAN_CIPHER_SUITE_TKIP                0x000FAC02
 +/* reserved:                          0x000FAC03 */
 +#define WLAN_CIPHER_SUITE_CCMP                0x000FAC04
 +#define WLAN_CIPHER_SUITE_WEP104      0x000FAC05
 +#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
 +
 +#define WLAN_CIPHER_SUITE_CKIP                0x00409600
 +#define WLAN_CIPHER_SUITE_CKIP_CMIC   0x00409601
 +#define WLAN_CIPHER_SUITE_CMIC                0x00409602
 +#define WLAN_CIPHER_SUITE_KRK         0x004096FF /* for nl80211 use only */
 +
 +/* AKM suite selectors */
 +#define WLAN_AKM_SUITE_8021X          0x000FAC01
 +#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 */
 +enum wnm_action {
 +      WNM_EVENT_REQ = 0,
 +      WNM_EVENT_REPORT = 1,
 +      WNM_DIAGNOSTIC_REQ = 2,
 +      WNM_DIAGNOSTIC_REPORT = 3,
 +      WNM_LOCATION_CFG_REQ = 4,
 +      WNM_LOCATION_CFG_RESP = 5,
 +      WNM_BSS_TRANS_MGMT_QUERY = 6,
 +      WNM_BSS_TRANS_MGMT_REQ = 7,
 +      WNM_BSS_TRANS_MGMT_RESP = 8,
 +      WNM_FMS_REQ = 9,
 +      WNM_FMS_RESP = 10,
 +      WNM_COLLOCATED_INTERFERENCE_REQ = 11,
 +      WNM_COLLOCATED_INTERFERENCE_REPORT = 12,
 +      WNM_TFS_REQ = 13,
 +      WNM_TFS_RESP = 14,
 +      WNM_TFS_NOTIFY = 15,
 +      WNM_SLEEP_MODE_REQ = 16,
 +      WNM_SLEEP_MODE_RESP = 17,
 +      WNM_TIM_BROADCAST_REQ = 18,
 +      WNM_TIM_BROADCAST_RESP = 19,
 +      WNM_QOS_TRAFFIC_CAPAB_UPDATE = 20,
 +      WNM_CHANNEL_USAGE_REQ = 21,
 +      WNM_CHANNEL_USAGE_RESP = 22,
 +      WNM_DMS_REQ = 23,
 +      WNM_DMS_RESP = 24,
 +      WNM_TIMING_MEASUREMENT_REQ = 25,
 +      WNM_NOTIFICATION_REQ = 26,
 +      WNM_NOTIFICATION_RESP = 27
 +};
 +
 +/* IEEE 802.11v - BSS Transition Management Request - Request Mode */
 +#define WNM_BSS_TM_REQ_PREF_CAND_LIST_INCLUDED BIT(0)
 +#define WNM_BSS_TM_REQ_ABRIDGED BIT(1)
 +#define WNM_BSS_TM_REQ_DISASSOC_IMMINENT BIT(2)
 +#define WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED BIT(3)
 +#define WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT BIT(4)
 +
 +/* IEEE Std 802.11-2012 - Table 8-253 */
 +enum bss_trans_mgmt_status_code {
 +      WNM_BSS_TM_ACCEPT = 0,
 +      WNM_BSS_TM_REJECT_UNSPECIFIED = 1,
 +      WNM_BSS_TM_REJECT_INSUFFICIENT_BEACON = 2,
 +      WNM_BSS_TM_REJECT_INSUFFICIENT_CAPABITY = 3,
 +      WNM_BSS_TM_REJECT_UNDESIRED = 4,
 +      WNM_BSS_TM_REJECT_DELAY_REQUEST = 5,
 +      WNM_BSS_TM_REJECT_STA_CANDIDATE_LIST_PROVIDED = 6,
 +      WNM_BSS_TM_REJECT_NO_SUITABLE_CANDIDATES = 7,
 +      WNM_BSS_TM_REJECT_LEAVING_ESS = 8
 +};
 +
 +#define WNM_NEIGHBOR_TSF                         1
 +#define WNM_NEIGHBOR_CONDENSED_COUNTRY_STRING    2
 +#define WNM_NEIGHBOR_BSS_TRANSITION_CANDIDATE    3
 +#define WNM_NEIGHBOR_BSS_TERMINATION_DURATION    4
 +#define WNM_NEIGHBOR_BEARING                     5
 +#define WNM_NEIGHBOR_MEASUREMENT_PILOT          66
 +#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)
 +#define WLAN_20_40_BSS_COEX_20MHZ_WIDTH_REQ     BIT(2)
 +#define WLAN_20_40_BSS_COEX_OBSS_EXEMPT_REQ     BIT(3)
 +#define WLAN_20_40_BSS_COEX_OBSS_EXEMPT_GRNT    BIT(4)
 +
 +struct ieee80211_2040_bss_coex_ie {
 +      u8 element_id;
 +      u8 length;
 +      u8 coex_param;
 +} STRUCT_PACKED;
 +
 +struct ieee80211_2040_intol_chan_report {
 +      u8 element_id;
 +      u8 length;
 +      u8 op_class;
 +      u8 variable[0]; /* Channel List */
 +} STRUCT_PACKED;
 +
 +/* IEEE 802.11v - WNM-Sleep Mode element */
 +struct wnm_sleep_element {
 +      u8 eid;     /* WLAN_EID_WNMSLEEP */
 +      u8 len;
 +      u8 action_type; /* WNM_SLEEP_ENTER/WNM_SLEEP_MODE_EXIT */
 +      u8 status;
 +      le16 intval;
 +} STRUCT_PACKED;
 +
 +#define WNM_SLEEP_MODE_ENTER 0
 +#define WNM_SLEEP_MODE_EXIT 1
 +
 +enum wnm_sleep_mode_response_status {
 +      WNM_STATUS_SLEEP_ACCEPT = 0,
 +      WNM_STATUS_SLEEP_EXIT_ACCEPT_GTK_UPDATE = 1,
 +      WNM_STATUS_DENIED_ACTION = 2,
 +      WNM_STATUS_DENIED_TMP = 3,
 +      WNM_STATUS_DENIED_KEY = 4,
 +      WNM_STATUS_DENIED_OTHER_WNM_SERVICE = 5
 +};
 +
 +/* WNM-Sleep Mode subelement IDs */
 +enum wnm_sleep_mode_subelement_id {
 +      WNM_SLEEP_SUBELEM_GTK = 0,
 +      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;
 +
++#define SSID_MAX_LEN 32
++
++/* IEEE Std 802.11ad-2012 - Multi-band element */
++struct multi_band_ie {
++      u8 eid; /* WLAN_EID_MULTI_BAND */
++      u8 len;
++      u8 mb_ctrl;
++      u8 band_id;
++      u8 op_class;
++      u8 chan;
++      u8 bssid[ETH_ALEN];
++      le16 beacon_int;
++      u8 tsf_offs[8];
++      u8 mb_connection_capability;
++      u8 fst_session_tmout;
++      /* Optional:
++       *   STA MAC Address
++       *   Pairwise Cipher Suite Count
++       *   Pairwise Cipher Suite List
++       */
++      u8 variable[0];
++} STRUCT_PACKED;
++
++enum mb_ctrl_sta_role {
++      MB_STA_ROLE_AP = 0,
++      MB_STA_ROLE_TDLS_STA = 1,
++      MB_STA_ROLE_IBSS_STA = 2,
++      MB_STA_ROLE_PCP = 3,
++      MB_STA_ROLE_NON_PCP_NON_AP = 4
++};
++
++#define MB_CTRL_ROLE_MASK (BIT(0) | BIT(1) | BIT(2))
++#define MB_CTRL_ROLE(ctrl) ((u8) ((ctrl) & MB_CTRL_ROLE_MASK))
++#define MB_CTRL_STA_MAC_PRESENT ((u8) (BIT(3)))
++#define MB_CTRL_PAIRWISE_CIPHER_SUITE_PRESENT ((u8) (BIT(4)))
++
++enum mb_band_id {
++      MB_BAND_ID_WIFI_2_4GHZ = 2, /* 2.4 GHz */
++      MB_BAND_ID_WIFI_5GHZ = 4, /* 4.9 and 5 GHz */
++      MB_BAND_ID_WIFI_60GHZ = 5, /* 60 GHz */
++};
++
++#define MB_CONNECTION_CAPABILITY_AP ((u8) (BIT(0)))
++#define MB_CONNECTION_CAPABILITY_PCP ((u8) (BIT(1)))
++#define MB_CONNECTION_CAPABILITY_DLS ((u8) (BIT(2)))
++#define MB_CONNECTION_CAPABILITY_TDLS ((u8) (BIT(3)))
++#define MB_CONNECTION_CAPABILITY_IBSS ((u8) (BIT(4)))
++
++/* IEEE Std 802.11ad-2014 - FST Action field */
++enum fst_action {
++      FST_ACTION_SETUP_REQUEST = 0,
++      FST_ACTION_SETUP_RESPONSE = 1,
++      FST_ACTION_TEAR_DOWN = 2,
++      FST_ACTION_ACK_REQUEST = 3,
++      FST_ACTION_ACK_RESPONSE = 4,
++      FST_ACTION_ON_CHANNEL_TUNNEL = 5,
++};
++
 +#endif /* IEEE802_11_DEFS_H */
index 4dc34c4ad322b370ed96b79ae301b4d7404f241b,0000000000000000000000000000000000000000..8dff30382b60559e5d9527877886476ce5f0428b
mode 100644,000000..100644
--- /dev/null
@@@ -1,71 -1,0 +1,104 @@@
-       u8 ssid[32];
 +/*
 + * WPA Supplicant - privilege separation commands
 + * Copyright (c) 2007-2009, Jouni Malinen <j@w1.fi>
 + *
 + * This software may be distributed under the terms of the BSD license.
 + * See README for more details.
 + */
 +
 +#ifndef PRIVSEP_COMMANDS_H
 +#define PRIVSEP_COMMANDS_H
 +
++#include "common/ieee802_11_defs.h"
++
 +enum privsep_cmd {
 +      PRIVSEP_CMD_REGISTER,
 +      PRIVSEP_CMD_UNREGISTER,
 +      PRIVSEP_CMD_SCAN,
 +      PRIVSEP_CMD_GET_SCAN_RESULTS,
 +      PRIVSEP_CMD_ASSOCIATE,
 +      PRIVSEP_CMD_GET_BSSID,
 +      PRIVSEP_CMD_GET_SSID,
 +      PRIVSEP_CMD_SET_KEY,
 +      PRIVSEP_CMD_GET_CAPA,
 +      PRIVSEP_CMD_L2_REGISTER,
 +      PRIVSEP_CMD_L2_UNREGISTER,
 +      PRIVSEP_CMD_L2_NOTIFY_AUTH_START,
 +      PRIVSEP_CMD_L2_SEND,
 +      PRIVSEP_CMD_SET_COUNTRY,
++      PRIVSEP_CMD_AUTHENTICATE,
++};
++
++struct privsep_cmd_authenticate
++{
++      int freq;
++      u8 bssid[ETH_ALEN];
++      u8 ssid[SSID_MAX_LEN];
++      size_t ssid_len;
++      int auth_alg;
++      size_t ie_len;
++      u8 wep_key[4][16];
++      size_t wep_key_len[4];
++      int wep_tx_keyidx;
++      int local_state_change;
++      int p2p;
++      size_t sae_data_len;
++      /* followed by ie_len bytes of ie */
++      /* followed by sae_data_len bytes of sae_data */
 +};
 +
 +struct privsep_cmd_associate
 +{
 +      u8 bssid[ETH_ALEN];
++      u8 ssid[SSID_MAX_LEN];
 +      size_t ssid_len;
 +      int hwmode;
 +      int freq;
 +      int channel;
 +      int pairwise_suite;
 +      int group_suite;
 +      int key_mgmt_suite;
 +      int auth_alg;
 +      int mode;
 +      size_t wpa_ie_len;
 +      /* followed by wpa_ie_len bytes of wpa_ie */
 +};
 +
 +struct privsep_cmd_set_key
 +{
 +      int alg;
 +      u8 addr[ETH_ALEN];
 +      int key_idx;
 +      int set_tx;
 +      u8 seq[8];
 +      size_t seq_len;
 +      u8 key[32];
 +      size_t key_len;
 +};
 +
 +enum privsep_event {
 +      PRIVSEP_EVENT_SCAN_RESULTS,
 +      PRIVSEP_EVENT_ASSOC,
 +      PRIVSEP_EVENT_DISASSOC,
 +      PRIVSEP_EVENT_ASSOCINFO,
 +      PRIVSEP_EVENT_MICHAEL_MIC_FAILURE,
 +      PRIVSEP_EVENT_INTERFACE_STATUS,
 +      PRIVSEP_EVENT_PMKID_CANDIDATE,
 +      PRIVSEP_EVENT_STKSTART,
 +      PRIVSEP_EVENT_FT_RESPONSE,
 +      PRIVSEP_EVENT_RX_EAPOL,
++      PRIVSEP_EVENT_SCAN_STARTED,
++      PRIVSEP_EVENT_AUTH,
++};
++
++struct privsep_event_auth {
++      u8 peer[ETH_ALEN];
++      u8 bssid[ETH_ALEN];
++      u16 auth_type;
++      u16 auth_transaction;
++      u16 status_code;
++      size_t ies_len;
++      /* followed by ies_len bytes of ies */
 +};
 +
 +#endif /* PRIVSEP_COMMANDS_H */
index 2117ee7028c1d4d4857a01234f8b9ea6ae8a6566,0000000000000000000000000000000000000000..28985f5194e21d5eee4813b0acaab9417de06e2a
mode 100644,000000..100644
--- /dev/null
@@@ -1,246 -1,0 +1,357 @@@
-       /* 53 - reserved for QCA */
 +/*
 + * 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 - was used by QCA, but not in use anymore */
 +      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,
++      QCA_NL80211_VENDOR_SUBCMD_OCB_SET_CONFIG = 92,
++      QCA_NL80211_VENDOR_SUBCMD_OCB_SET_UTC_TIME = 93,
++      QCA_NL80211_VENDOR_SUBCMD_OCB_START_TIMING_ADVERT = 94,
++      QCA_NL80211_VENDOR_SUBCMD_OCB_STOP_TIMING_ADVERT = 95,
++      QCA_NL80211_VENDOR_SUBCMD_OCB_GET_TSF_TIMER = 96,
++      QCA_NL80211_VENDOR_SUBCMD_DCC_GET_STATS = 97,
++      QCA_NL80211_VENDOR_SUBCMD_DCC_CLEAR_STATS = 98,
++      QCA_NL80211_VENDOR_SUBCMD_DCC_UPDATE_NDL = 99,
++      QCA_NL80211_VENDOR_SUBCMD_DCC_STATS_EVENT = 100,
++      QCA_NL80211_VENDOR_SUBCMD_LINK_PROPERTIES = 101,
++      QCA_NL80211_VENDOR_SUBCMD_GW_PARAM_CONFIG = 102,
++      QCA_NL80211_VENDOR_SUBCMD_GET_PREFERRED_FREQ_LIST = 103,
++      QCA_NL80211_VENDOR_SUBCMD_SET_PROBABLE_OPER_CHANNEL = 104,
++      QCA_NL80211_VENDOR_SUBCMD_SETBAND = 105,
 +};
 +
 +
 +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,
++      /* used by QCA_NL80211_VENDOR_SUBCMD_GET_FEATURES */
++      /* Unsigned 32-bit value. */
++      QCA_WLAN_VENDOR_ATTR_CONCURRENCY_CAPA = 9,
++      /* Unsigned 32-bit value */
++      QCA_WLAN_VENDOR_ATTR_MAX_CONCURRENT_CHANNELS_2_4_BAND = 10,
++      /* Unsigned 32-bit value */
++      QCA_WLAN_VENDOR_ATTR_MAX_CONCURRENT_CHANNELS_5_0_BAND = 11,
++      /* Unsigned 32-bit value from enum qca_set_band. */
++      QCA_WLAN_VENDOR_ATTR_SETBAND_VALUE = 12,
 +      /* 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,
++      QCA_WLAN_VENDOR_ATTR_ACS_VHT_ENABLED,
++      QCA_WLAN_VENDOR_ATTR_ACS_CHWIDTH,
++      QCA_WLAN_VENDOR_ATTR_ACS_CH_LIST,
++      QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG0_CENTER_CHANNEL,
++      QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG1_CENTER_CHANNEL,
++      QCA_WLAN_VENDOR_ATTR_ACS_FREQ_LIST,
 +      /* 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,
++      QCA_ACS_MODE_IEEE80211ANY,
 +};
 +
 +/**
 + * 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.
++ * @QCA_WLAN_VENDOR_FEATURE_SUPPORT_HW_MODE_ANY: Device supports automatic
++ *    band selection based on channel selection results.
 + * @NUM_QCA_WLAN_VENDOR_FEATURES: Number of assigned feature bits
 + */
 +enum qca_wlan_vendor_features {
 +      QCA_WLAN_VENDOR_FEATURE_KEY_MGMT_OFFLOAD        = 0,
++      QCA_WLAN_VENDOR_FEATURE_SUPPORT_HW_MODE_ANY     = 1,
 +      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
 +};
++
++enum qca_vendor_attr_get_preferred_freq_list {
++      QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_INVALID,
++      /* A 32-unsigned value; the interface type/mode for which the preferred
++       * frequency list is requested (see enum qca_iface_type for possible
++       * values); used in GET_PREFERRED_FREQ_LIST command from user-space to
++       * kernel and in the kernel response back to user-space.
++       */
++      QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_IFACE_TYPE,
++      /* An array of 32-unsigned values; values are frequency (MHz); sent
++       * from kernel space to user space.
++       */
++      QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST,
++      /* keep last */
++      QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_AFTER_LAST,
++      QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_MAX =
++      QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_AFTER_LAST - 1
++};
++
++enum qca_vendor_attr_probable_oper_channel {
++      QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_INVALID,
++      /* 32-bit unsigned value; indicates the connection/iface type likely to
++       * come on this channel (see enum qca_iface_type).
++       */
++      QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_IFACE_TYPE,
++      /* 32-bit unsigned value; the frequency (MHz) of the probable channel */
++      QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_FREQ,
++      /* keep last */
++      QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_AFTER_LAST,
++      QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_MAX =
++      QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_AFTER_LAST - 1
++};
++
++enum qca_iface_type {
++      QCA_IFACE_TYPE_STA,
++      QCA_IFACE_TYPE_AP,
++      QCA_IFACE_TYPE_P2P_CLIENT,
++      QCA_IFACE_TYPE_P2P_GO,
++      QCA_IFACE_TYPE_IBSS,
++      QCA_IFACE_TYPE_TDLS,
++};
++
++enum qca_set_band {
++      QCA_SETBAND_AUTO,
++      QCA_SETBAND_5G,
++      QCA_SETBAND_2G,
++};
++
++/* IEEE 802.11 Vendor Specific elements */
++
++/**
++ * enum qca_vendor_element_id - QCA Vendor Specific element types
++ *
++ * These values are used to identify QCA Vendor Specific elements. The
++ * payload of the element starts with the three octet OUI (OUI_QCA) and
++ * is followed by a single octet type which is defined by this enum.
++ *
++ * @QCA_VENDOR_ELEM_P2P_PREF_CHAN_LIST: P2P preferred channel list.
++ *    This element can be used to specify preference order for supported
++ *    channels. The channels in this list are in preference order (the first
++ *    one has the highest preference) and are described as a pair of
++ *    (global) Operating Class and Channel Number (each one octet) fields.
++ *
++ *    This extends the standard P2P functionality by providing option to have
++ *    more than one preferred operating channel. When this element is present,
++ *    it replaces the preference indicated in the Operating Channel attribute.
++ *    For supporting other implementations, the Operating Channel attribute is
++ *    expected to be used with the highest preference channel. Similarly, all
++ *    the channels included in this Preferred channel list element are
++ *    expected to be included in the Channel List attribute.
++ *
++ *    This vendor element may be included in GO Negotiation Request, P2P
++ *    Invitation Request, and Provision Discovery Request frames.
++ */
++enum qca_vendor_element_id {
++      QCA_VENDOR_ELEM_P2P_PREF_CHAN_LIST = 0,
++};
++
 +#endif /* QCA_VENDOR_H */
index 588895808fde2bd01cc8f96b0c96f6e0589d0121,0000000000000000000000000000000000000000..503fa1d7b9a96143c778a8d6f38965b1469c070d
mode 100644,000000..100644
--- /dev/null
@@@ -1,1069 -1,0 +1,1292 @@@
-  * Copyright (c) 2012-2013, Jouni Malinen <j@w1.fi>
 +/*
 + * Simultaneous authentication of equals
-               if (iter++ > 100)
-                       return NULL;
-               if (random_get_bytes(val, order_len) < 0)
++ * Copyright (c) 2012-2015, Jouni Malinen <j@w1.fi>
 + *
 + * This software may be distributed under the terms of the BSD license.
 + * See README for more details.
 + */
 +
 +#include "includes.h"
 +
 +#include "common.h"
 +#include "crypto/crypto.h"
 +#include "crypto/sha256.h"
 +#include "crypto/random.h"
 +#include "crypto/dh_groups.h"
 +#include "ieee802_11_defs.h"
 +#include "sae.h"
 +
 +
 +int sae_set_group(struct sae_data *sae, int group)
 +{
 +      struct sae_temporary_data *tmp;
 +
 +      sae_clear_data(sae);
 +      tmp = sae->tmp = os_zalloc(sizeof(*tmp));
 +      if (tmp == NULL)
 +              return -1;
 +
 +      /* First, check if this is an ECC group */
 +      tmp->ec = crypto_ec_init(group);
 +      if (tmp->ec) {
 +              sae->group = group;
 +              tmp->prime_len = crypto_ec_prime_len(tmp->ec);
 +              tmp->prime = crypto_ec_get_prime(tmp->ec);
 +              tmp->order = crypto_ec_get_order(tmp->ec);
 +              return 0;
 +      }
 +
 +      /* Not an ECC group, check FFC */
 +      tmp->dh = dh_groups_get(group);
 +      if (tmp->dh) {
 +              sae->group = group;
 +              tmp->prime_len = tmp->dh->prime_len;
 +              if (tmp->prime_len > SAE_MAX_PRIME_LEN) {
 +                      sae_clear_data(sae);
 +                      return -1;
 +              }
 +
 +              tmp->prime_buf = crypto_bignum_init_set(tmp->dh->prime,
 +                                                      tmp->prime_len);
 +              if (tmp->prime_buf == NULL) {
 +                      sae_clear_data(sae);
 +                      return -1;
 +              }
 +              tmp->prime = tmp->prime_buf;
 +
 +              tmp->order_buf = crypto_bignum_init_set(tmp->dh->order,
 +                                                      tmp->dh->order_len);
 +              if (tmp->order_buf == NULL) {
 +                      sae_clear_data(sae);
 +                      return -1;
 +              }
 +              tmp->order = tmp->order_buf;
 +
 +              return 0;
 +      }
 +
 +      /* Unsupported group */
 +      return -1;
 +}
 +
 +
 +void sae_clear_temp_data(struct sae_data *sae)
 +{
 +      struct sae_temporary_data *tmp;
 +      if (sae == NULL || sae->tmp == NULL)
 +              return;
 +      tmp = sae->tmp;
 +      crypto_ec_deinit(tmp->ec);
 +      crypto_bignum_deinit(tmp->prime_buf, 0);
 +      crypto_bignum_deinit(tmp->order_buf, 0);
 +      crypto_bignum_deinit(tmp->sae_rand, 1);
 +      crypto_bignum_deinit(tmp->pwe_ffc, 1);
 +      crypto_bignum_deinit(tmp->own_commit_scalar, 0);
 +      crypto_bignum_deinit(tmp->own_commit_element_ffc, 0);
 +      crypto_bignum_deinit(tmp->peer_commit_element_ffc, 0);
 +      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);
 +      wpabuf_free(tmp->anti_clogging_token);
 +      bin_clear_free(tmp, sizeof(*tmp));
 +      sae->tmp = NULL;
 +}
 +
 +
 +void sae_clear_data(struct sae_data *sae)
 +{
 +      if (sae == NULL)
 +              return;
 +      sae_clear_temp_data(sae);
 +      crypto_bignum_deinit(sae->peer_commit_scalar, 0);
 +      os_memset(sae, 0, sizeof(*sae));
 +}
 +
 +
 +static void buf_shift_right(u8 *buf, size_t len, size_t bits)
 +{
 +      size_t i;
 +      for (i = len - 1; i > 0; i--)
 +              buf[i] = (buf[i - 1] << (8 - bits)) | (buf[i] >> bits);
 +      buf[0] >>= bits;
 +}
 +
 +
 +static struct crypto_bignum * sae_get_rand(struct sae_data *sae)
 +{
 +      u8 val[SAE_MAX_PRIME_LEN];
 +      int iter = 0;
 +      struct crypto_bignum *bn = NULL;
 +      int order_len_bits = crypto_bignum_bits(sae->tmp->order);
 +      size_t order_len = (order_len_bits + 7) / 8;
 +
 +      if (order_len > sizeof(val))
 +              return NULL;
 +
 +      for (;;) {
-                                struct crypto_ec_point *pwe)
++              if (iter++ > 100 || random_get_bytes(val, order_len) < 0)
 +                      return NULL;
 +              if (order_len_bits % 8)
 +                      buf_shift_right(val, order_len, 8 - order_len_bits % 8);
 +              bn = crypto_bignum_init_set(val, order_len);
 +              if (bn == NULL)
 +                      return NULL;
 +              if (crypto_bignum_is_zero(bn) ||
 +                  crypto_bignum_is_one(bn) ||
 +                  crypto_bignum_cmp(bn, sae->tmp->order) >= 0) {
 +                      crypto_bignum_deinit(bn, 0);
 +                      continue;
 +              }
 +              break;
 +      }
 +
 +      os_memset(val, 0, order_len);
 +      return bn;
 +}
 +
 +
 +static struct crypto_bignum * sae_get_rand_and_mask(struct sae_data *sae)
 +{
 +      crypto_bignum_deinit(sae->tmp->sae_rand, 1);
 +      sae->tmp->sae_rand = sae_get_rand(sae);
 +      if (sae->tmp->sae_rand == NULL)
 +              return NULL;
 +      return sae_get_rand(sae);
 +}
 +
 +
 +static void sae_pwd_seed_key(const u8 *addr1, const u8 *addr2, u8 *key)
 +{
 +      wpa_printf(MSG_DEBUG, "SAE: PWE derivation - addr1=" MACSTR
 +                 " addr2=" MACSTR, MAC2STR(addr1), MAC2STR(addr2));
 +      if (os_memcmp(addr1, addr2, ETH_ALEN) > 0) {
 +              os_memcpy(key, addr1, ETH_ALEN);
 +              os_memcpy(key + ETH_ALEN, addr2, ETH_ALEN);
 +      } else {
 +              os_memcpy(key, addr2, ETH_ALEN);
 +              os_memcpy(key + ETH_ALEN, addr1, ETH_ALEN);
 +      }
 +}
 +
 +
++static struct crypto_bignum *
++get_rand_1_to_p_1(const u8 *prime, size_t prime_len, size_t prime_bits,
++                int *r_odd)
++{
++      for (;;) {
++              struct crypto_bignum *r;
++              u8 tmp[SAE_MAX_ECC_PRIME_LEN];
++
++              if (random_get_bytes(tmp, prime_len) < 0)
++                      break;
++              if (prime_bits % 8)
++                      buf_shift_right(tmp, prime_len, 8 - prime_bits % 8);
++              if (os_memcmp(tmp, prime, prime_len) >= 0)
++                      continue;
++              r = crypto_bignum_init_set(tmp, prime_len);
++              if (!r)
++                      break;
++              if (crypto_bignum_is_zero(r)) {
++                      crypto_bignum_deinit(r, 0);
++                      continue;
++              }
++
++              *r_odd = tmp[prime_len - 1] & 0x01;
++              return r;
++      }
++
++      return NULL;
++}
++
++
++static int is_quadratic_residue_blind(struct sae_data *sae,
++                                    const u8 *prime, size_t bits,
++                                    const struct crypto_bignum *qr,
++                                    const struct crypto_bignum *qnr,
++                                    const struct crypto_bignum *y_sqr)
++{
++      struct crypto_bignum *r, *num;
++      int r_odd, check, res = -1;
++
++      /*
++       * Use the blinding technique to mask y_sqr while determining
++       * whether it is a quadratic residue modulo p to avoid leaking
++       * timing information while determining the Legendre symbol.
++       *
++       * v = y_sqr
++       * r = a random number between 1 and p-1, inclusive
++       * num = (v * r * r) modulo p
++       */
++      r = get_rand_1_to_p_1(prime, sae->tmp->prime_len, bits, &r_odd);
++      if (!r)
++              return -1;
++
++      num = crypto_bignum_init();
++      if (!num ||
++          crypto_bignum_mulmod(y_sqr, r, sae->tmp->prime, num) < 0 ||
++          crypto_bignum_mulmod(num, r, sae->tmp->prime, num) < 0)
++              goto fail;
++
++      if (r_odd) {
++              /*
++               * num = (num * qr) module p
++               * LGR(num, p) = 1 ==> quadratic residue
++               */
++              if (crypto_bignum_mulmod(num, qr, sae->tmp->prime, num) < 0)
++                      goto fail;
++              check = 1;
++      } else {
++              /*
++               * num = (num * qnr) module p
++               * LGR(num, p) = -1 ==> quadratic residue
++               */
++              if (crypto_bignum_mulmod(num, qnr, sae->tmp->prime, num) < 0)
++                      goto fail;
++              check = -1;
++      }
++
++      res = crypto_bignum_legendre(num, sae->tmp->prime);
++      if (res == -2) {
++              res = -1;
++              goto fail;
++      }
++      res = res == check;
++fail:
++      crypto_bignum_deinit(num, 1);
++      crypto_bignum_deinit(r, 1);
++      return res;
++}
++
++
 +static int sae_test_pwd_seed_ecc(struct sae_data *sae, const u8 *pwd_seed,
-       u8 pwd_value[SAE_MAX_ECC_PRIME_LEN], prime[SAE_MAX_ECC_PRIME_LEN];
-       struct crypto_bignum *x;
-       int y_bit;
++                               const u8 *prime,
++                               const struct crypto_bignum *qr,
++                               const struct crypto_bignum *qnr,
++                               struct crypto_bignum **ret_x_cand)
 +{
-       if (crypto_bignum_to_bin(sae->tmp->prime, prime, sizeof(prime),
-                                sae->tmp->prime_len) < 0)
-               return -1;
++      u8 pwd_value[SAE_MAX_ECC_PRIME_LEN];
++      struct crypto_bignum *y_sqr, *x_cand;
++      int res;
 +      size_t bits;
 +
-       y_bit = pwd_seed[SHA256_MAC_LEN - 1] & 0x01;
-       x = crypto_bignum_init_set(pwd_value, sae->tmp->prime_len);
-       if (x == NULL)
++      *ret_x_cand = NULL;
 +
 +      wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-seed", pwd_seed, SHA256_MAC_LEN);
 +
 +      /* pwd-value = KDF-z(pwd-seed, "SAE Hunting and Pecking", p) */
 +      bits = crypto_ec_prime_len_bits(sae->tmp->ec);
 +      sha256_prf_bits(pwd_seed, SHA256_MAC_LEN, "SAE Hunting and Pecking",
 +                      prime, sae->tmp->prime_len, pwd_value, bits);
 +      if (bits % 8)
 +              buf_shift_right(pwd_value, sizeof(pwd_value), 8 - bits % 8);
 +      wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-value",
 +                      pwd_value, sae->tmp->prime_len);
 +
 +      if (os_memcmp(pwd_value, prime, sae->tmp->prime_len) >= 0)
 +              return 0;
 +
-       if (crypto_ec_point_solve_y_coord(sae->tmp->ec, pwe, x, y_bit) < 0) {
-               crypto_bignum_deinit(x, 0);
-               wpa_printf(MSG_DEBUG, "SAE: No solution found");
-               return 0;
++      x_cand = crypto_bignum_init_set(pwd_value, sae->tmp->prime_len);
++      if (!x_cand)
++              return -1;
++      y_sqr = crypto_ec_point_compute_y_sqr(sae->tmp->ec, x_cand);
++      if (!y_sqr) {
++              crypto_bignum_deinit(x_cand, 1);
 +              return -1;
-       crypto_bignum_deinit(x, 0);
 +      }
-       wpa_printf(MSG_DEBUG, "SAE: PWE found");
 +
-       u8 counter, k = 4;
++      res = is_quadratic_residue_blind(sae, prime, bits, qr, qnr, y_sqr);
++      crypto_bignum_deinit(y_sqr, 1);
++      if (res <= 0) {
++              crypto_bignum_deinit(x_cand, 1);
++              return res;
++      }
 +
++      *ret_x_cand = x_cand;
 +      return 1;
 +}
 +
 +
 +static int sae_test_pwd_seed_ffc(struct sae_data *sae, const u8 *pwd_seed,
 +                               struct crypto_bignum *pwe)
 +{
 +      u8 pwd_value[SAE_MAX_PRIME_LEN];
 +      size_t bits = sae->tmp->prime_len * 8;
 +      u8 exp[1];
 +      struct crypto_bignum *a, *b;
 +      int res;
 +
 +      wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-seed", pwd_seed, SHA256_MAC_LEN);
 +
 +      /* pwd-value = KDF-z(pwd-seed, "SAE Hunting and Pecking", p) */
 +      sha256_prf_bits(pwd_seed, SHA256_MAC_LEN, "SAE Hunting and Pecking",
 +                      sae->tmp->dh->prime, sae->tmp->prime_len, pwd_value,
 +                      bits);
 +      if (bits % 8)
 +              buf_shift_right(pwd_value, sizeof(pwd_value), 8 - bits % 8);
 +      wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-value", pwd_value,
 +                      sae->tmp->prime_len);
 +
 +      if (os_memcmp(pwd_value, sae->tmp->dh->prime, sae->tmp->prime_len) >= 0)
 +      {
 +              wpa_printf(MSG_DEBUG, "SAE: pwd-value >= p");
 +              return 0;
 +      }
 +
 +      /* PWE = pwd-value^((p-1)/r) modulo p */
 +
 +      a = crypto_bignum_init_set(pwd_value, sae->tmp->prime_len);
 +
 +      if (sae->tmp->dh->safe_prime) {
 +              /*
 +               * r = (p-1)/2 for the group used here, so this becomes:
 +               * PWE = pwd-value^2 modulo p
 +               */
 +              exp[0] = 2;
 +              b = crypto_bignum_init_set(exp, sizeof(exp));
 +      } else {
 +              /* Calculate exponent: (p-1)/r */
 +              exp[0] = 1;
 +              b = crypto_bignum_init_set(exp, sizeof(exp));
 +              if (b == NULL ||
 +                  crypto_bignum_sub(sae->tmp->prime, b, b) < 0 ||
 +                  crypto_bignum_div(b, sae->tmp->order, b) < 0) {
 +                      crypto_bignum_deinit(b, 0);
 +                      b = NULL;
 +              }
 +      }
 +
 +      if (a == NULL || b == NULL)
 +              res = -1;
 +      else
 +              res = crypto_bignum_exptmod(a, b, sae->tmp->prime, pwe);
 +
 +      crypto_bignum_deinit(a, 0);
 +      crypto_bignum_deinit(b, 0);
 +
 +      if (res < 0) {
 +              wpa_printf(MSG_DEBUG, "SAE: Failed to calculate PWE");
 +              return -1;
 +      }
 +
 +      /* if (PWE > 1) --> found */
 +      if (crypto_bignum_is_zero(pwe) || crypto_bignum_is_one(pwe)) {
 +              wpa_printf(MSG_DEBUG, "SAE: PWE <= 1");
 +              return 0;
 +      }
 +
 +      wpa_printf(MSG_DEBUG, "SAE: PWE found");
 +      return 1;
 +}
 +
 +
++static int get_random_qr_qnr(const u8 *prime, size_t prime_len,
++                           const struct crypto_bignum *prime_bn,
++                           size_t prime_bits, struct crypto_bignum **qr,
++                           struct crypto_bignum **qnr)
++{
++      *qr = NULL;
++      *qnr = NULL;
++
++      while (!(*qr) || !(*qnr)) {
++              u8 tmp[SAE_MAX_ECC_PRIME_LEN];
++              struct crypto_bignum *q;
++              int res;
++
++              if (random_get_bytes(tmp, prime_len) < 0)
++                      break;
++              if (prime_bits % 8)
++                      buf_shift_right(tmp, prime_len, 8 - prime_bits % 8);
++              if (os_memcmp(tmp, prime, prime_len) >= 0)
++                      continue;
++              q = crypto_bignum_init_set(tmp, prime_len);
++              if (!q)
++                      break;
++              res = crypto_bignum_legendre(q, prime_bn);
++
++              if (res == 1 && !(*qr))
++                      *qr = q;
++              else if (res == -1 && !(*qnr))
++                      *qnr = q;
++              else
++                      crypto_bignum_deinit(q, 0);
++      }
++
++      return (*qr && *qnr) ? 0 : -1;
++}
++
++
 +static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1,
 +                            const u8 *addr2, const u8 *password,
 +                            size_t password_len)
 +{
-       int found = 0;
-       struct crypto_ec_point *pwe_tmp;
++      u8 counter, k = 40;
 +      u8 addrs[2 * ETH_ALEN];
 +      const u8 *addr[2];
 +      size_t len[2];
-       if (sae->tmp->pwe_ecc == NULL) {
-               sae->tmp->pwe_ecc = crypto_ec_point_init(sae->tmp->ec);
-               if (sae->tmp->pwe_ecc == NULL)
-                       return -1;
-       }
-       pwe_tmp = crypto_ec_point_init(sae->tmp->ec);
-       if (pwe_tmp == NULL)
++      u8 dummy_password[32];
++      size_t dummy_password_len;
++      int pwd_seed_odd = 0;
++      u8 prime[SAE_MAX_ECC_PRIME_LEN];
++      size_t prime_len;
++      struct crypto_bignum *x = NULL, *qr, *qnr;
++      size_t bits;
++      int res;
 +
-        *              password || counter)
++      dummy_password_len = password_len;
++      if (dummy_password_len > sizeof(dummy_password))
++              dummy_password_len = sizeof(dummy_password);
++      if (random_get_bytes(dummy_password, dummy_password_len) < 0)
++              return -1;
++
++      prime_len = sae->tmp->prime_len;
++      if (crypto_bignum_to_bin(sae->tmp->prime, prime, sizeof(prime),
++                               prime_len) < 0)
++              return -1;
++      bits = crypto_ec_prime_len_bits(sae->tmp->ec);
++
++      /*
++       * Create a random quadratic residue (qr) and quadratic non-residue
++       * (qnr) modulo p for blinding purposes during the loop.
++       */
++      if (get_random_qr_qnr(prime, prime_len, sae->tmp->prime, bits,
++                            &qr, &qnr) < 0)
 +              return -1;
 +
 +      wpa_hexdump_ascii_key(MSG_DEBUG, "SAE: password",
 +                            password, password_len);
 +
 +      /*
 +       * H(salt, ikm) = HMAC-SHA256(salt, ikm)
++       * base = password
 +       * pwd-seed = H(MAX(STA-A-MAC, STA-B-MAC) || MIN(STA-A-MAC, STA-B-MAC),
-       for (counter = 1; counter < k || !found; counter++) {
++       *              base || counter)
 +       */
 +      sae_pwd_seed_key(addr1, addr2, addrs);
 +
 +      addr[0] = password;
 +      len[0] = password_len;
 +      addr[1] = &counter;
 +      len[1] = sizeof(counter);
 +
 +      /*
 +       * Continue for at least k iterations to protect against side-channel
 +       * attacks that attempt to determine the number of iterations required
 +       * in the loop.
 +       */
-               int res;
++      for (counter = 1; counter <= k || !x; counter++) {
 +              u8 pwd_seed[SHA256_MAC_LEN];
-                                           found ? pwe_tmp :
-                                           sae->tmp->pwe_ecc);
++              struct crypto_bignum *x_cand;
 +
 +              if (counter > 200) {
 +                      /* This should not happen in practice */
 +                      wpa_printf(MSG_DEBUG, "SAE: Failed to derive PWE");
 +                      break;
 +              }
 +
 +              wpa_printf(MSG_DEBUG, "SAE: counter = %u", counter);
 +              if (hmac_sha256_vector(addrs, sizeof(addrs), 2, addr, len,
 +                                     pwd_seed) < 0)
 +                      break;
++
 +              res = sae_test_pwd_seed_ecc(sae, pwd_seed,
-                       break;
-               if (res == 0)
-                       continue;
-               if (found) {
-                       wpa_printf(MSG_DEBUG, "SAE: Ignore this PWE (one was "
-                                  "already selected)");
-               } else {
-                       wpa_printf(MSG_DEBUG, "SAE: Use this PWE");
-                       found = 1;
++                                          prime, qr, qnr, &x_cand);
 +              if (res < 0)
-       crypto_ec_point_deinit(pwe_tmp, 1);
++                      goto fail;
++              if (res > 0 && !x) {
++                      wpa_printf(MSG_DEBUG,
++                                 "SAE: Selected pwd-seed with counter %u",
++                                 counter);
++                      x = x_cand;
++                      pwd_seed_odd = pwd_seed[SHA256_MAC_LEN - 1] & 0x01;
++                      os_memset(pwd_seed, 0, sizeof(pwd_seed));
++
++                      /*
++                       * Use a dummy password for the following rounds, if
++                       * any.
++                       */
++                      addr[0] = dummy_password;
++                      len[0] = dummy_password_len;
++              } else if (res > 0) {
++                      crypto_bignum_deinit(x_cand, 1);
 +              }
 +      }
 +
-       return found ? 0 : -1;
++      if (!x) {
++              wpa_printf(MSG_DEBUG, "SAE: Could not generate PWE");
++              res = -1;
++              goto fail;
++      }
 +
-       mask = sae_get_rand_and_mask(sae);
-       if (mask == NULL) {
-               wpa_printf(MSG_DEBUG, "SAE: Could not get rand/mask");
-               return -1;
-       }
-       /* commit-scalar = (rand + mask) modulo r */
-       if (!sae->tmp->own_commit_scalar) {
-               sae->tmp->own_commit_scalar = crypto_bignum_init();
-               if (!sae->tmp->own_commit_scalar)
-                       goto fail;
-       }
-       crypto_bignum_add(sae->tmp->sae_rand, mask,
-                         sae->tmp->own_commit_scalar);
-       crypto_bignum_mod(sae->tmp->own_commit_scalar, sae->tmp->order,
-                         sae->tmp->own_commit_scalar);
++      if (!sae->tmp->pwe_ecc)
++              sae->tmp->pwe_ecc = crypto_ec_point_init(sae->tmp->ec);
++      if (!sae->tmp->pwe_ecc)
++              res = -1;
++      else
++              res = crypto_ec_point_solve_y_coord(sae->tmp->ec,
++                                                  sae->tmp->pwe_ecc, x,
++                                                  pwd_seed_odd);
++      crypto_bignum_deinit(x, 1);
++      if (res < 0) {
++              /*
++               * This should not happen since we already checked that there
++               * is a result.
++               */
++              wpa_printf(MSG_DEBUG, "SAE: Could not solve y");
++      }
++
++fail:
++      crypto_bignum_deinit(qr, 0);
++      crypto_bignum_deinit(qnr, 0);
++
++      return res;
 +}
 +
 +
 +static int sae_derive_pwe_ffc(struct sae_data *sae, const u8 *addr1,
 +                            const u8 *addr2, const u8 *password,
 +                            size_t password_len)
 +{
 +      u8 counter;
 +      u8 addrs[2 * ETH_ALEN];
 +      const u8 *addr[2];
 +      size_t len[2];
 +      int found = 0;
 +
 +      if (sae->tmp->pwe_ffc == NULL) {
 +              sae->tmp->pwe_ffc = crypto_bignum_init();
 +              if (sae->tmp->pwe_ffc == NULL)
 +                      return -1;
 +      }
 +
 +      wpa_hexdump_ascii_key(MSG_DEBUG, "SAE: password",
 +                            password, password_len);
 +
 +      /*
 +       * H(salt, ikm) = HMAC-SHA256(salt, ikm)
 +       * pwd-seed = H(MAX(STA-A-MAC, STA-B-MAC) || MIN(STA-A-MAC, STA-B-MAC),
 +       *              password || counter)
 +       */
 +      sae_pwd_seed_key(addr1, addr2, addrs);
 +
 +      addr[0] = password;
 +      len[0] = password_len;
 +      addr[1] = &counter;
 +      len[1] = sizeof(counter);
 +
 +      for (counter = 1; !found; counter++) {
 +              u8 pwd_seed[SHA256_MAC_LEN];
 +              int res;
 +
 +              if (counter > 200) {
 +                      /* This should not happen in practice */
 +                      wpa_printf(MSG_DEBUG, "SAE: Failed to derive PWE");
 +                      break;
 +              }
 +
 +              wpa_printf(MSG_DEBUG, "SAE: counter = %u", counter);
 +              if (hmac_sha256_vector(addrs, sizeof(addrs), 2, addr, len,
 +                                     pwd_seed) < 0)
 +                      break;
 +              res = sae_test_pwd_seed_ffc(sae, pwd_seed, sae->tmp->pwe_ffc);
 +              if (res < 0)
 +                      break;
 +              if (res > 0) {
 +                      wpa_printf(MSG_DEBUG, "SAE: Use this PWE");
 +                      found = 1;
 +              }
 +      }
 +
 +      return found ? 0 : -1;
 +}
 +
 +
 +static int sae_derive_commit_element_ecc(struct sae_data *sae,
 +                                       struct crypto_bignum *mask)
 +{
 +      /* COMMIT-ELEMENT = inverse(scalar-op(mask, PWE)) */
 +      if (!sae->tmp->own_commit_element_ecc) {
 +              sae->tmp->own_commit_element_ecc =
 +                      crypto_ec_point_init(sae->tmp->ec);
 +              if (!sae->tmp->own_commit_element_ecc)
 +                      return -1;
 +      }
 +
 +      if (crypto_ec_point_mul(sae->tmp->ec, sae->tmp->pwe_ecc, mask,
 +                              sae->tmp->own_commit_element_ecc) < 0 ||
 +          crypto_ec_point_invert(sae->tmp->ec,
 +                                 sae->tmp->own_commit_element_ecc) < 0) {
 +              wpa_printf(MSG_DEBUG, "SAE: Could not compute commit-element");
 +              return -1;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +static int sae_derive_commit_element_ffc(struct sae_data *sae,
 +                                       struct crypto_bignum *mask)
 +{
 +      /* COMMIT-ELEMENT = inverse(scalar-op(mask, PWE)) */
 +      if (!sae->tmp->own_commit_element_ffc) {
 +              sae->tmp->own_commit_element_ffc = crypto_bignum_init();
 +              if (!sae->tmp->own_commit_element_ffc)
 +                      return -1;
 +      }
 +
 +      if (crypto_bignum_exptmod(sae->tmp->pwe_ffc, mask, sae->tmp->prime,
 +                                sae->tmp->own_commit_element_ffc) < 0 ||
 +          crypto_bignum_inverse(sae->tmp->own_commit_element_ffc,
 +                                sae->tmp->prime,
 +                                sae->tmp->own_commit_element_ffc) < 0) {
 +              wpa_printf(MSG_DEBUG, "SAE: Could not compute commit-element");
 +              return -1;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +static int sae_derive_commit(struct sae_data *sae)
 +{
 +      struct crypto_bignum *mask;
 +      int ret = -1;
++      unsigned int counter = 0;
++
++      do {
++              counter++;
++              if (counter > 100) {
++                      /*
++                       * This cannot really happen in practice if the random
++                       * number generator is working. Anyway, to avoid even a
++                       * theoretical infinite loop, break out after 100
++                       * attemps.
++                       */
++                      return -1;
++              }
 +
-       if (sae->tmp->ec && sae_derive_commit_element_ecc(sae, mask) < 0)
-               goto fail;
-       if (sae->tmp->dh && sae_derive_commit_element_ffc(sae, mask) < 0)
++              mask = sae_get_rand_and_mask(sae);
++              if (mask == NULL) {
++                      wpa_printf(MSG_DEBUG, "SAE: Could not get rand/mask");
++                      return -1;
++              }
 +
-       if (sae->tmp == NULL)
-               return -1;
-       if (sae->tmp->ec && sae_derive_pwe_ecc(sae, addr1, addr2, password,
-                                         password_len) < 0)
-               return -1;
-       if (sae->tmp->dh && sae_derive_pwe_ffc(sae, addr1, addr2, password,
-                                         password_len) < 0)
-               return -1;
-       if (sae_derive_commit(sae) < 0)
++              /* commit-scalar = (rand + mask) modulo r */
++              if (!sae->tmp->own_commit_scalar) {
++                      sae->tmp->own_commit_scalar = crypto_bignum_init();
++                      if (!sae->tmp->own_commit_scalar)
++                              goto fail;
++              }
++              crypto_bignum_add(sae->tmp->sae_rand, mask,
++                                sae->tmp->own_commit_scalar);
++              crypto_bignum_mod(sae->tmp->own_commit_scalar, sae->tmp->order,
++                                sae->tmp->own_commit_scalar);
++      } while (crypto_bignum_is_zero(sae->tmp->own_commit_scalar) ||
++               crypto_bignum_is_one(sae->tmp->own_commit_scalar));
++
++      if ((sae->tmp->ec && sae_derive_commit_element_ecc(sae, mask) < 0) ||
++          (sae->tmp->dh && sae_derive_commit_element_ffc(sae, mask) < 0))
 +              goto fail;
 +
 +      ret = 0;
 +fail:
 +      crypto_bignum_deinit(mask, 1);
 +      return ret;
 +}
 +
 +
 +int sae_prepare_commit(const u8 *addr1, const u8 *addr2,
 +                     const u8 *password, size_t password_len,
 +                     struct sae_data *sae)
 +{
-       /* 0 < scalar < r */
++      if (sae->tmp == NULL ||
++          (sae->tmp->ec && sae_derive_pwe_ecc(sae, addr1, addr2, password,
++                                              password_len) < 0) ||
++          (sae->tmp->dh && sae_derive_pwe_ffc(sae, addr1, addr2, password,
++                                              password_len) < 0) ||
++          sae_derive_commit(sae) < 0)
 +              return -1;
 +      return 0;
 +}
 +
 +
 +static int sae_derive_k_ecc(struct sae_data *sae, u8 *k)
 +{
 +      struct crypto_ec_point *K;
 +      int ret = -1;
 +
 +      K = crypto_ec_point_init(sae->tmp->ec);
 +      if (K == NULL)
 +              goto fail;
 +
 +      /*
 +       * K = scalar-op(rand, (elem-op(scalar-op(peer-commit-scalar, PWE),
 +       *                                        PEER-COMMIT-ELEMENT)))
 +       * If K is identity element (point-at-infinity), reject
 +       * k = F(K) (= x coordinate)
 +       */
 +
 +      if (crypto_ec_point_mul(sae->tmp->ec, sae->tmp->pwe_ecc,
 +                              sae->peer_commit_scalar, K) < 0 ||
 +          crypto_ec_point_add(sae->tmp->ec, K,
 +                              sae->tmp->peer_commit_element_ecc, K) < 0 ||
 +          crypto_ec_point_mul(sae->tmp->ec, K, sae->tmp->sae_rand, K) < 0 ||
 +          crypto_ec_point_is_at_infinity(sae->tmp->ec, K) ||
 +          crypto_ec_point_to_bin(sae->tmp->ec, K, k, NULL) < 0) {
 +              wpa_printf(MSG_DEBUG, "SAE: Failed to calculate K and k");
 +              goto fail;
 +      }
 +
 +      wpa_hexdump_key(MSG_DEBUG, "SAE: k", k, sae->tmp->prime_len);
 +
 +      ret = 0;
 +fail:
 +      crypto_ec_point_deinit(K, 1);
 +      return ret;
 +}
 +
 +
 +static int sae_derive_k_ffc(struct sae_data *sae, u8 *k)
 +{
 +      struct crypto_bignum *K;
 +      int ret = -1;
 +
 +      K = crypto_bignum_init();
 +      if (K == NULL)
 +              goto fail;
 +
 +      /*
 +       * K = scalar-op(rand, (elem-op(scalar-op(peer-commit-scalar, PWE),
 +       *                                        PEER-COMMIT-ELEMENT)))
 +       * If K is identity element (one), reject.
 +       * k = F(K) (= x coordinate)
 +       */
 +
 +      if (crypto_bignum_exptmod(sae->tmp->pwe_ffc, sae->peer_commit_scalar,
 +                                sae->tmp->prime, K) < 0 ||
 +          crypto_bignum_mulmod(K, sae->tmp->peer_commit_element_ffc,
 +                               sae->tmp->prime, K) < 0 ||
 +          crypto_bignum_exptmod(K, sae->tmp->sae_rand, sae->tmp->prime, K) < 0
 +          ||
 +          crypto_bignum_is_one(K) ||
 +          crypto_bignum_to_bin(K, k, SAE_MAX_PRIME_LEN, sae->tmp->prime_len) <
 +          0) {
 +              wpa_printf(MSG_DEBUG, "SAE: Failed to calculate K and k");
 +              goto fail;
 +      }
 +
 +      wpa_hexdump_key(MSG_DEBUG, "SAE: k", k, sae->tmp->prime_len);
 +
 +      ret = 0;
 +fail:
 +      crypto_bignum_deinit(K, 1);
 +      return ret;
 +}
 +
 +
 +static int sae_derive_keys(struct sae_data *sae, const u8 *k)
 +{
 +      u8 null_key[SAE_KEYSEED_KEY_LEN], val[SAE_MAX_PRIME_LEN];
 +      u8 keyseed[SHA256_MAC_LEN];
 +      u8 keys[SAE_KCK_LEN + SAE_PMK_LEN];
 +      struct crypto_bignum *tmp;
 +      int ret = -1;
 +
 +      tmp = crypto_bignum_init();
 +      if (tmp == NULL)
 +              goto fail;
 +
 +      /* keyseed = H(<0>32, k)
 +       * KCK || PMK = KDF-512(keyseed, "SAE KCK and PMK",
 +       *                      (commit-scalar + peer-commit-scalar) modulo r)
 +       * PMKID = L((commit-scalar + peer-commit-scalar) modulo r, 0, 128)
 +       */
 +
 +      os_memset(null_key, 0, sizeof(null_key));
 +      hmac_sha256(null_key, sizeof(null_key), k, sae->tmp->prime_len,
 +                  keyseed);
 +      wpa_hexdump_key(MSG_DEBUG, "SAE: keyseed", keyseed, sizeof(keyseed));
 +
 +      crypto_bignum_add(sae->tmp->own_commit_scalar, sae->peer_commit_scalar,
 +                        tmp);
 +      crypto_bignum_mod(tmp, sae->tmp->order, tmp);
 +      crypto_bignum_to_bin(tmp, val, sizeof(val), sae->tmp->prime_len);
 +      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);
 +
 +      ret = 0;
 +fail:
 +      crypto_bignum_deinit(tmp, 0);
 +      return ret;
 +}
 +
 +
 +int sae_process_commit(struct sae_data *sae)
 +{
 +      u8 k[SAE_MAX_PRIME_LEN];
 +      if (sae->tmp == NULL ||
 +          (sae->tmp->ec && sae_derive_k_ecc(sae, k) < 0) ||
 +          (sae->tmp->dh && sae_derive_k_ffc(sae, k) < 0) ||
 +          sae_derive_keys(sae, k) < 0)
 +              return -1;
 +      return 0;
 +}
 +
 +
 +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) {
 +              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);
 +      wpa_hexdump(MSG_DEBUG, "SAE: own commit-scalar",
 +                  pos, sae->tmp->prime_len);
 +      if (sae->tmp->ec) {
 +              pos = wpabuf_put(buf, 2 * sae->tmp->prime_len);
 +              crypto_ec_point_to_bin(sae->tmp->ec,
 +                                     sae->tmp->own_commit_element_ecc,
 +                                     pos, pos + sae->tmp->prime_len);
 +              wpa_hexdump(MSG_DEBUG, "SAE: own commit-element(x)",
 +                          pos, sae->tmp->prime_len);
 +              wpa_hexdump(MSG_DEBUG, "SAE: own commit-element(y)",
 +                          pos + sae->tmp->prime_len, sae->tmp->prime_len);
 +      } else {
 +              pos = wpabuf_put(buf, sae->tmp->prime_len);
 +              crypto_bignum_to_bin(sae->tmp->own_commit_element_ffc, pos,
 +                                   sae->tmp->prime_len, sae->tmp->prime_len);
 +              wpa_hexdump(MSG_DEBUG, "SAE: own commit-element",
 +                          pos, sae->tmp->prime_len);
 +      }
 +}
 +
 +
 +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++) {
 +                      if (allowed_groups[i] == group)
 +                              break;
 +              }
 +              if (allowed_groups[i] != group) {
 +                      wpa_printf(MSG_DEBUG, "SAE: Proposed group %u not "
 +                                 "enabled in the current configuration",
 +                                 group);
 +                      return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
 +              }
 +      }
 +
 +      if (sae->state == SAE_COMMITTED && group != sae->group) {
 +              wpa_printf(MSG_DEBUG, "SAE: Do not allow group to be changed");
 +              return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
 +      }
 +
 +      if (group != sae->group && sae_set_group(sae, group) < 0) {
 +              wpa_printf(MSG_DEBUG, "SAE: Unsupported Finite Cyclic Group %u",
 +                         group);
 +              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);
 +              return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
 +      }
 +
 +      return WLAN_STATUS_SUCCESS;
 +}
 +
 +
 +static void sae_parse_commit_token(struct sae_data *sae, const u8 **pos,
 +                                 const u8 *end, const u8 **token,
 +                                 size_t *token_len)
 +{
 +      if (*pos + (sae->tmp->ec ? 3 : 2) * sae->tmp->prime_len < end) {
 +              size_t tlen = end - (*pos + (sae->tmp->ec ? 3 : 2) *
 +                                   sae->tmp->prime_len);
 +              wpa_hexdump(MSG_DEBUG, "SAE: Anti-Clogging Token", *pos, tlen);
 +              if (token)
 +                      *token = *pos;
 +              if (token_len)
 +                      *token_len = tlen;
 +              *pos += tlen;
 +      } else {
 +              if (token)
 +                      *token = NULL;
 +              if (token_len)
 +                      *token_len = 0;
 +      }
 +}
 +
 +
 +static u16 sae_parse_commit_scalar(struct sae_data *sae, const u8 **pos,
 +                                 const u8 *end)
 +{
 +      struct crypto_bignum *peer_scalar;
 +
 +      if (*pos + sae->tmp->prime_len > end) {
 +              wpa_printf(MSG_DEBUG, "SAE: Not enough data for scalar");
 +              return WLAN_STATUS_UNSPECIFIED_FAILURE;
 +      }
 +
 +      peer_scalar = crypto_bignum_init_set(*pos, sae->tmp->prime_len);
 +      if (peer_scalar == NULL)
 +              return WLAN_STATUS_UNSPECIFIED_FAILURE;
 +
 +      /*
 +       * IEEE Std 802.11-2012, 11.3.8.6.1: If there is a protocol instance for
 +       * the peer and it is in Authenticated state, the new Commit Message
 +       * shall be dropped if the peer-scalar is identical to the one used in
 +       * the existing protocol instance.
 +       */
 +      if (sae->state == SAE_ACCEPTED && sae->peer_commit_scalar &&
 +          crypto_bignum_cmp(sae->peer_commit_scalar, peer_scalar) == 0) {
 +              wpa_printf(MSG_DEBUG, "SAE: Do not accept re-use of previous "
 +                         "peer-commit-scalar");
 +              crypto_bignum_deinit(peer_scalar, 0);
 +              return WLAN_STATUS_UNSPECIFIED_FAILURE;
 +      }
 +
-       struct crypto_bignum *res;
++      /* 1 < scalar < r */
 +      if (crypto_bignum_is_zero(peer_scalar) ||
++          crypto_bignum_is_one(peer_scalar) ||
 +          crypto_bignum_cmp(peer_scalar, sae->tmp->order) >= 0) {
 +              wpa_printf(MSG_DEBUG, "SAE: Invalid peer scalar");
 +              crypto_bignum_deinit(peer_scalar, 0);
 +              return WLAN_STATUS_UNSPECIFIED_FAILURE;
 +      }
 +
 +
 +      crypto_bignum_deinit(sae->peer_commit_scalar, 0);
 +      sae->peer_commit_scalar = peer_scalar;
 +      wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-scalar",
 +                  *pos, sae->tmp->prime_len);
 +      *pos += sae->tmp->prime_len;
 +
 +      return WLAN_STATUS_SUCCESS;
 +}
 +
 +
 +static u16 sae_parse_commit_element_ecc(struct sae_data *sae, const u8 *pos,
 +                                      const u8 *end)
 +{
 +      u8 prime[SAE_MAX_ECC_PRIME_LEN];
 +
 +      if (pos + 2 * sae->tmp->prime_len > end) {
 +              wpa_printf(MSG_DEBUG, "SAE: Not enough data for "
 +                         "commit-element");
 +              return WLAN_STATUS_UNSPECIFIED_FAILURE;
 +      }
 +
 +      if (crypto_bignum_to_bin(sae->tmp->prime, prime, sizeof(prime),
 +                               sae->tmp->prime_len) < 0)
 +              return WLAN_STATUS_UNSPECIFIED_FAILURE;
 +
 +      /* element x and y coordinates < p */
 +      if (os_memcmp(pos, prime, sae->tmp->prime_len) >= 0 ||
 +          os_memcmp(pos + sae->tmp->prime_len, prime,
 +                    sae->tmp->prime_len) >= 0) {
 +              wpa_printf(MSG_DEBUG, "SAE: Invalid coordinates in peer "
 +                         "element");
 +              return WLAN_STATUS_UNSPECIFIED_FAILURE;
 +      }
 +
 +      wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-element(x)",
 +                  pos, sae->tmp->prime_len);
 +      wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-element(y)",
 +                  pos + sae->tmp->prime_len, sae->tmp->prime_len);
 +
 +      crypto_ec_point_deinit(sae->tmp->peer_commit_element_ecc, 0);
 +      sae->tmp->peer_commit_element_ecc =
 +              crypto_ec_point_from_bin(sae->tmp->ec, pos);
 +      if (sae->tmp->peer_commit_element_ecc == NULL)
 +              return WLAN_STATUS_UNSPECIFIED_FAILURE;
 +
 +      if (!crypto_ec_point_is_on_curve(sae->tmp->ec,
 +                                       sae->tmp->peer_commit_element_ecc)) {
 +              wpa_printf(MSG_DEBUG, "SAE: Peer element is not on curve");
 +              return WLAN_STATUS_UNSPECIFIED_FAILURE;
 +      }
 +
 +      return WLAN_STATUS_SUCCESS;
 +}
 +
 +
 +static u16 sae_parse_commit_element_ffc(struct sae_data *sae, const u8 *pos,
 +                                      const u8 *end)
 +{
-       if (crypto_bignum_is_zero(sae->tmp->peer_commit_element_ffc) ||
++      struct crypto_bignum *res, *one;
++      const u8 one_bin[1] = { 0x01 };
 +
 +      if (pos + sae->tmp->prime_len > end) {
 +              wpa_printf(MSG_DEBUG, "SAE: Not enough data for "
 +                         "commit-element");
 +              return WLAN_STATUS_UNSPECIFIED_FAILURE;
 +      }
 +      wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-element", pos,
 +                  sae->tmp->prime_len);
 +
 +      crypto_bignum_deinit(sae->tmp->peer_commit_element_ffc, 0);
 +      sae->tmp->peer_commit_element_ffc =
 +              crypto_bignum_init_set(pos, sae->tmp->prime_len);
 +      if (sae->tmp->peer_commit_element_ffc == NULL)
 +              return WLAN_STATUS_UNSPECIFIED_FAILURE;
-           crypto_bignum_cmp(sae->tmp->peer_commit_element_ffc,
-                             sae->tmp->prime) >= 0) {
++      /* 1 < element < p - 1 */
++      res = crypto_bignum_init();
++      one = crypto_bignum_init_set(one_bin, sizeof(one_bin));
++      if (!res || !one ||
++          crypto_bignum_sub(sae->tmp->prime, one, res) ||
++          crypto_bignum_is_zero(sae->tmp->peer_commit_element_ffc) ||
 +          crypto_bignum_is_one(sae->tmp->peer_commit_element_ffc) ||
-       res = crypto_bignum_init();
-       if (res == NULL ||
-           crypto_bignum_exptmod(sae->tmp->peer_commit_element_ffc,
++          crypto_bignum_cmp(sae->tmp->peer_commit_element_ffc, res) >= 0) {
++              crypto_bignum_deinit(res, 0);
++              crypto_bignum_deinit(one, 0);
 +              wpa_printf(MSG_DEBUG, "SAE: Invalid peer element");
 +              return WLAN_STATUS_UNSPECIFIED_FAILURE;
 +      }
++      crypto_bignum_deinit(one, 0);
 +
 +      /* scalar-op(r, ELEMENT) = 1 modulo p */
-       return sae_parse_commit_element(sae, pos, end);
++      if (crypto_bignum_exptmod(sae->tmp->peer_commit_element_ffc,
 +                                sae->tmp->order, sae->tmp->prime, res) < 0 ||
 +          !crypto_bignum_is_one(res)) {
 +              wpa_printf(MSG_DEBUG, "SAE: Invalid peer element (scalar-op)");
 +              crypto_bignum_deinit(res, 0);
 +              return WLAN_STATUS_UNSPECIFIED_FAILURE;
 +      }
 +      crypto_bignum_deinit(res, 0);
 +
 +      return WLAN_STATUS_SUCCESS;
 +}
 +
 +
 +static u16 sae_parse_commit_element(struct sae_data *sae, const u8 *pos,
 +                                  const u8 *end)
 +{
 +      if (sae->tmp->dh)
 +              return sae_parse_commit_element_ffc(sae, pos, end);
 +      return sae_parse_commit_element_ecc(sae, pos, end);
 +}
 +
 +
 +u16 sae_parse_commit(struct sae_data *sae, const u8 *data, size_t len,
 +                   const u8 **token, size_t *token_len, int *allowed_groups)
 +{
 +      const u8 *pos = data, *end = data + len;
 +      u16 res;
 +
 +      /* Check Finite Cyclic Group */
 +      if (pos + 2 > end)
 +              return WLAN_STATUS_UNSPECIFIED_FAILURE;
 +      res = sae_group_allowed(sae, allowed_groups, WPA_GET_LE16(pos));
 +      if (res != WLAN_STATUS_SUCCESS)
 +              return res;
 +      pos += 2;
 +
 +      /* Optional Anti-Clogging Token */
 +      sae_parse_commit_token(sae, &pos, end, token, token_len);
 +
 +      /* commit-scalar */
 +      res = sae_parse_commit_scalar(sae, &pos, end);
 +      if (res != WLAN_STATUS_SUCCESS)
 +              return res;
 +
 +      /* commit-element */
++      res = sae_parse_commit_element(sae, pos, end);
++      if (res != WLAN_STATUS_SUCCESS)
++              return res;
++
++      /*
++       * Check whether peer-commit-scalar and PEER-COMMIT-ELEMENT are same as
++       * the values we sent which would be evidence of a reflection attack.
++       */
++      if (!sae->tmp->own_commit_scalar ||
++          crypto_bignum_cmp(sae->tmp->own_commit_scalar,
++                            sae->peer_commit_scalar) != 0 ||
++          (sae->tmp->dh &&
++           (!sae->tmp->own_commit_element_ffc ||
++            crypto_bignum_cmp(sae->tmp->own_commit_element_ffc,
++                              sae->tmp->peer_commit_element_ffc) != 0)) ||
++          (sae->tmp->ec &&
++           (!sae->tmp->own_commit_element_ecc ||
++            crypto_ec_point_cmp(sae->tmp->ec,
++                                sae->tmp->own_commit_element_ecc,
++                                sae->tmp->peer_commit_element_ecc) != 0)))
++              return WLAN_STATUS_SUCCESS; /* scalars/elements are different */
++
++      /*
++       * This is a reflection attack - return special value to trigger caller
++       * to silently discard the frame instead of replying with a specific
++       * status code.
++       */
++      return SAE_SILENTLY_DISCARD;
 +}
 +
 +
 +static void sae_cn_confirm(struct sae_data *sae, const u8 *sc,
 +                         const struct crypto_bignum *scalar1,
 +                         const u8 *element1, size_t element1_len,
 +                         const struct crypto_bignum *scalar2,
 +                         const u8 *element2, size_t element2_len,
 +                         u8 *confirm)
 +{
 +      const u8 *addr[5];
 +      size_t len[5];
 +      u8 scalar_b1[SAE_MAX_PRIME_LEN], scalar_b2[SAE_MAX_PRIME_LEN];
 +
 +      /* Confirm
 +       * CN(key, X, Y, Z, ...) =
 +       *    HMAC-SHA256(key, D2OS(X) || D2OS(Y) || D2OS(Z) | ...)
 +       * confirm = CN(KCK, send-confirm, commit-scalar, COMMIT-ELEMENT,
 +       *              peer-commit-scalar, PEER-COMMIT-ELEMENT)
 +       * verifier = CN(KCK, peer-send-confirm, peer-commit-scalar,
 +       *               PEER-COMMIT-ELEMENT, commit-scalar, COMMIT-ELEMENT)
 +       */
 +      addr[0] = sc;
 +      len[0] = 2;
 +      crypto_bignum_to_bin(scalar1, scalar_b1, sizeof(scalar_b1),
 +                           sae->tmp->prime_len);
 +      addr[1] = scalar_b1;
 +      len[1] = sae->tmp->prime_len;
 +      addr[2] = element1;
 +      len[2] = element1_len;
 +      crypto_bignum_to_bin(scalar2, scalar_b2, sizeof(scalar_b2),
 +                           sae->tmp->prime_len);
 +      addr[3] = scalar_b2;
 +      len[3] = sae->tmp->prime_len;
 +      addr[4] = element2;
 +      len[4] = element2_len;
 +      hmac_sha256_vector(sae->tmp->kck, sizeof(sae->tmp->kck), 5, addr, len,
 +                         confirm);
 +}
 +
 +
 +static void sae_cn_confirm_ecc(struct sae_data *sae, const u8 *sc,
 +                             const struct crypto_bignum *scalar1,
 +                             const struct crypto_ec_point *element1,
 +                             const struct crypto_bignum *scalar2,
 +                             const struct crypto_ec_point *element2,
 +                             u8 *confirm)
 +{
 +      u8 element_b1[2 * SAE_MAX_ECC_PRIME_LEN];
 +      u8 element_b2[2 * SAE_MAX_ECC_PRIME_LEN];
 +
 +      crypto_ec_point_to_bin(sae->tmp->ec, element1, element_b1,
 +                             element_b1 + sae->tmp->prime_len);
 +      crypto_ec_point_to_bin(sae->tmp->ec, element2, element_b2,
 +                             element_b2 + sae->tmp->prime_len);
 +
 +      sae_cn_confirm(sae, sc, scalar1, element_b1, 2 * sae->tmp->prime_len,
 +                     scalar2, element_b2, 2 * sae->tmp->prime_len, confirm);
 +}
 +
 +
 +static void sae_cn_confirm_ffc(struct sae_data *sae, const u8 *sc,
 +                             const struct crypto_bignum *scalar1,
 +                             const struct crypto_bignum *element1,
 +                             const struct crypto_bignum *scalar2,
 +                             const struct crypto_bignum *element2,
 +                             u8 *confirm)
 +{
 +      u8 element_b1[SAE_MAX_PRIME_LEN];
 +      u8 element_b2[SAE_MAX_PRIME_LEN];
 +
 +      crypto_bignum_to_bin(element1, element_b1, sizeof(element_b1),
 +                           sae->tmp->prime_len);
 +      crypto_bignum_to_bin(element2, element_b2, sizeof(element_b2),
 +                           sae->tmp->prime_len);
 +
 +      sae_cn_confirm(sae, sc, scalar1, element_b1, sae->tmp->prime_len,
 +                     scalar2, element_b2, sae->tmp->prime_len, confirm);
 +}
 +
 +
 +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);
 +      sae->send_confirm++;
 +
 +      if (sae->tmp->ec)
 +              sae_cn_confirm_ecc(sae, sc, sae->tmp->own_commit_scalar,
 +                                 sae->tmp->own_commit_element_ecc,
 +                                 sae->peer_commit_scalar,
 +                                 sae->tmp->peer_commit_element_ecc,
 +                                 wpabuf_put(buf, SHA256_MAC_LEN));
 +      else
 +              sae_cn_confirm_ffc(sae, sc, sae->tmp->own_commit_scalar,
 +                                 sae->tmp->own_commit_element_ffc,
 +                                 sae->peer_commit_scalar,
 +                                 sae->tmp->peer_commit_element_ffc,
 +                                 wpabuf_put(buf, SHA256_MAC_LEN));
 +}
 +
 +
 +int sae_check_confirm(struct sae_data *sae, const u8 *data, size_t len)
 +{
 +      u8 verifier[SHA256_MAC_LEN];
 +
 +      if (len < 2 + SHA256_MAC_LEN) {
 +              wpa_printf(MSG_DEBUG, "SAE: Too short confirm message");
 +              return -1;
 +      }
 +
 +      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,
 +                                 sae->tmp->own_commit_scalar,
 +                                 sae->tmp->own_commit_element_ecc,
 +                                 verifier);
 +      else
 +              sae_cn_confirm_ffc(sae, data, sae->peer_commit_scalar,
 +                                 sae->tmp->peer_commit_element_ffc,
 +                                 sae->tmp->own_commit_scalar,
 +                                 sae->tmp->own_commit_element_ffc,
 +                                 verifier);
 +
 +      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);
 +              wpa_hexdump(MSG_DEBUG, "SAE: Calculated verifier",
 +                          verifier, SHA256_MAC_LEN);
 +              return -1;
 +      }
 +
 +      return 0;
 +}
index 3ebf40cf4a45d795093a6728053125280515aaae,0000000000000000000000000000000000000000..c07026cd497ccaa2de1cb0643776131d39ec102d
mode 100644,000000..100644
--- /dev/null
@@@ -1,67 -1,0 +1,70 @@@
 +/*
 + * Simultaneous authentication of equals
 + * 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.
 + */
 +
 +#ifndef SAE_H
 +#define SAE_H
 +
 +#define SAE_KCK_LEN 32
 +#define SAE_PMK_LEN 32
 +#define SAE_PMKID_LEN 16
 +#define SAE_KEYSEED_KEY_LEN 32
 +#define SAE_MAX_PRIME_LEN 512
 +#define SAE_MAX_ECC_PRIME_LEN 66
 +#define SAE_COMMIT_MAX_LEN (2 + 3 * SAE_MAX_PRIME_LEN)
 +#define SAE_CONFIRM_MAX_LEN (2 + SAE_MAX_PRIME_LEN)
 +
++/* Special value returned by sae_parse_commit() */
++#define SAE_SILENTLY_DISCARD 65535
++
 +struct sae_temporary_data {
 +      u8 kck[SAE_KCK_LEN];
 +      struct crypto_bignum *own_commit_scalar;
 +      struct crypto_bignum *own_commit_element_ffc;
 +      struct crypto_ec_point *own_commit_element_ecc;
 +      struct crypto_bignum *peer_commit_element_ffc;
 +      struct crypto_ec_point *peer_commit_element_ecc;
 +      struct crypto_ec_point *pwe_ecc;
 +      struct crypto_bignum *pwe_ffc;
 +      struct crypto_bignum *sae_rand;
 +      struct crypto_ec *ec;
 +      int prime_len;
 +      const struct dh_group *dh;
 +      const struct crypto_bignum *prime;
 +      const struct crypto_bignum *order;
 +      struct crypto_bignum *prime_buf;
 +      struct crypto_bignum *order_buf;
 +      struct wpabuf *anti_clogging_token;
 +};
 +
 +struct sae_data {
 +      enum { SAE_NOTHING, SAE_COMMITTED, SAE_CONFIRMED, SAE_ACCEPTED } state;
 +      u16 send_confirm;
 +      u8 pmk[SAE_PMK_LEN];
 +      struct crypto_bignum *peer_commit_scalar;
 +      int group;
 +      int sync;
 +      struct sae_temporary_data *tmp;
 +};
 +
 +int sae_set_group(struct sae_data *sae, int group);
 +void sae_clear_temp_data(struct sae_data *sae);
 +void sae_clear_data(struct sae_data *sae);
 +
 +int sae_prepare_commit(const u8 *addr1, const u8 *addr2,
 +                     const u8 *password, size_t password_len,
 +                     struct sae_data *sae);
 +int sae_process_commit(struct sae_data *sae);
 +void sae_write_commit(struct sae_data *sae, struct wpabuf *buf,
 +                    const struct wpabuf *token);
 +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 */
index e39a8dbf92e79800a21a46bfc6042a3fdf4a8fc6,0000000000000000000000000000000000000000..a5cc5b7b5bccb96698fb3bf06a57ae32444b50a0
mode 100644,000000..100644
--- /dev/null
@@@ -1,10 -1,0 +1,10 @@@
- #define VERSION_STR "2.4" VERSION_STR_POSTFIX
 +#ifndef VERSION_H
 +#define VERSION_H
 +
 +#ifndef VERSION_STR_POSTFIX
 +#define VERSION_STR_POSTFIX ""
 +#endif /* VERSION_STR_POSTFIX */
 +
++#define VERSION_STR "2.5" VERSION_STR_POSTFIX
 +
 +#endif /* VERSION_H */
index de81d53694c2910c3a1e4c0b69e0f99fa2f7da3c,0000000000000000000000000000000000000000..e9d4248d72d4545805b9ce95d81a23f174d687e3
mode 100644,000000..100644
--- /dev/null
@@@ -1,1676 -1,0 +1,1664 @@@
-       u8 *buf, *pos;
-       size_t buf_len;
 +/*
 + * WPA/RSN - Shared functions for supplicant and authenticator
 + * 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.
 + */
 +
 +#include "includes.h"
 +
 +#include "common.h"
 +#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 "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)
 + * @mic: Pointer to the buffer to which the EAPOL-Key MIC is written
 + * Returns: 0 on success, -1 on failure
 + *
 + * Calculate EAPOL-Key MIC for an EAPOL-Key packet. The EAPOL-Key MIC field has
 + * to be cleared (all zeroes) when calling this function.
 + *
 + * Note: 'IEEE Std 802.11i-2004 - 8.5.2 EAPOL-Key frames' has an error in the
 + * description of the Key MIC calculation. It includes packet data from the
 + * beginning of the EAPOL-Key header, not EAPOL header. This incorrect change
 + * happened during final editing of the standard and the correct behavior is
 + * defined in the last draft (IEEE 802.11i/D10).
 + */
 +int wpa_eapol_key_mic(const u8 *key, size_t key_len, int akmp, int ver,
 +                    const u8 *buf, size_t len, u8 *mic)
 +{
 +      u8 hash[SHA384_MAC_LEN];
 +
 +      switch (ver) {
 +#ifndef CONFIG_FIPS
 +      case WPA_KEY_INFO_TYPE_HMAC_MD5_RC4:
 +              return hmac_md5(key, key_len, buf, len, mic);
 +#endif /* CONFIG_FIPS */
 +      case WPA_KEY_INFO_TYPE_HMAC_SHA1_AES:
 +              if (hmac_sha1(key, key_len, buf, len, hash))
 +                      return -1;
 +              os_memcpy(mic, hash, MD5_MAC_LEN);
 +              break;
 +#if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W)
 +      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;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +/**
 + * wpa_pmk_to_ptk - Calculate PTK from PMK, addresses, and nonces
 + * @pmk: Pairwise master key
 + * @pmk_len: Length of PMK
 + * @label: Label to use in derivation
 + * @addr1: AA or SA
 + * @addr2: SA or AA
 + * @nonce1: ANonce or SNonce
 + * @nonce2: SNonce or ANonce
 + * @ptk: Buffer for pairwise transient key
 + * @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",
 + *             Min(AA, SA) || Max(AA, SA) ||
 + *             Min(ANonce, SNonce) || Max(ANonce, SNonce))
 + *
 + * STK = PRF-X(SMK, "Peer key expansion",
 + *             Min(MAC_I, MAC_P) || Max(MAC_I, MAC_P) ||
 + *             Min(INonce, PNonce) || Max(INonce, PNonce))
 + */
 +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);
 +              os_memcpy(data + ETH_ALEN, addr2, ETH_ALEN);
 +      } else {
 +              os_memcpy(data, addr2, ETH_ALEN);
 +              os_memcpy(data + ETH_ALEN, addr1, ETH_ALEN);
 +      }
 +
 +      if (os_memcmp(nonce1, nonce2, WPA_NONCE_LEN) < 0) {
 +              os_memcpy(data + 2 * ETH_ALEN, nonce1, WPA_NONCE_LEN);
 +              os_memcpy(data + 2 * ETH_ALEN + WPA_NONCE_LEN, nonce2,
 +                        WPA_NONCE_LEN);
 +      } else {
 +              os_memcpy(data + 2 * ETH_ALEN, nonce2, WPA_NONCE_LEN);
 +              os_memcpy(data + 2 * ETH_ALEN + WPA_NONCE_LEN, nonce1,
 +                        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_SUITEB192
++      if (wpa_key_mgmt_sha384(akmp))
++              sha384_prf(pmk, pmk_len, label, data, sizeof(data),
++                         tmp, ptk_len);
++      else
++#endif /* CONFIG_SUITEB192 */
 +#ifdef CONFIG_IEEE80211W
 +      if (wpa_key_mgmt_sha256(akmp))
 +              sha256_prf(pmk, pmk_len, label, data, sizeof(data),
 +                         tmp, ptk_len);
 +      else
 +#endif /* CONFIG_IEEE80211W */
 +              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", 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, 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)
 +{
-       buf_len = 2 * ETH_ALEN + 1 + mdie_len + ftie_len + rsnie_len + ric_len;
-       buf = os_malloc(buf_len);
-       if (buf == NULL)
-               return -1;
++      const u8 *addr[9];
++      size_t len[9];
++      size_t i, num_elem = 0;
++      u8 zero_mic[16];
 +
 +      if (kck_len != 16) {
 +              wpa_printf(MSG_WARNING, "FT: Unsupported KCK length %u",
 +                         (unsigned int) kck_len);
 +              return -1;
 +      }
 +
-       pos = buf;
-       os_memcpy(pos, sta_addr, ETH_ALEN);
-       pos += ETH_ALEN;
-       os_memcpy(pos, ap_addr, ETH_ALEN);
-       pos += ETH_ALEN;
-       *pos++ = transaction_seqnum;
++      addr[num_elem] = sta_addr;
++      len[num_elem] = ETH_ALEN;
++      num_elem++;
++
++      addr[num_elem] = ap_addr;
++      len[num_elem] = ETH_ALEN;
++      num_elem++;
++
++      addr[num_elem] = &transaction_seqnum;
++      len[num_elem] = 1;
++      num_elem++;
 +
-               os_memcpy(pos, rsnie, rsnie_len);
-               pos += rsnie_len;
 +      if (rsnie) {
-               os_memcpy(pos, mdie, mdie_len);
-               pos += mdie_len;
++              addr[num_elem] = rsnie;
++              len[num_elem] = rsnie_len;
++              num_elem++;
 +      }
 +      if (mdie) {
-               struct rsn_ftie *_ftie;
-               os_memcpy(pos, ftie, ftie_len);
-               if (ftie_len < 2 + sizeof(*_ftie)) {
-                       os_free(buf);
++              addr[num_elem] = mdie;
++              len[num_elem] = mdie_len;
++              num_elem++;
 +      }
 +      if (ftie) {
-               }
-               _ftie = (struct rsn_ftie *) (pos + 2);
-               os_memset(_ftie->mic, 0, sizeof(_ftie->mic));
-               pos += ftie_len;
++              if (ftie_len < 2 + sizeof(struct rsn_ftie))
 +                      return -1;
-               os_memcpy(pos, ric, ric_len);
-               pos += ric_len;
++
++              /* IE hdr and mic_control */
++              addr[num_elem] = ftie;
++              len[num_elem] = 2 + 2;
++              num_elem++;
++
++              /* MIC field with all zeros */
++              os_memset(zero_mic, 0, sizeof(zero_mic));
++              addr[num_elem] = zero_mic;
++              len[num_elem] = sizeof(zero_mic);
++              num_elem++;
++
++              /* Rest of FTIE */
++              addr[num_elem] = ftie + 2 + 2 + 16;
++              len[num_elem] = ftie_len - (2 + 2 + 16);
++              num_elem++;
 +      }
 +      if (ric) {
-       wpa_hexdump(MSG_MSGDUMP, "FT: MIC data", buf, pos - buf);
-       if (omac1_aes_128(kck, buf, pos - buf, mic)) {
-               os_free(buf);
++              addr[num_elem] = ric;
++              len[num_elem] = ric_len;
++              num_elem++;
 +      }
 +
-       }
-       os_free(buf);
++      for (i = 0; i < num_elem; i++)
++              wpa_hexdump(MSG_MSGDUMP, "FT: MIC data", addr[i], len[i]);
++      if (omac1_aes_128_vector(kck, num_elem, addr, len, mic))
 +              return -1;
-       if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_WEP40)
-               return WPA_CIPHER_WEP40;
 +
 +      return 0;
 +}
 +
 +
 +static int wpa_ft_parse_ftie(const u8 *ie, size_t ie_len,
 +                           struct wpa_ft_ies *parse)
 +{
 +      const u8 *end, *pos;
 +
 +      parse->ftie = ie;
 +      parse->ftie_len = ie_len;
 +
 +      pos = ie + sizeof(struct rsn_ftie);
 +      end = ie + ie_len;
 +
 +      while (pos + 2 <= end && pos + 2 + pos[1] <= end) {
 +              switch (pos[0]) {
 +              case FTIE_SUBELEM_R1KH_ID:
 +                      if (pos[1] != FT_R1KH_ID_LEN) {
 +                              wpa_printf(MSG_DEBUG, "FT: Invalid R1KH-ID "
 +                                         "length in FTIE: %d", pos[1]);
 +                              return -1;
 +                      }
 +                      parse->r1kh_id = pos + 2;
 +                      break;
 +              case FTIE_SUBELEM_GTK:
 +                      parse->gtk = pos + 2;
 +                      parse->gtk_len = pos[1];
 +                      break;
 +              case FTIE_SUBELEM_R0KH_ID:
 +                      if (pos[1] < 1 || pos[1] > FT_R0KH_ID_MAX_LEN) {
 +                              wpa_printf(MSG_DEBUG, "FT: Invalid R0KH-ID "
 +                                         "length in FTIE: %d", pos[1]);
 +                              return -1;
 +                      }
 +                      parse->r0kh_id = pos + 2;
 +                      parse->r0kh_id_len = pos[1];
 +                      break;
 +#ifdef CONFIG_IEEE80211W
 +              case FTIE_SUBELEM_IGTK:
 +                      parse->igtk = pos + 2;
 +                      parse->igtk_len = pos[1];
 +                      break;
 +#endif /* CONFIG_IEEE80211W */
 +              }
 +
 +              pos += 2 + pos[1];
 +      }
 +
 +      return 0;
 +}
 +
 +
 +int wpa_ft_parse_ies(const u8 *ies, size_t ies_len,
 +                   struct wpa_ft_ies *parse)
 +{
 +      const u8 *end, *pos;
 +      struct wpa_ie_data data;
 +      int ret;
 +      const struct rsn_ftie *ftie;
 +      int prot_ie_count = 0;
 +
 +      os_memset(parse, 0, sizeof(*parse));
 +      if (ies == NULL)
 +              return 0;
 +
 +      pos = ies;
 +      end = ies + ies_len;
 +      while (pos + 2 <= end && pos + 2 + pos[1] <= end) {
 +              switch (pos[0]) {
 +              case WLAN_EID_RSN:
 +                      parse->rsn = pos + 2;
 +                      parse->rsn_len = pos[1];
 +                      ret = wpa_parse_wpa_ie_rsn(parse->rsn - 2,
 +                                                 parse->rsn_len + 2,
 +                                                 &data);
 +                      if (ret < 0) {
 +                              wpa_printf(MSG_DEBUG, "FT: Failed to parse "
 +                                         "RSN IE: %d", ret);
 +                              return -1;
 +                      }
 +                      if (data.num_pmkid == 1 && data.pmkid)
 +                              parse->rsn_pmkid = data.pmkid;
 +                      break;
 +              case WLAN_EID_MOBILITY_DOMAIN:
++                      if (pos[1] < sizeof(struct rsn_mdie))
++                              return -1;
 +                      parse->mdie = pos + 2;
 +                      parse->mdie_len = pos[1];
 +                      break;
 +              case WLAN_EID_FAST_BSS_TRANSITION:
 +                      if (pos[1] < sizeof(*ftie))
 +                              return -1;
 +                      ftie = (const struct rsn_ftie *) (pos + 2);
 +                      prot_ie_count = ftie->mic_control[1];
 +                      if (wpa_ft_parse_ftie(pos + 2, pos[1], parse) < 0)
 +                              return -1;
 +                      break;
 +              case WLAN_EID_TIMEOUT_INTERVAL:
++                      if (pos[1] != 5)
++                              break;
 +                      parse->tie = pos + 2;
 +                      parse->tie_len = pos[1];
 +                      break;
 +              case WLAN_EID_RIC_DATA:
 +                      if (parse->ric == NULL)
 +                              parse->ric = pos;
 +                      break;
 +              }
 +
 +              pos += 2 + pos[1];
 +      }
 +
 +      if (prot_ie_count == 0)
 +              return 0; /* no MIC */
 +
 +      /*
 +       * Check that the protected IE count matches with IEs included in the
 +       * frame.
 +       */
 +      if (parse->rsn)
 +              prot_ie_count--;
 +      if (parse->mdie)
 +              prot_ie_count--;
 +      if (parse->ftie)
 +              prot_ie_count--;
 +      if (prot_ie_count < 0) {
 +              wpa_printf(MSG_DEBUG, "FT: Some required IEs not included in "
 +                         "the protected IE count");
 +              return -1;
 +      }
 +
 +      if (prot_ie_count == 0 && parse->ric) {
 +              wpa_printf(MSG_DEBUG, "FT: RIC IE(s) in the frame, but not "
 +                         "included in protected IE count");
 +              return -1;
 +      }
 +
 +      /* Determine the end of the RIC IE(s) */
 +      pos = parse->ric;
 +      while (pos && pos + 2 <= end && pos + 2 + pos[1] <= end &&
 +             prot_ie_count) {
 +              prot_ie_count--;
 +              pos += 2 + pos[1];
 +      }
 +      parse->ric_len = pos - parse->ric;
 +      if (prot_ie_count) {
 +              wpa_printf(MSG_DEBUG, "FT: %d protected IEs missing from "
 +                         "frame", (int) prot_ie_count);
 +              return -1;
 +      }
 +
 +      return 0;
 +}
 +#endif /* CONFIG_IEEE80211R */
 +
 +
 +static int rsn_selector_to_bitfield(const u8 *s)
 +{
 +      if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_NONE)
 +              return WPA_CIPHER_NONE;
-       if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_WEP104)
-               return WPA_CIPHER_WEP104;
 +      if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_TKIP)
 +              return WPA_CIPHER_TKIP;
 +      if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_CCMP)
 +              return WPA_CIPHER_CCMP;
- static int wpa_cipher_valid_group(int cipher)
 +#ifdef CONFIG_IEEE80211W
 +      if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_AES_128_CMAC)
 +              return WPA_CIPHER_AES_128_CMAC;
 +#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;
 +}
 +
 +
 +static int rsn_key_mgmt_to_bitfield(const u8 *s)
 +{
 +      if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_UNSPEC_802_1X)
 +              return WPA_KEY_MGMT_IEEE8021X;
 +      if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X)
 +              return WPA_KEY_MGMT_PSK;
 +#ifdef CONFIG_IEEE80211R
 +      if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_FT_802_1X)
 +              return WPA_KEY_MGMT_FT_IEEE8021X;
 +      if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_FT_PSK)
 +              return WPA_KEY_MGMT_FT_PSK;
 +#endif /* CONFIG_IEEE80211R */
 +#ifdef CONFIG_IEEE80211W
 +      if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_802_1X_SHA256)
 +              return WPA_KEY_MGMT_IEEE8021X_SHA256;
 +      if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_PSK_SHA256)
 +              return WPA_KEY_MGMT_PSK_SHA256;
 +#endif /* CONFIG_IEEE80211W */
 +#ifdef CONFIG_SAE
 +      if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_SAE)
 +              return WPA_KEY_MGMT_SAE;
 +      if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_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;
++      if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_OSEN)
++              return WPA_KEY_MGMT_OSEN;
 +      return 0;
 +}
 +
 +
-               cipher == WPA_CIPHER_WEP104 ||
-               cipher == WPA_CIPHER_WEP40 ||
++int wpa_cipher_valid_group(int cipher)
 +{
 +      return wpa_cipher_valid_pairwise(cipher) ||
-       const struct rsn_ie_hdr *hdr;
 +              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
 + * @rsn_ie_len: RSN IE buffer length (including IE number and length octets)
 + * @data: Pointer to structure that will be filled in with parsed data
 + * Returns: 0 on success, <0 on failure
 + */
 +int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_len,
 +                       struct wpa_ie_data *data)
 +{
-       hdr = (const struct rsn_ie_hdr *) rsn_ie;
 +      const u8 *pos;
 +      int left;
 +      int i, count;
 +
 +      os_memset(data, 0, sizeof(*data));
 +      data->proto = WPA_PROTO_RSN;
 +      data->pairwise_cipher = WPA_CIPHER_CCMP;
 +      data->group_cipher = WPA_CIPHER_CCMP;
 +      data->key_mgmt = WPA_KEY_MGMT_IEEE8021X;
 +      data->capabilities = 0;
 +      data->pmkid = NULL;
 +      data->num_pmkid = 0;
 +#ifdef CONFIG_IEEE80211W
 +      data->mgmt_group_cipher = WPA_CIPHER_AES_128_CMAC;
 +#else /* CONFIG_IEEE80211W */
 +      data->mgmt_group_cipher = 0;
 +#endif /* CONFIG_IEEE80211W */
 +
 +      if (rsn_ie_len == 0) {
 +              /* No RSN IE - fail silently */
 +              return -1;
 +      }
 +
 +      if (rsn_ie_len < sizeof(struct rsn_ie_hdr)) {
 +              wpa_printf(MSG_DEBUG, "%s: ie len too short %lu",
 +                         __func__, (unsigned long) rsn_ie_len);
 +              return -1;
 +      }
 +
-       if (hdr->elem_id != WLAN_EID_RSN ||
-           hdr->len != rsn_ie_len - 2 ||
-           WPA_GET_LE16(hdr->version) != RSN_VERSION) {
-               wpa_printf(MSG_DEBUG, "%s: malformed ie or unknown version",
-                          __func__);
-               return -2;
-       }
++      if (rsn_ie_len >= 6 && rsn_ie[1] >= 4 &&
++          rsn_ie[1] == rsn_ie_len - 2 &&
++          WPA_GET_BE32(&rsn_ie[2]) == OSEN_IE_VENDOR_TYPE) {
++              pos = rsn_ie + 6;
++              left = rsn_ie_len - 6;
 +
-       pos = (const u8 *) (hdr + 1);
-       left = rsn_ie_len - sizeof(*hdr);
++              data->proto = WPA_PROTO_OSEN;
++      } else {
++              const struct rsn_ie_hdr *hdr;
 +
-       if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_WEP40)
-               return WPA_CIPHER_WEP40;
++              hdr = (const struct rsn_ie_hdr *) rsn_ie;
++
++              if (hdr->elem_id != WLAN_EID_RSN ||
++                  hdr->len != rsn_ie_len - 2 ||
++                  WPA_GET_LE16(hdr->version) != RSN_VERSION) {
++                      wpa_printf(MSG_DEBUG, "%s: malformed ie or unknown version",
++                                 __func__);
++                      return -2;
++              }
++
++              pos = (const u8 *) (hdr + 1);
++              left = rsn_ie_len - sizeof(*hdr);
++      }
 +
 +      if (left >= RSN_SELECTOR_LEN) {
 +              data->group_cipher = rsn_selector_to_bitfield(pos);
 +              if (!wpa_cipher_valid_group(data->group_cipher)) {
 +                      wpa_printf(MSG_DEBUG, "%s: invalid group cipher 0x%x",
 +                                 __func__, data->group_cipher);
 +                      return -1;
 +              }
 +              pos += RSN_SELECTOR_LEN;
 +              left -= RSN_SELECTOR_LEN;
 +      } else if (left > 0) {
 +              wpa_printf(MSG_DEBUG, "%s: ie length mismatch, %u too much",
 +                         __func__, left);
 +              return -3;
 +      }
 +
 +      if (left >= 2) {
 +              data->pairwise_cipher = 0;
 +              count = WPA_GET_LE16(pos);
 +              pos += 2;
 +              left -= 2;
 +              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;
 +              }
 +              for (i = 0; i < count; i++) {
 +                      data->pairwise_cipher |= rsn_selector_to_bitfield(pos);
 +                      pos += RSN_SELECTOR_LEN;
 +                      left -= RSN_SELECTOR_LEN;
 +              }
 +#ifdef CONFIG_IEEE80211W
 +              if (data->pairwise_cipher & WPA_CIPHER_AES_128_CMAC) {
 +                      wpa_printf(MSG_DEBUG, "%s: AES-128-CMAC used as "
 +                                 "pairwise cipher", __func__);
 +                      return -1;
 +              }
 +#endif /* CONFIG_IEEE80211W */
 +      } else if (left == 1) {
 +              wpa_printf(MSG_DEBUG, "%s: ie too short (for key mgmt)",
 +                         __func__);
 +              return -5;
 +      }
 +
 +      if (left >= 2) {
 +              data->key_mgmt = 0;
 +              count = WPA_GET_LE16(pos);
 +              pos += 2;
 +              left -= 2;
 +              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;
 +              }
 +              for (i = 0; i < count; i++) {
 +                      data->key_mgmt |= rsn_key_mgmt_to_bitfield(pos);
 +                      pos += RSN_SELECTOR_LEN;
 +                      left -= RSN_SELECTOR_LEN;
 +              }
 +      } else if (left == 1) {
 +              wpa_printf(MSG_DEBUG, "%s: ie too short (for capabilities)",
 +                         __func__);
 +              return -7;
 +      }
 +
 +      if (left >= 2) {
 +              data->capabilities = WPA_GET_LE16(pos);
 +              pos += 2;
 +              left -= 2;
 +      }
 +
 +      if (left >= 2) {
 +              u16 num_pmkid = WPA_GET_LE16(pos);
 +              pos += 2;
 +              left -= 2;
 +              if (num_pmkid > (unsigned int) left / PMKID_LEN) {
 +                      wpa_printf(MSG_DEBUG, "%s: PMKID underflow "
 +                                 "(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;
 +              }
 +      }
 +
 +#ifdef CONFIG_IEEE80211W
 +      if (left >= 4) {
 +              data->mgmt_group_cipher = rsn_selector_to_bitfield(pos);
 +              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);
 +                      return -10;
 +              }
 +              pos += RSN_SELECTOR_LEN;
 +              left -= RSN_SELECTOR_LEN;
 +      }
 +#endif /* CONFIG_IEEE80211W */
 +
 +      if (left > 0) {
 +              wpa_hexdump(MSG_DEBUG,
 +                          "wpa_parse_wpa_ie_rsn: ignore trailing bytes",
 +                          pos, left);
 +      }
 +
 +      return 0;
 +}
 +
 +
 +static int wpa_selector_to_bitfield(const u8 *s)
 +{
 +      if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_NONE)
 +              return WPA_CIPHER_NONE;
-       if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_WEP104)
-               return WPA_CIPHER_WEP104;
 +      if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_TKIP)
 +              return WPA_CIPHER_TKIP;
 +      if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_CCMP)
 +              return WPA_CIPHER_CCMP;
-       if (wpa_ie_len == 0) {
-               /* No WPA IE - fail silently */
-               return -1;
-       }
 +      return 0;
 +}
 +
 +
 +static int wpa_key_mgmt_to_bitfield(const u8 *s)
 +{
 +      if (RSN_SELECTOR_GET(s) == WPA_AUTH_KEY_MGMT_UNSPEC_802_1X)
 +              return WPA_KEY_MGMT_IEEE8021X;
 +      if (RSN_SELECTOR_GET(s) == WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X)
 +              return WPA_KEY_MGMT_PSK;
 +      if (RSN_SELECTOR_GET(s) == WPA_AUTH_KEY_MGMT_NONE)
 +              return WPA_KEY_MGMT_WPA_NONE;
 +      return 0;
 +}
 +
 +
 +int wpa_parse_wpa_ie_wpa(const u8 *wpa_ie, size_t wpa_ie_len,
 +                       struct wpa_ie_data *data)
 +{
 +      const struct wpa_ie_hdr *hdr;
 +      const u8 *pos;
 +      int left;
 +      int i, count;
 +
 +      os_memset(data, 0, sizeof(*data));
 +      data->proto = WPA_PROTO_WPA;
 +      data->pairwise_cipher = WPA_CIPHER_TKIP;
 +      data->group_cipher = WPA_CIPHER_TKIP;
 +      data->key_mgmt = WPA_KEY_MGMT_IEEE8021X;
 +      data->capabilities = 0;
 +      data->pmkid = NULL;
 +      data->num_pmkid = 0;
 +      data->mgmt_group_cipher = 0;
 +
-       u8 buf[1 + WPA_MAX_SSID_LEN + MOBILITY_DOMAIN_ID_LEN + 1 +
 +      if (wpa_ie_len < sizeof(struct wpa_ie_hdr)) {
 +              wpa_printf(MSG_DEBUG, "%s: ie len too short %lu",
 +                         __func__, (unsigned long) wpa_ie_len);
 +              return -1;
 +      }
 +
 +      hdr = (const struct wpa_ie_hdr *) wpa_ie;
 +
 +      if (hdr->elem_id != WLAN_EID_VENDOR_SPECIFIC ||
 +          hdr->len != wpa_ie_len - 2 ||
 +          RSN_SELECTOR_GET(hdr->oui) != WPA_OUI_TYPE ||
 +          WPA_GET_LE16(hdr->version) != WPA_VERSION) {
 +              wpa_printf(MSG_DEBUG, "%s: malformed ie or unknown version",
 +                         __func__);
 +              return -2;
 +      }
 +
 +      pos = (const u8 *) (hdr + 1);
 +      left = wpa_ie_len - sizeof(*hdr);
 +
 +      if (left >= WPA_SELECTOR_LEN) {
 +              data->group_cipher = wpa_selector_to_bitfield(pos);
 +              pos += WPA_SELECTOR_LEN;
 +              left -= WPA_SELECTOR_LEN;
 +      } else if (left > 0) {
 +              wpa_printf(MSG_DEBUG, "%s: ie length mismatch, %u too much",
 +                         __func__, left);
 +              return -3;
 +      }
 +
 +      if (left >= 2) {
 +              data->pairwise_cipher = 0;
 +              count = WPA_GET_LE16(pos);
 +              pos += 2;
 +              left -= 2;
 +              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;
 +              }
 +              for (i = 0; i < count; i++) {
 +                      data->pairwise_cipher |= wpa_selector_to_bitfield(pos);
 +                      pos += WPA_SELECTOR_LEN;
 +                      left -= WPA_SELECTOR_LEN;
 +              }
 +      } else if (left == 1) {
 +              wpa_printf(MSG_DEBUG, "%s: ie too short (for key mgmt)",
 +                         __func__);
 +              return -5;
 +      }
 +
 +      if (left >= 2) {
 +              data->key_mgmt = 0;
 +              count = WPA_GET_LE16(pos);
 +              pos += 2;
 +              left -= 2;
 +              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;
 +              }
 +              for (i = 0; i < count; i++) {
 +                      data->key_mgmt |= wpa_key_mgmt_to_bitfield(pos);
 +                      pos += WPA_SELECTOR_LEN;
 +                      left -= WPA_SELECTOR_LEN;
 +              }
 +      } else if (left == 1) {
 +              wpa_printf(MSG_DEBUG, "%s: ie too short (for capabilities)",
 +                         __func__);
 +              return -7;
 +      }
 +
 +      if (left >= 2) {
 +              data->capabilities = WPA_GET_LE16(pos);
 +              pos += 2;
 +              left -= 2;
 +      }
 +
 +      if (left > 0) {
 +              wpa_hexdump(MSG_DEBUG,
 +                          "wpa_parse_wpa_ie_wpa: ignore trailing bytes",
 +                          pos, left);
 +      }
 +
 +      return 0;
 +}
 +
 +
 +#ifdef CONFIG_IEEE80211R
 +
 +/**
 + * wpa_derive_pmk_r0 - Derive PMK-R0 and PMKR0Name
 + *
 + * IEEE Std 802.11r-2008 - 8.5.1.5.3
 + */
 +void wpa_derive_pmk_r0(const u8 *xxkey, size_t xxkey_len,
 +                     const u8 *ssid, size_t ssid_len,
 +                     const u8 *mdid, const u8 *r0kh_id, size_t r0kh_id_len,
 +                     const u8 *s0kh_id, u8 *pmk_r0, u8 *pmk_r0_name)
 +{
-       if (ssid_len > WPA_MAX_SSID_LEN || r0kh_id_len > FT_R0KH_ID_MAX_LEN)
++      u8 buf[1 + SSID_MAX_LEN + MOBILITY_DOMAIN_ID_LEN + 1 +
 +             FT_R0KH_ID_MAX_LEN + ETH_ALEN];
 +      u8 *pos, r0_key_data[48], hash[32];
 +      const u8 *addr[2];
 +      size_t len[2];
 +
 +      /*
 +       * R0-Key-Data = KDF-384(XXKey, "FT-R0",
 +       *                       SSIDlength || SSID || MDID || R0KHlength ||
 +       *                       R0KH-ID || S0KH-ID)
 +       * XXKey is either the second 256 bits of MSK or PSK.
 +       * PMK-R0 = L(R0-Key-Data, 0, 256)
 +       * PMK-R0Name-Salt = L(R0-Key-Data, 256, 128)
 +       */
-               os_memmove(rpos + 2 + PMKID_LEN, rpos, end - rpos);
++      if (ssid_len > SSID_MAX_LEN || r0kh_id_len > FT_R0KH_ID_MAX_LEN)
 +              return;
 +      pos = buf;
 +      *pos++ = ssid_len;
 +      os_memcpy(pos, ssid, ssid_len);
 +      pos += ssid_len;
 +      os_memcpy(pos, mdid, MOBILITY_DOMAIN_ID_LEN);
 +      pos += MOBILITY_DOMAIN_ID_LEN;
 +      *pos++ = r0kh_id_len;
 +      os_memcpy(pos, r0kh_id, r0kh_id_len);
 +      pos += r0kh_id_len;
 +      os_memcpy(pos, s0kh_id, ETH_ALEN);
 +      pos += ETH_ALEN;
 +
 +      sha256_prf(xxkey, xxkey_len, "FT-R0", buf, pos - buf,
 +                 r0_key_data, sizeof(r0_key_data));
 +      os_memcpy(pmk_r0, r0_key_data, PMK_LEN);
 +
 +      /*
 +       * PMKR0Name = Truncate-128(SHA-256("FT-R0N" || PMK-R0Name-Salt)
 +       */
 +      addr[0] = (const u8 *) "FT-R0N";
 +      len[0] = 6;
 +      addr[1] = r0_key_data + PMK_LEN;
 +      len[1] = 16;
 +
 +      sha256_vector(2, addr, len, hash);
 +      os_memcpy(pmk_r0_name, hash, WPA_PMK_NAME_LEN);
 +}
 +
 +
 +/**
 + * wpa_derive_pmk_r1_name - Derive PMKR1Name
 + *
 + * IEEE Std 802.11r-2008 - 8.5.1.5.4
 + */
 +void wpa_derive_pmk_r1_name(const u8 *pmk_r0_name, const u8 *r1kh_id,
 +                          const u8 *s1kh_id, u8 *pmk_r1_name)
 +{
 +      u8 hash[32];
 +      const u8 *addr[4];
 +      size_t len[4];
 +
 +      /*
 +       * PMKR1Name = Truncate-128(SHA-256("FT-R1N" || PMKR0Name ||
 +       *                                  R1KH-ID || S1KH-ID))
 +       */
 +      addr[0] = (const u8 *) "FT-R1N";
 +      len[0] = 6;
 +      addr[1] = pmk_r0_name;
 +      len[1] = WPA_PMK_NAME_LEN;
 +      addr[2] = r1kh_id;
 +      len[2] = FT_R1KH_ID_LEN;
 +      addr[3] = s1kh_id;
 +      len[3] = ETH_ALEN;
 +
 +      sha256_vector(4, addr, len, hash);
 +      os_memcpy(pmk_r1_name, hash, WPA_PMK_NAME_LEN);
 +}
 +
 +
 +/**
 + * wpa_derive_pmk_r1 - Derive PMK-R1 and PMKR1Name from PMK-R0
 + *
 + * IEEE Std 802.11r-2008 - 8.5.1.5.4
 + */
 +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)
 +{
 +      u8 buf[FT_R1KH_ID_LEN + ETH_ALEN];
 +      u8 *pos;
 +
 +      /* PMK-R1 = KDF-256(PMK-R0, "FT-R1", R1KH-ID || S1KH-ID) */
 +      pos = buf;
 +      os_memcpy(pos, r1kh_id, FT_R1KH_ID_LEN);
 +      pos += FT_R1KH_ID_LEN;
 +      os_memcpy(pos, s1kh_id, ETH_ALEN);
 +      pos += ETH_ALEN;
 +
 +      sha256_prf(pmk_r0, PMK_LEN, "FT-R1", buf, pos - buf, pmk_r1, PMK_LEN);
 +
 +      wpa_derive_pmk_r1_name(pmk_r0_name, r1kh_id, s1kh_id, pmk_r1_name);
 +}
 +
 +
 +/**
 + * wpa_pmk_r1_to_ptk - Derive PTK and PTKName from PMK-R1
 + *
 + * IEEE Std 802.11r-2008 - 8.5.1.5.5
 + */
 +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 ||
 +       *                  BSSID || STA-ADDR)
 +       */
 +      pos = buf;
 +      os_memcpy(pos, snonce, WPA_NONCE_LEN);
 +      pos += WPA_NONCE_LEN;
 +      os_memcpy(pos, anonce, WPA_NONCE_LEN);
 +      pos += WPA_NONCE_LEN;
 +      os_memcpy(pos, bssid, ETH_ALEN);
 +      pos += ETH_ALEN;
 +      os_memcpy(pos, sta_addr, ETH_ALEN);
 +      pos += ETH_ALEN;
 +
 +      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 ||
 +       *                                ANonce || BSSID || STA-ADDR))
 +       */
 +      addr[0] = pmk_r1_name;
 +      len[0] = WPA_PMK_NAME_LEN;
 +      addr[1] = (const u8 *) "FT-PTKN";
 +      len[1] = 7;
 +      addr[2] = snonce;
 +      len[2] = WPA_NONCE_LEN;
 +      addr[3] = anonce;
 +      len[3] = WPA_NONCE_LEN;
 +      addr[4] = bssid;
 +      len[4] = ETH_ALEN;
 +      addr[5] = sta_addr;
 +      len[5] = ETH_ALEN;
 +
 +      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 */
 +
 +
 +/**
 + * rsn_pmkid - Calculate PMK identifier
 + * @pmk: Pairwise master key
 + * @pmk_len: Length of pmk in bytes
 + * @aa: Authenticator address
 + * @spa: Supplicant address
 + * @pmkid: Buffer for PMKID
 + * @use_sha256: Whether to use SHA256-based KDF
 + *
 + * IEEE Std 802.11i-2004 - 8.5.1.2 Pairwise key hierarchy
 + * PMKID = HMAC-SHA1-128(PMK, "PMK Name" || AA || SPA)
 + */
 +void rsn_pmkid(const u8 *pmk, size_t pmk_len, const u8 *aa, const u8 *spa,
 +             u8 *pmkid, int use_sha256)
 +{
 +      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;
 +
 +#ifdef CONFIG_IEEE80211W
 +      if (use_sha256)
 +              hmac_sha256_vector(pmk, pmk_len, 3, addr, len, hash);
 +      else
 +#endif /* CONFIG_IEEE80211W */
 +              hmac_sha1_vector(pmk, pmk_len, 3, addr, len, hash);
 +      os_memcpy(pmkid, hash, PMKID_LEN);
 +}
 +
 +
 +#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)
 + * Returns: Pointer to a text string of the cipher suite name
 + */
 +const char * wpa_cipher_txt(int cipher)
 +{
 +      switch (cipher) {
 +      case WPA_CIPHER_NONE:
 +              return "NONE";
 +      case WPA_CIPHER_WEP40:
 +              return "WEP-40";
 +      case WPA_CIPHER_WEP104:
 +              return "WEP-104";
 +      case WPA_CIPHER_TKIP:
 +              return "TKIP";
 +      case WPA_CIPHER_CCMP:
 +              return "CCMP";
 +      case WPA_CIPHER_CCMP | WPA_CIPHER_TKIP:
 +              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";
 +      }
 +}
 +
 +
 +/**
 + * wpa_key_mgmt_txt - Convert key management suite to a text string
 + * @key_mgmt: Key management suite (WPA_KEY_MGMT_* enum)
 + * @proto: WPA/WPA2 version (WPA_PROTO_*)
 + * Returns: Pointer to a text string of the key management suite name
 + */
 +const char * wpa_key_mgmt_txt(int key_mgmt, int proto)
 +{
 +      switch (key_mgmt) {
 +      case WPA_KEY_MGMT_IEEE8021X:
 +              if (proto == (WPA_PROTO_RSN | WPA_PROTO_WPA))
 +                      return "WPA2+WPA/IEEE 802.1X/EAP";
 +              return proto == WPA_PROTO_RSN ?
 +                      "WPA2/IEEE 802.1X/EAP" : "WPA/IEEE 802.1X/EAP";
 +      case WPA_KEY_MGMT_PSK:
 +              if (proto == (WPA_PROTO_RSN | WPA_PROTO_WPA))
 +                      return "WPA2-PSK+WPA-PSK";
 +              return proto == WPA_PROTO_RSN ?
 +                      "WPA2-PSK" : "WPA-PSK";
 +      case WPA_KEY_MGMT_NONE:
 +              return "NONE";
 +      case WPA_KEY_MGMT_IEEE8021X_NO_WPA:
 +              return "IEEE 802.1X (no WPA)";
 +#ifdef CONFIG_IEEE80211R
 +      case WPA_KEY_MGMT_FT_IEEE8021X:
 +              return "FT-EAP";
 +      case WPA_KEY_MGMT_FT_PSK:
 +              return "FT-PSK";
 +#endif /* CONFIG_IEEE80211R */
 +#ifdef CONFIG_IEEE80211W
 +      case WPA_KEY_MGMT_IEEE8021X_SHA256:
 +              return "WPA2-EAP-SHA256";
 +      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)
 +{
 +      if (ie1 == NULL || ie2 == NULL)
 +              return -1;
 +
 +      if (ie1len == ie2len && os_memcmp(ie1, ie2, ie1len) == 0)
 +              return 0; /* identical IEs */
 +
 +#ifdef CONFIG_IEEE80211R
 +      if (ft_initial_assoc) {
 +              struct wpa_ie_data ie1d, ie2d;
 +              /*
 +               * The PMKID-List in RSN IE is different between Beacon/Probe
 +               * Response/(Re)Association Request frames and EAPOL-Key
 +               * messages in FT initial mobility domain association. Allow
 +               * for this, but verify that other parts of the RSN IEs are
 +               * identical.
 +               */
 +              if (wpa_parse_wpa_ie_rsn(ie1, ie1len, &ie1d) < 0 ||
 +                  wpa_parse_wpa_ie_rsn(ie2, ie2len, &ie2d) < 0)
 +                      return -1;
 +              if (ie1d.proto == ie2d.proto &&
 +                  ie1d.pairwise_cipher == ie2d.pairwise_cipher &&
 +                  ie1d.group_cipher == ie2d.group_cipher &&
 +                  ie1d.key_mgmt == ie2d.key_mgmt &&
 +                  ie1d.capabilities == ie2d.capabilities &&
 +                  ie1d.mgmt_group_cipher == ie2d.mgmt_group_cipher)
 +                      return 0;
 +      }
 +#endif /* CONFIG_IEEE80211R */
 +
 +      return -1;
 +}
 +
 +
 +#ifdef CONFIG_IEEE80211R
 +int wpa_insert_pmkid(u8 *ies, size_t ies_len, const u8 *pmkid)
 +{
 +      u8 *start, *end, *rpos, *rend;
 +      int added = 0;
 +
 +      start = ies;
 +      end = ies + ies_len;
 +
 +      while (start < end) {
 +              if (*start == WLAN_EID_RSN)
 +                      break;
 +              start += 2 + start[1];
 +      }
 +      if (start >= end) {
 +              wpa_printf(MSG_ERROR, "FT: Could not find RSN IE in "
 +                         "IEs data");
 +              return -1;
 +      }
 +      wpa_hexdump(MSG_DEBUG, "FT: RSN IE before modification",
 +                  start, 2 + start[1]);
 +
 +      /* Find start of PMKID-Count */
 +      rpos = start + 2;
 +      rend = rpos + start[1];
 +
 +      /* Skip Version and Group Data Cipher Suite */
 +      rpos += 2 + 4;
 +      /* Skip Pairwise Cipher Suite Count and List */
 +      rpos += 2 + WPA_GET_LE16(rpos) * RSN_SELECTOR_LEN;
 +      /* Skip AKM Suite Count and List */
 +      rpos += 2 + WPA_GET_LE16(rpos) * RSN_SELECTOR_LEN;
 +
 +      if (rpos == rend) {
 +              /* Add RSN Capabilities */
 +              os_memmove(rpos + 2, rpos, end - rpos);
 +              *rpos++ = 0;
 +              *rpos++ = 0;
++              added += 2;
++              start[1] += 2;
++              rend = rpos;
 +      } else {
 +              /* Skip RSN Capabilities */
 +              rpos += 2;
 +              if (rpos > rend) {
 +                      wpa_printf(MSG_ERROR, "FT: Could not parse RSN IE in "
 +                                 "IEs data");
 +                      return -1;
 +              }
 +      }
 +
 +      if (rpos == rend) {
 +              /* No PMKID-Count field included; add it */
-               os_memmove(rpos + PMKID_LEN, rpos, end - rpos);
++              os_memmove(rpos + 2 + PMKID_LEN, rpos, end + added - rpos);
 +              WPA_PUT_LE16(rpos, 1);
 +              rpos += 2;
 +              os_memcpy(rpos, pmkid, PMKID_LEN);
 +              added += 2 + PMKID_LEN;
 +              start[1] += 2 + PMKID_LEN;
 +      } else {
 +              /* PMKID-Count was included; use it */
 +              if (WPA_GET_LE16(rpos) != 0) {
 +                      wpa_printf(MSG_ERROR, "FT: Unexpected PMKID "
 +                                 "in RSN IE in EAPOL-Key data");
 +                      return -1;
 +              }
 +              WPA_PUT_LE16(rpos, 1);
 +              rpos += 2;
-       case WPA_CIPHER_WEP104:
-               return 13;
-       case WPA_CIPHER_WEP40:
-               return 5;
++              os_memmove(rpos + PMKID_LEN, rpos, end + added - rpos);
 +              os_memcpy(rpos, pmkid, PMKID_LEN);
 +              added += PMKID_LEN;
 +              start[1] += PMKID_LEN;
 +      }
 +
 +      wpa_hexdump(MSG_DEBUG, "FT: RSN IE after modification "
 +                  "(PMKID inserted)", start, 2 + start[1]);
 +
 +      return added;
 +}
 +#endif /* CONFIG_IEEE80211R */
 +
 +
 +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;
-       case WPA_CIPHER_WEP104:
-       case WPA_CIPHER_WEP40:
-               return 0;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +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:
 +              return 6;
-       case WPA_CIPHER_WEP104:
-       case WPA_CIPHER_WEP40:
-               return WPA_ALG_WEP;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +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:
 +              return WPA_ALG_GCMP;
 +      case WPA_CIPHER_TKIP:
 +              return WPA_ALG_TKIP;
-       if (cipher & WPA_CIPHER_WEP104)
-               return (proto == WPA_PROTO_RSN ?
-                       RSN_CIPHER_SUITE_WEP104 : WPA_CIPHER_SUITE_WEP104);
-       if (cipher & WPA_CIPHER_WEP40)
-               return (proto == WPA_PROTO_RSN ?
-                       RSN_CIPHER_SUITE_WEP40 : WPA_CIPHER_SUITE_WEP40);
 +      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;
 +}
 +
 +
 +int wpa_cipher_valid_pairwise(int cipher)
 +{
 +      return cipher == WPA_CIPHER_CCMP_256 ||
 +              cipher == WPA_CIPHER_GCMP_256 ||
 +              cipher == WPA_CIPHER_CCMP ||
 +              cipher == WPA_CIPHER_GCMP ||
 +              cipher == WPA_CIPHER_TKIP;
 +}
 +
 +
 +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);
 +      if (cipher & WPA_CIPHER_GCMP)
 +              return RSN_CIPHER_SUITE_GCMP;
 +      if (cipher & WPA_CIPHER_TKIP)
 +              return (proto == WPA_PROTO_RSN ?
 +                      RSN_CIPHER_SUITE_TKIP : WPA_CIPHER_SUITE_TKIP);
-       if (ciphers & WPA_CIPHER_WEP104)
-               return WPA_CIPHER_WEP104;
-       if (ciphers & WPA_CIPHER_WEP40)
-               return WPA_CIPHER_WEP40;
 +      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 *start, int ciphers)
 +{
 +      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;
 +      }
 +      if (ciphers & WPA_CIPHER_GCMP) {
 +              RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_GCMP);
 +              pos += RSN_SELECTOR_LEN;
 +      }
 +      if (ciphers & WPA_CIPHER_TKIP) {
 +              RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP);
 +              pos += RSN_SELECTOR_LEN;
 +      }
 +      if (ciphers & WPA_CIPHER_NONE) {
 +              RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_NONE);
 +              pos += RSN_SELECTOR_LEN;
 +      }
 +
 +      return (pos - start) / RSN_SELECTOR_LEN;
 +}
 +
 +
 +int wpa_cipher_put_suites(u8 *start, int ciphers)
 +{
 +      u8 *pos = start;
 +
 +      if (ciphers & WPA_CIPHER_CCMP) {
 +              RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_CCMP);
 +              pos += WPA_SELECTOR_LEN;
 +      }
 +      if (ciphers & WPA_CIPHER_TKIP) {
 +              RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_TKIP);
 +              pos += WPA_SELECTOR_LEN;
 +      }
 +      if (ciphers & WPA_CIPHER_NONE) {
 +              RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_NONE);
 +              pos += WPA_SELECTOR_LEN;
 +      }
 +
 +      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)
 +              return WPA_CIPHER_GCMP;
 +      if (ciphers & WPA_CIPHER_TKIP)
 +              return WPA_CIPHER_TKIP;
 +      if (none_allowed && (ciphers & WPA_CIPHER_NONE))
 +              return WPA_CIPHER_NONE;
 +      return -1;
 +}
 +
 +
 +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 (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 (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);
 +      return -1;
 +}
 +
 +
 +int wpa_parse_cipher(const char *value)
 +{
 +      int val = 0, last;
 +      char *start, *end, *buf;
 +
 +      buf = os_strdup(value);
 +      if (buf == NULL)
 +              return -1;
 +      start = buf;
 +
 +      while (*start != '\0') {
 +              while (*start == ' ' || *start == '\t')
 +                      start++;
 +              if (*start == '\0')
 +                      break;
 +              end = start;
 +              while (*end != ' ' && *end != '\t' && *end != '\0')
 +                      end++;
 +              last = *end == '\0';
 +              *end = '\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;
 +              else if (os_strcmp(start, "TKIP") == 0)
 +                      val |= WPA_CIPHER_TKIP;
 +              else if (os_strcmp(start, "WEP104") == 0)
 +                      val |= WPA_CIPHER_WEP104;
 +              else if (os_strcmp(start, "WEP40") == 0)
 +                      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;
 +              }
 +
 +              if (last)
 +                      break;
 +              start = end + 1;
 +      }
 +      os_free(buf);
 +
 +      return val;
 +}
 +
 +
 +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 (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 (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 (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 (os_snprintf_error(end - pos, ret))
 +                      return -1;
 +              pos += ret;
 +      }
 +
 +      return pos - start;
 +}
 +
 +
 +int wpa_select_ap_group_cipher(int wpa, int wpa_pairwise, int rsn_pairwise)
 +{
 +      int pairwise = 0;
 +
 +      /* Select group cipher based on the enabled pairwise cipher suites */
 +      if (wpa & 1)
 +              pairwise |= wpa_pairwise;
 +      if (wpa & 2)
 +              pairwise |= rsn_pairwise;
 +
 +      if (pairwise & WPA_CIPHER_TKIP)
 +              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 091e317fdd68a2e2f625e1cbe83f61d7fc189ff3,0000000000000000000000000000000000000000..c08f6514ab5732df723270aac27af5c267cca777
mode 100644,000000..100644
--- /dev/null
@@@ -1,461 -1,0 +1,451 @@@
- #define WPA_MAX_SSID_LEN 32
 +/*
 + * WPA definitions shared between hostapd and wpa_supplicant
 + * 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.
 + */
 +
 +#ifndef WPA_COMMON_H
 +#define WPA_COMMON_H
 +
- (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP | WPA_CIPHER_TKIP | WPA_CIPHER_WEP104 | \
- WPA_CIPHER_WEP40 | WPA_CIPHER_GCMP_256 | WPA_CIPHER_CCMP_256 | \
 +/* IEEE 802.11i */
 +#define PMKID_LEN 16
 +#define PMK_LEN 32
 +#define WPA_REPLAY_COUNTER_LEN 8
 +#define WPA_NONCE_LEN 32
 +#define WPA_KEY_RSC_LEN 8
 +#define WPA_GMK_LEN 32
 +#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_GCMP_256 | WPA_CIPHER_CCMP_256)
 +#define WPA_ALLOWED_GROUP_CIPHERS \
- #define WPA_CIPHER_SUITE_WEP40 RSN_SELECTOR(0x00, 0x50, 0xf2, 1)
++(WPA_CIPHER_CCMP | WPA_CIPHER_GCMP | WPA_CIPHER_TKIP | \
++WPA_CIPHER_GCMP_256 | WPA_CIPHER_CCMP_256 | \
 +WPA_CIPHER_GTK_NOT_USED)
 +
 +#define WPA_SELECTOR_LEN 4
 +#define WPA_VERSION 1
 +#define RSN_SELECTOR_LEN 4
 +#define RSN_VERSION 1
 +
 +#define RSN_SELECTOR(a, b, c, d) \
 +      ((((u32) (a)) << 24) | (((u32) (b)) << 16) | (((u32) (c)) << 8) | \
 +       (u32) (d))
 +
 +#define WPA_AUTH_KEY_MGMT_NONE RSN_SELECTOR(0x00, 0x50, 0xf2, 0)
 +#define WPA_AUTH_KEY_MGMT_UNSPEC_802_1X RSN_SELECTOR(0x00, 0x50, 0xf2, 1)
 +#define WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X RSN_SELECTOR(0x00, 0x50, 0xf2, 2)
 +#define WPA_AUTH_KEY_MGMT_CCKM RSN_SELECTOR(0x00, 0x40, 0x96, 0)
 +#define WPA_CIPHER_SUITE_NONE RSN_SELECTOR(0x00, 0x50, 0xf2, 0)
- #if 0
- #define WPA_CIPHER_SUITE_WRAP RSN_SELECTOR(0x00, 0x50, 0xf2, 3)
- #endif
 +#define WPA_CIPHER_SUITE_TKIP RSN_SELECTOR(0x00, 0x50, 0xf2, 2)
- #define WPA_CIPHER_SUITE_WEP104 RSN_SELECTOR(0x00, 0x50, 0xf2, 5)
 +#define WPA_CIPHER_SUITE_CCMP RSN_SELECTOR(0x00, 0x50, 0xf2, 4)
- #define RSN_CIPHER_SUITE_WEP40 RSN_SELECTOR(0x00, 0x0f, 0xac, 1)
 +
 +
 +#define RSN_AUTH_KEY_MGMT_UNSPEC_802_1X RSN_SELECTOR(0x00, 0x0f, 0xac, 1)
 +#define RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X RSN_SELECTOR(0x00, 0x0f, 0xac, 2)
 +#ifdef CONFIG_IEEE80211R
 +#define RSN_AUTH_KEY_MGMT_FT_802_1X RSN_SELECTOR(0x00, 0x0f, 0xac, 3)
 +#define RSN_AUTH_KEY_MGMT_FT_PSK RSN_SELECTOR(0x00, 0x0f, 0xac, 4)
 +#endif /* CONFIG_IEEE80211R */
 +#define RSN_AUTH_KEY_MGMT_802_1X_SHA256 RSN_SELECTOR(0x00, 0x0f, 0xac, 5)
 +#define RSN_AUTH_KEY_MGMT_PSK_SHA256 RSN_SELECTOR(0x00, 0x0f, 0xac, 6)
 +#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_WEP104 RSN_SELECTOR(0x00, 0x0f, 0xac, 5)
 +#define RSN_CIPHER_SUITE_TKIP RSN_SELECTOR(0x00, 0x0f, 0xac, 2)
 +#if 0
 +#define RSN_CIPHER_SUITE_WRAP RSN_SELECTOR(0x00, 0x0f, 0xac, 3)
 +#endif
 +#define RSN_CIPHER_SUITE_CCMP RSN_SELECTOR(0x00, 0x0f, 0xac, 4)
- #ifdef CONFIG_IEEE80211R
 +#define RSN_CIPHER_SUITE_AES_128_CMAC RSN_SELECTOR(0x00, 0x0f, 0xac, 6)
 +#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.
 + */
 +#define RSN_KEY_DATA_GROUPKEY RSN_SELECTOR(0x00, 0x0f, 0xac, 1)
 +#if 0
 +#define RSN_KEY_DATA_STAKEY RSN_SELECTOR(0x00, 0x0f, 0xac, 2)
 +#endif
 +#define RSN_KEY_DATA_MAC_ADDR RSN_SELECTOR(0x00, 0x0f, 0xac, 3)
 +#define RSN_KEY_DATA_PMKID RSN_SELECTOR(0x00, 0x0f, 0xac, 4)
 +#ifdef CONFIG_PEERKEY
 +#define RSN_KEY_DATA_SMK RSN_SELECTOR(0x00, 0x0f, 0xac, 5)
 +#define RSN_KEY_DATA_NONCE RSN_SELECTOR(0x00, 0x0f, 0xac, 6)
 +#define RSN_KEY_DATA_LIFETIME RSN_SELECTOR(0x00, 0x0f, 0xac, 7)
 +#define RSN_KEY_DATA_ERROR RSN_SELECTOR(0x00, 0x0f, 0xac, 8)
 +#endif /* CONFIG_PEERKEY */
 +#ifdef CONFIG_IEEE80211W
 +#define RSN_KEY_DATA_IGTK RSN_SELECTOR(0x00, 0x0f, 0xac, 9)
 +#endif /* CONFIG_IEEE80211W */
 +#define RSN_KEY_DATA_KEYID RSN_SELECTOR(0x00, 0x0f, 0xac, 10)
 +#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))
 +#define RSN_SELECTOR_GET(a) WPA_GET_BE32((const u8 *) (a))
 +
 +#define RSN_NUM_REPLAY_COUNTERS_1 0
 +#define RSN_NUM_REPLAY_COUNTERS_2 1
 +#define RSN_NUM_REPLAY_COUNTERS_4 2
 +#define RSN_NUM_REPLAY_COUNTERS_16 3
 +
 +
 +#ifdef _MSC_VER
 +#pragma pack(push, 1)
 +#endif /* _MSC_VER */
 +
 +#ifdef CONFIG_IEEE80211W
 +#define WPA_IGTK_LEN 16
 +#define WPA_IGTK_MAX_LEN 32
 +#endif /* CONFIG_IEEE80211W */
 +
 +
 +/* IEEE 802.11, 7.3.2.25.3 RSN Capabilities */
 +#define WPA_CAPABILITY_PREAUTH BIT(0)
 +#define WPA_CAPABILITY_NO_PAIRWISE BIT(1)
 +/* B2-B3: PTKSA Replay Counter */
 +/* B4-B5: GTKSA Replay Counter */
 +#define WPA_CAPABILITY_MFPR BIT(6)
 +#define WPA_CAPABILITY_MFPC BIT(7)
 +/* B8: Reserved */
 +#define WPA_CAPABILITY_PEERKEY_ENABLED BIT(9)
 +#define WPA_CAPABILITY_SPP_A_MSDU_CAPABLE BIT(10)
 +#define WPA_CAPABILITY_SPP_A_MSDU_REQUIRED BIT(11)
 +#define WPA_CAPABILITY_PBAC BIT(12)
 +#define WPA_CAPABILITY_EXT_KEY_ID_FOR_UNICAST BIT(13)
 +/* B14-B15: Reserved */
 +
 +
 +/* IEEE 802.11r */
 +#define MOBILITY_DOMAIN_ID_LEN 2
 +#define FT_R0KH_ID_MAX_LEN 48
 +#define FT_R1KH_ID_LEN 6
 +#define WPA_PMK_NAME_LEN 16
 +
 +
 +/* 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
 +#define WPA_KEY_INFO_KEY_TYPE BIT(3) /* 1 = Pairwise, 0 = Group key */
 +/* bit4..5 is used in WPA, but is reserved in IEEE 802.11i/RSN */
 +#define WPA_KEY_INFO_KEY_INDEX_MASK (BIT(4) | BIT(5))
 +#define WPA_KEY_INFO_KEY_INDEX_SHIFT 4
 +#define WPA_KEY_INFO_INSTALL BIT(6) /* pairwise */
 +#define WPA_KEY_INFO_TXRX BIT(6) /* group */
 +#define WPA_KEY_INFO_ACK BIT(7)
 +#define WPA_KEY_INFO_MIC BIT(8)
 +#define WPA_KEY_INFO_SECURE BIT(9)
 +#define WPA_KEY_INFO_ERROR BIT(10)
 +#define WPA_KEY_INFO_REQUEST BIT(11)
 +#define WPA_KEY_INFO_ENCR_KEY_DATA BIT(12) /* IEEE 802.11i/RSN only */
 +#define WPA_KEY_INFO_SMK_MESSAGE BIT(13)
 +
 +
 +struct wpa_eapol_key {
 +      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[16];
 +      u8 key_data_length[2]; /* big endian */
 +      /* 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[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
 + * 00-50-f2:1 (OUI:OUI type)
 + * 0x01 0x00 (version; little endian)
 + * (all following fields are optional:)
 + * Group Suite Selector (4 octets) (default: TKIP)
 + * Pairwise Suite Count (2 octets, little endian) (default: 1)
 + * Pairwise Suite List (4 * n octets) (default: TKIP)
 + * Authenticated Key Management Suite Count (2 octets, little endian)
 + *    (default: 1)
 + * Authenticated Key Management Suite List (4 * n octets)
 + *    (default: unspec 802.1X)
 + * WPA Capabilities (2 octets, little endian) (default: 0)
 + */
 +
 +struct wpa_ie_hdr {
 +      u8 elem_id;
 +      u8 len;
 +      u8 oui[4]; /* 24-bit OUI followed by 8-bit OUI type */
 +      u8 version[2]; /* little endian */
 +} STRUCT_PACKED;
 +
 +
 +/* 1/4: PMKID
 + * 2/4: RSN IE
 + * 3/4: one or two RSN IEs + GTK IE (encrypted)
 + * 4/4: empty
 + * 1/2: GTK IE (encrypted)
 + * 2/2: empty
 + */
 +
 +/* RSN IE version 1
 + * 0x01 0x00 (version; little endian)
 + * (all following fields are optional:)
 + * Group Suite Selector (4 octets) (default: CCMP)
 + * Pairwise Suite Count (2 octets, little endian) (default: 1)
 + * Pairwise Suite List (4 * n octets) (default: CCMP)
 + * Authenticated Key Management Suite Count (2 octets, little endian)
 + *    (default: 1)
 + * Authenticated Key Management Suite List (4 * n octets)
 + *    (default: unspec 802.1X)
 + * RSN Capabilities (2 octets, little endian) (default: 0)
 + * PMKID Count (2 octets) (default: 0)
 + * PMKID List (16 * n octets)
 + * Management Group Cipher Suite (4 octets) (default: AES-128-CMAC)
 + */
 +
 +struct rsn_ie_hdr {
 +      u8 elem_id; /* WLAN_EID_RSN */
 +      u8 len;
 +      u8 version[2]; /* little endian */
 +} STRUCT_PACKED;
 +
 +
 +#ifdef CONFIG_PEERKEY
 +enum {
 +      STK_MUI_4WAY_STA_AP = 1,
 +      STK_MUI_4WAY_STAT_STA = 2,
 +      STK_MUI_GTK = 3,
 +      STK_MUI_SMK = 4
 +};
 +
 +enum {
 +      STK_ERR_STA_NR = 1,
 +      STK_ERR_STA_NRSN = 2,
 +      STK_ERR_CPHR_NS = 3,
 +      STK_ERR_NO_STSL = 4
 +};
 +#endif /* CONFIG_PEERKEY */
 +
 +struct rsn_error_kde {
 +      be16 mui;
 +      be16 error_type;
 +} 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_MAX_LEN];
 +} STRUCT_PACKED;
 +#endif /* CONFIG_IEEE80211W */
 +
- #endif /* CONFIG_IEEE80211R */
 +struct rsn_mdie {
 +      u8 mobility_domain[MOBILITY_DOMAIN_ID_LEN];
 +      u8 ft_capab;
 +} STRUCT_PACKED;
 +
 +#define RSN_FT_CAPAB_FT_OVER_DS BIT(0)
 +#define RSN_FT_CAPAB_FT_RESOURCE_REQ_SUPP BIT(1)
 +
 +struct rsn_ftie {
 +      u8 mic_control[2];
 +      u8 mic[16];
 +      u8 anonce[WPA_NONCE_LEN];
 +      u8 snonce[WPA_NONCE_LEN];
 +      /* followed by optional parameters */
 +} STRUCT_PACKED;
 +
 +#define FTIE_SUBELEM_R1KH_ID 1
 +#define FTIE_SUBELEM_GTK 2
 +#define FTIE_SUBELEM_R0KH_ID 3
 +#define FTIE_SUBELEM_IGTK 4
 +
 +struct rsn_rdie {
 +      u8 id;
 +      u8 descr_count;
 +      le16 status_code;
 +} STRUCT_PACKED;
 +
 +
 +#ifdef _MSC_VER
 +#pragma pack(pop)
 +#endif /* _MSC_VER */
 +
 +
 +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, 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);
 +void wpa_derive_pmk_r0(const u8 *xxkey, size_t xxkey_len,
 +                     const u8 *ssid, size_t ssid_len,
 +                     const u8 *mdid, const u8 *r0kh_id, size_t r0kh_id_len,
 +                     const u8 *s0kh_id, u8 *pmk_r0, u8 *pmk_r0_name);
 +void wpa_derive_pmk_r1_name(const u8 *pmk_r0_name, const u8 *r1kh_id,
 +                          const u8 *s1kh_id, u8 *pmk_r1_name);
 +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);
 +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 {
 +      int proto;
 +      int pairwise_cipher;
 +      int group_cipher;
 +      int key_mgmt;
 +      int capabilities;
 +      size_t num_pmkid;
 +      const u8 *pmkid;
 +      int mgmt_group_cipher;
 +};
 +
 +
 +int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_len,
 +                       struct wpa_ie_data *data);
 +int wpa_parse_wpa_ie_wpa(const u8 *wpa_ie, size_t wpa_ie_len,
 +                       struct wpa_ie_data *data);
 +
 +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);
 +int wpa_insert_pmkid(u8 *ies, size_t ies_len, const u8 *pmkid);
 +
 +struct wpa_ft_ies {
 +      const u8 *mdie;
 +      size_t mdie_len;
 +      const u8 *ftie;
 +      size_t ftie_len;
 +      const u8 *r1kh_id;
 +      const u8 *gtk;
 +      size_t gtk_len;
 +      const u8 *r0kh_id;
 +      size_t r0kh_id_len;
 +      const u8 *rsn;
 +      size_t rsn_len;
 +      const u8 *rsn_pmkid;
 +      const u8 *tie;
 +      size_t tie_len;
 +      const u8 *igtk;
 +      size_t igtk_len;
 +      const u8 *ric;
 +      size_t ric_len;
 +};
 +
 +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);
++int wpa_cipher_valid_group(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);
 +int wpa_pick_pairwise_cipher(int ciphers, int none_allowed);
 +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 ccaaf1b056b586a63ec8052e8fd5c939357b1e2f,0000000000000000000000000000000000000000..5733aa605d18c2afc656349dcc8c16d73bba9e49
mode 100644,000000..100644
--- /dev/null
@@@ -1,732 -1,0 +1,750 @@@
-       ret = os_snprintf(ctrl->local.sun_path, sizeof(ctrl->local.sun_path),
-                         CONFIG_CTRL_IFACE_CLIENT_DIR "/"
-                         CONFIG_CTRL_IFACE_CLIENT_PREFIX "%d-%d",
-                         (int) getpid(), counter);
 +/*
 + * wpa_supplicant/hostapd control interface library
 + * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
 + *
 + * This software may be distributed under the terms of the BSD license.
 + * See README for more details.
 + */
 +
 +#include "includes.h"
 +
 +#ifdef CONFIG_CTRL_IFACE
 +
 +#ifdef CONFIG_CTRL_IFACE_UNIX
 +#include <sys/un.h>
 +#include <unistd.h>
 +#include <fcntl.h>
 +#endif /* CONFIG_CTRL_IFACE_UNIX */
 +#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE
 +#include <netdb.h>
 +#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
 +
 +#ifdef ANDROID
 +#include <dirent.h>
++#include <sys/stat.h>
 +#include <cutils/sockets.h>
 +#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"
 +
 +
 +#if defined(CONFIG_CTRL_IFACE_UNIX) || defined(CONFIG_CTRL_IFACE_UDP)
 +#define CTRL_IFACE_SOCKET
 +#endif /* CONFIG_CTRL_IFACE_UNIX || CONFIG_CTRL_IFACE_UDP */
 +
 +
 +/**
 + * struct wpa_ctrl - Internal structure for control interface library
 + *
 + * This structure is used by the wpa_supplicant/hostapd control interface
 + * library to store internal data. Programs using the library should not touch
 + * this data directly. They can only use the pointer to the data structure as
 + * an identifier for the control interface connection and use this as one of
 + * the arguments for most of the control interface library functions.
 + */
 +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;
 +#endif /* CONFIG_CTRL_IFACE_UDP */
 +#ifdef CONFIG_CTRL_IFACE_UNIX
 +      int s;
 +      struct sockaddr_un local;
 +      struct sockaddr_un dest;
 +#endif /* CONFIG_CTRL_IFACE_UNIX */
 +#ifdef CONFIG_CTRL_IFACE_NAMED_PIPE
 +      HANDLE pipe;
 +#endif /* CONFIG_CTRL_IFACE_NAMED_PIPE */
 +};
 +
 +
 +#ifdef CONFIG_CTRL_IFACE_UNIX
 +
 +#ifndef CONFIG_CTRL_IFACE_CLIENT_DIR
 +#define CONFIG_CTRL_IFACE_CLIENT_DIR "/tmp"
 +#endif /* CONFIG_CTRL_IFACE_CLIENT_DIR */
 +#ifndef CONFIG_CTRL_IFACE_CLIENT_PREFIX
 +#define CONFIG_CTRL_IFACE_CLIENT_PREFIX "wpa_ctrl_"
 +#endif /* CONFIG_CTRL_IFACE_CLIENT_PREFIX */
 +
 +
 +struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path)
++{
++      return wpa_ctrl_open2(ctrl_path, NULL);
++}
++
++
++struct wpa_ctrl * wpa_ctrl_open2(const char *ctrl_path,
++                               const char *cli_path)
 +{
 +      struct wpa_ctrl *ctrl;
 +      static int counter = 0;
 +      int ret;
 +      size_t res;
 +      int tries = 0;
 +      int flags;
 +
 +      if (ctrl_path == NULL)
 +              return NULL;
 +
 +      ctrl = os_zalloc(sizeof(*ctrl));
 +      if (ctrl == NULL)
 +              return NULL;
 +
 +      ctrl->s = socket(PF_UNIX, SOCK_DGRAM, 0);
 +      if (ctrl->s < 0) {
 +              os_free(ctrl);
 +              return NULL;
 +      }
 +
 +      ctrl->local.sun_family = AF_UNIX;
 +      counter++;
 +try_again:
++      if (cli_path && cli_path[0] == '/') {
++              ret = os_snprintf(ctrl->local.sun_path,
++                                sizeof(ctrl->local.sun_path),
++                                "%s/" CONFIG_CTRL_IFACE_CLIENT_PREFIX "%d-%d",
++                                cli_path, (int) getpid(), counter);
++      } else {
++              ret = os_snprintf(ctrl->local.sun_path,
++                                sizeof(ctrl->local.sun_path),
++                                CONFIG_CTRL_IFACE_CLIENT_DIR "/"
++                                CONFIG_CTRL_IFACE_CLIENT_PREFIX "%d-%d",
++                                (int) getpid(), counter);
++      }
 +      if (os_snprintf_error(sizeof(ctrl->local.sun_path), ret)) {
 +              close(ctrl->s);
 +              os_free(ctrl);
 +              return NULL;
 +      }
 +      tries++;
 +      if (bind(ctrl->s, (struct sockaddr *) &ctrl->local,
 +                  sizeof(ctrl->local)) < 0) {
 +              if (errno == EADDRINUSE && tries < 2) {
 +                      /*
 +                       * getpid() returns unique identifier for this instance
 +                       * of wpa_ctrl, so the existing socket file must have
 +                       * been left by unclean termination of an earlier run.
 +                       * Remove the file and try again.
 +                       */
 +                      unlink(ctrl->local.sun_path);
 +                      goto try_again;
 +              }
 +              close(ctrl->s);
 +              os_free(ctrl);
 +              return NULL;
 +      }
 +
 +#ifdef ANDROID
 +      chmod(ctrl->local.sun_path, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
++      /* Set group even if we do not have privileges to change owner */
++      chown(ctrl->local.sun_path, -1, AID_WIFI);
 +      chown(ctrl->local.sun_path, AID_SYSTEM, AID_WIFI);
 +
 +      if (os_strncmp(ctrl_path, "@android:", 9) == 0) {
 +              if (socket_local_client_connect(
 +                          ctrl->s, ctrl_path + 9,
 +                          ANDROID_SOCKET_NAMESPACE_RESERVED,
 +                          SOCK_DGRAM) < 0) {
 +                      close(ctrl->s);
 +                      unlink(ctrl->local.sun_path);
 +                      os_free(ctrl);
 +                      return NULL;
 +              }
 +              return ctrl;
 +      }
 +
 +      /*
 +       * If the ctrl_path isn't an absolute pathname, assume that
 +       * it's the name of a socket in the Android reserved namespace.
 +       * Otherwise, it's a normal UNIX domain socket appearing in the
 +       * filesystem.
 +       */
 +      if (*ctrl_path != '/') {
 +              char buf[21];
 +              os_snprintf(buf, sizeof(buf), "wpa_%s", ctrl_path);
 +              if (socket_local_client_connect(
 +                          ctrl->s, buf,
 +                          ANDROID_SOCKET_NAMESPACE_RESERVED,
 +                          SOCK_DGRAM) < 0) {
 +                      close(ctrl->s);
 +                      unlink(ctrl->local.sun_path);
 +                      os_free(ctrl);
 +                      return NULL;
 +              }
 +              return ctrl;
 +      }
 +#endif /* ANDROID */
 +
 +      ctrl->dest.sun_family = AF_UNIX;
 +      if (os_strncmp(ctrl_path, "@abstract:", 10) == 0) {
 +              ctrl->dest.sun_path[0] = '\0';
 +              os_strlcpy(ctrl->dest.sun_path + 1, ctrl_path + 10,
 +                         sizeof(ctrl->dest.sun_path) - 1);
 +      } else {
 +              res = os_strlcpy(ctrl->dest.sun_path, ctrl_path,
 +                               sizeof(ctrl->dest.sun_path));
 +              if (res >= sizeof(ctrl->dest.sun_path)) {
 +                      close(ctrl->s);
 +                      os_free(ctrl);
 +                      return NULL;
 +              }
 +      }
 +      if (connect(ctrl->s, (struct sockaddr *) &ctrl->dest,
 +                  sizeof(ctrl->dest)) < 0) {
 +              close(ctrl->s);
 +              unlink(ctrl->local.sun_path);
 +              os_free(ctrl);
 +              return NULL;
 +      }
 +
 +      /*
 +       * Make socket non-blocking so that we don't hang forever if
 +       * target dies unexpectedly.
 +       */
 +      flags = fcntl(ctrl->s, F_GETFL);
 +      if (flags >= 0) {
 +              flags |= O_NONBLOCK;
 +              if (fcntl(ctrl->s, F_SETFL, flags) < 0) {
 +                      perror("fcntl(ctrl->s, O_NONBLOCK)");
 +                      /* Not fatal, continue on.*/
 +              }
 +      }
 +
 +      return ctrl;
 +}
 +
 +
 +void wpa_ctrl_close(struct wpa_ctrl *ctrl)
 +{
 +      if (ctrl == NULL)
 +              return;
 +      unlink(ctrl->local.sun_path);
 +      if (ctrl->s >= 0)
 +              close(ctrl->s);
 +      os_free(ctrl);
 +}
 +
 +
 +#ifdef ANDROID
 +/**
 + * wpa_ctrl_cleanup() - Delete any local UNIX domain socket files that
 + * may be left over from clients that were previously connected to
 + * wpa_supplicant. This keeps these files from being orphaned in the
 + * event of crashes that prevented them from being removed as part
 + * of the normal orderly shutdown.
 + */
 +void wpa_ctrl_cleanup(void)
 +{
 +      DIR *dir;
 +      struct dirent entry;
 +      struct dirent *result;
 +      size_t dirnamelen;
 +      size_t maxcopy;
 +      char pathname[PATH_MAX];
 +      char *namep;
 +
 +      if ((dir = opendir(CONFIG_CTRL_IFACE_CLIENT_DIR)) == NULL)
 +              return;
 +
 +      dirnamelen = (size_t) os_snprintf(pathname, sizeof(pathname), "%s/",
 +                                        CONFIG_CTRL_IFACE_CLIENT_DIR);
 +      if (dirnamelen >= sizeof(pathname)) {
 +              closedir(dir);
 +              return;
 +      }
 +      namep = pathname + dirnamelen;
 +      maxcopy = PATH_MAX - dirnamelen;
 +      while (readdir_r(dir, &entry, &result) == 0 && result != NULL) {
 +              if (os_strlcpy(namep, entry.d_name, maxcopy) < maxcopy)
 +                      unlink(pathname);
 +      }
 +      closedir(dir);
 +}
 +#endif /* ANDROID */
 +
 +#else /* CONFIG_CTRL_IFACE_UNIX */
 +
 +#ifdef ANDROID
 +void wpa_ctrl_cleanup(void)
 +{
 +}
 +#endif /* ANDROID */
 +
 +#endif /* CONFIG_CTRL_IFACE_UNIX */
 +
 +
 +#ifdef CONFIG_CTRL_IFACE_UDP
 +
 +struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path)
 +{
 +      struct wpa_ctrl *ctrl;
 +      char buf[128];
 +      size_t len;
 +#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE
 +      struct hostent *h;
 +#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
 +
 +      ctrl = os_zalloc(sizeof(*ctrl));
 +      if (ctrl == NULL)
 +              return NULL;
 +
 +#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);
 +              os_free(ctrl);
 +              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) {
 +                      close(ctrl->s);
 +                      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]);
 +                      port[0] = '\0';
 +              } 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) {
 +                      perror("gethostbyname");
 +                      close(ctrl->s);
 +                      os_free(ctrl->remote_ip);
 +                      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(&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) {
 +#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);
 +              return NULL;
 +      }
 +
 +      len = sizeof(buf) - 1;
 +      if (wpa_ctrl_request(ctrl, "GET_COOKIE", 10, buf, &len, NULL) == 0) {
 +              buf[len] = '\0';
 +              ctrl->cookie = os_strdup(buf);
 +      }
 +
 +      if (wpa_ctrl_request(ctrl, "IFNAME", 6, buf, &len, NULL) == 0) {
 +              buf[len] = '\0';
 +              ctrl->remote_ifname = os_strdup(buf);
 +      }
 +
 +      return ctrl;
 +}
 +
 +
 +char * wpa_ctrl_get_remote_ifname(struct wpa_ctrl *ctrl)
 +{
 +#define WPA_CTRL_MAX_PS_NAME 100
 +      static char ps[WPA_CTRL_MAX_PS_NAME] = {};
 +      os_snprintf(ps, WPA_CTRL_MAX_PS_NAME, "%s/%s",
 +                  ctrl->remote_ip, ctrl->remote_ifname);
 +      return ps;
 +}
 +
 +
 +void wpa_ctrl_close(struct wpa_ctrl *ctrl)
 +{
 +      close(ctrl->s);
 +      os_free(ctrl->cookie);
 +      os_free(ctrl->remote_ifname);
 +      os_free(ctrl->remote_ip);
 +      os_free(ctrl);
 +}
 +
 +#endif /* CONFIG_CTRL_IFACE_UDP */
 +
 +
 +#ifdef CTRL_IFACE_SOCKET
 +int wpa_ctrl_request(struct wpa_ctrl *ctrl, const char *cmd, size_t cmd_len,
 +                   char *reply, size_t *reply_len,
 +                   void (*msg_cb)(char *msg, size_t len))
 +{
 +      struct timeval tv;
 +      struct os_reltime started_at;
 +      int res;
 +      fd_set rfds;
 +      const char *_cmd;
 +      char *cmd_buf = NULL;
 +      size_t _cmd_len;
 +
 +#ifdef CONFIG_CTRL_IFACE_UDP
 +      if (ctrl->cookie) {
 +              char *pos;
 +              _cmd_len = os_strlen(ctrl->cookie) + 1 + cmd_len;
 +              cmd_buf = os_malloc(_cmd_len);
 +              if (cmd_buf == NULL)
 +                      return -1;
 +              _cmd = cmd_buf;
 +              pos = cmd_buf;
 +              os_strlcpy(pos, ctrl->cookie, _cmd_len);
 +              pos += os_strlen(ctrl->cookie);
 +              *pos++ = ' ';
 +              os_memcpy(pos, cmd, cmd_len);
 +      } else
 +#endif /* CONFIG_CTRL_IFACE_UDP */
 +      {
 +              _cmd = cmd;
 +              _cmd_len = cmd_len;
 +      }
 +
 +      errno = 0;
 +      started_at.sec = 0;
 +      started_at.usec = 0;
 +retry_send:
 +      if (send(ctrl->s, _cmd, _cmd_len, 0) < 0) {
 +              if (errno == EAGAIN || errno == EBUSY || errno == EWOULDBLOCK)
 +              {
 +                      /*
 +                       * Must be a non-blocking socket... Try for a bit
 +                       * longer before giving up.
 +                       */
 +                      if (started_at.sec == 0)
 +                              os_get_reltime(&started_at);
 +                      else {
 +                              struct os_reltime n;
 +                              os_get_reltime(&n);
 +                              /* Try for a few seconds. */
 +                              if (os_reltime_expired(&n, &started_at, 5))
 +                                      goto send_err;
 +                      }
 +                      os_sleep(1, 0);
 +                      goto retry_send;
 +              }
 +      send_err:
 +              os_free(cmd_buf);
 +              return -1;
 +      }
 +      os_free(cmd_buf);
 +
 +      for (;;) {
 +              tv.tv_sec = 10;
 +              tv.tv_usec = 0;
 +              FD_ZERO(&rfds);
 +              FD_SET(ctrl->s, &rfds);
 +              res = select(ctrl->s + 1, &rfds, NULL, NULL, &tv);
 +              if (res < 0)
 +                      return res;
 +              if (FD_ISSET(ctrl->s, &rfds)) {
 +                      res = recv(ctrl->s, reply, *reply_len, 0);
 +                      if (res < 0)
 +                              return res;
 +                      if (res > 0 && reply[0] == '<') {
 +                              /* This is an unsolicited message from
 +                               * wpa_supplicant, not the reply to the
 +                               * request. Use msg_cb to report this to the
 +                               * caller. */
 +                              if (msg_cb) {
 +                                      /* Make sure the message is nul
 +                                       * terminated. */
 +                                      if ((size_t) res == *reply_len)
 +                                              res = (*reply_len) - 1;
 +                                      reply[res] = '\0';
 +                                      msg_cb(reply, res);
 +                              }
 +                              continue;
 +                      }
 +                      *reply_len = res;
 +                      break;
 +              } else {
 +                      return -2;
 +              }
 +      }
 +      return 0;
 +}
 +#endif /* CTRL_IFACE_SOCKET */
 +
 +
 +static int wpa_ctrl_attach_helper(struct wpa_ctrl *ctrl, int attach)
 +{
 +      char buf[10];
 +      int ret;
 +      size_t len = 10;
 +
 +      ret = wpa_ctrl_request(ctrl, attach ? "ATTACH" : "DETACH", 6,
 +                             buf, &len, NULL);
 +      if (ret < 0)
 +              return ret;
 +      if (len == 3 && os_memcmp(buf, "OK\n", 3) == 0)
 +              return 0;
 +      return -1;
 +}
 +
 +
 +int wpa_ctrl_attach(struct wpa_ctrl *ctrl)
 +{
 +      return wpa_ctrl_attach_helper(ctrl, 1);
 +}
 +
 +
 +int wpa_ctrl_detach(struct wpa_ctrl *ctrl)
 +{
 +      return wpa_ctrl_attach_helper(ctrl, 0);
 +}
 +
 +
 +#ifdef CTRL_IFACE_SOCKET
 +
 +int wpa_ctrl_recv(struct wpa_ctrl *ctrl, char *reply, size_t *reply_len)
 +{
 +      int res;
 +
 +      res = recv(ctrl->s, reply, *reply_len, 0);
 +      if (res < 0)
 +              return res;
 +      *reply_len = res;
 +      return 0;
 +}
 +
 +
 +int wpa_ctrl_pending(struct wpa_ctrl *ctrl)
 +{
 +      struct timeval tv;
 +      fd_set rfds;
 +      tv.tv_sec = 0;
 +      tv.tv_usec = 0;
 +      FD_ZERO(&rfds);
 +      FD_SET(ctrl->s, &rfds);
 +      select(ctrl->s + 1, &rfds, NULL, NULL, &tv);
 +      return FD_ISSET(ctrl->s, &rfds);
 +}
 +
 +
 +int wpa_ctrl_get_fd(struct wpa_ctrl *ctrl)
 +{
 +      return ctrl->s;
 +}
 +
 +#endif /* CTRL_IFACE_SOCKET */
 +
 +
 +#ifdef CONFIG_CTRL_IFACE_NAMED_PIPE
 +
 +#ifndef WPA_SUPPLICANT_NAMED_PIPE
 +#define WPA_SUPPLICANT_NAMED_PIPE "WpaSupplicant"
 +#endif
 +#define NAMED_PIPE_PREFIX TEXT("\\\\.\\pipe\\") TEXT(WPA_SUPPLICANT_NAMED_PIPE)
 +
 +struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path)
 +{
 +      struct wpa_ctrl *ctrl;
 +      DWORD mode;
 +      TCHAR name[256];
 +      int i, ret;
 +
 +      ctrl = os_malloc(sizeof(*ctrl));
 +      if (ctrl == NULL)
 +              return NULL;
 +      os_memset(ctrl, 0, sizeof(*ctrl));
 +
 +#ifdef UNICODE
 +      if (ctrl_path == NULL)
 +              ret = _snwprintf(name, 256, NAMED_PIPE_PREFIX);
 +      else
 +              ret = _snwprintf(name, 256, NAMED_PIPE_PREFIX TEXT("-%S"),
 +                               ctrl_path);
 +#else /* UNICODE */
 +      if (ctrl_path == NULL)
 +              ret = os_snprintf(name, 256, NAMED_PIPE_PREFIX);
 +      else
 +              ret = os_snprintf(name, 256, NAMED_PIPE_PREFIX "-%s",
 +                                ctrl_path);
 +#endif /* UNICODE */
 +      if (os_snprintf_error(256, ret)) {
 +              os_free(ctrl);
 +              return NULL;
 +      }
 +
 +      for (i = 0; i < 10; i++) {
 +              ctrl->pipe = CreateFile(name, GENERIC_READ | GENERIC_WRITE, 0,
 +                                      NULL, OPEN_EXISTING, 0, NULL);
 +              /*
 +               * Current named pipe server side in wpa_supplicant is
 +               * re-opening the pipe for new clients only after the previous
 +               * one is taken into use. This leaves a small window for race
 +               * conditions when two connections are being opened at almost
 +               * the same time. Retry if that was the case.
 +               */
 +              if (ctrl->pipe != INVALID_HANDLE_VALUE ||
 +                  GetLastError() != ERROR_PIPE_BUSY)
 +                      break;
 +              WaitNamedPipe(name, 1000);
 +      }
 +      if (ctrl->pipe == INVALID_HANDLE_VALUE) {
 +              os_free(ctrl);
 +              return NULL;
 +      }
 +
 +      mode = PIPE_READMODE_MESSAGE;
 +      if (!SetNamedPipeHandleState(ctrl->pipe, &mode, NULL, NULL)) {
 +              CloseHandle(ctrl->pipe);
 +              os_free(ctrl);
 +              return NULL;
 +      }
 +
 +      return ctrl;
 +}
 +
 +
 +void wpa_ctrl_close(struct wpa_ctrl *ctrl)
 +{
 +      CloseHandle(ctrl->pipe);
 +      os_free(ctrl);
 +}
 +
 +
 +int wpa_ctrl_request(struct wpa_ctrl *ctrl, const char *cmd, size_t cmd_len,
 +                   char *reply, size_t *reply_len,
 +                   void (*msg_cb)(char *msg, size_t len))
 +{
 +      DWORD written;
 +      DWORD readlen = *reply_len;
 +
 +      if (!WriteFile(ctrl->pipe, cmd, cmd_len, &written, NULL))
 +              return -1;
 +
 +      if (!ReadFile(ctrl->pipe, reply, *reply_len, &readlen, NULL))
 +              return -1;
 +      *reply_len = readlen;
 +
 +      return 0;
 +}
 +
 +
 +int wpa_ctrl_recv(struct wpa_ctrl *ctrl, char *reply, size_t *reply_len)
 +{
 +      DWORD len = *reply_len;
 +      if (!ReadFile(ctrl->pipe, reply, *reply_len, &len, NULL))
 +              return -1;
 +      *reply_len = len;
 +      return 0;
 +}
 +
 +
 +int wpa_ctrl_pending(struct wpa_ctrl *ctrl)
 +{
 +      DWORD left;
 +
 +      if (!PeekNamedPipe(ctrl->pipe, NULL, 0, NULL, &left, NULL))
 +              return -1;
 +      return left ? 1 : 0;
 +}
 +
 +
 +int wpa_ctrl_get_fd(struct wpa_ctrl *ctrl)
 +{
 +      return -1;
 +}
 +
 +#endif /* CONFIG_CTRL_IFACE_NAMED_PIPE */
 +
 +#endif /* CONFIG_CTRL_IFACE */
index 1d19fc550df651e9a0c461a791cb47a9eacc7090,0000000000000000000000000000000000000000..3de46823588b85ddfd054f1c138804b5a0216cce
mode 100644,000000..100644
--- /dev/null
@@@ -1,452 -1,0 +1,472 @@@
 +/*
 + * wpa_supplicant/hostapd control interface library
 + * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi>
 + *
 + * This software may be distributed under the terms of the BSD license.
 + * See README for more details.
 + */
 +
 +#ifndef WPA_CTRL_H
 +#define WPA_CTRL_H
 +
 +#ifdef  __cplusplus
 +extern "C" {
 +#endif
 +
 +/* wpa_supplicant control interface - fixed message prefixes */
 +
 +/** Interactive request for identity/password/pin */
 +#define WPA_CTRL_REQ "CTRL-REQ-"
 +
 +/** Response to identity/password/pin request */
 +#define WPA_CTRL_RSP "CTRL-RSP-"
 +
 +/* Event messages with fixed prefix */
 +/** Authentication completed successfully and data connection enabled */
 +#define WPA_EVENT_CONNECTED "CTRL-EVENT-CONNECTED "
 +/** Disconnected, data connection is not available */
 +#define WPA_EVENT_DISCONNECTED "CTRL-EVENT-DISCONNECTED "
 +/** Association rejected during connection attempt */
 +#define WPA_EVENT_ASSOC_REJECT "CTRL-EVENT-ASSOC-REJECT "
++/** Authentication rejected during connection attempt */
++#define WPA_EVENT_AUTH_REJECT "CTRL-EVENT-AUTH-REJECT "
 +/** wpa_supplicant is exiting */
 +#define WPA_EVENT_TERMINATING "CTRL-EVENT-TERMINATING "
 +/** Password change was completed successfully */
 +#define WPA_EVENT_PASSWORD_CHANGED "CTRL-EVENT-PASSWORD-CHANGED "
 +/** EAP-Request/Notification received */
 +#define WPA_EVENT_EAP_NOTIFICATION "CTRL-EVENT-EAP-NOTIFICATION "
 +/** EAP authentication started (EAP-Request/Identity received) */
 +#define WPA_EVENT_EAP_STARTED "CTRL-EVENT-EAP-STARTED "
 +/** EAP method proposed by the server */
 +#define WPA_EVENT_EAP_PROPOSED_METHOD "CTRL-EVENT-EAP-PROPOSED-METHOD "
 +/** EAP method selected */
 +#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 */
 +#define WPA_EVENT_EAP_STATUS "CTRL-EVENT-EAP-STATUS "
 +/** EAP authentication completed successfully */
 +#define WPA_EVENT_EAP_SUCCESS "CTRL-EVENT-EAP-SUCCESS "
 +/** EAP authentication failed (EAP-Failure received) */
 +#define WPA_EVENT_EAP_FAILURE "CTRL-EVENT-EAP-FAILURE "
 +/** Network block temporarily disabled (e.g., due to authentication failure) */
 +#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 "
++/** No suitable network was found */
++#define WPA_EVENT_NETWORK_NOT_FOUND "CTRL-EVENT-NETWORK-NOT-FOUND "
 +/** 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 */
 +#define WPS_EVENT_AP_AVAILABLE_PBC "WPS-AP-AVAILABLE-PBC "
 +/** Available WPS AP with our address as authorized in scan results */
 +#define WPS_EVENT_AP_AVAILABLE_AUTH "WPS-AP-AVAILABLE-AUTH "
 +/** Available WPS AP with recently selected PIN registrar found in scan results
 + */
 +#define WPS_EVENT_AP_AVAILABLE_PIN "WPS-AP-AVAILABLE-PIN "
 +/** Available WPS AP found in scan results */
 +#define WPS_EVENT_AP_AVAILABLE "WPS-AP-AVAILABLE "
 +/** A new credential received */
 +#define WPS_EVENT_CRED_RECEIVED "WPS-CRED-RECEIVED "
 +/** M2D received */
 +#define WPS_EVENT_M2D "WPS-M2D "
 +/** WPS registration failed after M2/M2D */
 +#define WPS_EVENT_FAIL "WPS-FAIL "
 +/** WPS registration completed successfully */
 +#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 "
 +
 +#define WPS_EVENT_OPEN_NETWORK "WPS-OPEN-NETWORK "
 +
 +/* WPS ER events */
 +#define WPS_EVENT_ER_AP_ADD "WPS-ER-AP-ADD "
 +#define WPS_EVENT_ER_AP_REMOVE "WPS-ER-AP-REMOVE "
 +#define WPS_EVENT_ER_ENROLLEE_ADD "WPS-ER-ENROLLEE-ADD "
 +#define WPS_EVENT_ER_ENROLLEE_REMOVE "WPS-ER-ENROLLEE-REMOVE "
 +#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 "
 +
 +/** P2P device lost */
 +#define P2P_EVENT_DEVICE_LOST "P2P-DEVICE-LOST "
 +
 +/** A P2P device requested GO negotiation, but we were not ready to start the
 + * negotiation */
 +#define P2P_EVENT_GO_NEG_REQUEST "P2P-GO-NEG-REQUEST "
 +#define P2P_EVENT_GO_NEG_SUCCESS "P2P-GO-NEG-SUCCESS "
 +#define P2P_EVENT_GO_NEG_FAILURE "P2P-GO-NEG-FAILURE "
 +#define P2P_EVENT_GROUP_FORMATION_SUCCESS "P2P-GROUP-FORMATION-SUCCESS "
 +#define P2P_EVENT_GROUP_FORMATION_FAILURE "P2P-GROUP-FORMATION-FAILURE "
 +#define P2P_EVENT_GROUP_STARTED "P2P-GROUP-STARTED "
 +#define P2P_EVENT_GROUP_REMOVED "P2P-GROUP-REMOVED "
 +#define P2P_EVENT_CROSS_CONNECT_ENABLE "P2P-CROSS-CONNECT-ENABLE "
 +#define P2P_EVENT_CROSS_CONNECT_DISABLE "P2P-CROSS-CONNECT-DISABLE "
 +/* parameters: <peer address> <PIN> */
 +#define P2P_EVENT_PROV_DISC_SHOW_PIN "P2P-PROV-DISC-SHOW-PIN "
 +/* parameters: <peer address> */
 +#define P2P_EVENT_PROV_DISC_ENTER_PIN "P2P-PROV-DISC-ENTER-PIN "
 +/* parameters: <peer address> */
 +#define P2P_EVENT_PROV_DISC_PBC_REQ "P2P-PROV-DISC-PBC-REQ "
 +/* parameters: <peer address> */
 +#define P2P_EVENT_PROV_DISC_PBC_RESP "P2P-PROV-DISC-PBC-RESP "
 +/* parameters: <peer address> <status> */
 +#define P2P_EVENT_PROV_DISC_FAILURE "P2P-PROV-DISC-FAILURE"
 +/* parameters: <freq> <src addr> <dialog token> <update indicator> <TLVs> */
 +#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 "
 +#define WPS_EVENT_NEW_AP_SETTINGS "WPS-NEW-AP-SETTINGS "
 +#define WPS_EVENT_REG_SUCCESS "WPS-REG-SUCCESS "
 +#define WPS_EVENT_AP_SETUP_LOCKED "WPS-AP-SETUP-LOCKED "
 +#define WPS_EVENT_AP_SETUP_UNLOCKED "WPS-AP-SETUP-UNLOCKED "
 +#define WPS_EVENT_AP_PIN_ENABLED "WPS-AP-PIN-ENABLED "
 +#define WPS_EVENT_AP_PIN_DISABLED "WPS-AP-PIN-DISABLED "
 +#define AP_STA_CONNECTED "AP-STA-CONNECTED "
 +#define AP_STA_DISCONNECTED "AP-STA-DISCONNECTED "
++#define AP_STA_POSSIBLE_PSK_MISMATCH "AP-STA-POSSIBLE-PSK-MISMATCH "
 +
 +#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
 +#define WPA_BSS_MASK_ID                       BIT(0)
 +#define WPA_BSS_MASK_BSSID            BIT(1)
 +#define WPA_BSS_MASK_FREQ             BIT(2)
 +#define WPA_BSS_MASK_BEACON_INT               BIT(3)
 +#define WPA_BSS_MASK_CAPABILITIES     BIT(4)
 +#define WPA_BSS_MASK_QUAL             BIT(5)
 +#define WPA_BSS_MASK_NOISE            BIT(6)
 +#define WPA_BSS_MASK_LEVEL            BIT(7)
 +#define WPA_BSS_MASK_TSF              BIT(8)
 +#define WPA_BSS_MASK_AGE              BIT(9)
 +#define WPA_BSS_MASK_IE                       BIT(10)
 +#define WPA_BSS_MASK_FLAGS            BIT(11)
 +#define WPA_BSS_MASK_SSID             BIT(12)
 +#define WPA_BSS_MASK_WPS_SCAN         BIT(13)
 +#define WPA_BSS_MASK_P2P_SCAN         BIT(14)
 +#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)
++#define WPA_BSS_MASK_FST              BIT(21)
 +
 +
 +/* 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 */
 +
 +/**
 + * wpa_ctrl_open - Open a control interface to wpa_supplicant/hostapd
 + * @ctrl_path: Path for UNIX domain sockets; ignored if UDP sockets are used.
 + * Returns: Pointer to abstract control interface data or %NULL on failure
 + *
 + * This function is used to open a control interface to wpa_supplicant/hostapd.
 + * ctrl_path is usually /var/run/wpa_supplicant or /var/run/hostapd. This path
 + * is configured in wpa_supplicant/hostapd and other programs using the control
 + * interface need to use matching path configuration.
 + */
 +struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path);
 +
++/**
++ * wpa_ctrl_open2 - Open a control interface to wpa_supplicant/hostapd
++ * @ctrl_path: Path for UNIX domain sockets; ignored if UDP sockets are used.
++ * @cli_path: Path for client UNIX domain sockets; ignored if UDP socket
++ *            is used.
++ * Returns: Pointer to abstract control interface data or %NULL on failure
++ *
++ * This function is used to open a control interface to wpa_supplicant/hostapd
++ * when the socket path for client need to be specified explicitly. Default
++ * ctrl_path is usually /var/run/wpa_supplicant or /var/run/hostapd and client
++ * socket path is /tmp.
++ */
++struct wpa_ctrl * wpa_ctrl_open2(const char *ctrl_path, const char *cli_path);
++
 +
 +/**
 + * wpa_ctrl_close - Close a control interface to wpa_supplicant/hostapd
 + * @ctrl: Control interface data from wpa_ctrl_open()
 + *
 + * This function is used to close a control interface.
 + */
 +void wpa_ctrl_close(struct wpa_ctrl *ctrl);
 +
 +
 +/**
 + * wpa_ctrl_request - Send a command to wpa_supplicant/hostapd
 + * @ctrl: Control interface data from wpa_ctrl_open()
 + * @cmd: Command; usually, ASCII text, e.g., "PING"
 + * @cmd_len: Length of the cmd in bytes
 + * @reply: Buffer for the response
 + * @reply_len: Reply buffer length
 + * @msg_cb: Callback function for unsolicited messages or %NULL if not used
 + * Returns: 0 on success, -1 on error (send or receive failed), -2 on timeout
 + *
 + * This function is used to send commands to wpa_supplicant/hostapd. Received
 + * response will be written to reply and reply_len is set to the actual length
 + * of the reply. This function will block for up to two seconds while waiting
 + * for the reply. If unsolicited messages are received, the blocking time may
 + * be longer.
 + *
 + * msg_cb can be used to register a callback function that will be called for
 + * unsolicited messages received while waiting for the command response. These
 + * messages may be received if wpa_ctrl_request() is called at the same time as
 + * wpa_supplicant/hostapd is sending such a message. This can happen only if
 + * the program has used wpa_ctrl_attach() to register itself as a monitor for
 + * event messages. Alternatively to msg_cb, programs can register two control
 + * interface connections and use one of them for commands and the other one for
 + * receiving event messages, in other words, call wpa_ctrl_attach() only for
 + * the control interface connection that will be used for event messages.
 + */
 +int wpa_ctrl_request(struct wpa_ctrl *ctrl, const char *cmd, size_t cmd_len,
 +                   char *reply, size_t *reply_len,
 +                   void (*msg_cb)(char *msg, size_t len));
 +
 +
 +/**
 + * wpa_ctrl_attach - Register as an event monitor for the control interface
 + * @ctrl: Control interface data from wpa_ctrl_open()
 + * Returns: 0 on success, -1 on failure, -2 on timeout
 + *
 + * This function registers the control interface connection as a monitor for
 + * wpa_supplicant/hostapd events. After a success wpa_ctrl_attach() call, the
 + * control interface connection starts receiving event messages that can be
 + * read with wpa_ctrl_recv().
 + */
 +int wpa_ctrl_attach(struct wpa_ctrl *ctrl);
 +
 +
 +/**
 + * wpa_ctrl_detach - Unregister event monitor from the control interface
 + * @ctrl: Control interface data from wpa_ctrl_open()
 + * Returns: 0 on success, -1 on failure, -2 on timeout
 + *
 + * This function unregisters the control interface connection as a monitor for
 + * wpa_supplicant/hostapd events, i.e., cancels the registration done with
 + * wpa_ctrl_attach().
 + */
 +int wpa_ctrl_detach(struct wpa_ctrl *ctrl);
 +
 +
 +/**
 + * wpa_ctrl_recv - Receive a pending control interface message
 + * @ctrl: Control interface data from wpa_ctrl_open()
 + * @reply: Buffer for the message data
 + * @reply_len: Length of the reply buffer
 + * Returns: 0 on success, -1 on failure
 + *
 + * 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.
 + */
 +int wpa_ctrl_recv(struct wpa_ctrl *ctrl, char *reply, size_t *reply_len);
 +
 +
 +/**
 + * wpa_ctrl_pending - Check whether there are pending event messages
 + * @ctrl: Control interface data from wpa_ctrl_open()
 + * Returns: 1 if there are pending messages, 0 if no, or -1 on error
 + *
 + * This function will check whether there are any pending control interface
 + * message available to be received with wpa_ctrl_recv(). wpa_ctrl_pending() is
 + * only used for event messages, i.e., wpa_ctrl_attach() must have been used to
 + * register the control interface as an event monitor.
 + */
 +int wpa_ctrl_pending(struct wpa_ctrl *ctrl);
 +
 +
 +/**
 + * wpa_ctrl_get_fd - Get file descriptor used by the control interface
 + * @ctrl: Control interface data from wpa_ctrl_open()
 + * Returns: File descriptor used for the connection
 + *
 + * This function can be used to get the file descriptor that is used for the
 + * control interface connection. The returned value can be used, e.g., with
 + * select() while waiting for multiple events.
 + *
 + * The returned file descriptor must not be used directly for sending or
 + * receiving packets; instead, the library functions wpa_ctrl_request() and
 + * wpa_ctrl_recv() must be used for this.
 + */
 +int wpa_ctrl_get_fd(struct wpa_ctrl *ctrl);
 +
 +#ifdef ANDROID
 +/**
 + * wpa_ctrl_cleanup() - Delete any local UNIX domain socket files that
 + * may be left over from clients that were previously connected to
 + * wpa_supplicant. This keeps these files from being orphaned in the
 + * event of crashes that prevented them from being removed as part
 + * of the normal orderly shutdown.
 + */
 +void wpa_ctrl_cleanup(void);
 +#endif /* ANDROID */
 +
 +#ifdef CONFIG_CTRL_IFACE_UDP
 +/* Port range for multiple wpa_supplicant instances and multiple VIFs */
 +#define WPA_CTRL_IFACE_PORT 9877
 +#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 */
 +
 +
 +#ifdef  __cplusplus
 +}
 +#endif
 +
 +#endif /* WPA_CTRL_H */
index f2d5662ff01e2f3baf8cf98d96d6a60045f08bea,0000000000000000000000000000000000000000..534c4bd78654347c092128b2636ff70accef4159
mode 100644,000000..100644
--- /dev/null
@@@ -1,779 -1,0 +1,809 @@@
 +/*
 + * Wrapper functions for crypto libraries
 + * 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.
 + *
 + * This file defines the cryptographic functions that need to be implemented
 + * for wpa_supplicant and hostapd. When TLS is not used, internal
 + * implementation of MD5, SHA1, and AES is used and no external libraries are
 + * required. When TLS is enabled (e.g., by enabling EAP-TLS or EAP-PEAP), the
 + * crypto library used by the TLS implementation is expected to be used for
 + * non-TLS needs, too, in order to save space by not implementing these
 + * functions twice.
 + *
 + * Wrapper code for using each crypto library is in its own file (crypto*.c)
 + * and one of these files is build and linked in to provide the functions
 + * defined here.
 + */
 +
 +#ifndef CRYPTO_H
 +#define CRYPTO_H
 +
 +/**
 + * md4_vector - MD4 hash for data vector
 + * @num_elem: Number of elements in the data vector
 + * @addr: Pointers to the data areas
 + * @len: Lengths of the data blocks
 + * @mac: Buffer for the hash
 + * Returns: 0 on success, -1 on failure
 + */
 +int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac);
 +
 +/**
 + * md5_vector - MD5 hash for data vector
 + * @num_elem: Number of elements in the data vector
 + * @addr: Pointers to the data areas
 + * @len: Lengths of the data blocks
 + * @mac: Buffer for the hash
 + * Returns: 0 on success, -1 on failure
 + */
 +int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac);
 +
 +
 +/**
 + * sha1_vector - SHA-1 hash for data vector
 + * @num_elem: Number of elements in the data vector
 + * @addr: Pointers to the data areas
 + * @len: Lengths of the data blocks
 + * @mac: Buffer for the hash
 + * Returns: 0 on success, -1 on failure
 + */
 +int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len,
 +              u8 *mac);
 +
 +/**
 + * fips186_2-prf - NIST FIPS Publication 186-2 change notice 1 PRF
 + * @seed: Seed/key for the PRF
 + * @seed_len: Seed length in bytes
 + * @x: Buffer for PRF output
 + * @xlen: Output length in bytes
 + * Returns: 0 on success, -1 on failure
 + *
 + * This function implements random number generation specified in NIST FIPS
 + * Publication 186-2 for EAP-SIM. This PRF uses a function that is similar to
 + * SHA-1, but has different message padding.
 + */
 +int __must_check fips186_2_prf(const u8 *seed, size_t seed_len, u8 *x,
 +                             size_t xlen);
 +
 +/**
 + * sha256_vector - SHA256 hash for data vector
 + * @num_elem: Number of elements in the data vector
 + * @addr: Pointers to the data areas
 + * @len: Lengths of the data blocks
 + * @mac: Buffer for the hash
 + * Returns: 0 on success, -1 on failure
 + */
 +int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len,
 +                u8 *mac);
 +
 +/**
 + * des_encrypt - Encrypt one block with DES
 + * @clear: 8 octets (in)
 + * @key: 7 octets (in) (no parity bits included)
 + * @cypher: 8 octets (out)
 + */
 +void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher);
 +
 +/**
 + * aes_encrypt_init - Initialize AES for encryption
 + * @key: Encryption key
 + * @len: Key length in bytes (usually 16, i.e., 128 bits)
 + * Returns: Pointer to context data or %NULL on failure
 + */
 +void * aes_encrypt_init(const u8 *key, size_t len);
 +
 +/**
 + * aes_encrypt - Encrypt one AES block
 + * @ctx: Context pointer from aes_encrypt_init()
 + * @plain: Plaintext data to be encrypted (16 bytes)
 + * @crypt: Buffer for the encrypted data (16 bytes)
 + */
 +void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt);
 +
 +/**
 + * aes_encrypt_deinit - Deinitialize AES encryption
 + * @ctx: Context pointer from aes_encrypt_init()
 + */
 +void aes_encrypt_deinit(void *ctx);
 +
 +/**
 + * aes_decrypt_init - Initialize AES for decryption
 + * @key: Decryption key
 + * @len: Key length in bytes (usually 16, i.e., 128 bits)
 + * Returns: Pointer to context data or %NULL on failure
 + */
 +void * aes_decrypt_init(const u8 *key, size_t len);
 +
 +/**
 + * aes_decrypt - Decrypt one AES block
 + * @ctx: Context pointer from aes_encrypt_init()
 + * @crypt: Encrypted data (16 bytes)
 + * @plain: Buffer for the decrypted data (16 bytes)
 + */
 +void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain);
 +
 +/**
 + * aes_decrypt_deinit - Deinitialize AES decryption
 + * @ctx: Context pointer from aes_encrypt_init()
 + */
 +void aes_decrypt_deinit(void *ctx);
 +
 +
 +enum crypto_hash_alg {
 +      CRYPTO_HASH_ALG_MD5, CRYPTO_HASH_ALG_SHA1,
 +      CRYPTO_HASH_ALG_HMAC_MD5, CRYPTO_HASH_ALG_HMAC_SHA1,
 +      CRYPTO_HASH_ALG_SHA256, CRYPTO_HASH_ALG_HMAC_SHA256
 +};
 +
 +struct crypto_hash;
 +
 +/**
 + * crypto_hash_init - Initialize hash/HMAC function
 + * @alg: Hash algorithm
 + * @key: Key for keyed hash (e.g., HMAC) or %NULL if not needed
 + * @key_len: Length of the key in bytes
 + * Returns: Pointer to hash context to use with other hash functions or %NULL
 + * on failure
 + *
 + * This function is only used with internal TLSv1 implementation
 + * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
 + * to implement this.
 + */
 +struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key,
 +                                    size_t key_len);
 +
 +/**
 + * crypto_hash_update - Add data to hash calculation
 + * @ctx: Context pointer from crypto_hash_init()
 + * @data: Data buffer to add
 + * @len: Length of the buffer
 + *
 + * This function is only used with internal TLSv1 implementation
 + * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
 + * to implement this.
 + */
 +void crypto_hash_update(struct crypto_hash *ctx, const u8 *data, size_t len);
 +
 +/**
 + * crypto_hash_finish - Complete hash calculation
 + * @ctx: Context pointer from crypto_hash_init()
 + * @hash: Buffer for hash value or %NULL if caller is just freeing the hash
 + * context
 + * @len: Pointer to length of the buffer or %NULL if caller is just freeing the
 + * hash context; on return, this is set to the actual length of the hash value
 + * Returns: 0 on success, -1 if buffer is too small (len set to needed length),
 + * or -2 on other failures (including failed crypto_hash_update() operations)
 + *
 + * This function calculates the hash value and frees the context buffer that
 + * was used for hash calculation.
 + *
 + * This function is only used with internal TLSv1 implementation
 + * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
 + * to implement this.
 + */
 +int crypto_hash_finish(struct crypto_hash *ctx, u8 *hash, size_t *len);
 +
 +
 +enum crypto_cipher_alg {
 +      CRYPTO_CIPHER_NULL = 0, CRYPTO_CIPHER_ALG_AES, CRYPTO_CIPHER_ALG_3DES,
 +      CRYPTO_CIPHER_ALG_DES, CRYPTO_CIPHER_ALG_RC2, CRYPTO_CIPHER_ALG_RC4
 +};
 +
 +struct crypto_cipher;
 +
 +/**
 + * crypto_cipher_init - Initialize block/stream cipher function
 + * @alg: Cipher algorithm
 + * @iv: Initialization vector for block ciphers or %NULL for stream ciphers
 + * @key: Cipher key
 + * @key_len: Length of key in bytes
 + * Returns: Pointer to cipher context to use with other cipher functions or
 + * %NULL on failure
 + *
 + * This function is only used with internal TLSv1 implementation
 + * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
 + * to implement this.
 + */
 +struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg,
 +                                        const u8 *iv, const u8 *key,
 +                                        size_t key_len);
 +
 +/**
 + * crypto_cipher_encrypt - Cipher encrypt
 + * @ctx: Context pointer from crypto_cipher_init()
 + * @plain: Plaintext to cipher
 + * @crypt: Resulting ciphertext
 + * @len: Length of the plaintext
 + * Returns: 0 on success, -1 on failure
 + *
 + * This function is only used with internal TLSv1 implementation
 + * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
 + * to implement this.
 + */
 +int __must_check crypto_cipher_encrypt(struct crypto_cipher *ctx,
 +                                     const u8 *plain, u8 *crypt, size_t len);
 +
 +/**
 + * crypto_cipher_decrypt - Cipher decrypt
 + * @ctx: Context pointer from crypto_cipher_init()
 + * @crypt: Ciphertext to decrypt
 + * @plain: Resulting plaintext
 + * @len: Length of the cipher text
 + * Returns: 0 on success, -1 on failure
 + *
 + * This function is only used with internal TLSv1 implementation
 + * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
 + * to implement this.
 + */
 +int __must_check crypto_cipher_decrypt(struct crypto_cipher *ctx,
 +                                     const u8 *crypt, u8 *plain, size_t len);
 +
 +/**
 + * crypto_cipher_decrypt - Free cipher context
 + * @ctx: Context pointer from crypto_cipher_init()
 + *
 + * This function is only used with internal TLSv1 implementation
 + * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
 + * to implement this.
 + */
 +void crypto_cipher_deinit(struct crypto_cipher *ctx);
 +
 +
 +struct crypto_public_key;
 +struct crypto_private_key;
 +
 +/**
 + * crypto_public_key_import - Import an RSA public key
 + * @key: Key buffer (DER encoded RSA public key)
 + * @len: Key buffer length in bytes
 + * Returns: Pointer to the public key or %NULL on failure
 + *
 + * This function can just return %NULL if the crypto library supports X.509
 + * parsing. In that case, crypto_public_key_from_cert() is used to import the
 + * public key from a certificate.
 + *
 + * This function is only used with internal TLSv1 implementation
 + * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
 + * to implement this.
 + */
 +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)
 + * @len: Key buffer length in bytes
 + * @passwd: Key encryption password or %NULL if key is not encrypted
 + * Returns: Pointer to the private key or %NULL on failure
 + *
 + * This function is only used with internal TLSv1 implementation
 + * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
 + * to implement this.
 + */
 +struct crypto_private_key * crypto_private_key_import(const u8 *key,
 +                                                    size_t len,
 +                                                    const char *passwd);
 +
 +/**
 + * crypto_public_key_from_cert - Import an RSA public key from a certificate
 + * @buf: DER encoded X.509 certificate
 + * @len: Certificate buffer length in bytes
 + * Returns: Pointer to public key or %NULL on failure
 + *
 + * This function can just return %NULL if the crypto library does not support
 + * X.509 parsing. In that case, internal code will be used to parse the
 + * certificate and public key is imported using crypto_public_key_import().
 + *
 + * This function is only used with internal TLSv1 implementation
 + * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
 + * to implement this.
 + */
 +struct crypto_public_key * crypto_public_key_from_cert(const u8 *buf,
 +                                                     size_t len);
 +
 +/**
 + * crypto_public_key_encrypt_pkcs1_v15 - Public key encryption (PKCS #1 v1.5)
 + * @key: Public key
 + * @in: Plaintext buffer
 + * @inlen: Length of plaintext buffer in bytes
 + * @out: Output buffer for encrypted data
 + * @outlen: Length of output buffer in bytes; set to used length on success
 + * Returns: 0 on success, -1 on failure
 + *
 + * This function is only used with internal TLSv1 implementation
 + * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
 + * to implement this.
 + */
 +int __must_check crypto_public_key_encrypt_pkcs1_v15(
 +      struct crypto_public_key *key, const u8 *in, size_t inlen,
 +      u8 *out, size_t *outlen);
 +
 +/**
 + * crypto_private_key_decrypt_pkcs1_v15 - Private key decryption (PKCS #1 v1.5)
 + * @key: Private key
 + * @in: Encrypted buffer
 + * @inlen: Length of encrypted buffer in bytes
 + * @out: Output buffer for encrypted data
 + * @outlen: Length of output buffer in bytes; set to used length on success
 + * Returns: 0 on success, -1 on failure
 + *
 + * This function is only used with internal TLSv1 implementation
 + * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
 + * to implement this.
 + */
 +int __must_check crypto_private_key_decrypt_pkcs1_v15(
 +      struct crypto_private_key *key, const u8 *in, size_t inlen,
 +      u8 *out, size_t *outlen);
 +
 +/**
 + * crypto_private_key_sign_pkcs1 - Sign with private key (PKCS #1)
 + * @key: Private key from crypto_private_key_import()
 + * @in: Plaintext buffer
 + * @inlen: Length of plaintext buffer in bytes
 + * @out: Output buffer for encrypted (signed) data
 + * @outlen: Length of output buffer in bytes; set to used length on success
 + * Returns: 0 on success, -1 on failure
 + *
 + * This function is only used with internal TLSv1 implementation
 + * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
 + * to implement this.
 + */
 +int __must_check crypto_private_key_sign_pkcs1(struct crypto_private_key *key,
 +                                             const u8 *in, size_t inlen,
 +                                             u8 *out, size_t *outlen);
 +
 +/**
 + * crypto_public_key_free - Free public key
 + * @key: Public key
 + *
 + * This function is only used with internal TLSv1 implementation
 + * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
 + * to implement this.
 + */
 +void crypto_public_key_free(struct crypto_public_key *key);
 +
 +/**
 + * crypto_private_key_free - Free private key
 + * @key: Private key from crypto_private_key_import()
 + *
 + * This function is only used with internal TLSv1 implementation
 + * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
 + * to implement this.
 + */
 +void crypto_private_key_free(struct crypto_private_key *key);
 +
 +/**
 + * crypto_public_key_decrypt_pkcs1 - Decrypt PKCS #1 signature
 + * @key: Public key
 + * @crypt: Encrypted signature data (using the private key)
 + * @crypt_len: Encrypted signature data length
 + * @plain: Buffer for plaintext (at least crypt_len bytes)
 + * @plain_len: Plaintext length (max buffer size on input, real len on output);
 + * Returns: 0 on success, -1 on failure
 + */
 +int __must_check crypto_public_key_decrypt_pkcs1(
 +      struct crypto_public_key *key, const u8 *crypt, size_t crypt_len,
 +      u8 *plain, size_t *plain_len);
 +
 +/**
 + * crypto_global_init - Initialize crypto wrapper
 + *
 + * This function is only used with internal TLSv1 implementation
 + * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
 + * to implement this.
 + */
 +int __must_check crypto_global_init(void);
 +
 +/**
 + * crypto_global_deinit - Deinitialize crypto wrapper
 + *
 + * This function is only used with internal TLSv1 implementation
 + * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
 + * to implement this.
 + */
 +void crypto_global_deinit(void);
 +
 +/**
 + * crypto_mod_exp - Modular exponentiation of large integers
 + * @base: Base integer (big endian byte array)
 + * @base_len: Length of base integer in bytes
 + * @power: Power integer (big endian byte array)
 + * @power_len: Length of power integer in bytes
 + * @modulus: Modulus integer (big endian byte array)
 + * @modulus_len: Length of modulus integer in bytes
 + * @result: Buffer for the result
 + * @result_len: Result length (max buffer size on input, real len on output)
 + * Returns: 0 on success, -1 on failure
 + *
 + * This function calculates result = base ^ power mod modulus. modules_len is
 + * used as the maximum size of modulus buffer. It is set to the used size on
 + * success.
 + *
 + * This function is only used with internal TLSv1 implementation
 + * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
 + * to implement this.
 + */
 +int __must_check 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);
 +
 +/**
 + * rc4_skip - XOR RC4 stream to given data with skip-stream-start
 + * @key: RC4 key
 + * @keylen: RC4 key length
 + * @skip: number of bytes to skip from the beginning of the RC4 stream
 + * @data: data to be XOR'ed with RC4 stream
 + * @data_len: buf length
 + * Returns: 0 on success, -1 on failure
 + *
 + * Generate RC4 pseudo random stream for the given key, skip beginning of the
 + * stream, and XOR the end result with the data buffer to perform RC4
 + * encryption/decryption.
 + */
 +int rc4_skip(const u8 *key, size_t keylen, size_t skip,
 +           u8 *data, size_t data_len);
 +
 +/**
 + * crypto_get_random - Generate cryptographically strong pseudy-random bytes
 + * @buf: Buffer for data
 + * @len: Number of bytes to generate
 + * Returns: 0 on success, -1 on failure
 + *
 + * If the PRNG does not have enough entropy to ensure unpredictable byte
 + * sequence, this functions must return -1.
 + */
 +int crypto_get_random(void *buf, size_t len);
 +
 +
 +/**
 + * struct crypto_bignum - bignum
 + *
 + * Internal data structure for bignum implementation. The contents is specific
 + * to the used crypto library.
 + */
 +struct crypto_bignum;
 +
 +/**
 + * crypto_bignum_init - Allocate memory for bignum
 + * Returns: Pointer to allocated bignum or %NULL on failure
 + */
 +struct crypto_bignum * crypto_bignum_init(void);
 +
 +/**
 + * crypto_bignum_init_set - Allocate memory for bignum and set the value
 + * @buf: Buffer with unsigned binary value
 + * @len: Length of buf in octets
 + * Returns: Pointer to allocated bignum or %NULL on failure
 + */
 +struct crypto_bignum * crypto_bignum_init_set(const u8 *buf, size_t len);
 +
 +/**
 + * crypto_bignum_deinit - Free bignum
 + * @n: Bignum from crypto_bignum_init() or crypto_bignum_init_set()
 + * @clear: Whether to clear the value from memory
 + */
 +void crypto_bignum_deinit(struct crypto_bignum *n, int clear);
 +
 +/**
 + * crypto_bignum_to_bin - Set binary buffer to unsigned bignum
 + * @a: Bignum
 + * @buf: Buffer for the binary number
 + * @len: Length of @buf in octets
 + * @padlen: Length in octets to pad the result to or 0 to indicate no padding
 + * Returns: Number of octets written on success, -1 on failure
 + */
 +int crypto_bignum_to_bin(const struct crypto_bignum *a,
 +                       u8 *buf, size_t buflen, size_t padlen);
 +
 +/**
 + * crypto_bignum_add - c = a + b
 + * @a: Bignum
 + * @b: Bignum
 + * @c: Bignum; used to store the result of a + b
 + * Returns: 0 on success, -1 on failure
 + */
 +int crypto_bignum_add(const struct crypto_bignum *a,
 +                    const struct crypto_bignum *b,
 +                    struct crypto_bignum *c);
 +
 +/**
 + * crypto_bignum_mod - c = a % b
 + * @a: Bignum
 + * @b: Bignum
 + * @c: Bignum; used to store the result of a % b
 + * Returns: 0 on success, -1 on failure
 + */
 +int crypto_bignum_mod(const struct crypto_bignum *a,
 +                    const struct crypto_bignum *b,
 +                    struct crypto_bignum *c);
 +
 +/**
 + * crypto_bignum_exptmod - Modular exponentiation: d = a^b (mod c)
 + * @a: Bignum; base
 + * @b: Bignum; exponent
 + * @c: Bignum; modulus
 + * @d: Bignum; used to store the result of a^b (mod c)
 + * Returns: 0 on success, -1 on failure
 + */
 +int crypto_bignum_exptmod(const struct crypto_bignum *a,
 +                        const struct crypto_bignum *b,
 +                        const struct crypto_bignum *c,
 +                        struct crypto_bignum *d);
 +
 +/**
 + * crypto_bignum_inverse - Inverse a bignum so that a * c = 1 (mod b)
 + * @a: Bignum
 + * @b: Bignum
 + * @c: Bignum; used to store the result
 + * Returns: 0 on success, -1 on failure
 + */
 +int crypto_bignum_inverse(const struct crypto_bignum *a,
 +                        const struct crypto_bignum *b,
 +                        struct crypto_bignum *c);
 +
 +/**
 + * crypto_bignum_sub - c = a - b
 + * @a: Bignum
 + * @b: Bignum
 + * @c: Bignum; used to store the result of a - b
 + * Returns: 0 on success, -1 on failure
 + */
 +int crypto_bignum_sub(const struct crypto_bignum *a,
 +                    const struct crypto_bignum *b,
 +                    struct crypto_bignum *c);
 +
 +/**
 + * crypto_bignum_div - c = a / b
 + * @a: Bignum
 + * @b: Bignum
 + * @c: Bignum; used to store the result of a / b
 + * Returns: 0 on success, -1 on failure
 + */
 +int crypto_bignum_div(const struct crypto_bignum *a,
 +                    const struct crypto_bignum *b,
 +                    struct crypto_bignum *c);
 +
 +/**
 + * crypto_bignum_mulmod - d = a * b (mod c)
 + * @a: Bignum
 + * @b: Bignum
 + * @c: Bignum
 + * @d: Bignum; used to store the result of (a * b) % c
 + * Returns: 0 on success, -1 on failure
 + */
 +int crypto_bignum_mulmod(const struct crypto_bignum *a,
 +                       const struct crypto_bignum *b,
 +                       const struct crypto_bignum *c,
 +                       struct crypto_bignum *d);
 +
 +/**
 + * crypto_bignum_cmp - Compare two bignums
 + * @a: Bignum
 + * @b: Bignum
 + * Returns: -1 if a < b, 0 if a == b, or 1 if a > b
 + */
 +int crypto_bignum_cmp(const struct crypto_bignum *a,
 +                    const struct crypto_bignum *b);
 +
 +/**
 + * crypto_bignum_bits - Get size of a bignum in bits
 + * @a: Bignum
 + * Returns: Number of bits in the bignum
 + */
 +int crypto_bignum_bits(const struct crypto_bignum *a);
 +
 +/**
 + * crypto_bignum_is_zero - Is the given bignum zero
 + * @a: Bignum
 + * Returns: 1 if @a is zero or 0 if not
 + */
 +int crypto_bignum_is_zero(const struct crypto_bignum *a);
 +
 +/**
 + * crypto_bignum_is_one - Is the given bignum one
 + * @a: Bignum
 + * Returns: 1 if @a is one or 0 if not
 + */
 +int crypto_bignum_is_one(const struct crypto_bignum *a);
 +
++/**
++ * crypto_bignum_legendre - Compute the Legendre symbol (a/p)
++ * @a: Bignum
++ * @p: Bignum
++ * Returns: Legendre symbol -1,0,1 on success; -2 on calculation failure
++ */
++int crypto_bignum_legendre(const struct crypto_bignum *a,
++                         const struct crypto_bignum *p);
++
 +/**
 + * struct crypto_ec - Elliptic curve context
 + *
 + * Internal data structure for EC implementation. The contents is specific
 + * to the used crypto library.
 + */
 +struct crypto_ec;
 +
 +/**
 + * crypto_ec_init - Initialize elliptic curve context
 + * @group: Identifying number for the ECC group (IANA "Group Description"
 + *    attribute registrty for RFC 2409)
 + * Returns: Pointer to EC context or %NULL on failure
 + */
 +struct crypto_ec * crypto_ec_init(int group);
 +
 +/**
 + * crypto_ec_deinit - Deinitialize elliptic curve context
 + * @e: EC context from crypto_ec_init()
 + */
 +void crypto_ec_deinit(struct crypto_ec *e);
 +
 +/**
 + * crypto_ec_prime_len - Get length of the prime in octets
 + * @e: EC context from crypto_ec_init()
 + * Returns: Length of the prime defining the group
 + */
 +size_t crypto_ec_prime_len(struct crypto_ec *e);
 +
 +/**
 + * crypto_ec_prime_len_bits - Get length of the prime in bits
 + * @e: EC context from crypto_ec_init()
 + * Returns: Length of the prime defining the group in bits
 + */
 +size_t crypto_ec_prime_len_bits(struct crypto_ec *e);
 +
 +/**
 + * crypto_ec_get_prime - Get prime defining an EC group
 + * @e: EC context from crypto_ec_init()
 + * Returns: Prime (bignum) defining the group
 + */
 +const struct crypto_bignum * crypto_ec_get_prime(struct crypto_ec *e);
 +
 +/**
 + * crypto_ec_get_order - Get order of an EC group
 + * @e: EC context from crypto_ec_init()
 + * Returns: Order (bignum) of the group
 + */
 +const struct crypto_bignum * crypto_ec_get_order(struct crypto_ec *e);
 +
 +/**
 + * struct crypto_ec_point - Elliptic curve point
 + *
 + * Internal data structure for EC implementation to represent a point. The
 + * contents is specific to the used crypto library.
 + */
 +struct crypto_ec_point;
 +
 +/**
 + * crypto_ec_point_init - Initialize data for an EC point
 + * @e: EC context from crypto_ec_init()
 + * Returns: Pointer to EC point data or %NULL on failure
 + */
 +struct crypto_ec_point * crypto_ec_point_init(struct crypto_ec *e);
 +
 +/**
 + * crypto_ec_point_deinit - Deinitialize EC point data
 + * @p: EC point data from crypto_ec_point_init()
 + * @clear: Whether to clear the EC point value from memory
 + */
 +void crypto_ec_point_deinit(struct crypto_ec_point *p, int clear);
 +
 +/**
 + * crypto_ec_point_to_bin - Write EC point value as binary data
 + * @e: EC context from crypto_ec_init()
 + * @p: EC point data from crypto_ec_point_init()
 + * @x: Buffer for writing the binary data for x coordinate or %NULL if not used
 + * @y: Buffer for writing the binary data for y coordinate or %NULL if not used
 + * Returns: 0 on success, -1 on failure
 + *
 + * This function can be used to write an EC point as binary data in a format
 + * that has the x and y coordinates in big endian byte order fields padded to
 + * the length of the prime defining the group.
 + */
 +int crypto_ec_point_to_bin(struct crypto_ec *e,
 +                         const struct crypto_ec_point *point, u8 *x, u8 *y);
 +
 +/**
 + * crypto_ec_point_from_bin - Create EC point from binary data
 + * @e: EC context from crypto_ec_init()
 + * @val: Binary data to read the EC point from
 + * Returns: Pointer to EC point data or %NULL on failure
 + *
 + * This function readers x and y coordinates of the EC point from the provided
 + * buffer assuming the values are in big endian byte order with fields padded to
 + * the length of the prime defining the group.
 + */
 +struct crypto_ec_point * crypto_ec_point_from_bin(struct crypto_ec *e,
 +                                                const u8 *val);
 +
 +/**
 + * crypto_bignum_add - c = a + b
 + * @e: EC context from crypto_ec_init()
 + * @a: Bignum
 + * @b: Bignum
 + * @c: Bignum; used to store the result of a + b
 + * Returns: 0 on success, -1 on failure
 + */
 +int crypto_ec_point_add(struct crypto_ec *e, const struct crypto_ec_point *a,
 +                      const struct crypto_ec_point *b,
 +                      struct crypto_ec_point *c);
 +
 +/**
 + * crypto_bignum_mul - res = b * p
 + * @e: EC context from crypto_ec_init()
 + * @p: EC point
 + * @b: Bignum
 + * @res: EC point; used to store the result of b * p
 + * Returns: 0 on success, -1 on failure
 + */
 +int crypto_ec_point_mul(struct crypto_ec *e, const struct crypto_ec_point *p,
 +                      const struct crypto_bignum *b,
 +                      struct crypto_ec_point *res);
 +
 +/**
 + * crypto_ec_point_invert - Compute inverse of an EC point
 + * @e: EC context from crypto_ec_init()
 + * @p: EC point to invert (and result of the operation)
 + * Returns: 0 on success, -1 on failure
 + */
 +int crypto_ec_point_invert(struct crypto_ec *e, struct crypto_ec_point *p);
 +
 +/**
 + * crypto_ec_point_solve_y_coord - Solve y coordinate for an x coordinate
 + * @e: EC context from crypto_ec_init()
 + * @p: EC point to use for the returning the result
 + * @x: x coordinate
 + * @y_bit: y-bit (0 or 1) for selecting the y value to use
 + * Returns: 0 on success, -1 on failure
 + */
 +int crypto_ec_point_solve_y_coord(struct crypto_ec *e,
 +                                struct crypto_ec_point *p,
 +                                const struct crypto_bignum *x, int y_bit);
 +
++/**
++ * crypto_ec_point_compute_y_sqr - Compute y^2 = x^3 + ax + b
++ * @e: EC context from crypto_ec_init()
++ * @x: x coordinate
++ * Returns: y^2 on success, %NULL failure
++ */
++struct crypto_bignum *
++crypto_ec_point_compute_y_sqr(struct crypto_ec *e,
++                            const struct crypto_bignum *x);
++
 +/**
 + * crypto_ec_point_is_at_infinity - Check whether EC point is neutral element
 + * @e: EC context from crypto_ec_init()
 + * @p: EC point
 + * Returns: 1 if the specified EC point is the neutral element of the group or
 + *    0 if not
 + */
 +int crypto_ec_point_is_at_infinity(struct crypto_ec *e,
 +                                 const struct crypto_ec_point *p);
 +
 +/**
 + * crypto_ec_point_is_on_curve - Check whether EC point is on curve
 + * @e: EC context from crypto_ec_init()
 + * @p: EC point
 + * Returns: 1 if the specified EC point is on the curve or 0 if not
 + */
 +int crypto_ec_point_is_on_curve(struct crypto_ec *e,
 +                              const struct crypto_ec_point *p);
 +
++/**
++ * crypto_ec_point_cmp - Compare two EC points
++ * @e: EC context from crypto_ec_init()
++ * @a: EC point
++ * @b: EC point
++ * Returns: 0 on equal, non-zero otherwise
++ */
++int crypto_ec_point_cmp(const struct crypto_ec *e,
++                      const struct crypto_ec_point *a,
++                      const struct crypto_ec_point *b);
++
 +#endif /* CRYPTO_H */
index 7137c27d0e8c516168a87147f049c4afac94b138,0000000000000000000000000000000000000000..581005df3e39ec00b9e2b87e12b6c076c6a3cd33
mode 100644,000000..100644
--- /dev/null
@@@ -1,1679 -1,0 +1,1702 @@@
- static struct omac1_test_vector omac1_test_vectors[] =
 +/*
 + * crypto 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 "crypto/aes_siv.h"
 +#include "crypto/aes_wrap.h"
 +#include "crypto/aes.h"
 +#include "crypto/ms_funcs.h"
 +#include "crypto/crypto.h"
 +#include "crypto/sha1.h"
 +#include "crypto/sha256.h"
 +
 +
 +static int test_siv(void)
 +{
 +#ifdef CONFIG_MESH
 +      /* RFC 5297, A.1. Deterministic Authenticated Encryption Example */
 +      u8 key[] = {
 +              0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8,
 +              0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0,
 +              0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
 +              0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
 +      };
 +      u8 ad[] = {
 +              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 plaintext[] = {
 +              0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88,
 +              0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee
 +      };
 +      u8 iv_c[] = {
 +              0x85, 0x63, 0x2d, 0x07, 0xc6, 0xe8, 0xf3, 0x7f,
 +              0x95, 0x0a, 0xcd, 0x32, 0x0a, 0x2e, 0xcc, 0x93,
 +              0x40, 0xc0, 0x2b, 0x96, 0x90, 0xc4, 0xdc, 0x04,
 +              0xda, 0xef, 0x7f, 0x6a, 0xfe, 0x5c
 +      };
 +      /* RFC 5297, A.2. Nonce-Based Authenticated Encryption Example */
 +      u8 key_2[] = {
 +              0x7f, 0x7e, 0x7d, 0x7c, 0x7b, 0x7a, 0x79, 0x78,
 +              0x77, 0x76, 0x75, 0x74, 0x73, 0x72, 0x71, 0x70,
 +              0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
 +              0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f
 +      };
 +      u8 ad1_2[] = {
 +              0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
 +              0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
 +              0xde, 0xad, 0xda, 0xda, 0xde, 0xad, 0xda, 0xda,
 +              0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
 +              0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x00
 +      };
 +      u8 ad2_2[] = {
 +              0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80,
 +              0x90, 0xa0
 +      };
 +      u8 nonce_2[] = {
 +              0x09, 0xf9, 0x11, 0x02, 0x9d, 0x74, 0xe3, 0x5b,
 +              0xd8, 0x41, 0x56, 0xc5, 0x63, 0x56, 0x88, 0xc0
 +      };
 +      u8 plaintext_2[] = {
 +              0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20,
 +              0x73, 0x6f, 0x6d, 0x65, 0x20, 0x70, 0x6c, 0x61,
 +              0x69, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x20, 0x74,
 +              0x6f, 0x20, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70,
 +              0x74, 0x20, 0x75, 0x73, 0x69, 0x6e, 0x67, 0x20,
 +              0x53, 0x49, 0x56, 0x2d, 0x41, 0x45, 0x53
 +      };
 +      u8 iv_c_2[] = {
 +              0x7b, 0xdb, 0x6e, 0x3b, 0x43, 0x26, 0x67, 0xeb,
 +              0x06, 0xf4, 0xd1, 0x4b, 0xff, 0x2f, 0xbd, 0x0f,
 +              0xcb, 0x90, 0x0f, 0x2f, 0xdd, 0xbe, 0x40, 0x43,
 +              0x26, 0x60, 0x19, 0x65, 0xc8, 0x89, 0xbf, 0x17,
 +              0xdb, 0xa7, 0x7c, 0xeb, 0x09, 0x4f, 0xa6, 0x63,
 +              0xb7, 0xa3, 0xf7, 0x48, 0xba, 0x8a, 0xf8, 0x29,
 +              0xea, 0x64, 0xad, 0x54, 0x4a, 0x27, 0x2e, 0x9c,
 +              0x48, 0x5b, 0x62, 0xa3, 0xfd, 0x5c, 0x0d
 +      };
 +      u8 out[2 * AES_BLOCK_SIZE + sizeof(plaintext_2)];
 +      const u8 *addr[3];
 +      size_t len[3];
 +
 +      /* RFC 5297, A.1. Deterministic Authenticated Encryption Example */
 +      addr[0] = ad;
 +      len[0] = sizeof(ad);
 +
 +      if (aes_siv_encrypt(key, plaintext, sizeof(plaintext),
 +                          1, addr, len, out)) {
 +              wpa_printf(MSG_ERROR, "AES-SIV mode encryption failed");
 +              return 1;
 +      }
 +      if (os_memcmp(out, iv_c, sizeof(iv_c)) != 0) {
 +              wpa_printf(MSG_ERROR,
 +                         "AES-SIV mode encryption returned invalid cipher text");
 +              return 1;
 +      }
 +
 +      if (aes_siv_decrypt(key, iv_c, sizeof(iv_c), 1, addr, len, out)) {
 +              wpa_printf(MSG_ERROR, "AES-SIV mode decryption failed");
 +              return 1;
 +      }
 +      if (os_memcmp(out, plaintext, sizeof(plaintext)) != 0) {
 +              wpa_printf(MSG_ERROR,
 +                         "AES-SIV mode decryption returned invalid plain text");
 +              return 1;
 +      }
 +
 +      /* RFC 5297, A.2. Nonce-Based Authenticated Encryption Example */
 +      addr[0] = ad1_2;
 +      len[0] = sizeof(ad1_2);
 +      addr[1] = ad2_2;
 +      len[1] = sizeof(ad2_2);
 +      addr[2] = nonce_2;
 +      len[2] = sizeof(nonce_2);
 +
 +      if (aes_siv_encrypt(key_2, plaintext_2, sizeof(plaintext_2),
 +                          3, addr, len, out)) {
 +              wpa_printf(MSG_ERROR, "AES-SIV mode encryption failed");
 +              return 1;
 +      }
 +      if (os_memcmp(out, iv_c_2, sizeof(iv_c_2)) != 0) {
 +              wpa_printf(MSG_ERROR,
 +                         "AES-SIV mode encryption returned invalid cipher text");
 +              return 1;
 +      }
 +
 +      if (aes_siv_decrypt(key_2, iv_c_2, sizeof(iv_c_2), 3, addr, len, out)) {
 +              wpa_printf(MSG_ERROR, "AES-SIV mode decryption failed");
 +              return 1;
 +      }
 +      if (os_memcmp(out, plaintext_2, sizeof(plaintext_2)) != 0) {
 +              wpa_printf(MSG_ERROR,
 +                         "AES-SIV mode decryption returned invalid plain text");
 +              return 1;
 +      }
 +
 +      wpa_printf(MSG_INFO, "AES-SIV test cases passed");
 +#endif /* CONFIG_MESH */
 +
 +      return 0;
 +}
 +
 +
 +/* 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
 + */
 +
 +struct omac1_test_vector {
 +      u8 k[16];
 +      u8 msg[64];
 +      int msg_len;
 +      u8 tag[16];
 +};
 +
- static int test_omac1_vector(struct omac1_test_vector *tv, unsigned int i)
++static const struct omac1_test_vector omac1_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 }
 +      },
 +};
 +
 +
- static u8 key0[] =
++static int test_omac1_vector(const struct omac1_test_vector *tv,
++                           unsigned int i)
 +{
 +      u8 key[] = {
 +              0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
 +              0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c
 +      };
 +      u8 msg[] = { 0x12, 0x34, 0x56 };
 +      u8 result[24], result2[24];
 +      const u8 *addr[3];
 +      size_t len[3];
 +
 +      if (omac1_aes_128(tv->k, tv->msg, tv->msg_len, result) ||
 +          os_memcmp(result, tv->tag, 16) != 0) {
 +              wpa_printf(MSG_ERROR, "OMAC1-AES-128 test vector %u failed", i);
 +              return 1;
 +      }
 +
 +      if (tv->msg_len > 1) {
 +
 +              addr[0] = tv->msg;
 +              len[0] = 1;
 +              addr[1] = tv->msg + 1;
 +              len[1] = tv->msg_len - 1;
 +
 +              if (omac1_aes_128_vector(tv->k, 2, addr, len, result) ||
 +                  os_memcmp(result, tv->tag, 16) != 0) {
 +                      wpa_printf(MSG_ERROR,
 +                                 "OMAC1-AES-128(vector) test vector %u failed",
 +                                 i);
 +                      return 1;
 +              }
 +
 +              addr[0] = tv->msg;
 +              len[0] = tv->msg_len - 2;
 +              addr[1] = tv->msg + tv->msg_len - 2;
 +              len[1] = 1;
 +              addr[2] = tv->msg + tv->msg_len - 1;
 +              len[2] = 1;
 +
 +              if (omac1_aes_128_vector(tv->k, 3, addr, len, result) ||
 +                  os_memcmp(result, tv->tag, 16) != 0) {
 +                      wpa_printf(MSG_ERROR,
 +                                 "OMAC1-AES-128(vector2) test vector %u failed",
 +                                 i);
 +                      return 1;
 +              }
 +      }
 +
 +      addr[0] = &msg[0];
 +      len[0] = 1;
 +      addr[1] = &msg[1];
 +      len[1] = 1;
 +      addr[2] = &msg[2];
 +      len[2] = 1;
 +      if (omac1_aes_128(key, msg, sizeof(msg), result) ||
 +          omac1_aes_128_vector(key, 3, addr, len, result2) ||
 +          os_memcmp(result, result2, 16) != 0) {
 +              wpa_printf(MSG_ERROR, "OMAC1-AES-128 short test mismatch");
 +              return 1;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +static int test_omac1(void)
 +{
 +      unsigned int i;
 +
 +      for (i = 0; i < ARRAY_SIZE(omac1_test_vectors); i++) {
 +              if (test_omac1_vector(&omac1_test_vectors[i], i))
 +                      return 1;
 +      }
 +
 +      wpa_printf(MSG_INFO, "OMAC1-AES-128 test cases passed");
 +
 +      return 0;
 +}
 +
 +
 +static int test_eax(void)
 +{
 +#ifdef EAP_PSK
 +      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[AES_BLOCK_SIZE];
 +
 +      os_memcpy(data, msg, sizeof(msg));
 +      if (aes_128_eax_encrypt(key, nonce, sizeof(nonce), hdr, sizeof(hdr),
 +                              data, sizeof(data), tag)) {
 +              wpa_printf(MSG_ERROR, "AES-128 EAX mode encryption failed");
 +              return 1;
 +      }
 +      if (os_memcmp(data, cipher, sizeof(data)) != 0) {
 +              wpa_printf(MSG_ERROR,
 +                         "AES-128 EAX mode encryption returned invalid cipher text");
 +              return 1;
 +      }
 +      if (os_memcmp(tag, cipher + sizeof(data), AES_BLOCK_SIZE) != 0) {
 +              wpa_printf(MSG_ERROR,
 +                         "AES-128 EAX mode encryption returned invalid tag");
 +              return 1;
 +      }
 +
 +      if (aes_128_eax_decrypt(key, nonce, sizeof(nonce), hdr, sizeof(hdr),
 +                              data, sizeof(data), tag)) {
 +              wpa_printf(MSG_ERROR, "AES-128 EAX mode decryption failed");
 +              return 1;
 +      }
 +      if (os_memcmp(data, msg, sizeof(data)) != 0) {
 +              wpa_printf(MSG_ERROR,
 +                         "AES-128 EAX mode decryption returned invalid plain text");
 +              return 1;
 +      }
 +
 +      wpa_printf(MSG_INFO, "AES-128 EAX mode test cases passed");
 +#endif /* EAP_PSK */
 +
 +      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 < ARRAY_SIZE(vectors); i++) {
 +              struct cbc_test_vector *tv = &vectors[i];
 +
 +              buf = os_malloc(tv->len);
 +              if (buf == NULL) {
 +                      ret++;
 +                      break;
 +              }
 +
 +              os_memcpy(buf, tv->plain, tv->len);
 +              if (aes_128_cbc_encrypt(tv->key, tv->iv, buf, tv->len) ||
 +                  os_memcmp(buf, tv->cipher, tv->len) != 0) {
 +                      wpa_printf(MSG_ERROR, "AES-CBC encrypt %d failed", i);
 +                      ret++;
 +              }
 +
 +              os_memcpy(buf, tv->cipher, tv->len);
 +              if (aes_128_cbc_decrypt(tv->key, tv->iv, buf, tv->len) ||
 +                  os_memcmp(buf, tv->plain, tv->len) != 0) {
 +                      wpa_printf(MSG_ERROR, "AES-CBC decrypt %d failed", i);
 +                      ret++;
 +              }
 +
 +              os_free(buf);
 +      }
 +
 +      return ret;
 +}
 +
 +
 +static int test_ecb(void)
 +{
 +#ifdef EAP_PSK
 +      struct ecb_test_vector {
 +              char *key;
 +              char *plaintext;
 +              char *ciphertext;
 +      } vectors[] = {
 +              /* CAVS 11.1 - ECBGFSbox128.rsp */
 +              {
 +                      "00000000000000000000000000000000",
 +                      "f34481ec3cc627bacd5dc3fb08f273e6",
 +                      "0336763e966d92595a567cc9ce537f5e"
 +              },
 +              {
 +                      "00000000000000000000000000000000",
 +                      "9798c4640bad75c7c3227db910174e72",
 +                      "a9a1631bf4996954ebc093957b234589"
 +              },
 +              {
 +                      "00000000000000000000000000000000",
 +                      "96ab5c2ff612d9dfaae8c31f30c42168",
 +                      "ff4f8391a6a40ca5b25d23bedd44a597"
 +              },
 +              {
 +                      "00000000000000000000000000000000",
 +                      "6a118a874519e64e9963798a503f1d35",
 +                      "dc43be40be0e53712f7e2bf5ca707209"
 +              },
 +              {
 +                      "00000000000000000000000000000000",
 +                      "cb9fceec81286ca3e989bd979b0cb284",
 +                      "92beedab1895a94faa69b632e5cc47ce"
 +              },
 +              {
 +                      "00000000000000000000000000000000",
 +                      "b26aeb1874e47ca8358ff22378f09144",
 +                      "459264f4798f6a78bacb89c15ed3d601"
 +              },
 +              {
 +                      "00000000000000000000000000000000",
 +                      "58c8e00b2631686d54eab84b91f0aca1",
 +                      "08a4e2efec8a8e3312ca7460b9040bbf"
 +              },
 +              /* CAVS 11.1 - ECBKeySbox128.rsp */
 +              {
 +                      "10a58869d74be5a374cf867cfb473859",
 +                      "00000000000000000000000000000000",
 +                      "6d251e6944b051e04eaa6fb4dbf78465"
 +              },
 +              {
 +                      "caea65cdbb75e9169ecd22ebe6e54675",
 +                      "00000000000000000000000000000000",
 +                      "6e29201190152df4ee058139def610bb",
 +              }
 +      };
 +      int ret = 0;
 +      unsigned int i;
 +      u8 key[16], plain[16], cipher[16], out[16];
 +
 +      for (i = 0; i < ARRAY_SIZE(vectors); i++) {
 +              struct ecb_test_vector *tv = &vectors[i];
 +
 +              if (hexstr2bin(tv->key, key, sizeof(key)) ||
 +                  hexstr2bin(tv->plaintext, plain, sizeof(plain)) ||
 +                  hexstr2bin(tv->ciphertext, cipher, sizeof(cipher))) {
 +                      wpa_printf(MSG_ERROR, "Invalid AES-ECB test vector %u",
 +                                 i);
 +                      ret++;
 +                      continue;
 +              }
 +
 +              if (aes_128_encrypt_block(key, plain, out) < 0 ||
 +                  os_memcmp(out, cipher, 16) != 0) {
 +                      wpa_printf(MSG_ERROR, "AES-ECB encrypt %u failed", i);
 +                      ret++;
 +              }
 +      }
 +
 +      if (!ret)
 +              wpa_printf(MSG_INFO, "AES ECB mode test cases passed");
 +
 +      return ret;
 +#endif /* EAP_PSK */
 +
 +      return 0;
 +}
 +
 +
 +static int test_key_wrap(void)
 +{
 +      int ret = 0;
 +
 +      /* RFC 3394 - Test vector 4.1 */
 +      u8 kek41[] = {
 +              0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
 +              0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f
 +      };
 +      u8 plain41[] = {
 +              0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
 +              0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff
 +      };
 +      u8 crypt41[] = {
 +              0x1F, 0xA6, 0x8B, 0x0A, 0x81, 0x12, 0xB4, 0x47,
 +              0xAE, 0xF3, 0x4B, 0xD8, 0xFB, 0x5A, 0x7B, 0x82,
 +              0x9D, 0x3E, 0x86, 0x23, 0x71, 0xD2, 0xCF, 0xE5
 +      };
++#ifndef CONFIG_BORINGSSL
 +      /* RFC 3394 - Test vector 4.2 */
 +      u8 kek42[] = {
 +              0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
 +              0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
 +              0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17
 +      };
 +      u8 plain42[] = {
 +              0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
 +              0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff
 +      };
 +      u8 crypt42[] = {
 +              0x96, 0x77, 0x8B, 0x25, 0xAE, 0x6C, 0xA4, 0x35,
 +              0xF9, 0x2B, 0x5B, 0x97, 0xC0, 0x50, 0xAE, 0xD2,
 +              0x46, 0x8A, 0xB8, 0xA1, 0x7A, 0xD8, 0x4E, 0x5D
 +      };
++#endif /* CONFIG_BORINGSSL */
 +      /* RFC 3394 - Test vector 4.3 */
 +      u8 kek43[] = {
 +              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
 +      };
 +      u8 plain43[] = {
 +              0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
 +              0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff
 +      };
 +      u8 crypt43[] = {
 +              0x64, 0xE8, 0xC3, 0xF9, 0xCE, 0x0F, 0x5B, 0xA2,
 +              0x63, 0xE9, 0x77, 0x79, 0x05, 0x81, 0x8A, 0x2A,
 +              0x93, 0xC8, 0x19, 0x1E, 0x7D, 0x6E, 0x8A, 0xE7,
 +      };
++#ifndef CONFIG_BORINGSSL
 +      /* RFC 3394 - Test vector 4.4 */
 +      u8 kek44[] = {
 +              0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
 +              0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
 +              0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17
 +      };
 +      u8 plain44[] = {
 +              0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
 +              0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
 +              0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07
 +      };
 +      u8 crypt44[] = {
 +              0x03, 0x1D, 0x33, 0x26, 0x4E, 0x15, 0xD3, 0x32,
 +              0x68, 0xF2, 0x4E, 0xC2, 0x60, 0x74, 0x3E, 0xDC,
 +              0xE1, 0xC6, 0xC7, 0xDD, 0xEE, 0x72, 0x5A, 0x93,
 +              0x6B, 0xA8, 0x14, 0x91, 0x5C, 0x67, 0x62, 0xD2
 +      };
++#endif /* CONFIG_BORINGSSL */
 +      /* RFC 3394 - Test vector 4.5 */
 +      u8 kek45[] = {
 +              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
 +      };
 +      u8 plain45[] = {
 +              0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
 +              0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
 +              0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07
 +      };
 +      u8 crypt45[] = {
 +              0xA8, 0xF9, 0xBC, 0x16, 0x12, 0xC6, 0x8B, 0x3F,
 +              0xF6, 0xE6, 0xF4, 0xFB, 0xE3, 0x0E, 0x71, 0xE4,
 +              0x76, 0x9C, 0x8B, 0x80, 0xA3, 0x2C, 0xB8, 0x95,
 +              0x8C, 0xD5, 0xD1, 0x7D, 0x6B, 0x25, 0x4D, 0xA1,
 +      };
 +      /* RFC 3394 - Test vector 4.6 */
 +      u8 kek46[] = {
 +              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
 +      };
 +      u8 plain46[] = {
 +              0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
 +              0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF,
 +              0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
 +              0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F
 +      };
 +      u8 crypt46[] = {
 +              0x28, 0xC9, 0xF4, 0x04, 0xC4, 0xB8, 0x10, 0xF4,
 +              0xCB, 0xCC, 0xB3, 0x5C, 0xFB, 0x87, 0xF8, 0x26,
 +              0x3F, 0x57, 0x86, 0xE2, 0xD8, 0x0E, 0xD3, 0x26,
 +              0xCB, 0xC7, 0xF0, 0xE7, 0x1A, 0x99, 0xF4, 0x3B,
 +              0xFB, 0x98, 0x8B, 0x9B, 0x7A, 0x02, 0xDD, 0x21
 +      };
 +      u8 result[40];
 +
 +      wpa_printf(MSG_INFO, "RFC 3394 - Test vector 4.1");
 +      if (aes_wrap(kek41, sizeof(kek41), sizeof(plain41) / 8, plain41,
 +                   result)) {
 +              wpa_printf(MSG_ERROR, "AES-WRAP-128 reported failure");
 +              ret++;
 +      }
 +      if (os_memcmp(result, crypt41, sizeof(crypt41)) != 0) {
 +              wpa_printf(MSG_ERROR, "AES-WRAP-128 failed");
 +              ret++;
 +      }
 +      if (aes_unwrap(kek41, sizeof(kek41), sizeof(plain41) / 8, crypt41,
 +                     result)) {
 +              wpa_printf(MSG_ERROR, "AES-UNWRAP-128 reported failure");
 +              ret++;
 +      }
 +      if (os_memcmp(result, plain41, sizeof(plain41)) != 0) {
 +              wpa_printf(MSG_ERROR, "AES-UNWRAP-128 failed");
 +              ret++;
 +      }
 +
++#ifndef CONFIG_BORINGSSL
 +      wpa_printf(MSG_INFO, "RFC 3394 - Test vector 4.2");
 +      if (aes_wrap(kek42, sizeof(kek42), sizeof(plain42) / 8, plain42,
 +                   result)) {
 +              wpa_printf(MSG_ERROR, "AES-WRAP-192 reported failure");
 +              ret++;
 +      }
 +      if (os_memcmp(result, crypt42, sizeof(crypt42)) != 0) {
 +              wpa_printf(MSG_ERROR, "AES-WRAP-192 failed");
 +              ret++;
 +      }
 +      if (aes_unwrap(kek42, sizeof(kek42), sizeof(plain42) / 8, crypt42,
 +                     result)) {
 +              wpa_printf(MSG_ERROR, "AES-UNWRAP-192 reported failure");
 +              ret++;
 +      }
 +      if (os_memcmp(result, plain42, sizeof(plain42)) != 0) {
 +              wpa_printf(MSG_ERROR, "AES-UNWRAP-192 failed");
 +              ret++;
 +      }
++#endif /* CONFIG_BORINGSSL */
 +
 +      wpa_printf(MSG_INFO, "RFC 3394 - Test vector 4.3");
 +      if (aes_wrap(kek43, sizeof(kek43), sizeof(plain43) / 8, plain43,
 +                   result)) {
 +              wpa_printf(MSG_ERROR, "AES-WRAP-256 reported failure");
 +              ret++;
 +      }
 +      if (os_memcmp(result, crypt43, sizeof(crypt43)) != 0) {
 +              wpa_printf(MSG_ERROR, "AES-WRAP-256 failed");
 +              ret++;
 +      }
 +      if (aes_unwrap(kek43, sizeof(kek43), sizeof(plain43) / 8, crypt43,
 +                     result)) {
 +              wpa_printf(MSG_ERROR, "AES-UNWRAP-256 reported failure");
 +              ret++;
 +      }
 +      if (os_memcmp(result, plain43, sizeof(plain43)) != 0) {
 +              wpa_printf(MSG_ERROR, "AES-UNWRAP-256 failed");
 +              ret++;
 +      }
 +
++#ifndef CONFIG_BORINGSSL
 +      wpa_printf(MSG_INFO, "RFC 3394 - Test vector 4.4");
 +      if (aes_wrap(kek44, sizeof(kek44), sizeof(plain44) / 8, plain44,
 +                   result)) {
 +              wpa_printf(MSG_ERROR, "AES-WRAP-192 reported failure");
 +              ret++;
 +      }
 +      if (os_memcmp(result, crypt44, sizeof(crypt44)) != 0) {
 +              wpa_printf(MSG_ERROR, "AES-WRAP-192 failed");
 +              ret++;
 +      }
 +      if (aes_unwrap(kek44, sizeof(kek44), sizeof(plain44) / 8, crypt44,
 +                     result)) {
 +              wpa_printf(MSG_ERROR, "AES-UNWRAP-192 reported failure");
 +              ret++;
 +      }
 +      if (os_memcmp(result, plain44, sizeof(plain44)) != 0) {
 +              wpa_printf(MSG_ERROR, "AES-UNWRAP-192 failed");
 +              ret++;
 +      }
++#endif /* CONFIG_BORINGSSL */
 +
 +      wpa_printf(MSG_INFO, "RFC 3394 - Test vector 4.5");
 +      if (aes_wrap(kek45, sizeof(kek45), sizeof(plain45) / 8, plain45,
 +                   result)) {
 +              wpa_printf(MSG_ERROR, "AES-WRAP-256 reported failure");
 +              ret++;
 +      }
 +      if (os_memcmp(result, crypt45, sizeof(crypt45)) != 0) {
 +              wpa_printf(MSG_ERROR, "AES-WRAP-256 failed");
 +              ret++;
 +      }
 +      if (aes_unwrap(kek45, sizeof(kek45), sizeof(plain45) / 8, crypt45,
 +                     result)) {
 +              wpa_printf(MSG_ERROR, "AES-UNWRAP-256 reported failure");
 +              ret++;
 +      }
 +      if (os_memcmp(result, plain45, sizeof(plain45)) != 0) {
 +              wpa_printf(MSG_ERROR, "AES-UNWRAP-256 failed");
 +              ret++;
 +      }
 +
 +      wpa_printf(MSG_INFO, "RFC 3394 - Test vector 4.6");
 +      if (aes_wrap(kek46, sizeof(kek46), sizeof(plain46) / 8, plain46,
 +                   result)) {
 +              wpa_printf(MSG_ERROR, "AES-WRAP-256 reported failure");
 +              ret++;
 +      }
 +      if (os_memcmp(result, crypt46, sizeof(crypt46)) != 0) {
 +              wpa_printf(MSG_ERROR, "AES-WRAP-256 failed");
 +              ret++;
 +      }
 +      if (aes_unwrap(kek46, sizeof(kek46), sizeof(plain46) / 8, crypt46,
 +                     result)) {
 +              wpa_printf(MSG_ERROR, "AES-UNWRAP-256 reported failure");
 +              ret++;
 +      }
 +      if (os_memcmp(result, plain46, sizeof(plain46)) != 0) {
 +              wpa_printf(MSG_ERROR, "AES-UNWRAP-256 failed");
 +              ret++;
 +      }
 +
 +      if (!ret)
 +              wpa_printf(MSG_INFO, "AES key wrap/unwrap test cases passed");
 +
 +      return ret;
 +}
 +
 +
 +static int test_md5(void)
 +{
++#ifndef CONFIG_FIPS
 +      struct {
 +              char *data;
 +              char *hash;
 +      } tests[] = {
 +              {
 +                      "",
 +                      "\xd4\x1d\x8c\xd9\x8f\x00\xb2\x04"
 +                      "\xe9\x80\x09\x98\xec\xf8\x42\x7e"
 +              },
 +              {
 +                      "a",
 +                      "\x0c\xc1\x75\xb9\xc0\xf1\xb6\xa8"
 +                      "\x31\xc3\x99\xe2\x69\x77\x26\x61"
 +              },
 +              {
 +                      "abc",
 +                      "\x90\x01\x50\x98\x3c\xd2\x4f\xb0"
 +                      "\xd6\x96\x3f\x7d\x28\xe1\x7f\x72"
 +              },
 +              {
 +                      "message digest",
 +                      "\xf9\x6b\x69\x7d\x7c\xb7\x93\x8d"
 +                      "\x52\x5a\x2f\x31\xaa\xf1\x61\xd0"
 +              },
 +              {
 +                      "abcdefghijklmnopqrstuvwxyz",
 +                      "\xc3\xfc\xd3\xd7\x61\x92\xe4\x00"
 +                      "\x7d\xfb\x49\x6c\xca\x67\xe1\x3b"
 +              },
 +              {
 +                      "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
 +                      "0123456789",
 +                      "\xd1\x74\xab\x98\xd2\x77\xd9\xf5"
 +                      "\xa5\x61\x1c\x2c\x9f\x41\x9d\x9f"
 +              },
 +              {
 +                      "12345678901234567890123456789012345678901234567890"
 +                      "123456789012345678901234567890",
 +                      "\x57\xed\xf4\xa2\x2b\xe3\xc9\x55"
 +                      "\xac\x49\xda\x2e\x21\x07\xb6\x7a"
 +              }
 +      };
 +      unsigned int i;
 +      u8 hash[16];
 +      const u8 *addr[2];
 +      size_t len[2];
 +      int errors = 0;
 +
 +      for (i = 0; i < ARRAY_SIZE(tests); i++) {
 +              wpa_printf(MSG_INFO, "MD5 test case %d", i);
 +
 +              addr[0] = (u8 *) tests[i].data;
 +              len[0] = strlen(tests[i].data);
 +              if (md5_vector(1, addr, len, hash) < 0 ||
 +                  os_memcmp(hash, tests[i].hash, 16) != 0) {
 +                      wpa_printf(MSG_INFO, " FAIL");
 +                      errors++;
 +              } else
 +                      wpa_printf(MSG_INFO, " OK");
 +
 +              if (len[0]) {
 +                      addr[0] = (u8 *) tests[i].data;
 +                      len[0] = strlen(tests[i].data);
 +                      addr[1] = (u8 *) tests[i].data + 1;
 +                      len[1] = strlen(tests[i].data) - 1;
 +                      if (md5_vector(1, addr, len, hash) < 0 ||
 +                          os_memcmp(hash, tests[i].hash, 16) != 0) {
 +                              wpa_printf(MSG_INFO, " FAIL");
 +                              errors++;
 +                      } else
 +                              wpa_printf(MSG_INFO, " OK");
 +              }
 +      }
 +
 +      if (!errors)
 +              wpa_printf(MSG_INFO, "MD5 test cases passed");
 +
 +      return errors;
++#else /* CONFIG_FIPS */
++      wpa_printf(MSG_INFO, "MD5 test cases skipped due to CONFIG_FIPS");
++      return 0;
++#endif /* CONFIG_FIPS */
 +}
 +
 +
 +static int test_eap_fast(void)
 +{
 +#ifdef EAP_FAST
 +      /* 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
 +      };
++#ifndef CONFIG_FIPS
 +      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
 +      };
++#endif /* CONFIG_FIPS */
 +      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;
 +
 +      wpa_printf(MSG_INFO, "EAP-FAST test cases");
 +
 +      wpa_printf(MSG_INFO, "- T-PRF (SHA1) test case / master_secret");
 +      if (sha1_t_prf(pac_key, sizeof(pac_key),
 +                     "PAC to master secret label hash",
 +                     seed, sizeof(seed), buf, sizeof(master_secret)) < 0 ||
 +          os_memcmp(master_secret, buf, sizeof(master_secret)) != 0) {
 +              wpa_printf(MSG_INFO, "T-PRF test - FAILED!");
 +              errors++;
 +      }
 +
++#ifndef CONFIG_FIPS
 +      wpa_printf(MSG_INFO, "- PRF (TLS, SHA1/MD5) test case / key_block");
 +      if (tls_prf_sha1_md5(master_secret, sizeof(master_secret),
 +                           "key expansion", seed, sizeof(seed),
 +                           buf, sizeof(key_block)) ||
 +          os_memcmp(key_block, buf, sizeof(key_block)) != 0) {
 +              wpa_printf(MSG_INFO, "PRF test - FAILED!");
 +              errors++;
 +      }
++#endif /* CONFIG_FIPS */
 +
 +      wpa_printf(MSG_INFO, "- T-PRF (SHA1) test case / IMCK");
 +      if (sha1_t_prf(sks, sizeof(sks), "Inner Methods Compound Keys",
 +                     isk, sizeof(isk), buf, sizeof(imck)) < 0 ||
 +          os_memcmp(imck, buf, sizeof(imck)) != 0) {
 +              wpa_printf(MSG_INFO, "T-PRF test - FAILED!");
 +              errors++;
 +      }
 +
 +      simck = imck;
 +      cmk = imck + 40;
 +
 +      wpa_printf(MSG_INFO, "- T-PRF (SHA1) test case / MSK");
 +      if (sha1_t_prf(simck, 40, "Session Key Generating Function",
 +                     (u8 *) "", 0, buf, sizeof(msk)) < 0 ||
 +          os_memcmp(msk, buf, sizeof(msk)) != 0) {
 +              wpa_printf(MSG_INFO, "T-PRF test - FAILED!");
 +              errors++;
 +      }
 +
 +      wpa_printf(MSG_INFO, "- T-PRF (SHA1) test case / EMSK");
 +      if (sha1_t_prf(simck, 40, "Extended Session Key Generating Function",
 +                     (u8 *) "", 0, buf, sizeof(msk)) < 0 ||
 +          os_memcmp(emsk, buf, sizeof(emsk)) != 0) {
 +              wpa_printf(MSG_INFO, "T-PRF test - FAILED!");
 +              errors++;
 +      }
 +
 +      wpa_printf(MSG_INFO, "- Compound MAC test case");
 +      os_memset(tlv + sizeof(tlv) - 20, 0, 20);
 +      if (hmac_sha1(cmk, 20, tlv, sizeof(tlv), tlv + sizeof(tlv) - 20) < 0 ||
 +          os_memcmp(tlv + sizeof(tlv) - 20, compound_mac,
 +                    sizeof(compound_mac)) != 0) {
 +              wpa_printf(MSG_INFO, "Compound MAC test - FAILED!");
 +              errors++;
 +      }
 +
 +      return errors;
 +#else /* EAP_FAST */
 +      return 0;
 +#endif /* EAP_FAST */
 +}
 +
 +
- static u8 data0[] = "Hi There";
- static u8 prf0[] =
++static const u8 key0[] =
 +{
 +      0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
 +      0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
 +      0x0b, 0x0b, 0x0b, 0x0b
 +};
- static u8 key1[] = "Jefe";
- static u8 data1[] = "what do ya want for nothing?";
- static u8 prf1[] =
++static const u8 data0[] = "Hi There";
++static const 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 key2[] =
++static const u8 key1[] = "Jefe";
++static const u8 data1[] = "what do ya want for nothing?";
++static const 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 data2[] =
++static const u8 key2[] =
 +{
 +      0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
 +      0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
 +      0xaa, 0xaa, 0xaa, 0xaa
 +};
- static u8 prf2[] =
++static const 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 struct passphrase_test passphrase_tests[] =
++static const 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 rfc6070_test rfc6070_tests[] =
++static const 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 ARRAY_SIZE(passphrase_tests)
 +
 +
 +struct rfc6070_test {
 +      char *p;
 +      char *s;
 +      int c;
 +      char dk[32];
 +      size_t dk_len;
 +};
 +
-               struct passphrase_test *test = &passphrase_tests[i];
++static const 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 ARRAY_SIZE(rfc6070_tests)
 +
 +
 +static int test_sha1(void)
 +{
 +      u8 res[512];
 +      int ret = 0;
 +      unsigned int i;
 +
 +      wpa_printf(MSG_INFO, "PRF-SHA1 test cases:");
 +
 +      if (sha1_prf(key0, sizeof(key0), "prefix", data0, sizeof(data0) - 1,
 +                   res, sizeof(prf0)) == 0 &&
 +          os_memcmp(res, prf0, sizeof(prf0)) == 0)
 +              wpa_printf(MSG_INFO, "Test case 0 - OK");
 +      else {
 +              wpa_printf(MSG_INFO, "Test case 0 - FAILED!");
 +              ret++;
 +      }
 +
 +      if (sha1_prf(key1, sizeof(key1) - 1, "prefix", data1, sizeof(data1) - 1,
 +                   res, sizeof(prf1)) == 0 &&
 +          os_memcmp(res, prf1, sizeof(prf1)) == 0)
 +              wpa_printf(MSG_INFO, "Test case 1 - OK");
 +      else {
 +              wpa_printf(MSG_INFO, "Test case 1 - FAILED!");
 +              ret++;
 +      }
 +
 +      if (sha1_prf(key2, sizeof(key2), "prefix", data2, sizeof(data2),
 +                   res, sizeof(prf2)) == 0 &&
 +          os_memcmp(res, prf2, sizeof(prf2)) == 0)
 +              wpa_printf(MSG_INFO, "Test case 2 - OK");
 +      else {
 +              wpa_printf(MSG_INFO, "Test case 2 - FAILED!");
 +              ret++;
 +      }
 +
 +      ret += test_eap_fast();
 +
 +      wpa_printf(MSG_INFO, "PBKDF2-SHA1 Passphrase test cases:");
 +      for (i = 0; i < NUM_PASSPHRASE_TESTS; i++) {
 +              u8 psk[32];
-               struct rfc6070_test *test = &rfc6070_tests[i];
++              const struct passphrase_test *test = &passphrase_tests[i];
 +
 +              if (pbkdf2_sha1(test->passphrase,
 +                              (const u8 *) test->ssid, strlen(test->ssid),
 +                              4096, psk, 32) == 0 &&
 +                  os_memcmp(psk, test->psk, 32) == 0)
 +                      wpa_printf(MSG_INFO, "Test case %d - OK", i);
 +              else {
 +                      wpa_printf(MSG_INFO, "Test case %d - FAILED!", i);
 +                      ret++;
 +              }
 +      }
 +
 +      wpa_printf(MSG_INFO, "PBKDF2-SHA1 test cases (RFC 6070):");
 +      for (i = 0; i < NUM_RFC6070_TESTS; i++) {
 +              u8 dk[25];
- struct {
++              const struct rfc6070_test *test = &rfc6070_tests[i];
 +
 +              if (pbkdf2_sha1(test->p, (const u8 *) test->s, strlen(test->s),
 +                              test->c, dk, test->dk_len) == 0 &&
 +                  os_memcmp(dk, test->dk, test->dk_len) == 0)
 +                      wpa_printf(MSG_INFO, "Test case %d - OK", i);
 +              else {
 +                      wpa_printf(MSG_INFO, "Test case %d - FAILED!", i);
 +                      ret++;
 +              }
 +      }
 +
 +      if (!ret)
 +              wpa_printf(MSG_INFO, "SHA1 test cases passed");
 +      return ret;
 +}
 +
 +
- struct hmac_test {
++const 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 *t = &hmac_tests[i];
++const 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
 +              }
 +      }
 +};
 +
 +
 +static int test_sha256(void)
 +{
 +      unsigned int i;
 +      u8 hash[32];
 +      const u8 *addr[2];
 +      size_t len[2];
 +      int errors = 0;
 +
 +      for (i = 0; i < ARRAY_SIZE(tests); i++) {
 +              wpa_printf(MSG_INFO, "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) {
 +                      wpa_printf(MSG_INFO, " FAIL");
 +                      errors++;
 +              } else
 +                      wpa_printf(MSG_INFO, " 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) {
 +                              wpa_printf(MSG_INFO, " FAIL");
 +                              errors++;
 +                      } else
 +                              wpa_printf(MSG_INFO, " OK");
 +              }
 +      }
 +
 +      for (i = 0; i < ARRAY_SIZE(hmac_tests); i++) {
++              const struct hmac_test *t = &hmac_tests[i];
 +
 +              wpa_printf(MSG_INFO, "HMAC-SHA256 test case %d:", i + 1);
 +
 +              if (hmac_sha256(t->key, t->key_len, t->data, t->data_len,
 +                              hash) < 0 ||
 +                  os_memcmp(hash, t->hash, 32) != 0) {
 +                      wpa_printf(MSG_INFO, " FAIL");
 +                      errors++;
 +              } else
 +                      wpa_printf(MSG_INFO, " OK");
 +
 +              addr[0] = t->data;
 +              len[0] = t->data_len;
 +              if (hmac_sha256_vector(t->key, t->key_len, 1, addr, len,
 +                                     hash) < 0 ||
 +                  os_memcmp(hash, t->hash, 32) != 0) {
 +                      wpa_printf(MSG_INFO, " FAIL");
 +                      errors++;
 +              } else
 +                      wpa_printf(MSG_INFO, " OK");
 +
 +              if (len[0]) {
 +                      addr[0] = t->data;
 +                      len[0] = 1;
 +                      addr[1] = t->data + 1;
 +                      len[1] = t->data_len - 1;
 +                      if (hmac_sha256_vector(t->key, t->key_len, 2, addr, len,
 +                                             hash) < 0 ||
 +                          os_memcmp(hash, t->hash, 32) != 0) {
 +                              wpa_printf(MSG_INFO, " FAIL");
 +                              errors++;
 +                      } else
 +                              wpa_printf(MSG_INFO, " OK");
 +              }
 +      }
 +
 +      wpa_printf(MSG_INFO, "Test IEEE 802.11r KDF");
 +      sha256_prf((u8 *) "abc", 3, "KDF test", (u8 *) "data", 4,
 +                 hash, sizeof(hash));
 +      /* TODO: add proper test case for this */
 +
 +      if (!errors)
 +              wpa_printf(MSG_INFO, "SHA256 test cases passed");
 +      return errors;
 +}
 +
 +
 +static int test_ms_funcs(void)
 +{
++#ifndef CONFIG_FIPS
 +      /* Test vector from RFC2759 example */
 +      char *username = "User";
 +      char *password = "clientPass";
 +      u8 auth_challenge[] = {
 +              0x5B, 0x5D, 0x7C, 0x7D, 0x7B, 0x3F, 0x2F, 0x3E,
 +              0x3C, 0x2C, 0x60, 0x21, 0x32, 0x26, 0x26, 0x28
 +      };
 +      u8 peer_challenge[] = {
 +              0x21, 0x40, 0x23, 0x24, 0x25, 0x5E, 0x26, 0x2A,
 +              0x28, 0x29, 0x5F, 0x2B, 0x3A, 0x33, 0x7C, 0x7E
 +      };
 +      u8 password_hash[] = {
 +              0x44, 0xEB, 0xBA, 0x8D, 0x53, 0x12, 0xB8, 0xD6,
 +              0x11, 0x47, 0x44, 0x11, 0xF5, 0x69, 0x89, 0xAE
 +      };
 +      u8 nt_response[] = {
 +              0x82, 0x30, 0x9E, 0xCD, 0x8D, 0x70, 0x8B, 0x5E,
 +              0xA0, 0x8F, 0xAA, 0x39, 0x81, 0xCD, 0x83, 0x54,
 +              0x42, 0x33, 0x11, 0x4A, 0x3D, 0x85, 0xD6, 0xDF
 +      };
 +      u8 password_hash_hash[] = {
 +              0x41, 0xC0, 0x0C, 0x58, 0x4B, 0xD2, 0xD9, 0x1C,
 +              0x40, 0x17, 0xA2, 0xA1, 0x2F, 0xA5, 0x9F, 0x3F
 +      };
 +      u8 authenticator_response[] = {
 +              0x40, 0x7A, 0x55, 0x89, 0x11, 0x5F, 0xD0, 0xD6,
 +              0x20, 0x9F, 0x51, 0x0F, 0xE9, 0xC0, 0x45, 0x66,
 +              0x93, 0x2C, 0xDA, 0x56
 +      };
 +      u8 master_key[] = {
 +              0xFD, 0xEC, 0xE3, 0x71, 0x7A, 0x8C, 0x83, 0x8C,
 +              0xB3, 0x88, 0xE5, 0x27, 0xAE, 0x3C, 0xDD, 0x31
 +      };
 +      u8 send_start_key[] = {
 +              0x8B, 0x7C, 0xDC, 0x14, 0x9B, 0x99, 0x3A, 0x1B,
 +              0xA1, 0x18, 0xCB, 0x15, 0x3F, 0x56, 0xDC, 0xCB
 +      };
 +      u8 buf[32];
 +      int errors = 0;
 +
 +      if (nt_password_hash((u8 *) password, os_strlen(password), buf) ||
 +          os_memcmp(password_hash, buf, sizeof(password_hash)) != 0) {
 +              wpa_printf(MSG_ERROR, "nt_password_hash failed");
 +              errors++;
 +      }
 +
 +      if (generate_nt_response(auth_challenge, peer_challenge,
 +                               (u8 *) username, os_strlen(username),
 +                               (u8 *) password, os_strlen(password), buf) ||
 +          os_memcmp(nt_response, buf, sizeof(nt_response)) != 0) {
 +              wpa_printf(MSG_ERROR, "generate_nt_response failed");
 +              errors++;
 +      }
 +
 +      if (hash_nt_password_hash(password_hash, buf) ||
 +          os_memcmp(password_hash_hash, buf,
 +                    sizeof(password_hash_hash)) != 0) {
 +              wpa_printf(MSG_ERROR, "hash_nt_password_hash failed");
 +              errors++;
 +      }
 +
 +      if (generate_authenticator_response((u8 *) password,
 +                                          os_strlen(password),
 +                                          peer_challenge, auth_challenge,
 +                                          (u8 *) username,
 +                                          os_strlen(username),
 +                                          nt_response, buf) ||
 +          os_memcmp(authenticator_response, buf,
 +                    sizeof(authenticator_response)) != 0) {
 +              wpa_printf(MSG_ERROR, "generate_authenticator_response failed");
 +              errors++;
 +      }
 +
 +      if (get_master_key(password_hash_hash, nt_response, buf) ||
 +          os_memcmp(master_key, buf, sizeof(master_key)) != 0) {
 +              wpa_printf(MSG_ERROR, "get_master_key failed");
 +              errors++;
 +      }
 +
 +      if (get_asymetric_start_key(master_key, buf, sizeof(send_start_key),
 +                                  1, 1) ||
 +          os_memcmp(send_start_key, buf, sizeof(send_start_key)) != 0) {
 +              wpa_printf(MSG_ERROR, "get_asymetric_start_key failed");
 +              errors++;
 +      }
 +
 +      if (errors)
 +              wpa_printf(MSG_ERROR, "ms_funcs: %d errors", errors);
 +      else
 +              wpa_printf(MSG_INFO, "ms_funcs test cases passed");
 +
 +      return errors;
++#else /* CONFIG_FIPS */
++      wpa_printf(MSG_INFO, "ms_funcs test cases skipped due to CONFIG_FIPS");
++      return 0;
++#endif /* CONFIG_FIPS */
 +}
 +
 +
 +int crypto_module_tests(void)
 +{
 +      int ret = 0;
 +
 +      wpa_printf(MSG_INFO, "crypto module tests");
 +      if (test_siv() ||
 +          test_omac1() ||
 +          test_eax() ||
 +          test_cbc() ||
 +          test_ecb() ||
 +          test_key_wrap() ||
 +          test_md5() ||
 +          test_sha1() ||
 +          test_sha256() ||
 +          test_ms_funcs())
 +              ret = -1;
 +
 +      return ret;
 +}
index f158ef43a645c5a787c77507f6f57af0aa400c26,0000000000000000000000000000000000000000..6cff75c64ae5a918a193d1ec777e9968d3ddcac1
mode 100644,000000..100644
--- /dev/null
@@@ -1,1279 -1,0 +1,1442 @@@
-           e->order == NULL ||
-           !EC_GROUP_get_curve_GFp(e->group, e->prime, NULL, NULL, e->bnctx) ||
 +/*
 + * Wrapper functions for OpenSSL libcrypto
 + * 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 "includes.h"
 +#include <openssl/opensslv.h>
 +#include <openssl/err.h>
 +#include <openssl/des.h>
 +#include <openssl/aes.h>
 +#include <openssl/bn.h>
 +#include <openssl/evp.h>
 +#include <openssl/dh.h>
 +#include <openssl/hmac.h>
 +#include <openssl/rand.h>
 +#ifdef CONFIG_OPENSSL_CMAC
 +#include <openssl/cmac.h>
 +#endif /* CONFIG_OPENSSL_CMAC */
 +#ifdef CONFIG_ECC
 +#include <openssl/ec.h>
 +#endif /* CONFIG_ECC */
 +
 +#include "common.h"
 +#include "wpabuf.h"
 +#include "dh_group5.h"
 +#include "sha1.h"
 +#include "sha256.h"
 +#include "sha384.h"
 +#include "crypto.h"
 +
 +static BIGNUM * get_group5_prime(void)
 +{
 +#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,
 +              0x29,0x02,0x4E,0x08,0x8A,0x67,0xCC,0x74,0x02,0x0B,0xBE,0xA6,
 +              0x3B,0x13,0x9B,0x22,0x51,0x4A,0x08,0x79,0x8E,0x34,0x04,0xDD,
 +              0xEF,0x95,0x19,0xB3,0xCD,0x3A,0x43,0x1B,0x30,0x2B,0x0A,0x6D,
 +              0xF2,0x5F,0x14,0x37,0x4F,0xE1,0x35,0x6D,0x6D,0x51,0xC2,0x45,
 +              0xE4,0x85,0xB5,0x76,0x62,0x5E,0x7E,0xC6,0xF4,0x4C,0x42,0xE9,
 +              0xA6,0x37,0xED,0x6B,0x0B,0xFF,0x5C,0xB6,0xF4,0x06,0xB7,0xED,
 +              0xEE,0x38,0x6B,0xFB,0x5A,0x89,0x9F,0xA5,0xAE,0x9F,0x24,0x11,
 +              0x7C,0x4B,0x1F,0xE6,0x49,0x28,0x66,0x51,0xEC,0xE4,0x5B,0x3D,
 +              0xC2,0x00,0x7C,0xB8,0xA1,0x63,0xBF,0x05,0x98,0xDA,0x48,0x36,
 +              0x1C,0x55,0xD3,0x9A,0x69,0x16,0x3F,0xA8,0xFD,0x24,0xCF,0x5F,
 +              0x83,0x65,0x5D,0x23,0xDC,0xA3,0xAD,0x96,0x1C,0x62,0xF3,0x56,
 +              0x20,0x85,0x52,0xBB,0x9E,0xD5,0x29,0x07,0x70,0x96,0x96,0x6D,
 +              0x67,0x0C,0x35,0x4E,0x4A,0xBC,0x98,0x04,0xF1,0x74,0x6C,0x08,
 +              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_IS_BORINGSSL */
 +      return get_rfc3526_prime_1536(NULL);
 +#endif /* OPENSSL_IS_BORINGSSL */
 +}
 +
 +#ifdef OPENSSL_NO_SHA256
 +#define NO_SHA256_WRAPPER
 +#endif
 +
 +static int openssl_digest_vector(const EVP_MD *type, size_t num_elem,
 +                               const u8 *addr[], const size_t *len, u8 *mac)
 +{
 +      EVP_MD_CTX ctx;
 +      size_t i;
 +      unsigned int mac_len;
 +
 +      EVP_MD_CTX_init(&ctx);
 +      if (!EVP_DigestInit_ex(&ctx, type, NULL)) {
 +              wpa_printf(MSG_ERROR, "OpenSSL: EVP_DigestInit_ex failed: %s",
 +                         ERR_error_string(ERR_get_error(), NULL));
 +              return -1;
 +      }
 +      for (i = 0; i < num_elem; i++) {
 +              if (!EVP_DigestUpdate(&ctx, addr[i], len[i])) {
 +                      wpa_printf(MSG_ERROR, "OpenSSL: EVP_DigestUpdate "
 +                                 "failed: %s",
 +                                 ERR_error_string(ERR_get_error(), NULL));
 +                      return -1;
 +              }
 +      }
 +      if (!EVP_DigestFinal(&ctx, mac, &mac_len)) {
 +              wpa_printf(MSG_ERROR, "OpenSSL: EVP_DigestFinal failed: %s",
 +                         ERR_error_string(ERR_get_error(), NULL));
 +              return -1;
 +      }
 +
 +      return 0;
 +}
 +
 +
++#ifndef CONFIG_FIPS
 +int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
 +{
 +      return openssl_digest_vector(EVP_md4(), num_elem, addr, len, mac);
 +}
++#endif /* CONFIG_FIPS */
 +
 +
 +void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
 +{
 +      u8 pkey[8], next, tmp;
 +      int i;
 +      DES_key_schedule ks;
 +
 +      /* 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;
 +
 +      DES_set_key((DES_cblock *) &pkey, &ks);
 +      DES_ecb_encrypt((DES_cblock *) clear, (DES_cblock *) cypher, &ks,
 +                      DES_ENCRYPT);
 +}
 +
 +
++#ifndef CONFIG_NO_RC4
 +int rc4_skip(const u8 *key, size_t keylen, size_t skip,
 +           u8 *data, size_t data_len)
 +{
 +#ifdef OPENSSL_NO_RC4
 +      return -1;
 +#else /* OPENSSL_NO_RC4 */
 +      EVP_CIPHER_CTX ctx;
 +      int outl;
 +      int res = -1;
 +      unsigned char skip_buf[16];
 +
 +      EVP_CIPHER_CTX_init(&ctx);
 +      if (!EVP_CIPHER_CTX_set_padding(&ctx, 0) ||
 +          !EVP_CipherInit_ex(&ctx, EVP_rc4(), NULL, NULL, NULL, 1) ||
 +          !EVP_CIPHER_CTX_set_key_length(&ctx, keylen) ||
 +          !EVP_CipherInit_ex(&ctx, NULL, NULL, key, NULL, 1))
 +              goto out;
 +
 +      while (skip >= sizeof(skip_buf)) {
 +              size_t len = skip;
 +              if (len > sizeof(skip_buf))
 +                      len = sizeof(skip_buf);
 +              if (!EVP_CipherUpdate(&ctx, skip_buf, &outl, skip_buf, len))
 +                      goto out;
 +              skip -= len;
 +      }
 +
 +      if (EVP_CipherUpdate(&ctx, data, &outl, data, data_len))
 +              res = 0;
 +
 +out:
 +      EVP_CIPHER_CTX_cleanup(&ctx);
 +      return res;
 +#endif /* OPENSSL_NO_RC4 */
 +}
++#endif /* CONFIG_NO_RC4 */
 +
 +
++#ifndef CONFIG_FIPS
 +int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
 +{
 +      return openssl_digest_vector(EVP_md5(), num_elem, addr, len, mac);
 +}
++#endif /* CONFIG_FIPS */
 +
 +
 +int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
 +{
 +      return openssl_digest_vector(EVP_sha1(), num_elem, addr, len, mac);
 +}
 +
 +
 +#ifndef NO_SHA256_WRAPPER
 +int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len,
 +                u8 *mac)
 +{
 +      return openssl_digest_vector(EVP_sha256(), num_elem, addr, len, mac);
 +}
 +#endif /* NO_SHA256_WRAPPER */
 +
 +
 +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();
 +      }
 +
 +      return NULL;
 +}
 +
 +
 +void * aes_encrypt_init(const u8 *key, size_t len)
 +{
 +      EVP_CIPHER_CTX *ctx;
 +      const EVP_CIPHER *type;
 +
 +      type = aes_get_evp_cipher(len);
 +      if (type == NULL)
 +              return NULL;
 +
 +      ctx = os_malloc(sizeof(*ctx));
 +      if (ctx == NULL)
 +              return NULL;
 +      EVP_CIPHER_CTX_init(ctx);
 +      if (EVP_EncryptInit_ex(ctx, type, NULL, key, NULL) != 1) {
 +              os_free(ctx);
 +              return NULL;
 +      }
 +      EVP_CIPHER_CTX_set_padding(ctx, 0);
 +      return ctx;
 +}
 +
 +
 +void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt)
 +{
 +      EVP_CIPHER_CTX *c = ctx;
 +      int clen = 16;
 +      if (EVP_EncryptUpdate(c, crypt, &clen, plain, 16) != 1) {
 +              wpa_printf(MSG_ERROR, "OpenSSL: EVP_EncryptUpdate failed: %s",
 +                         ERR_error_string(ERR_get_error(), NULL));
 +      }
 +}
 +
 +
 +void aes_encrypt_deinit(void *ctx)
 +{
 +      EVP_CIPHER_CTX *c = ctx;
 +      u8 buf[16];
 +      int len = sizeof(buf);
 +      if (EVP_EncryptFinal_ex(c, buf, &len) != 1) {
 +              wpa_printf(MSG_ERROR, "OpenSSL: EVP_EncryptFinal_ex failed: "
 +                         "%s", ERR_error_string(ERR_get_error(), NULL));
 +      }
 +      if (len != 0) {
 +              wpa_printf(MSG_ERROR, "OpenSSL: Unexpected padding length %d "
 +                         "in AES encrypt", len);
 +      }
 +      EVP_CIPHER_CTX_cleanup(c);
 +      bin_clear_free(c, sizeof(*c));
 +}
 +
 +
 +void * aes_decrypt_init(const u8 *key, size_t len)
 +{
 +      EVP_CIPHER_CTX *ctx;
 +      const EVP_CIPHER *type;
 +
 +      type = aes_get_evp_cipher(len);
 +      if (type == NULL)
 +              return NULL;
 +
 +      ctx = os_malloc(sizeof(*ctx));
 +      if (ctx == NULL)
 +              return NULL;
 +      EVP_CIPHER_CTX_init(ctx);
 +      if (EVP_DecryptInit_ex(ctx, type, NULL, key, NULL) != 1) {
 +              os_free(ctx);
 +              return NULL;
 +      }
 +      EVP_CIPHER_CTX_set_padding(ctx, 0);
 +      return ctx;
 +}
 +
 +
 +void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain)
 +{
 +      EVP_CIPHER_CTX *c = ctx;
 +      int plen = 16;
 +      if (EVP_DecryptUpdate(c, plain, &plen, crypt, 16) != 1) {
 +              wpa_printf(MSG_ERROR, "OpenSSL: EVP_DecryptUpdate failed: %s",
 +                         ERR_error_string(ERR_get_error(), NULL));
 +      }
 +}
 +
 +
 +void aes_decrypt_deinit(void *ctx)
 +{
 +      EVP_CIPHER_CTX *c = ctx;
 +      u8 buf[16];
 +      int len = sizeof(buf);
 +      if (EVP_DecryptFinal_ex(c, buf, &len) != 1) {
 +              wpa_printf(MSG_ERROR, "OpenSSL: EVP_DecryptFinal_ex failed: "
 +                         "%s", ERR_error_string(ERR_get_error(), NULL));
 +      }
 +      if (len != 0) {
 +              wpa_printf(MSG_ERROR, "OpenSSL: Unexpected padding length %d "
 +                         "in AES decrypt", len);
 +      }
 +      EVP_CIPHER_CTX_cleanup(c);
 +      bin_clear_free(c, sizeof(*c));
 +}
 +
 +
++#ifndef CONFIG_FIPS
++#ifndef CONFIG_OPENSSL_INTERNAL_AES_WRAP
++
 +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;
 +}
 +
++#endif /* CONFIG_OPENSSL_INTERNAL_AES_WRAP */
++#endif /* CONFIG_FIPS */
++
++
++int aes_128_cbc_encrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len)
++{
++      EVP_CIPHER_CTX ctx;
++      int clen, len;
++      u8 buf[16];
++
++      EVP_CIPHER_CTX_init(&ctx);
++      if (EVP_EncryptInit_ex(&ctx, EVP_aes_128_cbc(), NULL, key, iv) != 1)
++              return -1;
++      EVP_CIPHER_CTX_set_padding(&ctx, 0);
++
++      clen = data_len;
++      if (EVP_EncryptUpdate(&ctx, data, &clen, data, data_len) != 1 ||
++          clen != (int) data_len)
++              return -1;
++
++      len = sizeof(buf);
++      if (EVP_EncryptFinal_ex(&ctx, buf, &len) != 1 || len != 0)
++              return -1;
++      EVP_CIPHER_CTX_cleanup(&ctx);
++
++      return 0;
++}
++
++
++int aes_128_cbc_decrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len)
++{
++      EVP_CIPHER_CTX ctx;
++      int plen, len;
++      u8 buf[16];
++
++      EVP_CIPHER_CTX_init(&ctx);
++      if (EVP_DecryptInit_ex(&ctx, EVP_aes_128_cbc(), NULL, key, iv) != 1)
++              return -1;
++      EVP_CIPHER_CTX_set_padding(&ctx, 0);
++
++      plen = data_len;
++      if (EVP_DecryptUpdate(&ctx, data, &plen, data, data_len) != 1 ||
++          plen != (int) data_len)
++              return -1;
++
++      len = sizeof(buf);
++      if (EVP_DecryptFinal_ex(&ctx, buf, &len) != 1 || len != 0)
++              return -1;
++      EVP_CIPHER_CTX_cleanup(&ctx);
++
++      return 0;
++}
++
 +
 +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)
 +{
 +      BIGNUM *bn_base, *bn_exp, *bn_modulus, *bn_result;
 +      int ret = -1;
 +      BN_CTX *ctx;
 +
 +      ctx = BN_CTX_new();
 +      if (ctx == NULL)
 +              return -1;
 +
 +      bn_base = BN_bin2bn(base, base_len, NULL);
 +      bn_exp = BN_bin2bn(power, power_len, NULL);
 +      bn_modulus = BN_bin2bn(modulus, modulus_len, NULL);
 +      bn_result = BN_new();
 +
 +      if (bn_base == NULL || bn_exp == NULL || bn_modulus == NULL ||
 +          bn_result == NULL)
 +              goto error;
 +
 +      if (BN_mod_exp(bn_result, bn_base, bn_exp, bn_modulus, ctx) != 1)
 +              goto error;
 +
 +      *result_len = BN_bn2bin(bn_result, result);
 +      ret = 0;
 +
 +error:
 +      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;
 +}
 +
 +
 +struct crypto_cipher {
 +      EVP_CIPHER_CTX enc;
 +      EVP_CIPHER_CTX dec;
 +};
 +
 +
 +struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg,
 +                                        const u8 *iv, const u8 *key,
 +                                        size_t key_len)
 +{
 +      struct crypto_cipher *ctx;
 +      const EVP_CIPHER *cipher;
 +
 +      ctx = os_zalloc(sizeof(*ctx));
 +      if (ctx == NULL)
 +              return NULL;
 +
 +      switch (alg) {
++#ifndef CONFIG_NO_RC4
 +#ifndef OPENSSL_NO_RC4
 +      case CRYPTO_CIPHER_ALG_RC4:
 +              cipher = EVP_rc4();
 +              break;
 +#endif /* OPENSSL_NO_RC4 */
++#endif /* CONFIG_NO_RC4 */
 +#ifndef OPENSSL_NO_AES
 +      case CRYPTO_CIPHER_ALG_AES:
 +              switch (key_len) {
 +              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;
 +              default:
 +                      os_free(ctx);
 +                      return NULL;
 +              }
 +              break;
 +#endif /* OPENSSL_NO_AES */
 +#ifndef OPENSSL_NO_DES
 +      case CRYPTO_CIPHER_ALG_3DES:
 +              cipher = EVP_des_ede3_cbc();
 +              break;
 +      case CRYPTO_CIPHER_ALG_DES:
 +              cipher = EVP_des_cbc();
 +              break;
 +#endif /* OPENSSL_NO_DES */
 +#ifndef OPENSSL_NO_RC2
 +      case CRYPTO_CIPHER_ALG_RC2:
 +              cipher = EVP_rc2_ecb();
 +              break;
 +#endif /* OPENSSL_NO_RC2 */
 +      default:
 +              os_free(ctx);
 +              return NULL;
 +      }
 +
 +      EVP_CIPHER_CTX_init(&ctx->enc);
 +      EVP_CIPHER_CTX_set_padding(&ctx->enc, 0);
 +      if (!EVP_EncryptInit_ex(&ctx->enc, cipher, NULL, NULL, NULL) ||
 +          !EVP_CIPHER_CTX_set_key_length(&ctx->enc, key_len) ||
 +          !EVP_EncryptInit_ex(&ctx->enc, NULL, NULL, key, iv)) {
 +              EVP_CIPHER_CTX_cleanup(&ctx->enc);
 +              os_free(ctx);
 +              return NULL;
 +      }
 +
 +      EVP_CIPHER_CTX_init(&ctx->dec);
 +      EVP_CIPHER_CTX_set_padding(&ctx->dec, 0);
 +      if (!EVP_DecryptInit_ex(&ctx->dec, cipher, NULL, NULL, NULL) ||
 +          !EVP_CIPHER_CTX_set_key_length(&ctx->dec, key_len) ||
 +          !EVP_DecryptInit_ex(&ctx->dec, NULL, NULL, key, iv)) {
 +              EVP_CIPHER_CTX_cleanup(&ctx->enc);
 +              EVP_CIPHER_CTX_cleanup(&ctx->dec);
 +              os_free(ctx);
 +              return NULL;
 +      }
 +
 +      return ctx;
 +}
 +
 +
 +int crypto_cipher_encrypt(struct crypto_cipher *ctx, const u8 *plain,
 +                        u8 *crypt, size_t len)
 +{
 +      int outl;
 +      if (!EVP_EncryptUpdate(&ctx->enc, crypt, &outl, plain, len))
 +              return -1;
 +      return 0;
 +}
 +
 +
 +int crypto_cipher_decrypt(struct crypto_cipher *ctx, const u8 *crypt,
 +                        u8 *plain, size_t len)
 +{
 +      int outl;
 +      outl = len;
 +      if (!EVP_DecryptUpdate(&ctx->dec, plain, &outl, crypt, len))
 +              return -1;
 +      return 0;
 +}
 +
 +
 +void crypto_cipher_deinit(struct crypto_cipher *ctx)
 +{
 +      EVP_CIPHER_CTX_cleanup(&ctx->enc);
 +      EVP_CIPHER_CTX_cleanup(&ctx->dec);
 +      os_free(ctx);
 +}
 +
 +
 +void * dh5_init(struct wpabuf **priv, struct wpabuf **publ)
 +{
 +      DH *dh;
 +      struct wpabuf *pubkey = NULL, *privkey = NULL;
 +      size_t publen, privlen;
 +
 +      *priv = NULL;
 +      *publ = NULL;
 +
 +      dh = DH_new();
 +      if (dh == NULL)
 +              return NULL;
 +
 +      dh->g = BN_new();
 +      if (dh->g == NULL || BN_set_word(dh->g, 2) != 1)
 +              goto err;
 +
 +      dh->p = get_group5_prime();
 +      if (dh->p == NULL)
 +              goto err;
 +
 +      if (DH_generate_key(dh) != 1)
 +              goto err;
 +
 +      publen = BN_num_bytes(dh->pub_key);
 +      pubkey = wpabuf_alloc(publen);
 +      if (pubkey == NULL)
 +              goto err;
 +      privlen = BN_num_bytes(dh->priv_key);
 +      privkey = wpabuf_alloc(privlen);
 +      if (privkey == NULL)
 +              goto err;
 +
 +      BN_bn2bin(dh->pub_key, wpabuf_put(pubkey, publen));
 +      BN_bn2bin(dh->priv_key, wpabuf_put(privkey, privlen));
 +
 +      *priv = privkey;
 +      *publ = pubkey;
 +      return dh;
 +
 +err:
 +      wpabuf_clear_free(pubkey);
 +      wpabuf_clear_free(privkey);
 +      DH_free(dh);
 +      return NULL;
 +}
 +
 +
 +void * dh5_init_fixed(const struct wpabuf *priv, const struct wpabuf *publ)
 +{
 +      DH *dh;
 +
 +      dh = DH_new();
 +      if (dh == NULL)
 +              return NULL;
 +
 +      dh->g = BN_new();
 +      if (dh->g == NULL || BN_set_word(dh->g, 2) != 1)
 +              goto err;
 +
 +      dh->p = get_group5_prime();
 +      if (dh->p == NULL)
 +              goto err;
 +
 +      dh->priv_key = BN_bin2bn(wpabuf_head(priv), wpabuf_len(priv), NULL);
 +      if (dh->priv_key == NULL)
 +              goto err;
 +
 +      dh->pub_key = BN_bin2bn(wpabuf_head(publ), wpabuf_len(publ), NULL);
 +      if (dh->pub_key == NULL)
 +              goto err;
 +
 +      if (DH_generate_key(dh) != 1)
 +              goto err;
 +
 +      return dh;
 +
 +err:
 +      DH_free(dh);
 +      return NULL;
 +}
 +
 +
 +struct wpabuf * dh5_derive_shared(void *ctx, const struct wpabuf *peer_public,
 +                                const struct wpabuf *own_private)
 +{
 +      BIGNUM *pub_key;
 +      struct wpabuf *res = NULL;
 +      size_t rlen;
 +      DH *dh = ctx;
 +      int keylen;
 +
 +      if (ctx == NULL)
 +              return NULL;
 +
 +      pub_key = BN_bin2bn(wpabuf_head(peer_public), wpabuf_len(peer_public),
 +                          NULL);
 +      if (pub_key == NULL)
 +              return NULL;
 +
 +      rlen = DH_size(dh);
 +      res = wpabuf_alloc(rlen);
 +      if (res == NULL)
 +              goto err;
 +
 +      keylen = DH_compute_key(wpabuf_mhead(res), pub_key, dh);
 +      if (keylen < 0)
 +              goto err;
 +      wpabuf_put(res, keylen);
 +      BN_clear_free(pub_key);
 +
 +      return res;
 +
 +err:
 +      BN_clear_free(pub_key);
 +      wpabuf_clear_free(res);
 +      return NULL;
 +}
 +
 +
 +void dh5_free(void *ctx)
 +{
 +      DH *dh;
 +      if (ctx == NULL)
 +              return;
 +      dh = ctx;
 +      DH_free(dh);
 +}
 +
 +
 +struct crypto_hash {
 +      HMAC_CTX ctx;
 +};
 +
 +
 +struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key,
 +                                    size_t key_len)
 +{
 +      struct crypto_hash *ctx;
 +      const EVP_MD *md;
 +
 +      switch (alg) {
 +#ifndef OPENSSL_NO_MD5
 +      case CRYPTO_HASH_ALG_HMAC_MD5:
 +              md = EVP_md5();
 +              break;
 +#endif /* OPENSSL_NO_MD5 */
 +#ifndef OPENSSL_NO_SHA
 +      case CRYPTO_HASH_ALG_HMAC_SHA1:
 +              md = EVP_sha1();
 +              break;
 +#endif /* OPENSSL_NO_SHA */
 +#ifndef OPENSSL_NO_SHA256
 +#ifdef CONFIG_SHA256
 +      case CRYPTO_HASH_ALG_HMAC_SHA256:
 +              md = EVP_sha256();
 +              break;
 +#endif /* CONFIG_SHA256 */
 +#endif /* OPENSSL_NO_SHA256 */
 +      default:
 +              return NULL;
 +      }
 +
 +      ctx = os_zalloc(sizeof(*ctx));
 +      if (ctx == NULL)
 +              return NULL;
 +      HMAC_CTX_init(&ctx->ctx);
 +
 +#if OPENSSL_VERSION_NUMBER < 0x00909000
 +      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) {
 +              bin_clear_free(ctx, sizeof(*ctx));
 +              return NULL;
 +      }
 +#endif /* openssl < 0.9.9 */
 +
 +      return ctx;
 +}
 +
 +
 +void crypto_hash_update(struct crypto_hash *ctx, const u8 *data, size_t len)
 +{
 +      if (ctx == NULL)
 +              return;
 +      HMAC_Update(&ctx->ctx, data, len);
 +}
 +
 +
 +int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len)
 +{
 +      unsigned int mdlen;
 +      int res;
 +
 +      if (ctx == NULL)
 +              return -2;
 +
 +      if (mac == NULL || len == NULL) {
 +              bin_clear_free(ctx, sizeof(*ctx));
 +              return 0;
 +      }
 +
 +      mdlen = *len;
 +#if OPENSSL_VERSION_NUMBER < 0x00909000
 +      HMAC_Final(&ctx->ctx, mac, &mdlen);
 +      res = 1;
 +#else /* openssl < 0.9.9 */
 +      res = HMAC_Final(&ctx->ctx, mac, &mdlen);
 +#endif /* openssl < 0.9.9 */
 +      HMAC_CTX_cleanup(&ctx->ctx);
 +      bin_clear_free(ctx, sizeof(*ctx));
 +
 +      if (res == 1) {
 +              *len = mdlen;
 +              return 0;
 +      }
 +
 +      return -1;
 +}
 +
 +
 +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;
 +      int res;
 +
 +      HMAC_CTX_init(&ctx);
 +#if OPENSSL_VERSION_NUMBER < 0x00909000
 +      HMAC_Init_ex(&ctx, key, key_len, type, NULL);
 +#else /* openssl < 0.9.9 */
 +      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]);
 +
 +#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);
 +
 +      return res == 1 ? 0 : -1;
 +}
 +
 +
 +#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)
 +{
 +      return hmac_sha1_vector(key, key_len, 1, &data, &data_len, mac);
 +}
 +
 +
 +#ifdef CONFIG_SHA256
 +
 +int hmac_sha256_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_sha256(), key, key_len, num_elem, addr,
 +                                 len, mac, 32);
 +}
 +
 +
 +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);
 +}
 +
 +#endif /* CONFIG_SHA256 */
 +
 +
 +#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_sha384(const u8 *key, size_t key_len, const u8 *data,
 +              size_t data_len, u8 *mac)
 +{
 +      return hmac_sha384_vector(key, key_len, 1, &data, &data_len, mac);
 +}
 +
 +#endif /* CONFIG_SHA384 */
 +
 +
 +int crypto_get_random(void *buf, size_t len)
 +{
 +      if (RAND_bytes(buf, len) != 1)
 +              return -1;
 +      return 0;
 +}
 +
 +
 +#ifdef CONFIG_OPENSSL_CMAC
 +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;
 +      size_t outlen, i;
 +
 +      ctx = CMAC_CTX_new();
 +      if (ctx == NULL)
 +              return -1;
 +
 +      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;
 +      }
 +      if (!CMAC_Final(ctx, mac, &outlen) || outlen != 16)
 +              goto fail;
 +
 +      ret = 0;
 +fail:
 +      CMAC_CTX_free(ctx);
 +      return ret;
 +}
 +
 +
 +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 */
 +
 +
 +struct crypto_bignum * crypto_bignum_init(void)
 +{
 +      return (struct crypto_bignum *) BN_new();
 +}
 +
 +
 +struct crypto_bignum * crypto_bignum_init_set(const u8 *buf, size_t len)
 +{
 +      BIGNUM *bn = BN_bin2bn(buf, len, NULL);
 +      return (struct crypto_bignum *) bn;
 +}
 +
 +
 +void crypto_bignum_deinit(struct crypto_bignum *n, int clear)
 +{
 +      if (clear)
 +              BN_clear_free((BIGNUM *) n);
 +      else
 +              BN_free((BIGNUM *) n);
 +}
 +
 +
 +int crypto_bignum_to_bin(const struct crypto_bignum *a,
 +                       u8 *buf, size_t buflen, size_t padlen)
 +{
 +      int num_bytes, offset;
 +
 +      if (padlen > buflen)
 +              return -1;
 +
 +      num_bytes = BN_num_bytes((const BIGNUM *) a);
 +      if ((size_t) num_bytes > buflen)
 +              return -1;
 +      if (padlen > (size_t) num_bytes)
 +              offset = padlen - num_bytes;
 +      else
 +              offset = 0;
 +
 +      os_memset(buf, 0, offset);
 +      BN_bn2bin((const BIGNUM *) a, buf + offset);
 +
 +      return num_bytes + offset;
 +}
 +
 +
 +int crypto_bignum_add(const struct crypto_bignum *a,
 +                    const struct crypto_bignum *b,
 +                    struct crypto_bignum *c)
 +{
 +      return BN_add((BIGNUM *) c, (const BIGNUM *) a, (const BIGNUM *) b) ?
 +              0 : -1;
 +}
 +
 +
 +int crypto_bignum_mod(const struct crypto_bignum *a,
 +                    const struct crypto_bignum *b,
 +                    struct crypto_bignum *c)
 +{
 +      int res;
 +      BN_CTX *bnctx;
 +
 +      bnctx = BN_CTX_new();
 +      if (bnctx == NULL)
 +              return -1;
 +      res = BN_mod((BIGNUM *) c, (const BIGNUM *) a, (const BIGNUM *) b,
 +                   bnctx);
 +      BN_CTX_free(bnctx);
 +
 +      return res ? 0 : -1;
 +}
 +
 +
 +int crypto_bignum_exptmod(const struct crypto_bignum *a,
 +                        const struct crypto_bignum *b,
 +                        const struct crypto_bignum *c,
 +                        struct crypto_bignum *d)
 +{
 +      int res;
 +      BN_CTX *bnctx;
 +
 +      bnctx = BN_CTX_new();
 +      if (bnctx == NULL)
 +              return -1;
 +      res = BN_mod_exp((BIGNUM *) d, (const BIGNUM *) a, (const BIGNUM *) b,
 +                       (const BIGNUM *) c, bnctx);
 +      BN_CTX_free(bnctx);
 +
 +      return res ? 0 : -1;
 +}
 +
 +
 +int crypto_bignum_inverse(const struct crypto_bignum *a,
 +                        const struct crypto_bignum *b,
 +                        struct crypto_bignum *c)
 +{
 +      BIGNUM *res;
 +      BN_CTX *bnctx;
 +
 +      bnctx = BN_CTX_new();
 +      if (bnctx == NULL)
 +              return -1;
 +      res = BN_mod_inverse((BIGNUM *) c, (const BIGNUM *) a,
 +                           (const BIGNUM *) b, bnctx);
 +      BN_CTX_free(bnctx);
 +
 +      return res ? 0 : -1;
 +}
 +
 +
 +int crypto_bignum_sub(const struct crypto_bignum *a,
 +                    const struct crypto_bignum *b,
 +                    struct crypto_bignum *c)
 +{
 +      return BN_sub((BIGNUM *) c, (const BIGNUM *) a, (const BIGNUM *) b) ?
 +              0 : -1;
 +}
 +
 +
 +int crypto_bignum_div(const struct crypto_bignum *a,
 +                    const struct crypto_bignum *b,
 +                    struct crypto_bignum *c)
 +{
 +      int res;
 +
 +      BN_CTX *bnctx;
 +
 +      bnctx = BN_CTX_new();
 +      if (bnctx == NULL)
 +              return -1;
 +      res = BN_div((BIGNUM *) c, NULL, (const BIGNUM *) a,
 +                   (const BIGNUM *) b, bnctx);
 +      BN_CTX_free(bnctx);
 +
 +      return res ? 0 : -1;
 +}
 +
 +
 +int crypto_bignum_mulmod(const struct crypto_bignum *a,
 +                       const struct crypto_bignum *b,
 +                       const struct crypto_bignum *c,
 +                       struct crypto_bignum *d)
 +{
 +      int res;
 +
 +      BN_CTX *bnctx;
 +
 +      bnctx = BN_CTX_new();
 +      if (bnctx == NULL)
 +              return -1;
 +      res = BN_mod_mul((BIGNUM *) d, (const BIGNUM *) a, (const BIGNUM *) b,
 +                       (const BIGNUM *) c, bnctx);
 +      BN_CTX_free(bnctx);
 +
 +      return res ? 0 : -1;
 +}
 +
 +
 +int crypto_bignum_cmp(const struct crypto_bignum *a,
 +                    const struct crypto_bignum *b)
 +{
 +      return BN_cmp((const BIGNUM *) a, (const BIGNUM *) b);
 +}
 +
 +
 +int crypto_bignum_bits(const struct crypto_bignum *a)
 +{
 +      return BN_num_bits((const BIGNUM *) a);
 +}
 +
 +
 +int crypto_bignum_is_zero(const struct crypto_bignum *a)
 +{
 +      return BN_is_zero((const BIGNUM *) a);
 +}
 +
 +
 +int crypto_bignum_is_one(const struct crypto_bignum *a)
 +{
 +      return BN_is_one((const BIGNUM *) a);
 +}
 +
 +
++int crypto_bignum_legendre(const struct crypto_bignum *a,
++                         const struct crypto_bignum *p)
++{
++      BN_CTX *bnctx;
++      BIGNUM *exp = NULL, *tmp = NULL;
++      int res = -2;
++
++      bnctx = BN_CTX_new();
++      if (bnctx == NULL)
++              return -2;
++
++      exp = BN_new();
++      tmp = BN_new();
++      if (!exp || !tmp ||
++          /* exp = (p-1) / 2 */
++          !BN_sub(exp, (const BIGNUM *) p, BN_value_one()) ||
++          !BN_rshift1(exp, exp) ||
++          !BN_mod_exp(tmp, (const BIGNUM *) a, exp, (const BIGNUM *) p,
++                      bnctx))
++              goto fail;
++
++      if (BN_is_word(tmp, 1))
++              res = 1;
++      else if (BN_is_zero(tmp))
++              res = 0;
++      else
++              res = -1;
++
++fail:
++      BN_clear_free(tmp);
++      BN_clear_free(exp);
++      BN_CTX_free(bnctx);
++      return res;
++}
++
++
 +#ifdef CONFIG_ECC
 +
 +struct crypto_ec {
 +      EC_GROUP *group;
 +      BN_CTX *bnctx;
 +      BIGNUM *prime;
 +      BIGNUM *order;
++      BIGNUM *a;
++      BIGNUM *b;
 +};
 +
 +struct crypto_ec * crypto_ec_init(int group)
 +{
 +      struct crypto_ec *e;
 +      int nid;
 +
 +      /* Map from IANA registry for IKE D-H groups to OpenSSL NID */
 +      switch (group) {
 +      case 19:
 +              nid = NID_X9_62_prime256v1;
 +              break;
 +      case 20:
 +              nid = NID_secp384r1;
 +              break;
 +      case 21:
 +              nid = NID_secp521r1;
 +              break;
 +      case 25:
 +              nid = NID_X9_62_prime192v1;
 +              break;
 +      case 26:
 +              nid = NID_secp224r1;
 +              break;
++#ifdef NID_brainpoolP224r1
++      case 27:
++              nid = NID_brainpoolP224r1;
++              break;
++#endif /* NID_brainpoolP224r1 */
++#ifdef NID_brainpoolP256r1
++      case 28:
++              nid = NID_brainpoolP256r1;
++              break;
++#endif /* NID_brainpoolP256r1 */
++#ifdef NID_brainpoolP384r1
++      case 29:
++              nid = NID_brainpoolP384r1;
++              break;
++#endif /* NID_brainpoolP384r1 */
++#ifdef NID_brainpoolP512r1
++      case 30:
++              nid = NID_brainpoolP512r1;
++              break;
++#endif /* NID_brainpoolP512r1 */
 +      default:
 +              return NULL;
 +      }
 +
 +      e = os_zalloc(sizeof(*e));
 +      if (e == NULL)
 +              return NULL;
 +
 +      e->bnctx = BN_CTX_new();
 +      e->group = EC_GROUP_new_by_curve_name(nid);
 +      e->prime = BN_new();
 +      e->order = BN_new();
++      e->a = BN_new();
++      e->b = BN_new();
 +      if (e->group == NULL || e->bnctx == NULL || e->prime == NULL ||
-       return EC_POINT_is_on_curve(e->group, (const EC_POINT *) p, e->bnctx);
++          e->order == NULL || e->a == NULL || e->b == NULL ||
++          !EC_GROUP_get_curve_GFp(e->group, e->prime, e->a, e->b, e->bnctx) ||
 +          !EC_GROUP_get_order(e->group, e->order, e->bnctx)) {
 +              crypto_ec_deinit(e);
 +              e = NULL;
 +      }
 +
 +      return e;
 +}
 +
 +
 +void crypto_ec_deinit(struct crypto_ec *e)
 +{
 +      if (e == NULL)
 +              return;
++      BN_clear_free(e->b);
++      BN_clear_free(e->a);
 +      BN_clear_free(e->order);
 +      BN_clear_free(e->prime);
 +      EC_GROUP_free(e->group);
 +      BN_CTX_free(e->bnctx);
 +      os_free(e);
 +}
 +
 +
 +struct crypto_ec_point * crypto_ec_point_init(struct crypto_ec *e)
 +{
 +      if (e == NULL)
 +              return NULL;
 +      return (struct crypto_ec_point *) EC_POINT_new(e->group);
 +}
 +
 +
 +size_t crypto_ec_prime_len(struct crypto_ec *e)
 +{
 +      return BN_num_bytes(e->prime);
 +}
 +
 +
 +size_t crypto_ec_prime_len_bits(struct crypto_ec *e)
 +{
 +      return BN_num_bits(e->prime);
 +}
 +
 +
 +const struct crypto_bignum * crypto_ec_get_prime(struct crypto_ec *e)
 +{
 +      return (const struct crypto_bignum *) e->prime;
 +}
 +
 +
 +const struct crypto_bignum * crypto_ec_get_order(struct crypto_ec *e)
 +{
 +      return (const struct crypto_bignum *) e->order;
 +}
 +
 +
 +void crypto_ec_point_deinit(struct crypto_ec_point *p, int clear)
 +{
 +      if (clear)
 +              EC_POINT_clear_free((EC_POINT *) p);
 +      else
 +              EC_POINT_free((EC_POINT *) p);
 +}
 +
 +
 +int crypto_ec_point_to_bin(struct crypto_ec *e,
 +                         const struct crypto_ec_point *point, u8 *x, u8 *y)
 +{
 +      BIGNUM *x_bn, *y_bn;
 +      int ret = -1;
 +      int len = BN_num_bytes(e->prime);
 +
 +      x_bn = BN_new();
 +      y_bn = BN_new();
 +
 +      if (x_bn && y_bn &&
 +          EC_POINT_get_affine_coordinates_GFp(e->group, (EC_POINT *) point,
 +                                              x_bn, y_bn, e->bnctx)) {
 +              if (x) {
 +                      crypto_bignum_to_bin((struct crypto_bignum *) x_bn,
 +                                           x, len, len);
 +              }
 +              if (y) {
 +                      crypto_bignum_to_bin((struct crypto_bignum *) y_bn,
 +                                           y, len, len);
 +              }
 +              ret = 0;
 +      }
 +
 +      BN_clear_free(x_bn);
 +      BN_clear_free(y_bn);
 +      return ret;
 +}
 +
 +
 +struct crypto_ec_point * crypto_ec_point_from_bin(struct crypto_ec *e,
 +                                                const u8 *val)
 +{
 +      BIGNUM *x, *y;
 +      EC_POINT *elem;
 +      int len = BN_num_bytes(e->prime);
 +
 +      x = BN_bin2bn(val, len, NULL);
 +      y = BN_bin2bn(val + len, len, NULL);
 +      elem = EC_POINT_new(e->group);
 +      if (x == NULL || y == NULL || elem == NULL) {
 +              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_clear_free(elem);
 +              elem = NULL;
 +      }
 +
 +      BN_clear_free(x);
 +      BN_clear_free(y);
 +
 +      return (struct crypto_ec_point *) elem;
 +}
 +
 +
 +int crypto_ec_point_add(struct crypto_ec *e, const struct crypto_ec_point *a,
 +                      const struct crypto_ec_point *b,
 +                      struct crypto_ec_point *c)
 +{
 +      return EC_POINT_add(e->group, (EC_POINT *) c, (const EC_POINT *) a,
 +                          (const EC_POINT *) b, e->bnctx) ? 0 : -1;
 +}
 +
 +
 +int crypto_ec_point_mul(struct crypto_ec *e, const struct crypto_ec_point *p,
 +                      const struct crypto_bignum *b,
 +                      struct crypto_ec_point *res)
 +{
 +      return EC_POINT_mul(e->group, (EC_POINT *) res, NULL,
 +                          (const EC_POINT *) p, (const BIGNUM *) b, e->bnctx)
 +              ? 0 : -1;
 +}
 +
 +
 +int crypto_ec_point_invert(struct crypto_ec *e, struct crypto_ec_point *p)
 +{
 +      return EC_POINT_invert(e->group, (EC_POINT *) p, e->bnctx) ? 0 : -1;
 +}
 +
 +
 +int crypto_ec_point_solve_y_coord(struct crypto_ec *e,
 +                                struct crypto_ec_point *p,
 +                                const struct crypto_bignum *x, int y_bit)
 +{
 +      if (!EC_POINT_set_compressed_coordinates_GFp(e->group, (EC_POINT *) p,
 +                                                   (const BIGNUM *) x, y_bit,
 +                                                   e->bnctx) ||
 +          !EC_POINT_is_on_curve(e->group, (EC_POINT *) p, e->bnctx))
 +              return -1;
 +      return 0;
 +}
 +
 +
++struct crypto_bignum *
++crypto_ec_point_compute_y_sqr(struct crypto_ec *e,
++                            const struct crypto_bignum *x)
++{
++      BIGNUM *tmp, *tmp2, *y_sqr = NULL;
++
++      tmp = BN_new();
++      tmp2 = BN_new();
++
++      /* y^2 = x^3 + ax + b */
++      if (tmp && tmp2 &&
++          BN_mod_sqr(tmp, (const BIGNUM *) x, e->prime, e->bnctx) &&
++          BN_mod_mul(tmp, tmp, (const BIGNUM *) x, e->prime, e->bnctx) &&
++          BN_mod_mul(tmp2, e->a, (const BIGNUM *) x, e->prime, e->bnctx) &&
++          BN_mod_add_quick(tmp2, tmp2, tmp, e->prime) &&
++          BN_mod_add_quick(tmp2, tmp2, e->b, e->prime)) {
++              y_sqr = tmp2;
++              tmp2 = NULL;
++      }
++
++      BN_clear_free(tmp);
++      BN_clear_free(tmp2);
++
++      return (struct crypto_bignum *) y_sqr;
++}
++
++
 +int crypto_ec_point_is_at_infinity(struct crypto_ec *e,
 +                                 const struct crypto_ec_point *p)
 +{
 +      return EC_POINT_is_at_infinity(e->group, (const EC_POINT *) p);
 +}
 +
 +
 +int crypto_ec_point_is_on_curve(struct crypto_ec *e,
 +                              const struct crypto_ec_point *p)
 +{
++      return EC_POINT_is_on_curve(e->group, (const EC_POINT *) p,
++                                  e->bnctx) == 1;
++}
++
++
++int crypto_ec_point_cmp(const struct crypto_ec *e,
++                      const struct crypto_ec_point *a,
++                      const struct crypto_ec_point *b)
++{
++      return EC_POINT_cmp(e->group, (const EC_POINT *) a,
++                          (const EC_POINT *) b, e->bnctx);
 +}
 +
 +#endif /* CONFIG_ECC */
index d3b263196e2d1cfcc38c4c4a1636f6f380318fd7,0000000000000000000000000000000000000000..3aeb2bbc60af3f5b405f861c55e0076b00a00404
mode 100644,000000..100644
--- /dev/null
@@@ -1,1271 -1,0 +1,1271 @@@
- static struct dh_group dh_groups[] = {
 +/*
 + * Diffie-Hellman groups
 + * Copyright (c) 2007, Jouni Malinen <j@w1.fi>
 + *
 + * This software may be distributed under the terms of the BSD license.
 + * See README for more details.
 + */
 +
 +#include "includes.h"
 +
 +#include "common.h"
 +#include "crypto.h"
 +#include "random.h"
 +#include "dh_groups.h"
 +
 +
 +#ifdef ALL_DH_GROUPS
 +
 +/* RFC 4306, B.1. Group 1 - 768 Bit MODP
 + * Generator: 2
 + * Prime: 2^768 - 2 ^704 - 1 + 2^64 * { [2^638 pi] + 149686 }
 + */
 +static const u8 dh_group1_generator[1] = { 0x02 };
 +static const u8 dh_group1_prime[96] = {
 +      0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
 +      0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34,
 +      0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
 +      0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74,
 +      0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22,
 +      0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
 +      0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B,
 +      0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37,
 +      0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,
 +      0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6,
 +      0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x3A, 0x36, 0x20,
 +      0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
 +};
 +static const u8 dh_group1_order[96] = {
 +      0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
 +      0xE4, 0x87, 0xED, 0x51, 0x10, 0xB4, 0x61, 0x1A,
 +      0x62, 0x63, 0x31, 0x45, 0xC0, 0x6E, 0x0E, 0x68,
 +      0x94, 0x81, 0x27, 0x04, 0x45, 0x33, 0xE6, 0x3A,
 +      0x01, 0x05, 0xDF, 0x53, 0x1D, 0x89, 0xCD, 0x91,
 +      0x28, 0xA5, 0x04, 0x3C, 0xC7, 0x1A, 0x02, 0x6E,
 +      0xF7, 0xCA, 0x8C, 0xD9, 0xE6, 0x9D, 0x21, 0x8D,
 +      0x98, 0x15, 0x85, 0x36, 0xF9, 0x2F, 0x8A, 0x1B,
 +      0xA7, 0xF0, 0x9A, 0xB6, 0xB6, 0xA8, 0xE1, 0x22,
 +      0xF2, 0x42, 0xDA, 0xBB, 0x31, 0x2F, 0x3F, 0x63,
 +      0x7A, 0x26, 0x21, 0x74, 0xD3, 0x1D, 0x1B, 0x10,
 +      0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
 +};
 +
 +/* RFC 4306, B.2. Group 2 - 1024 Bit MODP
 + * Generator: 2
 + * Prime: 2^1024 - 2^960 - 1 + 2^64 * { [2^894 pi] + 129093 }
 + */
 +static const u8 dh_group2_generator[1] = { 0x02 };
 +static const u8 dh_group2_prime[128] = {
 +      0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
 +      0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34,
 +      0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
 +      0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74,
 +      0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22,
 +      0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
 +      0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B,
 +      0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37,
 +      0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,
 +      0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6,
 +      0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B,
 +      0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,
 +      0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5,
 +      0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6,
 +      0x49, 0x28, 0x66, 0x51, 0xEC, 0xE6, 0x53, 0x81,
 +      0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
 +};
 +static const u8 dh_group2_order[128] = {
 +      0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
 +      0xE4, 0x87, 0xED, 0x51, 0x10, 0xB4, 0x61, 0x1A,
 +      0x62, 0x63, 0x31, 0x45, 0xC0, 0x6E, 0x0E, 0x68,
 +      0x94, 0x81, 0x27, 0x04, 0x45, 0x33, 0xE6, 0x3A,
 +      0x01, 0x05, 0xDF, 0x53, 0x1D, 0x89, 0xCD, 0x91,
 +      0x28, 0xA5, 0x04, 0x3C, 0xC7, 0x1A, 0x02, 0x6E,
 +      0xF7, 0xCA, 0x8C, 0xD9, 0xE6, 0x9D, 0x21, 0x8D,
 +      0x98, 0x15, 0x85, 0x36, 0xF9, 0x2F, 0x8A, 0x1B,
 +      0xA7, 0xF0, 0x9A, 0xB6, 0xB6, 0xA8, 0xE1, 0x22,
 +      0xF2, 0x42, 0xDA, 0xBB, 0x31, 0x2F, 0x3F, 0x63,
 +      0x7A, 0x26, 0x21, 0x74, 0xD3, 0x1B, 0xF6, 0xB5,
 +      0x85, 0xFF, 0xAE, 0x5B, 0x7A, 0x03, 0x5B, 0xF6,
 +      0xF7, 0x1C, 0x35, 0xFD, 0xAD, 0x44, 0xCF, 0xD2,
 +      0xD7, 0x4F, 0x92, 0x08, 0xBE, 0x25, 0x8F, 0xF3,
 +      0x24, 0x94, 0x33, 0x28, 0xF6, 0x73, 0x29, 0xC0,
 +      0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
 +};
 +
 +#endif /* ALL_DH_GROUPS */
 +
 +/* RFC 3526, 2. Group 5 - 1536 Bit MODP
 + * Generator: 2
 + * Prime: 2^1536 - 2^1472 - 1 + 2^64 * { [2^1406 pi] + 741804 }
 + */
 +static const u8 dh_group5_generator[1] = { 0x02 };
 +static const u8 dh_group5_prime[192] = {
 +      0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
 +      0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34,
 +      0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
 +      0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74,
 +      0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22,
 +      0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
 +      0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B,
 +      0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37,
 +      0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,
 +      0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6,
 +      0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B,
 +      0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,
 +      0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5,
 +      0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6,
 +      0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D,
 +      0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05,
 +      0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A,
 +      0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F,
 +      0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96,
 +      0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB,
 +      0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D,
 +      0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04,
 +      0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x23, 0x73, 0x27,
 +      0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
 +};
 +static const u8 dh_group5_order[192] = {
 +      0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
 +      0xE4, 0x87, 0xED, 0x51, 0x10, 0xB4, 0x61, 0x1A,
 +      0x62, 0x63, 0x31, 0x45, 0xC0, 0x6E, 0x0E, 0x68,
 +      0x94, 0x81, 0x27, 0x04, 0x45, 0x33, 0xE6, 0x3A,
 +      0x01, 0x05, 0xDF, 0x53, 0x1D, 0x89, 0xCD, 0x91,
 +      0x28, 0xA5, 0x04, 0x3C, 0xC7, 0x1A, 0x02, 0x6E,
 +      0xF7, 0xCA, 0x8C, 0xD9, 0xE6, 0x9D, 0x21, 0x8D,
 +      0x98, 0x15, 0x85, 0x36, 0xF9, 0x2F, 0x8A, 0x1B,
 +      0xA7, 0xF0, 0x9A, 0xB6, 0xB6, 0xA8, 0xE1, 0x22,
 +      0xF2, 0x42, 0xDA, 0xBB, 0x31, 0x2F, 0x3F, 0x63,
 +      0x7A, 0x26, 0x21, 0x74, 0xD3, 0x1B, 0xF6, 0xB5,
 +      0x85, 0xFF, 0xAE, 0x5B, 0x7A, 0x03, 0x5B, 0xF6,
 +      0xF7, 0x1C, 0x35, 0xFD, 0xAD, 0x44, 0xCF, 0xD2,
 +      0xD7, 0x4F, 0x92, 0x08, 0xBE, 0x25, 0x8F, 0xF3,
 +      0x24, 0x94, 0x33, 0x28, 0xF6, 0x72, 0x2D, 0x9E,
 +      0xE1, 0x00, 0x3E, 0x5C, 0x50, 0xB1, 0xDF, 0x82,
 +      0xCC, 0x6D, 0x24, 0x1B, 0x0E, 0x2A, 0xE9, 0xCD,
 +      0x34, 0x8B, 0x1F, 0xD4, 0x7E, 0x92, 0x67, 0xAF,
 +      0xC1, 0xB2, 0xAE, 0x91, 0xEE, 0x51, 0xD6, 0xCB,
 +      0x0E, 0x31, 0x79, 0xAB, 0x10, 0x42, 0xA9, 0x5D,
 +      0xCF, 0x6A, 0x94, 0x83, 0xB8, 0x4B, 0x4B, 0x36,
 +      0xB3, 0x86, 0x1A, 0xA7, 0x25, 0x5E, 0x4C, 0x02,
 +      0x78, 0xBA, 0x36, 0x04, 0x65, 0x11, 0xB9, 0x93,
 +      0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
 +};
 +
 +#ifdef ALL_DH_GROUPS
 +
 +/* RFC 3526, 3. Group 14 - 2048 Bit MODP
 + * Generator: 2
 + * Prime: 2^2048 - 2^1984 - 1 + 2^64 * { [2^1918 pi] + 124476 }
 + */
 +static const u8 dh_group14_generator[1] = { 0x02 };
 +static const u8 dh_group14_prime[256] = {
 +      0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
 +      0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34,
 +      0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
 +      0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74,
 +      0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22,
 +      0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
 +      0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B,
 +      0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37,
 +      0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,
 +      0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6,
 +      0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B,
 +      0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,
 +      0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5,
 +      0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6,
 +      0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D,
 +      0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05,
 +      0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A,
 +      0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F,
 +      0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96,
 +      0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB,
 +      0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D,
 +      0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04,
 +      0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x18, 0x21, 0x7C,
 +      0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B,
 +      0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03,
 +      0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, 0x8F,
 +      0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9,
 +      0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18,
 +      0x39, 0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, 0xE5,
 +      0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10,
 +      0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAC, 0xAA, 0x68,
 +      0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
 +};
 +static const u8 dh_group14_order[256] = {
 +      0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
 +      0xE4, 0x87, 0xED, 0x51, 0x10, 0xB4, 0x61, 0x1A,
 +      0x62, 0x63, 0x31, 0x45, 0xC0, 0x6E, 0x0E, 0x68,
 +      0x94, 0x81, 0x27, 0x04, 0x45, 0x33, 0xE6, 0x3A,
 +      0x01, 0x05, 0xDF, 0x53, 0x1D, 0x89, 0xCD, 0x91,
 +      0x28, 0xA5, 0x04, 0x3C, 0xC7, 0x1A, 0x02, 0x6E,
 +      0xF7, 0xCA, 0x8C, 0xD9, 0xE6, 0x9D, 0x21, 0x8D,
 +      0x98, 0x15, 0x85, 0x36, 0xF9, 0x2F, 0x8A, 0x1B,
 +      0xA7, 0xF0, 0x9A, 0xB6, 0xB6, 0xA8, 0xE1, 0x22,
 +      0xF2, 0x42, 0xDA, 0xBB, 0x31, 0x2F, 0x3F, 0x63,
 +      0x7A, 0x26, 0x21, 0x74, 0xD3, 0x1B, 0xF6, 0xB5,
 +      0x85, 0xFF, 0xAE, 0x5B, 0x7A, 0x03, 0x5B, 0xF6,
 +      0xF7, 0x1C, 0x35, 0xFD, 0xAD, 0x44, 0xCF, 0xD2,
 +      0xD7, 0x4F, 0x92, 0x08, 0xBE, 0x25, 0x8F, 0xF3,
 +      0x24, 0x94, 0x33, 0x28, 0xF6, 0x72, 0x2D, 0x9E,
 +      0xE1, 0x00, 0x3E, 0x5C, 0x50, 0xB1, 0xDF, 0x82,
 +      0xCC, 0x6D, 0x24, 0x1B, 0x0E, 0x2A, 0xE9, 0xCD,
 +      0x34, 0x8B, 0x1F, 0xD4, 0x7E, 0x92, 0x67, 0xAF,
 +      0xC1, 0xB2, 0xAE, 0x91, 0xEE, 0x51, 0xD6, 0xCB,
 +      0x0E, 0x31, 0x79, 0xAB, 0x10, 0x42, 0xA9, 0x5D,
 +      0xCF, 0x6A, 0x94, 0x83, 0xB8, 0x4B, 0x4B, 0x36,
 +      0xB3, 0x86, 0x1A, 0xA7, 0x25, 0x5E, 0x4C, 0x02,
 +      0x78, 0xBA, 0x36, 0x04, 0x65, 0x0C, 0x10, 0xBE,
 +      0x19, 0x48, 0x2F, 0x23, 0x17, 0x1B, 0x67, 0x1D,
 +      0xF1, 0xCF, 0x3B, 0x96, 0x0C, 0x07, 0x43, 0x01,
 +      0xCD, 0x93, 0xC1, 0xD1, 0x76, 0x03, 0xD1, 0x47,
 +      0xDA, 0xE2, 0xAE, 0xF8, 0x37, 0xA6, 0x29, 0x64,
 +      0xEF, 0x15, 0xE5, 0xFB, 0x4A, 0xAC, 0x0B, 0x8C,
 +      0x1C, 0xCA, 0xA4, 0xBE, 0x75, 0x4A, 0xB5, 0x72,
 +      0x8A, 0xE9, 0x13, 0x0C, 0x4C, 0x7D, 0x02, 0x88,
 +      0x0A, 0xB9, 0x47, 0x2D, 0x45, 0x56, 0x55, 0x34,
 +      0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
 +};
 +
 +/* RFC 3526, 4. Group 15 - 3072 Bit MODP
 + * Generator: 2
 + * Prime: 2^3072 - 2^3008 - 1 + 2^64 * { [2^2942 pi] + 1690314 }
 + */
 +static const u8 dh_group15_generator[1] = { 0x02 };
 +static const u8 dh_group15_prime[384] = {
 +      0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
 +      0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34,
 +      0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
 +      0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74,
 +      0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22,
 +      0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
 +      0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B,
 +      0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37,
 +      0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,
 +      0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6,
 +      0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B,
 +      0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,
 +      0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5,
 +      0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6,
 +      0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D,
 +      0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05,
 +      0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A,
 +      0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F,
 +      0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96,
 +      0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB,
 +      0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D,
 +      0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04,
 +      0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x18, 0x21, 0x7C,
 +      0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B,
 +      0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03,
 +      0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, 0x8F,
 +      0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9,
 +      0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18,
 +      0x39, 0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, 0xE5,
 +      0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10,
 +      0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAA, 0xC4, 0x2D,
 +      0xAD, 0x33, 0x17, 0x0D, 0x04, 0x50, 0x7A, 0x33,
 +      0xA8, 0x55, 0x21, 0xAB, 0xDF, 0x1C, 0xBA, 0x64,
 +      0xEC, 0xFB, 0x85, 0x04, 0x58, 0xDB, 0xEF, 0x0A,
 +      0x8A, 0xEA, 0x71, 0x57, 0x5D, 0x06, 0x0C, 0x7D,
 +      0xB3, 0x97, 0x0F, 0x85, 0xA6, 0xE1, 0xE4, 0xC7,
 +      0xAB, 0xF5, 0xAE, 0x8C, 0xDB, 0x09, 0x33, 0xD7,
 +      0x1E, 0x8C, 0x94, 0xE0, 0x4A, 0x25, 0x61, 0x9D,
 +      0xCE, 0xE3, 0xD2, 0x26, 0x1A, 0xD2, 0xEE, 0x6B,
 +      0xF1, 0x2F, 0xFA, 0x06, 0xD9, 0x8A, 0x08, 0x64,
 +      0xD8, 0x76, 0x02, 0x73, 0x3E, 0xC8, 0x6A, 0x64,
 +      0x52, 0x1F, 0x2B, 0x18, 0x17, 0x7B, 0x20, 0x0C,
 +      0xBB, 0xE1, 0x17, 0x57, 0x7A, 0x61, 0x5D, 0x6C,
 +      0x77, 0x09, 0x88, 0xC0, 0xBA, 0xD9, 0x46, 0xE2,
 +      0x08, 0xE2, 0x4F, 0xA0, 0x74, 0xE5, 0xAB, 0x31,
 +      0x43, 0xDB, 0x5B, 0xFC, 0xE0, 0xFD, 0x10, 0x8E,
 +      0x4B, 0x82, 0xD1, 0x20, 0xA9, 0x3A, 0xD2, 0xCA,
 +      0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
 +};
 +static const u8 dh_group15_order[384] = {
 +      0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
 +      0xE4, 0x87, 0xED, 0x51, 0x10, 0xB4, 0x61, 0x1A,
 +      0x62, 0x63, 0x31, 0x45, 0xC0, 0x6E, 0x0E, 0x68,
 +      0x94, 0x81, 0x27, 0x04, 0x45, 0x33, 0xE6, 0x3A,
 +      0x01, 0x05, 0xDF, 0x53, 0x1D, 0x89, 0xCD, 0x91,
 +      0x28, 0xA5, 0x04, 0x3C, 0xC7, 0x1A, 0x02, 0x6E,
 +      0xF7, 0xCA, 0x8C, 0xD9, 0xE6, 0x9D, 0x21, 0x8D,
 +      0x98, 0x15, 0x85, 0x36, 0xF9, 0x2F, 0x8A, 0x1B,
 +      0xA7, 0xF0, 0x9A, 0xB6, 0xB6, 0xA8, 0xE1, 0x22,
 +      0xF2, 0x42, 0xDA, 0xBB, 0x31, 0x2F, 0x3F, 0x63,
 +      0x7A, 0x26, 0x21, 0x74, 0xD3, 0x1B, 0xF6, 0xB5,
 +      0x85, 0xFF, 0xAE, 0x5B, 0x7A, 0x03, 0x5B, 0xF6,
 +      0xF7, 0x1C, 0x35, 0xFD, 0xAD, 0x44, 0xCF, 0xD2,
 +      0xD7, 0x4F, 0x92, 0x08, 0xBE, 0x25, 0x8F, 0xF3,
 +      0x24, 0x94, 0x33, 0x28, 0xF6, 0x72, 0x2D, 0x9E,
 +      0xE1, 0x00, 0x3E, 0x5C, 0x50, 0xB1, 0xDF, 0x82,
 +      0xCC, 0x6D, 0x24, 0x1B, 0x0E, 0x2A, 0xE9, 0xCD,
 +      0x34, 0x8B, 0x1F, 0xD4, 0x7E, 0x92, 0x67, 0xAF,
 +      0xC1, 0xB2, 0xAE, 0x91, 0xEE, 0x51, 0xD6, 0xCB,
 +      0x0E, 0x31, 0x79, 0xAB, 0x10, 0x42, 0xA9, 0x5D,
 +      0xCF, 0x6A, 0x94, 0x83, 0xB8, 0x4B, 0x4B, 0x36,
 +      0xB3, 0x86, 0x1A, 0xA7, 0x25, 0x5E, 0x4C, 0x02,
 +      0x78, 0xBA, 0x36, 0x04, 0x65, 0x0C, 0x10, 0xBE,
 +      0x19, 0x48, 0x2F, 0x23, 0x17, 0x1B, 0x67, 0x1D,
 +      0xF1, 0xCF, 0x3B, 0x96, 0x0C, 0x07, 0x43, 0x01,
 +      0xCD, 0x93, 0xC1, 0xD1, 0x76, 0x03, 0xD1, 0x47,
 +      0xDA, 0xE2, 0xAE, 0xF8, 0x37, 0xA6, 0x29, 0x64,
 +      0xEF, 0x15, 0xE5, 0xFB, 0x4A, 0xAC, 0x0B, 0x8C,
 +      0x1C, 0xCA, 0xA4, 0xBE, 0x75, 0x4A, 0xB5, 0x72,
 +      0x8A, 0xE9, 0x13, 0x0C, 0x4C, 0x7D, 0x02, 0x88,
 +      0x0A, 0xB9, 0x47, 0x2D, 0x45, 0x55, 0x62, 0x16,
 +      0xD6, 0x99, 0x8B, 0x86, 0x82, 0x28, 0x3D, 0x19,
 +      0xD4, 0x2A, 0x90, 0xD5, 0xEF, 0x8E, 0x5D, 0x32,
 +      0x76, 0x7D, 0xC2, 0x82, 0x2C, 0x6D, 0xF7, 0x85,
 +      0x45, 0x75, 0x38, 0xAB, 0xAE, 0x83, 0x06, 0x3E,
 +      0xD9, 0xCB, 0x87, 0xC2, 0xD3, 0x70, 0xF2, 0x63,
 +      0xD5, 0xFA, 0xD7, 0x46, 0x6D, 0x84, 0x99, 0xEB,
 +      0x8F, 0x46, 0x4A, 0x70, 0x25, 0x12, 0xB0, 0xCE,
 +      0xE7, 0x71, 0xE9, 0x13, 0x0D, 0x69, 0x77, 0x35,
 +      0xF8, 0x97, 0xFD, 0x03, 0x6C, 0xC5, 0x04, 0x32,
 +      0x6C, 0x3B, 0x01, 0x39, 0x9F, 0x64, 0x35, 0x32,
 +      0x29, 0x0F, 0x95, 0x8C, 0x0B, 0xBD, 0x90, 0x06,
 +      0x5D, 0xF0, 0x8B, 0xAB, 0xBD, 0x30, 0xAE, 0xB6,
 +      0x3B, 0x84, 0xC4, 0x60, 0x5D, 0x6C, 0xA3, 0x71,
 +      0x04, 0x71, 0x27, 0xD0, 0x3A, 0x72, 0xD5, 0x98,
 +      0xA1, 0xED, 0xAD, 0xFE, 0x70, 0x7E, 0x88, 0x47,
 +      0x25, 0xC1, 0x68, 0x90, 0x54, 0x9D, 0x69, 0x65,
 +      0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
 +};
 +
 +/* RFC 3526, 5. Group 16 - 4096 Bit MODP
 + * Generator: 2
 + * Prime: 2^4096 - 2^4032 - 1 + 2^64 * { [2^3966 pi] + 240904 }
 + */
 +static const u8 dh_group16_generator[1] = { 0x02 };
 +static const u8 dh_group16_prime[512] = {
 +      0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
 +      0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34,
 +      0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
 +      0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74,
 +      0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22,
 +      0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
 +      0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B,
 +      0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37,
 +      0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,
 +      0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6,
 +      0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B,
 +      0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,
 +      0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5,
 +      0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6,
 +      0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D,
 +      0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05,
 +      0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A,
 +      0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F,
 +      0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96,
 +      0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB,
 +      0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D,
 +      0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04,
 +      0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x18, 0x21, 0x7C,
 +      0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B,
 +      0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03,
 +      0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, 0x8F,
 +      0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9,
 +      0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18,
 +      0x39, 0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, 0xE5,
 +      0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10,
 +      0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAA, 0xC4, 0x2D,
 +      0xAD, 0x33, 0x17, 0x0D, 0x04, 0x50, 0x7A, 0x33,
 +      0xA8, 0x55, 0x21, 0xAB, 0xDF, 0x1C, 0xBA, 0x64,
 +      0xEC, 0xFB, 0x85, 0x04, 0x58, 0xDB, 0xEF, 0x0A,
 +      0x8A, 0xEA, 0x71, 0x57, 0x5D, 0x06, 0x0C, 0x7D,
 +      0xB3, 0x97, 0x0F, 0x85, 0xA6, 0xE1, 0xE4, 0xC7,
 +      0xAB, 0xF5, 0xAE, 0x8C, 0xDB, 0x09, 0x33, 0xD7,
 +      0x1E, 0x8C, 0x94, 0xE0, 0x4A, 0x25, 0x61, 0x9D,
 +      0xCE, 0xE3, 0xD2, 0x26, 0x1A, 0xD2, 0xEE, 0x6B,
 +      0xF1, 0x2F, 0xFA, 0x06, 0xD9, 0x8A, 0x08, 0x64,
 +      0xD8, 0x76, 0x02, 0x73, 0x3E, 0xC8, 0x6A, 0x64,
 +      0x52, 0x1F, 0x2B, 0x18, 0x17, 0x7B, 0x20, 0x0C,
 +      0xBB, 0xE1, 0x17, 0x57, 0x7A, 0x61, 0x5D, 0x6C,
 +      0x77, 0x09, 0x88, 0xC0, 0xBA, 0xD9, 0x46, 0xE2,
 +      0x08, 0xE2, 0x4F, 0xA0, 0x74, 0xE5, 0xAB, 0x31,
 +      0x43, 0xDB, 0x5B, 0xFC, 0xE0, 0xFD, 0x10, 0x8E,
 +      0x4B, 0x82, 0xD1, 0x20, 0xA9, 0x21, 0x08, 0x01,
 +      0x1A, 0x72, 0x3C, 0x12, 0xA7, 0x87, 0xE6, 0xD7,
 +      0x88, 0x71, 0x9A, 0x10, 0xBD, 0xBA, 0x5B, 0x26,
 +      0x99, 0xC3, 0x27, 0x18, 0x6A, 0xF4, 0xE2, 0x3C,
 +      0x1A, 0x94, 0x68, 0x34, 0xB6, 0x15, 0x0B, 0xDA,
 +      0x25, 0x83, 0xE9, 0xCA, 0x2A, 0xD4, 0x4C, 0xE8,
 +      0xDB, 0xBB, 0xC2, 0xDB, 0x04, 0xDE, 0x8E, 0xF9,
 +      0x2E, 0x8E, 0xFC, 0x14, 0x1F, 0xBE, 0xCA, 0xA6,
 +      0x28, 0x7C, 0x59, 0x47, 0x4E, 0x6B, 0xC0, 0x5D,
 +      0x99, 0xB2, 0x96, 0x4F, 0xA0, 0x90, 0xC3, 0xA2,
 +      0x23, 0x3B, 0xA1, 0x86, 0x51, 0x5B, 0xE7, 0xED,
 +      0x1F, 0x61, 0x29, 0x70, 0xCE, 0xE2, 0xD7, 0xAF,
 +      0xB8, 0x1B, 0xDD, 0x76, 0x21, 0x70, 0x48, 0x1C,
 +      0xD0, 0x06, 0x91, 0x27, 0xD5, 0xB0, 0x5A, 0xA9,
 +      0x93, 0xB4, 0xEA, 0x98, 0x8D, 0x8F, 0xDD, 0xC1,
 +      0x86, 0xFF, 0xB7, 0xDC, 0x90, 0xA6, 0xC0, 0x8F,
 +      0x4D, 0xF4, 0x35, 0xC9, 0x34, 0x06, 0x31, 0x99,
 +      0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
 +};
 +static const u8 dh_group16_order[512] = {
 +      0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
 +      0xE4, 0x87, 0xED, 0x51, 0x10, 0xB4, 0x61, 0x1A,
 +      0x62, 0x63, 0x31, 0x45, 0xC0, 0x6E, 0x0E, 0x68,
 +      0x94, 0x81, 0x27, 0x04, 0x45, 0x33, 0xE6, 0x3A,
 +      0x01, 0x05, 0xDF, 0x53, 0x1D, 0x89, 0xCD, 0x91,
 +      0x28, 0xA5, 0x04, 0x3C, 0xC7, 0x1A, 0x02, 0x6E,
 +      0xF7, 0xCA, 0x8C, 0xD9, 0xE6, 0x9D, 0x21, 0x8D,
 +      0x98, 0x15, 0x85, 0x36, 0xF9, 0x2F, 0x8A, 0x1B,
 +      0xA7, 0xF0, 0x9A, 0xB6, 0xB6, 0xA8, 0xE1, 0x22,
 +      0xF2, 0x42, 0xDA, 0xBB, 0x31, 0x2F, 0x3F, 0x63,
 +      0x7A, 0x26, 0x21, 0x74, 0xD3, 0x1B, 0xF6, 0xB5,
 +      0x85, 0xFF, 0xAE, 0x5B, 0x7A, 0x03, 0x5B, 0xF6,
 +      0xF7, 0x1C, 0x35, 0xFD, 0xAD, 0x44, 0xCF, 0xD2,
 +      0xD7, 0x4F, 0x92, 0x08, 0xBE, 0x25, 0x8F, 0xF3,
 +      0x24, 0x94, 0x33, 0x28, 0xF6, 0x72, 0x2D, 0x9E,
 +      0xE1, 0x00, 0x3E, 0x5C, 0x50, 0xB1, 0xDF, 0x82,
 +      0xCC, 0x6D, 0x24, 0x1B, 0x0E, 0x2A, 0xE9, 0xCD,
 +      0x34, 0x8B, 0x1F, 0xD4, 0x7E, 0x92, 0x67, 0xAF,
 +      0xC1, 0xB2, 0xAE, 0x91, 0xEE, 0x51, 0xD6, 0xCB,
 +      0x0E, 0x31, 0x79, 0xAB, 0x10, 0x42, 0xA9, 0x5D,
 +      0xCF, 0x6A, 0x94, 0x83, 0xB8, 0x4B, 0x4B, 0x36,
 +      0xB3, 0x86, 0x1A, 0xA7, 0x25, 0x5E, 0x4C, 0x02,
 +      0x78, 0xBA, 0x36, 0x04, 0x65, 0x0C, 0x10, 0xBE,
 +      0x19, 0x48, 0x2F, 0x23, 0x17, 0x1B, 0x67, 0x1D,
 +      0xF1, 0xCF, 0x3B, 0x96, 0x0C, 0x07, 0x43, 0x01,
 +      0xCD, 0x93, 0xC1, 0xD1, 0x76, 0x03, 0xD1, 0x47,
 +      0xDA, 0xE2, 0xAE, 0xF8, 0x37, 0xA6, 0x29, 0x64,
 +      0xEF, 0x15, 0xE5, 0xFB, 0x4A, 0xAC, 0x0B, 0x8C,
 +      0x1C, 0xCA, 0xA4, 0xBE, 0x75, 0x4A, 0xB5, 0x72,
 +      0x8A, 0xE9, 0x13, 0x0C, 0x4C, 0x7D, 0x02, 0x88,
 +      0x0A, 0xB9, 0x47, 0x2D, 0x45, 0x55, 0x62, 0x16,
 +      0xD6, 0x99, 0x8B, 0x86, 0x82, 0x28, 0x3D, 0x19,
 +      0xD4, 0x2A, 0x90, 0xD5, 0xEF, 0x8E, 0x5D, 0x32,
 +      0x76, 0x7D, 0xC2, 0x82, 0x2C, 0x6D, 0xF7, 0x85,
 +      0x45, 0x75, 0x38, 0xAB, 0xAE, 0x83, 0x06, 0x3E,
 +      0xD9, 0xCB, 0x87, 0xC2, 0xD3, 0x70, 0xF2, 0x63,
 +      0xD5, 0xFA, 0xD7, 0x46, 0x6D, 0x84, 0x99, 0xEB,
 +      0x8F, 0x46, 0x4A, 0x70, 0x25, 0x12, 0xB0, 0xCE,
 +      0xE7, 0x71, 0xE9, 0x13, 0x0D, 0x69, 0x77, 0x35,
 +      0xF8, 0x97, 0xFD, 0x03, 0x6C, 0xC5, 0x04, 0x32,
 +      0x6C, 0x3B, 0x01, 0x39, 0x9F, 0x64, 0x35, 0x32,
 +      0x29, 0x0F, 0x95, 0x8C, 0x0B, 0xBD, 0x90, 0x06,
 +      0x5D, 0xF0, 0x8B, 0xAB, 0xBD, 0x30, 0xAE, 0xB6,
 +      0x3B, 0x84, 0xC4, 0x60, 0x5D, 0x6C, 0xA3, 0x71,
 +      0x04, 0x71, 0x27, 0xD0, 0x3A, 0x72, 0xD5, 0x98,
 +      0xA1, 0xED, 0xAD, 0xFE, 0x70, 0x7E, 0x88, 0x47,
 +      0x25, 0xC1, 0x68, 0x90, 0x54, 0x90, 0x84, 0x00,
 +      0x8D, 0x39, 0x1E, 0x09, 0x53, 0xC3, 0xF3, 0x6B,
 +      0xC4, 0x38, 0xCD, 0x08, 0x5E, 0xDD, 0x2D, 0x93,
 +      0x4C, 0xE1, 0x93, 0x8C, 0x35, 0x7A, 0x71, 0x1E,
 +      0x0D, 0x4A, 0x34, 0x1A, 0x5B, 0x0A, 0x85, 0xED,
 +      0x12, 0xC1, 0xF4, 0xE5, 0x15, 0x6A, 0x26, 0x74,
 +      0x6D, 0xDD, 0xE1, 0x6D, 0x82, 0x6F, 0x47, 0x7C,
 +      0x97, 0x47, 0x7E, 0x0A, 0x0F, 0xDF, 0x65, 0x53,
 +      0x14, 0x3E, 0x2C, 0xA3, 0xA7, 0x35, 0xE0, 0x2E,
 +      0xCC, 0xD9, 0x4B, 0x27, 0xD0, 0x48, 0x61, 0xD1,
 +      0x11, 0x9D, 0xD0, 0xC3, 0x28, 0xAD, 0xF3, 0xF6,
 +      0x8F, 0xB0, 0x94, 0xB8, 0x67, 0x71, 0x6B, 0xD7,
 +      0xDC, 0x0D, 0xEE, 0xBB, 0x10, 0xB8, 0x24, 0x0E,
 +      0x68, 0x03, 0x48, 0x93, 0xEA, 0xD8, 0x2D, 0x54,
 +      0xC9, 0xDA, 0x75, 0x4C, 0x46, 0xC7, 0xEE, 0xE0,
 +      0xC3, 0x7F, 0xDB, 0xEE, 0x48, 0x53, 0x60, 0x47,
 +      0xA6, 0xFA, 0x1A, 0xE4, 0x9A, 0x03, 0x18, 0xCC,
 +      0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
 +};
 +
 +/* RFC 3526, 6. Group 17 - 6144 Bit MODP
 + * Generator: 2
 + * Prime: 2^6144 - 2^6080 - 1 + 2^64 * { [2^6014 pi] + 929484 }
 + */
 +static const u8 dh_group17_generator[1] = { 0x02 };
 +static const u8 dh_group17_prime[768] = {
 +      0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
 +      0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34,
 +      0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
 +      0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74,
 +      0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22,
 +      0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
 +      0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B,
 +      0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37,
 +      0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,
 +      0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6,
 +      0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B,
 +      0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,
 +      0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5,
 +      0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6,
 +      0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D,
 +      0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05,
 +      0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A,
 +      0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F,
 +      0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96,
 +      0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB,
 +      0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D,
 +      0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04,
 +      0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x18, 0x21, 0x7C,
 +      0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B,
 +      0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03,
 +      0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, 0x8F,
 +      0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9,
 +      0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18,
 +      0x39, 0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, 0xE5,
 +      0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10,
 +      0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAA, 0xC4, 0x2D,
 +      0xAD, 0x33, 0x17, 0x0D, 0x04, 0x50, 0x7A, 0x33,
 +      0xA8, 0x55, 0x21, 0xAB, 0xDF, 0x1C, 0xBA, 0x64,
 +      0xEC, 0xFB, 0x85, 0x04, 0x58, 0xDB, 0xEF, 0x0A,
 +      0x8A, 0xEA, 0x71, 0x57, 0x5D, 0x06, 0x0C, 0x7D,
 +      0xB3, 0x97, 0x0F, 0x85, 0xA6, 0xE1, 0xE4, 0xC7,
 +      0xAB, 0xF5, 0xAE, 0x8C, 0xDB, 0x09, 0x33, 0xD7,
 +      0x1E, 0x8C, 0x94, 0xE0, 0x4A, 0x25, 0x61, 0x9D,
 +      0xCE, 0xE3, 0xD2, 0x26, 0x1A, 0xD2, 0xEE, 0x6B,
 +      0xF1, 0x2F, 0xFA, 0x06, 0xD9, 0x8A, 0x08, 0x64,
 +      0xD8, 0x76, 0x02, 0x73, 0x3E, 0xC8, 0x6A, 0x64,
 +      0x52, 0x1F, 0x2B, 0x18, 0x17, 0x7B, 0x20, 0x0C,
 +      0xBB, 0xE1, 0x17, 0x57, 0x7A, 0x61, 0x5D, 0x6C,
 +      0x77, 0x09, 0x88, 0xC0, 0xBA, 0xD9, 0x46, 0xE2,
 +      0x08, 0xE2, 0x4F, 0xA0, 0x74, 0xE5, 0xAB, 0x31,
 +      0x43, 0xDB, 0x5B, 0xFC, 0xE0, 0xFD, 0x10, 0x8E,
 +      0x4B, 0x82, 0xD1, 0x20, 0xA9, 0x21, 0x08, 0x01,
 +      0x1A, 0x72, 0x3C, 0x12, 0xA7, 0x87, 0xE6, 0xD7,
 +      0x88, 0x71, 0x9A, 0x10, 0xBD, 0xBA, 0x5B, 0x26,
 +      0x99, 0xC3, 0x27, 0x18, 0x6A, 0xF4, 0xE2, 0x3C,
 +      0x1A, 0x94, 0x68, 0x34, 0xB6, 0x15, 0x0B, 0xDA,
 +      0x25, 0x83, 0xE9, 0xCA, 0x2A, 0xD4, 0x4C, 0xE8,
 +      0xDB, 0xBB, 0xC2, 0xDB, 0x04, 0xDE, 0x8E, 0xF9,
 +      0x2E, 0x8E, 0xFC, 0x14, 0x1F, 0xBE, 0xCA, 0xA6,
 +      0x28, 0x7C, 0x59, 0x47, 0x4E, 0x6B, 0xC0, 0x5D,
 +      0x99, 0xB2, 0x96, 0x4F, 0xA0, 0x90, 0xC3, 0xA2,
 +      0x23, 0x3B, 0xA1, 0x86, 0x51, 0x5B, 0xE7, 0xED,
 +      0x1F, 0x61, 0x29, 0x70, 0xCE, 0xE2, 0xD7, 0xAF,
 +      0xB8, 0x1B, 0xDD, 0x76, 0x21, 0x70, 0x48, 0x1C,
 +      0xD0, 0x06, 0x91, 0x27, 0xD5, 0xB0, 0x5A, 0xA9,
 +      0x93, 0xB4, 0xEA, 0x98, 0x8D, 0x8F, 0xDD, 0xC1,
 +      0x86, 0xFF, 0xB7, 0xDC, 0x90, 0xA6, 0xC0, 0x8F,
 +      0x4D, 0xF4, 0x35, 0xC9, 0x34, 0x02, 0x84, 0x92,
 +      0x36, 0xC3, 0xFA, 0xB4, 0xD2, 0x7C, 0x70, 0x26,
 +      0xC1, 0xD4, 0xDC, 0xB2, 0x60, 0x26, 0x46, 0xDE,
 +      0xC9, 0x75, 0x1E, 0x76, 0x3D, 0xBA, 0x37, 0xBD,
 +      0xF8, 0xFF, 0x94, 0x06, 0xAD, 0x9E, 0x53, 0x0E,
 +      0xE5, 0xDB, 0x38, 0x2F, 0x41, 0x30, 0x01, 0xAE,
 +      0xB0, 0x6A, 0x53, 0xED, 0x90, 0x27, 0xD8, 0x31,
 +      0x17, 0x97, 0x27, 0xB0, 0x86, 0x5A, 0x89, 0x18,
 +      0xDA, 0x3E, 0xDB, 0xEB, 0xCF, 0x9B, 0x14, 0xED,
 +      0x44, 0xCE, 0x6C, 0xBA, 0xCE, 0xD4, 0xBB, 0x1B,
 +      0xDB, 0x7F, 0x14, 0x47, 0xE6, 0xCC, 0x25, 0x4B,
 +      0x33, 0x20, 0x51, 0x51, 0x2B, 0xD7, 0xAF, 0x42,
 +      0x6F, 0xB8, 0xF4, 0x01, 0x37, 0x8C, 0xD2, 0xBF,
 +      0x59, 0x83, 0xCA, 0x01, 0xC6, 0x4B, 0x92, 0xEC,
 +      0xF0, 0x32, 0xEA, 0x15, 0xD1, 0x72, 0x1D, 0x03,
 +      0xF4, 0x82, 0xD7, 0xCE, 0x6E, 0x74, 0xFE, 0xF6,
 +      0xD5, 0x5E, 0x70, 0x2F, 0x46, 0x98, 0x0C, 0x82,
 +      0xB5, 0xA8, 0x40, 0x31, 0x90, 0x0B, 0x1C, 0x9E,
 +      0x59, 0xE7, 0xC9, 0x7F, 0xBE, 0xC7, 0xE8, 0xF3,
 +      0x23, 0xA9, 0x7A, 0x7E, 0x36, 0xCC, 0x88, 0xBE,
 +      0x0F, 0x1D, 0x45, 0xB7, 0xFF, 0x58, 0x5A, 0xC5,
 +      0x4B, 0xD4, 0x07, 0xB2, 0x2B, 0x41, 0x54, 0xAA,
 +      0xCC, 0x8F, 0x6D, 0x7E, 0xBF, 0x48, 0xE1, 0xD8,
 +      0x14, 0xCC, 0x5E, 0xD2, 0x0F, 0x80, 0x37, 0xE0,
 +      0xA7, 0x97, 0x15, 0xEE, 0xF2, 0x9B, 0xE3, 0x28,
 +      0x06, 0xA1, 0xD5, 0x8B, 0xB7, 0xC5, 0xDA, 0x76,
 +      0xF5, 0x50, 0xAA, 0x3D, 0x8A, 0x1F, 0xBF, 0xF0,
 +      0xEB, 0x19, 0xCC, 0xB1, 0xA3, 0x13, 0xD5, 0x5C,
 +      0xDA, 0x56, 0xC9, 0xEC, 0x2E, 0xF2, 0x96, 0x32,
 +      0x38, 0x7F, 0xE8, 0xD7, 0x6E, 0x3C, 0x04, 0x68,
 +      0x04, 0x3E, 0x8F, 0x66, 0x3F, 0x48, 0x60, 0xEE,
 +      0x12, 0xBF, 0x2D, 0x5B, 0x0B, 0x74, 0x74, 0xD6,
 +      0xE6, 0x94, 0xF9, 0x1E, 0x6D, 0xCC, 0x40, 0x24,
 +      0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
 +};
 +static const u8 dh_group17_order[768] = {
 +      0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
 +      0xE4, 0x87, 0xED, 0x51, 0x10, 0xB4, 0x61, 0x1A,
 +      0x62, 0x63, 0x31, 0x45, 0xC0, 0x6E, 0x0E, 0x68,
 +      0x94, 0x81, 0x27, 0x04, 0x45, 0x33, 0xE6, 0x3A,
 +      0x01, 0x05, 0xDF, 0x53, 0x1D, 0x89, 0xCD, 0x91,
 +      0x28, 0xA5, 0x04, 0x3C, 0xC7, 0x1A, 0x02, 0x6E,
 +      0xF7, 0xCA, 0x8C, 0xD9, 0xE6, 0x9D, 0x21, 0x8D,
 +      0x98, 0x15, 0x85, 0x36, 0xF9, 0x2F, 0x8A, 0x1B,
 +      0xA7, 0xF0, 0x9A, 0xB6, 0xB6, 0xA8, 0xE1, 0x22,
 +      0xF2, 0x42, 0xDA, 0xBB, 0x31, 0x2F, 0x3F, 0x63,
 +      0x7A, 0x26, 0x21, 0x74, 0xD3, 0x1B, 0xF6, 0xB5,
 +      0x85, 0xFF, 0xAE, 0x5B, 0x7A, 0x03, 0x5B, 0xF6,
 +      0xF7, 0x1C, 0x35, 0xFD, 0xAD, 0x44, 0xCF, 0xD2,
 +      0xD7, 0x4F, 0x92, 0x08, 0xBE, 0x25, 0x8F, 0xF3,
 +      0x24, 0x94, 0x33, 0x28, 0xF6, 0x72, 0x2D, 0x9E,
 +      0xE1, 0x00, 0x3E, 0x5C, 0x50, 0xB1, 0xDF, 0x82,
 +      0xCC, 0x6D, 0x24, 0x1B, 0x0E, 0x2A, 0xE9, 0xCD,
 +      0x34, 0x8B, 0x1F, 0xD4, 0x7E, 0x92, 0x67, 0xAF,
 +      0xC1, 0xB2, 0xAE, 0x91, 0xEE, 0x51, 0xD6, 0xCB,
 +      0x0E, 0x31, 0x79, 0xAB, 0x10, 0x42, 0xA9, 0x5D,
 +      0xCF, 0x6A, 0x94, 0x83, 0xB8, 0x4B, 0x4B, 0x36,
 +      0xB3, 0x86, 0x1A, 0xA7, 0x25, 0x5E, 0x4C, 0x02,
 +      0x78, 0xBA, 0x36, 0x04, 0x65, 0x0C, 0x10, 0xBE,
 +      0x19, 0x48, 0x2F, 0x23, 0x17, 0x1B, 0x67, 0x1D,
 +      0xF1, 0xCF, 0x3B, 0x96, 0x0C, 0x07, 0x43, 0x01,
 +      0xCD, 0x93, 0xC1, 0xD1, 0x76, 0x03, 0xD1, 0x47,
 +      0xDA, 0xE2, 0xAE, 0xF8, 0x37, 0xA6, 0x29, 0x64,
 +      0xEF, 0x15, 0xE5, 0xFB, 0x4A, 0xAC, 0x0B, 0x8C,
 +      0x1C, 0xCA, 0xA4, 0xBE, 0x75, 0x4A, 0xB5, 0x72,
 +      0x8A, 0xE9, 0x13, 0x0C, 0x4C, 0x7D, 0x02, 0x88,
 +      0x0A, 0xB9, 0x47, 0x2D, 0x45, 0x55, 0x62, 0x16,
 +      0xD6, 0x99, 0x8B, 0x86, 0x82, 0x28, 0x3D, 0x19,
 +      0xD4, 0x2A, 0x90, 0xD5, 0xEF, 0x8E, 0x5D, 0x32,
 +      0x76, 0x7D, 0xC2, 0x82, 0x2C, 0x6D, 0xF7, 0x85,
 +      0x45, 0x75, 0x38, 0xAB, 0xAE, 0x83, 0x06, 0x3E,
 +      0xD9, 0xCB, 0x87, 0xC2, 0xD3, 0x70, 0xF2, 0x63,
 +      0xD5, 0xFA, 0xD7, 0x46, 0x6D, 0x84, 0x99, 0xEB,
 +      0x8F, 0x46, 0x4A, 0x70, 0x25, 0x12, 0xB0, 0xCE,
 +      0xE7, 0x71, 0xE9, 0x13, 0x0D, 0x69, 0x77, 0x35,
 +      0xF8, 0x97, 0xFD, 0x03, 0x6C, 0xC5, 0x04, 0x32,
 +      0x6C, 0x3B, 0x01, 0x39, 0x9F, 0x64, 0x35, 0x32,
 +      0x29, 0x0F, 0x95, 0x8C, 0x0B, 0xBD, 0x90, 0x06,
 +      0x5D, 0xF0, 0x8B, 0xAB, 0xBD, 0x30, 0xAE, 0xB6,
 +      0x3B, 0x84, 0xC4, 0x60, 0x5D, 0x6C, 0xA3, 0x71,
 +      0x04, 0x71, 0x27, 0xD0, 0x3A, 0x72, 0xD5, 0x98,
 +      0xA1, 0xED, 0xAD, 0xFE, 0x70, 0x7E, 0x88, 0x47,
 +      0x25, 0xC1, 0x68, 0x90, 0x54, 0x90, 0x84, 0x00,
 +      0x8D, 0x39, 0x1E, 0x09, 0x53, 0xC3, 0xF3, 0x6B,
 +      0xC4, 0x38, 0xCD, 0x08, 0x5E, 0xDD, 0x2D, 0x93,
 +      0x4C, 0xE1, 0x93, 0x8C, 0x35, 0x7A, 0x71, 0x1E,
 +      0x0D, 0x4A, 0x34, 0x1A, 0x5B, 0x0A, 0x85, 0xED,
 +      0x12, 0xC1, 0xF4, 0xE5, 0x15, 0x6A, 0x26, 0x74,
 +      0x6D, 0xDD, 0xE1, 0x6D, 0x82, 0x6F, 0x47, 0x7C,
 +      0x97, 0x47, 0x7E, 0x0A, 0x0F, 0xDF, 0x65, 0x53,
 +      0x14, 0x3E, 0x2C, 0xA3, 0xA7, 0x35, 0xE0, 0x2E,
 +      0xCC, 0xD9, 0x4B, 0x27, 0xD0, 0x48, 0x61, 0xD1,
 +      0x11, 0x9D, 0xD0, 0xC3, 0x28, 0xAD, 0xF3, 0xF6,
 +      0x8F, 0xB0, 0x94, 0xB8, 0x67, 0x71, 0x6B, 0xD7,
 +      0xDC, 0x0D, 0xEE, 0xBB, 0x10, 0xB8, 0x24, 0x0E,
 +      0x68, 0x03, 0x48, 0x93, 0xEA, 0xD8, 0x2D, 0x54,
 +      0xC9, 0xDA, 0x75, 0x4C, 0x46, 0xC7, 0xEE, 0xE0,
 +      0xC3, 0x7F, 0xDB, 0xEE, 0x48, 0x53, 0x60, 0x47,
 +      0xA6, 0xFA, 0x1A, 0xE4, 0x9A, 0x01, 0x42, 0x49,
 +      0x1B, 0x61, 0xFD, 0x5A, 0x69, 0x3E, 0x38, 0x13,
 +      0x60, 0xEA, 0x6E, 0x59, 0x30, 0x13, 0x23, 0x6F,
 +      0x64, 0xBA, 0x8F, 0x3B, 0x1E, 0xDD, 0x1B, 0xDE,
 +      0xFC, 0x7F, 0xCA, 0x03, 0x56, 0xCF, 0x29, 0x87,
 +      0x72, 0xED, 0x9C, 0x17, 0xA0, 0x98, 0x00, 0xD7,
 +      0x58, 0x35, 0x29, 0xF6, 0xC8, 0x13, 0xEC, 0x18,
 +      0x8B, 0xCB, 0x93, 0xD8, 0x43, 0x2D, 0x44, 0x8C,
 +      0x6D, 0x1F, 0x6D, 0xF5, 0xE7, 0xCD, 0x8A, 0x76,
 +      0xA2, 0x67, 0x36, 0x5D, 0x67, 0x6A, 0x5D, 0x8D,
 +      0xED, 0xBF, 0x8A, 0x23, 0xF3, 0x66, 0x12, 0xA5,
 +      0x99, 0x90, 0x28, 0xA8, 0x95, 0xEB, 0xD7, 0xA1,
 +      0x37, 0xDC, 0x7A, 0x00, 0x9B, 0xC6, 0x69, 0x5F,
 +      0xAC, 0xC1, 0xE5, 0x00, 0xE3, 0x25, 0xC9, 0x76,
 +      0x78, 0x19, 0x75, 0x0A, 0xE8, 0xB9, 0x0E, 0x81,
 +      0xFA, 0x41, 0x6B, 0xE7, 0x37, 0x3A, 0x7F, 0x7B,
 +      0x6A, 0xAF, 0x38, 0x17, 0xA3, 0x4C, 0x06, 0x41,
 +      0x5A, 0xD4, 0x20, 0x18, 0xC8, 0x05, 0x8E, 0x4F,
 +      0x2C, 0xF3, 0xE4, 0xBF, 0xDF, 0x63, 0xF4, 0x79,
 +      0x91, 0xD4, 0xBD, 0x3F, 0x1B, 0x66, 0x44, 0x5F,
 +      0x07, 0x8E, 0xA2, 0xDB, 0xFF, 0xAC, 0x2D, 0x62,
 +      0xA5, 0xEA, 0x03, 0xD9, 0x15, 0xA0, 0xAA, 0x55,
 +      0x66, 0x47, 0xB6, 0xBF, 0x5F, 0xA4, 0x70, 0xEC,
 +      0x0A, 0x66, 0x2F, 0x69, 0x07, 0xC0, 0x1B, 0xF0,
 +      0x53, 0xCB, 0x8A, 0xF7, 0x79, 0x4D, 0xF1, 0x94,
 +      0x03, 0x50, 0xEA, 0xC5, 0xDB, 0xE2, 0xED, 0x3B,
 +      0x7A, 0xA8, 0x55, 0x1E, 0xC5, 0x0F, 0xDF, 0xF8,
 +      0x75, 0x8C, 0xE6, 0x58, 0xD1, 0x89, 0xEA, 0xAE,
 +      0x6D, 0x2B, 0x64, 0xF6, 0x17, 0x79, 0x4B, 0x19,
 +      0x1C, 0x3F, 0xF4, 0x6B, 0xB7, 0x1E, 0x02, 0x34,
 +      0x02, 0x1F, 0x47, 0xB3, 0x1F, 0xA4, 0x30, 0x77,
 +      0x09, 0x5F, 0x96, 0xAD, 0x85, 0xBA, 0x3A, 0x6B,
 +      0x73, 0x4A, 0x7C, 0x8F, 0x36, 0xE6, 0x20, 0x12,
 +      0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
 +};
 +
 +/* RFC 3526, 7. Group 18 - 8192 Bit MODP
 + * Generator: 2
 + * Prime: 2^8192 - 2^8128 - 1 + 2^64 * { [2^8062 pi] + 4743158 }
 + */
 +static const u8 dh_group18_generator[1] = { 0x02 };
 +static const u8 dh_group18_prime[1024] = {
 +      0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
 +      0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34,
 +      0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
 +      0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74,
 +      0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22,
 +      0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
 +      0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B,
 +      0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37,
 +      0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,
 +      0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6,
 +      0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B,
 +      0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,
 +      0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5,
 +      0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6,
 +      0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D,
 +      0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05,
 +      0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A,
 +      0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F,
 +      0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96,
 +      0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB,
 +      0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D,
 +      0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04,
 +      0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x18, 0x21, 0x7C,
 +      0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B,
 +      0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03,
 +      0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, 0x8F,
 +      0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9,
 +      0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18,
 +      0x39, 0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, 0xE5,
 +      0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10,
 +      0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAA, 0xC4, 0x2D,
 +      0xAD, 0x33, 0x17, 0x0D, 0x04, 0x50, 0x7A, 0x33,
 +      0xA8, 0x55, 0x21, 0xAB, 0xDF, 0x1C, 0xBA, 0x64,
 +      0xEC, 0xFB, 0x85, 0x04, 0x58, 0xDB, 0xEF, 0x0A,
 +      0x8A, 0xEA, 0x71, 0x57, 0x5D, 0x06, 0x0C, 0x7D,
 +      0xB3, 0x97, 0x0F, 0x85, 0xA6, 0xE1, 0xE4, 0xC7,
 +      0xAB, 0xF5, 0xAE, 0x8C, 0xDB, 0x09, 0x33, 0xD7,
 +      0x1E, 0x8C, 0x94, 0xE0, 0x4A, 0x25, 0x61, 0x9D,
 +      0xCE, 0xE3, 0xD2, 0x26, 0x1A, 0xD2, 0xEE, 0x6B,
 +      0xF1, 0x2F, 0xFA, 0x06, 0xD9, 0x8A, 0x08, 0x64,
 +      0xD8, 0x76, 0x02, 0x73, 0x3E, 0xC8, 0x6A, 0x64,
 +      0x52, 0x1F, 0x2B, 0x18, 0x17, 0x7B, 0x20, 0x0C,
 +      0xBB, 0xE1, 0x17, 0x57, 0x7A, 0x61, 0x5D, 0x6C,
 +      0x77, 0x09, 0x88, 0xC0, 0xBA, 0xD9, 0x46, 0xE2,
 +      0x08, 0xE2, 0x4F, 0xA0, 0x74, 0xE5, 0xAB, 0x31,
 +      0x43, 0xDB, 0x5B, 0xFC, 0xE0, 0xFD, 0x10, 0x8E,
 +      0x4B, 0x82, 0xD1, 0x20, 0xA9, 0x21, 0x08, 0x01,
 +      0x1A, 0x72, 0x3C, 0x12, 0xA7, 0x87, 0xE6, 0xD7,
 +      0x88, 0x71, 0x9A, 0x10, 0xBD, 0xBA, 0x5B, 0x26,
 +      0x99, 0xC3, 0x27, 0x18, 0x6A, 0xF4, 0xE2, 0x3C,
 +      0x1A, 0x94, 0x68, 0x34, 0xB6, 0x15, 0x0B, 0xDA,
 +      0x25, 0x83, 0xE9, 0xCA, 0x2A, 0xD4, 0x4C, 0xE8,
 +      0xDB, 0xBB, 0xC2, 0xDB, 0x04, 0xDE, 0x8E, 0xF9,
 +      0x2E, 0x8E, 0xFC, 0x14, 0x1F, 0xBE, 0xCA, 0xA6,
 +      0x28, 0x7C, 0x59, 0x47, 0x4E, 0x6B, 0xC0, 0x5D,
 +      0x99, 0xB2, 0x96, 0x4F, 0xA0, 0x90, 0xC3, 0xA2,
 +      0x23, 0x3B, 0xA1, 0x86, 0x51, 0x5B, 0xE7, 0xED,
 +      0x1F, 0x61, 0x29, 0x70, 0xCE, 0xE2, 0xD7, 0xAF,
 +      0xB8, 0x1B, 0xDD, 0x76, 0x21, 0x70, 0x48, 0x1C,
 +      0xD0, 0x06, 0x91, 0x27, 0xD5, 0xB0, 0x5A, 0xA9,
 +      0x93, 0xB4, 0xEA, 0x98, 0x8D, 0x8F, 0xDD, 0xC1,
 +      0x86, 0xFF, 0xB7, 0xDC, 0x90, 0xA6, 0xC0, 0x8F,
 +      0x4D, 0xF4, 0x35, 0xC9, 0x34, 0x02, 0x84, 0x92,
 +      0x36, 0xC3, 0xFA, 0xB4, 0xD2, 0x7C, 0x70, 0x26,
 +      0xC1, 0xD4, 0xDC, 0xB2, 0x60, 0x26, 0x46, 0xDE,
 +      0xC9, 0x75, 0x1E, 0x76, 0x3D, 0xBA, 0x37, 0xBD,
 +      0xF8, 0xFF, 0x94, 0x06, 0xAD, 0x9E, 0x53, 0x0E,
 +      0xE5, 0xDB, 0x38, 0x2F, 0x41, 0x30, 0x01, 0xAE,
 +      0xB0, 0x6A, 0x53, 0xED, 0x90, 0x27, 0xD8, 0x31,
 +      0x17, 0x97, 0x27, 0xB0, 0x86, 0x5A, 0x89, 0x18,
 +      0xDA, 0x3E, 0xDB, 0xEB, 0xCF, 0x9B, 0x14, 0xED,
 +      0x44, 0xCE, 0x6C, 0xBA, 0xCE, 0xD4, 0xBB, 0x1B,
 +      0xDB, 0x7F, 0x14, 0x47, 0xE6, 0xCC, 0x25, 0x4B,
 +      0x33, 0x20, 0x51, 0x51, 0x2B, 0xD7, 0xAF, 0x42,
 +      0x6F, 0xB8, 0xF4, 0x01, 0x37, 0x8C, 0xD2, 0xBF,
 +      0x59, 0x83, 0xCA, 0x01, 0xC6, 0x4B, 0x92, 0xEC,
 +      0xF0, 0x32, 0xEA, 0x15, 0xD1, 0x72, 0x1D, 0x03,
 +      0xF4, 0x82, 0xD7, 0xCE, 0x6E, 0x74, 0xFE, 0xF6,
 +      0xD5, 0x5E, 0x70, 0x2F, 0x46, 0x98, 0x0C, 0x82,
 +      0xB5, 0xA8, 0x40, 0x31, 0x90, 0x0B, 0x1C, 0x9E,
 +      0x59, 0xE7, 0xC9, 0x7F, 0xBE, 0xC7, 0xE8, 0xF3,
 +      0x23, 0xA9, 0x7A, 0x7E, 0x36, 0xCC, 0x88, 0xBE,
 +      0x0F, 0x1D, 0x45, 0xB7, 0xFF, 0x58, 0x5A, 0xC5,
 +      0x4B, 0xD4, 0x07, 0xB2, 0x2B, 0x41, 0x54, 0xAA,
 +      0xCC, 0x8F, 0x6D, 0x7E, 0xBF, 0x48, 0xE1, 0xD8,
 +      0x14, 0xCC, 0x5E, 0xD2, 0x0F, 0x80, 0x37, 0xE0,
 +      0xA7, 0x97, 0x15, 0xEE, 0xF2, 0x9B, 0xE3, 0x28,
 +      0x06, 0xA1, 0xD5, 0x8B, 0xB7, 0xC5, 0xDA, 0x76,
 +      0xF5, 0x50, 0xAA, 0x3D, 0x8A, 0x1F, 0xBF, 0xF0,
 +      0xEB, 0x19, 0xCC, 0xB1, 0xA3, 0x13, 0xD5, 0x5C,
 +      0xDA, 0x56, 0xC9, 0xEC, 0x2E, 0xF2, 0x96, 0x32,
 +      0x38, 0x7F, 0xE8, 0xD7, 0x6E, 0x3C, 0x04, 0x68,
 +      0x04, 0x3E, 0x8F, 0x66, 0x3F, 0x48, 0x60, 0xEE,
 +      0x12, 0xBF, 0x2D, 0x5B, 0x0B, 0x74, 0x74, 0xD6,
 +      0xE6, 0x94, 0xF9, 0x1E, 0x6D, 0xBE, 0x11, 0x59,
 +      0x74, 0xA3, 0x92, 0x6F, 0x12, 0xFE, 0xE5, 0xE4,
 +      0x38, 0x77, 0x7C, 0xB6, 0xA9, 0x32, 0xDF, 0x8C,
 +      0xD8, 0xBE, 0xC4, 0xD0, 0x73, 0xB9, 0x31, 0xBA,
 +      0x3B, 0xC8, 0x32, 0xB6, 0x8D, 0x9D, 0xD3, 0x00,
 +      0x74, 0x1F, 0xA7, 0xBF, 0x8A, 0xFC, 0x47, 0xED,
 +      0x25, 0x76, 0xF6, 0x93, 0x6B, 0xA4, 0x24, 0x66,
 +      0x3A, 0xAB, 0x63, 0x9C, 0x5A, 0xE4, 0xF5, 0x68,
 +      0x34, 0x23, 0xB4, 0x74, 0x2B, 0xF1, 0xC9, 0x78,
 +      0x23, 0x8F, 0x16, 0xCB, 0xE3, 0x9D, 0x65, 0x2D,
 +      0xE3, 0xFD, 0xB8, 0xBE, 0xFC, 0x84, 0x8A, 0xD9,
 +      0x22, 0x22, 0x2E, 0x04, 0xA4, 0x03, 0x7C, 0x07,
 +      0x13, 0xEB, 0x57, 0xA8, 0x1A, 0x23, 0xF0, 0xC7,
 +      0x34, 0x73, 0xFC, 0x64, 0x6C, 0xEA, 0x30, 0x6B,
 +      0x4B, 0xCB, 0xC8, 0x86, 0x2F, 0x83, 0x85, 0xDD,
 +      0xFA, 0x9D, 0x4B, 0x7F, 0xA2, 0xC0, 0x87, 0xE8,
 +      0x79, 0x68, 0x33, 0x03, 0xED, 0x5B, 0xDD, 0x3A,
 +      0x06, 0x2B, 0x3C, 0xF5, 0xB3, 0xA2, 0x78, 0xA6,
 +      0x6D, 0x2A, 0x13, 0xF8, 0x3F, 0x44, 0xF8, 0x2D,
 +      0xDF, 0x31, 0x0E, 0xE0, 0x74, 0xAB, 0x6A, 0x36,
 +      0x45, 0x97, 0xE8, 0x99, 0xA0, 0x25, 0x5D, 0xC1,
 +      0x64, 0xF3, 0x1C, 0xC5, 0x08, 0x46, 0x85, 0x1D,
 +      0xF9, 0xAB, 0x48, 0x19, 0x5D, 0xED, 0x7E, 0xA1,
 +      0xB1, 0xD5, 0x10, 0xBD, 0x7E, 0xE7, 0x4D, 0x73,
 +      0xFA, 0xF3, 0x6B, 0xC3, 0x1E, 0xCF, 0xA2, 0x68,
 +      0x35, 0x90, 0x46, 0xF4, 0xEB, 0x87, 0x9F, 0x92,
 +      0x40, 0x09, 0x43, 0x8B, 0x48, 0x1C, 0x6C, 0xD7,
 +      0x88, 0x9A, 0x00, 0x2E, 0xD5, 0xEE, 0x38, 0x2B,
 +      0xC9, 0x19, 0x0D, 0xA6, 0xFC, 0x02, 0x6E, 0x47,
 +      0x95, 0x58, 0xE4, 0x47, 0x56, 0x77, 0xE9, 0xAA,
 +      0x9E, 0x30, 0x50, 0xE2, 0x76, 0x56, 0x94, 0xDF,
 +      0xC8, 0x1F, 0x56, 0xE8, 0x80, 0xB9, 0x6E, 0x71,
 +      0x60, 0xC9, 0x80, 0xDD, 0x98, 0xED, 0xD3, 0xDF,
 +      0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
 +};
 +static const u8 dh_group18_order[1024] = {
 +      0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
 +      0xE4, 0x87, 0xED, 0x51, 0x10, 0xB4, 0x61, 0x1A,
 +      0x62, 0x63, 0x31, 0x45, 0xC0, 0x6E, 0x0E, 0x68,
 +      0x94, 0x81, 0x27, 0x04, 0x45, 0x33, 0xE6, 0x3A,
 +      0x01, 0x05, 0xDF, 0x53, 0x1D, 0x89, 0xCD, 0x91,
 +      0x28, 0xA5, 0x04, 0x3C, 0xC7, 0x1A, 0x02, 0x6E,
 +      0xF7, 0xCA, 0x8C, 0xD9, 0xE6, 0x9D, 0x21, 0x8D,
 +      0x98, 0x15, 0x85, 0x36, 0xF9, 0x2F, 0x8A, 0x1B,
 +      0xA7, 0xF0, 0x9A, 0xB6, 0xB6, 0xA8, 0xE1, 0x22,
 +      0xF2, 0x42, 0xDA, 0xBB, 0x31, 0x2F, 0x3F, 0x63,
 +      0x7A, 0x26, 0x21, 0x74, 0xD3, 0x1B, 0xF6, 0xB5,
 +      0x85, 0xFF, 0xAE, 0x5B, 0x7A, 0x03, 0x5B, 0xF6,
 +      0xF7, 0x1C, 0x35, 0xFD, 0xAD, 0x44, 0xCF, 0xD2,
 +      0xD7, 0x4F, 0x92, 0x08, 0xBE, 0x25, 0x8F, 0xF3,
 +      0x24, 0x94, 0x33, 0x28, 0xF6, 0x72, 0x2D, 0x9E,
 +      0xE1, 0x00, 0x3E, 0x5C, 0x50, 0xB1, 0xDF, 0x82,
 +      0xCC, 0x6D, 0x24, 0x1B, 0x0E, 0x2A, 0xE9, 0xCD,
 +      0x34, 0x8B, 0x1F, 0xD4, 0x7E, 0x92, 0x67, 0xAF,
 +      0xC1, 0xB2, 0xAE, 0x91, 0xEE, 0x51, 0xD6, 0xCB,
 +      0x0E, 0x31, 0x79, 0xAB, 0x10, 0x42, 0xA9, 0x5D,
 +      0xCF, 0x6A, 0x94, 0x83, 0xB8, 0x4B, 0x4B, 0x36,
 +      0xB3, 0x86, 0x1A, 0xA7, 0x25, 0x5E, 0x4C, 0x02,
 +      0x78, 0xBA, 0x36, 0x04, 0x65, 0x0C, 0x10, 0xBE,
 +      0x19, 0x48, 0x2F, 0x23, 0x17, 0x1B, 0x67, 0x1D,
 +      0xF1, 0xCF, 0x3B, 0x96, 0x0C, 0x07, 0x43, 0x01,
 +      0xCD, 0x93, 0xC1, 0xD1, 0x76, 0x03, 0xD1, 0x47,
 +      0xDA, 0xE2, 0xAE, 0xF8, 0x37, 0xA6, 0x29, 0x64,
 +      0xEF, 0x15, 0xE5, 0xFB, 0x4A, 0xAC, 0x0B, 0x8C,
 +      0x1C, 0xCA, 0xA4, 0xBE, 0x75, 0x4A, 0xB5, 0x72,
 +      0x8A, 0xE9, 0x13, 0x0C, 0x4C, 0x7D, 0x02, 0x88,
 +      0x0A, 0xB9, 0x47, 0x2D, 0x45, 0x55, 0x62, 0x16,
 +      0xD6, 0x99, 0x8B, 0x86, 0x82, 0x28, 0x3D, 0x19,
 +      0xD4, 0x2A, 0x90, 0xD5, 0xEF, 0x8E, 0x5D, 0x32,
 +      0x76, 0x7D, 0xC2, 0x82, 0x2C, 0x6D, 0xF7, 0x85,
 +      0x45, 0x75, 0x38, 0xAB, 0xAE, 0x83, 0x06, 0x3E,
 +      0xD9, 0xCB, 0x87, 0xC2, 0xD3, 0x70, 0xF2, 0x63,
 +      0xD5, 0xFA, 0xD7, 0x46, 0x6D, 0x84, 0x99, 0xEB,
 +      0x8F, 0x46, 0x4A, 0x70, 0x25, 0x12, 0xB0, 0xCE,
 +      0xE7, 0x71, 0xE9, 0x13, 0x0D, 0x69, 0x77, 0x35,
 +      0xF8, 0x97, 0xFD, 0x03, 0x6C, 0xC5, 0x04, 0x32,
 +      0x6C, 0x3B, 0x01, 0x39, 0x9F, 0x64, 0x35, 0x32,
 +      0x29, 0x0F, 0x95, 0x8C, 0x0B, 0xBD, 0x90, 0x06,
 +      0x5D, 0xF0, 0x8B, 0xAB, 0xBD, 0x30, 0xAE, 0xB6,
 +      0x3B, 0x84, 0xC4, 0x60, 0x5D, 0x6C, 0xA3, 0x71,
 +      0x04, 0x71, 0x27, 0xD0, 0x3A, 0x72, 0xD5, 0x98,
 +      0xA1, 0xED, 0xAD, 0xFE, 0x70, 0x7E, 0x88, 0x47,
 +      0x25, 0xC1, 0x68, 0x90, 0x54, 0x90, 0x84, 0x00,
 +      0x8D, 0x39, 0x1E, 0x09, 0x53, 0xC3, 0xF3, 0x6B,
 +      0xC4, 0x38, 0xCD, 0x08, 0x5E, 0xDD, 0x2D, 0x93,
 +      0x4C, 0xE1, 0x93, 0x8C, 0x35, 0x7A, 0x71, 0x1E,
 +      0x0D, 0x4A, 0x34, 0x1A, 0x5B, 0x0A, 0x85, 0xED,
 +      0x12, 0xC1, 0xF4, 0xE5, 0x15, 0x6A, 0x26, 0x74,
 +      0x6D, 0xDD, 0xE1, 0x6D, 0x82, 0x6F, 0x47, 0x7C,
 +      0x97, 0x47, 0x7E, 0x0A, 0x0F, 0xDF, 0x65, 0x53,
 +      0x14, 0x3E, 0x2C, 0xA3, 0xA7, 0x35, 0xE0, 0x2E,
 +      0xCC, 0xD9, 0x4B, 0x27, 0xD0, 0x48, 0x61, 0xD1,
 +      0x11, 0x9D, 0xD0, 0xC3, 0x28, 0xAD, 0xF3, 0xF6,
 +      0x8F, 0xB0, 0x94, 0xB8, 0x67, 0x71, 0x6B, 0xD7,
 +      0xDC, 0x0D, 0xEE, 0xBB, 0x10, 0xB8, 0x24, 0x0E,
 +      0x68, 0x03, 0x48, 0x93, 0xEA, 0xD8, 0x2D, 0x54,
 +      0xC9, 0xDA, 0x75, 0x4C, 0x46, 0xC7, 0xEE, 0xE0,
 +      0xC3, 0x7F, 0xDB, 0xEE, 0x48, 0x53, 0x60, 0x47,
 +      0xA6, 0xFA, 0x1A, 0xE4, 0x9A, 0x01, 0x42, 0x49,
 +      0x1B, 0x61, 0xFD, 0x5A, 0x69, 0x3E, 0x38, 0x13,
 +      0x60, 0xEA, 0x6E, 0x59, 0x30, 0x13, 0x23, 0x6F,
 +      0x64, 0xBA, 0x8F, 0x3B, 0x1E, 0xDD, 0x1B, 0xDE,
 +      0xFC, 0x7F, 0xCA, 0x03, 0x56, 0xCF, 0x29, 0x87,
 +      0x72, 0xED, 0x9C, 0x17, 0xA0, 0x98, 0x00, 0xD7,
 +      0x58, 0x35, 0x29, 0xF6, 0xC8, 0x13, 0xEC, 0x18,
 +      0x8B, 0xCB, 0x93, 0xD8, 0x43, 0x2D, 0x44, 0x8C,
 +      0x6D, 0x1F, 0x6D, 0xF5, 0xE7, 0xCD, 0x8A, 0x76,
 +      0xA2, 0x67, 0x36, 0x5D, 0x67, 0x6A, 0x5D, 0x8D,
 +      0xED, 0xBF, 0x8A, 0x23, 0xF3, 0x66, 0x12, 0xA5,
 +      0x99, 0x90, 0x28, 0xA8, 0x95, 0xEB, 0xD7, 0xA1,
 +      0x37, 0xDC, 0x7A, 0x00, 0x9B, 0xC6, 0x69, 0x5F,
 +      0xAC, 0xC1, 0xE5, 0x00, 0xE3, 0x25, 0xC9, 0x76,
 +      0x78, 0x19, 0x75, 0x0A, 0xE8, 0xB9, 0x0E, 0x81,
 +      0xFA, 0x41, 0x6B, 0xE7, 0x37, 0x3A, 0x7F, 0x7B,
 +      0x6A, 0xAF, 0x38, 0x17, 0xA3, 0x4C, 0x06, 0x41,
 +      0x5A, 0xD4, 0x20, 0x18, 0xC8, 0x05, 0x8E, 0x4F,
 +      0x2C, 0xF3, 0xE4, 0xBF, 0xDF, 0x63, 0xF4, 0x79,
 +      0x91, 0xD4, 0xBD, 0x3F, 0x1B, 0x66, 0x44, 0x5F,
 +      0x07, 0x8E, 0xA2, 0xDB, 0xFF, 0xAC, 0x2D, 0x62,
 +      0xA5, 0xEA, 0x03, 0xD9, 0x15, 0xA0, 0xAA, 0x55,
 +      0x66, 0x47, 0xB6, 0xBF, 0x5F, 0xA4, 0x70, 0xEC,
 +      0x0A, 0x66, 0x2F, 0x69, 0x07, 0xC0, 0x1B, 0xF0,
 +      0x53, 0xCB, 0x8A, 0xF7, 0x79, 0x4D, 0xF1, 0x94,
 +      0x03, 0x50, 0xEA, 0xC5, 0xDB, 0xE2, 0xED, 0x3B,
 +      0x7A, 0xA8, 0x55, 0x1E, 0xC5, 0x0F, 0xDF, 0xF8,
 +      0x75, 0x8C, 0xE6, 0x58, 0xD1, 0x89, 0xEA, 0xAE,
 +      0x6D, 0x2B, 0x64, 0xF6, 0x17, 0x79, 0x4B, 0x19,
 +      0x1C, 0x3F, 0xF4, 0x6B, 0xB7, 0x1E, 0x02, 0x34,
 +      0x02, 0x1F, 0x47, 0xB3, 0x1F, 0xA4, 0x30, 0x77,
 +      0x09, 0x5F, 0x96, 0xAD, 0x85, 0xBA, 0x3A, 0x6B,
 +      0x73, 0x4A, 0x7C, 0x8F, 0x36, 0xDF, 0x08, 0xAC,
 +      0xBA, 0x51, 0xC9, 0x37, 0x89, 0x7F, 0x72, 0xF2,
 +      0x1C, 0x3B, 0xBE, 0x5B, 0x54, 0x99, 0x6F, 0xC6,
 +      0x6C, 0x5F, 0x62, 0x68, 0x39, 0xDC, 0x98, 0xDD,
 +      0x1D, 0xE4, 0x19, 0x5B, 0x46, 0xCE, 0xE9, 0x80,
 +      0x3A, 0x0F, 0xD3, 0xDF, 0xC5, 0x7E, 0x23, 0xF6,
 +      0x92, 0xBB, 0x7B, 0x49, 0xB5, 0xD2, 0x12, 0x33,
 +      0x1D, 0x55, 0xB1, 0xCE, 0x2D, 0x72, 0x7A, 0xB4,
 +      0x1A, 0x11, 0xDA, 0x3A, 0x15, 0xF8, 0xE4, 0xBC,
 +      0x11, 0xC7, 0x8B, 0x65, 0xF1, 0xCE, 0xB2, 0x96,
 +      0xF1, 0xFE, 0xDC, 0x5F, 0x7E, 0x42, 0x45, 0x6C,
 +      0x91, 0x11, 0x17, 0x02, 0x52, 0x01, 0xBE, 0x03,
 +      0x89, 0xF5, 0xAB, 0xD4, 0x0D, 0x11, 0xF8, 0x63,
 +      0x9A, 0x39, 0xFE, 0x32, 0x36, 0x75, 0x18, 0x35,
 +      0xA5, 0xE5, 0xE4, 0x43, 0x17, 0xC1, 0xC2, 0xEE,
 +      0xFD, 0x4E, 0xA5, 0xBF, 0xD1, 0x60, 0x43, 0xF4,
 +      0x3C, 0xB4, 0x19, 0x81, 0xF6, 0xAD, 0xEE, 0x9D,
 +      0x03, 0x15, 0x9E, 0x7A, 0xD9, 0xD1, 0x3C, 0x53,
 +      0x36, 0x95, 0x09, 0xFC, 0x1F, 0xA2, 0x7C, 0x16,
 +      0xEF, 0x98, 0x87, 0x70, 0x3A, 0x55, 0xB5, 0x1B,
 +      0x22, 0xCB, 0xF4, 0x4C, 0xD0, 0x12, 0xAE, 0xE0,
 +      0xB2, 0x79, 0x8E, 0x62, 0x84, 0x23, 0x42, 0x8E,
 +      0xFC, 0xD5, 0xA4, 0x0C, 0xAE, 0xF6, 0xBF, 0x50,
 +      0xD8, 0xEA, 0x88, 0x5E, 0xBF, 0x73, 0xA6, 0xB9,
 +      0xFD, 0x79, 0xB5, 0xE1, 0x8F, 0x67, 0xD1, 0x34,
 +      0x1A, 0xC8, 0x23, 0x7A, 0x75, 0xC3, 0xCF, 0xC9,
 +      0x20, 0x04, 0xA1, 0xC5, 0xA4, 0x0E, 0x36, 0x6B,
 +      0xC4, 0x4D, 0x00, 0x17, 0x6A, 0xF7, 0x1C, 0x15,
 +      0xE4, 0x8C, 0x86, 0xD3, 0x7E, 0x01, 0x37, 0x23,
 +      0xCA, 0xAC, 0x72, 0x23, 0xAB, 0x3B, 0xF4, 0xD5,
 +      0x4F, 0x18, 0x28, 0x71, 0x3B, 0x2B, 0x4A, 0x6F,
 +      0xE4, 0x0F, 0xAB, 0x74, 0x40, 0x5C, 0xB7, 0x38,
 +      0xB0, 0x64, 0xC0, 0x6E, 0xCC, 0x76, 0xE9, 0xEF,
 +      0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
 +};
 +
 +/*
 + * RFC 5114, 2.1.
 + * Group 22 - 1024-bit MODP Group with 160-bit Prime Order Subgroup
 + */
 +static const u8 dh_group22_generator[] = {
 +      0xA4, 0xD1, 0xCB, 0xD5, 0xC3, 0xFD, 0x34, 0x12,
 +      0x67, 0x65, 0xA4, 0x42, 0xEF, 0xB9, 0x99, 0x05,
 +      0xF8, 0x10, 0x4D, 0xD2, 0x58, 0xAC, 0x50, 0x7F,
 +      0xD6, 0x40, 0x6C, 0xFF, 0x14, 0x26, 0x6D, 0x31,
 +      0x26, 0x6F, 0xEA, 0x1E, 0x5C, 0x41, 0x56, 0x4B,
 +      0x77, 0x7E, 0x69, 0x0F, 0x55, 0x04, 0xF2, 0x13,
 +      0x16, 0x02, 0x17, 0xB4, 0xB0, 0x1B, 0x88, 0x6A,
 +      0x5E, 0x91, 0x54, 0x7F, 0x9E, 0x27, 0x49, 0xF4,
 +      0xD7, 0xFB, 0xD7, 0xD3, 0xB9, 0xA9, 0x2E, 0xE1,
 +      0x90, 0x9D, 0x0D, 0x22, 0x63, 0xF8, 0x0A, 0x76,
 +      0xA6, 0xA2, 0x4C, 0x08, 0x7A, 0x09, 0x1F, 0x53,
 +      0x1D, 0xBF, 0x0A, 0x01, 0x69, 0xB6, 0xA2, 0x8A,
 +      0xD6, 0x62, 0xA4, 0xD1, 0x8E, 0x73, 0xAF, 0xA3,
 +      0x2D, 0x77, 0x9D, 0x59, 0x18, 0xD0, 0x8B, 0xC8,
 +      0x85, 0x8F, 0x4D, 0xCE, 0xF9, 0x7C, 0x2A, 0x24,
 +      0x85, 0x5E, 0x6E, 0xEB, 0x22, 0xB3, 0xB2, 0xE5
 +};
 +static const u8 dh_group22_prime[] = {
 +      0xB1, 0x0B, 0x8F, 0x96, 0xA0, 0x80, 0xE0, 0x1D,
 +      0xDE, 0x92, 0xDE, 0x5E, 0xAE, 0x5D, 0x54, 0xEC,
 +      0x52, 0xC9, 0x9F, 0xBC, 0xFB, 0x06, 0xA3, 0xC6,
 +      0x9A, 0x6A, 0x9D, 0xCA, 0x52, 0xD2, 0x3B, 0x61,
 +      0x60, 0x73, 0xE2, 0x86, 0x75, 0xA2, 0x3D, 0x18,
 +      0x98, 0x38, 0xEF, 0x1E, 0x2E, 0xE6, 0x52, 0xC0,
 +      0x13, 0xEC, 0xB4, 0xAE, 0xA9, 0x06, 0x11, 0x23,
 +      0x24, 0x97, 0x5C, 0x3C, 0xD4, 0x9B, 0x83, 0xBF,
 +      0xAC, 0xCB, 0xDD, 0x7D, 0x90, 0xC4, 0xBD, 0x70,
 +      0x98, 0x48, 0x8E, 0x9C, 0x21, 0x9A, 0x73, 0x72,
 +      0x4E, 0xFF, 0xD6, 0xFA, 0xE5, 0x64, 0x47, 0x38,
 +      0xFA, 0xA3, 0x1A, 0x4F, 0xF5, 0x5B, 0xCC, 0xC0,
 +      0xA1, 0x51, 0xAF, 0x5F, 0x0D, 0xC8, 0xB4, 0xBD,
 +      0x45, 0xBF, 0x37, 0xDF, 0x36, 0x5C, 0x1A, 0x65,
 +      0xE6, 0x8C, 0xFD, 0xA7, 0x6D, 0x4D, 0xA7, 0x08,
 +      0xDF, 0x1F, 0xB2, 0xBC, 0x2E, 0x4A, 0x43, 0x71
 +};
 +static const u8 dh_group22_order[] = {
 +      0xF5, 0x18, 0xAA, 0x87, 0x81, 0xA8, 0xDF, 0x27,
 +      0x8A, 0xBA, 0x4E, 0x7D, 0x64, 0xB7, 0xCB, 0x9D,
 +      0x49, 0x46, 0x23, 0x53
 +};
 +
 +/*
 + * RFC 5114, 2.2.
 + * Group 23 - 2048-bit MODP Group with 224-bit Prime Order Subgroup
 + */
 +static const u8 dh_group23_generator[] = {
 +      0xAC, 0x40, 0x32, 0xEF, 0x4F, 0x2D, 0x9A, 0xE3,
 +      0x9D, 0xF3, 0x0B, 0x5C, 0x8F, 0xFD, 0xAC, 0x50,
 +      0x6C, 0xDE, 0xBE, 0x7B, 0x89, 0x99, 0x8C, 0xAF,
 +      0x74, 0x86, 0x6A, 0x08, 0xCF, 0xE4, 0xFF, 0xE3,
 +      0xA6, 0x82, 0x4A, 0x4E, 0x10, 0xB9, 0xA6, 0xF0,
 +      0xDD, 0x92, 0x1F, 0x01, 0xA7, 0x0C, 0x4A, 0xFA,
 +      0xAB, 0x73, 0x9D, 0x77, 0x00, 0xC2, 0x9F, 0x52,
 +      0xC5, 0x7D, 0xB1, 0x7C, 0x62, 0x0A, 0x86, 0x52,
 +      0xBE, 0x5E, 0x90, 0x01, 0xA8, 0xD6, 0x6A, 0xD7,
 +      0xC1, 0x76, 0x69, 0x10, 0x19, 0x99, 0x02, 0x4A,
 +      0xF4, 0xD0, 0x27, 0x27, 0x5A, 0xC1, 0x34, 0x8B,
 +      0xB8, 0xA7, 0x62, 0xD0, 0x52, 0x1B, 0xC9, 0x8A,
 +      0xE2, 0x47, 0x15, 0x04, 0x22, 0xEA, 0x1E, 0xD4,
 +      0x09, 0x93, 0x9D, 0x54, 0xDA, 0x74, 0x60, 0xCD,
 +      0xB5, 0xF6, 0xC6, 0xB2, 0x50, 0x71, 0x7C, 0xBE,
 +      0xF1, 0x80, 0xEB, 0x34, 0x11, 0x8E, 0x98, 0xD1,
 +      0x19, 0x52, 0x9A, 0x45, 0xD6, 0xF8, 0x34, 0x56,
 +      0x6E, 0x30, 0x25, 0xE3, 0x16, 0xA3, 0x30, 0xEF,
 +      0xBB, 0x77, 0xA8, 0x6F, 0x0C, 0x1A, 0xB1, 0x5B,
 +      0x05, 0x1A, 0xE3, 0xD4, 0x28, 0xC8, 0xF8, 0xAC,
 +      0xB7, 0x0A, 0x81, 0x37, 0x15, 0x0B, 0x8E, 0xEB,
 +      0x10, 0xE1, 0x83, 0xED, 0xD1, 0x99, 0x63, 0xDD,
 +      0xD9, 0xE2, 0x63, 0xE4, 0x77, 0x05, 0x89, 0xEF,
 +      0x6A, 0xA2, 0x1E, 0x7F, 0x5F, 0x2F, 0xF3, 0x81,
 +      0xB5, 0x39, 0xCC, 0xE3, 0x40, 0x9D, 0x13, 0xCD,
 +      0x56, 0x6A, 0xFB, 0xB4, 0x8D, 0x6C, 0x01, 0x91,
 +      0x81, 0xE1, 0xBC, 0xFE, 0x94, 0xB3, 0x02, 0x69,
 +      0xED, 0xFE, 0x72, 0xFE, 0x9B, 0x6A, 0xA4, 0xBD,
 +      0x7B, 0x5A, 0x0F, 0x1C, 0x71, 0xCF, 0xFF, 0x4C,
 +      0x19, 0xC4, 0x18, 0xE1, 0xF6, 0xEC, 0x01, 0x79,
 +      0x81, 0xBC, 0x08, 0x7F, 0x2A, 0x70, 0x65, 0xB3,
 +      0x84, 0xB8, 0x90, 0xD3, 0x19, 0x1F, 0x2B, 0xFA
 +};
 +static const u8 dh_group23_prime[] = {
 +      0xAD, 0x10, 0x7E, 0x1E, 0x91, 0x23, 0xA9, 0xD0,
 +      0xD6, 0x60, 0xFA, 0xA7, 0x95, 0x59, 0xC5, 0x1F,
 +      0xA2, 0x0D, 0x64, 0xE5, 0x68, 0x3B, 0x9F, 0xD1,
 +      0xB5, 0x4B, 0x15, 0x97, 0xB6, 0x1D, 0x0A, 0x75,
 +      0xE6, 0xFA, 0x14, 0x1D, 0xF9, 0x5A, 0x56, 0xDB,
 +      0xAF, 0x9A, 0x3C, 0x40, 0x7B, 0xA1, 0xDF, 0x15,
 +      0xEB, 0x3D, 0x68, 0x8A, 0x30, 0x9C, 0x18, 0x0E,
 +      0x1D, 0xE6, 0xB8, 0x5A, 0x12, 0x74, 0xA0, 0xA6,
 +      0x6D, 0x3F, 0x81, 0x52, 0xAD, 0x6A, 0xC2, 0x12,
 +      0x90, 0x37, 0xC9, 0xED, 0xEF, 0xDA, 0x4D, 0xF8,
 +      0xD9, 0x1E, 0x8F, 0xEF, 0x55, 0xB7, 0x39, 0x4B,
 +      0x7A, 0xD5, 0xB7, 0xD0, 0xB6, 0xC1, 0x22, 0x07,
 +      0xC9, 0xF9, 0x8D, 0x11, 0xED, 0x34, 0xDB, 0xF6,
 +      0xC6, 0xBA, 0x0B, 0x2C, 0x8B, 0xBC, 0x27, 0xBE,
 +      0x6A, 0x00, 0xE0, 0xA0, 0xB9, 0xC4, 0x97, 0x08,
 +      0xB3, 0xBF, 0x8A, 0x31, 0x70, 0x91, 0x88, 0x36,
 +      0x81, 0x28, 0x61, 0x30, 0xBC, 0x89, 0x85, 0xDB,
 +      0x16, 0x02, 0xE7, 0x14, 0x41, 0x5D, 0x93, 0x30,
 +      0x27, 0x82, 0x73, 0xC7, 0xDE, 0x31, 0xEF, 0xDC,
 +      0x73, 0x10, 0xF7, 0x12, 0x1F, 0xD5, 0xA0, 0x74,
 +      0x15, 0x98, 0x7D, 0x9A, 0xDC, 0x0A, 0x48, 0x6D,
 +      0xCD, 0xF9, 0x3A, 0xCC, 0x44, 0x32, 0x83, 0x87,
 +      0x31, 0x5D, 0x75, 0xE1, 0x98, 0xC6, 0x41, 0xA4,
 +      0x80, 0xCD, 0x86, 0xA1, 0xB9, 0xE5, 0x87, 0xE8,
 +      0xBE, 0x60, 0xE6, 0x9C, 0xC9, 0x28, 0xB2, 0xB9,
 +      0xC5, 0x21, 0x72, 0xE4, 0x13, 0x04, 0x2E, 0x9B,
 +      0x23, 0xF1, 0x0B, 0x0E, 0x16, 0xE7, 0x97, 0x63,
 +      0xC9, 0xB5, 0x3D, 0xCF, 0x4B, 0xA8, 0x0A, 0x29,
 +      0xE3, 0xFB, 0x73, 0xC1, 0x6B, 0x8E, 0x75, 0xB9,
 +      0x7E, 0xF3, 0x63, 0xE2, 0xFF, 0xA3, 0x1F, 0x71,
 +      0xCF, 0x9D, 0xE5, 0x38, 0x4E, 0x71, 0xB8, 0x1C,
 +      0x0A, 0xC4, 0xDF, 0xFE, 0x0C, 0x10, 0xE6, 0x4F
 +};
 +static const u8 dh_group23_order[] = {
 +      0x80, 0x1C, 0x0D, 0x34, 0xC5, 0x8D, 0x93, 0xFE,
 +      0x99, 0x71, 0x77, 0x10, 0x1F, 0x80, 0x53, 0x5A,
 +      0x47, 0x38, 0xCE, 0xBC, 0xBF, 0x38, 0x9A, 0x99,
 +      0xB3, 0x63, 0x71, 0xEB
 +};
 +
 +/*
 + * RFC 5114, 2.3.
 + * Group 24 - 2048-bit MODP Group with 256-bit Prime Order Subgroup
 + */
 +static const u8 dh_group24_generator[] = {
 +      0x3F, 0xB3, 0x2C, 0x9B, 0x73, 0x13, 0x4D, 0x0B,
 +      0x2E, 0x77, 0x50, 0x66, 0x60, 0xED, 0xBD, 0x48,
 +      0x4C, 0xA7, 0xB1, 0x8F, 0x21, 0xEF, 0x20, 0x54,
 +      0x07, 0xF4, 0x79, 0x3A, 0x1A, 0x0B, 0xA1, 0x25,
 +      0x10, 0xDB, 0xC1, 0x50, 0x77, 0xBE, 0x46, 0x3F,
 +      0xFF, 0x4F, 0xED, 0x4A, 0xAC, 0x0B, 0xB5, 0x55,
 +      0xBE, 0x3A, 0x6C, 0x1B, 0x0C, 0x6B, 0x47, 0xB1,
 +      0xBC, 0x37, 0x73, 0xBF, 0x7E, 0x8C, 0x6F, 0x62,
 +      0x90, 0x12, 0x28, 0xF8, 0xC2, 0x8C, 0xBB, 0x18,
 +      0xA5, 0x5A, 0xE3, 0x13, 0x41, 0x00, 0x0A, 0x65,
 +      0x01, 0x96, 0xF9, 0x31, 0xC7, 0x7A, 0x57, 0xF2,
 +      0xDD, 0xF4, 0x63, 0xE5, 0xE9, 0xEC, 0x14, 0x4B,
 +      0x77, 0x7D, 0xE6, 0x2A, 0xAA, 0xB8, 0xA8, 0x62,
 +      0x8A, 0xC3, 0x76, 0xD2, 0x82, 0xD6, 0xED, 0x38,
 +      0x64, 0xE6, 0x79, 0x82, 0x42, 0x8E, 0xBC, 0x83,
 +      0x1D, 0x14, 0x34, 0x8F, 0x6F, 0x2F, 0x91, 0x93,
 +      0xB5, 0x04, 0x5A, 0xF2, 0x76, 0x71, 0x64, 0xE1,
 +      0xDF, 0xC9, 0x67, 0xC1, 0xFB, 0x3F, 0x2E, 0x55,
 +      0xA4, 0xBD, 0x1B, 0xFF, 0xE8, 0x3B, 0x9C, 0x80,
 +      0xD0, 0x52, 0xB9, 0x85, 0xD1, 0x82, 0xEA, 0x0A,
 +      0xDB, 0x2A, 0x3B, 0x73, 0x13, 0xD3, 0xFE, 0x14,
 +      0xC8, 0x48, 0x4B, 0x1E, 0x05, 0x25, 0x88, 0xB9,
 +      0xB7, 0xD2, 0xBB, 0xD2, 0xDF, 0x01, 0x61, 0x99,
 +      0xEC, 0xD0, 0x6E, 0x15, 0x57, 0xCD, 0x09, 0x15,
 +      0xB3, 0x35, 0x3B, 0xBB, 0x64, 0xE0, 0xEC, 0x37,
 +      0x7F, 0xD0, 0x28, 0x37, 0x0D, 0xF9, 0x2B, 0x52,
 +      0xC7, 0x89, 0x14, 0x28, 0xCD, 0xC6, 0x7E, 0xB6,
 +      0x18, 0x4B, 0x52, 0x3D, 0x1D, 0xB2, 0x46, 0xC3,
 +      0x2F, 0x63, 0x07, 0x84, 0x90, 0xF0, 0x0E, 0xF8,
 +      0xD6, 0x47, 0xD1, 0x48, 0xD4, 0x79, 0x54, 0x51,
 +      0x5E, 0x23, 0x27, 0xCF, 0xEF, 0x98, 0xC5, 0x82,
 +      0x66, 0x4B, 0x4C, 0x0F, 0x6C, 0xC4, 0x16, 0x59
 +};
 +static const u8 dh_group24_prime[] = {
 +      0x87, 0xA8, 0xE6, 0x1D, 0xB4, 0xB6, 0x66, 0x3C,
 +      0xFF, 0xBB, 0xD1, 0x9C, 0x65, 0x19, 0x59, 0x99,
 +      0x8C, 0xEE, 0xF6, 0x08, 0x66, 0x0D, 0xD0, 0xF2,
 +      0x5D, 0x2C, 0xEE, 0xD4, 0x43, 0x5E, 0x3B, 0x00,
 +      0xE0, 0x0D, 0xF8, 0xF1, 0xD6, 0x19, 0x57, 0xD4,
 +      0xFA, 0xF7, 0xDF, 0x45, 0x61, 0xB2, 0xAA, 0x30,
 +      0x16, 0xC3, 0xD9, 0x11, 0x34, 0x09, 0x6F, 0xAA,
 +      0x3B, 0xF4, 0x29, 0x6D, 0x83, 0x0E, 0x9A, 0x7C,
 +      0x20, 0x9E, 0x0C, 0x64, 0x97, 0x51, 0x7A, 0xBD,
 +      0x5A, 0x8A, 0x9D, 0x30, 0x6B, 0xCF, 0x67, 0xED,
 +      0x91, 0xF9, 0xE6, 0x72, 0x5B, 0x47, 0x58, 0xC0,
 +      0x22, 0xE0, 0xB1, 0xEF, 0x42, 0x75, 0xBF, 0x7B,
 +      0x6C, 0x5B, 0xFC, 0x11, 0xD4, 0x5F, 0x90, 0x88,
 +      0xB9, 0x41, 0xF5, 0x4E, 0xB1, 0xE5, 0x9B, 0xB8,
 +      0xBC, 0x39, 0xA0, 0xBF, 0x12, 0x30, 0x7F, 0x5C,
 +      0x4F, 0xDB, 0x70, 0xC5, 0x81, 0xB2, 0x3F, 0x76,
 +      0xB6, 0x3A, 0xCA, 0xE1, 0xCA, 0xA6, 0xB7, 0x90,
 +      0x2D, 0x52, 0x52, 0x67, 0x35, 0x48, 0x8A, 0x0E,
 +      0xF1, 0x3C, 0x6D, 0x9A, 0x51, 0xBF, 0xA4, 0xAB,
 +      0x3A, 0xD8, 0x34, 0x77, 0x96, 0x52, 0x4D, 0x8E,
 +      0xF6, 0xA1, 0x67, 0xB5, 0xA4, 0x18, 0x25, 0xD9,
 +      0x67, 0xE1, 0x44, 0xE5, 0x14, 0x05, 0x64, 0x25,
 +      0x1C, 0xCA, 0xCB, 0x83, 0xE6, 0xB4, 0x86, 0xF6,
 +      0xB3, 0xCA, 0x3F, 0x79, 0x71, 0x50, 0x60, 0x26,
 +      0xC0, 0xB8, 0x57, 0xF6, 0x89, 0x96, 0x28, 0x56,
 +      0xDE, 0xD4, 0x01, 0x0A, 0xBD, 0x0B, 0xE6, 0x21,
 +      0xC3, 0xA3, 0x96, 0x0A, 0x54, 0xE7, 0x10, 0xC3,
 +      0x75, 0xF2, 0x63, 0x75, 0xD7, 0x01, 0x41, 0x03,
 +      0xA4, 0xB5, 0x43, 0x30, 0xC1, 0x98, 0xAF, 0x12,
 +      0x61, 0x16, 0xD2, 0x27, 0x6E, 0x11, 0x71, 0x5F,
 +      0x69, 0x38, 0x77, 0xFA, 0xD7, 0xEF, 0x09, 0xCA,
 +      0xDB, 0x09, 0x4A, 0xE9, 0x1E, 0x1A, 0x15, 0x97
 +};
 +static const u8 dh_group24_order[] = {
 +      0x8C, 0xF8, 0x36, 0x42, 0xA7, 0x09, 0xA0, 0x97,
 +      0xB4, 0x47, 0x99, 0x76, 0x40, 0x12, 0x9D, 0xA2,
 +      0x99, 0xB1, 0xA4, 0x7D, 0x1E, 0xB3, 0x75, 0x0B,
 +      0xA3, 0x08, 0xB0, 0xFE, 0x64, 0xF5, 0xFB, 0xD3
 +};
 +
 +#endif /* ALL_DH_GROUPS */
 +
 +
 +#define DH_GROUP(id,safe) \
 +{ id, dh_group ## id ## _generator, sizeof(dh_group ## id ## _generator), \
 +dh_group ## id ## _prime, sizeof(dh_group ## id ## _prime), \
 +dh_group ## id ## _order, sizeof(dh_group ## id ## _order), safe }
 +              
 +
++static const struct dh_group dh_groups[] = {
 +      DH_GROUP(5, 1),
 +#ifdef ALL_DH_GROUPS
 +      DH_GROUP(1, 1),
 +      DH_GROUP(2, 1),
 +      DH_GROUP(14, 1),
 +      DH_GROUP(15, 1),
 +      DH_GROUP(16, 1),
 +      DH_GROUP(17, 1),
 +      DH_GROUP(18, 1),
 +      DH_GROUP(22, 0),
 +      DH_GROUP(23, 0),
 +      DH_GROUP(24, 0)
 +#endif /* ALL_DH_GROUPS */
 +};
 +
 +#define NUM_DH_GROUPS ARRAY_SIZE(dh_groups)
 +
 +
 +const struct dh_group * dh_groups_get(int id)
 +{
 +      size_t i;
 +
 +      for (i = 0; i < NUM_DH_GROUPS; i++) {
 +              if (dh_groups[i].id == id)
 +                      return &dh_groups[i];
 +      }
 +      return NULL;
 +}
 +
 +
 +/**
 + * dh_init - Initialize Diffie-Hellman handshake
 + * @dh: Selected Diffie-Hellman group
 + * @priv: Pointer for returning Diffie-Hellman private key
 + * Returns: Diffie-Hellman public value
 + */
 +struct wpabuf * dh_init(const struct dh_group *dh, struct wpabuf **priv)
 +{
 +      struct wpabuf *pv;
 +      size_t pv_len;
 +
 +      if (dh == NULL)
 +              return NULL;
 +
 +      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_clear_free(*priv);
 +              *priv = NULL;
 +              return NULL;
 +      }
 +
 +      if (os_memcmp(wpabuf_head(*priv), dh->prime, dh->prime_len) > 0) {
 +              /* Make sure private value is smaller than prime */
 +              *(wpabuf_mhead_u8(*priv)) = 0;
 +      }
 +      wpa_hexdump_buf_key(MSG_DEBUG, "DH: private value", *priv);
 +
 +      pv_len = dh->prime_len;
 +      pv = wpabuf_alloc(pv_len);
 +      if (pv == NULL)
 +              return NULL;
 +      if (crypto_mod_exp(dh->generator, dh->generator_len,
 +                         wpabuf_head(*priv), wpabuf_len(*priv),
 +                         dh->prime, dh->prime_len, wpabuf_mhead(pv),
 +                         &pv_len) < 0) {
 +              wpabuf_clear_free(pv);
 +              wpa_printf(MSG_INFO, "DH: crypto_mod_exp failed");
 +              return NULL;
 +      }
 +      wpabuf_put(pv, pv_len);
 +      wpa_hexdump_buf(MSG_DEBUG, "DH: public value", pv);
 +
 +      return pv;
 +}
 +
 +
 +/**
 + * dh_derive_shared - Derive shared Diffie-Hellman key
 + * @peer_public: Diffie-Hellman public value from peer
 + * @own_private: Diffie-Hellman private key from dh_init()
 + * @dh: Selected Diffie-Hellman group
 + * Returns: Diffie-Hellman shared key
 + */
 +struct wpabuf * dh_derive_shared(const struct wpabuf *peer_public,
 +                               const struct wpabuf *own_private,
 +                               const struct dh_group *dh)
 +{
 +      struct wpabuf *shared;
 +      size_t shared_len;
 +
 +      if (dh == NULL || peer_public == NULL || own_private == NULL)
 +              return NULL;
 +
 +      shared_len = dh->prime_len;
 +      shared = wpabuf_alloc(shared_len);
 +      if (shared == NULL)
 +              return NULL;
 +      if (crypto_mod_exp(wpabuf_head(peer_public), wpabuf_len(peer_public),
 +                         wpabuf_head(own_private), wpabuf_len(own_private),
 +                         dh->prime, dh->prime_len,
 +                         wpabuf_mhead(shared), &shared_len) < 0) {
 +              wpabuf_clear_free(shared);
 +              wpa_printf(MSG_INFO, "DH: crypto_mod_exp failed");
 +              return NULL;
 +      }
 +      wpabuf_put(shared, shared_len);
 +      wpa_hexdump_buf_key(MSG_DEBUG, "DH: shared key", shared);
 +
 +      return shared;
 +}
index d69eceabff1c56015cd016c38e6fdbdb9e79ec20,0000000000000000000000000000000000000000..fb03efcd4ffc2ccd9b936e87183670502c5c6049
mode 100644,000000..100644
--- /dev/null
@@@ -1,78 -1,0 +1,86 @@@
- static void sha1_transform(u8 *state, const u8 data[64])
 +/*
 + * FIPS 186-2 PRF for libcrypto
 + * Copyright (c) 2004-2005, Jouni Malinen <j@w1.fi>
 + *
 + * This software may be distributed under the terms of the BSD license.
 + * See README for more details.
 + */
 +
 +#include "includes.h"
 +#include <openssl/sha.h>
 +
 +#include "common.h"
 +#include "crypto.h"
 +
 +
-       os_memcpy(&context.h0, state, 5 * 4);
++static void sha1_transform(u32 *state, const u8 data[64])
 +{
 +      SHA_CTX context;
 +      os_memset(&context, 0, sizeof(context));
-       os_memcpy(state, &context.h0, 5 * 4);
++      context.h0 = state[0];
++      context.h1 = state[1];
++      context.h2 = state[2];
++      context.h3 = state[3];
++      context.h4 = state[4];
 +      SHA1_Transform(&context, data);
-                       sha1_transform((u8 *) _t, xkey);
++      state[0] = context.h0;
++      state[1] = context.h1;
++      state[2] = context.h2;
++      state[3] = context.h3;
++      state[4] = context.h4;
 +}
 +
 +
 +int fips186_2_prf(const u8 *seed, size_t seed_len, u8 *x, size_t xlen)
 +{
 +      u8 xkey[64];
 +      u32 t[5], _t[5];
 +      int i, j, m, k;
 +      u8 *xpos = x;
 +      u32 carry;
 +
 +      if (seed_len < sizeof(xkey))
 +              os_memset(xkey + seed_len, 0, sizeof(xkey) - seed_len);
 +      else
 +              seed_len = sizeof(xkey);
 +
 +      /* FIPS 186-2 + change notice 1 */
 +
 +      os_memcpy(xkey, seed, seed_len);
 +      t[0] = 0x67452301;
 +      t[1] = 0xEFCDAB89;
 +      t[2] = 0x98BADCFE;
 +      t[3] = 0x10325476;
 +      t[4] = 0xC3D2E1F0;
 +
 +      m = xlen / 40;
 +      for (j = 0; j < m; j++) {
 +              /* XSEED_j = 0 */
 +              for (i = 0; i < 2; i++) {
 +                      /* XVAL = (XKEY + XSEED_j) mod 2^b */
 +
 +                      /* w_i = G(t, XVAL) */
 +                      os_memcpy(_t, t, 20);
++                      sha1_transform(_t, xkey);
 +                      _t[0] = host_to_be32(_t[0]);
 +                      _t[1] = host_to_be32(_t[1]);
 +                      _t[2] = host_to_be32(_t[2]);
 +                      _t[3] = host_to_be32(_t[3]);
 +                      _t[4] = host_to_be32(_t[4]);
 +                      os_memcpy(xpos, _t, 20);
 +
 +                      /* XKEY = (1 + XKEY + w_i) mod 2^b */
 +                      carry = 1;
 +                      for (k = 19; k >= 0; k--) {
 +                              carry += xkey[k] + xpos[k];
 +                              xkey[k] = carry & 0xff;
 +                              carry >>= 8;
 +                      }
 +
 +                      xpos += 20;
 +              }
 +              /* x_j = w_0|w_1 */
 +      }
 +
 +      return 0;
 +}
index 49a5c1c245d67e8fc4091df55846de00ed38f231,0000000000000000000000000000000000000000..053d203cb65bb408ae1c1d211ff515e2da2e0376
mode 100644,000000..100644
--- /dev/null
@@@ -1,527 -1,0 +1,524 @@@
- static int challenge_hash(const u8 *peer_challenge, const u8 *auth_challenge,
-                         const u8 *username, size_t username_len,
-                         u8 *challenge)
 +/*
 + * WPA Supplicant / shared MSCHAPV2 helper functions / RFC 2433 / RFC 2759
 + * Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi>
 + *
 + * This software may be distributed under the terms of the BSD license.
 + * See README for more details.
 + */
 +
 +#include "includes.h"
 +
 +#include "common.h"
 +#include "sha1.h"
 +#include "ms_funcs.h"
 +#include "crypto.h"
 +
 +/**
 + * utf8_to_ucs2 - Convert UTF-8 string to UCS-2 encoding
 + * @utf8_string: UTF-8 string (IN)
 + * @utf8_string_len: Length of utf8_string (IN)
 + * @ucs2_buffer: UCS-2 buffer (OUT)
 + * @ucs2_buffer_size: Length of UCS-2 buffer (IN)
 + * @ucs2_string_size: Number of 2-byte words in the resulting UCS-2 string
 + * Returns: 0 on success, -1 on failure
 + */
 +static int utf8_to_ucs2(const u8 *utf8_string, size_t utf8_string_len,
 +                        u8 *ucs2_buffer, size_t ucs2_buffer_size,
 +                        size_t *ucs2_string_size)
 +{
 +      size_t i, j;
 +
 +      for (i = 0, j = 0; i < utf8_string_len; i++) {
 +              u8 c = utf8_string[i];
 +              if (j >= ucs2_buffer_size) {
 +                      /* input too long */
 +                      return -1;
 +              }
 +              if (c <= 0x7F) {
 +                      WPA_PUT_LE16(ucs2_buffer + j, c);
 +                      j += 2;
 +              } else if (i == utf8_string_len - 1 ||
 +                         j >= ucs2_buffer_size - 1) {
 +                      /* incomplete surrogate */
 +                      return -1;
 +              } else {
 +                      u8 c2 = utf8_string[++i];
 +                      if ((c & 0xE0) == 0xC0) {
 +                              /* two-byte encoding */
 +                              WPA_PUT_LE16(ucs2_buffer + j,
 +                                           ((c & 0x1F) << 6) | (c2 & 0x3F));
 +                              j += 2;
 +                      } else if (i == utf8_string_len ||
 +                                 j >= ucs2_buffer_size - 1) {
 +                              /* incomplete surrogate */
 +                              return -1;
 +                      } else {
 +                              /* three-byte encoding */
 +                              u8 c3 = utf8_string[++i];
 +                              WPA_PUT_LE16(ucs2_buffer + j,
 +                                           ((c & 0xF) << 12) |
 +                                           ((c2 & 0x3F) << 6) | (c3 & 0x3F));
 +                              j += 2;
 +                      }
 +              }
 +      }
 +
 +      if (ucs2_string_size)
 +              *ucs2_string_size = j / 2;
 +      return 0;
 +}
 +
 +
 +/**
 + * challenge_hash - ChallengeHash() - RFC 2759, Sect. 8.2
 + * @peer_challenge: 16-octet PeerChallenge (IN)
 + * @auth_challenge: 16-octet AuthenticatorChallenge (IN)
 + * @username: 0-to-256-char UserName (IN)
 + * @username_len: Length of username
 + * @challenge: 8-octet Challenge (OUT)
 + * Returns: 0 on success, -1 on failure
 + */
-                          username_len, challenge))
-               return -1;
-       if (nt_password_hash(password, password_len, password_hash))
++int challenge_hash(const u8 *peer_challenge, const u8 *auth_challenge,
++                 const u8 *username, size_t username_len, u8 *challenge)
 +{
 +      u8 hash[SHA1_MAC_LEN];
 +      const unsigned char *addr[3];
 +      size_t len[3];
 +
 +      addr[0] = peer_challenge;
 +      len[0] = 16;
 +      addr[1] = auth_challenge;
 +      len[1] = 16;
 +      addr[2] = username;
 +      len[2] = username_len;
 +
 +      if (sha1_vector(3, addr, len, hash))
 +              return -1;
 +      os_memcpy(challenge, hash, 8);
 +      return 0;
 +}
 +
 +
 +/**
 + * nt_password_hash - NtPasswordHash() - RFC 2759, Sect. 8.3
 + * @password: 0-to-256-unicode-char Password (IN; UTF-8)
 + * @password_len: Length of password
 + * @password_hash: 16-octet PasswordHash (OUT)
 + * Returns: 0 on success, -1 on failure
 + */
 +int nt_password_hash(const u8 *password, size_t password_len,
 +                    u8 *password_hash)
 +{
 +      u8 buf[512], *pos;
 +      size_t len, max_len;
 +
 +      max_len = sizeof(buf);
 +      if (utf8_to_ucs2(password, password_len, buf, max_len, &len) < 0)
 +              return -1;
 +
 +      len *= 2;
 +      pos = buf;
 +      return md4_vector(1, (const u8 **) &pos, &len, password_hash);
 +}
 +
 +
 +/**
 + * hash_nt_password_hash - HashNtPasswordHash() - RFC 2759, Sect. 8.4
 + * @password_hash: 16-octet PasswordHash (IN)
 + * @password_hash_hash: 16-octet PasswordHashHash (OUT)
 + * Returns: 0 on success, -1 on failure
 + */
 +int hash_nt_password_hash(const u8 *password_hash, u8 *password_hash_hash)
 +{
 +      size_t len = 16;
 +      return md4_vector(1, &password_hash, &len, password_hash_hash);
 +}
 +
 +
 +/**
 + * challenge_response - ChallengeResponse() - RFC 2759, Sect. 8.5
 + * @challenge: 8-octet Challenge (IN)
 + * @password_hash: 16-octet PasswordHash (IN)
 + * @response: 24-octet Response (OUT)
 + */
 +void challenge_response(const u8 *challenge, const u8 *password_hash,
 +                      u8 *response)
 +{
 +      u8 zpwd[7];
 +      des_encrypt(challenge, password_hash, response);
 +      des_encrypt(challenge, password_hash + 7, response + 8);
 +      zpwd[0] = password_hash[14];
 +      zpwd[1] = password_hash[15];
 +      os_memset(zpwd + 2, 0, 5);
 +      des_encrypt(challenge, zpwd, response + 16);
 +}
 +
 +
 +/**
 + * generate_nt_response - GenerateNTResponse() - RFC 2759, Sect. 8.1
 + * @auth_challenge: 16-octet AuthenticatorChallenge (IN)
 + * @peer_challenge: 16-octet PeerChallenge (IN)
 + * @username: 0-to-256-char UserName (IN)
 + * @username_len: Length of username
 + * @password: 0-to-256-unicode-char Password (IN; UTF-8)
 + * @password_len: Length of password
 + * @response: 24-octet Response (OUT)
 + * Returns: 0 on success, -1 on failure
 + */
 +int generate_nt_response(const u8 *auth_challenge, const u8 *peer_challenge,
 +                       const u8 *username, size_t username_len,
 +                       const u8 *password, size_t password_len,
 +                       u8 *response)
 +{
 +      u8 challenge[8];
 +      u8 password_hash[16];
 +
 +      if (challenge_hash(peer_challenge, auth_challenge, username,
-       if (hash_nt_password_hash(password_hash, password_hash_hash))
-               return -1;
-       if (sha1_vector(3, addr1, len1, response))
-               return -1;
-       if (challenge_hash(peer_challenge, auth_challenge, username,
++                         username_len, challenge) ||
++          nt_password_hash(password, password_len, password_hash))
 +              return -1;
 +      challenge_response(challenge, password_hash, response);
 +      return 0;
 +}
 +
 +
 +/**
 + * generate_nt_response_pwhash - GenerateNTResponse() - RFC 2759, Sect. 8.1
 + * @auth_challenge: 16-octet AuthenticatorChallenge (IN)
 + * @peer_challenge: 16-octet PeerChallenge (IN)
 + * @username: 0-to-256-char UserName (IN)
 + * @username_len: Length of username
 + * @password_hash: 16-octet PasswordHash (IN)
 + * @response: 24-octet Response (OUT)
 + * Returns: 0 on success, -1 on failure
 + */
 +int generate_nt_response_pwhash(const u8 *auth_challenge,
 +                              const u8 *peer_challenge,
 +                              const u8 *username, size_t username_len,
 +                              const u8 *password_hash,
 +                              u8 *response)
 +{
 +      u8 challenge[8];
 +
 +      if (challenge_hash(peer_challenge, auth_challenge,
 +                         username, username_len,
 +                         challenge))
 +              return -1;
 +      challenge_response(challenge, password_hash, response);
 +      return 0;
 +}
 +
 +
 +/**
 + * generate_authenticator_response_pwhash - GenerateAuthenticatorResponse() - RFC 2759, Sect. 8.7
 + * @password_hash: 16-octet PasswordHash (IN)
 + * @nt_response: 24-octet NT-Response (IN)
 + * @peer_challenge: 16-octet PeerChallenge (IN)
 + * @auth_challenge: 16-octet AuthenticatorChallenge (IN)
 + * @username: 0-to-256-char UserName (IN)
 + * @username_len: Length of username
 + * @response: 20-octet AuthenticatorResponse (OUT) (note: this value is usually
 + * encoded as a 42-octet ASCII string (S=hexdump_of_response)
 + * Returns: 0 on success, -1 on failure
 + */
 +int generate_authenticator_response_pwhash(
 +      const u8 *password_hash,
 +      const u8 *peer_challenge, const u8 *auth_challenge,
 +      const u8 *username, size_t username_len,
 +      const u8 *nt_response, u8 *response)
 +{
 +      static const u8 magic1[39] = {
 +              0x4D, 0x61, 0x67, 0x69, 0x63, 0x20, 0x73, 0x65, 0x72, 0x76,
 +              0x65, 0x72, 0x20, 0x74, 0x6F, 0x20, 0x63, 0x6C, 0x69, 0x65,
 +              0x6E, 0x74, 0x20, 0x73, 0x69, 0x67, 0x6E, 0x69, 0x6E, 0x67,
 +              0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74
 +      };
 +      static const u8 magic2[41] = {
 +              0x50, 0x61, 0x64, 0x20, 0x74, 0x6F, 0x20, 0x6D, 0x61, 0x6B,
 +              0x65, 0x20, 0x69, 0x74, 0x20, 0x64, 0x6F, 0x20, 0x6D, 0x6F,
 +              0x72, 0x65, 0x20, 0x74, 0x68, 0x61, 0x6E, 0x20, 0x6F, 0x6E,
 +              0x65, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6F,
 +              0x6E
 +      };
 +
 +      u8 password_hash_hash[16], challenge[8];
 +      const unsigned char *addr1[3];
 +      const size_t len1[3] = { 16, 24, sizeof(magic1) };
 +      const unsigned char *addr2[3];
 +      const size_t len2[3] = { SHA1_MAC_LEN, 8, sizeof(magic2) };
 +
 +      addr1[0] = password_hash_hash;
 +      addr1[1] = nt_response;
 +      addr1[2] = magic1;
 +
 +      addr2[0] = response;
 +      addr2[1] = challenge;
 +      addr2[2] = magic2;
 +
-       if (utf8_to_ucs2(password, password_len, pw_block, 512, &ucs2_len) < 0)
-               return -1;
-       if (ucs2_len > 256)
++      if (hash_nt_password_hash(password_hash, password_hash_hash) ||
++          sha1_vector(3, addr1, len1, response) ||
++          challenge_hash(peer_challenge, auth_challenge, username,
 +                         username_len, challenge))
 +              return -1;
 +      return sha1_vector(3, addr2, len2, response);
 +}
 +
 +
 +/**
 + * generate_authenticator_response - GenerateAuthenticatorResponse() - RFC 2759, Sect. 8.7
 + * @password: 0-to-256-unicode-char Password (IN; UTF-8)
 + * @password_len: Length of password
 + * @nt_response: 24-octet NT-Response (IN)
 + * @peer_challenge: 16-octet PeerChallenge (IN)
 + * @auth_challenge: 16-octet AuthenticatorChallenge (IN)
 + * @username: 0-to-256-char UserName (IN)
 + * @username_len: Length of username
 + * @response: 20-octet AuthenticatorResponse (OUT) (note: this value is usually
 + * encoded as a 42-octet ASCII string (S=hexdump_of_response)
 + * Returns: 0 on success, -1 on failure
 + */
 +int generate_authenticator_response(const u8 *password, size_t password_len,
 +                                  const u8 *peer_challenge,
 +                                  const u8 *auth_challenge,
 +                                  const u8 *username, size_t username_len,
 +                                  const u8 *nt_response, u8 *response)
 +{
 +      u8 password_hash[16];
 +      if (nt_password_hash(password, password_len, password_hash))
 +              return -1;
 +      return generate_authenticator_response_pwhash(
 +              password_hash, peer_challenge, auth_challenge,
 +              username, username_len, nt_response, response);
 +}
 +
 +
 +/**
 + * nt_challenge_response - NtChallengeResponse() - RFC 2433, Sect. A.5
 + * @challenge: 8-octet Challenge (IN)
 + * @password: 0-to-256-unicode-char Password (IN; UTF-8)
 + * @password_len: Length of password
 + * @response: 24-octet Response (OUT)
 + * Returns: 0 on success, -1 on failure
 + */
 +int nt_challenge_response(const u8 *challenge, const u8 *password,
 +                        size_t password_len, u8 *response)
 +{
 +      u8 password_hash[16];
 +      if (nt_password_hash(password, password_len, password_hash))
 +              return -1;
 +      challenge_response(challenge, password_hash, response);
 +      return 0;
 +}
 +
 +
 +/**
 + * get_master_key - GetMasterKey() - RFC 3079, Sect. 3.4
 + * @password_hash_hash: 16-octet PasswordHashHash (IN)
 + * @nt_response: 24-octet NTResponse (IN)
 + * @master_key: 16-octet MasterKey (OUT)
 + * Returns: 0 on success, -1 on failure
 + */
 +int get_master_key(const u8 *password_hash_hash, const u8 *nt_response,
 +                 u8 *master_key)
 +{
 +      static const u8 magic1[27] = {
 +              0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74,
 +              0x68, 0x65, 0x20, 0x4d, 0x50, 0x50, 0x45, 0x20, 0x4d,
 +              0x61, 0x73, 0x74, 0x65, 0x72, 0x20, 0x4b, 0x65, 0x79
 +      };
 +      const unsigned char *addr[3];
 +      const size_t len[3] = { 16, 24, sizeof(magic1) };
 +      u8 hash[SHA1_MAC_LEN];
 +
 +      addr[0] = password_hash_hash;
 +      addr[1] = nt_response;
 +      addr[2] = magic1;
 +
 +      if (sha1_vector(3, addr, len, hash))
 +              return -1;
 +      os_memcpy(master_key, hash, 16);
 +      return 0;
 +}
 +
 +
 +/**
 + * get_asymetric_start_key - GetAsymetricStartKey() - RFC 3079, Sect. 3.4
 + * @master_key: 16-octet MasterKey (IN)
 + * @session_key: 8-to-16 octet SessionKey (OUT)
 + * @session_key_len: SessionKeyLength (Length of session_key) (IN)
 + * @is_send: IsSend (IN, BOOLEAN)
 + * @is_server: IsServer (IN, BOOLEAN)
 + * Returns: 0 on success, -1 on failure
 + */
 +int get_asymetric_start_key(const u8 *master_key, u8 *session_key,
 +                          size_t session_key_len, int is_send,
 +                          int is_server)
 +{
 +      static const u8 magic2[84] = {
 +              0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69,
 +              0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20,
 +              0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
 +              0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20, 0x6b, 0x65, 0x79,
 +              0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73,
 +              0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73, 0x69, 0x64, 0x65,
 +              0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
 +              0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20,
 +              0x6b, 0x65, 0x79, 0x2e
 +      };
 +      static const u8 magic3[84] = {
 +              0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69,
 +              0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20,
 +              0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
 +              0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20,
 +              0x6b, 0x65, 0x79, 0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68,
 +              0x65, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73,
 +              0x69, 0x64, 0x65, 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73,
 +              0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20,
 +              0x6b, 0x65, 0x79, 0x2e
 +      };
 +      static const u8 shs_pad1[40] = {
 +              0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 +              0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 +              0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 +              0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
 +      };
 +
 +      static const u8 shs_pad2[40] = {
 +              0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
 +              0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
 +              0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
 +              0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2
 +      };
 +      u8 digest[SHA1_MAC_LEN];
 +      const unsigned char *addr[4];
 +      const size_t len[4] = { 16, 40, 84, 40 };
 +
 +      addr[0] = master_key;
 +      addr[1] = shs_pad1;
 +      if (is_send) {
 +              addr[2] = is_server ? magic3 : magic2;
 +      } else {
 +              addr[2] = is_server ? magic2 : magic3;
 +      }
 +      addr[3] = shs_pad2;
 +
 +      if (sha1_vector(4, addr, len, digest))
 +              return -1;
 +
 +      if (session_key_len > SHA1_MAC_LEN)
 +              session_key_len = SHA1_MAC_LEN;
 +      os_memcpy(session_key, digest, session_key_len);
 +      return 0;
 +}
 +
 +
++#ifndef CONFIG_NO_RC4
++
 +#define PWBLOCK_LEN 516
 +
 +/**
 + * encrypt_pw_block_with_password_hash - EncryptPwBlockWithPasswordHash() - RFC 2759, Sect. 8.10
 + * @password: 0-to-256-unicode-char Password (IN; UTF-8)
 + * @password_len: Length of password
 + * @password_hash: 16-octet PasswordHash (IN)
 + * @pw_block: 516-byte PwBlock (OUT)
 + * Returns: 0 on success, -1 on failure
 + */
 +int encrypt_pw_block_with_password_hash(
 +      const u8 *password, size_t password_len,
 +      const u8 *password_hash, u8 *pw_block)
 +{
 +      size_t ucs2_len, offset;
 +      u8 *pos;
 +
 +      os_memset(pw_block, 0, PWBLOCK_LEN);
 +
++      if (utf8_to_ucs2(password, password_len, pw_block, 512, &ucs2_len) < 0
++          || ucs2_len > 256)
 +              return -1;
 +
 +      offset = (256 - ucs2_len) * 2;
 +      if (offset != 0) {
 +              os_memmove(pw_block + offset, pw_block, ucs2_len * 2);
 +              if (os_get_random(pw_block, offset) < 0)
 +                      return -1;
 +      }
 +      /*
 +       * PasswordLength is 4 octets, but since the maximum password length is
 +       * 256, only first two (in little endian byte order) can be non-zero.
 +       */
 +      pos = &pw_block[2 * 256];
 +      WPA_PUT_LE16(pos, password_len * 2);
 +      rc4_skip(password_hash, 16, 0, pw_block, PWBLOCK_LEN);
 +      return 0;
 +}
 +
 +
 +/**
 + * new_password_encrypted_with_old_nt_password_hash - NewPasswordEncryptedWithOldNtPasswordHash() - RFC 2759, Sect. 8.9
 + * @new_password: 0-to-256-unicode-char NewPassword (IN; UTF-8)
 + * @new_password_len: Length of new_password
 + * @old_password: 0-to-256-unicode-char OldPassword (IN; UTF-8)
 + * @old_password_len: Length of old_password
 + * @encrypted_pw_block: 516-octet EncryptedPwBlock (OUT)
 + * Returns: 0 on success, -1 on failure
 + */
 +int new_password_encrypted_with_old_nt_password_hash(
 +      const u8 *new_password, size_t new_password_len,
 +      const u8 *old_password, size_t old_password_len,
 +      u8 *encrypted_pw_block)
 +{
 +      u8 password_hash[16];
 +
 +      if (nt_password_hash(old_password, old_password_len, password_hash))
 +              return -1;
 +      if (encrypt_pw_block_with_password_hash(new_password, new_password_len,
 +                                              password_hash,
 +                                              encrypted_pw_block))
 +              return -1;
 +      return 0;
 +}
 +
++#endif /* CONFIG_NO_RC4 */
++
 +
 +/**
 + * nt_password_hash_encrypted_with_block - NtPasswordHashEncryptedWithBlock() - RFC 2759, Sect 8.13
 + * @password_hash: 16-octer PasswordHash (IN)
 + * @block: 16-octet Block (IN)
 + * @cypher: 16-octer Cypher (OUT)
 + */
 +void nt_password_hash_encrypted_with_block(const u8 *password_hash,
 +                                         const u8 *block, u8 *cypher)
 +{
 +      des_encrypt(password_hash, block, cypher);
 +      des_encrypt(password_hash + 8, block + 7, cypher + 8);
 +}
 +
 +
 +/**
 + * old_nt_password_hash_encrypted_with_new_nt_password_hash - OldNtPasswordHashEncryptedWithNewNtPasswordHash() - RFC 2759, Sect. 8.12
 + * @new_password: 0-to-256-unicode-char NewPassword (IN; UTF-8)
 + * @new_password_len: Length of new_password
 + * @old_password: 0-to-256-unicode-char OldPassword (IN; UTF-8)
 + * @old_password_len: Length of old_password
 + * @encrypted_password_hash: 16-octet EncryptedPasswordHash (OUT)
 + * Returns: 0 on success, -1 on failure
 + */
 +int old_nt_password_hash_encrypted_with_new_nt_password_hash(
 +      const u8 *new_password, size_t new_password_len,
 +      const u8 *old_password, size_t old_password_len,
 +      u8 *encrypted_password_hash)
 +{
 +      u8 old_password_hash[16], new_password_hash[16];
 +
 +      if (nt_password_hash(old_password, old_password_len,
 +                           old_password_hash) ||
 +          nt_password_hash(new_password, new_password_len,
 +                           new_password_hash))
 +              return -1;
 +      nt_password_hash_encrypted_with_block(old_password_hash,
 +                                            new_password_hash,
 +                                            encrypted_password_hash);
 +      return 0;
 +}
index bd9bfee95b7c27dfd1c22bc0f33ce994158df2aa,0000000000000000000000000000000000000000..b5b5918e1a55563cb076283098f1ca167ce909ad
mode 100644,000000..100644
--- /dev/null
@@@ -1,58 -1,0 +1,60 @@@
 +/*
 + * WPA Supplicant / shared MSCHAPV2 helper functions / RFC 2433 / RFC 2759
 + * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
 + *
 + * This software may be distributed under the terms of the BSD license.
 + * See README for more details.
 + */
 +
 +#ifndef MS_FUNCS_H
 +#define MS_FUNCS_H
 +
 +int generate_nt_response(const u8 *auth_challenge, const u8 *peer_challenge,
 +                       const u8 *username, size_t username_len,
 +                       const u8 *password, size_t password_len,
 +                       u8 *response);
 +int generate_nt_response_pwhash(const u8 *auth_challenge,
 +                              const u8 *peer_challenge,
 +                              const u8 *username, size_t username_len,
 +                              const u8 *password_hash,
 +                              u8 *response);
 +int generate_authenticator_response(const u8 *password, size_t password_len,
 +                                  const u8 *peer_challenge,
 +                                  const u8 *auth_challenge,
 +                                  const u8 *username, size_t username_len,
 +                                  const u8 *nt_response, u8 *response);
 +int generate_authenticator_response_pwhash(
 +      const u8 *password_hash,
 +      const u8 *peer_challenge, const u8 *auth_challenge,
 +      const u8 *username, size_t username_len,
 +      const u8 *nt_response, u8 *response);
 +int nt_challenge_response(const u8 *challenge, const u8 *password,
 +                        size_t password_len, u8 *response);
 +
 +void challenge_response(const u8 *challenge, const u8 *password_hash,
 +                      u8 *response);
++int challenge_hash(const u8 *peer_challenge, const u8 *auth_challenge,
++                 const u8 *username, size_t username_len, u8 *challenge);
 +int nt_password_hash(const u8 *password, size_t password_len,
 +                   u8 *password_hash);
 +int hash_nt_password_hash(const u8 *password_hash, u8 *password_hash_hash);
 +int get_master_key(const u8 *password_hash_hash, const u8 *nt_response,
 +                 u8 *master_key);
 +int get_asymetric_start_key(const u8 *master_key, u8 *session_key,
 +                          size_t session_key_len, int is_send,
 +                          int is_server);
 +int __must_check encrypt_pw_block_with_password_hash(
 +      const u8 *password, size_t password_len,
 +      const u8 *password_hash, u8 *pw_block);
 +int __must_check new_password_encrypted_with_old_nt_password_hash(
 +      const u8 *new_password, size_t new_password_len,
 +      const u8 *old_password, size_t old_password_len,
 +      u8 *encrypted_pw_block);
 +void nt_password_hash_encrypted_with_block(const u8 *password_hash,
 +                                         const u8 *block, u8 *cypher);
 +int old_nt_password_hash_encrypted_with_new_nt_password_hash(
 +      const u8 *new_password, size_t new_password_len,
 +      const u8 *old_password, size_t old_password_len,
 +      u8 *encrypted_password_hash);
 +
 +#endif /* MS_FUNCS_H */
index bc758aa572327eda561749224abe0bf4d00438fe,0000000000000000000000000000000000000000..3a86a93a46a8bbe56fbc0fd5681fe1827af0fd2f
mode 100644,000000..100644
--- /dev/null
@@@ -1,438 -1,0 +1,439 @@@
 +/*
 + * Random number generator
 + * Copyright (c) 2010-2011, Jouni Malinen <j@w1.fi>
 + *
 + * This software may be distributed under the terms of the BSD license.
 + * See README for more details.
 + *
 + * This random number generator is used to provide additional entropy to the
 + * one provided by the operating system (os_get_random()) for session key
 + * generation. The os_get_random() output is expected to be secure and the
 + * implementation here is expected to provide only limited protection against
 + * cases where os_get_random() cannot provide strong randomness. This
 + * implementation shall not be assumed to be secure as the sole source of
 + * randomness. The random_get_bytes() function mixes in randomness from
 + * os_get_random() and as such, calls to os_get_random() can be replaced with
 + * calls to random_get_bytes() without reducing security.
 + *
 + * The design here follows partially the design used in the Linux
 + * drivers/char/random.c, but the implementation here is simpler and not as
 + * strong. This is a compromise to reduce duplicated CPU effort and to avoid
 + * extra code/memory size. As pointed out above, os_get_random() needs to be
 + * guaranteed to be secure for any of the security assumptions to hold.
 + */
 +
 +#include "utils/includes.h"
 +#ifdef __linux__
 +#include <fcntl.h>
 +#endif /* __linux__ */
 +
 +#include "utils/common.h"
 +#include "utils/eloop.h"
 +#include "crypto/crypto.h"
 +#include "sha1.h"
 +#include "random.h"
 +
 +#define POOL_WORDS 32
 +#define POOL_WORDS_MASK (POOL_WORDS - 1)
 +#define POOL_TAP1 26
 +#define POOL_TAP2 20
 +#define POOL_TAP3 14
 +#define POOL_TAP4 7
 +#define POOL_TAP5 1
 +#define EXTRACT_LEN 16
 +#define MIN_READY_MARK 2
 +
 +static u32 pool[POOL_WORDS];
 +static unsigned int input_rotate = 0;
 +static unsigned int pool_pos = 0;
 +static u8 dummy_key[20];
 +#ifdef __linux__
 +static size_t dummy_key_avail = 0;
 +static int random_fd = -1;
 +#endif /* __linux__ */
 +static unsigned int own_pool_ready = 0;
 +#define RANDOM_ENTROPY_SIZE 20
 +static char *random_entropy_file = NULL;
 +static int random_entropy_file_read = 0;
 +
 +#define MIN_COLLECT_ENTROPY 1000
 +static unsigned int entropy = 0;
 +static unsigned int total_collected = 0;
 +
 +
 +static void random_write_entropy(void);
 +
 +
 +static u32 __ROL32(u32 x, u32 y)
 +{
 +      return (x << (y & 31)) | (x >> (32 - (y & 31)));
 +}
 +
 +
 +static void random_mix_pool(const void *buf, size_t len)
 +{
 +      static const u32 twist[8] = {
 +              0x00000000, 0x3b6e20c8, 0x76dc4190, 0x4db26158,
 +              0xedb88320, 0xd6d6a3e8, 0x9b64c2b0, 0xa00ae278
 +      };
 +      const u8 *pos = buf;
 +      u32 w;
 +
 +      wpa_hexdump_key(MSG_EXCESSIVE, "random_mix_pool", buf, len);
 +
 +      while (len--) {
 +              w = __ROL32(*pos++, input_rotate & 31);
 +              input_rotate += pool_pos ? 7 : 14;
 +              pool_pos = (pool_pos - 1) & POOL_WORDS_MASK;
 +              w ^= pool[pool_pos];
 +              w ^= pool[(pool_pos + POOL_TAP1) & POOL_WORDS_MASK];
 +              w ^= pool[(pool_pos + POOL_TAP2) & POOL_WORDS_MASK];
 +              w ^= pool[(pool_pos + POOL_TAP3) & POOL_WORDS_MASK];
 +              w ^= pool[(pool_pos + POOL_TAP4) & POOL_WORDS_MASK];
 +              w ^= pool[(pool_pos + POOL_TAP5) & POOL_WORDS_MASK];
 +              pool[pool_pos] = (w >> 3) ^ twist[w & 7];
 +      }
 +}
 +
 +
 +static void random_extract(u8 *out)
 +{
 +      unsigned int i;
 +      u8 hash[SHA1_MAC_LEN];
 +      u32 *hash_ptr;
 +      u32 buf[POOL_WORDS / 2];
 +
 +      /* First, add hash back to pool to make backtracking more difficult. */
 +      hmac_sha1(dummy_key, sizeof(dummy_key), (const u8 *) pool,
 +                sizeof(pool), hash);
 +      random_mix_pool(hash, sizeof(hash));
 +      /* Hash half the pool to extra data */
 +      for (i = 0; i < POOL_WORDS / 2; i++)
 +              buf[i] = pool[(pool_pos - i) & POOL_WORDS_MASK];
 +      hmac_sha1(dummy_key, sizeof(dummy_key), (const u8 *) buf,
 +                sizeof(buf), hash);
 +      /*
 +       * Fold the hash to further reduce any potential output pattern.
 +       * Though, compromise this to reduce CPU use for the most common output
 +       * length (32) and return 16 bytes from instead of only half.
 +       */
 +      hash_ptr = (u32 *) hash;
 +      hash_ptr[0] ^= hash_ptr[4];
 +      os_memcpy(out, hash, EXTRACT_LEN);
 +}
 +
 +
 +void random_add_randomness(const void *buf, size_t len)
 +{
 +      struct os_time t;
 +      static unsigned int count = 0;
 +
 +      count++;
 +      if (entropy > MIN_COLLECT_ENTROPY && (count & 0x3ff) != 0) {
 +              /*
 +               * No need to add more entropy at this point, so save CPU and
 +               * skip the update.
 +               */
 +              return;
 +      }
 +      wpa_printf(MSG_EXCESSIVE, "Add randomness: count=%u entropy=%u",
 +                 count, entropy);
 +
 +      os_get_time(&t);
 +      wpa_hexdump_key(MSG_EXCESSIVE, "random pool",
 +                      (const u8 *) pool, sizeof(pool));
 +      random_mix_pool(&t, sizeof(t));
 +      random_mix_pool(buf, len);
 +      wpa_hexdump_key(MSG_EXCESSIVE, "random pool",
 +                      (const u8 *) pool, sizeof(pool));
 +      entropy++;
 +      total_collected++;
 +}
 +
 +
 +int random_get_bytes(void *buf, size_t len)
 +{
 +      int ret;
 +      u8 *bytes = buf;
 +      size_t left;
 +
 +      wpa_printf(MSG_MSGDUMP, "Get randomness: len=%u entropy=%u",
 +                 (unsigned int) len, entropy);
 +
 +      /* Start with assumed strong randomness from OS */
 +      ret = os_get_random(buf, len);
 +      wpa_hexdump_key(MSG_EXCESSIVE, "random from os_get_random",
 +                      buf, len);
 +
 +      /* Mix in additional entropy extracted from the internal pool */
 +      left = len;
 +      while (left) {
 +              size_t siz, i;
 +              u8 tmp[EXTRACT_LEN];
 +              random_extract(tmp);
 +              wpa_hexdump_key(MSG_EXCESSIVE, "random from internal pool",
 +                              tmp, sizeof(tmp));
 +              siz = left > EXTRACT_LEN ? EXTRACT_LEN : left;
 +              for (i = 0; i < siz; i++)
 +                      *bytes++ ^= tmp[i];
 +              left -= siz;
 +      }
 +
 +#ifdef CONFIG_FIPS
 +      /* Mix in additional entropy from the crypto module */
++      bytes = buf;
 +      left = len;
 +      while (left) {
 +              size_t siz, i;
 +              u8 tmp[EXTRACT_LEN];
 +              if (crypto_get_random(tmp, sizeof(tmp)) < 0) {
 +                      wpa_printf(MSG_ERROR, "random: No entropy available "
 +                                 "for generating strong random bytes");
 +                      return -1;
 +              }
 +              wpa_hexdump_key(MSG_EXCESSIVE, "random from crypto module",
 +                              tmp, sizeof(tmp));
 +              siz = left > EXTRACT_LEN ? EXTRACT_LEN : left;
 +              for (i = 0; i < siz; i++)
 +                      *bytes++ ^= tmp[i];
 +              left -= siz;
 +      }
 +#endif /* CONFIG_FIPS */
 +
 +      wpa_hexdump_key(MSG_EXCESSIVE, "mixed random", buf, len);
 +
 +      if (entropy < len)
 +              entropy = 0;
 +      else
 +              entropy -= len;
 +
 +      return ret;
 +}
 +
 +
 +int random_pool_ready(void)
 +{
 +#ifdef __linux__
 +      int fd;
 +      ssize_t res;
 +
 +      /*
 +       * Make sure that there is reasonable entropy available before allowing
 +       * some key derivation operations to proceed.
 +       */
 +
 +      if (dummy_key_avail == sizeof(dummy_key))
 +              return 1; /* Already initialized - good to continue */
 +
 +      /*
 +       * Try to fetch some more data from the kernel high quality
 +       * /dev/random. There may not be enough data available at this point,
 +       * so use non-blocking read to avoid blocking the application
 +       * completely.
 +       */
 +      fd = open("/dev/random", O_RDONLY | O_NONBLOCK);
 +      if (fd < 0) {
 +              wpa_printf(MSG_ERROR, "random: Cannot open /dev/random: %s",
 +                         strerror(errno));
 +              return -1;
 +      }
 +
 +      res = read(fd, dummy_key + dummy_key_avail,
 +                 sizeof(dummy_key) - dummy_key_avail);
 +      if (res < 0) {
 +              wpa_printf(MSG_ERROR, "random: Cannot read from /dev/random: "
 +                         "%s", strerror(errno));
 +              res = 0;
 +      }
 +      wpa_printf(MSG_DEBUG, "random: Got %u/%u bytes from "
 +                 "/dev/random", (unsigned) res,
 +                 (unsigned) (sizeof(dummy_key) - dummy_key_avail));
 +      dummy_key_avail += res;
 +      close(fd);
 +
 +      if (dummy_key_avail == sizeof(dummy_key)) {
 +              if (own_pool_ready < MIN_READY_MARK)
 +                      own_pool_ready = MIN_READY_MARK;
 +              random_write_entropy();
 +              return 1;
 +      }
 +
 +      wpa_printf(MSG_INFO, "random: Only %u/%u bytes of strong "
 +                 "random data available from /dev/random",
 +                 (unsigned) dummy_key_avail, (unsigned) sizeof(dummy_key));
 +
 +      if (own_pool_ready >= MIN_READY_MARK ||
 +          total_collected + 10 * own_pool_ready > MIN_COLLECT_ENTROPY) {
 +              wpa_printf(MSG_INFO, "random: Allow operation to proceed "
 +                         "based on internal entropy");
 +              return 1;
 +      }
 +
 +      wpa_printf(MSG_INFO, "random: Not enough entropy pool available for "
 +                 "secure operations");
 +      return 0;
 +#else /* __linux__ */
 +      /* TODO: could do similar checks on non-Linux platforms */
 +      return 1;
 +#endif /* __linux__ */
 +}
 +
 +
 +void random_mark_pool_ready(void)
 +{
 +      own_pool_ready++;
 +      wpa_printf(MSG_DEBUG, "random: Mark internal entropy pool to be "
 +                 "ready (count=%u/%u)", own_pool_ready, MIN_READY_MARK);
 +      random_write_entropy();
 +}
 +
 +
 +#ifdef __linux__
 +
 +static void random_close_fd(void)
 +{
 +      if (random_fd >= 0) {
 +              eloop_unregister_read_sock(random_fd);
 +              close(random_fd);
 +              random_fd = -1;
 +      }
 +}
 +
 +
 +static void random_read_fd(int sock, void *eloop_ctx, void *sock_ctx)
 +{
 +      ssize_t res;
 +
 +      if (dummy_key_avail == sizeof(dummy_key)) {
 +              random_close_fd();
 +              return;
 +      }
 +
 +      res = read(sock, dummy_key + dummy_key_avail,
 +                 sizeof(dummy_key) - dummy_key_avail);
 +      if (res < 0) {
 +              wpa_printf(MSG_ERROR, "random: Cannot read from /dev/random: "
 +                         "%s", strerror(errno));
 +              return;
 +      }
 +
 +      wpa_printf(MSG_DEBUG, "random: Got %u/%u bytes from /dev/random",
 +                 (unsigned) res,
 +                 (unsigned) (sizeof(dummy_key) - dummy_key_avail));
 +      dummy_key_avail += res;
 +
 +      if (dummy_key_avail == sizeof(dummy_key)) {
 +              random_close_fd();
 +              if (own_pool_ready < MIN_READY_MARK)
 +                      own_pool_ready = MIN_READY_MARK;
 +              random_write_entropy();
 +      }
 +}
 +
 +#endif /* __linux__ */
 +
 +
 +static void random_read_entropy(void)
 +{
 +      char *buf;
 +      size_t len;
 +
 +      if (!random_entropy_file)
 +              return;
 +
 +      buf = os_readfile(random_entropy_file, &len);
 +      if (buf == NULL)
 +              return; /* entropy file not yet available */
 +
 +      if (len != 1 + RANDOM_ENTROPY_SIZE) {
 +              wpa_printf(MSG_DEBUG, "random: Invalid entropy file %s",
 +                         random_entropy_file);
 +              os_free(buf);
 +              return;
 +      }
 +
 +      own_pool_ready = (u8) buf[0];
 +      random_add_randomness(buf + 1, RANDOM_ENTROPY_SIZE);
 +      random_entropy_file_read = 1;
 +      os_free(buf);
 +      wpa_printf(MSG_DEBUG, "random: Added entropy from %s "
 +                 "(own_pool_ready=%u)",
 +                 random_entropy_file, own_pool_ready);
 +}
 +
 +
 +static void random_write_entropy(void)
 +{
 +      char buf[RANDOM_ENTROPY_SIZE];
 +      FILE *f;
 +      u8 opr;
 +      int fail = 0;
 +
 +      if (!random_entropy_file)
 +              return;
 +
 +      if (random_get_bytes(buf, RANDOM_ENTROPY_SIZE) < 0)
 +              return;
 +
 +      f = fopen(random_entropy_file, "wb");
 +      if (f == NULL) {
 +              wpa_printf(MSG_ERROR, "random: Could not open entropy file %s "
 +                         "for writing", random_entropy_file);
 +              return;
 +      }
 +
 +      opr = own_pool_ready > 0xff ? 0xff : own_pool_ready;
 +      if (fwrite(&opr, 1, 1, f) != 1 ||
 +          fwrite(buf, RANDOM_ENTROPY_SIZE, 1, f) != 1)
 +              fail = 1;
 +      fclose(f);
 +      if (fail) {
 +              wpa_printf(MSG_ERROR, "random: Could not write entropy data "
 +                         "to %s", random_entropy_file);
 +              return;
 +      }
 +
 +      wpa_printf(MSG_DEBUG, "random: Updated entropy file %s "
 +                 "(own_pool_ready=%u)",
 +                 random_entropy_file, own_pool_ready);
 +}
 +
 +
 +void random_init(const char *entropy_file)
 +{
 +      os_free(random_entropy_file);
 +      if (entropy_file)
 +              random_entropy_file = os_strdup(entropy_file);
 +      else
 +              random_entropy_file = NULL;
 +      random_read_entropy();
 +
 +#ifdef __linux__
 +      if (random_fd >= 0)
 +              return;
 +
 +      random_fd = open("/dev/random", O_RDONLY | O_NONBLOCK);
 +      if (random_fd < 0) {
 +              wpa_printf(MSG_ERROR, "random: Cannot open /dev/random: %s",
 +                         strerror(errno));
 +              return;
 +      }
 +      wpa_printf(MSG_DEBUG, "random: Trying to read entropy from "
 +                 "/dev/random");
 +
 +      eloop_register_read_sock(random_fd, random_read_fd, NULL, NULL);
 +#endif /* __linux__ */
 +
 +      random_write_entropy();
 +}
 +
 +
 +void random_deinit(void)
 +{
 +#ifdef __linux__
 +      random_close_fd();
 +#endif /* __linux__ */
 +      random_write_entropy();
 +      os_free(random_entropy_file);
 +      random_entropy_file = NULL;
 +}
index 0effd9b76dd89c15d8e53c8fc96c2bf098287daa,0000000000000000000000000000000000000000..f9bc0ebf6e3d024df6ef3e696a6705c3e7a91a92
mode 100644,000000..100644
--- /dev/null
@@@ -1,99 -1,0 +1,104 @@@
 +/*
 + * TLS PRF (SHA1 + MD5)
 + * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
 + *
 + * This software may be distributed under the terms of the BSD license.
 + * See README for more details.
 + */
 +
 +#include "includes.h"
 +
 +#include "common.h"
 +#include "sha1.h"
 +#include "md5.h"
 +
 +
 +/**
 + * tls_prf_sha1_md5 - Pseudo-Random Function for TLS (TLS-PRF, RFC 2246)
 + * @secret: Key for PRF
 + * @secret_len: Length of the key in bytes
 + * @label: A unique label for each purpose of the PRF
 + * @seed: Seed value to bind into the key
 + * @seed_len: Length of the seed
 + * @out: Buffer for the generated pseudo-random key
 + * @outlen: Number of bytes of key to generate
 + * Returns: 0 on success, -1 on failure.
 + *
 + * This function is used to derive new, cryptographically separate keys from a
 + * given key in TLS. This PRF is defined in RFC 2246, Chapter 5.
 + */
 +int tls_prf_sha1_md5(const u8 *secret, size_t secret_len, const char *label,
 +                   const u8 *seed, size_t seed_len, u8 *out, size_t outlen)
 +{
 +      size_t L_S1, L_S2, i;
 +      const u8 *S1, *S2;
 +      u8 A_MD5[MD5_MAC_LEN], A_SHA1[SHA1_MAC_LEN];
 +      u8 P_MD5[MD5_MAC_LEN], P_SHA1[SHA1_MAC_LEN];
 +      int MD5_pos, SHA1_pos;
 +      const u8 *MD5_addr[3];
 +      size_t MD5_len[3];
 +      const unsigned char *SHA1_addr[3];
 +      size_t SHA1_len[3];
 +
 +      if (secret_len & 1)
 +              return -1;
 +
 +      MD5_addr[0] = A_MD5;
 +      MD5_len[0] = MD5_MAC_LEN;
 +      MD5_addr[1] = (unsigned char *) label;
 +      MD5_len[1] = os_strlen(label);
 +      MD5_addr[2] = seed;
 +      MD5_len[2] = seed_len;
 +
 +      SHA1_addr[0] = A_SHA1;
 +      SHA1_len[0] = SHA1_MAC_LEN;
 +      SHA1_addr[1] = (unsigned char *) label;
 +      SHA1_len[1] = os_strlen(label);
 +      SHA1_addr[2] = seed;
 +      SHA1_len[2] = seed_len;
 +
 +      /* RFC 2246, Chapter 5
 +       * A(0) = seed, A(i) = HMAC(secret, A(i-1))
 +       * P_hash = HMAC(secret, A(1) + seed) + HMAC(secret, A(2) + seed) + ..
 +       * PRF = P_MD5(S1, label + seed) XOR P_SHA-1(S2, label + seed)
 +       */
 +
 +      L_S1 = L_S2 = (secret_len + 1) / 2;
 +      S1 = secret;
 +      S2 = secret + L_S1;
 +      if (secret_len & 1) {
 +              /* The last byte of S1 will be shared with S2 */
 +              S2--;
 +      }
 +
 +      hmac_md5_vector(S1, L_S1, 2, &MD5_addr[1], &MD5_len[1], A_MD5);
 +      hmac_sha1_vector(S2, L_S2, 2, &SHA1_addr[1], &SHA1_len[1], A_SHA1);
 +
 +      MD5_pos = MD5_MAC_LEN;
 +      SHA1_pos = SHA1_MAC_LEN;
 +      for (i = 0; i < outlen; i++) {
 +              if (MD5_pos == MD5_MAC_LEN) {
 +                      hmac_md5_vector(S1, L_S1, 3, MD5_addr, MD5_len, P_MD5);
 +                      MD5_pos = 0;
 +                      hmac_md5(S1, L_S1, A_MD5, MD5_MAC_LEN, A_MD5);
 +              }
 +              if (SHA1_pos == SHA1_MAC_LEN) {
 +                      hmac_sha1_vector(S2, L_S2, 3, SHA1_addr, SHA1_len,
 +                                       P_SHA1);
 +                      SHA1_pos = 0;
 +                      hmac_sha1(S2, L_S2, A_SHA1, SHA1_MAC_LEN, A_SHA1);
 +              }
 +
 +              out[i] = P_MD5[MD5_pos] ^ P_SHA1[SHA1_pos];
 +
 +              MD5_pos++;
 +              SHA1_pos++;
 +      }
 +
++      os_memset(A_MD5, 0, MD5_MAC_LEN);
++      os_memset(P_MD5, 0, MD5_MAC_LEN);
++      os_memset(A_SHA1, 0, SHA1_MAC_LEN);
++      os_memset(P_SHA1, 0, SHA1_MAC_LEN);
++
 +      return 0;
 +}
index a52949462f77b9746a3d459cb8a6c4cc624d9d0d,0000000000000000000000000000000000000000..562510f8937d07b316eed5f78b15cc10542e5b5f
mode 100644,000000..100644
--- /dev/null
@@@ -1,70 -1,0 +1,72 @@@
 +/*
 + * SHA1 T-PRF for EAP-FAST
 + * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
 + *
 + * This software may be distributed under the terms of the BSD license.
 + * See README for more details.
 + */
 +
 +#include "includes.h"
 +
 +#include "common.h"
 +#include "sha1.h"
 +#include "crypto.h"
 +
 +/**
 + * sha1_t_prf - EAP-FAST Pseudo-Random Function (T-PRF)
 + * @key: Key for PRF
 + * @key_len: Length of the key in bytes
 + * @label: A unique label for each purpose of the PRF
 + * @seed: Seed value to bind into the key
 + * @seed_len: Length of the seed
 + * @buf: Buffer for the generated pseudo-random key
 + * @buf_len: Number of bytes of key to generate
 + * Returns: 0 on success, -1 of failure
 + *
 + * This function is used to derive new, cryptographically separate keys from a
 + * given key for EAP-FAST. T-PRF is defined in RFC 4851, Section 5.5.
 + */
 +int sha1_t_prf(const u8 *key, size_t key_len, const char *label,
 +             const u8 *seed, size_t seed_len, u8 *buf, size_t buf_len)
 +{
 +      unsigned char counter = 0;
 +      size_t pos, plen;
 +      u8 hash[SHA1_MAC_LEN];
 +      size_t label_len = os_strlen(label);
 +      u8 output_len[2];
 +      const unsigned char *addr[5];
 +      size_t len[5];
 +
 +      addr[0] = hash;
 +      len[0] = 0;
 +      addr[1] = (unsigned char *) label;
 +      len[1] = label_len + 1;
 +      addr[2] = seed;
 +      len[2] = seed_len;
 +      addr[3] = output_len;
 +      len[3] = 2;
 +      addr[4] = &counter;
 +      len[4] = 1;
 +
 +      output_len[0] = (buf_len >> 8) & 0xff;
 +      output_len[1] = buf_len & 0xff;
 +      pos = 0;
 +      while (pos < buf_len) {
 +              counter++;
 +              plen = buf_len - pos;
 +              if (hmac_sha1_vector(key, key_len, 5, addr, len, hash))
 +                      return -1;
 +              if (plen >= SHA1_MAC_LEN) {
 +                      os_memcpy(&buf[pos], hash, SHA1_MAC_LEN);
 +                      pos += SHA1_MAC_LEN;
 +              } else {
 +                      os_memcpy(&buf[pos], hash, plen);
 +                      break;
 +              }
 +              len[0] = SHA1_MAC_LEN;
 +      }
 +
++      os_memset(hash, 0, SHA1_MAC_LEN);
++
 +      return 0;
 +}
index d8a1beb32e9080728865c32ddedd93f6c3dc72c8,0000000000000000000000000000000000000000..e7509ce41aba4dd13601b42fc6a62936281a0bfc
mode 100644,000000..100644
--- /dev/null
@@@ -1,76 -1,0 +1,79 @@@
 +/*
 + * 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);
++                      os_memset(T, 0, SHA256_MAC_LEN);
 +                      return -1;
 +              }
 +              iter++;
 +
 +              if (hmac_sha256_vector(secret, secret_len, 4, addr, len, T) < 0)
 +              {
 +                      os_memset(out, 0, outlen);
++                      os_memset(T, 0, SHA256_MAC_LEN);
 +                      return -1;
 +              }
 +      }
 +
++      os_memset(T, 0, SHA256_MAC_LEN);
 +      return 0;
 +}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..653920ba283d2fe3461425f66dbf50aa7a30cabb
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,100 @@@
++/*
++ * SHA384-based KDF (IEEE 802.11ac)
++ * 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 "includes.h"
++
++#include "common.h"
++#include "sha384.h"
++#include "crypto.h"
++
++
++/**
++ * sha384_prf - SHA384-based Key derivation function (IEEE 802.11ac, 11.6.1.7.2)
++ * @key: Key for KDF
++ * @key_len: Length of the key in bytes
++ * @label: A unique label for each purpose of the PRF
++ * @data: Extra data to bind into the key
++ * @data_len: Length of the data
++ * @buf: Buffer for the generated pseudo-random key
++ * @buf_len: Number of bytes of key to generate
++ *
++ * This function is used to derive new, cryptographically separate keys from a
++ * given key.
++ */
++void sha384_prf(const u8 *key, size_t key_len, const char *label,
++              const u8 *data, size_t data_len, u8 *buf, size_t buf_len)
++{
++      sha384_prf_bits(key, key_len, label, data, data_len, buf, buf_len * 8);
++}
++
++
++/**
++ * sha384_prf_bits - IEEE Std 802.11ac-2013, 11.6.1.7.2 Key derivation function
++ * @key: Key for KDF
++ * @key_len: Length of the key in bytes
++ * @label: A unique label for each purpose of the PRF
++ * @data: Extra data to bind into the key
++ * @data_len: Length of the data
++ * @buf: Buffer for the generated pseudo-random key
++ * @buf_len: Number of bits of key to generate
++ *
++ * This function is used to derive new, cryptographically separate keys from a
++ * given key. If the requested buf_len is not divisible by eight, the least
++ * significant 1-7 bits of the last octet in the output are not part of the
++ * requested output.
++ */
++void sha384_prf_bits(const u8 *key, size_t key_len, const char *label,
++                   const u8 *data, size_t data_len, u8 *buf,
++                   size_t buf_len_bits)
++{
++      u16 counter = 1;
++      size_t pos, plen;
++      u8 hash[SHA384_MAC_LEN];
++      const u8 *addr[4];
++      size_t len[4];
++      u8 counter_le[2], length_le[2];
++      size_t buf_len = (buf_len_bits + 7) / 8;
++
++      addr[0] = counter_le;
++      len[0] = 2;
++      addr[1] = (u8 *) label;
++      len[1] = os_strlen(label);
++      addr[2] = data;
++      len[2] = data_len;
++      addr[3] = length_le;
++      len[3] = sizeof(length_le);
++
++      WPA_PUT_LE16(length_le, buf_len_bits);
++      pos = 0;
++      while (pos < buf_len) {
++              plen = buf_len - pos;
++              WPA_PUT_LE16(counter_le, counter);
++              if (plen >= SHA384_MAC_LEN) {
++                      hmac_sha384_vector(key, key_len, 4, addr, len,
++                                         &buf[pos]);
++                      pos += SHA384_MAC_LEN;
++              } else {
++                      hmac_sha384_vector(key, key_len, 4, addr, len, hash);
++                      os_memcpy(&buf[pos], hash, plen);
++                      pos += plen;
++                      break;
++              }
++              counter++;
++      }
++
++      /*
++       * Mask out unused bits in the last octet if it does not use all the
++       * bits.
++       */
++      if (buf_len_bits % 8) {
++              u8 mask = 0xff << (8 - buf_len_bits % 8);
++              buf[pos - 1] &= mask;
++      }
++
++      os_memset(hash, 0, sizeof(hash));
++}
index e6a1fe41e1a102c465dbdebcbd50994e27e40058,0000000000000000000000000000000000000000..3deafa59ec290466958bc62a89a89d2a46299eba
mode 100644,000000..100644
--- /dev/null
@@@ -1,19 -1,0 +1,24 @@@
 +/*
 + * 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);
++void sha384_prf(const u8 *key, size_t key_len, const char *label,
++              const u8 *data, size_t data_len, u8 *buf, size_t buf_len);
++void sha384_prf_bits(const u8 *key, size_t key_len, const char *label,
++                   const u8 *data, size_t data_len, u8 *buf,
++                   size_t buf_len_bits);
 +
 +#endif /* SHA384_H */
index 9ae95a66c9ed69f098b126ca538c06eb71f58a40,0000000000000000000000000000000000000000..2e562339cc5c04cfc8e62b2a5fe426e93ab8e3e6
mode 100644,000000..100644
--- /dev/null
@@@ -1,572 -1,0 +1,588 @@@
- struct tls_keys {
-       const u8 *master_key; /* TLS master secret */
-       size_t master_key_len;
 +/*
 + * SSL/TLS interface definition
 + * 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.
 + */
 +
 +#ifndef TLS_H
 +#define TLS_H
 +
 +struct tls_connection;
 +
-  * TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED (-2) on possible PIN error causing
-  * PKCS#11 engine failure, or
++struct tls_random {
 +      const u8 *client_random;
 +      size_t client_random_len;
 +      const u8 *server_random;
 +      size_t server_random_len;
 +};
 +
 +enum tls_event {
 +      TLS_CERT_CHAIN_SUCCESS,
 +      TLS_CERT_CHAIN_FAILURE,
 +      TLS_PEER_CERTIFICATE,
 +      TLS_ALERT
 +};
 +
 +/*
 + * Note: These are used as identifier with external programs and as such, the
 + * values must not be changed.
 + */
 +enum tls_fail_reason {
 +      TLS_FAIL_UNSPECIFIED = 0,
 +      TLS_FAIL_UNTRUSTED = 1,
 +      TLS_FAIL_REVOKED = 2,
 +      TLS_FAIL_NOT_YET_VALID = 3,
 +      TLS_FAIL_EXPIRED = 4,
 +      TLS_FAIL_SUBJECT_MISMATCH = 5,
 +      TLS_FAIL_ALTSUBJECT_MISMATCH = 6,
 +      TLS_FAIL_BAD_CERTIFICATE = 7,
 +      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;
 +              const char *subject;
 +              enum tls_fail_reason reason;
 +              const char *reason_txt;
 +              const struct wpabuf *cert;
 +      } cert_fail;
 +
 +      struct {
 +              int depth;
 +              const char *subject;
 +              const struct wpabuf *cert;
 +              const u8 *hash;
 +              size_t hash_len;
 +              const char *altsubject[TLS_MAX_ALT_SUBJECT];
 +              int num_altsubject;
 +      } peer_cert;
 +
 +      struct {
 +              int is_local;
 +              const char *type;
 +              const char *description;
 +      } alert;
 +};
 +
 +struct tls_config {
 +      const char *opensc_engine_path;
 +      const char *pkcs11_engine_path;
 +      const char *pkcs11_module_path;
 +      int fips_mode;
 +      int cert_in_cb;
 +      const char *openssl_ciphers;
++      unsigned int tls_session_lifetime;
 +
 +      void (*event_cb)(void *ctx, enum tls_event ev,
 +                       union tls_event_data *data);
 +      void *cb_ctx;
 +};
 +
 +#define TLS_CONN_ALLOW_SIGN_RSA_MD5 BIT(0)
 +#define TLS_CONN_DISABLE_TIME_CHECKS BIT(1)
 +#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)
++#define TLS_CONN_DISABLE_TLSv1_0 BIT(8)
 +
 +/**
 + * struct tls_connection_params - Parameters for TLS connection
 + * @ca_cert: File or reference name for CA X.509 certificate in PEM or DER
 + * format
 + * @ca_cert_blob: ca_cert as inlined data or %NULL if not used
 + * @ca_cert_blob_len: ca_cert_blob length
 + * @ca_path: Path to CA certificates (OpenSSL specific)
 + * @subject_match: String to match in the subject of the peer certificate or
 + * %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
 + * @client_cert_blob_len: client_cert_blob length
 + * @private_key: File or reference name for client private key in PEM or DER
 + * format (traditional format (RSA PRIVATE KEY) or PKCS#8 (PRIVATE KEY)
 + * @private_key_blob: private_key as inlined data or %NULL if not used
 + * @private_key_blob_len: private_key_blob length
 + * @private_key_passwd: Passphrase for decrypted private key, %NULL if no
 + * passphrase is used.
 + * @dh_file: File name for DH/DSA data in PEM format, or %NULL if not used
 + * @dh_blob: dh_file as inlined data or %NULL if not used
 + * @dh_blob_len: dh_blob length
 + * @engine: 1 = use engine (e.g., a smartcard) for private key operations
 + * (this is OpenSSL specific for now)
 + * @engine_id: engine id string (this is OpenSSL specific for now)
 + * @ppin: pointer to the pin variable in the configuration
 + * (this is OpenSSL specific for now)
 + * @key_id: the private key's id when using engine (this is OpenSSL
 + * 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
 + *
 + * TLS connection parameters to be configured with tls_connection_set_params()
 + * and tls_global_set_params().
 + *
 + * Certificates and private key can be configured either as a reference name
 + * (file path or reference to certificate store) or by providing the same data
 + * as a pointer to the data in memory. Only one option will be used for each
 + * field.
 + */
 +struct tls_connection_params {
 +      const char *ca_cert;
 +      const u8 *ca_cert_blob;
 +      size_t ca_cert_blob_len;
 +      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;
 +      const char *private_key;
 +      const u8 *private_key_blob;
 +      size_t private_key_blob_len;
 +      const char *private_key_passwd;
 +      const char *dh_file;
 +      const u8 *dh_blob;
 +      size_t dh_blob_len;
 +
 +      /* OpenSSL specific variables */
 +      int engine;
 +      const char *engine_id;
 +      const char *pin;
 +      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;
 +};
 +
 +
 +/**
 + * tls_init - Initialize TLS library
 + * @conf: Configuration data for TLS library
 + * Returns: Context data to be used as tls_ctx in calls to other functions,
 + * or %NULL on failure.
 + *
 + * Called once during program startup and once for each RSN pre-authentication
 + * session. In other words, there can be two concurrent TLS contexts. If global
 + * library initialization is needed (i.e., one that is shared between both
 + * authentication types), the TLS library wrapper should maintain a reference
 + * counter and do global initialization only when moving from 0 to 1 reference.
 + */
 +void * tls_init(const struct tls_config *conf);
 +
 +/**
 + * tls_deinit - Deinitialize TLS library
 + * @tls_ctx: TLS context data from tls_init()
 + *
 + * Called once during program shutdown and once for each RSN pre-authentication
 + * session. If global library deinitialization is needed (i.e., one that is
 + * shared between both authentication types), the TLS library wrapper should
 + * maintain a reference counter and do global deinitialization only when moving
 + * from 1 to 0 references.
 + */
 +void tls_deinit(void *tls_ctx);
 +
 +/**
 + * tls_get_errors - Process pending errors
 + * @tls_ctx: TLS context data from tls_init()
 + * Returns: Number of found error, 0 if no errors detected.
 + *
 + * Process all pending TLS errors.
 + */
 +int tls_get_errors(void *tls_ctx);
 +
 +/**
 + * tls_connection_init - Initialize a new TLS connection
 + * @tls_ctx: TLS context data from tls_init()
 + * Returns: Connection context data, conn for other function calls
 + */
 +struct tls_connection * tls_connection_init(void *tls_ctx);
 +
 +/**
 + * tls_connection_deinit - Free TLS connection data
 + * @tls_ctx: TLS context data from tls_init()
 + * @conn: Connection context data from tls_connection_init()
 + *
 + * Release all resources allocated for TLS connection.
 + */
 +void tls_connection_deinit(void *tls_ctx, struct tls_connection *conn);
 +
 +/**
 + * tls_connection_established - Has the TLS connection been completed?
 + * @tls_ctx: TLS context data from tls_init()
 + * @conn: Connection context data from tls_connection_init()
 + * Returns: 1 if TLS connection has been completed, 0 if not.
 + */
 +int tls_connection_established(void *tls_ctx, struct tls_connection *conn);
 +
 +/**
 + * tls_connection_shutdown - Shutdown TLS connection
 + * @tls_ctx: TLS context data from tls_init()
 + * @conn: Connection context data from tls_connection_init()
 + * Returns: 0 on success, -1 on failure
 + *
 + * Shutdown current TLS connection without releasing all resources. New
 + * connection can be started by using the same conn without having to call
 + * tls_connection_init() or setting certificates etc. again. The new
 + * connection should try to use session resumption.
 + */
 +int tls_connection_shutdown(void *tls_ctx, struct tls_connection *conn);
 +
 +enum {
++      TLS_SET_PARAMS_ENGINE_PRV_BAD_PIN = -4,
 +      TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED = -3,
 +      TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED = -2
 +};
 +
 +/**
 + * tls_connection_set_params - Set TLS connection parameters
 + * @tls_ctx: TLS context data from tls_init()
 + * @conn: Connection context data from tls_connection_init()
 + * @params: Connection parameters
 + * Returns: 0 on success, -1 on failure,
-  * PKCS#11 engine private key.
++ * TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED (-2) on error causing PKCS#11 engine
++ * failure, or
 + * TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED (-3) on failure to verify the
-  * TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED (-2) on possible PIN error causing
-  * PKCS#11 engine failure, or
++ * PKCS#11 engine private key, or
++ * TLS_SET_PARAMS_ENGINE_PRV_BAD_PIN (-4) on PIN error causing PKCS#11 engine
++ * failure.
 + */
 +int __must_check
 +tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
 +                        const struct tls_connection_params *params);
 +
 +/**
 + * tls_global_set_params - Set TLS parameters for all TLS connection
 + * @tls_ctx: TLS context data from tls_init()
 + * @params: Global TLS parameters
 + * Returns: 0 on success, -1 on failure,
-  * PKCS#11 engine private key.
++ * TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED (-2) on error causing PKCS#11 engine
++ * failure, or
 + * TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED (-3) on failure to verify the
-                                          int verify_peer);
++ * PKCS#11 engine private key, or
++ * TLS_SET_PARAMS_ENGINE_PRV_BAD_PIN (-4) on PIN error causing PKCS#11 engine
++ * failure.
 + */
 +int __must_check tls_global_set_params(
 +      void *tls_ctx, const struct tls_connection_params *params);
 +
 +/**
 + * tls_global_set_verify - Set global certificate verification options
 + * @tls_ctx: TLS context data from tls_init()
 + * @check_crl: 0 = do not verify CRLs, 1 = verify CRL for the user certificate,
 + * 2 = verify CRL for all certificates
 + * Returns: 0 on success, -1 on failure
 + */
 +int __must_check tls_global_set_verify(void *tls_ctx, int check_crl);
 +
 +/**
 + * tls_connection_set_verify - Set certificate verification options
 + * @tls_ctx: TLS context data from tls_init()
 + * @conn: Connection context data from tls_connection_init()
 + * @verify_peer: 1 = verify peer certificate
++ * @flags: Connection flags (TLS_CONN_*)
++ * @session_ctx: Session caching context or %NULL to use default
++ * @session_ctx_len: Length of @session_ctx in bytes.
 + * Returns: 0 on success, -1 on failure
 + */
 +int __must_check tls_connection_set_verify(void *tls_ctx,
 +                                         struct tls_connection *conn,
-  * tls_connection_get_keys - Get master key and random data from TLS connection
++                                         int verify_peer,
++                                         unsigned int flags,
++                                         const u8 *session_ctx,
++                                         size_t session_ctx_len);
 +
 +/**
-  * @keys: Structure of key/random data (filled on success)
++ * tls_connection_get_random - Get random data from TLS connection
 + * @tls_ctx: TLS context data from tls_init()
 + * @conn: Connection context data from tls_connection_init()
- int __must_check tls_connection_get_keys(void *tls_ctx,
++ * @data: Structure of client/server random data (filled on success)
 + * Returns: 0 on success, -1 on failure
 + */
-                                        struct tls_keys *keys);
++int __must_check tls_connection_get_random(void *tls_ctx,
 +                                       struct tls_connection *conn,
-  * This function is optional to implement if tls_connection_get_keys() provides
-  * access to master secret and server/client random values. If these values are
-  * not exported from the TLS library, tls_connection_prf() is required so that
-  * further keying material can be derived from the master secret. If not
-  * implemented, the function will still need to be defined, but it can just
-  * return -1. Example implementation of this function is in tls_prf_sha1_md5()
-  * when it is called with seed set to client_random|server_random (or
-  * server_random|client_random).
++                                       struct tls_random *data);
 +
 +/**
 + * tls_connection_prf - Use TLS-PRF to derive keying material
 + * @tls_ctx: TLS context data from tls_init()
 + * @conn: Connection context data from tls_connection_init()
 + * @label: Label (e.g., description of the key) for PRF
 + * @server_random_first: seed is 0 = client_random|server_random,
 + * 1 = server_random|client_random
++ * @skip_keyblock: Skip TLS key block from the beginning of PRF output
 + * @out: Buffer for output data from TLS-PRF
 + * @out_len: Length of the output buffer
 + * Returns: 0 on success, -1 on failure
 + *
- /**
-  * tls_connection_get_keyblock_size - Get TLS key_block size
-  * @tls_ctx: TLS context data from tls_init()
-  * @conn: Connection context data from tls_connection_init()
-  * Returns: Size of the key_block for the negotiated cipher suite or -1 on
-  * failure
-  */
- int tls_connection_get_keyblock_size(void *tls_ctx,
-                                    struct tls_connection *conn);
- /**
-  * tls_capabilities - Get supported TLS capabilities
-  * @tls_ctx: TLS context data from tls_init()
-  * Returns: Bit field of supported TLS capabilities (TLS_CAPABILITY_*)
-  */
- unsigned int tls_capabilities(void *tls_ctx);
++ * tls_connection_prf() is required so that further keying material can be
++ * derived from the master secret. Example implementation of this function is in
++ * tls_prf_sha1_md5() when it is called with seed set to
++ * client_random|server_random (or server_random|client_random). For TLSv1.2 and
++ * newer, a different PRF is needed, though.
 + */
 +int __must_check  tls_connection_prf(void *tls_ctx,
 +                                   struct tls_connection *conn,
 +                                   const char *label,
 +                                   int server_random_first,
++                                   int skip_keyblock,
 +                                   u8 *out, size_t out_len);
 +
 +/**
 + * tls_connection_handshake - Process TLS handshake (client side)
 + * @tls_ctx: TLS context data from tls_init()
 + * @conn: Connection context data from tls_connection_init()
 + * @in_data: Input data from TLS server
 + * @appl_data: Pointer to application data pointer, or %NULL if dropped
 + * Returns: Output data, %NULL on failure
 + *
 + * The caller is responsible for freeing the returned output data. If the final
 + * handshake message includes application data, this is decrypted and
 + * appl_data (if not %NULL) is set to point this data. The caller is
 + * responsible for freeing appl_data.
 + *
 + * This function is used during TLS handshake. The first call is done with
 + * in_data == %NULL and the library is expected to return ClientHello packet.
 + * This packet is then send to the server and a response from server is given
 + * to TLS library by calling this function again with in_data pointing to the
 + * TLS message from the server.
 + *
 + * If the TLS handshake fails, this function may return %NULL. However, if the
 + * TLS library has a TLS alert to send out, that should be returned as the
 + * output data. In this case, tls_connection_get_failed() must return failure
 + * (> 0).
 + *
 + * tls_connection_established() should return 1 once the TLS handshake has been
 + * completed successfully.
 + */
 +struct wpabuf * tls_connection_handshake(void *tls_ctx,
 +                                       struct tls_connection *conn,
 +                                       const struct wpabuf *in_data,
 +                                       struct wpabuf **appl_data);
 +
 +struct wpabuf * tls_connection_handshake2(void *tls_ctx,
 +                                        struct tls_connection *conn,
 +                                        const struct wpabuf *in_data,
 +                                        struct wpabuf **appl_data,
 +                                        int *more_data_needed);
 +
 +/**
 + * tls_connection_server_handshake - Process TLS handshake (server side)
 + * @tls_ctx: TLS context data from tls_init()
 + * @conn: Connection context data from tls_connection_init()
 + * @in_data: Input data from TLS peer
 + * @appl_data: Pointer to application data pointer, or %NULL if dropped
 + * Returns: Output data, %NULL on failure
 + *
 + * The caller is responsible for freeing the returned output data.
 + */
 +struct wpabuf * tls_connection_server_handshake(void *tls_ctx,
 +                                              struct tls_connection *conn,
 +                                              const struct wpabuf *in_data,
 +                                              struct wpabuf **appl_data);
 +
 +/**
 + * tls_connection_encrypt - Encrypt data into TLS tunnel
 + * @tls_ctx: TLS context data from tls_init()
 + * @conn: Connection context data from tls_connection_init()
 + * @in_data: Plaintext data to be encrypted
 + * Returns: Encrypted TLS data or %NULL on failure
 + *
 + * This function is used after TLS handshake has been completed successfully to
 + * send data in the encrypted tunnel. The caller is responsible for freeing the
 + * returned output data.
 + */
 +struct wpabuf * tls_connection_encrypt(void *tls_ctx,
 +                                     struct tls_connection *conn,
 +                                     const struct wpabuf *in_data);
 +
 +/**
 + * tls_connection_decrypt - Decrypt data from TLS tunnel
 + * @tls_ctx: TLS context data from tls_init()
 + * @conn: Connection context data from tls_connection_init()
 + * @in_data: Encrypted TLS data
 + * Returns: Decrypted TLS data or %NULL on failure
 + *
 + * This function is used after TLS handshake has been completed successfully to
 + * receive data from the encrypted tunnel. The caller is responsible for
 + * freeing the returned output data.
 + */
 +struct wpabuf * tls_connection_decrypt(void *tls_ctx,
 +                                     struct tls_connection *conn,
 +                                     const struct wpabuf *in_data);
 +
 +struct wpabuf * tls_connection_decrypt2(void *tls_ctx,
 +                                      struct tls_connection *conn,
 +                                      const struct wpabuf *in_data,
 +                                      int *more_data_needed);
 +
 +/**
 + * tls_connection_resumed - Was session resumption used
 + * @tls_ctx: TLS context data from tls_init()
 + * @conn: Connection context data from tls_connection_init()
 + * Returns: 1 if current session used session resumption, 0 if not
 + */
 +int tls_connection_resumed(void *tls_ctx, struct tls_connection *conn);
 +
 +enum {
 +      TLS_CIPHER_NONE,
 +      TLS_CIPHER_RC4_SHA /* 0x0005 */,
 +      TLS_CIPHER_AES128_SHA /* 0x002f */,
 +      TLS_CIPHER_RSA_DHE_AES128_SHA /* 0x0031 */,
 +      TLS_CIPHER_ANON_DH_AES128_SHA /* 0x0034 */
 +};
 +
 +/**
 + * tls_connection_set_cipher_list - Configure acceptable cipher suites
 + * @tls_ctx: TLS context data from tls_init()
 + * @conn: Connection context data from tls_connection_init()
 + * @ciphers: Zero (TLS_CIPHER_NONE) terminated list of allowed ciphers
 + * (TLS_CIPHER_*).
 + * Returns: 0 on success, -1 on failure
 + */
 +int __must_check tls_connection_set_cipher_list(void *tls_ctx,
 +                                              struct tls_connection *conn,
 +                                              u8 *ciphers);
 +
++/**
++ * tls_get_version - Get the current TLS version number
++ * @tls_ctx: TLS context data from tls_init()
++ * @conn: Connection context data from tls_connection_init()
++ * @buf: Buffer for returning the TLS version number
++ * @buflen: buf size
++ * Returns: 0 on success, -1 on failure
++ *
++ * Get the currently used TLS version number.
++ */
++int __must_check tls_get_version(void *tls_ctx, struct tls_connection *conn,
++                               char *buf, size_t buflen);
++
 +/**
 + * tls_get_cipher - Get current cipher name
 + * @tls_ctx: TLS context data from tls_init()
 + * @conn: Connection context data from tls_connection_init()
 + * @buf: Buffer for the cipher name
 + * @buflen: buf size
 + * Returns: 0 on success, -1 on failure
 + *
 + * Get the name of the currently used cipher.
 + */
 +int __must_check tls_get_cipher(void *tls_ctx, struct tls_connection *conn,
 +                              char *buf, size_t buflen);
 +
 +/**
 + * tls_connection_enable_workaround - Enable TLS workaround options
 + * @tls_ctx: TLS context data from tls_init()
 + * @conn: Connection context data from tls_connection_init()
 + * Returns: 0 on success, -1 on failure
 + *
 + * This function is used to enable connection-specific workaround options for
 + * buffer SSL/TLS implementations.
 + */
 +int __must_check tls_connection_enable_workaround(void *tls_ctx,
 +                                                struct tls_connection *conn);
 +
 +/**
 + * tls_connection_client_hello_ext - Set TLS extension for ClientHello
 + * @tls_ctx: TLS context data from tls_init()
 + * @conn: Connection context data from tls_connection_init()
 + * @ext_type: Extension type
 + * @data: Extension payload (%NULL to remove extension)
 + * @data_len: Extension payload length
 + * Returns: 0 on success, -1 on failure
 + */
 +int __must_check tls_connection_client_hello_ext(void *tls_ctx,
 +                                               struct tls_connection *conn,
 +                                               int ext_type, const u8 *data,
 +                                               size_t data_len);
 +
 +/**
 + * tls_connection_get_failed - Get connection failure status
 + * @tls_ctx: TLS context data from tls_init()
 + * @conn: Connection context data from tls_connection_init()
 + *
 + * Returns >0 if connection has failed, 0 if not.
 + */
 +int tls_connection_get_failed(void *tls_ctx, struct tls_connection *conn);
 +
 +/**
 + * tls_connection_get_read_alerts - Get connection read alert status
 + * @tls_ctx: TLS context data from tls_init()
 + * @conn: Connection context data from tls_connection_init()
 + * Returns: Number of times a fatal read (remote end reported error) has
 + * happened during this connection.
 + */
 +int tls_connection_get_read_alerts(void *tls_ctx, struct tls_connection *conn);
 +
 +/**
 + * tls_connection_get_write_alerts - Get connection write alert status
 + * @tls_ctx: TLS context data from tls_init()
 + * @conn: Connection context data from tls_connection_init()
 + * Returns: Number of times a fatal write (locally detected error) has happened
 + * during this connection.
 + */
 +int tls_connection_get_write_alerts(void *tls_ctx,
 +                                  struct tls_connection *conn);
 +
 +typedef int (*tls_session_ticket_cb)
 +(void *ctx, const u8 *ticket, size_t len, const u8 *client_random,
 + const u8 *server_random, u8 *master_secret);
 +
 +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);
 +
++void tls_connection_set_success_data(struct tls_connection *conn,
++                                   struct wpabuf *data);
++
++void tls_connection_set_success_data_resumed(struct tls_connection *conn);
++
++const struct wpabuf *
++tls_connection_get_success_data(struct tls_connection *conn);
++
++void tls_connection_remove_session(struct tls_connection *conn);
++
 +#endif /* TLS_H */
index 65db6fcc256539b8dd2f55c0bce5cf21464e2ab9,0000000000000000000000000000000000000000..f994379b16b22a07596626ee65626711b4624d02
mode 100644,000000..100644
--- /dev/null
@@@ -1,1505 -1,0 +1,1523 @@@
-                             int verify_peer)
 +/*
 + * SSL/TLS interface functions for GnuTLS
 + * Copyright (c) 2004-2011, Jouni Malinen <j@w1.fi>
 + *
 + * This software may be distributed under the terms of the BSD license.
 + * See README for more details.
 + */
 +
 +#include "includes.h"
 +#include <gnutls/gnutls.h>
 +#include <gnutls/x509.h>
 +#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"
 +
 +
 +static int tls_gnutls_ref_count = 0;
 +
 +struct tls_global {
 +      /* Data for session resumption */
 +      void *session_data;
 +      size_t session_data_size;
 +
 +      int server;
 +
 +      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 {
 +      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;
 +      const u8 *pull_buf_offset;
 +
 +      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;
 +      if (level == 6 || level == 7) {
 +              /* These levels seem to be mostly I/O debug and msg dumps */
 +              return;
 +      }
 +
 +      s = os_strdup(msg);
 +      if (s == NULL)
 +              return;
 +
 +      pos = s;
 +      while (*pos != '\0') {
 +              if (*pos == '\n') {
 +                      *pos = '\0';
 +                      break;
 +              }
 +              pos++;
 +      }
 +      wpa_printf(level > 3 ? MSG_MSGDUMP : MSG_DEBUG,
 +                 "gnutls<%d> %s", level, s);
 +      os_free(s);
 +}
 +
 +
 +void * tls_init(const struct tls_config *conf)
 +{
 +      struct tls_global *global;
 +
 +      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)
 +              return NULL;
 +
 +      if (tls_gnutls_ref_count == 0 && gnutls_global_init() < 0) {
 +              os_free(global);
 +              return NULL;
 +      }
 +      tls_gnutls_ref_count++;
 +
 +      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;
 +}
 +
 +
 +void tls_deinit(void *ssl_ctx)
 +{
 +      struct tls_global *global = ssl_ctx;
 +      if (global) {
 +              if (global->params_set)
 +                      gnutls_certificate_free_credentials(global->xcred);
 +              os_free(global->session_data);
 +              os_free(global);
 +      }
 +
 +      tls_gnutls_ref_count--;
 +      if (tls_gnutls_ref_count == 0)
 +              gnutls_global_deinit();
 +}
 +
 +
 +int tls_get_errors(void *ssl_ctx)
 +{
 +      return 0;
 +}
 +
 +
 +static ssize_t tls_pull_func(gnutls_transport_ptr_t ptr, void *buf,
 +                           size_t len)
 +{
 +      struct tls_connection *conn = (struct tls_connection *) ptr;
 +      const u8 *end;
 +      if (conn->pull_buf == NULL) {
 +              errno = EWOULDBLOCK;
 +              return -1;
 +      }
 +
 +      end = wpabuf_head_u8(conn->pull_buf) + wpabuf_len(conn->pull_buf);
 +      if ((size_t) (end - conn->pull_buf_offset) < len)
 +              len = end - conn->pull_buf_offset;
 +      os_memcpy(buf, conn->pull_buf_offset, len);
 +      conn->pull_buf_offset += len;
 +      if (conn->pull_buf_offset == end) {
 +              wpa_printf(MSG_DEBUG, "%s - pull_buf consumed", __func__);
 +              wpabuf_free(conn->pull_buf);
 +              conn->pull_buf = NULL;
 +              conn->pull_buf_offset = NULL;
 +      } else {
 +              wpa_printf(MSG_DEBUG, "%s - %lu bytes remaining in pull_buf",
 +                         __func__,
 +                         (unsigned long) (end - conn->pull_buf_offset));
 +      }
 +      return len;
 +}
 +
 +
 +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;
 +
 +      if (wpabuf_resize(&conn->push_buf, len) < 0) {
 +              errno = ENOMEM;
 +              return -1;
 +      }
 +      wpabuf_put_data(conn->push_buf, buf, len);
 +
 +      return len;
 +}
 +
 +
 +static int tls_gnutls_init_session(struct tls_global *global,
 +                                 struct tls_connection *conn)
 +{
 +      const char *err;
 +      int ret;
 +
 +      ret = gnutls_init(&conn->session,
 +                        global->server ? GNUTLS_SERVER : GNUTLS_CLIENT);
 +      if (ret < 0) {
 +              wpa_printf(MSG_INFO, "TLS: Failed to initialize new TLS "
 +                         "connection: %s", gnutls_strerror(ret));
 +              return -1;
 +      }
 +
 +      ret = gnutls_set_default_priority(conn->session);
 +      if (ret < 0)
 +              goto fail;
 +
 +      ret = gnutls_priority_set_direct(conn->session, "NORMAL:-VERS-SSL3.0",
 +                                       &err);
 +      if (ret < 0) {
 +              wpa_printf(MSG_ERROR, "GnuTLS: Priority string failure at "
 +                         "'%s'", err);
 +              goto fail;
 +      }
 +
 +      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_t) conn);
 +      gnutls_session_set_ptr(conn->session, conn);
 +
 +      return 0;
 +
 +fail:
 +      wpa_printf(MSG_INFO, "TLS: Failed to setup new TLS connection: %s",
 +                 gnutls_strerror(ret));
 +      gnutls_deinit(conn->session);
 +      return -1;
 +}
 +
 +
 +struct tls_connection * tls_connection_init(void *ssl_ctx)
 +{
 +      struct tls_global *global = ssl_ctx;
 +      struct tls_connection *conn;
 +      int ret;
 +
 +      conn = os_zalloc(sizeof(*conn));
 +      if (conn == NULL)
 +              return NULL;
 +      conn->global = global;
 +
 +      if (tls_gnutls_init_session(global, conn)) {
 +              os_free(conn);
 +              return NULL;
 +      }
 +
 +      if (global->params_set) {
 +              ret = gnutls_credentials_set(conn->session,
 +                                           GNUTLS_CRD_CERTIFICATE,
 +                                           global->xcred);
 +              if (ret < 0) {
 +                      wpa_printf(MSG_INFO, "Failed to configure "
 +                                 "credentials: %s", gnutls_strerror(ret));
 +                      os_free(conn);
 +                      return NULL;
 +              }
 +      }
 +
 +      if (gnutls_certificate_allocate_credentials(&conn->xcred)) {
 +              os_free(conn);
 +              return NULL;
 +      }
 +
 +      return conn;
 +}
 +
 +
 +void tls_connection_deinit(void *ssl_ctx, struct tls_connection *conn)
 +{
 +      if (conn == NULL)
 +              return;
 +
 +      gnutls_certificate_free_credentials(conn->xcred);
 +      gnutls_deinit(conn->session);
 +      os_free(conn->pre_shared_secret);
 +      wpabuf_free(conn->push_buf);
 +      wpabuf_free(conn->pull_buf);
 +      os_free(conn->suffix_match);
 +      os_free(conn->domain_match);
 +      os_free(conn);
 +}
 +
 +
 +int tls_connection_established(void *ssl_ctx, struct tls_connection *conn)
 +{
 +      return conn ? conn->established : 0;
 +}
 +
 +
 +int tls_connection_shutdown(void *ssl_ctx, struct tls_connection *conn)
 +{
 +      struct tls_global *global = ssl_ctx;
 +      int ret;
 +
 +      if (conn == NULL)
 +              return -1;
 +
 +      /* Shutdown previous TLS connection without notifying the peer
 +       * because the connection was already terminated in practice
 +       * and "close notify" shutdown alert would confuse AS. */
 +      gnutls_bye(conn->session, GNUTLS_SHUT_RDWR);
 +      wpabuf_free(conn->push_buf);
 +      conn->push_buf = NULL;
 +      conn->established = 0;
 +
 +      gnutls_deinit(conn->session);
 +      if (tls_gnutls_init_session(global, conn)) {
 +              wpa_printf(MSG_INFO, "GnuTLS: Failed to preparare new session "
 +                         "for session resumption use");
 +              return -1;
 +      }
 +
 +      ret = gnutls_credentials_set(conn->session, GNUTLS_CRD_CERTIFICATE,
 +                                   conn->params_set ? conn->xcred :
 +                                   global->xcred);
 +      if (ret < 0) {
 +              wpa_printf(MSG_INFO, "GnuTLS: Failed to configure credentials "
 +                         "for session resumption: %s", gnutls_strerror(ret));
 +              return -1;
 +      }
 +
 +      if (global->session_data) {
 +              ret = gnutls_session_set_data(conn->session,
 +                                            global->session_data,
 +                                            global->session_data_size);
 +              if (ret < 0) {
 +                      wpa_printf(MSG_INFO, "GnuTLS: Failed to set session "
 +                                 "data: %s", gnutls_strerror(ret));
 +                      return -1;
 +              }
 +      }
 +
 +      return 0;
 +}
 +
 +
 +int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
 +                            const struct tls_connection_params *params)
 +{
 +      int ret;
 +
 +      if (conn == NULL || params == NULL)
 +              return -1;
 +
 +      if (params->subject_match) {
 +              wpa_printf(MSG_INFO, "GnuTLS: subject_match not supported");
 +              return -1;
 +      }
 +
 +      if (params->altsubject_match) {
 +              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) {
 +              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_DER);
 +              if (ret < 0) {
 +                      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_PEM);
 +                      if (ret < 0) {
 +                              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 (params->flags & TLS_CONN_DISABLE_TIME_CHECKS) {
 +                      conn->disable_time_checks = 1;
 +                      gnutls_certificate_set_verify_flags(
 +                              conn->xcred,
 +                              GNUTLS_VERIFY_DISABLE_TIME_CHECKS);
 +              }
 +      }
 +
 +      if (params->client_cert && params->private_key) {
 +#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_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_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_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) {
 +              int pkcs12_ok = 0;
 +#ifdef PKCS12_FUNCS
 +              /* Try to load in PKCS#12 format */
 +              ret = gnutls_certificate_set_x509_simple_pkcs12_file(
 +                      conn->xcred, params->private_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_ok = 1;
 +#endif /* PKCS12_FUNCS */
 +
 +              if (!pkcs12_ok) {
 +                      wpa_printf(MSG_DEBUG, "GnuTLS: PKCS#12 support not "
 +                                 "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,
 +                                   conn->xcred);
 +      if (ret < 0) {
 +              wpa_printf(MSG_INFO, "Failed to configure credentials: %s",
 +                         gnutls_strerror(ret));
 +      }
 +
 +      return ret;
 +}
 +
 +
 +int tls_global_set_params(void *tls_ctx,
 +                        const struct tls_connection_params *params)
 +{
 +      struct tls_global *global = tls_ctx;
 +      int ret;
 +
 +      /* Currently, global parameters are only set when running in server
 +       * mode. */
 +      global->server = 1;
 +
 +      if (global->params_set) {
 +              gnutls_certificate_free_credentials(global->xcred);
 +              global->params_set = 0;
 +      }
 +
 +      ret = gnutls_certificate_allocate_credentials(&global->xcred);
 +      if (ret) {
 +              wpa_printf(MSG_DEBUG, "Failed to allocate global credentials "
 +                         "%s", gnutls_strerror(ret));
 +              return -1;
 +      }
 +
 +      if (params->ca_cert) {
 +              ret = gnutls_certificate_set_x509_trust_file(
 +                      global->xcred, params->ca_cert, GNUTLS_X509_FMT_DER);
 +              if (ret < 0) {
 +                      wpa_printf(MSG_DEBUG, "Failed to read CA cert '%s' "
 +                                 "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_PEM);
 +                      if (ret < 0) {
 +                              wpa_printf(MSG_DEBUG, "Failed to read CA cert "
 +                                         "'%s' in PEM format: %s",
 +                                         params->ca_cert,
 +                                         gnutls_strerror(ret));
 +                              goto fail;
 +                      }
 +              }
 +
 +              if (params->flags & TLS_CONN_ALLOW_SIGN_RSA_MD5) {
 +                      gnutls_certificate_set_verify_flags(
 +                              global->xcred,
 +                              GNUTLS_VERIFY_ALLOW_SIGN_RSA_MD5);
 +              }
 +
 +              if (params->flags & TLS_CONN_DISABLE_TIME_CHECKS) {
 +                      gnutls_certificate_set_verify_flags(
 +                              global->xcred,
 +                              GNUTLS_VERIFY_DISABLE_TIME_CHECKS);
 +              }
 +      }
 +
 +      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_DER);
 +              if (ret < 0) {
 +                      wpa_printf(MSG_DEBUG, "Failed to read client cert/key "
 +                                 "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_PEM);
 +                      if (ret < 0) {
 +                              wpa_printf(MSG_DEBUG, "Failed to read client "
 +                                         "cert/key in PEM format: %s",
 +                                         gnutls_strerror(ret));
 +                              goto fail;
 +                      }
 +              }
 +      } else if (params->private_key) {
 +              int pkcs12_ok = 0;
 +#ifdef PKCS12_FUNCS
 +              /* Try to load in PKCS#12 format */
 +              ret = gnutls_certificate_set_x509_simple_pkcs12_file(
 +                      global->xcred, params->private_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));
 +                      goto fail;
 +              } else
 +                      pkcs12_ok = 1;
 +#endif /* PKCS12_FUNCS */
 +
 +              if (!pkcs12_ok) {
 +                      wpa_printf(MSG_DEBUG, "GnuTLS: PKCS#12 support not "
 +                                 "included");
 +                      goto fail;
 +              }
 +      }
 +
 +      global->params_set = 1;
 +
 +      return 0;
 +
 +fail:
 +      gnutls_certificate_free_credentials(global->xcred);
 +      return -1;
 +}
 +
 +
 +int tls_global_set_verify(void *ssl_ctx, int check_crl)
 +{
 +      /* TODO */
 +      return 0;
 +}
 +
 +
 +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)
++                            int verify_peer, unsigned int flags,
++                            const u8 *session_ctx, size_t session_ctx_len)
 +{
 +      if (conn == NULL || conn->session == NULL)
 +              return -1;
 +
 +      conn->verify_peer = verify_peer;
 +      gnutls_certificate_server_set_request(conn->session,
 +                                            verify_peer ? GNUTLS_CERT_REQUIRE
 +                                            : GNUTLS_CERT_REQUEST);
 +
 +      return 0;
 +}
 +
 +
-                      u8 *out, size_t out_len)
++int tls_connection_get_random(void *ssl_ctx, struct tls_connection *conn,
++                          struct tls_random *keys)
 +{
 +#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));
 +      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 */
 +}
 +
 +
 +int tls_connection_prf(void *tls_ctx, struct tls_connection *conn,
 +                     const char *label, int server_random_first,
-       if (conn == NULL || conn->session == NULL)
++                     int skip_keyblock, u8 *out, size_t out_len)
 +{
- int tls_connection_get_keyblock_size(void *tls_ctx,
-                                    struct tls_connection *conn)
++      if (conn == NULL || conn->session == NULL || skip_keyblock)
 +              return -1;
 +
 +      return gnutls_prf(conn->session, os_strlen(label), label,
 +                        server_random_first, 0, NULL, out_len, (char *) out);
 +}
 +
 +
 +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;
 +#else /* GnuTLS 3.1.3 or newer */
 +      return 0;
 +#endif /* GnuTLS 3.1.3 or newer */
 +}
 +
 +
 +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;
 +
 +              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;
 +              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");
 +              if (status & GNUTLS_CERT_INSECURE_ALGORITHM) {
 +                      wpa_printf(MSG_INFO, "TLS: Certificate uses insecure "
 +                                 "algorithm");
 +                      gnutls_tls_fail_event(conn, NULL, 0, NULL,
 +                                            "certificate uses insecure algorithm",
 +                                            TLS_FAIL_BAD_CERTIFICATE);
 +                      err = GNUTLS_A_INSUFFICIENT_SECURITY;
 +                      goto out;
 +              }
 +              if (status & GNUTLS_CERT_NOT_ACTIVATED) {
 +                      wpa_printf(MSG_INFO, "TLS: Certificate not yet "
 +                                 "activated");
 +                      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");
 +                      gnutls_tls_fail_event(conn, NULL, 0, NULL,
 +                                            "certificate has expired",
 +                                            TLS_FAIL_EXPIRED);
 +                      err = GNUTLS_A_CERTIFICATE_EXPIRED;
 +                      goto out;
 +              }
 +              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");
 +              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");
 +              gnutls_tls_fail_event(conn, NULL, 0, NULL,
 +                                    "certificate revoked",
 +                                    TLS_FAIL_REVOKED);
 +              err = GNUTLS_A_CERTIFICATE_REVOKED;
 +              goto out;
 +      }
 +
 +      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;
 +                      goto out;
 +              }
 +
 +              if (gnutls_x509_crt_import(cert, &certs[i],
 +                                         GNUTLS_X509_FMT_DER) < 0) {
 +                      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;
 +                      goto out;
 +              }
 +
 +              gnutls_x509_crt_get_dn(cert, NULL, &len);
 +              len++;
 +              buf = os_malloc(len + 1);
 +              if (buf) {
 +                      buf[0] = buf[len] = '\0';
 +                      gnutls_x509_crt_get_dn(cert, buf, &len);
 +              }
 +              wpa_printf(MSG_DEBUG, "TLS: Peer cert chain %d/%d: %s",
 +                         i + 1, num_certs, buf);
 +
 +              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);
 +              }
 +
 +              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_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);
 +                      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;
 +}
 +
 +
 +static struct wpabuf * gnutls_get_appl_data(struct tls_connection *conn)
 +{
 +      int res;
 +      struct wpabuf *ad;
 +      wpa_printf(MSG_DEBUG, "GnuTLS: Check for possible Application Data");
 +      ad = wpabuf_alloc((wpabuf_len(conn->pull_buf) + 500) * 3);
 +      if (ad == NULL)
 +              return NULL;
 +
 +      res = gnutls_record_recv(conn->session, wpabuf_mhead(ad),
 +                               wpabuf_size(ad));
 +      wpa_printf(MSG_DEBUG, "GnuTLS: gnutls_record_recv: %d", res);
 +      if (res < 0) {
 +              wpa_printf(MSG_DEBUG, "%s - gnutls_record_recv failed: %d "
 +                         "(%s)", __func__, (int) res,
 +                         gnutls_strerror(res));
 +              wpabuf_free(ad);
 +              return NULL;
 +      }
 +
 +      wpabuf_put(ad, res);
 +      wpa_printf(MSG_DEBUG, "GnuTLS: Received %d bytes of Application Data",
 +                 res);
 +      return ad;
 +}
 +
 +
 +struct wpabuf * tls_connection_handshake(void *tls_ctx,
 +                                       struct tls_connection *conn,
 +                                       const struct wpabuf *in_data,
 +                                       struct wpabuf **appl_data)
 +{
 +      struct tls_global *global = tls_ctx;
 +      struct wpabuf *out_data;
 +      int ret;
 +
 +      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) wpabuf_len(conn->pull_buf));
 +                      wpabuf_free(conn->pull_buf);
 +              }
 +              conn->pull_buf = wpabuf_dup(in_data);
 +              if (conn->pull_buf == NULL)
 +                      return NULL;
 +              conn->pull_buf_offset = wpabuf_head(conn->pull_buf);
 +      }
 +
 +      ret = gnutls_handshake(conn->session);
 +      if (ret < 0) {
 +              gnutls_alert_description_t alert;
 +
 +              switch (ret) {
 +              case GNUTLS_E_AGAIN:
 +                      if (global->server && conn->established &&
 +                          conn->push_buf == NULL) {
 +                              /* Need to return something to trigger
 +                               * completion of EAP-TLS. */
 +                              conn->push_buf = wpabuf_alloc(0);
 +                      }
 +                      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(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 "
 +                                 "-> %s", __func__, gnutls_strerror(ret));
 +                      conn->failed++;
 +              }
 +      } else {
 +              size_t size;
 +
 +              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 */
 +
 +              conn->established = 1;
 +              if (conn->push_buf == NULL) {
 +                      /* Need to return something to get final TLS ACK. */
 +                      conn->push_buf = wpabuf_alloc(0);
 +              }
 +
 +              gnutls_session_get_data(conn->session, NULL, &size);
 +              if (global->session_data == NULL ||
 +                  global->session_data_size < size) {
 +                      os_free(global->session_data);
 +                      global->session_data = os_malloc(size);
 +              }
 +              if (global->session_data) {
 +                      global->session_data_size = size;
 +                      gnutls_session_get_data(conn->session,
 +                                              global->session_data,
 +                                              &global->session_data_size);
 +              }
 +
 +              if (conn->pull_buf && appl_data)
 +                      *appl_data = gnutls_get_appl_data(conn);
 +      }
 +
 +      out_data = conn->push_buf;
 +      conn->push_buf = NULL;
 +      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 tls_connection_handshake(tls_ctx, conn, in_data, appl_data);
 +}
 +
 +
 +struct wpabuf * tls_connection_encrypt(void *tls_ctx,
 +                                     struct tls_connection *conn,
 +                                     const struct wpabuf *in_data)
 +{
 +      ssize_t res;
 +      struct wpabuf *buf;
 +
 +      res = gnutls_record_send(conn->session, wpabuf_head(in_data),
 +                               wpabuf_len(in_data));
 +      if (res < 0) {
 +              wpa_printf(MSG_INFO, "%s: Encryption failed: %s",
 +                         __func__, gnutls_strerror(res));
 +              return NULL;
 +      }
 +
 +      buf = conn->push_buf;
 +      conn->push_buf = NULL;
 +      return buf;
 +}
 +
 +
 +struct wpabuf * tls_connection_decrypt(void *tls_ctx,
 +                                     struct tls_connection *conn,
 +                                     const struct wpabuf *in_data)
 +{
 +      ssize_t res;
 +      struct wpabuf *out;
 +
 +      if (conn->pull_buf) {
 +              wpa_printf(MSG_DEBUG, "%s - %lu bytes remaining in "
 +                         "pull_buf", __func__,
 +                         (unsigned long) wpabuf_len(conn->pull_buf));
 +              wpabuf_free(conn->pull_buf);
 +      }
 +      conn->pull_buf = wpabuf_dup(in_data);
 +      if (conn->pull_buf == NULL)
 +              return NULL;
 +      conn->pull_buf_offset = wpabuf_head(conn->pull_buf);
 +
 +      /*
 +       * 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 = gnutls_record_recv(conn->session, wpabuf_mhead(out),
 +                               wpabuf_size(out));
 +      if (res < 0) {
 +              wpa_printf(MSG_DEBUG, "%s - gnutls_record_recv failed: %d "
 +                         "(%s)", __func__, (int) res, gnutls_strerror(res));
 +              wpabuf_free(out);
 +              return NULL;
 +      }
 +      wpabuf_put(out, res);
 +
 +      return out;
 +}
 +
 +
 +int tls_connection_resumed(void *ssl_ctx, struct tls_connection *conn)
 +{
 +      if (conn == NULL)
 +              return 0;
 +      return gnutls_session_is_resumed(conn->session);
 +}
 +
 +
 +int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn,
 +                                 u8 *ciphers)
 +{
 +      /* TODO */
 +      return -1;
 +}
 +
 +
++int tls_get_version(void *ssl_ctx, struct tls_connection *conn,
++                  char *buf, size_t buflen)
++{
++      /* TODO */
++      return -1;
++}
++
++
 +int tls_get_cipher(void *ssl_ctx, struct tls_connection *conn,
 +                 char *buf, size_t buflen)
 +{
 +      /* TODO */
 +      buf[0] = '\0';
 +      return 0;
 +}
 +
 +
 +int tls_connection_enable_workaround(void *ssl_ctx,
 +                                   struct tls_connection *conn)
 +{
 +      gnutls_record_disable_padding(conn->session);
 +      return 0;
 +}
 +
 +
 +int tls_connection_client_hello_ext(void *ssl_ctx, struct tls_connection *conn,
 +                                  int ext_type, const u8 *data,
 +                                  size_t data_len)
 +{
 +      /* TODO */
 +      return -1;
 +}
 +
 +
 +int tls_connection_get_failed(void *ssl_ctx, struct tls_connection *conn)
 +{
 +      if (conn == NULL)
 +              return -1;
 +      return conn->failed;
 +}
 +
 +
 +int tls_connection_get_read_alerts(void *ssl_ctx, struct tls_connection *conn)
 +{
 +      if (conn == NULL)
 +              return -1;
 +      return conn->read_alerts;
 +}
 +
 +
 +int tls_connection_get_write_alerts(void *ssl_ctx, struct tls_connection *conn)
 +{
 +      if (conn == NULL)
 +              return -1;
 +      return conn->write_alerts;
 +}
 +
 +
-       /* TODO */
++int tls_connection_set_session_ticket_cb(void *tls_ctx,
++                                       struct tls_connection *conn,
++                                       tls_session_ticket_cb cb, void *ctx)
 +{
- unsigned int tls_capabilities(void *tls_ctx)
 +      return -1;
 +}
 +
 +
-       return 0;
++int tls_get_library_version(char *buf, size_t buf_len)
 +{
- int tls_connection_set_session_ticket_cb(void *tls_ctx,
-                                        struct tls_connection *conn,
-                                        tls_session_ticket_cb cb, void *ctx)
++      return os_snprintf(buf, buf_len, "GnuTLS build=%s run=%s",
++                         GNUTLS_VERSION, gnutls_check_version(NULL));
 +}
 +
 +
-       return -1;
++void tls_connection_set_success_data(struct tls_connection *conn,
++                                   struct wpabuf *data)
 +{
- 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));
++void tls_connection_set_success_data_resumed(struct tls_connection *conn)
++{
++}
++
++
++const struct wpabuf *
++tls_connection_get_success_data(struct tls_connection *conn)
++{
++      return NULL;
++}
++
++
++void tls_connection_remove_session(struct tls_connection *conn)
 +{
 +}
index 0c955da29f1d51ff8839378a86e91ef8d38be33a,0000000000000000000000000000000000000000..704751d308fcf9f080de1a5f2aeab086573ff096
mode 100644,000000..100644
--- /dev/null
@@@ -1,685 -1,0 +1,733 @@@
-               wpa_printf(MSG_INFO, "GnuTLS: openssl_ciphers not supported");
 +/*
 + * TLS interface functions and an internal TLS implementation
 + * Copyright (c) 2004-2011, Jouni Malinen <j@w1.fi>
 + *
 + * This software may be distributed under the terms of the BSD license.
 + * See README for more details.
 + *
 + * This file interface functions for hostapd/wpa_supplicant to use the
 + * integrated TLSv1 implementation.
 + */
 +
 +#include "includes.h"
 +
 +#include "common.h"
 +#include "tls.h"
 +#include "tls/tlsv1_client.h"
 +#include "tls/tlsv1_server.h"
 +
 +
 +static int tls_ref_count = 0;
 +
 +struct tls_global {
 +      int server;
 +      struct tlsv1_credentials *server_cred;
 +      int check_crl;
 +};
 +
 +struct tls_connection {
 +      struct tlsv1_client *client;
 +      struct tlsv1_server *server;
 +      struct tls_global *global;
 +};
 +
 +
 +void * tls_init(const struct tls_config *conf)
 +{
 +      struct tls_global *global;
 +
 +      if (tls_ref_count == 0) {
 +#ifdef CONFIG_TLS_INTERNAL_CLIENT
 +              if (tlsv1_client_global_init())
 +                      return NULL;
 +#endif /* CONFIG_TLS_INTERNAL_CLIENT */
 +#ifdef CONFIG_TLS_INTERNAL_SERVER
 +              if (tlsv1_server_global_init())
 +                      return NULL;
 +#endif /* CONFIG_TLS_INTERNAL_SERVER */
 +      }
 +      tls_ref_count++;
 +
 +      global = os_zalloc(sizeof(*global));
 +      if (global == NULL)
 +              return NULL;
 +
 +      return global;
 +}
 +
 +void tls_deinit(void *ssl_ctx)
 +{
 +      struct tls_global *global = ssl_ctx;
 +      tls_ref_count--;
 +      if (tls_ref_count == 0) {
 +#ifdef CONFIG_TLS_INTERNAL_CLIENT
 +              tlsv1_client_global_deinit();
 +#endif /* CONFIG_TLS_INTERNAL_CLIENT */
 +#ifdef CONFIG_TLS_INTERNAL_SERVER
 +              tlsv1_cred_free(global->server_cred);
 +              tlsv1_server_global_deinit();
 +#endif /* CONFIG_TLS_INTERNAL_SERVER */
 +      }
 +      os_free(global);
 +}
 +
 +
 +int tls_get_errors(void *tls_ctx)
 +{
 +      return 0;
 +}
 +
 +
 +struct tls_connection * tls_connection_init(void *tls_ctx)
 +{
 +      struct tls_connection *conn;
 +      struct tls_global *global = tls_ctx;
 +
 +      conn = os_zalloc(sizeof(*conn));
 +      if (conn == NULL)
 +              return NULL;
 +      conn->global = global;
 +
 +#ifdef CONFIG_TLS_INTERNAL_CLIENT
 +      if (!global->server) {
 +              conn->client = tlsv1_client_init();
 +              if (conn->client == NULL) {
 +                      os_free(conn);
 +                      return NULL;
 +              }
 +      }
 +#endif /* CONFIG_TLS_INTERNAL_CLIENT */
 +#ifdef CONFIG_TLS_INTERNAL_SERVER
 +      if (global->server) {
 +              conn->server = tlsv1_server_init(global->server_cred);
 +              if (conn->server == NULL) {
 +                      os_free(conn);
 +                      return NULL;
 +              }
 +      }
 +#endif /* CONFIG_TLS_INTERNAL_SERVER */
 +
 +      return conn;
 +}
 +
 +
 +#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)
 +              return;
 +#ifdef CONFIG_TLS_INTERNAL_CLIENT
 +      if (conn->client)
 +              tlsv1_client_deinit(conn->client);
 +#endif /* CONFIG_TLS_INTERNAL_CLIENT */
 +#ifdef CONFIG_TLS_INTERNAL_SERVER
 +      if (conn->server)
 +              tlsv1_server_deinit(conn->server);
 +#endif /* CONFIG_TLS_INTERNAL_SERVER */
 +      os_free(conn);
 +}
 +
 +
 +int tls_connection_established(void *tls_ctx, struct tls_connection *conn)
 +{
 +#ifdef CONFIG_TLS_INTERNAL_CLIENT
 +      if (conn->client)
 +              return tlsv1_client_established(conn->client);
 +#endif /* CONFIG_TLS_INTERNAL_CLIENT */
 +#ifdef CONFIG_TLS_INTERNAL_SERVER
 +      if (conn->server)
 +              return tlsv1_server_established(conn->server);
 +#endif /* CONFIG_TLS_INTERNAL_SERVER */
 +      return 0;
 +}
 +
 +
 +int tls_connection_shutdown(void *tls_ctx, struct tls_connection *conn)
 +{
 +#ifdef CONFIG_TLS_INTERNAL_CLIENT
 +      if (conn->client)
 +              return tlsv1_client_shutdown(conn->client);
 +#endif /* CONFIG_TLS_INTERNAL_CLIENT */
 +#ifdef CONFIG_TLS_INTERNAL_SERVER
 +      if (conn->server)
 +              return tlsv1_server_shutdown(conn->server);
 +#endif /* CONFIG_TLS_INTERNAL_SERVER */
 +      return -1;
 +}
 +
 +
 +int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
 +                            const struct tls_connection_params *params)
 +{
 +#ifdef CONFIG_TLS_INTERNAL_CLIENT
 +      struct tlsv1_credentials *cred;
 +
 +      if (conn->client == NULL)
 +              return -1;
 +
 +      cred = tlsv1_cred_alloc();
 +      if (cred == NULL)
 +              return -1;
 +
 +      if (params->subject_match) {
 +              wpa_printf(MSG_INFO, "TLS: subject_match not supported");
++              tlsv1_cred_free(cred);
 +              return -1;
 +      }
 +
 +      if (params->altsubject_match) {
 +              wpa_printf(MSG_INFO, "TLS: altsubject_match not supported");
++              tlsv1_cred_free(cred);
 +              return -1;
 +      }
 +
 +      if (params->suffix_match) {
 +              wpa_printf(MSG_INFO, "TLS: suffix_match not supported");
++              tlsv1_cred_free(cred);
 +              return -1;
 +      }
 +
 +      if (params->domain_match) {
 +              wpa_printf(MSG_INFO, "TLS: domain_match not supported");
++              tlsv1_cred_free(cred);
 +              return -1;
 +      }
 +
 +      if (params->openssl_ciphers) {
-                             int verify_peer)
++              wpa_printf(MSG_INFO, "TLS: openssl_ciphers not supported");
++              tlsv1_cred_free(cred);
 +              return -1;
 +      }
 +
 +      if (tlsv1_set_ca_cert(cred, params->ca_cert,
 +                            params->ca_cert_blob, params->ca_cert_blob_len,
 +                            params->ca_path)) {
 +              wpa_printf(MSG_INFO, "TLS: Failed to configure trusted CA "
 +                         "certificates");
 +              tlsv1_cred_free(cred);
 +              return -1;
 +      }
 +
 +      if (tlsv1_set_cert(cred, params->client_cert,
 +                         params->client_cert_blob,
 +                         params->client_cert_blob_len)) {
 +              wpa_printf(MSG_INFO, "TLS: Failed to configure client "
 +                         "certificate");
 +              tlsv1_cred_free(cred);
 +              return -1;
 +      }
 +
 +      if (tlsv1_set_private_key(cred, params->private_key,
 +                                params->private_key_passwd,
 +                                params->private_key_blob,
 +                                params->private_key_blob_len)) {
 +              wpa_printf(MSG_INFO, "TLS: Failed to load private key");
 +              tlsv1_cred_free(cred);
 +              return -1;
 +      }
 +
 +      if (tlsv1_set_dhparams(cred, params->dh_file, params->dh_blob,
 +                             params->dh_blob_len)) {
 +              wpa_printf(MSG_INFO, "TLS: Failed to load DH parameters");
 +              tlsv1_cred_free(cred);
 +              return -1;
 +      }
 +
 +      if (tlsv1_client_set_cred(conn->client, cred) < 0) {
 +              tlsv1_cred_free(cred);
 +              return -1;
 +      }
 +
 +      tlsv1_client_set_time_checks(
 +              conn->client, !(params->flags & TLS_CONN_DISABLE_TIME_CHECKS));
 +
 +      return 0;
 +#else /* CONFIG_TLS_INTERNAL_CLIENT */
 +      return -1;
 +#endif /* CONFIG_TLS_INTERNAL_CLIENT */
 +}
 +
 +
 +int tls_global_set_params(void *tls_ctx,
 +                        const struct tls_connection_params *params)
 +{
 +#ifdef CONFIG_TLS_INTERNAL_SERVER
 +      struct tls_global *global = tls_ctx;
 +      struct tlsv1_credentials *cred;
 +
 +      /* Currently, global parameters are only set when running in server
 +       * mode. */
 +      global->server = 1;
 +      tlsv1_cred_free(global->server_cred);
 +      global->server_cred = cred = tlsv1_cred_alloc();
 +      if (cred == NULL)
 +              return -1;
 +
 +      if (tlsv1_set_ca_cert(cred, params->ca_cert, params->ca_cert_blob,
 +                            params->ca_cert_blob_len, params->ca_path)) {
 +              wpa_printf(MSG_INFO, "TLS: Failed to configure trusted CA "
 +                         "certificates");
 +              return -1;
 +      }
 +
 +      if (tlsv1_set_cert(cred, params->client_cert, params->client_cert_blob,
 +                         params->client_cert_blob_len)) {
 +              wpa_printf(MSG_INFO, "TLS: Failed to configure server "
 +                         "certificate");
 +              return -1;
 +      }
 +
 +      if (tlsv1_set_private_key(cred, params->private_key,
 +                                params->private_key_passwd,
 +                                params->private_key_blob,
 +                                params->private_key_blob_len)) {
 +              wpa_printf(MSG_INFO, "TLS: Failed to load private key");
 +              return -1;
 +      }
 +
 +      if (tlsv1_set_dhparams(cred, params->dh_file, params->dh_blob,
 +                             params->dh_blob_len)) {
 +              wpa_printf(MSG_INFO, "TLS: Failed to load DH parameters");
 +              return -1;
 +      }
 +
 +      return 0;
 +#else /* CONFIG_TLS_INTERNAL_SERVER */
 +      return -1;
 +#endif /* CONFIG_TLS_INTERNAL_SERVER */
 +}
 +
 +
 +int tls_global_set_verify(void *tls_ctx, int check_crl)
 +{
 +      struct tls_global *global = tls_ctx;
 +      global->check_crl = check_crl;
 +      return 0;
 +}
 +
 +
 +int tls_connection_set_verify(void *tls_ctx, struct tls_connection *conn,
- int tls_connection_get_keys(void *tls_ctx, struct tls_connection *conn,
-                           struct tls_keys *keys)
++                            int verify_peer, unsigned int flags,
++                            const u8 *session_ctx, size_t session_ctx_len)
 +{
 +#ifdef CONFIG_TLS_INTERNAL_SERVER
 +      if (conn->server)
 +              return tlsv1_server_set_verify(conn->server, verify_peer);
 +#endif /* CONFIG_TLS_INTERNAL_SERVER */
 +      return -1;
 +}
 +
 +
-               return tlsv1_client_get_keys(conn->client, keys);
++int tls_connection_get_random(void *tls_ctx, struct tls_connection *conn,
++                            struct tls_random *data)
++{
++#ifdef CONFIG_TLS_INTERNAL_CLIENT
++      if (conn->client)
++              return tlsv1_client_get_random(conn->client, data);
++#endif /* CONFIG_TLS_INTERNAL_CLIENT */
++#ifdef CONFIG_TLS_INTERNAL_SERVER
++      if (conn->server)
++              return tlsv1_server_get_random(conn->server, data);
++#endif /* CONFIG_TLS_INTERNAL_SERVER */
++      return -1;
++}
++
++
++static int tls_get_keyblock_size(struct tls_connection *conn)
 +{
 +#ifdef CONFIG_TLS_INTERNAL_CLIENT
 +      if (conn->client)
-               return tlsv1_server_get_keys(conn->server, keys);
++              return tlsv1_client_get_keyblock_size(conn->client);
 +#endif /* CONFIG_TLS_INTERNAL_CLIENT */
 +#ifdef CONFIG_TLS_INTERNAL_SERVER
 +      if (conn->server)
-                      u8 *out, size_t out_len)
- {
++              return tlsv1_server_get_keyblock_size(conn->server);
 +#endif /* CONFIG_TLS_INTERNAL_SERVER */
 +      return -1;
 +}
 +
 +
 +int tls_connection_prf(void *tls_ctx, struct tls_connection *conn,
 +                     const char *label, int server_random_first,
-               return tlsv1_client_prf(conn->client, label,
-                                       server_random_first,
-                                       out, out_len);
++                     int skip_keyblock, u8 *out, size_t out_len)
++{
++      int ret = -1, skip = 0;
++      u8 *tmp_out = NULL;
++      u8 *_out = out;
++
++      if (skip_keyblock) {
++              skip = tls_get_keyblock_size(conn);
++              if (skip < 0)
++                      return -1;
++              tmp_out = os_malloc(skip + out_len);
++              if (!tmp_out)
++                      return -1;
++              _out = tmp_out;
++      }
++
 +#ifdef CONFIG_TLS_INTERNAL_CLIENT
 +      if (conn->client) {
-               return tlsv1_server_prf(conn->server, label,
-                                       server_random_first,
-                                       out, out_len);
++              ret = tlsv1_client_prf(conn->client, label,
++                                     server_random_first,
++                                     _out, out_len);
 +      }
 +#endif /* CONFIG_TLS_INTERNAL_CLIENT */
 +#ifdef CONFIG_TLS_INTERNAL_SERVER
 +      if (conn->server) {
-       return -1;
++              ret = tlsv1_server_prf(conn->server, label,
++                                     server_random_first,
++                                     _out, out_len);
 +      }
 +#endif /* CONFIG_TLS_INTERNAL_SERVER */
- int tls_connection_get_keyblock_size(void *tls_ctx,
-                                    struct tls_connection *conn)
- {
- #ifdef CONFIG_TLS_INTERNAL_CLIENT
-       if (conn->client)
-               return tlsv1_client_get_keyblock_size(conn->client);
- #endif /* CONFIG_TLS_INTERNAL_CLIENT */
- #ifdef CONFIG_TLS_INTERNAL_SERVER
-       if (conn->server)
-               return tlsv1_server_get_keyblock_size(conn->server);
- #endif /* CONFIG_TLS_INTERNAL_SERVER */
-       return -1;
- }
- unsigned int tls_capabilities(void *tls_ctx)
- {
-       return 0;
- }
++      if (ret == 0 && skip_keyblock)
++              os_memcpy(out, _out + skip, out_len);
++      bin_clear_free(tmp_out, skip);
++
++      return ret;
 +}
 +
 +
 +struct wpabuf * tls_connection_handshake(void *tls_ctx,
 +                                       struct tls_connection *conn,
 +                                       const struct wpabuf *in_data,
 +                                       struct wpabuf **appl_data)
 +{
 +      return tls_connection_handshake2(tls_ctx, conn, in_data, appl_data,
 +                                       NULL);
 +}
 +
 +
 +struct wpabuf * tls_connection_handshake2(void *tls_ctx,
 +                                        struct tls_connection *conn,
 +                                        const struct wpabuf *in_data,
 +                                        struct wpabuf **appl_data,
 +                                        int *need_more_data)
 +{
 +#ifdef CONFIG_TLS_INTERNAL_CLIENT
 +      u8 *res, *ad;
 +      size_t res_len, ad_len;
 +      struct wpabuf *out;
 +
 +      if (conn->client == NULL)
 +              return NULL;
 +
 +      ad = NULL;
 +      res = tlsv1_client_handshake(conn->client,
 +                                   in_data ? wpabuf_head(in_data) : NULL,
 +                                   in_data ? wpabuf_len(in_data) : 0,
 +                                   &res_len, &ad, &ad_len, need_more_data);
 +      if (res == NULL)
 +              return NULL;
 +      out = wpabuf_alloc_ext_data(res, res_len);
 +      if (out == NULL) {
 +              os_free(res);
 +              os_free(ad);
 +              return NULL;
 +      }
 +      if (appl_data) {
 +              if (ad) {
 +                      *appl_data = wpabuf_alloc_ext_data(ad, ad_len);
 +                      if (*appl_data == NULL)
 +                              os_free(ad);
 +              } else
 +                      *appl_data = NULL;
 +      } else
 +              os_free(ad);
 +
 +      return out;
 +#else /* CONFIG_TLS_INTERNAL_CLIENT */
 +      return NULL;
 +#endif /* CONFIG_TLS_INTERNAL_CLIENT */
 +}
 +
 +
 +struct wpabuf * tls_connection_server_handshake(void *tls_ctx,
 +                                              struct tls_connection *conn,
 +                                              const struct wpabuf *in_data,
 +                                              struct wpabuf **appl_data)
 +{
 +#ifdef CONFIG_TLS_INTERNAL_SERVER
 +      u8 *res;
 +      size_t res_len;
 +      struct wpabuf *out;
 +
 +      if (conn->server == NULL)
 +              return NULL;
 +
 +      if (appl_data)
 +              *appl_data = NULL;
 +
 +      res = tlsv1_server_handshake(conn->server, wpabuf_head(in_data),
 +                                   wpabuf_len(in_data), &res_len);
 +      if (res == NULL && tlsv1_server_established(conn->server))
 +              return wpabuf_alloc(0);
 +      if (res == NULL)
 +              return NULL;
 +      out = wpabuf_alloc_ext_data(res, res_len);
 +      if (out == NULL) {
 +              os_free(res);
 +              return NULL;
 +      }
 +
 +      return out;
 +#else /* CONFIG_TLS_INTERNAL_SERVER */
 +      return NULL;
 +#endif /* CONFIG_TLS_INTERNAL_SERVER */
 +}
 +
 +
 +struct wpabuf * tls_connection_encrypt(void *tls_ctx,
 +                                     struct tls_connection *conn,
 +                                     const struct wpabuf *in_data)
 +{
 +#ifdef CONFIG_TLS_INTERNAL_CLIENT
 +      if (conn->client) {
 +              struct wpabuf *buf;
 +              int res;
 +              buf = wpabuf_alloc(wpabuf_len(in_data) + 300);
 +              if (buf == NULL)
 +                      return NULL;
 +              res = tlsv1_client_encrypt(conn->client, wpabuf_head(in_data),
 +                                         wpabuf_len(in_data),
 +                                         wpabuf_mhead(buf),
 +                                         wpabuf_size(buf));
 +              if (res < 0) {
 +                      wpabuf_free(buf);
 +                      return NULL;
 +              }
 +              wpabuf_put(buf, res);
 +              return buf;
 +      }
 +#endif /* CONFIG_TLS_INTERNAL_CLIENT */
 +#ifdef CONFIG_TLS_INTERNAL_SERVER
 +      if (conn->server) {
 +              struct wpabuf *buf;
 +              int res;
 +              buf = wpabuf_alloc(wpabuf_len(in_data) + 300);
 +              if (buf == NULL)
 +                      return NULL;
 +              res = tlsv1_server_encrypt(conn->server, wpabuf_head(in_data),
 +                                         wpabuf_len(in_data),
 +                                         wpabuf_mhead(buf),
 +                                         wpabuf_size(buf));
 +              if (res < 0) {
 +                      wpabuf_free(buf);
 +                      return NULL;
 +              }
 +              wpabuf_put(buf, res);
 +              return buf;
 +      }
 +#endif /* CONFIG_TLS_INTERNAL_SERVER */
 +      return NULL;
 +}
 +
 +
 +struct wpabuf * tls_connection_decrypt(void *tls_ctx,
 +                                     struct tls_connection *conn,
 +                                     const struct wpabuf *in_data)
 +{
 +      return tls_connection_decrypt2(tls_ctx, conn, in_data, NULL);
 +}
 +
 +
 +struct wpabuf * tls_connection_decrypt2(void *tls_ctx,
 +                                      struct tls_connection *conn,
 +                                      const struct wpabuf *in_data,
 +                                      int *need_more_data)
 +{
 +      if (need_more_data)
 +              *need_more_data = 0;
 +
 +#ifdef CONFIG_TLS_INTERNAL_CLIENT
 +      if (conn->client) {
 +              return tlsv1_client_decrypt(conn->client, wpabuf_head(in_data),
 +                                          wpabuf_len(in_data),
 +                                          need_more_data);
 +      }
 +#endif /* CONFIG_TLS_INTERNAL_CLIENT */
 +#ifdef CONFIG_TLS_INTERNAL_SERVER
 +      if (conn->server) {
 +              struct wpabuf *buf;
 +              int res;
 +              buf = wpabuf_alloc((wpabuf_len(in_data) + 500) * 3);
 +              if (buf == NULL)
 +                      return NULL;
 +              res = tlsv1_server_decrypt(conn->server, wpabuf_head(in_data),
 +                                         wpabuf_len(in_data),
 +                                         wpabuf_mhead(buf),
 +                                         wpabuf_size(buf));
 +              if (res < 0) {
 +                      wpabuf_free(buf);
 +                      return NULL;
 +              }
 +              wpabuf_put(buf, res);
 +              return buf;
 +      }
 +#endif /* CONFIG_TLS_INTERNAL_SERVER */
 +      return NULL;
 +}
 +
 +
 +int tls_connection_resumed(void *tls_ctx, struct tls_connection *conn)
 +{
 +#ifdef CONFIG_TLS_INTERNAL_CLIENT
 +      if (conn->client)
 +              return tlsv1_client_resumed(conn->client);
 +#endif /* CONFIG_TLS_INTERNAL_CLIENT */
 +#ifdef CONFIG_TLS_INTERNAL_SERVER
 +      if (conn->server)
 +              return tlsv1_server_resumed(conn->server);
 +#endif /* CONFIG_TLS_INTERNAL_SERVER */
 +      return -1;
 +}
 +
 +
 +int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn,
 +                                 u8 *ciphers)
 +{
 +#ifdef CONFIG_TLS_INTERNAL_CLIENT
 +      if (conn->client)
 +              return tlsv1_client_set_cipher_list(conn->client, ciphers);
 +#endif /* CONFIG_TLS_INTERNAL_CLIENT */
 +#ifdef CONFIG_TLS_INTERNAL_SERVER
 +      if (conn->server)
 +              return tlsv1_server_set_cipher_list(conn->server, ciphers);
 +#endif /* CONFIG_TLS_INTERNAL_SERVER */
 +      return -1;
 +}
 +
 +
++int tls_get_version(void *ssl_ctx, struct tls_connection *conn,
++                  char *buf, size_t buflen)
++{
++      /* TODO */
++      return -1;
++}
++
++
 +int tls_get_cipher(void *tls_ctx, struct tls_connection *conn,
 +                 char *buf, size_t buflen)
 +{
 +      if (conn == NULL)
 +              return -1;
 +#ifdef CONFIG_TLS_INTERNAL_CLIENT
 +      if (conn->client)
 +              return tlsv1_client_get_cipher(conn->client, buf, buflen);
 +#endif /* CONFIG_TLS_INTERNAL_CLIENT */
 +#ifdef CONFIG_TLS_INTERNAL_SERVER
 +      if (conn->server)
 +              return tlsv1_server_get_cipher(conn->server, buf, buflen);
 +#endif /* CONFIG_TLS_INTERNAL_SERVER */
 +      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)
 +{
 +#ifdef CONFIG_TLS_INTERNAL_CLIENT
 +      if (conn->client) {
 +              return tlsv1_client_hello_ext(conn->client, ext_type,
 +                                            data, data_len);
 +      }
 +#endif /* CONFIG_TLS_INTERNAL_CLIENT */
 +      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_set_session_ticket_cb(void *tls_ctx,
 +                                       struct tls_connection *conn,
 +                                       tls_session_ticket_cb cb,
 +                                       void *ctx)
 +{
 +#ifdef CONFIG_TLS_INTERNAL_CLIENT
 +      if (conn->client) {
 +              tlsv1_client_set_session_ticket_cb(conn->client, cb, ctx);
 +              return 0;
 +      }
 +#endif /* CONFIG_TLS_INTERNAL_CLIENT */
 +#ifdef CONFIG_TLS_INTERNAL_SERVER
 +      if (conn->server) {
 +              tlsv1_server_set_session_ticket_cb(conn->server, cb, ctx);
 +              return 0;
 +      }
 +#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");
 +}
++
++
++void tls_connection_set_success_data(struct tls_connection *conn,
++                                   struct wpabuf *data)
++{
++}
++
++
++void tls_connection_set_success_data_resumed(struct tls_connection *conn)
++{
++}
++
++
++const struct wpabuf *
++tls_connection_get_success_data(struct tls_connection *conn)
++{
++      return NULL;
++}
++
++
++void tls_connection_remove_session(struct tls_connection *conn)
++{
++}
index a6d210afcf0f3bea47c47f336589a2f992e80a4e,0000000000000000000000000000000000000000..ae392ad8aa0f56bcb819cf5276c567984a1ad112
mode 100644,000000..100644
--- /dev/null
@@@ -1,200 -1,0 +1,218 @@@
-                             int verify_peer)
 +/*
 + * SSL/TLS interface functions for no TLS case
 + * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
 + *
 + * This software may be distributed under the terms of the BSD license.
 + * See README for more details.
 + */
 +
 +#include "includes.h"
 +
 +#include "common.h"
 +#include "tls.h"
 +
 +void * tls_init(const struct tls_config *conf)
 +{
 +      return (void *) 1;
 +}
 +
 +
 +void tls_deinit(void *ssl_ctx)
 +{
 +}
 +
 +
 +int tls_get_errors(void *tls_ctx)
 +{
 +      return 0;
 +}
 +
 +
 +struct tls_connection * tls_connection_init(void *tls_ctx)
 +{
 +      return NULL;
 +}
 +
 +
 +void tls_connection_deinit(void *tls_ctx, struct tls_connection *conn)
 +{
 +}
 +
 +
 +int tls_connection_established(void *tls_ctx, struct tls_connection *conn)
 +{
 +      return -1;
 +}
 +
 +
 +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)
 +{
 +      return -1;
 +}
 +
 +
 +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 tls_connection_get_keys(void *tls_ctx, struct tls_connection *conn,
-                           struct tls_keys *keys)
++                            int verify_peer, unsigned int flags,
++                            const u8 *session_ctx, size_t session_ctx_len)
 +{
 +      return -1;
 +}
 +
 +
-                      u8 *out, size_t out_len)
++int tls_connection_get_random(void *tls_ctx, struct tls_connection *conn,
++                            struct tls_random *data)
 +{
 +      return -1;
 +}
 +
 +
 +int tls_connection_prf(void *tls_ctx, struct tls_connection *conn,
 +                     const char *label, int server_random_first,
- int tls_connection_get_keyblock_size(void *tls_ctx,
-                                    struct tls_connection *conn)
++                     int skip_keyblock, u8 *out, size_t out_len)
 +{
 +      return -1;
 +}
 +
 +
 +struct wpabuf * tls_connection_handshake(void *tls_ctx,
 +                                       struct tls_connection *conn,
 +                                       const struct wpabuf *in_data,
 +                                       struct wpabuf **appl_data)
 +{
 +      return NULL;
 +}
 +
 +
 +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)
 +{
 +      return NULL;
 +}
 +
 +
 +struct wpabuf * tls_connection_decrypt(void *tls_ctx,
 +                                     struct tls_connection *conn,
 +                                     const struct wpabuf *in_data)
 +{
 +      return NULL;
 +}
 +
 +
 +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_version(void *ssl_ctx, struct tls_connection *conn,
++                  char *buf, size_t buflen)
++{
++      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;
 +}
 +
 +
-       return -1;
++int tls_get_library_version(char *buf, size_t buf_len)
 +{
- unsigned int tls_capabilities(void *tls_ctx)
++      return os_snprintf(buf, buf_len, "none");
 +}
 +
 +
-       return 0;
++void tls_connection_set_success_data(struct tls_connection *conn,
++                                   struct wpabuf *data)
 +{
- int tls_get_library_version(char *buf, size_t buf_len)
 +}
 +
 +
-       return os_snprintf(buf, buf_len, "none");
++void tls_connection_set_success_data_resumed(struct tls_connection *conn)
++{
++}
++
++
++const struct wpabuf *
++tls_connection_get_success_data(struct tls_connection *conn)
++{
++      return NULL;
++}
++
++
++void tls_connection_remove_session(struct tls_connection *conn)
 +{
 +}
index 52db8fc076ac988da1968bb9ad1a28fde5ffed27,0000000000000000000000000000000000000000..8b7b47bc256d51b9536da03a4bca803b444ecc8f
mode 100644,000000..100644
--- /dev/null
@@@ -1,3658 -1,0 +1,4145 @@@
-  * Copyright (c) 2004-2013, Jouni Malinen <j@w1.fi>
 +/*
 + * SSL/TLS interface functions for OpenSSL
- #if defined(SSL_CTX_get_app_data) && defined(SSL_CTX_set_app_data)
- #define OPENSSL_SUPPORTS_CTX_APP_DATA
- #endif
++ * 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 "includes.h"
 +
 +#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/pkcs12.h>
 +#include <openssl/x509v3.h>
 +#ifndef OPENSSL_NO_ENGINE
 +#include <openssl/engine.h>
 +#endif /* OPENSSL_NO_ENGINE */
++#ifndef OPENSSL_NO_DSA
++#include <openssl/dsa.h>
++#endif
++#ifndef OPENSSL_NO_DH
++#include <openssl/dh.h>
++#endif
 +
 +#include "common.h"
 +#include "crypto.h"
++#include "sha1.h"
++#include "sha256.h"
 +#include "tls.h"
 +
-                       if (!FIPS_mode_set(1)) {
 +#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
 +#ifndef OPENSSL_NO_TLSEXT
 +#define HAVE_OCSP
 +#include <openssl/ocsp.h>
 +#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;
++static int tls_ex_idx_session = -1;
 +
 +struct tls_context {
 +      void (*event_cb)(void *ctx, enum tls_event ev,
 +                       union tls_event_data *data);
 +      void *cb_ctx;
 +      int cert_in_cb;
 +      char *ocsp_stapling_response;
 +};
 +
 +static struct tls_context *tls_global = NULL;
 +
 +
++struct tls_data {
++      SSL_CTX *ssl;
++      unsigned int tls_session_lifetime;
++};
++
 +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, *suffix_match, *domain_match;
 +      int read_alerts, write_alerts, failed;
 +
 +      tls_session_ticket_cb session_ticket_cb;
 +      void *session_ticket_cb_ctx;
 +
 +      /* SessionTicket received from OpenSSL hello_extension_cb (server) */
 +      u8 *session_ticket;
 +      size_t session_ticket_len;
 +
 +      unsigned int ca_cert_verify:1;
 +      unsigned int cert_probe:1;
 +      unsigned int server_cert_only:1;
 +      unsigned int invalid_hb_used:1;
++      unsigned int success_data:1;
 +
 +      u8 srv_cert_hash[32];
 +
 +      unsigned int flags;
 +
 +      X509 *peer_cert;
 +      X509 *peer_issuer;
 +      X509 *peer_issuer_issuer;
++
++#if OPENSSL_VERSION_NUMBER >= 0x10100000L
++      unsigned char client_random[SSL3_RANDOM_SIZE];
++      unsigned char server_random[SSL3_RANDOM_SIZE];
++#endif
 +};
 +
 +
 +static struct tls_context * tls_context_new(const struct tls_config *conf)
 +{
 +      struct tls_context *context = os_zalloc(sizeof(*context));
 +      if (context == NULL)
 +              return NULL;
 +      if (conf) {
 +              context->event_cb = conf->event_cb;
 +              context->cb_ctx = conf->cb_ctx;
 +              context->cert_in_cb = conf->cert_in_cb;
 +      }
 +      return context;
 +}
 +
 +
 +#ifdef CONFIG_NO_STDOUT_DEBUG
 +
 +static void _tls_show_errors(void)
 +{
 +      unsigned long err;
 +
 +      while ((err = ERR_get_error())) {
 +              /* Just ignore the errors, since stdout is disabled */
 +      }
 +}
 +#define tls_show_errors(l, f, t) _tls_show_errors()
 +
 +#else /* CONFIG_NO_STDOUT_DEBUG */
 +
 +static void tls_show_errors(int level, const char *func, const char *txt)
 +{
 +      unsigned long err;
 +
 +      wpa_printf(level, "OpenSSL: %s - %s %s",
 +                 func, txt, ERR_error_string(ERR_get_error(), NULL));
 +
 +      while ((err = ERR_get_error())) {
 +              wpa_printf(MSG_INFO, "OpenSSL: pending error: %s",
 +                         ERR_error_string(err, NULL));
 +      }
 +}
 +
 +#endif /* CONFIG_NO_STDOUT_DEBUG */
 +
 +
 +#ifdef CONFIG_NATIVE_WINDOWS
 +
 +/* Windows CryptoAPI and access to certificate stores */
 +#include <wincrypt.h>
 +
 +#ifdef __MINGW32_VERSION
 +/*
 + * MinGW does not yet include all the needed definitions for CryptoAPI, so
 + * define here whatever extra is needed.
 + */
 +#define CERT_SYSTEM_STORE_CURRENT_USER (1 << 16)
 +#define CERT_STORE_READONLY_FLAG 0x00008000
 +#define CERT_STORE_OPEN_EXISTING_FLAG 0x00004000
 +
 +#endif /* __MINGW32_VERSION */
 +
 +
 +struct cryptoapi_rsa_data {
 +      const CERT_CONTEXT *cert;
 +      HCRYPTPROV crypt_prov;
 +      DWORD key_spec;
 +      BOOL free_crypt_prov;
 +};
 +
 +
 +static void cryptoapi_error(const char *msg)
 +{
 +      wpa_printf(MSG_INFO, "CryptoAPI: %s; err=%u",
 +                 msg, (unsigned int) GetLastError());
 +}
 +
 +
 +static int cryptoapi_rsa_pub_enc(int flen, const unsigned char *from,
 +                               unsigned char *to, RSA *rsa, int padding)
 +{
 +      wpa_printf(MSG_DEBUG, "%s - not implemented", __func__);
 +      return 0;
 +}
 +
 +
 +static int cryptoapi_rsa_pub_dec(int flen, const unsigned char *from,
 +                               unsigned char *to, RSA *rsa, int padding)
 +{
 +      wpa_printf(MSG_DEBUG, "%s - not implemented", __func__);
 +      return 0;
 +}
 +
 +
 +static int cryptoapi_rsa_priv_enc(int flen, const unsigned char *from,
 +                                unsigned char *to, RSA *rsa, int padding)
 +{
 +      struct cryptoapi_rsa_data *priv =
 +              (struct cryptoapi_rsa_data *) rsa->meth->app_data;
 +      HCRYPTHASH hash;
 +      DWORD hash_size, len, i;
 +      unsigned char *buf = NULL;
 +      int ret = 0;
 +
 +      if (priv == NULL) {
 +              RSAerr(RSA_F_RSA_EAY_PRIVATE_ENCRYPT,
 +                     ERR_R_PASSED_NULL_PARAMETER);
 +              return 0;
 +      }
 +
 +      if (padding != RSA_PKCS1_PADDING) {
 +              RSAerr(RSA_F_RSA_EAY_PRIVATE_ENCRYPT,
 +                     RSA_R_UNKNOWN_PADDING_TYPE);
 +              return 0;
 +      }
 +
 +      if (flen != 16 /* MD5 */ + 20 /* SHA-1 */) {
 +              wpa_printf(MSG_INFO, "%s - only MD5-SHA1 hash supported",
 +                         __func__);
 +              RSAerr(RSA_F_RSA_EAY_PRIVATE_ENCRYPT,
 +                     RSA_R_INVALID_MESSAGE_LENGTH);
 +              return 0;
 +      }
 +
 +      if (!CryptCreateHash(priv->crypt_prov, CALG_SSL3_SHAMD5, 0, 0, &hash))
 +      {
 +              cryptoapi_error("CryptCreateHash failed");
 +              return 0;
 +      }
 +
 +      len = sizeof(hash_size);
 +      if (!CryptGetHashParam(hash, HP_HASHSIZE, (BYTE *) &hash_size, &len,
 +                             0)) {
 +              cryptoapi_error("CryptGetHashParam failed");
 +              goto err;
 +      }
 +
 +      if ((int) hash_size != flen) {
 +              wpa_printf(MSG_INFO, "CryptoAPI: Invalid hash size (%u != %d)",
 +                         (unsigned) hash_size, flen);
 +              RSAerr(RSA_F_RSA_EAY_PRIVATE_ENCRYPT,
 +                     RSA_R_INVALID_MESSAGE_LENGTH);
 +              goto err;
 +      }
 +      if (!CryptSetHashParam(hash, HP_HASHVAL, (BYTE * ) from, 0)) {
 +              cryptoapi_error("CryptSetHashParam failed");
 +              goto err;
 +      }
 +
 +      len = RSA_size(rsa);
 +      buf = os_malloc(len);
 +      if (buf == NULL) {
 +              RSAerr(RSA_F_RSA_EAY_PRIVATE_ENCRYPT, ERR_R_MALLOC_FAILURE);
 +              goto err;
 +      }
 +
 +      if (!CryptSignHash(hash, priv->key_spec, NULL, 0, buf, &len)) {
 +              cryptoapi_error("CryptSignHash failed");
 +              goto err;
 +      }
 +
 +      for (i = 0; i < len; i++)
 +              to[i] = buf[len - i - 1];
 +      ret = len;
 +
 +err:
 +      os_free(buf);
 +      CryptDestroyHash(hash);
 +
 +      return ret;
 +}
 +
 +
 +static int cryptoapi_rsa_priv_dec(int flen, const unsigned char *from,
 +                                unsigned char *to, RSA *rsa, int padding)
 +{
 +      wpa_printf(MSG_DEBUG, "%s - not implemented", __func__);
 +      return 0;
 +}
 +
 +
 +static void cryptoapi_free_data(struct cryptoapi_rsa_data *priv)
 +{
 +      if (priv == NULL)
 +              return;
 +      if (priv->crypt_prov && priv->free_crypt_prov)
 +              CryptReleaseContext(priv->crypt_prov, 0);
 +      if (priv->cert)
 +              CertFreeCertificateContext(priv->cert);
 +      os_free(priv);
 +}
 +
 +
 +static int cryptoapi_finish(RSA *rsa)
 +{
 +      cryptoapi_free_data((struct cryptoapi_rsa_data *) rsa->meth->app_data);
 +      os_free((void *) rsa->meth);
 +      rsa->meth = NULL;
 +      return 1;
 +}
 +
 +
 +static const CERT_CONTEXT * cryptoapi_find_cert(const char *name, DWORD store)
 +{
 +      HCERTSTORE cs;
 +      const CERT_CONTEXT *ret = NULL;
 +
 +      cs = CertOpenStore((LPCSTR) CERT_STORE_PROV_SYSTEM, 0, 0,
 +                         store | CERT_STORE_OPEN_EXISTING_FLAG |
 +                         CERT_STORE_READONLY_FLAG, L"MY");
 +      if (cs == NULL) {
 +              cryptoapi_error("Failed to open 'My system store'");
 +              return NULL;
 +      }
 +
 +      if (strncmp(name, "cert://", 7) == 0) {
 +              unsigned short wbuf[255];
 +              MultiByteToWideChar(CP_ACP, 0, name + 7, -1, wbuf, 255);
 +              ret = CertFindCertificateInStore(cs, X509_ASN_ENCODING |
 +                                               PKCS_7_ASN_ENCODING,
 +                                               0, CERT_FIND_SUBJECT_STR,
 +                                               wbuf, NULL);
 +      } else if (strncmp(name, "hash://", 7) == 0) {
 +              CRYPT_HASH_BLOB blob;
 +              int len;
 +              const char *hash = name + 7;
 +              unsigned char *buf;
 +
 +              len = os_strlen(hash) / 2;
 +              buf = os_malloc(len);
 +              if (buf && hexstr2bin(hash, buf, len) == 0) {
 +                      blob.cbData = len;
 +                      blob.pbData = buf;
 +                      ret = CertFindCertificateInStore(cs,
 +                                                       X509_ASN_ENCODING |
 +                                                       PKCS_7_ASN_ENCODING,
 +                                                       0, CERT_FIND_HASH,
 +                                                       &blob, NULL);
 +              }
 +              os_free(buf);
 +      }
 +
 +      CertCloseStore(cs, 0);
 +
 +      return ret;
 +}
 +
 +
 +static int tls_cryptoapi_cert(SSL *ssl, const char *name)
 +{
 +      X509 *cert = NULL;
 +      RSA *rsa = NULL, *pub_rsa;
 +      struct cryptoapi_rsa_data *priv;
 +      RSA_METHOD *rsa_meth;
 +
 +      if (name == NULL ||
 +          (strncmp(name, "cert://", 7) != 0 &&
 +           strncmp(name, "hash://", 7) != 0))
 +              return -1;
 +
 +      priv = os_zalloc(sizeof(*priv));
 +      rsa_meth = os_zalloc(sizeof(*rsa_meth));
 +      if (priv == NULL || rsa_meth == NULL) {
 +              wpa_printf(MSG_WARNING, "CryptoAPI: Failed to allocate memory "
 +                         "for CryptoAPI RSA method");
 +              os_free(priv);
 +              os_free(rsa_meth);
 +              return -1;
 +      }
 +
 +      priv->cert = cryptoapi_find_cert(name, CERT_SYSTEM_STORE_CURRENT_USER);
 +      if (priv->cert == NULL) {
 +              priv->cert = cryptoapi_find_cert(
 +                      name, CERT_SYSTEM_STORE_LOCAL_MACHINE);
 +      }
 +      if (priv->cert == NULL) {
 +              wpa_printf(MSG_INFO, "CryptoAPI: Could not find certificate "
 +                         "'%s'", name);
 +              goto err;
 +      }
 +
 +      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 "
 +                         "encoding");
 +              goto err;
 +      }
 +
 +      if (!CryptAcquireCertificatePrivateKey(priv->cert,
 +                                             CRYPT_ACQUIRE_COMPARE_KEY_FLAG,
 +                                             NULL, &priv->crypt_prov,
 +                                             &priv->key_spec,
 +                                             &priv->free_crypt_prov)) {
 +              cryptoapi_error("Failed to acquire a private key for the "
 +                              "certificate");
 +              goto err;
 +      }
 +
 +      rsa_meth->name = "Microsoft CryptoAPI RSA Method";
 +      rsa_meth->rsa_pub_enc = cryptoapi_rsa_pub_enc;
 +      rsa_meth->rsa_pub_dec = cryptoapi_rsa_pub_dec;
 +      rsa_meth->rsa_priv_enc = cryptoapi_rsa_priv_enc;
 +      rsa_meth->rsa_priv_dec = cryptoapi_rsa_priv_dec;
 +      rsa_meth->finish = cryptoapi_finish;
 +      rsa_meth->flags = RSA_METHOD_FLAG_NO_CHECK;
 +      rsa_meth->app_data = (char *) priv;
 +
 +      rsa = RSA_new();
 +      if (rsa == NULL) {
 +              SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_FILE,
 +                     ERR_R_MALLOC_FAILURE);
 +              goto err;
 +      }
 +
 +      if (!SSL_use_certificate(ssl, cert)) {
 +              RSA_free(rsa);
 +              rsa = NULL;
 +              goto err;
 +      }
 +      pub_rsa = cert->cert_info->key->pkey->pkey.rsa;
 +      X509_free(cert);
 +      cert = NULL;
 +
 +      rsa->n = BN_dup(pub_rsa->n);
 +      rsa->e = BN_dup(pub_rsa->e);
 +      if (!RSA_set_method(rsa, rsa_meth))
 +              goto err;
 +
 +      if (!SSL_use_RSAPrivateKey(ssl, rsa))
 +              goto err;
 +      RSA_free(rsa);
 +
 +      return 0;
 +
 +err:
 +      if (cert)
 +              X509_free(cert);
 +      if (rsa)
 +              RSA_free(rsa);
 +      else {
 +              os_free(rsa_meth);
 +              cryptoapi_free_data(priv);
 +      }
 +      return -1;
 +}
 +
 +
 +static int tls_cryptoapi_ca_cert(SSL_CTX *ssl_ctx, SSL *ssl, const char *name)
 +{
 +      HCERTSTORE cs;
 +      PCCERT_CONTEXT ctx = NULL;
 +      X509 *cert;
 +      char buf[128];
 +      const char *store;
 +#ifdef UNICODE
 +      WCHAR *wstore;
 +#endif /* UNICODE */
 +
 +      if (name == NULL || strncmp(name, "cert_store://", 13) != 0)
 +              return -1;
 +
 +      store = name + 13;
 +#ifdef UNICODE
 +      wstore = os_malloc((os_strlen(store) + 1) * sizeof(WCHAR));
 +      if (wstore == NULL)
 +              return -1;
 +      wsprintf(wstore, L"%S", store);
 +      cs = CertOpenSystemStore(0, wstore);
 +      os_free(wstore);
 +#else /* UNICODE */
 +      cs = CertOpenSystemStore(0, store);
 +#endif /* UNICODE */
 +      if (cs == NULL) {
 +              wpa_printf(MSG_DEBUG, "%s: failed to open system cert store "
 +                         "'%s': error=%d", __func__, store,
 +                         (int) GetLastError());
 +              return -1;
 +      }
 +
 +      while ((ctx = CertEnumCertificatesInStore(cs, ctx))) {
 +              cert = d2i_X509(NULL,
 +                              (const unsigned char **) &ctx->pbCertEncoded,
 +                              ctx->cbCertEncoded);
 +              if (cert == NULL) {
 +                      wpa_printf(MSG_INFO, "CryptoAPI: Could not process "
 +                                 "X509 DER encoding for CA cert");
 +                      continue;
 +              }
 +
 +              X509_NAME_oneline(X509_get_subject_name(cert), buf,
 +                                sizeof(buf));
 +              wpa_printf(MSG_DEBUG, "OpenSSL: Loaded CA certificate for "
 +                         "system certificate store: subject='%s'", buf);
 +
 +              if (!X509_STORE_add_cert(ssl_ctx->cert_store, cert)) {
 +                      tls_show_errors(MSG_WARNING, __func__,
 +                                      "Failed to add ca_cert to OpenSSL "
 +                                      "certificate store");
 +              }
 +
 +              X509_free(cert);
 +      }
 +
 +      if (!CertCloseStore(cs, 0)) {
 +              wpa_printf(MSG_DEBUG, "%s: failed to close system cert store "
 +                         "'%s': error=%d", __func__, name + 13,
 +                         (int) GetLastError());
 +      }
 +
 +      return 0;
 +}
 +
 +
 +#else /* CONFIG_NATIVE_WINDOWS */
 +
 +static int tls_cryptoapi_cert(SSL *ssl, const char *name)
 +{
 +      return -1;
 +}
 +
 +#endif /* CONFIG_NATIVE_WINDOWS */
 +
 +
 +static void ssl_info_cb(const SSL *ssl, int where, int ret)
 +{
 +      const char *str;
 +      int w;
 +
 +      wpa_printf(MSG_DEBUG, "SSL: (where=0x%x ret=0x%x)", where, ret);
 +      w = where & ~SSL_ST_MASK;
 +      if (w & SSL_ST_CONNECT)
 +              str = "SSL_connect";
 +      else if (w & SSL_ST_ACCEPT)
 +              str = "SSL_accept";
 +      else
 +              str = "undefined";
 +
 +      if (where & SSL_CB_LOOP) {
 +              wpa_printf(MSG_DEBUG, "SSL: %s:%s",
 +                         str, SSL_state_string_long(ssl));
 +      } else if (where & SSL_CB_ALERT) {
 +              struct tls_connection *conn = SSL_get_app_data((SSL *) ssl);
 +              wpa_printf(MSG_INFO, "SSL: SSL3 alert: %s:%s:%s",
 +                         where & SSL_CB_READ ?
 +                         "read (remote end reported an error)" :
 +                         "write (local SSL3 detected an error)",
 +                         SSL_alert_type_string_long(ret),
 +                         SSL_alert_desc_string_long(ret));
 +              if ((ret >> 8) == SSL3_AL_FATAL) {
 +                      if (where & SSL_CB_READ)
 +                              conn->read_alerts++;
 +                      else
 +                              conn->write_alerts++;
 +              }
 +              if (conn->context->event_cb != NULL) {
 +                      union tls_event_data ev;
 +                      struct tls_context *context = conn->context;
 +                      os_memset(&ev, 0, sizeof(ev));
 +                      ev.alert.is_local = !(where & SSL_CB_READ);
 +                      ev.alert.type = SSL_alert_type_string_long(ret);
 +                      ev.alert.description = SSL_alert_desc_string_long(ret);
 +                      context->event_cb(context->cb_ctx, TLS_ALERT, &ev);
 +              }
 +      } else if (where & SSL_CB_EXIT && ret <= 0) {
 +              wpa_printf(MSG_DEBUG, "SSL: %s:%s in %s",
 +                         str, ret == 0 ? "failed" : "error",
 +                         SSL_state_string_long(ssl));
 +      }
 +}
 +
 +
 +#ifndef OPENSSL_NO_ENGINE
 +/**
 + * tls_engine_load_dynamic_generic - load any openssl engine
 + * @pre: an array of commands and values that load an engine initialized
 + *       in the engine specific function
 + * @post: an array of commands and values that initialize an already loaded
 + *        engine (or %NULL if not required)
 + * @id: the engine id of the engine to load (only required if post is not %NULL
 + *
 + * This function is a generic function that loads any openssl engine.
 + *
 + * Returns: 0 on success, -1 on failure
 + */
 +static int tls_engine_load_dynamic_generic(const char *pre[],
 +                                         const char *post[], const char *id)
 +{
 +      ENGINE *engine;
 +      const char *dynamic_id = "dynamic";
 +
 +      engine = ENGINE_by_id(id);
 +      if (engine) {
 +              ENGINE_free(engine);
 +              wpa_printf(MSG_DEBUG, "ENGINE: engine '%s' is already "
 +                         "available", id);
 +              return 0;
 +      }
 +      ERR_clear_error();
 +
 +      engine = ENGINE_by_id(dynamic_id);
 +      if (engine == NULL) {
 +              wpa_printf(MSG_INFO, "ENGINE: Can't find engine %s [%s]",
 +                         dynamic_id,
 +                         ERR_error_string(ERR_get_error(), NULL));
 +              return -1;
 +      }
 +
 +      /* Perform the pre commands. This will load the engine. */
 +      while (pre && pre[0]) {
 +              wpa_printf(MSG_DEBUG, "ENGINE: '%s' '%s'", pre[0], pre[1]);
 +              if (ENGINE_ctrl_cmd_string(engine, pre[0], pre[1], 0) == 0) {
 +                      wpa_printf(MSG_INFO, "ENGINE: ctrl cmd_string failed: "
 +                                 "%s %s [%s]", pre[0], pre[1],
 +                                 ERR_error_string(ERR_get_error(), NULL));
 +                      ENGINE_free(engine);
 +                      return -1;
 +              }
 +              pre += 2;
 +      }
 +
 +      /*
 +       * Free the reference to the "dynamic" engine. The loaded engine can
 +       * now be looked up using ENGINE_by_id().
 +       */
 +      ENGINE_free(engine);
 +
 +      engine = ENGINE_by_id(id);
 +      if (engine == NULL) {
 +              wpa_printf(MSG_INFO, "ENGINE: Can't find engine %s [%s]",
 +                         id, ERR_error_string(ERR_get_error(), NULL));
 +              return -1;
 +      }
 +
 +      while (post && post[0]) {
 +              wpa_printf(MSG_DEBUG, "ENGINE: '%s' '%s'", post[0], post[1]);
 +              if (ENGINE_ctrl_cmd_string(engine, post[0], post[1], 0) == 0) {
 +                      wpa_printf(MSG_DEBUG, "ENGINE: ctrl cmd_string failed:"
 +                              " %s %s [%s]", post[0], post[1],
 +                                 ERR_error_string(ERR_get_error(), NULL));
 +                      ENGINE_remove(engine);
 +                      ENGINE_free(engine);
 +                      return -1;
 +              }
 +              post += 2;
 +      }
 +      ENGINE_free(engine);
 +
 +      return 0;
 +}
 +
 +
 +/**
 + * tls_engine_load_dynamic_pkcs11 - load the pkcs11 engine provided by opensc
 + * @pkcs11_so_path: pksc11_so_path from the configuration
 + * @pcks11_module_path: pkcs11_module_path from the configuration
 + */
 +static int tls_engine_load_dynamic_pkcs11(const char *pkcs11_so_path,
 +                                        const char *pkcs11_module_path)
 +{
 +      char *engine_id = "pkcs11";
 +      const char *pre_cmd[] = {
 +              "SO_PATH", NULL /* pkcs11_so_path */,
 +              "ID", NULL /* engine_id */,
 +              "LIST_ADD", "1",
 +              /* "NO_VCHECK", "1", */
 +              "LOAD", NULL,
 +              NULL, NULL
 +      };
 +      const char *post_cmd[] = {
 +              "MODULE_PATH", NULL /* pkcs11_module_path */,
 +              NULL, NULL
 +      };
 +
 +      if (!pkcs11_so_path)
 +              return 0;
 +
 +      pre_cmd[1] = pkcs11_so_path;
 +      pre_cmd[3] = engine_id;
 +      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);
 +
 +      return tls_engine_load_dynamic_generic(pre_cmd, post_cmd, engine_id);
 +}
 +
 +
 +/**
 + * tls_engine_load_dynamic_opensc - load the opensc engine provided by opensc
 + * @opensc_so_path: opensc_so_path from the configuration
 + */
 +static int tls_engine_load_dynamic_opensc(const char *opensc_so_path)
 +{
 +      char *engine_id = "opensc";
 +      const char *pre_cmd[] = {
 +              "SO_PATH", NULL /* opensc_so_path */,
 +              "ID", NULL /* engine_id */,
 +              "LIST_ADD", "1",
 +              "LOAD", NULL,
 +              NULL, NULL
 +      };
 +
 +      if (!opensc_so_path)
 +              return 0;
 +
 +      pre_cmd[1] = opensc_so_path;
 +      pre_cmd[3] = engine_id;
 +
 +      wpa_printf(MSG_DEBUG, "ENGINE: Loading OpenSC Engine from %s",
 +                 opensc_so_path);
 +
 +      return tls_engine_load_dynamic_generic(pre_cmd, NULL, engine_id);
 +}
 +#endif /* OPENSSL_NO_ENGINE */
 +
 +
++static void remove_session_cb(SSL_CTX *ctx, SSL_SESSION *sess)
++{
++      struct wpabuf *buf;
++
++      if (tls_ex_idx_session < 0)
++              return;
++      buf = SSL_SESSION_get_ex_data(sess, tls_ex_idx_session);
++      if (!buf)
++              return;
++      wpa_printf(MSG_DEBUG,
++                 "OpenSSL: Free application session data %p (sess %p)",
++                 buf, sess);
++      wpabuf_free(buf);
++
++      SSL_SESSION_set_ex_data(sess, tls_ex_idx_session, NULL);
++}
++
++
 +void * tls_init(const struct tls_config *conf)
 +{
++      struct tls_data *data;
 +      SSL_CTX *ssl;
 +      struct tls_context *context;
 +      const char *ciphers;
 +
 +      if (tls_openssl_ref_count == 0) {
 +              tls_global = context = tls_context_new(conf);
 +              if (context == NULL)
 +                      return NULL;
 +#ifdef CONFIG_FIPS
 +#ifdef OPENSSL_FIPS
 +              if (conf && conf->fips_mode) {
-                       } else
++                      static int fips_enabled = 0;
++
++                      if (!fips_enabled && !FIPS_mode_set(1)) {
 +                              wpa_printf(MSG_ERROR, "Failed to enable FIPS "
 +                                         "mode");
 +                              ERR_load_crypto_strings();
 +                              ERR_print_errors_fp(stderr);
 +                              os_free(tls_global);
 +                              tls_global = NULL;
 +                              return NULL;
- #ifdef OPENSSL_SUPPORTS_CTX_APP_DATA
-               /* Newer OpenSSL can store app-data per-SSL */
++                      } else {
 +                              wpa_printf(MSG_INFO, "Running in FIPS mode");
++                              fips_enabled = 1;
++                      }
 +              }
 +#else /* OPENSSL_FIPS */
 +              if (conf && conf->fips_mode) {
 +                      wpa_printf(MSG_ERROR, "FIPS mode requested, but not "
 +                                 "supported");
 +                      os_free(tls_global);
 +                      tls_global = NULL;
 +                      return NULL;
 +              }
 +#endif /* OPENSSL_FIPS */
 +#endif /* CONFIG_FIPS */
 +              SSL_load_error_strings();
 +              SSL_library_init();
 +#ifndef OPENSSL_NO_SHA256
 +              EVP_add_digest(EVP_sha256());
 +#endif /* OPENSSL_NO_SHA256 */
 +              /* TODO: if /dev/urandom is available, PRNG is seeded
 +               * automatically. If this is not the case, random data should
 +               * be added here. */
 +
 +#ifdef PKCS12_FUNCS
 +#ifndef OPENSSL_NO_RC2
 +              /*
 +               * 40-bit RC2 is commonly used in PKCS#12 files, so enable it.
 +               * This is enabled by PKCS12_PBE_add() in OpenSSL 0.9.8
 +               * versions, but it looks like OpenSSL 1.0.0 does not do that
 +               * anymore.
 +               */
 +              EVP_add_cipher(EVP_rc2_40_cbc());
 +#endif /* OPENSSL_NO_RC2 */
 +              PKCS12_PBE_add();
 +#endif  /* PKCS12_FUNCS */
 +      } else {
- #else /* OPENSSL_SUPPORTS_CTX_APP_DATA */
-               context = tls_global;
- #endif /* OPENSSL_SUPPORTS_CTX_APP_DATA */
 +              context = tls_context_new(conf);
 +              if (context == NULL)
 +                      return NULL;
-       ssl = SSL_CTX_new(SSLv23_method());
 +      }
 +      tls_openssl_ref_count++;
 +
- #ifdef OPENSSL_SUPPORTS_CTX_APP_DATA
++      data = os_zalloc(sizeof(*data));
++      if (data)
++              ssl = SSL_CTX_new(SSLv23_method());
++      else
++              ssl = NULL;
 +      if (ssl == NULL) {
 +              tls_openssl_ref_count--;
- #endif /* OPENSSL_SUPPORTS_CTX_APP_DATA */
 +              if (context != tls_global)
 +                      os_free(context);
- #ifdef OPENSSL_SUPPORTS_CTX_APP_DATA
 +              if (tls_openssl_ref_count == 0) {
 +                      os_free(tls_global);
 +                      tls_global = NULL;
 +              }
 +              return NULL;
 +      }
++      data->ssl = ssl;
++      if (conf)
++              data->tls_session_lifetime = conf->tls_session_lifetime;
 +
 +      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);
- #endif /* OPENSSL_SUPPORTS_CTX_APP_DATA */
 +      SSL_CTX_set_app_data(ssl, context);
-                       tls_deinit(ssl);
++      if (data->tls_session_lifetime > 0) {
++              SSL_CTX_set_quiet_shutdown(ssl, 1);
++              /*
++               * Set default context here. In practice, this will be replaced
++               * by the per-EAP method context in tls_connection_set_verify().
++               */
++              SSL_CTX_set_session_id_context(ssl, (u8 *) "hostapd", 7);
++              SSL_CTX_set_session_cache_mode(ssl, SSL_SESS_CACHE_SERVER);
++              SSL_CTX_set_timeout(ssl, data->tls_session_lifetime);
++              SSL_CTX_sess_set_remove_cb(ssl, remove_session_cb);
++      } else {
++              SSL_CTX_set_session_cache_mode(ssl, SSL_SESS_CACHE_OFF);
++      }
++
++      if (tls_ex_idx_session < 0) {
++              tls_ex_idx_session = SSL_SESSION_get_ex_new_index(
++                      0, NULL, NULL, NULL, NULL);
++              if (tls_ex_idx_session < 0) {
++                      tls_deinit(data);
++                      return NULL;
++              }
++      }
 +
 +#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)) {
 +              if (tls_engine_load_dynamic_opensc(conf->opensc_engine_path) ||
 +                  tls_engine_load_dynamic_pkcs11(conf->pkcs11_engine_path,
 +                                                 conf->pkcs11_module_path)) {
-               tls_deinit(ssl);
++                      tls_deinit(data);
 +                      return NULL;
 +              }
 +      }
 +#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);
-       return ssl;
++              tls_deinit(data);
 +              return NULL;
 +      }
 +
-       SSL_CTX *ssl = ssl_ctx;
- #ifdef OPENSSL_SUPPORTS_CTX_APP_DATA
++      return data;
 +}
 +
 +
 +void tls_deinit(void *ssl_ctx)
 +{
- #endif /* OPENSSL_SUPPORTS_CTX_APP_DATA */
++      struct tls_data *data = ssl_ctx;
++      SSL_CTX *ssl = data->ssl;
 +      struct tls_context *context = SSL_CTX_get_app_data(ssl);
 +      if (context != tls_global)
 +              os_free(context);
-                                  ERR_error_string(ERR_get_error(), NULL));
-                       ret = TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED;
++      if (data->tls_session_lifetime > 0)
++              SSL_CTX_flush_sessions(ssl, 0);
 +      SSL_CTX_free(ssl);
 +
 +      tls_openssl_ref_count--;
 +      if (tls_openssl_ref_count == 0) {
 +#ifndef OPENSSL_NO_ENGINE
 +              ENGINE_cleanup();
 +#endif /* OPENSSL_NO_ENGINE */
 +              CRYPTO_cleanup_all_ex_data();
 +              ERR_remove_thread_state(NULL);
 +              ERR_free_strings();
 +              EVP_cleanup();
 +              os_free(tls_global->ocsp_stapling_response);
 +              tls_global->ocsp_stapling_response = NULL;
 +              os_free(tls_global);
 +              tls_global = NULL;
 +      }
++
++      os_free(data);
++}
++
++
++#ifndef OPENSSL_NO_ENGINE
++
++/* Cryptoki return values */
++#define CKR_PIN_INCORRECT 0x000000a0
++#define CKR_PIN_INVALID 0x000000a1
++#define CKR_PIN_LEN_RANGE 0x000000a2
++
++/* libp11 */
++#define ERR_LIB_PKCS11        ERR_LIB_USER
++
++static int tls_is_pin_error(unsigned int err)
++{
++      return ERR_GET_LIB(err) == ERR_LIB_PKCS11 &&
++              (ERR_GET_REASON(err) == CKR_PIN_INCORRECT ||
++               ERR_GET_REASON(err) == CKR_PIN_INVALID ||
++               ERR_GET_REASON(err) == CKR_PIN_LEN_RANGE);
 +}
 +
++#endif /* OPENSSL_NO_ENGINE */
++
 +
 +static int tls_engine_init(struct tls_connection *conn, const char *engine_id,
 +                         const char *pin, const char *key_id,
 +                         const char *cert_id, const char *ca_cert_id)
 +{
 +#ifndef OPENSSL_NO_ENGINE
 +      int ret = -1;
 +      if (engine_id == NULL) {
 +              wpa_printf(MSG_ERROR, "ENGINE: Engine 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]",
 +                         engine_id, ERR_error_string(ERR_get_error(), NULL));
 +              goto err;
 +      }
 +      if (ENGINE_init(conn->engine) != 1) {
 +              wpa_printf(MSG_ERROR, "ENGINE: engine init failed "
 +                         "(engine: %s) [%s]", engine_id,
 +                         ERR_error_string(ERR_get_error(), NULL));
 +              goto err;
 +      }
 +      wpa_printf(MSG_DEBUG, "ENGINE: engine initialized");
 +
 +#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;
 +      }
 +#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) {
++                      unsigned long err = ERR_get_error();
++
 +                      wpa_printf(MSG_ERROR,
 +                                 "ENGINE: cannot load private key with id '%s' [%s]",
 +                                 key_id,
-       SSL_CTX *ssl = ssl_ctx;
++                                 ERR_error_string(err, NULL));
++                      if (tls_is_pin_error(err))
++                              ret = TLS_SET_PARAMS_ENGINE_PRV_BAD_PIN;
++                      else
++                              ret = TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED;
 +                      goto err;
 +              }
 +      }
 +
 +      /* handle a certificate and/or CA certificate */
 +      if (cert_id || ca_cert_id) {
 +              const char *cmd_name = "LOAD_CERT_CTRL";
 +
 +              /* test if the engine supports a LOAD_CERT_CTRL */
 +              if (!ENGINE_ctrl(conn->engine, ENGINE_CTRL_GET_CMD_FROM_NAME,
 +                               0, (void *)cmd_name, NULL)) {
 +                      wpa_printf(MSG_ERROR, "ENGINE: engine does not support"
 +                                 " loading certificates");
 +                      ret = TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED;
 +                      goto err;
 +              }
 +      }
 +
 +      return 0;
 +
 +err:
 +      if (conn->engine) {
 +              ENGINE_free(conn->engine);
 +              conn->engine = NULL;
 +      }
 +
 +      if (conn->private_key) {
 +              EVP_PKEY_free(conn->private_key);
 +              conn->private_key = NULL;
 +      }
 +
 +      return ret;
 +#else /* OPENSSL_NO_ENGINE */
 +      return 0;
 +#endif /* OPENSSL_NO_ENGINE */
 +}
 +
 +
 +static void tls_engine_deinit(struct tls_connection *conn)
 +{
 +#ifndef OPENSSL_NO_ENGINE
 +      wpa_printf(MSG_DEBUG, "ENGINE: engine deinit");
 +      if (conn->private_key) {
 +              EVP_PKEY_free(conn->private_key);
 +              conn->private_key = NULL;
 +      }
 +      if (conn->engine) {
 +              ENGINE_finish(conn->engine);
 +              conn->engine = NULL;
 +      }
 +#endif /* OPENSSL_NO_ENGINE */
 +}
 +
 +
 +int tls_get_errors(void *ssl_ctx)
 +{
 +      int count = 0;
 +      unsigned long err;
 +
 +      while ((err = ERR_get_error())) {
 +              wpa_printf(MSG_INFO, "TLS - SSL error: %s",
 +                         ERR_error_string(err, NULL));
 +              count++;
 +      }
 +
 +      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)
 +{
- #ifdef OPENSSL_SUPPORTS_CTX_APP_DATA
++      struct tls_data *data = ssl_ctx;
++      SSL_CTX *ssl = data->ssl;
 +      struct tls_connection *conn;
 +      long options;
- #else /* OPENSSL_SUPPORTS_CTX_APP_DATA */
-       struct tls_context *context = tls_global;
- #endif /* OPENSSL_SUPPORTS_CTX_APP_DATA */
 +      struct tls_context *context = SSL_CTX_get_app_data(ssl);
-       conn->ssl_ctx = ssl_ctx;
 +
 +      conn = os_zalloc(sizeof(*conn));
 +      if (conn == NULL)
 +              return NULL;
-       return 0;
++      conn->ssl_ctx = ssl;
 +      conn->ssl = SSL_new(ssl);
 +      if (conn->ssl == NULL) {
 +              tls_show_errors(MSG_INFO, __func__,
 +                              "Failed to initialize new SSL connection");
 +              os_free(conn);
 +              return NULL;
 +      }
 +
 +      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
 +      options |= SSL_OP_NO_COMPRESSION;
 +#endif /* SSL_OP_NO_COMPRESSION */
 +      SSL_set_options(conn->ssl, options);
 +
 +      conn->ssl_in = BIO_new(BIO_s_mem());
 +      if (!conn->ssl_in) {
 +              tls_show_errors(MSG_INFO, __func__,
 +                              "Failed to create a new BIO for ssl_in");
 +              SSL_free(conn->ssl);
 +              os_free(conn);
 +              return NULL;
 +      }
 +
 +      conn->ssl_out = BIO_new(BIO_s_mem());
 +      if (!conn->ssl_out) {
 +              tls_show_errors(MSG_INFO, __func__,
 +                              "Failed to create a new BIO for ssl_out");
 +              SSL_free(conn->ssl);
 +              BIO_free(conn->ssl_in);
 +              os_free(conn);
 +              return NULL;
 +      }
 +
 +      SSL_set_bio(conn->ssl, conn->ssl_in, conn->ssl_out);
 +
 +      return conn;
 +}
 +
 +
 +void tls_connection_deinit(void *ssl_ctx, struct tls_connection *conn)
 +{
 +      if (conn == NULL)
 +              return;
++      if (conn->success_data) {
++              /*
++               * Make sure ssl_clear_bad_session() does not remove this
++               * session.
++               */
++              SSL_set_quiet_shutdown(conn->ssl, 1);
++              SSL_shutdown(conn->ssl);
++      }
 +      SSL_free(conn->ssl);
 +      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);
 +}
 +
 +
 +int tls_connection_established(void *ssl_ctx, struct tls_connection *conn)
 +{
 +      return conn ? SSL_is_init_finished(conn->ssl) : 0;
 +}
 +
 +
 +int tls_connection_shutdown(void *ssl_ctx, struct tls_connection *conn)
 +{
 +      if (conn == NULL)
 +              return -1;
 +
 +      /* Shutdown previous TLS connection without notifying the peer
 +       * because the connection was already terminated in practice
 +       * and "close notify" shutdown alert would confuse AS. */
 +      SSL_set_quiet_shutdown(conn->ssl, 1);
 +      SSL_shutdown(conn->ssl);
- static int tls_load_ca_der(void *_ssl_ctx, const char *ca_cert)
++      return SSL_clear(conn->ssl) == 1 ? 0 : -1;
 +}
 +
 +
 +static int tls_match_altsubject_component(X509 *cert, int type,
 +                                        const char *value, size_t len)
 +{
 +      GENERAL_NAME *gen;
 +      void *ext;
 +      int found = 0;
 +      stack_index_t i;
 +
 +      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);
 +              if (gen->type != type)
 +                      continue;
 +              if (os_strlen((char *) gen->d.ia5->data) == len &&
 +                  os_memcmp(value, gen->d.ia5->data, len) == 0)
 +                      found++;
 +      }
 +
 +      return found;
 +}
 +
 +
 +static int tls_match_altsubject(X509 *cert, const char *match)
 +{
 +      int type;
 +      const char *pos, *end;
 +      size_t len;
 +
 +      pos = match;
 +      do {
 +              if (os_strncmp(pos, "EMAIL:", 6) == 0) {
 +                      type = GEN_EMAIL;
 +                      pos += 6;
 +              } else if (os_strncmp(pos, "DNS:", 4) == 0) {
 +                      type = GEN_DNS;
 +                      pos += 4;
 +              } else if (os_strncmp(pos, "URI:", 4) == 0) {
 +                      type = GEN_URI;
 +                      pos += 4;
 +              } else {
 +                      wpa_printf(MSG_INFO, "TLS: Invalid altSubjectName "
 +                                 "match '%s'", pos);
 +                      return 0;
 +              }
 +              end = os_strchr(pos, ';');
 +              while (end) {
 +                      if (os_strncmp(end + 1, "EMAIL:", 6) == 0 ||
 +                          os_strncmp(end + 1, "DNS:", 4) == 0 ||
 +                          os_strncmp(end + 1, "URI:", 4) == 0)
 +                              break;
 +                      end = os_strchr(end + 1, ';');
 +              }
 +              if (end)
 +                      len = end - pos;
 +              else
 +                      len = os_strlen(pos);
 +              if (tls_match_altsubject_component(cert, type, pos, len) > 0)
 +                      return 1;
 +              pos = end + 1;
 +      } while (end);
 +
 +      return 0;
 +}
 +
 +
 +#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) {
 +      case X509_V_ERR_CERT_REVOKED:
 +              return TLS_FAIL_REVOKED;
 +      case X509_V_ERR_CERT_NOT_YET_VALID:
 +      case X509_V_ERR_CRL_NOT_YET_VALID:
 +              return TLS_FAIL_NOT_YET_VALID;
 +      case X509_V_ERR_CERT_HAS_EXPIRED:
 +      case X509_V_ERR_CRL_HAS_EXPIRED:
 +              return TLS_FAIL_EXPIRED;
 +      case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
 +      case X509_V_ERR_UNABLE_TO_GET_CRL:
 +      case X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER:
 +      case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
 +      case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
 +      case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
 +      case X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE:
 +      case X509_V_ERR_CERT_CHAIN_TOO_LONG:
 +      case X509_V_ERR_PATH_LENGTH_EXCEEDED:
 +      case X509_V_ERR_INVALID_CA:
 +              return TLS_FAIL_UNTRUSTED;
 +      case X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE:
 +      case X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE:
 +      case X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY:
 +      case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
 +      case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
 +      case X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD:
 +      case X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD:
 +      case X509_V_ERR_CERT_UNTRUSTED:
 +      case X509_V_ERR_CERT_REJECTED:
 +              return TLS_FAIL_BAD_CERTIFICATE;
 +      default:
 +              return TLS_FAIL_UNSPECIFIED;
 +      }
 +}
 +
 +
 +static struct wpabuf * get_x509_cert(X509 *cert)
 +{
 +      struct wpabuf *buf;
 +      u8 *tmp;
 +
 +      int cert_len = i2d_X509(cert, NULL);
 +      if (cert_len <= 0)
 +              return NULL;
 +
 +      buf = wpabuf_alloc(cert_len);
 +      if (buf == NULL)
 +              return NULL;
 +
 +      tmp = wpabuf_put(buf, cert_len);
 +      i2d_X509(cert, &tmp);
 +      return buf;
 +}
 +
 +
 +static void openssl_tls_fail_event(struct tls_connection *conn,
 +                                 X509 *err_cert, int err, int depth,
 +                                 const char *subject, const char *err_str,
 +                                 enum tls_fail_reason reason)
 +{
 +      union tls_event_data ev;
 +      struct wpabuf *cert = NULL;
 +      struct tls_context *context = conn->context;
 +
 +      if (context->event_cb == NULL)
 +              return;
 +
 +      cert = get_x509_cert(err_cert);
 +      os_memset(&ev, 0, sizeof(ev));
 +      ev.cert_fail.reason = reason != TLS_FAIL_UNSPECIFIED ?
 +              reason : openssl_tls_fail_reason(err);
 +      ev.cert_fail.depth = depth;
 +      ev.cert_fail.subject = subject;
 +      ev.cert_fail.reason_txt = err_str;
 +      ev.cert_fail.cert = cert;
 +      context->event_cb(context->cb_ctx, TLS_CERT_CHAIN_FAILURE, &ev);
 +      wpabuf_free(cert);
 +}
 +
 +
 +static void openssl_tls_cert_event(struct tls_connection *conn,
 +                                 X509 *err_cert, int depth,
 +                                 const char *subject)
 +{
 +      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 */
 +
 +      if (context->event_cb == NULL)
 +              return;
 +
 +      os_memset(&ev, 0, sizeof(ev));
 +      if (conn->cert_probe || context->cert_in_cb) {
 +              cert = get_x509_cert(err_cert);
 +              ev.peer_cert.cert = cert;
 +      }
 +#ifdef CONFIG_SHA256
 +      if (cert) {
 +              const u8 *addr[1];
 +              size_t len[1];
 +              addr[0] = wpabuf_head(cert);
 +              len[0] = wpabuf_len(cert);
 +              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 = 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]);
 +}
 +
 +
 +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;
 +      struct tls_context *context;
 +      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,
 +                                       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);
 +      if (conn == NULL)
 +              return 0;
 +
 +      if (depth == 0)
 +              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;
 +      if (!preverify_ok && depth > 0 && conn->server_cert_only)
 +              preverify_ok = 1;
 +      if (!preverify_ok && (conn->flags & TLS_CONN_DISABLE_TIME_CHECKS) &&
 +          (err == X509_V_ERR_CERT_HAS_EXPIRED ||
 +           err == X509_V_ERR_CERT_NOT_YET_VALID)) {
 +              wpa_printf(MSG_DEBUG, "OpenSSL: Ignore certificate validity "
 +                         "time mismatch");
 +              preverify_ok = 1;
 +      }
 +
 +      err_str = X509_verify_cert_error_string(err);
 +
 +#ifdef CONFIG_SHA256
 +      /*
 +       * 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) {
 +                      wpa_printf(MSG_DEBUG, "OpenSSL: Could not fetch "
 +                                 "server certificate data");
 +                      preverify_ok = 0;
 +              } else {
 +                      u8 hash[32];
 +                      const u8 *addr[1];
 +                      size_t len[1];
 +                      addr[0] = wpabuf_head(cert);
 +                      len[0] = wpabuf_len(cert);
 +                      if (sha256_vector(1, addr, len, hash) < 0 ||
 +                          os_memcmp(conn->srv_cert_hash, hash, 32) != 0) {
 +                              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);
 +              }
 +      }
 +#endif /* CONFIG_SHA256 */
 +
 +      if (!preverify_ok) {
 +              wpa_printf(MSG_WARNING, "TLS: Certificate verification failed,"
 +                         " error %d (%s) depth %d for '%s'", err, err_str,
 +                         depth, buf);
 +              openssl_tls_fail_event(conn, err_cert, err, depth, buf,
 +                                     err_str, TLS_FAIL_UNSPECIFIED);
 +              return preverify_ok;
 +      }
 +
 +      wpa_printf(MSG_DEBUG, "TLS: tls_verify_cb - preverify_ok=%d "
 +                 "err=%d (%s) ca_cert_verify=%d depth=%d buf='%s'",
 +                 preverify_ok, err, err_str,
 +                 conn->ca_cert_verify, depth, buf);
 +      if (depth == 0 && match && os_strstr(buf, match) == NULL) {
 +              wpa_printf(MSG_WARNING, "TLS: Subject '%s' did not "
 +                         "match with '%s'", buf, match);
 +              preverify_ok = 0;
 +              openssl_tls_fail_event(conn, err_cert, err, depth, buf,
 +                                     "Subject mismatch",
 +                                     TLS_FAIL_SUBJECT_MISMATCH);
 +      } 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;
 +              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);
 +
 +      if (conn->cert_probe && preverify_ok && depth == 0) {
 +              wpa_printf(MSG_DEBUG, "OpenSSL: Reject server certificate "
 +                         "on probe-only run");
 +              preverify_ok = 0;
 +              openssl_tls_fail_event(conn, err_cert, err, depth, buf,
 +                                     "Server certificate chain probe",
 +                                     TLS_FAIL_SERVER_CHAIN_PROBE);
 +      }
 +
 +      if (preverify_ok && context->event_cb != NULL)
 +              context->event_cb(context->cb_ctx,
 +                                TLS_CERT_CHAIN_SUCCESS, NULL);
 +
 +      return preverify_ok;
 +}
 +
 +
 +#ifndef OPENSSL_NO_STDIO
-       SSL_CTX *ssl_ctx = _ssl_ctx;
++static int tls_load_ca_der(struct tls_data *data, const char *ca_cert)
 +{
- static int tls_connection_ca_cert(void *_ssl_ctx, struct tls_connection *conn,
++      SSL_CTX *ssl_ctx = data->ssl;
 +      X509_LOOKUP *lookup;
 +      int ret = 0;
 +
 +      lookup = X509_STORE_add_lookup(SSL_CTX_get_cert_store(ssl_ctx),
 +                                     X509_LOOKUP_file());
 +      if (lookup == NULL) {
 +              tls_show_errors(MSG_WARNING, __func__,
 +                              "Failed add lookup for X509 store");
 +              return -1;
 +      }
 +
 +      if (!X509_LOOKUP_load_file(lookup, ca_cert, X509_FILETYPE_ASN1)) {
 +              unsigned long err = ERR_peek_error();
 +              tls_show_errors(MSG_WARNING, __func__,
 +                              "Failed load CA in DER format");
 +              if (ERR_GET_LIB(err) == ERR_LIB_X509 &&
 +                  ERR_GET_REASON(err) == X509_R_CERT_ALREADY_IN_HASH_TABLE) {
 +                      wpa_printf(MSG_DEBUG, "OpenSSL: %s - ignoring "
 +                                 "cert already in hash table error",
 +                                 __func__);
 +              } else
 +                      ret = -1;
 +      }
 +
 +      return ret;
 +}
 +#endif /* OPENSSL_NO_STDIO */
 +
 +
-       SSL_CTX *ssl_ctx = _ssl_ctx;
++static int tls_connection_ca_cert(struct tls_data *data,
++                                struct tls_connection *conn,
 +                                const char *ca_cert, const u8 *ca_cert_blob,
 +                                size_t ca_cert_blob_len, const char *ca_path)
 +{
-                           tls_load_ca_der(ssl_ctx, ca_cert) == 0) {
++      SSL_CTX *ssl_ctx = data->ssl;
 +      X509_STORE *store;
 +
 +      /*
 +       * Remove previously configured trusted CA certificates before adding
 +       * new ones.
 +       */
 +      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;
 +
 +      if (ca_cert && os_strncmp(ca_cert, "probe://", 8) == 0) {
 +              wpa_printf(MSG_DEBUG, "OpenSSL: Probe for server certificate "
 +                         "chain");
 +              conn->cert_probe = 1;
 +              conn->ca_cert_verify = 0;
 +              return 0;
 +      }
 +
 +      if (ca_cert && os_strncmp(ca_cert, "hash://", 7) == 0) {
 +#ifdef CONFIG_SHA256
 +              const char *pos = ca_cert + 7;
 +              if (os_strncmp(pos, "server/sha256/", 14) != 0) {
 +                      wpa_printf(MSG_DEBUG, "OpenSSL: Unsupported ca_cert "
 +                                 "hash value '%s'", ca_cert);
 +                      return -1;
 +              }
 +              pos += 14;
 +              if (os_strlen(pos) != 32 * 2) {
 +                      wpa_printf(MSG_DEBUG, "OpenSSL: Unexpected SHA256 "
 +                                 "hash length in ca_cert '%s'", ca_cert);
 +                      return -1;
 +              }
 +              if (hexstr2bin(pos, conn->srv_cert_hash, 32) < 0) {
 +                      wpa_printf(MSG_DEBUG, "OpenSSL: Invalid SHA256 hash "
 +                                 "value in ca_cert '%s'", ca_cert);
 +                      return -1;
 +              }
 +              conn->server_cert_only = 1;
 +              wpa_printf(MSG_DEBUG, "OpenSSL: Checking only server "
 +                         "certificate match");
 +              return 0;
 +#else /* CONFIG_SHA256 */
 +              wpa_printf(MSG_INFO, "No SHA256 included in the build - "
 +                         "cannot validate server certificate hash");
 +              return -1;
 +#endif /* CONFIG_SHA256 */
 +      }
 +
 +      if (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__,
 +                                      "Failed to parse ca_cert_blob");
 +                      return -1;
 +              }
 +
 +              if (!X509_STORE_add_cert(SSL_CTX_get_cert_store(ssl_ctx),
 +                                       cert)) {
 +                      unsigned long err = ERR_peek_error();
 +                      tls_show_errors(MSG_WARNING, __func__,
 +                                      "Failed to add ca_cert_blob to "
 +                                      "certificate store");
 +                      if (ERR_GET_LIB(err) == ERR_LIB_X509 &&
 +                          ERR_GET_REASON(err) ==
 +                          X509_R_CERT_ALREADY_IN_HASH_TABLE) {
 +                              wpa_printf(MSG_DEBUG, "OpenSSL: %s - ignoring "
 +                                         "cert already in hash table error",
 +                                         __func__);
 +                      } else {
 +                              X509_free(cert);
 +                              return -1;
 +                      }
 +              }
 +              X509_free(cert);
 +              wpa_printf(MSG_DEBUG, "OpenSSL: %s - added ca_cert_blob "
 +                         "to certificate store", __func__);
 +              return 0;
 +      }
 +
 +#ifdef ANDROID
 +      if (ca_cert && os_strncmp("keystore://", ca_cert, 11) == 0) {
 +              BIO *bio = BIO_from_keystore(&ca_cert[11]);
 +              STACK_OF(X509_INFO) *stack = NULL;
 +              stack_index_t i;
 +
 +              if (bio) {
 +                      stack = PEM_X509_INFO_read_bio(bio, NULL, NULL, NULL);
 +                      BIO_free(bio);
 +              }
 +              if (!stack)
 +                      return -1;
 +
 +              for (i = 0; i < sk_X509_INFO_num(stack); ++i) {
 +                      X509_INFO *info = sk_X509_INFO_value(stack, i);
 +                      if (info->x509) {
 +                              X509_STORE_add_cert(ssl_ctx->cert_store,
 +                                                  info->x509);
 +                      }
 +                      if (info->crl) {
 +                              X509_STORE_add_crl(ssl_ctx->cert_store,
 +                                                 info->crl);
 +                      }
 +              }
 +              sk_X509_INFO_pop_free(stack, X509_INFO_free);
 +              SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, tls_verify_cb);
 +              return 0;
 +      }
 +#endif /* ANDROID */
 +
 +#ifdef CONFIG_NATIVE_WINDOWS
 +      if (ca_cert && tls_cryptoapi_ca_cert(ssl_ctx, conn->ssl, ca_cert) ==
 +          0) {
 +              wpa_printf(MSG_DEBUG, "OpenSSL: Added CA certificates from "
 +                         "system certificate store");
 +              return 0;
 +      }
 +#endif /* CONFIG_NATIVE_WINDOWS */
 +
 +      if (ca_cert || ca_path) {
 +#ifndef OPENSSL_NO_STDIO
 +              if (SSL_CTX_load_verify_locations(ssl_ctx, ca_cert, ca_path) !=
 +                  1) {
 +                      tls_show_errors(MSG_WARNING, __func__,
 +                                      "Failed to load root certificates");
 +                      if (ca_cert &&
-                       tls_get_errors(ssl_ctx);
++                          tls_load_ca_der(data, ca_cert) == 0) {
 +                              wpa_printf(MSG_DEBUG, "OpenSSL: %s - loaded "
 +                                         "DER format CA certificate",
 +                                         __func__);
 +                      } else
 +                              return -1;
 +              } else {
 +                      wpa_printf(MSG_DEBUG, "TLS: Trusted root "
 +                                 "certificate(s) loaded");
- static int tls_global_ca_cert(SSL_CTX *ssl_ctx, const char *ca_cert)
++                      tls_get_errors(data);
 +              }
 +#else /* OPENSSL_NO_STDIO */
 +              wpa_printf(MSG_DEBUG, "OpenSSL: %s - OPENSSL_NO_STDIO",
 +                         __func__);
 +              return -1;
 +#endif /* OPENSSL_NO_STDIO */
 +      } else {
 +              /* No ca_cert configured - do not try to verify server
 +               * certificate */
 +              conn->ca_cert_verify = 0;
 +      }
 +
 +      return 0;
 +}
 +
 +
-               X509_STORE *cs = SSL_CTX_get_cert_store(ssl_ctx);
++static int tls_global_ca_cert(struct tls_data *data, const char *ca_cert)
 +{
++      SSL_CTX *ssl_ctx = data->ssl;
++
 +      if (ca_cert) {
 +              if (SSL_CTX_load_verify_locations(ssl_ctx, ca_cert, NULL) != 1)
 +              {
 +                      tls_show_errors(MSG_WARNING, __func__,
 +                                      "Failed to load root certificates");
 +                      return -1;
 +              }
 +
 +              wpa_printf(MSG_DEBUG, "TLS: Trusted root "
 +                         "certificate(s) loaded");
 +
 +#ifndef OPENSSL_NO_STDIO
 +              /* Add the same CAs to the client certificate requests */
 +              SSL_CTX_set_client_CA_list(ssl_ctx,
 +                                         SSL_load_client_CA_file(ca_cert));
 +#endif /* OPENSSL_NO_STDIO */
 +      }
 +
 +      return 0;
 +}
 +
 +
 +int tls_global_set_verify(void *ssl_ctx, int check_crl)
 +{
 +      int flags;
 +
 +      if (check_crl) {
-                             int verify_peer)
++              struct tls_data *data = ssl_ctx;
++              X509_STORE *cs = SSL_CTX_get_cert_store(data->ssl);
 +              if (cs == NULL) {
 +                      tls_show_errors(MSG_INFO, __func__, "Failed to get "
 +                                      "certificate store when enabling "
 +                                      "check_crl");
 +                      return -1;
 +              }
 +              flags = X509_V_FLAG_CRL_CHECK;
 +              if (check_crl == 2)
 +                      flags |= X509_V_FLAG_CRL_CHECK_ALL;
 +              X509_STORE_set_flags(cs, flags);
 +      }
 +      return 0;
 +}
 +
 +
 +static int tls_connection_set_subject_match(struct tls_connection *conn,
 +                                          const char *subject_match,
 +                                          const char *altsubject_match,
 +                                          const char *suffix_match,
 +                                          const char *domain_match)
 +{
 +      os_free(conn->subject_match);
 +      conn->subject_match = NULL;
 +      if (subject_match) {
 +              conn->subject_match = os_strdup(subject_match);
 +              if (conn->subject_match == NULL)
 +                      return -1;
 +      }
 +
 +      os_free(conn->altsubject_match);
 +      conn->altsubject_match = NULL;
 +      if (altsubject_match) {
 +              conn->altsubject_match = os_strdup(altsubject_match);
 +              if (conn->altsubject_match == NULL)
 +                      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;
 +}
 +
 +
++static void tls_set_conn_flags(SSL *ssl, unsigned int flags)
++{
++#ifdef SSL_OP_NO_TICKET
++      if (flags & TLS_CONN_DISABLE_SESSION_TICKET)
++              SSL_set_options(ssl, SSL_OP_NO_TICKET);
++#ifdef SSL_clear_options
++      else
++              SSL_clear_options(ssl, SSL_OP_NO_TICKET);
++#endif /* SSL_clear_options */
++#endif /* SSL_OP_NO_TICKET */
++
++#ifdef SSL_OP_NO_TLSv1
++      if (flags & TLS_CONN_DISABLE_TLSv1_0)
++              SSL_set_options(ssl, SSL_OP_NO_TLSv1);
++      else
++              SSL_clear_options(ssl, SSL_OP_NO_TLSv1);
++#endif /* SSL_OP_NO_TLSv1 */
++#ifdef SSL_OP_NO_TLSv1_1
++      if (flags & TLS_CONN_DISABLE_TLSv1_1)
++              SSL_set_options(ssl, SSL_OP_NO_TLSv1_1);
++      else
++              SSL_clear_options(ssl, SSL_OP_NO_TLSv1_1);
++#endif /* SSL_OP_NO_TLSv1_1 */
++#ifdef SSL_OP_NO_TLSv1_2
++      if (flags & TLS_CONN_DISABLE_TLSv1_2)
++              SSL_set_options(ssl, SSL_OP_NO_TLSv1_2);
++      else
++              SSL_clear_options(ssl, SSL_OP_NO_TLSv1_2);
++#endif /* SSL_OP_NO_TLSv1_2 */
++}
++
++
 +int tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn,
-       /*
-        * Set session id context in order to avoid fatal errors when client
-        * tries to resume a session. However, set the context to a unique
-        * value in order to effectively disable session resumption for now
-        * since not all areas of the server code are ready for it (e.g.,
-        * EAP-TTLS needs special handling for Phase 2 after abbreviated TLS
-        * handshake).
-        */
-       counter++;
-       SSL_set_session_id_context(conn->ssl,
-                                  (const unsigned char *) &counter,
-                                  sizeof(counter));
++                            int verify_peer, unsigned int flags,
++                            const u8 *session_ctx, size_t session_ctx_len)
 +{
 +      static int counter = 0;
++      struct tls_data *data = ssl_ctx;
 +
 +      if (conn == NULL)
 +              return -1;
 +
 +      if (verify_peer) {
 +              conn->ca_cert_verify = 1;
 +              SSL_set_verify(conn->ssl, SSL_VERIFY_PEER |
 +                             SSL_VERIFY_FAIL_IF_NO_PEER_CERT |
 +                             SSL_VERIFY_CLIENT_ONCE, tls_verify_cb);
 +      } else {
 +              conn->ca_cert_verify = 0;
 +              SSL_set_verify(conn->ssl, SSL_VERIFY_NONE, NULL);
 +      }
 +
++      tls_set_conn_flags(conn->ssl, flags);
++      conn->flags = flags;
++
 +      SSL_set_accept_state(conn->ssl);
 +
- static int tls_global_client_cert(SSL_CTX *ssl_ctx, const char *client_cert)
++      if (data->tls_session_lifetime == 0) {
++              /*
++               * Set session id context to a unique value to make sure
++               * session resumption cannot be used either through session
++               * caching or TLS ticket extension.
++               */
++              counter++;
++              SSL_set_session_id_context(conn->ssl,
++                                         (const unsigned char *) &counter,
++                                         sizeof(counter));
++      } else if (session_ctx) {
++              SSL_set_session_id_context(conn->ssl, session_ctx,
++                                         session_ctx_len);
++      }
 +
 +      return 0;
 +}
 +
 +
 +static int tls_connection_client_cert(struct tls_connection *conn,
 +                                    const char *client_cert,
 +                                    const u8 *client_cert_blob,
 +                                    size_t client_cert_blob_len)
 +{
 +      if (client_cert == NULL && client_cert_blob == NULL)
 +              return 0;
 +
 +      if (client_cert_blob &&
 +          SSL_use_certificate_ASN1(conn->ssl, (u8 *) client_cert_blob,
 +                                   client_cert_blob_len) == 1) {
 +              wpa_printf(MSG_DEBUG, "OpenSSL: SSL_use_certificate_ASN1 --> "
 +                         "OK");
 +              return 0;
 +      } else if (client_cert_blob) {
 +              tls_show_errors(MSG_DEBUG, __func__,
 +                              "SSL_use_certificate_ASN1 failed");
 +      }
 +
 +      if (client_cert == NULL)
 +              return -1;
 +
 +#ifdef ANDROID
 +      if (os_strncmp("keystore://", client_cert, 11) == 0) {
 +              BIO *bio = BIO_from_keystore(&client_cert[11]);
 +              X509 *x509 = NULL;
 +              int ret = -1;
 +              if (bio) {
 +                      x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL);
 +                      BIO_free(bio);
 +              }
 +              if (x509) {
 +                      if (SSL_use_certificate(conn->ssl, x509) == 1)
 +                              ret = 0;
 +                      X509_free(x509);
 +              }
 +              return ret;
 +      }
 +#endif /* ANDROID */
 +
 +#ifndef OPENSSL_NO_STDIO
 +      if (SSL_use_certificate_file(conn->ssl, client_cert,
 +                                   SSL_FILETYPE_ASN1) == 1) {
 +              wpa_printf(MSG_DEBUG, "OpenSSL: SSL_use_certificate_file (DER)"
 +                         " --> OK");
 +              return 0;
 +      }
 +
 +      if (SSL_use_certificate_file(conn->ssl, client_cert,
 +                                   SSL_FILETYPE_PEM) == 1) {
 +              ERR_clear_error();
 +              wpa_printf(MSG_DEBUG, "OpenSSL: SSL_use_certificate_file (PEM)"
 +                         " --> OK");
 +              return 0;
 +      }
 +
 +      tls_show_errors(MSG_DEBUG, __func__,
 +                      "SSL_use_certificate_file failed");
 +#else /* OPENSSL_NO_STDIO */
 +      wpa_printf(MSG_DEBUG, "OpenSSL: %s - OPENSSL_NO_STDIO", __func__);
 +#endif /* OPENSSL_NO_STDIO */
 +
 +      return -1;
 +}
 +
 +
- static int tls_parse_pkcs12(SSL_CTX *ssl_ctx, SSL *ssl, PKCS12 *p12,
++static int tls_global_client_cert(struct tls_data *data,
++                                const char *client_cert)
 +{
 +#ifndef OPENSSL_NO_STDIO
++      SSL_CTX *ssl_ctx = data->ssl;
++
 +      if (client_cert == NULL)
 +              return 0;
 +
 +      if (SSL_CTX_use_certificate_file(ssl_ctx, client_cert,
 +                                       SSL_FILETYPE_ASN1) != 1 &&
 +          SSL_CTX_use_certificate_chain_file(ssl_ctx, client_cert) != 1 &&
 +          SSL_CTX_use_certificate_file(ssl_ctx, client_cert,
 +                                       SSL_FILETYPE_PEM) != 1) {
 +              tls_show_errors(MSG_INFO, __func__,
 +                              "Failed to load client certificate");
 +              return -1;
 +      }
 +      return 0;
 +#else /* OPENSSL_NO_STDIO */
 +      if (client_cert == NULL)
 +              return 0;
 +      wpa_printf(MSG_DEBUG, "OpenSSL: %s - OPENSSL_NO_STDIO", __func__);
 +      return -1;
 +#endif /* OPENSSL_NO_STDIO */
 +}
 +
 +
 +static int tls_passwd_cb(char *buf, int size, int rwflag, void *password)
 +{
 +      if (password == NULL) {
 +              return 0;
 +      }
 +      os_strlcpy(buf, (char *) password, size);
 +      return os_strlen(buf);
 +}
 +
 +
 +#ifdef PKCS12_FUNCS
-                       if (SSL_CTX_use_certificate(ssl_ctx, cert) != 1)
++static int tls_parse_pkcs12(struct tls_data *data, SSL *ssl, PKCS12 *p12,
 +                          const char *passwd)
 +{
 +      EVP_PKEY *pkey;
 +      X509 *cert;
 +      STACK_OF(X509) *certs;
 +      int res = 0;
 +      char buf[256];
 +
 +      pkey = NULL;
 +      cert = NULL;
 +      certs = NULL;
++      if (!passwd)
++              passwd = "";
 +      if (!PKCS12_parse(p12, passwd, &pkey, &cert, &certs)) {
 +              tls_show_errors(MSG_DEBUG, __func__,
 +                              "Failed to parse PKCS12 file");
 +              PKCS12_free(p12);
 +              return -1;
 +      }
 +      wpa_printf(MSG_DEBUG, "TLS: Successfully parsed PKCS12 data");
 +
 +      if (cert) {
 +              X509_NAME_oneline(X509_get_subject_name(cert), buf,
 +                                sizeof(buf));
 +              wpa_printf(MSG_DEBUG, "TLS: Got certificate from PKCS12: "
 +                         "subject='%s'", buf);
 +              if (ssl) {
 +                      if (SSL_use_certificate(ssl, cert) != 1)
 +                              res = -1;
 +              } else {
-                       if (SSL_CTX_use_PrivateKey(ssl_ctx, pkey) != 1)
++                      if (SSL_CTX_use_certificate(data->ssl, cert) != 1)
 +                              res = -1;
 +              }
 +              X509_free(cert);
 +      }
 +
 +      if (pkey) {
 +              wpa_printf(MSG_DEBUG, "TLS: Got private key from PKCS12");
 +              if (ssl) {
 +                      if (SSL_use_PrivateKey(ssl, pkey) != 1)
 +                              res = -1;
 +              } else {
-                       if (SSL_CTX_add_extra_chain_cert(ssl_ctx, cert) != 1) {
++                      if (SSL_CTX_use_PrivateKey(data->ssl, pkey) != 1)
 +                              res = -1;
 +              }
 +              EVP_PKEY_free(pkey);
 +      }
 +
 +      if (certs) {
++#if OPENSSL_VERSION_NUMBER >= 0x10002000L
++              SSL_clear_chain_certs(ssl);
++              while ((cert = sk_X509_pop(certs)) != NULL) {
++                      X509_NAME_oneline(X509_get_subject_name(cert), buf,
++                                        sizeof(buf));
++                      wpa_printf(MSG_DEBUG, "TLS: additional certificate"
++                                 " from PKCS12: subject='%s'", buf);
++                      if (SSL_add1_chain_cert(ssl, cert) != 1) {
++                              tls_show_errors(MSG_DEBUG, __func__,
++                                              "Failed to add additional certificate");
++                              res = -1;
++                              break;
++                      }
++              }
++              if (!res) {
++                      /* Try to continue anyway */
++              }
++              sk_X509_free(certs);
++#ifndef OPENSSL_IS_BORINGSSL
++              res = SSL_build_cert_chain(ssl,
++                                         SSL_BUILD_CHAIN_FLAG_CHECK |
++                                         SSL_BUILD_CHAIN_FLAG_IGNORE_ERROR);
++              if (!res) {
++                      tls_show_errors(MSG_DEBUG, __func__,
++                                      "Failed to build certificate chain");
++              } else if (res == 2) {
++                      wpa_printf(MSG_DEBUG,
++                                 "TLS: Ignore certificate chain verification error when building chain with PKCS#12 extra certificates");
++              }
++#endif /* OPENSSL_IS_BORINGSSL */
++              /*
++               * Try to continue regardless of result since it is possible for
++               * the extra certificates not to be required.
++               */
++              res = 0;
++#else /* OPENSSL_VERSION_NUMBER >= 0x10002000L */
++#if OPENSSL_VERSION_NUMBER >= 0x10001000L
++              SSL_CTX_clear_extra_chain_certs(data->ssl);
++#endif /* OPENSSL_VERSION_NUMBER >= 0x10001000L */
 +              while ((cert = sk_X509_pop(certs)) != NULL) {
 +                      X509_NAME_oneline(X509_get_subject_name(cert), buf,
 +                                        sizeof(buf));
 +                      wpa_printf(MSG_DEBUG, "TLS: additional certificate"
 +                                 " from PKCS12: subject='%s'", buf);
 +                      /*
 +                       * There is no SSL equivalent for the chain cert - so
 +                       * always add it to the context...
 +                       */
-               tls_get_errors(ssl_ctx);
++                      if (SSL_CTX_add_extra_chain_cert(data->ssl, cert) != 1)
++                      {
 +                              res = -1;
 +                              break;
 +                      }
 +              }
 +              sk_X509_free(certs);
++#endif /* OPENSSL_VERSION_NUMBER >= 0x10002000L */
 +      }
 +
 +      PKCS12_free(p12);
 +
 +      if (res < 0)
- static int tls_read_pkcs12(SSL_CTX *ssl_ctx, SSL *ssl, const char *private_key,
-                          const char *passwd)
++              tls_get_errors(data);
 +
 +      return res;
 +}
 +#endif  /* PKCS12_FUNCS */
 +
 +
-       return tls_parse_pkcs12(ssl_ctx, ssl, p12, passwd);
++static int tls_read_pkcs12(struct tls_data *data, SSL *ssl,
++                         const char *private_key, const char *passwd)
 +{
 +#ifdef PKCS12_FUNCS
 +      FILE *f;
 +      PKCS12 *p12;
 +
 +      f = fopen(private_key, "rb");
 +      if (f == NULL)
 +              return -1;
 +
 +      p12 = d2i_PKCS12_fp(f, NULL);
 +      fclose(f);
 +
 +      if (p12 == NULL) {
 +              tls_show_errors(MSG_INFO, __func__,
 +                              "Failed to use PKCS#12 file");
 +              return -1;
 +      }
 +
- static int tls_read_pkcs12_blob(SSL_CTX *ssl_ctx, SSL *ssl,
++      return tls_parse_pkcs12(data, ssl, p12, passwd);
 +
 +#else /* PKCS12_FUNCS */
 +      wpa_printf(MSG_INFO, "TLS: PKCS12 support disabled - cannot read "
 +                 "p12/pfx files");
 +      return -1;
 +#endif  /* PKCS12_FUNCS */
 +}
 +
 +
-       return tls_parse_pkcs12(ssl_ctx, ssl, p12, passwd);
++static int tls_read_pkcs12_blob(struct tls_data *data, SSL *ssl,
 +                              const u8 *blob, size_t len, const char *passwd)
 +{
 +#ifdef PKCS12_FUNCS
 +      PKCS12 *p12;
 +
 +      p12 = d2i_PKCS12(NULL, (const unsigned char **) &blob, len);
 +      if (p12 == NULL) {
 +              tls_show_errors(MSG_INFO, __func__,
 +                              "Failed to use PKCS#12 blob");
 +              return -1;
 +      }
 +
-                          ERR_error_string(ERR_get_error(), NULL));
++      return tls_parse_pkcs12(data, ssl, p12, passwd);
 +
 +#else /* PKCS12_FUNCS */
 +      wpa_printf(MSG_INFO, "TLS: PKCS12 support disabled - cannot parse "
 +                 "p12/pfx blobs");
 +      return -1;
 +#endif  /* PKCS12_FUNCS */
 +}
 +
 +
 +#ifndef OPENSSL_NO_ENGINE
 +static int tls_engine_get_cert(struct tls_connection *conn,
 +                             const char *cert_id,
 +                             X509 **cert)
 +{
 +      /* this runs after the private key is loaded so no PIN is required */
 +      struct {
 +              const char *cert_id;
 +              X509 *cert;
 +      } params;
 +      params.cert_id = cert_id;
 +      params.cert = NULL;
 +
 +      if (!ENGINE_ctrl_cmd(conn->engine, "LOAD_CERT_CTRL",
 +                           0, &params, NULL, 1)) {
++              unsigned long err = ERR_get_error();
++
 +              wpa_printf(MSG_ERROR, "ENGINE: cannot load client cert with id"
 +                         " '%s' [%s]", cert_id,
- static int tls_connection_engine_ca_cert(void *_ssl_ctx,
++                         ERR_error_string(err, NULL));
++              if (tls_is_pin_error(err))
++                      return TLS_SET_PARAMS_ENGINE_PRV_BAD_PIN;
 +              return TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED;
 +      }
 +      if (!params.cert) {
 +              wpa_printf(MSG_ERROR, "ENGINE: did not properly cert with id"
 +                         " '%s'", cert_id);
 +              return TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED;
 +      }
 +      *cert = params.cert;
 +      return 0;
 +}
 +#endif /* OPENSSL_NO_ENGINE */
 +
 +
 +static int tls_connection_engine_client_cert(struct tls_connection *conn,
 +                                           const char *cert_id)
 +{
 +#ifndef OPENSSL_NO_ENGINE
 +      X509 *cert;
 +
 +      if (tls_engine_get_cert(conn, cert_id, &cert))
 +              return -1;
 +
 +      if (!SSL_use_certificate(conn->ssl, cert)) {
 +              tls_show_errors(MSG_ERROR, __func__,
 +                              "SSL_use_certificate failed");
 +                X509_free(cert);
 +              return -1;
 +      }
 +      X509_free(cert);
 +      wpa_printf(MSG_DEBUG, "ENGINE: SSL_use_certificate --> "
 +                 "OK");
 +      return 0;
 +
 +#else /* OPENSSL_NO_ENGINE */
 +      return -1;
 +#endif /* OPENSSL_NO_ENGINE */
 +}
 +
 +
-       SSL_CTX *ssl_ctx = _ssl_ctx;
++static int tls_connection_engine_ca_cert(struct tls_data *data,
 +                                       struct tls_connection *conn,
 +                                       const char *ca_cert_id)
 +{
 +#ifndef OPENSSL_NO_ENGINE
 +      X509 *cert;
- static int tls_connection_private_key(void *_ssl_ctx,
++      SSL_CTX *ssl_ctx = data->ssl;
 +      X509_STORE *store;
 +
 +      if (tls_engine_get_cert(conn, ca_cert_id, &cert))
 +              return -1;
 +
 +      /* start off the same as tls_connection_ca_cert */
 +      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;
 +      }
 +      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 "
 +                              "to certificate store");
 +              if (ERR_GET_LIB(err) == ERR_LIB_X509 &&
 +                  ERR_GET_REASON(err) == X509_R_CERT_ALREADY_IN_HASH_TABLE) {
 +                      wpa_printf(MSG_DEBUG, "OpenSSL: %s - ignoring cert"
 +                                 " already in hash table error",
 +                                 __func__);
 +              } else {
 +                      X509_free(cert);
 +                      return -1;
 +              }
 +      }
 +      X509_free(cert);
 +      wpa_printf(MSG_DEBUG, "OpenSSL: %s - added CA certificate from engine "
 +                 "to certificate store", __func__);
 +      SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, tls_verify_cb);
 +      conn->ca_cert_verify = 1;
 +
 +      return 0;
 +
 +#else /* OPENSSL_NO_ENGINE */
 +      return -1;
 +#endif /* OPENSSL_NO_ENGINE */
 +}
 +
 +
 +static int tls_connection_engine_private_key(struct tls_connection *conn)
 +{
 +#ifndef OPENSSL_NO_ENGINE
 +      if (SSL_use_PrivateKey(conn->ssl, conn->private_key) != 1) {
 +              tls_show_errors(MSG_ERROR, __func__,
 +                              "ENGINE: cannot use private key for TLS");
 +              return -1;
 +      }
 +      if (!SSL_check_private_key(conn->ssl)) {
 +              tls_show_errors(MSG_INFO, __func__,
 +                              "Private key failed verification");
 +              return -1;
 +      }
 +      return 0;
 +#else /* OPENSSL_NO_ENGINE */
 +      wpa_printf(MSG_ERROR, "SSL: Configuration uses engine, but "
 +                 "engine support was not compiled in");
 +      return -1;
 +#endif /* OPENSSL_NO_ENGINE */
 +}
 +
 +
-       SSL_CTX *ssl_ctx = _ssl_ctx;
++static int tls_connection_private_key(struct tls_data *data,
 +                                    struct tls_connection *conn,
 +                                    const char *private_key,
 +                                    const char *private_key_passwd,
 +                                    const u8 *private_key_blob,
 +                                    size_t private_key_blob_len)
 +{
-               if (tls_read_pkcs12_blob(ssl_ctx, conn->ssl, private_key_blob,
++      SSL_CTX *ssl_ctx = data->ssl;
 +      char *passwd;
 +      int ok;
 +
 +      if (private_key == NULL && private_key_blob == NULL)
 +              return 0;
 +
 +      if (private_key_passwd) {
 +              passwd = os_strdup(private_key_passwd);
 +              if (passwd == NULL)
 +                      return -1;
 +      } else
 +              passwd = NULL;
 +
 +      SSL_CTX_set_default_passwd_cb(ssl_ctx, tls_passwd_cb);
 +      SSL_CTX_set_default_passwd_cb_userdata(ssl_ctx, passwd);
 +
 +      ok = 0;
 +      while (private_key_blob) {
 +              if (SSL_use_PrivateKey_ASN1(EVP_PKEY_RSA, conn->ssl,
 +                                          (u8 *) private_key_blob,
 +                                          private_key_blob_len) == 1) {
 +                      wpa_printf(MSG_DEBUG, "OpenSSL: SSL_use_PrivateKey_"
 +                                 "ASN1(EVP_PKEY_RSA) --> OK");
 +                      ok = 1;
 +                      break;
 +              }
 +
 +              if (SSL_use_PrivateKey_ASN1(EVP_PKEY_DSA, conn->ssl,
 +                                          (u8 *) private_key_blob,
 +                                          private_key_blob_len) == 1) {
 +                      wpa_printf(MSG_DEBUG, "OpenSSL: SSL_use_PrivateKey_"
 +                                 "ASN1(EVP_PKEY_DSA) --> OK");
 +                      ok = 1;
 +                      break;
 +              }
 +
 +              if (SSL_use_RSAPrivateKey_ASN1(conn->ssl,
 +                                             (u8 *) private_key_blob,
 +                                             private_key_blob_len) == 1) {
 +                      wpa_printf(MSG_DEBUG, "OpenSSL: "
 +                                 "SSL_use_RSAPrivateKey_ASN1 --> OK");
 +                      ok = 1;
 +                      break;
 +              }
 +
-               if (tls_read_pkcs12(ssl_ctx, conn->ssl, private_key, passwd)
++              if (tls_read_pkcs12_blob(data, conn->ssl, private_key_blob,
 +                                       private_key_blob_len, passwd) == 0) {
 +                      wpa_printf(MSG_DEBUG, "OpenSSL: PKCS#12 as blob --> "
 +                                 "OK");
 +                      ok = 1;
 +                      break;
 +              }
 +
 +              break;
 +      }
 +
 +      while (!ok && private_key) {
 +#ifndef OPENSSL_NO_STDIO
 +              if (SSL_use_PrivateKey_file(conn->ssl, private_key,
 +                                          SSL_FILETYPE_ASN1) == 1) {
 +                      wpa_printf(MSG_DEBUG, "OpenSSL: "
 +                                 "SSL_use_PrivateKey_File (DER) --> OK");
 +                      ok = 1;
 +                      break;
 +              }
 +
 +              if (SSL_use_PrivateKey_file(conn->ssl, private_key,
 +                                          SSL_FILETYPE_PEM) == 1) {
 +                      wpa_printf(MSG_DEBUG, "OpenSSL: "
 +                                 "SSL_use_PrivateKey_File (PEM) --> OK");
 +                      ok = 1;
 +                      break;
 +              }
 +#else /* OPENSSL_NO_STDIO */
 +              wpa_printf(MSG_DEBUG, "OpenSSL: %s - OPENSSL_NO_STDIO",
 +                         __func__);
 +#endif /* OPENSSL_NO_STDIO */
 +
- static int tls_global_private_key(SSL_CTX *ssl_ctx, const char *private_key,
++              if (tls_read_pkcs12(data, conn->ssl, private_key, passwd)
 +                  == 0) {
 +                      wpa_printf(MSG_DEBUG, "OpenSSL: Reading PKCS#12 file "
 +                                 "--> OK");
 +                      ok = 1;
 +                      break;
 +              }
 +
 +              if (tls_cryptoapi_cert(conn->ssl, private_key) == 0) {
 +                      wpa_printf(MSG_DEBUG, "OpenSSL: Using CryptoAPI to "
 +                                 "access certificate store --> OK");
 +                      ok = 1;
 +                      break;
 +              }
 +
 +              break;
 +      }
 +
 +      if (!ok) {
 +              tls_show_errors(MSG_INFO, __func__,
 +                              "Failed to load private key");
 +              os_free(passwd);
 +              return -1;
 +      }
 +      ERR_clear_error();
 +      SSL_CTX_set_default_passwd_cb(ssl_ctx, NULL);
 +      os_free(passwd);
 +
 +      if (!SSL_check_private_key(conn->ssl)) {
 +              tls_show_errors(MSG_INFO, __func__, "Private key failed "
 +                              "verification");
 +              return -1;
 +      }
 +
 +      wpa_printf(MSG_DEBUG, "SSL: Private key loaded successfully");
 +      return 0;
 +}
 +
 +
-           tls_read_pkcs12(ssl_ctx, NULL, private_key, passwd)) {
++static int tls_global_private_key(struct tls_data *data,
++                                const char *private_key,
 +                                const char *private_key_passwd)
 +{
++      SSL_CTX *ssl_ctx = data->ssl;
 +      char *passwd;
 +
 +      if (private_key == NULL)
 +              return 0;
 +
 +      if (private_key_passwd) {
 +              passwd = os_strdup(private_key_passwd);
 +              if (passwd == NULL)
 +                      return -1;
 +      } else
 +              passwd = NULL;
 +
 +      SSL_CTX_set_default_passwd_cb(ssl_ctx, tls_passwd_cb);
 +      SSL_CTX_set_default_passwd_cb_userdata(ssl_ctx, passwd);
 +      if (
 +#ifndef OPENSSL_NO_STDIO
 +          SSL_CTX_use_PrivateKey_file(ssl_ctx, private_key,
 +                                      SSL_FILETYPE_ASN1) != 1 &&
 +          SSL_CTX_use_PrivateKey_file(ssl_ctx, private_key,
 +                                      SSL_FILETYPE_PEM) != 1 &&
 +#endif /* OPENSSL_NO_STDIO */
- static int tls_global_dh(SSL_CTX *ssl_ctx, const char *dh_file)
++          tls_read_pkcs12(data, NULL, private_key, passwd)) {
 +              tls_show_errors(MSG_INFO, __func__,
 +                              "Failed to load private key");
 +              os_free(passwd);
 +              ERR_clear_error();
 +              return -1;
 +      }
 +      os_free(passwd);
 +      ERR_clear_error();
 +      SSL_CTX_set_default_passwd_cb(ssl_ctx, NULL);
 +
 +      if (!SSL_CTX_check_private_key(ssl_ctx)) {
 +              tls_show_errors(MSG_INFO, __func__,
 +                              "Private key failed verification");
 +              return -1;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +static int tls_connection_dh(struct tls_connection *conn, const char *dh_file)
 +{
 +#ifdef OPENSSL_NO_DH
 +      if (dh_file == NULL)
 +              return 0;
 +      wpa_printf(MSG_ERROR, "TLS: openssl does not include DH support, but "
 +                 "dh_file specified");
 +      return -1;
 +#else /* OPENSSL_NO_DH */
 +      DH *dh;
 +      BIO *bio;
 +
 +      /* TODO: add support for dh_blob */
 +      if (dh_file == NULL)
 +              return 0;
 +      if (conn == NULL)
 +              return -1;
 +
 +      bio = BIO_new_file(dh_file, "r");
 +      if (bio == NULL) {
 +              wpa_printf(MSG_INFO, "TLS: Failed to open DH file '%s': %s",
 +                         dh_file, ERR_error_string(ERR_get_error(), NULL));
 +              return -1;
 +      }
 +      dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL);
 +      BIO_free(bio);
 +#ifndef OPENSSL_NO_DSA
 +      while (dh == NULL) {
 +              DSA *dsa;
 +              wpa_printf(MSG_DEBUG, "TLS: Failed to parse DH file '%s': %s -"
 +                         " trying to parse as DSA params", dh_file,
 +                         ERR_error_string(ERR_get_error(), NULL));
 +              bio = BIO_new_file(dh_file, "r");
 +              if (bio == NULL)
 +                      break;
 +              dsa = PEM_read_bio_DSAparams(bio, NULL, NULL, NULL);
 +              BIO_free(bio);
 +              if (!dsa) {
 +                      wpa_printf(MSG_DEBUG, "TLS: Failed to parse DSA file "
 +                                 "'%s': %s", dh_file,
 +                                 ERR_error_string(ERR_get_error(), NULL));
 +                      break;
 +              }
 +
 +              wpa_printf(MSG_DEBUG, "TLS: DH file in DSA param format");
 +              dh = DSA_dup_DH(dsa);
 +              DSA_free(dsa);
 +              if (dh == NULL) {
 +                      wpa_printf(MSG_INFO, "TLS: Failed to convert DSA "
 +                                 "params into DH params");
 +                      break;
 +              }
 +              break;
 +      }
 +#endif /* !OPENSSL_NO_DSA */
 +      if (dh == NULL) {
 +              wpa_printf(MSG_INFO, "TLS: Failed to read/parse DH/DSA file "
 +                         "'%s'", dh_file);
 +              return -1;
 +      }
 +
 +      if (SSL_set_tmp_dh(conn->ssl, dh) != 1) {
 +              wpa_printf(MSG_INFO, "TLS: Failed to set DH params from '%s': "
 +                         "%s", dh_file,
 +                         ERR_error_string(ERR_get_error(), NULL));
 +              DH_free(dh);
 +              return -1;
 +      }
 +      DH_free(dh);
 +      return 0;
 +#endif /* OPENSSL_NO_DH */
 +}
 +
 +
- int tls_connection_get_keys(void *ssl_ctx, struct tls_connection *conn,
-                           struct tls_keys *keys)
++static int tls_global_dh(struct tls_data *data, const char *dh_file)
 +{
 +#ifdef OPENSSL_NO_DH
 +      if (dh_file == NULL)
 +              return 0;
 +      wpa_printf(MSG_ERROR, "TLS: openssl does not include DH support, but "
 +                 "dh_file specified");
 +      return -1;
 +#else /* OPENSSL_NO_DH */
++      SSL_CTX *ssl_ctx = data->ssl;
 +      DH *dh;
 +      BIO *bio;
 +
 +      /* TODO: add support for dh_blob */
 +      if (dh_file == NULL)
 +              return 0;
 +      if (ssl_ctx == NULL)
 +              return -1;
 +
 +      bio = BIO_new_file(dh_file, "r");
 +      if (bio == NULL) {
 +              wpa_printf(MSG_INFO, "TLS: Failed to open DH file '%s': %s",
 +                         dh_file, ERR_error_string(ERR_get_error(), NULL));
 +              return -1;
 +      }
 +      dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL);
 +      BIO_free(bio);
 +#ifndef OPENSSL_NO_DSA
 +      while (dh == NULL) {
 +              DSA *dsa;
 +              wpa_printf(MSG_DEBUG, "TLS: Failed to parse DH file '%s': %s -"
 +                         " trying to parse as DSA params", dh_file,
 +                         ERR_error_string(ERR_get_error(), NULL));
 +              bio = BIO_new_file(dh_file, "r");
 +              if (bio == NULL)
 +                      break;
 +              dsa = PEM_read_bio_DSAparams(bio, NULL, NULL, NULL);
 +              BIO_free(bio);
 +              if (!dsa) {
 +                      wpa_printf(MSG_DEBUG, "TLS: Failed to parse DSA file "
 +                                 "'%s': %s", dh_file,
 +                                 ERR_error_string(ERR_get_error(), NULL));
 +                      break;
 +              }
 +
 +              wpa_printf(MSG_DEBUG, "TLS: DH file in DSA param format");
 +              dh = DSA_dup_DH(dsa);
 +              DSA_free(dsa);
 +              if (dh == NULL) {
 +                      wpa_printf(MSG_INFO, "TLS: Failed to convert DSA "
 +                                 "params into DH params");
 +                      break;
 +              }
 +              break;
 +      }
 +#endif /* !OPENSSL_NO_DSA */
 +      if (dh == NULL) {
 +              wpa_printf(MSG_INFO, "TLS: Failed to read/parse DH/DSA file "
 +                         "'%s'", dh_file);
 +              return -1;
 +      }
 +
 +      if (SSL_CTX_set_tmp_dh(ssl_ctx, dh) != 1) {
 +              wpa_printf(MSG_INFO, "TLS: Failed to set DH params from '%s': "
 +                         "%s", dh_file,
 +                         ERR_error_string(ERR_get_error(), NULL));
 +              DH_free(dh);
 +              return -1;
 +      }
 +      DH_free(dh);
 +      return 0;
 +#endif /* OPENSSL_NO_DH */
 +}
 +
 +
- #ifdef CONFIG_FIPS
-       wpa_printf(MSG_ERROR, "OpenSSL: TLS keys cannot be exported in FIPS "
-                  "mode");
-       return -1;
- #else /* CONFIG_FIPS */
++int tls_connection_get_random(void *ssl_ctx, struct tls_connection *conn,
++                            struct tls_random *keys)
 +{
-       keys->master_key = ssl->session->master_key;
-       keys->master_key_len = ssl->session->master_key_length;
 +      SSL *ssl;
 +
 +      if (conn == NULL || keys == NULL)
 +              return -1;
 +      ssl = conn->ssl;
++#if OPENSSL_VERSION_NUMBER < 0x10100000L
 +      if (ssl == NULL || ssl->s3 == NULL || ssl->session == NULL)
 +              return -1;
 +
 +      os_memset(keys, 0, sizeof(*keys));
-                      u8 *out, size_t out_len)
 +      keys->client_random = ssl->s3->client_random;
 +      keys->client_random_len = SSL3_RANDOM_SIZE;
 +      keys->server_random = ssl->s3->server_random;
 +      keys->server_random_len = SSL3_RANDOM_SIZE;
++#else
++      if (ssl == NULL)
++              return -1;
++
++      os_memset(keys, 0, sizeof(*keys));
++      keys->client_random = conn->client_random;
++      keys->client_random_len = SSL_get_client_random(
++              ssl, conn->client_random, sizeof(conn->client_random));
++      keys->server_random = conn->server_random;
++      keys->server_random_len = SSL_get_server_random(
++              ssl, conn->server_random, sizeof(conn->server_random));
++#endif
 +
 +      return 0;
++}
++
++
++#ifndef CONFIG_FIPS
++static int openssl_get_keyblock_size(SSL *ssl)
++{
++#if OPENSSL_VERSION_NUMBER < 0x10100000L
++      const EVP_CIPHER *c;
++      const EVP_MD *h;
++      int md_size;
++
++      if (ssl->enc_read_ctx == NULL || ssl->enc_read_ctx->cipher == NULL ||
++          ssl->read_hash == NULL)
++              return -1;
++
++      c = ssl->enc_read_ctx->cipher;
++#if OPENSSL_VERSION_NUMBER >= 0x00909000L
++      h = EVP_MD_CTX_md(ssl->read_hash);
++#else
++      h = ssl->read_hash;
++#endif
++      if (h)
++              md_size = EVP_MD_size(h);
++#if OPENSSL_VERSION_NUMBER >= 0x10000000L
++      else if (ssl->s3)
++              md_size = ssl->s3->tmp.new_mac_secret_size;
++#endif
++      else
++              return -1;
++
++      wpa_printf(MSG_DEBUG, "OpenSSL: keyblock size: key_len=%d MD_size=%d "
++                 "IV_len=%d", EVP_CIPHER_key_length(c), md_size,
++                 EVP_CIPHER_iv_length(c));
++      return 2 * (EVP_CIPHER_key_length(c) +
++                  md_size +
++                  EVP_CIPHER_iv_length(c));
++#else
++      const SSL_CIPHER *ssl_cipher;
++      int cipher, digest;
++      const EVP_CIPHER *c;
++      const EVP_MD *h;
++
++      ssl_cipher = SSL_get_current_cipher(ssl);
++      if (!ssl_cipher)
++              return -1;
++      cipher = SSL_CIPHER_get_cipher_nid(ssl_cipher);
++      digest = SSL_CIPHER_get_digest_nid(ssl_cipher);
++      wpa_printf(MSG_DEBUG, "OpenSSL: cipher nid %d digest nid %d",
++                 cipher, digest);
++      if (cipher < 0 || digest < 0)
++              return -1;
++      c = EVP_get_cipherbynid(cipher);
++      h = EVP_get_digestbynid(digest);
++      if (!c || !h)
++              return -1;
++
++      wpa_printf(MSG_DEBUG,
++                 "OpenSSL: keyblock size: key_len=%d MD_size=%d IV_len=%d",
++                 EVP_CIPHER_key_length(c), EVP_MD_size(h),
++                 EVP_CIPHER_iv_length(c));
++      return 2 * (EVP_CIPHER_key_length(c) + EVP_MD_size(h) +
++                  EVP_CIPHER_iv_length(c));
++#endif
++}
++#endif /* CONFIG_FIPS */
++
++
++static int openssl_tls_prf(struct tls_connection *conn,
++                         const char *label, int server_random_first,
++                         int skip_keyblock, u8 *out, size_t out_len)
++{
++#ifdef CONFIG_FIPS
++      wpa_printf(MSG_ERROR, "OpenSSL: TLS keys cannot be exported in FIPS "
++                 "mode");
++      return -1;
++#else /* CONFIG_FIPS */
++#if OPENSSL_VERSION_NUMBER < 0x10100000L
++      SSL *ssl;
++      u8 *rnd;
++      int ret = -1;
++      int skip = 0;
++      u8 *tmp_out = NULL;
++      u8 *_out = out;
++      const char *ver;
++
++      /*
++       * TLS library did not support key generation, so get the needed TLS
++       * session parameters and use an internal implementation of TLS PRF to
++       * derive the key.
++       */
++
++      if (conn == NULL)
++              return -1;
++      ssl = conn->ssl;
++      if (ssl == NULL || ssl->s3 == NULL || ssl->session == NULL ||
++          ssl->session->master_key_length <= 0)
++              return -1;
++      ver = SSL_get_version(ssl);
++
++      if (skip_keyblock) {
++              skip = openssl_get_keyblock_size(ssl);
++              if (skip < 0)
++                      return -1;
++              tmp_out = os_malloc(skip + out_len);
++              if (!tmp_out)
++                      return -1;
++              _out = tmp_out;
++      }
++
++      rnd = os_malloc(2 * SSL3_RANDOM_SIZE);
++      if (!rnd) {
++              os_free(tmp_out);
++              return -1;
++      }
++
++      if (server_random_first) {
++              os_memcpy(rnd, ssl->s3->server_random, SSL3_RANDOM_SIZE);
++              os_memcpy(rnd + SSL3_RANDOM_SIZE, ssl->s3->client_random,
++                      SSL3_RANDOM_SIZE);
++      } else {
++              os_memcpy(rnd, ssl->s3->client_random, SSL3_RANDOM_SIZE);
++              os_memcpy(rnd + SSL3_RANDOM_SIZE, ssl->s3->server_random,
++                      SSL3_RANDOM_SIZE);
++      }
++
++      if (os_strcmp(ver, "TLSv1.2") == 0) {
++              tls_prf_sha256(ssl->session->master_key,
++                             ssl->session->master_key_length,
++                             label, rnd, 2 * SSL3_RANDOM_SIZE,
++                             _out, skip + out_len);
++              ret = 0;
++      } else if (tls_prf_sha1_md5(ssl->session->master_key,
++                                  ssl->session->master_key_length,
++                                  label, rnd, 2 * SSL3_RANDOM_SIZE,
++                                  _out, skip + out_len) == 0) {
++              ret = 0;
++      }
++      os_free(rnd);
++      if (ret == 0 && skip_keyblock)
++              os_memcpy(out, _out + skip, out_len);
++      bin_clear_free(tmp_out, skip);
++
++      return ret;
++#else
++      SSL *ssl;
++      SSL_SESSION *sess;
++      u8 *rnd;
++      int ret = -1;
++      int skip = 0;
++      u8 *tmp_out = NULL;
++      u8 *_out = out;
++      unsigned char client_random[SSL3_RANDOM_SIZE];
++      unsigned char server_random[SSL3_RANDOM_SIZE];
++      unsigned char master_key[64];
++      size_t master_key_len;
++      const char *ver;
++
++      /*
++       * TLS library did not support key generation, so get the needed TLS
++       * session parameters and use an internal implementation of TLS PRF to
++       * derive the key.
++       */
++
++      if (conn == NULL)
++              return -1;
++      ssl = conn->ssl;
++      if (ssl == NULL)
++              return -1;
++      ver = SSL_get_version(ssl);
++      sess = SSL_get_session(ssl);
++      if (!ver || !sess)
++              return -1;
++
++      if (skip_keyblock) {
++              skip = openssl_get_keyblock_size(ssl);
++              if (skip < 0)
++                      return -1;
++              tmp_out = os_malloc(skip + out_len);
++              if (!tmp_out)
++                      return -1;
++              _out = tmp_out;
++      }
++
++      rnd = os_malloc(2 * SSL3_RANDOM_SIZE);
++      if (!rnd) {
++              os_free(tmp_out);
++              return -1;
++      }
++
++      SSL_get_client_random(ssl, client_random, sizeof(client_random));
++      SSL_get_server_random(ssl, server_random, sizeof(server_random));
++      master_key_len = SSL_SESSION_get_master_key(sess, master_key,
++                                                  sizeof(master_key));
++
++      if (server_random_first) {
++              os_memcpy(rnd, server_random, SSL3_RANDOM_SIZE);
++              os_memcpy(rnd + SSL3_RANDOM_SIZE, client_random,
++                        SSL3_RANDOM_SIZE);
++      } else {
++              os_memcpy(rnd, client_random, SSL3_RANDOM_SIZE);
++              os_memcpy(rnd + SSL3_RANDOM_SIZE, server_random,
++                        SSL3_RANDOM_SIZE);
++      }
++
++      if (os_strcmp(ver, "TLSv1.2") == 0) {
++              tls_prf_sha256(master_key, master_key_len,
++                             label, rnd, 2 * SSL3_RANDOM_SIZE,
++                             _out, skip + out_len);
++              ret = 0;
++      } else if (tls_prf_sha1_md5(master_key, master_key_len,
++                                  label, rnd, 2 * SSL3_RANDOM_SIZE,
++                                  _out, skip + out_len) == 0) {
++              ret = 0;
++      }
++      os_memset(master_key, 0, sizeof(master_key));
++      os_free(rnd);
++      if (ret == 0 && skip_keyblock)
++              os_memcpy(out, _out + skip, out_len);
++      bin_clear_free(tmp_out, skip);
++
++      return ret;
++#endif
 +#endif /* CONFIG_FIPS */
 +}
 +
 +
 +int tls_connection_prf(void *tls_ctx, struct tls_connection *conn,
 +                     const char *label, int server_random_first,
-       if (server_random_first)
-               return -1;
++                     int skip_keyblock, u8 *out, size_t out_len)
 +{
 +#if OPENSSL_VERSION_NUMBER >= 0x10001000L
 +      SSL *ssl;
 +      if (conn == NULL)
 +              return -1;
-       return -1;
++      if (server_random_first || skip_keyblock)
++              return openssl_tls_prf(conn, label,
++                                     server_random_first, skip_keyblock,
++                                     out, out_len);
 +      ssl = conn->ssl;
 +      if (SSL_export_keying_material(ssl, out, out_len, label,
 +                                     os_strlen(label), NULL, 0, 0) == 1) {
 +              wpa_printf(MSG_DEBUG, "OpenSSL: Using internal PRF");
 +              return 0;
 +      }
 +#endif
-       if (in_data &&
++      return openssl_tls_prf(conn, label, server_random_first,
++                             skip_keyblock, out, out_len);
 +}
 +
 +
 +static struct wpabuf *
 +openssl_handshake(struct tls_connection *conn, const struct wpabuf *in_data,
 +                int server)
 +{
 +      int res;
 +      struct wpabuf *out_data;
 +
 +      /*
 +       * Give TLS handshake data from the server (if available) to OpenSSL
 +       * for processing.
 +       */
-       if (SSL_is_init_finished(conn->ssl) && appl_data && in_data)
-               *appl_data = openssl_get_appl_data(conn, wpabuf_len(in_data));
++      if (in_data && wpabuf_len(in_data) > 0 &&
 +          BIO_write(conn->ssl_in, wpabuf_head(in_data), wpabuf_len(in_data))
 +          < 0) {
 +              tls_show_errors(MSG_INFO, __func__,
 +                              "Handshake failed - BIO_write");
 +              return NULL;
 +      }
 +
 +      /* Initiate TLS handshake or continue the existing handshake */
 +      if (server)
 +              res = SSL_accept(conn->ssl);
 +      else
 +              res = SSL_connect(conn->ssl);
 +      if (res != 1) {
 +              int err = SSL_get_error(conn->ssl, res);
 +              if (err == SSL_ERROR_WANT_READ)
 +                      wpa_printf(MSG_DEBUG, "SSL: SSL_connect - want "
 +                                 "more data");
 +              else if (err == SSL_ERROR_WANT_WRITE)
 +                      wpa_printf(MSG_DEBUG, "SSL: SSL_connect - want to "
 +                                 "write");
 +              else {
 +                      tls_show_errors(MSG_INFO, __func__, "SSL_connect");
 +                      conn->failed++;
 +              }
 +      }
 +
 +      /* Get the TLS handshake data to be sent to the server */
 +      res = BIO_ctrl_pending(conn->ssl_out);
 +      wpa_printf(MSG_DEBUG, "SSL: %d bytes pending from ssl_out", res);
 +      out_data = wpabuf_alloc(res);
 +      if (out_data == NULL) {
 +              wpa_printf(MSG_DEBUG, "SSL: Failed to allocate memory for "
 +                         "handshake output (%d bytes)", res);
 +              if (BIO_reset(conn->ssl_out) < 0) {
 +                      tls_show_errors(MSG_INFO, __func__,
 +                                      "BIO_reset failed");
 +              }
 +              return NULL;
 +      }
 +      res = res == 0 ? 0 : BIO_read(conn->ssl_out, wpabuf_mhead(out_data),
 +                                    res);
 +      if (res < 0) {
 +              tls_show_errors(MSG_INFO, __func__,
 +                              "Handshake failed - BIO_read");
 +              if (BIO_reset(conn->ssl_out) < 0) {
 +                      tls_show_errors(MSG_INFO, __func__,
 +                                      "BIO_reset failed");
 +              }
 +              wpabuf_free(out_data);
 +              return NULL;
 +      }
 +      wpabuf_put(out_data, res);
 +
 +      return out_data;
 +}
 +
 +
 +static struct wpabuf *
 +openssl_get_appl_data(struct tls_connection *conn, size_t max_len)
 +{
 +      struct wpabuf *appl_data;
 +      int res;
 +
 +      appl_data = wpabuf_alloc(max_len + 100);
 +      if (appl_data == NULL)
 +              return NULL;
 +
 +      res = SSL_read(conn->ssl, wpabuf_mhead(appl_data),
 +                     wpabuf_size(appl_data));
 +      if (res < 0) {
 +              int err = SSL_get_error(conn->ssl, res);
 +              if (err == SSL_ERROR_WANT_READ ||
 +                  err == SSL_ERROR_WANT_WRITE) {
 +                      wpa_printf(MSG_DEBUG, "SSL: No Application Data "
 +                                 "included");
 +              } else {
 +                      tls_show_errors(MSG_INFO, __func__,
 +                                      "Failed to read possible "
 +                                      "Application Data");
 +              }
 +              wpabuf_free(appl_data);
 +              return NULL;
 +      }
 +
 +      wpabuf_put(appl_data, res);
 +      wpa_hexdump_buf_key(MSG_MSGDUMP, "SSL: Application Data in Finished "
 +                          "message", appl_data);
 +
 +      return appl_data;
 +}
 +
 +
 +static struct wpabuf *
 +openssl_connection_handshake(struct tls_connection *conn,
 +                           const struct wpabuf *in_data,
 +                           struct wpabuf **appl_data, int server)
 +{
 +      struct wpabuf *out_data;
 +
 +      if (appl_data)
 +              *appl_data = NULL;
 +
 +      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 (tls_connection_engine_ca_cert(tls_ctx, conn,
-                                                 ca_cert_id))
++      if (SSL_is_init_finished(conn->ssl)) {
++              wpa_printf(MSG_DEBUG,
++                         "OpenSSL: Handshake finished - resumed=%d",
++                         tls_connection_resumed(conn->ssl_ctx, conn));
++              if (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;
 +}
 +
 +
 +struct wpabuf *
 +tls_connection_handshake(void *ssl_ctx, struct tls_connection *conn,
 +                       const struct wpabuf *in_data,
 +                       struct wpabuf **appl_data)
 +{
 +      return openssl_connection_handshake(conn, in_data, appl_data, 0);
 +}
 +
 +
 +struct wpabuf * tls_connection_server_handshake(void *tls_ctx,
 +                                              struct tls_connection *conn,
 +                                              const struct wpabuf *in_data,
 +                                              struct wpabuf **appl_data)
 +{
 +      return openssl_connection_handshake(conn, in_data, appl_data, 1);
 +}
 +
 +
 +struct wpabuf * tls_connection_encrypt(void *tls_ctx,
 +                                     struct tls_connection *conn,
 +                                     const struct wpabuf *in_data)
 +{
 +      int res;
 +      struct wpabuf *buf;
 +
 +      if (conn == NULL)
 +              return NULL;
 +
 +      /* Give plaintext data for OpenSSL to encrypt into the TLS tunnel. */
 +      if ((res = BIO_reset(conn->ssl_in)) < 0 ||
 +          (res = BIO_reset(conn->ssl_out)) < 0) {
 +              tls_show_errors(MSG_INFO, __func__, "BIO_reset failed");
 +              return NULL;
 +      }
 +      res = SSL_write(conn->ssl, wpabuf_head(in_data), wpabuf_len(in_data));
 +      if (res < 0) {
 +              tls_show_errors(MSG_INFO, __func__,
 +                              "Encryption failed - SSL_write");
 +              return NULL;
 +      }
 +
 +      /* Read encrypted data to be sent to the server */
 +      buf = wpabuf_alloc(wpabuf_len(in_data) + 300);
 +      if (buf == NULL)
 +              return NULL;
 +      res = BIO_read(conn->ssl_out, wpabuf_mhead(buf), wpabuf_size(buf));
 +      if (res < 0) {
 +              tls_show_errors(MSG_INFO, __func__,
 +                              "Encryption failed - BIO_read");
 +              wpabuf_free(buf);
 +              return NULL;
 +      }
 +      wpabuf_put(buf, res);
 +
 +      return buf;
 +}
 +
 +
 +struct wpabuf * tls_connection_decrypt(void *tls_ctx,
 +                                     struct tls_connection *conn,
 +                                     const struct wpabuf *in_data)
 +{
 +      int res;
 +      struct wpabuf *buf;
 +
 +      /* Give encrypted data from TLS tunnel for OpenSSL to decrypt. */
 +      res = BIO_write(conn->ssl_in, wpabuf_head(in_data),
 +                      wpabuf_len(in_data));
 +      if (res < 0) {
 +              tls_show_errors(MSG_INFO, __func__,
 +                              "Decryption failed - BIO_write");
 +              return NULL;
 +      }
 +      if (BIO_reset(conn->ssl_out) < 0) {
 +              tls_show_errors(MSG_INFO, __func__, "BIO_reset failed");
 +              return NULL;
 +      }
 +
 +      /* Read decrypted data for further processing */
 +      /*
 +       * 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.
 +       */
 +      buf = wpabuf_alloc((wpabuf_len(in_data) + 500) * 3);
 +      if (buf == NULL)
 +              return NULL;
 +      res = SSL_read(conn->ssl, wpabuf_mhead(buf), wpabuf_size(buf));
 +      if (res < 0) {
 +              tls_show_errors(MSG_INFO, __func__,
 +                              "Decryption failed - SSL_read");
 +              wpabuf_free(buf);
 +              return NULL;
 +      }
 +      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
 +}
 +
 +
 +int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn,
 +                                 u8 *ciphers)
 +{
 +      char buf[100], *pos, *end;
 +      u8 *c;
 +      int ret;
 +
 +      if (conn == NULL || conn->ssl == NULL || ciphers == NULL)
 +              return -1;
 +
 +      buf[0] = '\0';
 +      pos = buf;
 +      end = pos + sizeof(buf);
 +
 +      c = ciphers;
 +      while (*c != TLS_CIPHER_NONE) {
 +              const char *suite;
 +
 +              switch (*c) {
 +              case TLS_CIPHER_RC4_SHA:
 +                      suite = "RC4-SHA";
 +                      break;
 +              case TLS_CIPHER_AES128_SHA:
 +                      suite = "AES128-SHA";
 +                      break;
 +              case TLS_CIPHER_RSA_DHE_AES128_SHA:
 +                      suite = "DHE-RSA-AES128-SHA";
 +                      break;
 +              case TLS_CIPHER_ANON_DH_AES128_SHA:
 +                      suite = "ADH-AES128-SHA";
 +                      break;
 +              default:
 +                      wpa_printf(MSG_DEBUG, "TLS: Unsupported "
 +                                 "cipher selection: %d", *c);
 +                      return -1;
 +              }
 +              ret = os_snprintf(pos, end - pos, ":%s", suite);
 +              if (os_snprintf_error(end - pos, ret))
 +                      break;
 +              pos += ret;
 +
 +              c++;
 +      }
 +
 +      wpa_printf(MSG_DEBUG, "OpenSSL: cipher suites: %s", buf + 1);
 +
++#if OPENSSL_VERSION_NUMBER >= 0x10100000L
++#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST)
++      if (os_strstr(buf, ":ADH-")) {
++              /*
++               * Need to drop to security level 0 to allow anonymous
++               * cipher suites for EAP-FAST.
++               */
++              SSL_set_security_level(conn->ssl, 0);
++      } else if (SSL_get_security_level(conn->ssl) == 0) {
++              /* Force at least security level 1 */
++              SSL_set_security_level(conn->ssl, 1);
++      }
++#endif /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */
++#endif
++
 +      if (SSL_set_cipher_list(conn->ssl, buf + 1) != 1) {
 +              tls_show_errors(MSG_INFO, __func__,
 +                              "Cipher suite configuration failed");
 +              return -1;
 +      }
 +
 +      return 0;
 +}
 +
 +
++int tls_get_version(void *ssl_ctx, struct tls_connection *conn,
++                  char *buf, size_t buflen)
++{
++      const char *name;
++      if (conn == NULL || conn->ssl == NULL)
++              return -1;
++
++      name = SSL_get_version(conn->ssl);
++      if (name == NULL)
++              return -1;
++
++      os_strlcpy(buf, name, buflen);
++      return 0;
++}
++
++
 +int tls_get_cipher(void *ssl_ctx, struct tls_connection *conn,
 +                 char *buf, size_t buflen)
 +{
 +      const char *name;
 +      if (conn == NULL || conn->ssl == NULL)
 +              return -1;
 +
 +      name = SSL_get_cipher(conn->ssl);
 +      if (name == NULL)
 +              return -1;
 +
 +      os_strlcpy(buf, name, buflen);
 +      return 0;
 +}
 +
 +
 +int tls_connection_enable_workaround(void *ssl_ctx,
 +                                   struct tls_connection *conn)
 +{
 +      SSL_set_options(conn->ssl, SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS);
 +
 +      return 0;
 +}
 +
 +
 +#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST)
 +/* ClientHello TLS extensions require a patch to openssl, so this function is
 + * commented out unless explicitly needed for EAP-FAST in order to be able to
 + * build this file with unmodified openssl. */
 +int tls_connection_client_hello_ext(void *ssl_ctx, struct tls_connection *conn,
 +                                  int ext_type, const u8 *data,
 +                                  size_t data_len)
 +{
 +      if (conn == NULL || conn->ssl == NULL || ext_type != 35)
 +              return -1;
 +
 +      if (SSL_set_session_ticket_ext(conn->ssl, (void *) data,
 +                                     data_len) != 1)
 +              return -1;
 +
 +      return 0;
 +}
 +#endif /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */
 +
 +
 +int tls_connection_get_failed(void *ssl_ctx, struct tls_connection *conn)
 +{
 +      if (conn == NULL)
 +              return -1;
 +      return conn->failed;
 +}
 +
 +
 +int tls_connection_get_read_alerts(void *ssl_ctx, struct tls_connection *conn)
 +{
 +      if (conn == NULL)
 +              return -1;
 +      return conn->read_alerts;
 +}
 +
 +
 +int tls_connection_get_write_alerts(void *ssl_ctx, struct tls_connection *conn)
 +{
 +      if (conn == NULL)
 +              return -1;
 +      return conn->write_alerts;
 +}
 +
 +
 +#ifdef HAVE_OCSP
 +
 +static void ocsp_debug_print_resp(OCSP_RESPONSE *rsp)
 +{
 +#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;
 +
 +      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_DEBUG, "OpenSSL: OCSP Response\n%s", txt);
 +      }
 +      os_free(txt);
 +      BIO_free(out);
 +#endif /* CONFIG_NO_STDOUT_DEBUG */
 +}
 +
 +
 +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;
 +      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");
 +              return (conn->flags & TLS_CONN_REQUIRE_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");
 +              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));
 +              return 0;
 +      }
 +
 +      basic = OCSP_response_get1_basic(rsp);
 +      if (!basic) {
 +              wpa_printf(MSG_INFO, "OpenSSL: Could not find BasicOCSPResponse");
 +              return 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");
 +              OCSP_BASICRESP_free(basic);
 +              OCSP_RESPONSE_free(rsp);
 +              return 0;
 +      }
 +
 +      wpa_printf(MSG_DEBUG, "OpenSSL: OCSP response verification succeeded");
 +
 +      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;
 +      }
 +
 +      id = OCSP_cert_to_id(NULL, conn->peer_cert, conn->peer_issuer);
 +      if (!id) {
 +              wpa_printf(MSG_DEBUG, "OpenSSL: Could not create OCSP certificate identifier");
 +              OCSP_BASICRESP_free(basic);
 +              OCSP_RESPONSE_free(rsp);
 +              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",
 +                         (conn->flags & TLS_CONN_REQUIRE_OCSP) ? "" :
 +                         " (OCSP not required)");
 +              OCSP_BASICRESP_free(basic);
 +              OCSP_RESPONSE_free(rsp);
 +              return (conn->flags & TLS_CONN_REQUIRE_OCSP) ? 0 : 1;
 +      }
 +
 +      if (!OCSP_check_validity(this_update, next_update, 5 * 60, -1)) {
 +              tls_show_errors(MSG_INFO, __func__,
 +                              "OpenSSL: OCSP status times invalid");
 +              OCSP_BASICRESP_free(basic);
 +              OCSP_RESPONSE_free(rsp);
 +              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)
 +              return 0;
 +      if (conn->flags & TLS_CONN_REQUIRE_OCSP) {
 +              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");
 +      return 1;
 +}
 +
 +
 +static int ocsp_status_cb(SSL *s, void *arg)
 +{
 +      char *tmp;
 +      char *resp;
 +      size_t len;
 +
 +      if (tls_global->ocsp_stapling_response == NULL) {
 +              wpa_printf(MSG_DEBUG, "OpenSSL: OCSP status callback - no response configured");
 +              return SSL_TLSEXT_ERR_OK;
 +      }
 +
 +      resp = os_readfile(tls_global->ocsp_stapling_response, &len);
 +      if (resp == NULL) {
 +              wpa_printf(MSG_DEBUG, "OpenSSL: OCSP status callback - could not read response file");
 +              /* TODO: Build OCSPResponse with responseStatus = internalError
 +               */
 +              return SSL_TLSEXT_ERR_OK;
 +      }
 +      wpa_printf(MSG_DEBUG, "OpenSSL: OCSP status callback - send cached response");
 +      tmp = OPENSSL_malloc(len);
 +      if (tmp == NULL) {
 +              os_free(resp);
 +              return SSL_TLSEXT_ERR_ALERT_FATAL;
 +      }
 +
 +      os_memcpy(tmp, resp, len);
 +      os_free(resp);
 +      SSL_set_tlsext_status_ocsp_resp(s, tmp, len);
 +
 +      return SSL_TLSEXT_ERR_OK;
 +}
 +
 +#endif /* HAVE_OCSP */
 +
 +
 +int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
 +                            const struct tls_connection_params *params)
 +{
++      struct tls_data *data = tls_ctx;
 +      int ret;
 +      unsigned long err;
 +      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 defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST)
++#if OPENSSL_VERSION_NUMBER < 0x10100000L
 +      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;
 +              }
 +      }
++#endif
++#endif /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */
 +
 +      while ((err = ERR_get_error())) {
 +              wpa_printf(MSG_INFO, "%s: Clearing pending SSL error: %s",
 +                         __func__, ERR_error_string(err, NULL));
 +      }
 +
 +      if (engine_id) {
 +              wpa_printf(MSG_DEBUG, "SSL: Initializing TLS engine");
 +              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->suffix_match,
 +                                           params->domain_match))
 +              return -1;
 +
 +      if (engine_id && ca_cert_id) {
-       } else if (tls_connection_ca_cert(tls_ctx, conn, params->ca_cert,
++              if (tls_connection_engine_ca_cert(data, conn, ca_cert_id))
 +                      return TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED;
-       } else if (tls_connection_private_key(tls_ctx, conn,
++      } else if (tls_connection_ca_cert(data, conn, params->ca_cert,
 +                                        params->ca_cert_blob,
 +                                        params->ca_cert_blob_len,
 +                                        params->ca_path))
 +              return -1;
 +
 +      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 (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;
- #ifdef SSL_OP_NO_TICKET
-       if (params->flags & TLS_CONN_DISABLE_SESSION_TICKET)
-               SSL_set_options(conn->ssl, SSL_OP_NO_TICKET);
- #ifdef SSL_clear_options
-       else
-               SSL_clear_options(conn->ssl, SSL_OP_NO_TICKET);
- #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 */
++      } else if (tls_connection_private_key(data, conn,
 +                                            params->private_key,
 +                                            params->private_key_passwd,
 +                                            params->private_key_blob,
 +                                            params->private_key_blob_len)) {
 +              wpa_printf(MSG_INFO, "TLS: Failed to load private key '%s'",
 +                         params->private_key);
 +              return -1;
 +      }
 +
 +      if (tls_connection_dh(conn, params->dh_file)) {
 +              wpa_printf(MSG_INFO, "TLS: Failed to load DH file '%s'",
 +                         params->dh_file);
 +              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;
 +      }
 +
-               SSL_CTX *ssl_ctx = tls_ctx;
++      tls_set_conn_flags(conn->ssl, params->flags);
 +
 +#ifdef HAVE_OCSP
 +      if (params->flags & TLS_CONN_REQUEST_OCSP) {
-       tls_get_errors(tls_ctx);
++              SSL_CTX *ssl_ctx = data->ssl;
 +              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);
 +      }
++#else /* HAVE_OCSP */
++      if (params->flags & TLS_CONN_REQUIRE_OCSP) {
++              wpa_printf(MSG_INFO,
++                         "OpenSSL: No OCSP support included - reject configuration");
++              return -1;
++      }
++      if (params->flags & TLS_CONN_REQUEST_OCSP) {
++              wpa_printf(MSG_DEBUG,
++                         "OpenSSL: No OCSP support included - allow optional OCSP case to continue");
++      }
 +#endif /* HAVE_OCSP */
 +
 +      conn->flags = params->flags;
 +
-       SSL_CTX *ssl_ctx = tls_ctx;
++      tls_get_errors(data);
 +
 +      return 0;
 +}
 +
 +
 +int tls_global_set_params(void *tls_ctx,
 +                        const struct tls_connection_params *params)
 +{
-       if (tls_global_ca_cert(ssl_ctx, params->ca_cert))
-               return -1;
-       if (tls_global_client_cert(ssl_ctx, params->client_cert))
-               return -1;
-       if (tls_global_private_key(ssl_ctx, params->private_key,
-                                  params->private_key_passwd))
-               return -1;
-       if (tls_global_dh(ssl_ctx, params->dh_file)) {
-               wpa_printf(MSG_INFO, "TLS: Failed to load DH file '%s'",
-                          params->dh_file);
++      struct tls_data *data = tls_ctx;
++      SSL_CTX *ssl_ctx = data->ssl;
 +      unsigned long err;
 +
 +      while ((err = ERR_get_error())) {
 +              wpa_printf(MSG_INFO, "%s: Clearing pending SSL error: %s",
 +                         __func__, ERR_error_string(err, NULL));
 +      }
 +
- int tls_connection_get_keyblock_size(void *tls_ctx,
-                                    struct tls_connection *conn)
- {
-       const EVP_CIPHER *c;
-       const EVP_MD *h;
-       int md_size;
-       if (conn == NULL || conn->ssl == NULL ||
-           conn->ssl->enc_read_ctx == NULL ||
-           conn->ssl->enc_read_ctx->cipher == NULL ||
-           conn->ssl->read_hash == NULL)
-               return -1;
-       c = conn->ssl->enc_read_ctx->cipher;
- #if OPENSSL_VERSION_NUMBER >= 0x00909000L
-       h = EVP_MD_CTX_md(conn->ssl->read_hash);
- #else
-       h = conn->ssl->read_hash;
- #endif
-       if (h)
-               md_size = EVP_MD_size(h);
- #if OPENSSL_VERSION_NUMBER >= 0x10000000L
-       else if (conn->ssl->s3)
-               md_size = conn->ssl->s3->tmp.new_mac_secret_size;
- #endif
-       else
-               return -1;
-       wpa_printf(MSG_DEBUG, "OpenSSL: keyblock size: key_len=%d MD_size=%d "
-                  "IV_len=%d", EVP_CIPHER_key_length(c), md_size,
-                  EVP_CIPHER_iv_length(c));
-       return 2 * (EVP_CIPHER_key_length(c) +
-                   md_size +
-                   EVP_CIPHER_iv_length(c));
- }
- unsigned int tls_capabilities(void *tls_ctx)
- {
-       return 0;
- }
++      if (tls_global_ca_cert(data, params->ca_cert) ||
++          tls_global_client_cert(data, params->client_cert) ||
++          tls_global_private_key(data, params->private_key,
++                                 params->private_key_passwd) ||
++          tls_global_dh(data, params->dh_file)) {
++              wpa_printf(MSG_INFO, "TLS: Failed to set global parameters");
 +              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);
 +#ifdef SSL_CTX_clear_options
 +      else
 +              SSL_CTX_clear_options(ssl_ctx, SSL_OP_NO_TICKET);
 +#endif /* SSL_clear_options */
 +#endif /*  SSL_OP_NO_TICKET */
 +
 +#ifdef HAVE_OCSP
 +      SSL_CTX_set_tlsext_status_cb(ssl_ctx, ocsp_status_cb);
 +      SSL_CTX_set_tlsext_status_arg(ssl_ctx, ssl_ctx);
 +      os_free(tls_global->ocsp_stapling_response);
 +      if (params->ocsp_stapling_response)
 +              tls_global->ocsp_stapling_response =
 +                      os_strdup(params->ocsp_stapling_response);
 +      else
 +              tls_global->ocsp_stapling_response = NULL;
 +#endif /* HAVE_OCSP */
 +
 +      return 0;
 +}
 +
 +
 +#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST)
 +/* Pre-shared secred requires a patch to openssl, so this function is
 + * 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;
 +
++#if OPENSSL_VERSION_NUMBER < 0x10100000L
 +      if (conn == NULL || conn->session_ticket_cb == NULL)
 +              return 0;
 +
 +      ret = conn->session_ticket_cb(conn->session_ticket_cb_ctx,
 +                                    conn->session_ticket,
 +                                    conn->session_ticket_len,
 +                                    s->s3->client_random,
 +                                    s->s3->server_random, secret);
++#else
++      unsigned char client_random[SSL3_RANDOM_SIZE];
++      unsigned char server_random[SSL3_RANDOM_SIZE];
++
++      if (conn == NULL || conn->session_ticket_cb == NULL)
++              return 0;
++
++      SSL_get_client_random(s, client_random, sizeof(client_random));
++      SSL_get_server_random(s, server_random, sizeof(server_random));
++
++      ret = conn->session_ticket_cb(conn->session_ticket_cb_ctx,
++                                    conn->session_ticket,
++                                    conn->session_ticket_len,
++                                    client_random,
++                                    server_random, secret);
++#endif
++
 +      os_free(conn->session_ticket);
 +      conn->session_ticket = NULL;
 +
 +      if (ret <= 0)
 +              return 0;
 +
 +      *secret_len = SSL_MAX_MASTER_KEY_LENGTH;
 +      return 1;
 +}
 +
 +
 +static int tls_session_ticket_ext_cb(SSL *s, const unsigned char *data,
 +                                   int len, void *arg)
 +{
 +      struct tls_connection *conn = arg;
 +
 +      if (conn == NULL || conn->session_ticket_cb == NULL)
 +              return 0;
 +
 +      wpa_printf(MSG_DEBUG, "OpenSSL: %s: length=%d", __func__, len);
 +
 +      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 0;
 +
 +      os_memcpy(conn->session_ticket, data, len);
 +      conn->session_ticket_len = len;
 +
 +      return 1;
 +}
 +#endif /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */
 +
 +
 +int tls_connection_set_session_ticket_cb(void *tls_ctx,
 +                                       struct tls_connection *conn,
 +                                       tls_session_ticket_cb cb,
 +                                       void *ctx)
 +{
 +#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST)
 +      conn->session_ticket_cb = cb;
 +      conn->session_ticket_cb_ctx = ctx;
 +
 +      if (cb) {
 +              if (SSL_set_session_secret_cb(conn->ssl, tls_sess_sec_cb,
 +                                            conn) != 1)
 +                      return -1;
 +              SSL_set_session_ticket_ext_cb(conn->ssl,
 +                                            tls_session_ticket_ext_cb, conn);
 +      } else {
 +              if (SSL_set_session_secret_cb(conn->ssl, NULL, NULL) != 1)
 +                      return -1;
 +              SSL_set_session_ticket_ext_cb(conn->ssl, NULL, NULL);
 +      }
 +
 +      return 0;
 +#else /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */
 +      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));
 +}
++
++
++void tls_connection_set_success_data(struct tls_connection *conn,
++                                   struct wpabuf *data)
++{
++      SSL_SESSION *sess;
++      struct wpabuf *old;
++
++      if (tls_ex_idx_session < 0)
++              goto fail;
++      sess = SSL_get_session(conn->ssl);
++      if (!sess)
++              goto fail;
++      old = SSL_SESSION_get_ex_data(sess, tls_ex_idx_session);
++      if (old) {
++              wpa_printf(MSG_DEBUG, "OpenSSL: Replacing old success data %p",
++                         old);
++              wpabuf_free(old);
++      }
++      if (SSL_SESSION_set_ex_data(sess, tls_ex_idx_session, data) != 1)
++              goto fail;
++
++      wpa_printf(MSG_DEBUG, "OpenSSL: Stored success data %p", data);
++      conn->success_data = 1;
++      return;
++
++fail:
++      wpa_printf(MSG_INFO, "OpenSSL: Failed to store success data");
++      wpabuf_free(data);
++}
++
++
++void tls_connection_set_success_data_resumed(struct tls_connection *conn)
++{
++      wpa_printf(MSG_DEBUG,
++                 "OpenSSL: Success data accepted for resumed session");
++      conn->success_data = 1;
++}
++
++
++const struct wpabuf *
++tls_connection_get_success_data(struct tls_connection *conn)
++{
++      SSL_SESSION *sess;
++
++      if (tls_ex_idx_session < 0 ||
++          !(sess = SSL_get_session(conn->ssl)))
++              return NULL;
++      return SSL_SESSION_get_ex_data(sess, tls_ex_idx_session);
++}
++
++
++void tls_connection_remove_session(struct tls_connection *conn)
++{
++      SSL_SESSION *sess;
++
++      sess = SSL_get_session(conn->ssl);
++      if (!sess)
++              return;
++
++      if (SSL_CTX_remove_session(conn->ssl_ctx, sess) != 1)
++              wpa_printf(MSG_DEBUG,
++                         "OpenSSL: Session was not cached");
++      else
++              wpa_printf(MSG_DEBUG,
++                         "OpenSSL: Removed cached session to disable session resumption");
++}
index 03bd1a79a14c0f2479aee0e9661dca1b20bd2df1,0000000000000000000000000000000000000000..3cdab5a7a87d61464e140c62da4dc5c182c683bc
mode 100644,000000..100644
--- /dev/null
@@@ -1,4636 -1,0 +1,4704 @@@
-               u8 ssid[32];
 +/*
 + * Driver interface definition
 + * 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.
 + *
 + * This file defines a driver interface used by both %wpa_supplicant and
 + * hostapd. The first part of the file defines data structures used in various
 + * driver operations. This is followed by the struct wpa_driver_ops that each
 + * driver wrapper will beed to define with callback functions for requesting
 + * driver operations. After this, there are definitions for driver event
 + * reporting with wpa_supplicant_event() and some convenience helper functions
 + * that can be used to report events.
 + */
 +
 +#ifndef DRIVER_H
 +#define DRIVER_H
 +
 +#define WPA_SUPPLICANT_DRIVER_VERSION 4
 +
 +#include "common/defs.h"
++#include "common/ieee802_11_defs.h"
 +#include "utils/list.h"
 +
 +#define HOSTAPD_CHAN_DISABLED 0x00000001
 +#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_UNAVAILABLE 0x00000200
 +#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
 + */
 +struct hostapd_channel_data {
 +      /**
 +       * chan - Channel number (IEEE 802.11)
 +       */
 +      short chan;
 +
 +      /**
 +       * freq - Frequency in MHz
 +       */
 +      int freq;
 +
 +      /**
 +       * flag - Channel flags (HOSTAPD_CHAN_*)
 +       */
 +      int flag;
 +
 +      /**
 +       * 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
 + */
 +struct hostapd_hw_modes {
 +      /**
 +       * mode - Hardware mode
 +       */
 +      enum hostapd_hw_mode mode;
 +
 +      /**
 +       * num_channels - Number of entries in the channels array
 +       */
 +      int num_channels;
 +
 +      /**
 +       * channels - Array of supported channels
 +       */
 +      struct hostapd_channel_data *channels;
 +
 +      /**
 +       * num_rates - Number of entries in the rates array
 +       */
 +      int num_rates;
 +
 +      /**
 +       * rates - Array of supported rates in 100 kbps units
 +       */
 +      int *rates;
 +
 +      /**
 +       * ht_capab - HT (IEEE 802.11n) capabilities
 +       */
 +      u16 ht_capab;
 +
 +      /**
 +       * mcs_set - MCS (IEEE 802.11n) rate parameters
 +       */
 +      u8 mcs_set[16];
 +
 +      /**
 +       * a_mpdu_params - A-MPDU (IEEE 802.11n) parameters
 +       */
 +      u8 a_mpdu_params;
 +
 +      /**
 +       * vht_capab - VHT (IEEE 802.11ac) capabilities
 +       */
 +      u32 vht_capab;
 +
 +      /**
 +       * vht_mcs_set - VHT MCS (IEEE 802.11ac) rate parameters
 +       */
 +      u8 vht_mcs_set[8];
 +
 +      unsigned int flags; /* HOSTAPD_MODE_FLAG_* */
 +};
 +
 +
 +#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 */
 +#define IEEE80211_CAP_DMG_MASK        0x0003
 +#define IEEE80211_CAP_DMG_IBSS        0x0001 /* Tx by: STA */
 +#define IEEE80211_CAP_DMG_PBSS        0x0002 /* Tx by: PCP */
 +#define IEEE80211_CAP_DMG_AP  0x0003 /* Tx by: AP */
 +
 +#define WPA_SCAN_QUAL_INVALID         BIT(0)
 +#define WPA_SCAN_NOISE_INVALID                BIT(1)
 +#define WPA_SCAN_LEVEL_INVALID                BIT(2)
 +#define WPA_SCAN_LEVEL_DBM            BIT(3)
 +#define WPA_SCAN_ASSOCIATED           BIT(5)
 +
 +/**
 + * struct wpa_scan_res - Scan result for an BSS/IBSS
 + * @flags: information flags about the BSS/IBSS (WPA_SCAN_*)
 + * @bssid: BSSID
 + * @freq: frequency of the channel in MHz (e.g., 2412 = channel 1)
 + * @beacon_int: beacon interval in TUs (host byte order)
 + * @caps: capability information field in host byte order
 + * @qual: signal quality
 + * @noise: noise level
 + * @level: signal level
 + * @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
 + *
 + * This structure is used as a generic format for scan results from the
 + * driver. Each driver interface implementation is responsible for converting
 + * the driver or OS specific scan results into this format.
 + *
 + * If the driver does not support reporting all IEs, the IE data structure is
 + * 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;
 +      u8 bssid[ETH_ALEN];
 +      int freq;
 +      u16 beacon_int;
 +      u16 caps;
 +      int qual;
 +      int noise;
 +      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 + beacon_ie_len octets of IE data */
 +};
 +
 +/**
 + * struct wpa_scan_results - Scan results
 + * @res: Array of pointers to allocated variable length scan result entries
 + * @num: Number of entries in the scan result array
 + * @fetch_time: Time when the results were fetched from the driver
 + */
 +struct wpa_scan_results {
 +      struct wpa_scan_res **res;
 +      size_t num;
 +      struct os_reltime fetch_time;
 +};
 +
 +/**
 + * struct wpa_interface_info - Network interface information
 + * @next: Pointer to the next interface or NULL if this is the last one
 + * @ifname: Interface name that can be used with init() or init2()
 + * @desc: Human readable adapter description (e.g., vendor/model) or NULL if
 + *    not available
 + * @drv_name: struct wpa_driver_ops::name (note: unlike other strings, this one
 + *    is not an allocated copy, i.e., get_interfaces() caller will not free
 + *    this)
 + */
 +struct wpa_interface_info {
 +      struct wpa_interface_info *next;
 +      char *ifname;
 +      char *desc;
 +      const char *drv_name;
 +};
 +
 +#define WPAS_MAX_SCAN_SSIDS 16
 +
 +/**
 + * struct wpa_driver_scan_params - Scan parameters
 + * Data for struct wpa_driver_ops::scan2().
 + */
 +struct wpa_driver_scan_params {
 +      /**
 +       * ssids - SSIDs to scan for
 +       */
 +      struct wpa_driver_scan_ssid {
 +              /**
 +               * ssid - specific SSID to scan for (ProbeReq)
 +               * %NULL or zero-length SSID is used to indicate active scan
 +               * with wildcard SSID.
 +               */
 +              const u8 *ssid;
 +              /**
 +               * ssid_len: Length of the SSID in octets
 +               */
 +              size_t ssid_len;
 +      } ssids[WPAS_MAX_SCAN_SSIDS];
 +
 +      /**
 +       * num_ssids - Number of entries in ssids array
 +       * Zero indicates a request for a passive scan.
 +       */
 +      size_t num_ssids;
 +
 +      /**
 +       * extra_ies - Extra IE(s) to add into Probe Request or %NULL
 +       */
 +      const u8 *extra_ies;
 +
 +      /**
 +       * extra_ies_len - Length of extra_ies in octets
 +       */
 +      size_t extra_ies_len;
 +
 +      /**
 +       * freqs - Array of frequencies to scan or %NULL for all frequencies
 +       *
 +       * The frequency is set in MHz. The array is zero-terminated.
 +       */
 +      int *freqs;
 +
 +      /**
 +       * filter_ssids - Filter for reporting SSIDs
 +       *
 +       * This optional parameter can be used to request the driver wrapper to
 +       * filter scan results to include only the specified SSIDs. %NULL
 +       * indicates that no filtering is to be done. This can be used to
 +       * reduce memory needs for scan results in environments that have large
 +       * number of APs with different SSIDs.
 +       *
 +       * The driver wrapper is allowed to take this allocated buffer into its
 +       * own use by setting the pointer to %NULL. In that case, the driver
 +       * wrapper is responsible for freeing the buffer with os_free() once it
 +       * is not needed anymore.
 +       */
 +      struct wpa_driver_scan_filter {
-                        int noack);
++              u8 ssid[SSID_MAX_LEN];
 +              size_t ssid_len;
 +      } *filter_ssids;
 +
 +      /**
 +       * num_filter_ssids - Number of entries in filter_ssids array
 +       */
 +      size_t num_filter_ssids;
 +
 +      /**
 +       * filter_rssi - Filter by RSSI
 +       *
 +       * The driver may filter scan results in firmware to reduce host
 +       * wakeups and thereby save power. Specify the RSSI threshold in s32
 +       * dBm.
 +       */
 +      s32 filter_rssi;
 +
 +      /**
 +       * p2p_probe - Used to disable CCK (802.11b) rates for P2P probes
 +       *
 +       * When set, the driver is expected to remove rates 1, 2, 5.5, and 11
 +       * Mbps from the support rates element(s) in the Probe Request frames
 +       * and not to transmit the frames at any of those rates.
 +       */
 +      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.
 +       */
 +};
 +
 +/**
 + * struct wpa_driver_auth_params - Authentication parameters
 + * Data for struct wpa_driver_ops::authenticate().
 + */
 +struct wpa_driver_auth_params {
 +      int freq;
 +      const u8 *bssid;
 +      const u8 *ssid;
 +      size_t ssid_len;
 +      int auth_alg;
 +      const u8 *ie;
 +      size_t ie_len;
 +      const u8 *wep_key[4];
 +      size_t wep_key_len[4];
 +      int wep_tx_keyidx;
 +      int local_state_change;
 +
 +      /**
 +       * p2p - Whether this connection is a P2P group
 +       */
 +      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;
 +
 +      /**
 +       * 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_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;
 +};
 +
 +/**
 + * struct wpa_driver_associate_params - Association parameters
 + * Data for struct wpa_driver_ops::associate().
 + */
 +struct wpa_driver_associate_params {
 +      /**
 +       * bssid - BSSID of the selected AP
 +       * This can be %NULL, if ap_scan=2 mode is used and the driver is
 +       * responsible for selecting with which BSS to associate. */
 +      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;
 +
 +      /**
 +       * ssid_len - Length of the SSID (1..32)
 +       */
 +      size_t ssid_len;
 +
 +      /**
 +       * freq - channel parameters
 +       */
 +      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
 +       * background scan, or -1 to indicate no change to default driver
 +       * configuration
 +       */
 +      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
 +       * of this WPA IE is optional. If the driver generates the WPA
 +       * IE, it can use pairwise_suite, group_suite, and
 +       * key_mgmt_suite to select proper algorithms. In this case,
 +       * the driver has to notify wpa_supplicant about the used WPA
 +       * IE by generating an event that the interface code will
 +       * convert into EVENT_ASSOCINFO data (see below).
 +       *
 +       * When using WPA2/IEEE 802.11i, wpa_ie is used for RSN IE
 +       * instead. The driver can determine which version is used by
 +       * looking at the first byte of the IE (0xdd for WPA, 0x30 for
 +       * WPA2/RSN).
 +       *
 +       * When using WPS, wpa_ie is used for WPS IE instead of WPA/RSN IE.
 +       */
 +      const u8 *wpa_ie;
 +
 +      /**
 +       * wpa_ie_len - length of the wpa_ie
 +       */
 +      size_t wpa_ie_len;
 +
 +      /**
 +       * wpa_proto - Bitfield of WPA_PROTO_* values to indicate WPA/WPA2
 +       */
 +      unsigned int wpa_proto;
 +
 +      /**
 +       * pairwise_suite - Selected pairwise cipher suite (WPA_CIPHER_*)
 +       *
 +       * This is usually ignored if @wpa_ie is used.
 +       */
 +      unsigned int pairwise_suite;
 +
 +      /**
 +       * group_suite - Selected group cipher suite (WPA_CIPHER_*)
 +       *
 +       * This is usually ignored if @wpa_ie is used.
 +       */
 +      unsigned int group_suite;
 +
 +      /**
 +       * key_mgmt_suite - Selected key management suite (WPA_KEY_MGMT_*)
 +       *
 +       * This is usually ignored if @wpa_ie is used.
 +       */
 +      unsigned int key_mgmt_suite;
 +
 +      /**
 +       * auth_alg - Allowed authentication algorithms
 +       * Bit field of WPA_AUTH_ALG_*
 +       */
 +      int auth_alg;
 +
 +      /**
 +       * mode - Operation mode (infra/ibss) IEEE80211_MODE_*
 +       */
 +      int mode;
 +
 +      /**
 +       * wep_key - WEP keys for static WEP configuration
 +       */
 +      const u8 *wep_key[4];
 +
 +      /**
 +       * wep_key_len - WEP key length for static WEP configuration
 +       */
 +      size_t wep_key_len[4];
 +
 +      /**
 +       * wep_tx_keyidx - WEP TX key index for static WEP configuration
 +       */
 +      int wep_tx_keyidx;
 +
 +      /**
 +       * mgmt_frame_protection - IEEE 802.11w management frame protection
 +       */
 +      enum mfp_options mgmt_frame_protection;
 +
 +      /**
 +       * ft_ies - IEEE 802.11r / FT information elements
 +       * If the supplicant is using IEEE 802.11r (FT) and has the needed keys
 +       * for fast transition, this parameter is set to include the IEs that
 +       * are to be sent in the next FT Authentication Request message.
 +       * update_ft_ies() handler is called to update the IEs for further
 +       * FT messages in the sequence.
 +       *
 +       * The driver should use these IEs only if the target AP is advertising
 +       * the same mobility domain as the one included in the MDIE here.
 +       *
 +       * In ap_scan=2 mode, the driver can use these IEs when moving to a new
 +       * AP after the initial association. These IEs can only be used if the
 +       * target AP is advertising support for FT and is using the same MDIE
 +       * and SSID as the current AP.
 +       *
 +       * The driver is responsible for reporting the FT IEs received from the
 +       * AP's response using wpa_supplicant_event() with EVENT_FT_RESPONSE
 +       * type. update_ft_ies() handler will then be called with the FT IEs to
 +       * include in the next frame in the authentication sequence.
 +       */
 +      const u8 *ft_ies;
 +
 +      /**
 +       * ft_ies_len - Length of ft_ies in bytes
 +       */
 +      size_t ft_ies_len;
 +
 +      /**
 +       * ft_md - FT Mobility domain (6 octets) (also included inside ft_ies)
 +       *
 +       * This value is provided to allow the driver interface easier access
 +       * to the current mobility domain. This value is set to %NULL if no
 +       * mobility domain is currently active.
 +       */
 +      const u8 *ft_md;
 +
 +      /**
 +       * passphrase - RSN passphrase for PSK
 +       *
 +       * This value is made available only for WPA/WPA2-Personal (PSK) and
 +       * only for drivers that set WPA_DRIVER_FLAGS_4WAY_HANDSHAKE. This is
 +       * the 8..63 character ASCII passphrase, if available. Please note that
 +       * this can be %NULL if passphrase was not used to generate the PSK. In
 +       * that case, the psk field must be used to fetch the PSK.
 +       */
 +      const char *passphrase;
 +
 +      /**
 +       * psk - RSN PSK (alternative for passphrase for PSK)
 +       *
 +       * This value is made available only for WPA/WPA2-Personal (PSK) and
 +       * only for drivers that set WPA_DRIVER_FLAGS_4WAY_HANDSHAKE. This is
 +       * the 32-octet (256-bit) PSK, if available. The driver wrapper should
 +       * be prepared to handle %NULL value as an error.
 +       */
 +      const u8 *psk;
 +
 +      /**
 +       * drop_unencrypted - Enable/disable unencrypted frame filtering
 +       *
 +       * Configure the driver to drop all non-EAPOL frames (both receive and
 +       * transmit paths). Unencrypted EAPOL frames (ethertype 0x888e) must
 +       * still be allowed for key negotiation.
 +       */
 +      int drop_unencrypted;
 +
 +      /**
 +       * prev_bssid - Previously used BSSID in this ESS
 +       *
 +       * When not %NULL, this is a request to use reassociation instead of
 +       * association.
 +       */
 +      const u8 *prev_bssid;
 +
 +      /**
 +       * wps - WPS mode
 +       *
 +       * If the driver needs to do special configuration for WPS association,
 +       * this variable provides more information on what type of association
 +       * is being requested. Most drivers should not need ot use this.
 +       */
 +      enum wps_mode wps;
 +
 +      /**
 +       * p2p - Whether this connection is a P2P group
 +       */
 +      int p2p;
 +
 +      /**
 +       * uapsd - UAPSD parameters for the network
 +       * -1 = do not change defaults
 +       * AP mode: 1 = enabled, 0 = disabled
 +       * STA mode: bits 0..3 UAPSD enabled for VO,VI,BK,BE
 +       */
 +      int uapsd;
 +
 +      /**
 +       * fixed_bssid - Whether to force this BSSID in IBSS mode
 +       * 1 = Fix this BSSID and prevent merges.
 +       * 0 = Do not fix BSSID.
 +       */
 +      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;
 +
 +      /**
 +       * 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_mask;
 +
 +#ifdef CONFIG_VHT_OVERRIDES
 +      /**
 +       * disable_vht - Disable VHT for this connection
 +       */
 +      int disable_vht;
 +
 +      /**
 +       * VHT capability overrides.
 +       */
 +      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 {
 +      NO_SSID_HIDING,
 +      HIDDEN_SSID_ZERO_LEN,
 +      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
 +       */
 +      u8 *head;
 +
 +      /**
 +       * head_len - Length of the head buffer in octets
 +       */
 +      size_t head_len;
 +
 +      /**
 +       * tail - Beacon tail following TIM IE
 +       */
 +      u8 *tail;
 +
 +      /**
 +       * tail_len - Length of the tail buffer in octets
 +       */
 +      size_t tail_len;
 +
 +      /**
 +       * dtim_period - DTIM period
 +       */
 +      int dtim_period;
 +
 +      /**
 +       * beacon_int - Beacon interval
 +       */
 +      int beacon_int;
 +
 +      /**
 +       * basic_rates: -1 terminated array of basic rates in 100 kbps
 +       *
 +       * This parameter can be used to set a specific basic rate set for the
 +       * BSS. If %NULL, default basic rate set is used.
 +       */
 +      int *basic_rates;
 +
 +      /**
 +       * proberesp - Probe Response template
 +       *
 +       * This is used by drivers that reply to Probe Requests internally in
 +       * AP mode and require the full Probe Response template.
 +       */
 +      u8 *proberesp;
 +
 +      /**
 +       * proberesp_len - Length of the proberesp buffer in octets
 +       */
 +      size_t proberesp_len;
 +
 +      /**
 +       * ssid - The SSID to use in Beacon/Probe Response frames
 +       */
 +      const u8 *ssid;
 +
 +      /**
 +       * ssid_len - Length of the SSID (1..32)
 +       */
 +      size_t ssid_len;
 +
 +      /**
 +       * hide_ssid - Whether to hide the SSID
 +       */
 +      enum hide_ssid hide_ssid;
 +
 +      /**
 +       * pairwise_ciphers - WPA_CIPHER_* bitfield
 +       */
 +      unsigned int pairwise_ciphers;
 +
 +      /**
 +       * group_cipher - WPA_CIPHER_*
 +       */
 +      unsigned int group_cipher;
 +
 +      /**
 +       * key_mgmt_suites - WPA_KEY_MGMT_* bitfield
 +       */
 +      unsigned int key_mgmt_suites;
 +
 +      /**
 +       * auth_algs - WPA_AUTH_ALG_* bitfield
 +       */
 +      unsigned int auth_algs;
 +
 +      /**
 +       * wpa_version - WPA_PROTO_* bitfield
 +       */
 +      unsigned int wpa_version;
 +
 +      /**
 +       * privacy - Whether privacy is used in the BSS
 +       */
 +      int privacy;
 +
 +      /**
 +       * beacon_ies - WPS/P2P IE(s) for Beacon frames
 +       *
 +       * This is used to add IEs like WPS IE and P2P IE by drivers that do
 +       * not use the full Beacon template.
 +       */
 +      const struct wpabuf *beacon_ies;
 +
 +      /**
 +       * proberesp_ies - P2P/WPS IE(s) for Probe Response frames
 +       *
 +       * This is used to add IEs like WPS IE and P2P IE by drivers that
 +       * reply to Probe Request frames internally.
 +       */
 +      const struct wpabuf *proberesp_ies;
 +
 +      /**
 +       * assocresp_ies - WPS IE(s) for (Re)Association Response frames
 +       *
 +       * This is used to add IEs like WPS IE by drivers that reply to
 +       * (Re)Association Request frames internally.
 +       */
 +      const struct wpabuf *assocresp_ies;
 +
 +      /**
 +       * isolate - Whether to isolate frames between associated stations
 +       *
 +       * If this is non-zero, the AP is requested to disable forwarding of
 +       * frames between associated stations.
 +       */
 +      int isolate;
 +
 +      /**
 +       * cts_protect - Whether CTS protection is enabled
 +       */
 +      int cts_protect;
 +
 +      /**
 +       * preamble - Whether short preamble is enabled
 +       */
 +      int preamble;
 +
 +      /**
 +       * short_slot_time - Whether short slot time is enabled
 +       *
 +       * 0 = short slot time disable, 1 = short slot time enabled, -1 = do
 +       * not set (e.g., when 802.11g mode is not in use)
 +       */
 +      int short_slot_time;
 +
 +      /**
 +       * ht_opmode - HT operation mode or -1 if HT not in use
 +       */
 +      int ht_opmode;
 +
 +      /**
 +       * interworking - Whether Interworking is enabled
 +       */
 +      int interworking;
 +
 +      /**
 +       * hessid - Homogeneous ESS identifier or %NULL if not set
 +       */
 +      const u8 *hessid;
 +
 +      /**
 +       * access_network_type - Access Network Type (0..15)
 +       *
 +       * This is used for filtering Probe Request frames when Interworking is
 +       * enabled.
 +       */
 +      u8 access_network_type;
 +
 +      /**
 +       * ap_max_inactivity - Timeout in seconds to detect STA's inactivity
 +       *
 +       * This is used by driver which advertises this capability.
 +       */
 +      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;
 +};
 +
 +/**
 + * struct wpa_driver_capa - Driver capability information
 + */
 +struct wpa_driver_capa {
 +#define WPA_DRIVER_CAPA_KEY_MGMT_WPA          0x00000001
 +#define WPA_DRIVER_CAPA_KEY_MGMT_WPA2         0x00000002
 +#define WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK      0x00000004
 +#define WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK     0x00000008
 +#define WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE     0x00000010
 +#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
 +#define WPA_DRIVER_CAPA_ENC_WEP104    0x00000002
 +#define WPA_DRIVER_CAPA_ENC_TKIP      0x00000004
 +#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 */
 +#define WPA_DRIVER_FLAGS_DRIVER_IE    0x00000001
 +/** Driver needs static WEP key setup after association command */
 +#define WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC 0x00000002
 +/** 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
 + * wpa_supplicant). */
 +#define WPA_DRIVER_FLAGS_SME          0x00000020
 +/** Driver supports AP mode */
 +#define WPA_DRIVER_FLAGS_AP           0x00000040
 +/** Driver needs static WEP key setup after association has been completed */
 +#define WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC_DONE    0x00000080
 +/** 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) */
 +#define WPA_DRIVER_FLAGS_P2P_CAPABLE  0x00000800
 +/** 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 */
 +#define WPA_DRIVER_FLAGS_OFFCHANNEL_TX                        0x00008000
 +/** 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 */
 +#define WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS             0x00020000
 +/** Driver supports roaming (BSS selection) in firmware */
 +#define WPA_DRIVER_FLAGS_BSS_SELECTION                        0x00040000
 +/** Driver supports operating as a TDLS peer */
 +#define WPA_DRIVER_FLAGS_TDLS_SUPPORT                 0x00080000
 +/** 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 */
 +#define WPA_DRIVER_FLAGS_PROBE_RESP_OFFLOAD           0x00200000
 +/** Driver supports U-APSD in AP mode */
 +#define WPA_DRIVER_FLAGS_AP_UAPSD                     0x00400000
 +/** Driver supports inactivity timer in AP mode */
 +#define WPA_DRIVER_FLAGS_INACTIVITY_TIMER             0x00800000
 +/** Driver expects user space implementation of MLME in AP mode */
 +#define WPA_DRIVER_FLAGS_AP_MLME                      0x01000000
 +/** Driver supports SAE with user space SME */
 +#define WPA_DRIVER_FLAGS_SAE                          0x02000000
 +/** Driver makes use of OBSS scan mechanism in wpa_supplicant */
 +#define WPA_DRIVER_FLAGS_OBSS_SCAN                    0x04000000
 +/** Driver supports IBSS (Ad-hoc) mode */
 +#define WPA_DRIVER_FLAGS_IBSS                         0x08000000
 +/** Driver supports radar detection */
 +#define WPA_DRIVER_FLAGS_RADAR                                0x10000000
 +/** Driver supports a dedicated interface for P2P Device */
 +#define WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE         0x20000000
 +/** 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
++/** Driver supports automatic band selection */
++#define WPA_DRIVER_FLAGS_SUPPORT_HW_MODE_ANY  0x0000004000000000ULL
 +      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;
 +
 +      /**
 +       * max_remain_on_chan - Maximum remain-on-channel duration in msec
 +       */
 +      unsigned int max_remain_on_chan;
 +
 +      /**
 +       * max_stations - Maximum number of associated stations the driver
 +       * supports in AP mode
 +       */
 +      unsigned int max_stations;
 +
 +      /**
 +       * probe_resp_offloads - Bitmap of supported protocols by the driver
 +       * for Probe Response offloading.
 +       */
 +/** 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 */
 +#define WPA_DRIVER_PROBE_RESP_OFFLOAD_WPS2            0x00000002
 +/** 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) */
 +#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
 +       * valid for the lifetime of the driver, i.e., freed in deinit()
 +       */
 +      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;
++
++      /* Driver concurrency capabilities */
++      unsigned int conc_capab;
++      /* Maximum number of concurrent channels on 2.4 GHz */
++      unsigned int max_conc_chan_2_4;
++      /* Maximum number of concurrent channels on 5 GHz */
++      unsigned int max_conc_chan_5_0;
 +};
 +
 +
 +struct hostapd_data;
 +
 +struct hostap_sta_driver_data {
 +      unsigned long rx_packets, tx_packets, rx_bytes, tx_bytes;
 +      unsigned long current_tx_rate;
 +      unsigned long inactive_msec;
 +      unsigned long flags;
 +      unsigned long num_ps_buf_frames;
 +      unsigned long tx_retry_failed;
 +      unsigned long tx_retry_count;
 +      int last_rssi;
 +      int last_ack_rssi;
 +};
 +
 +struct hostapd_sta_add_params {
 +      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_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;
 +      const u8 *supp_channels;
 +      size_t supp_channels_len;
 +      const u8 *supp_oper_classes;
 +      size_t supp_oper_classes_len;
 +};
 +
 +struct mac_address {
 +      u8 addr[ETH_ALEN];
 +};
 +
 +struct hostapd_acl_params {
 +      u8 acl_policy;
 +      unsigned int num_mac_acl;
 +      struct mac_address mac_acl[0];
 +};
 +
 +enum wpa_driver_if_type {
 +      /**
 +       * WPA_IF_STATION - Station mode interface
 +       */
 +      WPA_IF_STATION,
 +
 +      /**
 +       * WPA_IF_AP_VLAN - AP mode VLAN interface
 +       *
 +       * This interface shares its address and Beacon frame with the main
 +       * BSS.
 +       */
 +      WPA_IF_AP_VLAN,
 +
 +      /**
 +       * WPA_IF_AP_BSS - AP mode BSS interface
 +       *
 +       * This interface has its own address and Beacon frame.
 +       */
 +      WPA_IF_AP_BSS,
 +
 +      /**
 +       * WPA_IF_P2P_GO - P2P Group Owner
 +       */
 +      WPA_IF_P2P_GO,
 +
 +      /**
 +       * WPA_IF_P2P_CLIENT - P2P Client
 +       */
 +      WPA_IF_P2P_CLIENT,
 +
 +      /**
 +       * WPA_IF_P2P_GROUP - P2P Group interface (will become either
 +       * WPA_IF_P2P_GO or WPA_IF_P2P_CLIENT, but the role is not yet known)
 +       */
 +      WPA_IF_P2P_GROUP,
 +
 +      /**
 +       * 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_MESH - Mesh interface
 +       */
 +      WPA_IF_MESH,
++
++      /*
++       * WPA_IF_TDLS - TDLS offchannel interface (used for pref freq only)
++       */
++      WPA_IF_TDLS,
++
++      /*
++       * WPA_IF_IBSS - IBSS interface (used for pref freq only)
++       */
++      WPA_IF_IBSS,
 +};
 +
 +struct wpa_init_params {
 +      void *global_priv;
 +      const u8 *bssid;
 +      const char *ifname;
 +      const char *driver_params;
 +      int use_pae_group_addr;
 +      char **bridge;
 +      size_t num_bridge;
 +
 +      u8 *own_addr; /* buffer for writing own MAC address */
 +};
 +
 +
 +struct wpa_bss_params {
 +      /** Interface name (for multi-SSID/VLAN support) */
 +      const char *ifname;
 +      /** Whether IEEE 802.1X or WPA/WPA2 is enabled */
 +      int enabled;
 +
 +      int wpa;
 +      int ieee802_1x;
 +      int wpa_group;
 +      int wpa_pairwise;
 +      int wpa_key_mgmt;
 +      int rsn_preauth;
 +      enum mfp_options ieee80211w;
 +};
 +
 +#define WPA_STA_AUTHORIZED BIT(0)
 +#define WPA_STA_WMM BIT(1)
 +#define WPA_STA_SHORT_PREAMBLE BIT(2)
 +#define WPA_STA_MFP BIT(3)
 +#define WPA_STA_TDLS_PEER BIT(4)
 +#define WPA_STA_AUTHENTICATED BIT(5)
 +
 +enum tdls_oper {
 +      TDLS_DISCOVERY_REQ,
 +      TDLS_SETUP,
 +      TDLS_TEARDOWN,
 +      TDLS_ENABLE_LINK,
 +      TDLS_DISABLE_LINK,
 +      TDLS_ENABLE,
 +      TDLS_DISABLE
 +};
 +
 +enum wnm_oper {
 +      WNM_SLEEP_ENTER_CONFIRM,
 +      WNM_SLEEP_ENTER_FAIL,
 +      WNM_SLEEP_EXIT_CONFIRM,
 +      WNM_SLEEP_EXIT_FAIL,
 +      WNM_SLEEP_TFS_REQ_IE_ADD,   /* STA requests driver to add TFS req IE */
 +      WNM_SLEEP_TFS_REQ_IE_NONE,  /* STA requests empty TFS req IE */
 +      WNM_SLEEP_TFS_REQ_IE_SET,   /* AP requests driver to set TFS req IE for
 +                                   * a STA */
 +      WNM_SLEEP_TFS_RESP_IE_ADD,  /* AP requests driver to add TFS resp IE
 +                                   * for a STA */
 +      WNM_SLEEP_TFS_RESP_IE_NONE, /* AP requests empty TFS resp IE */
 +      WNM_SLEEP_TFS_RESP_IE_SET,  /* AP requests driver to set TFS resp IE
 +                                   * for a STA */
 +      WNM_SLEEP_TFS_IE_DEL        /* AP delete the TFS IE */
 +};
 +
 +/* enum chan_width - Channel width definitions */
 +enum chan_width {
 +      CHAN_WIDTH_20_NOHT,
 +      CHAN_WIDTH_20,
 +      CHAN_WIDTH_40,
 +      CHAN_WIDTH_80,
 +      CHAN_WIDTH_80P80,
 +      CHAN_WIDTH_160,
 +      CHAN_WIDTH_UNKNOWN
 +};
 +
 +/**
 + * struct wpa_signal_info - Information about channel signal quality
 + */
 +struct wpa_signal_info {
 +      u32 frequency;
 +      int above_threshold;
 +      int current_signal;
 +      int avg_signal;
++      int avg_beacon_signal;
 +      int current_noise;
 +      int current_txrate;
 +      enum chan_width chanwidth;
 +      int center_frq1;
 +      int center_frq2;
 +};
 +
 +/**
 + * 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,
++      DRV_BR_MULTICAST_SNOOPING,
 +};
 +
 +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;
++
++      /* Indicates whether VHT is enabled */
++      int vht_enabled;
++
++      /* Configured ACS channel width */
++      u16 ch_width;
++
++      /* ACS channel list info */
++      unsigned int ch_list_len;
++      const u8 *ch_list;
++      const int *freq_list;
 +};
 +
 +
 +/**
 + * struct wpa_driver_ops - Driver interface API definition
 + *
 + * This structure defines the API that each driver interface needs to implement
 + * for core wpa_supplicant code. All driver specific functionality is captured
 + * in this wrapper.
 + */
 +struct wpa_driver_ops {
 +      /** Name of the driver interface */
 +      const char *name;
 +      /** One line description of the driver interface */
 +      const char *desc;
 +
 +      /**
 +       * get_bssid - Get the current BSSID
 +       * @priv: private driver interface data
 +       * @bssid: buffer for BSSID (ETH_ALEN = 6 bytes)
 +       *
 +       * Returns: 0 on success, -1 on failure
 +       *
 +       * Query kernel driver for the current BSSID and copy it to bssid.
 +       * Setting bssid to 00:00:00:00:00:00 is recommended if the STA is not
 +       * associated.
 +       */
 +      int (*get_bssid)(void *priv, u8 *bssid);
 +
 +      /**
 +       * get_ssid - Get the current SSID
 +       * @priv: private driver interface data
 +       * @ssid: buffer for SSID (at least 32 bytes)
 +       *
 +       * Returns: Length of the SSID on success, -1 on failure
 +       *
 +       * Query kernel driver for the current SSID and copy it to ssid.
 +       * Returning zero is recommended if the STA is not associated.
 +       *
 +       * Note: SSID is an array of octets, i.e., it is not nul terminated and
 +       * can, at least in theory, contain control characters (including nul)
 +       * and as such, should be processed as binary data, not a printable
 +       * string.
 +       */
 +      int (*get_ssid)(void *priv, u8 *ssid);
 +
 +      /**
 +       * set_key - Configure encryption key
 +       * @ifname: Interface name (for multi-SSID/VLAN support)
 +       * @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_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
 +       *      broadcast keys, %NULL for default keys that are used both for
 +       *      broadcast and unicast; when clearing keys, %NULL is used to
 +       *      indicate that both the broadcast-only and default key of the
 +       *      specified key index is to be cleared
 +       * @key_idx: key index (0..3), usually 0 for unicast keys; 0..4095 for
 +       *      IGTK
 +       * @set_tx: configure this key as the default Tx key (only used when
 +       *      driver does not support separate unicast/individual key
 +       * @seq: sequence number/packet number, seq_len octets, the next
 +       *      packet number to be used for in replay protection; configured
 +       *      for Rx keys (in most cases, this is only used with broadcast
 +       *      keys and set to zero for unicast keys); %NULL if not set
 +       * @seq_len: length of the seq, depends on the algorithm:
 +       *      TKIP: 6 octets, CCMP/GCMP: 6 octets, IGTK: 6 octets
 +       * @key: key buffer; TKIP: 16-byte temporal key, 8-byte Tx Mic key,
 +       *      8-byte Rx Mic Key
 +       * @key_len: length of the key buffer in octets (WEP: 5 or 13,
 +       *      TKIP: 32, CCMP/GCMP: 16, IGTK: 16)
 +       *
 +       * Returns: 0 on success, -1 on failure
 +       *
 +       * Configure the given key for the kernel driver. If the driver
 +       * supports separate individual keys (4 default keys + 1 individual),
 +       * addr can be used to determine whether the key is default or
 +       * individual. If only 4 keys are supported, the default key with key
 +       * index 0 is used as the individual key. STA must be configured to use
 +       * it as the default Tx key (set_tx is set) and accept Rx for all the
 +       * key indexes. In most cases, WPA uses only key indexes 1 and 2 for
 +       * broadcast keys, so key index 0 is available for this kind of
 +       * configuration.
 +       *
 +       * Please note that TKIP keys include separate TX and RX MIC keys and
 +       * some drivers may expect them in different order than wpa_supplicant
 +       * is using. If the TX/RX keys are swapped, all TKIP encrypted packets
 +       * will trigger Michael MIC errors. This can be fixed by changing the
 +       * order of MIC keys by swapping te bytes 16..23 and 24..31 of the key
 +       * in driver_*.c set_key() implementation, see driver_ndis.c for an
 +       * example on how this can be done.
 +       */
 +      int (*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);
 +
 +      /**
 +       * init - Initialize driver interface
 +       * @ctx: context to be used when calling wpa_supplicant functions,
 +       * e.g., wpa_supplicant_event()
 +       * @ifname: interface name, e.g., wlan0
 +       *
 +       * Returns: Pointer to private data, %NULL on failure
 +       *
 +       * Initialize driver interface, including event processing for kernel
 +       * driver events (e.g., associated, scan results, Michael MIC failure).
 +       * This function can allocate a private configuration data area for
 +       * @ctx, file descriptor, interface name, etc. information that may be
 +       * needed in future driver operations. If this is not used, non-NULL
 +       * value will need to be returned because %NULL is used to indicate
 +       * failure. The returned value will be used as 'void *priv' data for
 +       * all other driver_ops functions.
 +       *
 +       * The main event loop (eloop.c) of wpa_supplicant can be used to
 +       * register callback for read sockets (eloop_register_read_sock()).
 +       *
 +       * See below for more information about events and
 +       * wpa_supplicant_event() function.
 +       */
 +      void * (*init)(void *ctx, const char *ifname);
 +
 +      /**
 +       * deinit - Deinitialize driver interface
 +       * @priv: private driver interface data from init()
 +       *
 +       * Shut down driver interface and processing of driver events. Free
 +       * private data buffer if one was allocated in init() handler.
 +       */
 +      void (*deinit)(void *priv);
 +
 +      /**
 +       * set_param - Set driver configuration parameters
 +       * @priv: private driver interface data from init()
 +       * @param: driver specific configuration parameters
 +       *
 +       * Returns: 0 on success, -1 on failure
 +       *
 +       * Optional handler for notifying driver interface about configuration
 +       * parameters (driver_param).
 +       */
 +      int (*set_param)(void *priv, const char *param);
 +
 +      /**
 +       * set_countermeasures - Enable/disable TKIP countermeasures
 +       * @priv: private driver interface data
 +       * @enabled: 1 = countermeasures enabled, 0 = disabled
 +       *
 +       * Returns: 0 on success, -1 on failure
 +       *
 +       * Configure TKIP countermeasures. When these are enabled, the driver
 +       * should drop all received and queued frames that are using TKIP.
 +       */
 +      int (*set_countermeasures)(void *priv, int enabled);
 +
 +      /**
 +       * deauthenticate - Request driver to deauthenticate
 +       * @priv: private driver interface data
 +       * @addr: peer address (BSSID of the AP)
 +       * @reason_code: 16-bit reason code to be sent in the deauthentication
 +       *      frame
 +       *
 +       * Returns: 0 on success, -1 on failure
 +       */
 +      int (*deauthenticate)(void *priv, const u8 *addr, int reason_code);
 +
 +      /**
 +       * associate - Request driver to associate
 +       * @priv: private driver interface data
 +       * @params: association parameters
 +       *
 +       * Returns: 0 on success, -1 on failure
 +       */
 +      int (*associate)(void *priv,
 +                       struct wpa_driver_associate_params *params);
 +
 +      /**
 +       * add_pmkid - Add PMKSA cache entry to the driver
 +       * @priv: private driver interface data
 +       * @bssid: BSSID for the PMKSA cache entry
 +       * @pmkid: PMKID for the PMKSA cache entry
 +       *
 +       * Returns: 0 on success, -1 on failure
 +       *
 +       * This function is called when a new PMK is received, as a result of
 +       * either normal authentication or RSN pre-authentication.
 +       *
 +       * If the driver generates RSN IE, i.e., it does not use wpa_ie in
 +       * associate(), add_pmkid() can be used to add new PMKSA cache entries
 +       * in the driver. If the driver uses wpa_ie from wpa_supplicant, this
 +       * driver_ops function does not need to be implemented. Likewise, if
 +       * the driver does not support WPA, this function is not needed.
 +       */
 +      int (*add_pmkid)(void *priv, const u8 *bssid, const u8 *pmkid);
 +
 +      /**
 +       * remove_pmkid - Remove PMKSA cache entry to the driver
 +       * @priv: private driver interface data
 +       * @bssid: BSSID for the PMKSA cache entry
 +       * @pmkid: PMKID for the PMKSA cache entry
 +       *
 +       * Returns: 0 on success, -1 on failure
 +       *
 +       * This function is called when the supplicant drops a PMKSA cache
 +       * entry for any reason.
 +       *
 +       * If the driver generates RSN IE, i.e., it does not use wpa_ie in
 +       * associate(), remove_pmkid() can be used to synchronize PMKSA caches
 +       * between the driver and wpa_supplicant. If the driver uses wpa_ie
 +       * from wpa_supplicant, this driver_ops function does not need to be
 +       * implemented. Likewise, if the driver does not support WPA, this
 +       * function is not needed.
 +       */
 +      int (*remove_pmkid)(void *priv, const u8 *bssid, const u8 *pmkid);
 +
 +      /**
 +       * flush_pmkid - Flush PMKSA cache
 +       * @priv: private driver interface data
 +       *
 +       * Returns: 0 on success, -1 on failure
 +       *
 +       * This function is called when the supplicant drops all PMKSA cache
 +       * entries for any reason.
 +       *
 +       * If the driver generates RSN IE, i.e., it does not use wpa_ie in
 +       * associate(), remove_pmkid() can be used to synchronize PMKSA caches
 +       * between the driver and wpa_supplicant. If the driver uses wpa_ie
 +       * from wpa_supplicant, this driver_ops function does not need to be
 +       * implemented. Likewise, if the driver does not support WPA, this
 +       * function is not needed.
 +       */
 +      int (*flush_pmkid)(void *priv);
 +
 +      /**
 +       * get_capa - Get driver capabilities
 +       * @priv: private driver interface data
 +       *
 +       * Returns: 0 on success, -1 on failure
 +       *
 +       * Get driver/firmware/hardware capabilities.
 +       */
 +      int (*get_capa)(void *priv, struct wpa_driver_capa *capa);
 +
 +      /**
 +       * poll - Poll driver for association information
 +       * @priv: private driver interface data
 +       *
 +       * This is an option callback that can be used when the driver does not
 +       * provide event mechanism for association events. This is called when
 +       * receiving WPA EAPOL-Key messages that require association
 +       * information. The driver interface is supposed to generate associnfo
 +       * event before returning from this callback function. In addition, the
 +       * driver interface should generate an association event after having
 +       * sent out associnfo.
 +       */
 +      void (*poll)(void *priv);
 +
 +      /**
 +       * get_ifname - Get interface name
 +       * @priv: private driver interface data
 +       *
 +       * Returns: Pointer to the interface name. This can differ from the
 +       * interface name used in init() call. Init() is called first.
 +       *
 +       * This optional function can be used to allow the driver interface to
 +       * replace the interface name with something else, e.g., based on an
 +       * interface mapping from a more descriptive name.
 +       */
 +      const char * (*get_ifname)(void *priv);
 +
 +      /**
 +       * get_mac_addr - Get own MAC address
 +       * @priv: private driver interface data
 +       *
 +       * Returns: Pointer to own MAC address or %NULL on failure
 +       *
 +       * This optional function can be used to get the own MAC address of the
 +       * device from the driver interface code. This is only needed if the
 +       * l2_packet implementation for the OS does not provide easy access to
 +       * a MAC address. */
 +      const u8 * (*get_mac_addr)(void *priv);
 +
 +      /**
 +       * set_operstate - Sets device operating state to DORMANT or UP
 +       * @priv: private driver interface data
 +       * @state: 0 = dormant, 1 = up
 +       * Returns: 0 on success, -1 on failure
 +       *
 +       * This is an optional function that can be used on operating systems
 +       * that support a concept of controlling network device state from user
 +       * space applications. This function, if set, gets called with
 +       * state = 1 when authentication has been completed and with state = 0
 +       * when connection is lost.
 +       */
 +      int (*set_operstate)(void *priv, int state);
 +
 +      /**
 +       * mlme_setprotection - MLME-SETPROTECTION.request primitive
 +       * @priv: Private driver interface data
 +       * @addr: Address of the station for which to set protection (may be
 +       * %NULL for group keys)
 +       * @protect_type: MLME_SETPROTECTION_PROTECT_TYPE_*
 +       * @key_type: MLME_SETPROTECTION_KEY_TYPE_*
 +       * Returns: 0 on success, -1 on failure
 +       *
 +       * This is an optional function that can be used to set the driver to
 +       * require protection for Tx and/or Rx frames. This uses the layer
 +       * interface defined in IEEE 802.11i-2004 clause 10.3.22.1
 +       * (MLME-SETPROTECTION.request). Many drivers do not use explicit
 +       * set protection operation; instead, they set protection implicitly
 +       * based on configured keys.
 +       */
 +      int (*mlme_setprotection)(void *priv, const u8 *addr, int protect_type,
 +                                int key_type);
 +
 +      /**
 +       * get_hw_feature_data - Get hardware support data (channels and rates)
 +       * @priv: Private driver interface data
 +       * @num_modes: Variable for returning the number of returned modes
 +       * flags: Variable for returning hardware feature flags
 +       * Returns: Pointer to allocated hardware data on success or %NULL on
 +       * failure. Caller is responsible for freeing this.
 +       */
 +      struct hostapd_hw_modes * (*get_hw_feature_data)(void *priv,
 +                                                       u16 *num_modes,
 +                                                       u16 *flags);
 +
 +      /**
 +       * send_mlme - Send management frame from MLME
 +       * @priv: Private driver interface data
 +       * @data: IEEE 802.11 management frame with IEEE 802.11 header
 +       * @data_len: Size of the management frame
 +       * @noack: Do not wait for this frame to be acked (disable retries)
++       * @freq: Frequency (in MHz) to send the frame on, or 0 to let the
++       * driver decide
 +       * Returns: 0 on success, -1 on failure
 +       */
 +      int (*send_mlme)(void *priv, const u8 *data, size_t data_len,
-                            int total_flags, int flags_or, int flags_and);
++                       int noack, unsigned int freq);
 +
 +      /**
 +       * update_ft_ies - Update FT (IEEE 802.11r) IEs
 +       * @priv: Private driver interface data
 +       * @md: Mobility domain (2 octets) (also included inside ies)
 +       * @ies: FT IEs (MDIE, FTIE, ...) or %NULL to remove IEs
 +       * @ies_len: Length of FT IEs in bytes
 +       * Returns: 0 on success, -1 on failure
 +       *
 +       * The supplicant uses this callback to let the driver know that keying
 +       * material for FT is available and that the driver can use the
 +       * provided IEs in the next message in FT authentication sequence.
 +       *
 +       * This function is only needed for driver that support IEEE 802.11r
 +       * (Fast BSS Transition).
 +       */
 +      int (*update_ft_ies)(void *priv, const u8 *md, const u8 *ies,
 +                           size_t ies_len);
 +
 +      /**
 +       * get_scan_results2 - Fetch the latest scan results
 +       * @priv: private driver interface data
 +       *
 +       * Returns: Allocated buffer of scan results (caller is responsible for
 +       * freeing the data structure) on success, NULL on failure
 +       */
 +       struct wpa_scan_results * (*get_scan_results2)(void *priv);
 +
 +      /**
 +       * set_country - Set country
 +       * @priv: Private driver interface data
 +       * @alpha2: country to which to switch to
 +       * Returns: 0 on success, -1 on failure
 +       *
 +       * This function is for drivers which support some form
 +       * of setting a regulatory domain.
 +       */
 +      int (*set_country)(void *priv, const char *alpha2);
 +
 +      /**
 +       * 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
 +       * use init2() function instead of init() to get the pointer to global
 +       * data available to per-interface initializer.
 +       */
 +      void * (*global_init)(void);
 +
 +      /**
 +       * global_deinit - Global driver deinitialization
 +       * @priv: private driver global data from global_init()
 +       *
 +       * Terminate any global driver related functionality and free the
 +       * global data structure.
 +       */
 +      void (*global_deinit)(void *priv);
 +
 +      /**
 +       * init2 - Initialize driver interface (with global data)
 +       * @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
 +       *
 +       * This function can be used instead of init() if the driver wrapper
 +       * uses global data.
 +       */
 +      void * (*init2)(void *ctx, const char *ifname, void *global_priv);
 +
 +      /**
 +       * get_interfaces - Get information about available interfaces
 +       * @global_priv: private driver global data from global_init()
 +       * Returns: Allocated buffer of interface information (caller is
 +       * responsible for freeing the data structure) on success, NULL on
 +       * failure
 +       */
 +      struct wpa_interface_info * (*get_interfaces)(void *global_priv);
 +
 +      /**
 +       * scan2 - Request the driver to initiate scan
 +       * @priv: private driver interface data
 +       * @params: Scan parameters
 +       *
 +       * Returns: 0 on success, -1 on failure
 +       *
 +       * Once the scan results are ready, the driver should report scan
 +       * results event for wpa_supplicant which will eventually request the
 +       * results with wpa_driver_get_scan_results2().
 +       */
 +      int (*scan2)(void *priv, struct wpa_driver_scan_params *params);
 +
 +      /**
 +       * authenticate - Request driver to authenticate
 +       * @priv: private driver interface data
 +       * @params: authentication parameters
 +       * Returns: 0 on success, -1 on failure
 +       *
 +       * This is an optional function that can be used with drivers that
 +       * support separate authentication and association steps, i.e., when
 +       * wpa_supplicant can act as the SME. If not implemented, associate()
 +       * function is expected to take care of IEEE 802.11 authentication,
 +       * too.
 +       */
 +      int (*authenticate)(void *priv,
 +                          struct wpa_driver_auth_params *params);
 +
 +      /**
 +       * set_ap - Set Beacon and Probe Response information for AP mode
 +       * @priv: Private driver interface data
 +       * @params: Parameters to use in AP mode
 +       *
 +       * This function is used to configure Beacon template and/or extra IEs
 +       * to add for Beacon and Probe Response frames for the driver in
 +       * AP mode. The driver is responsible for building the full Beacon
 +       * frame by concatenating the head part with TIM IE generated by the
 +       * driver/firmware and finishing with the tail part. Depending on the
 +       * driver architectue, this can be done either by using the full
 +       * template or the set of additional IEs (e.g., WPS and P2P IE).
 +       * Similarly, Probe Response processing depends on the driver design.
 +       * If the driver (or firmware) takes care of replying to Probe Request
 +       * frames, the extra IEs provided here needs to be added to the Probe
 +       * Response frames.
 +       *
 +       * Returns: 0 on success, -1 on failure
 +       */
 +      int (*set_ap)(void *priv, struct wpa_driver_ap_params *params);
 +
 +      /**
 +       * set_acl - Set ACL in AP mode
 +       * @priv: Private driver interface data
 +       * @params: Parameters to configure ACL
 +       * Returns: 0 on success, -1 on failure
 +       *
 +       * This is used only for the drivers which support MAC address ACL.
 +       */
 +      int (*set_acl)(void *priv, struct hostapd_acl_params *params);
 +
 +      /**
 +       * hapd_init - Initialize driver interface (hostapd only)
 +       * @hapd: Pointer to hostapd context
 +       * @params: Configuration for the driver wrapper
 +       * Returns: Pointer to private data, %NULL on failure
 +       *
 +       * This function is used instead of init() or init2() when the driver
 +       * wrapper is used with hostapd.
 +       */
 +      void * (*hapd_init)(struct hostapd_data *hapd,
 +                          struct wpa_init_params *params);
 +
 +      /**
 +       * hapd_deinit - Deinitialize driver interface (hostapd only)
 +       * @priv: Private driver interface data from hapd_init()
 +       */
 +      void (*hapd_deinit)(void *priv);
 +
 +      /**
 +       * set_ieee8021x - Enable/disable IEEE 802.1X support (AP only)
 +       * @priv: Private driver interface data
 +       * @params: BSS parameters
 +       * Returns: 0 on success, -1 on failure
 +       *
 +       * This is an optional function to configure the kernel driver to
 +       * enable/disable IEEE 802.1X support and set WPA/WPA2 parameters. This
 +       * can be left undefined (set to %NULL) if IEEE 802.1X support is
 +       * always enabled and the driver uses set_ap() to set WPA/RSN IE
 +       * for Beacon frames.
 +       *
 +       * DEPRECATED - use set_ap() instead
 +       */
 +      int (*set_ieee8021x)(void *priv, struct wpa_bss_params *params);
 +
 +      /**
 +       * set_privacy - Enable/disable privacy (AP only)
 +       * @priv: Private driver interface data
 +       * @enabled: 1 = privacy enabled, 0 = disabled
 +       * Returns: 0 on success, -1 on failure
 +       *
 +       * This is an optional function to configure privacy field in the
 +       * kernel driver for Beacon frames. This can be left undefined (set to
 +       * %NULL) if the driver uses the Beacon template from set_ap().
 +       *
 +       * DEPRECATED - use set_ap() instead
 +       */
 +      int (*set_privacy)(void *priv, int enabled);
 +
 +      /**
 +       * get_seqnum - Fetch the current TSC/packet number (AP only)
 +       * @ifname: The interface name (main or virtual)
 +       * @priv: Private driver interface data
 +       * @addr: MAC address of the station or %NULL for group keys
 +       * @idx: Key index
 +       * @seq: Buffer for returning the latest used TSC/packet number
 +       * Returns: 0 on success, -1 on failure
 +       *
 +       * This function is used to fetch the last used TSC/packet number for
 +       * a TKIP, CCMP, GCMP, or BIP/IGTK key. It is mainly used with group
 +       * keys, so there is no strict requirement on implementing support for
 +       * unicast keys (i.e., addr != %NULL).
 +       */
 +      int (*get_seqnum)(const char *ifname, void *priv, const u8 *addr,
 +                        int idx, u8 *seq);
 +
 +      /**
 +       * flush - Flush all association stations (AP only)
 +       * @priv: Private driver interface data
 +       * Returns: 0 on success, -1 on failure
 +       *
 +       * This function requests the driver to disassociate all associated
 +       * stations. This function does not need to be implemented if the
 +       * driver does not process association frames internally.
 +       */
 +      int (*flush)(void *priv);
 +
 +      /**
 +       * set_generic_elem - Add IEs into Beacon/Probe Response frames (AP)
 +       * @priv: Private driver interface data
 +       * @elem: Information elements
 +       * @elem_len: Length of the elem buffer in octets
 +       * Returns: 0 on success, -1 on failure
 +       *
 +       * This is an optional function to add information elements in the
 +       * kernel driver for Beacon and Probe Response frames. This can be left
 +       * undefined (set to %NULL) if the driver uses the Beacon template from
 +       * set_ap().
 +       *
 +       * DEPRECATED - use set_ap() instead
 +       */
 +      int (*set_generic_elem)(void *priv, const u8 *elem, size_t elem_len);
 +
 +      /**
 +       * read_sta_data - Fetch station data
 +       * @priv: Private driver interface data
 +       * @data: Buffer for returning station information
 +       * @addr: MAC address of the station
 +       * Returns: 0 on success, -1 on failure
 +       */
 +      int (*read_sta_data)(void *priv, struct hostap_sta_driver_data *data,
 +                           const u8 *addr);
 +
 +      /**
 +       * hapd_send_eapol - Send an EAPOL packet (AP only)
 +       * @priv: private driver interface data
 +       * @addr: Destination MAC address
 +       * @data: EAPOL packet starting with IEEE 802.1X header
 +       * @data_len: Length of the EAPOL packet in octets
 +       * @encrypt: Whether the frame should be encrypted
 +       * @own_addr: Source MAC address
 +       * @flags: WPA_STA_* flags for the destination station
 +       *
 +       * Returns: 0 on success, -1 on failure
 +       */
 +      int (*hapd_send_eapol)(void *priv, const u8 *addr, const u8 *data,
 +                             size_t data_len, int encrypt,
 +                             const u8 *own_addr, u32 flags);
 +
 +      /**
 +       * sta_deauth - Deauthenticate a station (AP only)
 +       * @priv: Private driver interface data
 +       * @own_addr: Source address and BSSID for the Deauthentication frame
 +       * @addr: MAC address of the station to deauthenticate
 +       * @reason: Reason code for the Deauthentiation frame
 +       * Returns: 0 on success, -1 on failure
 +       *
 +       * This function requests a specific station to be deauthenticated and
 +       * a Deauthentication frame to be sent to it.
 +       */
 +      int (*sta_deauth)(void *priv, const u8 *own_addr, const u8 *addr,
 +                        int reason);
 +
 +      /**
 +       * sta_disassoc - Disassociate a station (AP only)
 +       * @priv: Private driver interface data
 +       * @own_addr: Source address and BSSID for the Disassociation frame
 +       * @addr: MAC address of the station to disassociate
 +       * @reason: Reason code for the Disassociation frame
 +       * Returns: 0 on success, -1 on failure
 +       *
 +       * This function requests a specific station to be disassociated and
 +       * a Disassociation frame to be sent to it.
 +       */
 +      int (*sta_disassoc)(void *priv, const u8 *own_addr, const u8 *addr,
 +                          int reason);
 +
 +      /**
 +       * sta_remove - Remove a station entry (AP only)
 +       * @priv: Private driver interface data
 +       * @addr: MAC address of the station to be removed
 +       * Returns: 0 on success, -1 on failure
 +       */
 +      int (*sta_remove)(void *priv, const u8 *addr);
 +
 +      /**
 +       * hapd_get_ssid - Get the current SSID (AP only)
 +       * @priv: Private driver interface data
 +       * @buf: Buffer for returning the SSID
 +       * @len: Maximum length of the buffer
 +       * Returns: Length of the SSID on success, -1 on failure
 +       *
 +       * This function need not be implemented if the driver uses Beacon
 +       * template from set_ap() and does not reply to Probe Request frames.
 +       */
 +      int (*hapd_get_ssid)(void *priv, u8 *buf, int len);
 +
 +      /**
 +       * hapd_set_ssid - Set SSID (AP only)
 +       * @priv: Private driver interface data
 +       * @buf: SSID
 +       * @len: Length of the SSID in octets
 +       * Returns: 0 on success, -1 on failure
 +       *
 +       * DEPRECATED - use set_ap() instead
 +       */
 +      int (*hapd_set_ssid)(void *priv, const u8 *buf, int len);
 +
 +      /**
 +       * hapd_set_countermeasures - Enable/disable TKIP countermeasures (AP)
 +       * @priv: Private driver interface data
 +       * @enabled: 1 = countermeasures enabled, 0 = disabled
 +       * Returns: 0 on success, -1 on failure
 +       *
 +       * This need not be implemented if the driver does not take care of
 +       * association processing.
 +       */
 +      int (*hapd_set_countermeasures)(void *priv, int enabled);
 +
 +      /**
 +       * sta_add - Add a station entry
 +       * @priv: Private driver interface data
 +       * @params: Station parameters
 +       * Returns: 0 on success, -1 on failure
 +       *
 +       * This function is used to add a station entry to the driver once the
 +       * station has completed association. This is only used if the driver
 +       * does not take care of association processing.
 +       *
 +       * With TDLS, this function is also used to add or set (params->set 1)
 +       * TDLS peer entries.
 +       */
 +      int (*sta_add)(void *priv, struct hostapd_sta_add_params *params);
 +
 +      /**
 +       * get_inact_sec - Get station inactivity duration (AP only)
 +       * @priv: Private driver interface data
 +       * @addr: Station address
 +       * Returns: Number of seconds station has been inactive, -1 on failure
 +       */
 +      int (*get_inact_sec)(void *priv, const u8 *addr);
 +
 +      /**
 +       * sta_clear_stats - Clear station statistics (AP only)
 +       * @priv: Private driver interface data
 +       * @addr: Station address
 +       * Returns: 0 on success, -1 on failure
 +       */
 +      int (*sta_clear_stats)(void *priv, const u8 *addr);
 +
 +      /**
 +       * set_freq - Set channel/frequency (AP only)
 +       * @priv: Private driver interface data
 +       * @freq: Channel parameters
 +       * Returns: 0 on success, -1 on failure
 +       */
 +      int (*set_freq)(void *priv, struct hostapd_freq_params *freq);
 +
 +      /**
 +       * set_rts - Set RTS threshold
 +       * @priv: Private driver interface data
 +       * @rts: RTS threshold in octets
 +       * Returns: 0 on success, -1 on failure
 +       */
 +      int (*set_rts)(void *priv, int rts);
 +
 +      /**
 +       * set_frag - Set fragmentation threshold
 +       * @priv: Private driver interface data
 +       * @frag: Fragmentation threshold in octets
 +       * Returns: 0 on success, -1 on failure
 +       */
 +      int (*set_frag)(void *priv, int frag);
 +
 +      /**
 +       * sta_set_flags - Set station flags (AP only)
 +       * @priv: Private driver interface data
 +       * @addr: Station address
 +       * @total_flags: Bitmap of all WPA_STA_* flags currently set
 +       * @flags_or: Bitmap of WPA_STA_* flags to add
 +       * @flags_and: Bitmap of WPA_STA_* flags to us as a mask
 +       * Returns: 0 on success, -1 on failure
 +       */
 +      int (*sta_set_flags)(void *priv, const u8 *addr,
-       /**
-        * shared_freq - Get operating frequency of shared interface(s)
-        * @priv: Private driver interface data
-        * Returns: Operating frequency in MHz, 0 if no shared operation in
-        * use, or -1 on failure
-        *
-        * This command can be used to request the current operating frequency
-        * of any virtual interface that shares the same radio to provide
-        * information for channel selection for other virtual interfaces.
-        */
-       int (*shared_freq)(void *priv);
++                           unsigned int total_flags, unsigned int flags_or,
++                           unsigned int flags_and);
 +
 +      /**
 +       * set_tx_queue_params - Set TX queue parameters
 +       * @priv: Private driver interface data
 +       * @queue: Queue number (0 = VO, 1 = VI, 2 = BE, 3 = BK)
 +       * @aifs: AIFS
 +       * @cw_min: cwMin
 +       * @cw_max: cwMax
 +       * @burst_time: Maximum length for bursting in 0.1 msec units
 +       */
 +      int (*set_tx_queue_params)(void *priv, int queue, int aifs, int cw_min,
 +                                 int cw_max, int burst_time);
 +
 +      /**
 +       * if_add - Add a virtual interface
 +       * @priv: Private driver interface data
 +       * @type: Interface type
 +       * @ifname: Interface name for the new virtual interface
 +       * @addr: Local address to use for the interface or %NULL to use the
 +       *      parent interface address
 +       * @bss_ctx: BSS context for %WPA_IF_AP_BSS interfaces
 +       * @drv_priv: Pointer for overwriting the driver context or %NULL if
 +       *      not allowed (applies only to %WPA_IF_AP_BSS type)
 +       * @force_ifname: Buffer for returning an interface name that the
 +       *      driver ended up using if it differs from the requested ifname
 +       * @if_addr: Buffer for returning the allocated interface address
 +       *      (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, int use_existing);
 +
 +      /**
 +       * if_remove - Remove a virtual interface
 +       * @priv: Private driver interface data
 +       * @type: Interface type
 +       * @ifname: Interface name of the virtual interface to be removed
 +       * Returns: 0 on success, -1 on failure
 +       */
 +      int (*if_remove)(void *priv, enum wpa_driver_if_type type,
 +                       const char *ifname);
 +
 +      /**
 +       * set_sta_vlan - Bind a station into a specific interface (AP only)
 +       * @priv: Private driver interface data
 +       * @ifname: Interface (main or virtual BSS or VLAN)
 +       * @addr: MAC address of the associated station
 +       * @vlan_id: VLAN ID
 +       * Returns: 0 on success, -1 on failure
 +       *
 +       * This function is used to bind a station to a specific virtual
 +       * interface. It is only used if when virtual interfaces are supported,
 +       * e.g., to assign stations to different VLAN interfaces based on
 +       * information from a RADIUS server. This allows separate broadcast
 +       * domains to be used with a single BSS.
 +       */
 +      int (*set_sta_vlan)(void *priv, const u8 *addr, const char *ifname,
 +                          int vlan_id);
 +
 +      /**
 +       * commit - Optional commit changes handler (AP only)
 +       * @priv: driver private data
 +       * Returns: 0 on success, -1 on failure
 +       *
 +       * This optional handler function can be registered if the driver
 +       * interface implementation needs to commit changes (e.g., by setting
 +       * network interface up) at the end of initial configuration. If set,
 +       * this handler will be called after initial setup has been completed.
 +       */
 +      int (*commit)(void *priv);
 +
 +      /**
 +       * send_ether - Send an ethernet packet (AP only)
 +       * @priv: private driver interface data
 +       * @dst: Destination MAC address
 +       * @src: Source MAC address
 +       * @proto: Ethertype
 +       * @data: EAPOL packet starting with IEEE 802.1X header
 +       * @data_len: Length of the EAPOL packet in octets
 +       * Returns: 0 on success, -1 on failure
 +       */
 +      int (*send_ether)(void *priv, const u8 *dst, const u8 *src, u16 proto,
 +                        const u8 *data, size_t data_len);
 +
 +      /**
 +       * set_radius_acl_auth - Notification of RADIUS ACL change
 +       * @priv: Private driver interface data
 +       * @mac: MAC address of the station
 +       * @accepted: Whether the station was accepted
 +       * @session_timeout: Session timeout for the station
 +       * Returns: 0 on success, -1 on failure
 +       */
 +      int (*set_radius_acl_auth)(void *priv, const u8 *mac, int accepted,
 +                                 u32 session_timeout);
 +
 +      /**
 +       * set_radius_acl_expire - Notification of RADIUS ACL expiration
 +       * @priv: Private driver interface data
 +       * @mac: MAC address of the station
 +       * Returns: 0 on success, -1 on failure
 +       */
 +      int (*set_radius_acl_expire)(void *priv, const u8 *mac);
 +
 +      /**
 +       * set_ap_wps_ie - Add WPS IE(s) into Beacon/Probe Response frames (AP)
 +       * @priv: Private driver interface data
 +       * @beacon: WPS IE(s) for Beacon frames or %NULL to remove extra IE(s)
 +       * @proberesp: WPS IE(s) for Probe Response frames or %NULL to remove
 +       *      extra IE(s)
 +       * @assocresp: WPS IE(s) for (Re)Association Response frames or %NULL
 +       *      to remove extra IE(s)
 +       * Returns: 0 on success, -1 on failure
 +       *
 +       * This is an optional function to add WPS IE in the kernel driver for
 +       * Beacon and Probe Response frames. This can be left undefined (set
 +       * to %NULL) if the driver uses the Beacon template from set_ap()
 +       * and does not process Probe Request frames. If the driver takes care
 +       * of (Re)Association frame processing, the assocresp buffer includes
 +       * WPS IE(s) that need to be added to (Re)Association Response frames
 +       * whenever a (Re)Association Request frame indicated use of WPS.
 +       *
 +       * This will also be used to add P2P IE(s) into Beacon/Probe Response
 +       * frames when operating as a GO. The driver is responsible for adding
 +       * timing related attributes (e.g., NoA) in addition to the IEs
 +       * included here by appending them after these buffers. This call is
 +       * also used to provide Probe Response IEs for P2P Listen state
 +       * operations for drivers that generate the Probe Response frames
 +       * internally.
 +       *
 +       * DEPRECATED - use set_ap() instead
 +       */
 +      int (*set_ap_wps_ie)(void *priv, const struct wpabuf *beacon,
 +                           const struct wpabuf *proberesp,
 +                           const struct wpabuf *assocresp);
 +
 +      /**
 +       * set_supp_port - Set IEEE 802.1X Supplicant Port status
 +       * @priv: Private driver interface data
 +       * @authorized: Whether the port is authorized
 +       * Returns: 0 on success, -1 on failure
 +       */
 +      int (*set_supp_port)(void *priv, int authorized);
 +
 +      /**
 +       * set_wds_sta - Bind a station into a 4-address WDS (AP only)
 +       * @priv: Private driver interface data
 +       * @addr: MAC address of the associated station
 +       * @aid: Association ID
 +       * @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, char *ifname_wds);
 +
 +      /**
 +       * send_action - Transmit an Action frame
 +       * @priv: Private driver interface data
 +       * @freq: Frequency (in MHz) of the channel
 +       * @wait: Time to wait off-channel for a response (in ms), or zero
 +       * @dst: Destination MAC address (Address 1)
 +       * @src: Source MAC address (Address 2)
 +       * @bssid: BSSID (Address 3)
 +       * @data: Frame body
 +       * @data_len: data length in octets
 +       @ @no_cck: Whether CCK rates must not be used to transmit this frame
 +       * Returns: 0 on success, -1 on failure
 +       *
 +       * This command can be used to request the driver to transmit an action
 +       * frame to the specified destination.
 +       *
 +       * If the %WPA_DRIVER_FLAGS_OFFCHANNEL_TX flag is set, the frame will
 +       * be transmitted on the given channel and the device will wait for a
 +       * response on that channel for the given wait time.
 +       *
 +       * If the flag is not set, the wait time will be ignored. In this case,
 +       * if a remain-on-channel duration is in progress, the frame must be
 +       * transmitted on that channel; alternatively the frame may be sent on
 +       * the current operational channel (if in associated state in station
 +       * mode or while operating as an AP.)
 +       */
 +      int (*send_action)(void *priv, unsigned int freq, unsigned int wait,
 +                         const u8 *dst, const u8 *src, const u8 *bssid,
 +                         const u8 *data, size_t data_len, int no_cck);
 +
 +      /**
 +       * send_action_cancel_wait - Cancel action frame TX wait
 +       * @priv: Private driver interface data
 +       *
 +       * This command cancels the wait time associated with sending an action
 +       * frame. It is only available when %WPA_DRIVER_FLAGS_OFFCHANNEL_TX is
 +       * set in the driver flags.
 +       */
 +      void (*send_action_cancel_wait)(void *priv);
 +
 +      /**
 +       * remain_on_channel - Remain awake on a channel
 +       * @priv: Private driver interface data
 +       * @freq: Frequency (in MHz) of the channel
 +       * @duration: Duration in milliseconds
 +       * Returns: 0 on success, -1 on failure
 +       *
 +       * 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_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.
 +       *
 +       * The driver may not be at the requested channel when this function
 +       * returns, i.e., the return code is only indicating whether the
 +       * request was accepted. The caller will need to wait until the
 +       * EVENT_REMAIN_ON_CHANNEL event indicates that the driver has
 +       * completed the channel change. This may take some time due to other
 +       * need for the radio and the caller should be prepared to timing out
 +       * its wait since there are no guarantees on when this request can be
 +       * executed.
 +       */
 +      int (*remain_on_channel)(void *priv, unsigned int freq,
 +                               unsigned int duration);
 +
 +      /**
 +       * cancel_remain_on_channel - Cancel remain-on-channel operation
 +       * @priv: Private driver interface data
 +       *
 +       * This command can be used to cancel a remain-on-channel operation
 +       * before its originally requested duration has passed. This could be
 +       * used, e.g., when remain_on_channel() is used to request extra time
 +       * to receive a response to an Action frame and the response is
 +       * received when there is still unneeded time remaining on the
 +       * remain-on-channel operation.
 +       */
 +      int (*cancel_remain_on_channel)(void *priv);
 +
 +      /**
 +       * probe_req_report - Request Probe Request frames to be indicated
 +       * @priv: Private driver interface data
 +       * @report: Whether to report received Probe Request frames
 +       * Returns: 0 on success, -1 on failure (or if not supported)
 +       *
 +       * This command can be used to request the driver to indicate when
 +       * Probe Request frames are received with EVENT_RX_PROBE_REQ events.
 +       * Since this operation may require extra resources, e.g., due to less
 +       * optimal hardware/firmware RX filtering, many drivers may disable
 +       * Probe Request reporting at least in station mode. This command is
 +       * used to notify the driver when the Probe Request frames need to be
 +       * reported, e.g., during remain-on-channel operations.
 +       */
 +      int (*probe_req_report)(void *priv, int report);
 +
 +      /**
 +       * deinit_ap - Deinitialize AP mode
 +       * @priv: Private driver interface data
 +       * Returns: 0 on success, -1 on failure (or if not supported)
 +       *
 +       * This optional function can be used to disable AP mode related
 +       * 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);
 +
 +      /**
 +       * deinit_p2p_cli - Deinitialize P2P client mode
 +       * @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. If the
 +       * interface was not dynamically added, change the interface type back
 +       * to station mode.
 +       */
 +      int (*deinit_p2p_cli)(void *priv);
 +
 +      /**
 +       * suspend - Notification on system suspend/hibernate event
 +       * @priv: Private driver interface data
 +       */
 +      void (*suspend)(void *priv);
 +
 +      /**
 +       * resume - Notification on system resume/thaw event
 +       * @priv: Private driver interface data
 +       */
 +      void (*resume)(void *priv);
 +
 +      /**
 +       * signal_monitor - Set signal monitoring parameters
 +       * @priv: Private driver interface data
 +       * @threshold: Threshold value for signal change events; 0 = disabled
 +       * @hysteresis: Minimum change in signal strength before indicating a
 +       *      new event
 +       * Returns: 0 on success, -1 on failure (or if not supported)
 +       *
 +       * This function can be used to configure monitoring of signal strength
 +       * with the current AP. Whenever signal strength drops below the
 +       * %threshold value or increases above it, EVENT_SIGNAL_CHANGE event
 +       * should be generated assuming the signal strength has changed at
 +       * least %hysteresis from the previously indicated signal change event.
 +       */
 +      int (*signal_monitor)(void *priv, int threshold, int hysteresis);
 +
 +      /**
 +       * send_frame - Send IEEE 802.11 frame (testing use only)
 +       * @priv: Private driver interface data
 +       * @data: IEEE 802.11 frame with IEEE 802.11 header
 +       * @data_len: Size of the frame
 +       * @encrypt: Whether to encrypt the frame (if keys are set)
 +       * Returns: 0 on success, -1 on failure
 +       *
 +       * This function is only used for debugging purposes and is not
 +       * required to be implemented for normal operations.
 +       */
 +      int (*send_frame)(void *priv, const u8 *data, size_t data_len,
 +                        int encrypt);
 +
- extern struct wpa_driver_ops *wpa_drivers[];
 +      /**
 +       * get_noa - Get current Notice of Absence attribute payload
 +       * @priv: Private driver interface data
 +       * @buf: Buffer for returning NoA
 +       * @buf_len: Buffer length in octets
 +       * Returns: Number of octets used in buf, 0 to indicate no NoA is being
 +       * advertized, or -1 on failure
 +       *
 +       * This function is used to fetch the current Notice of Absence
 +       * attribute value from GO.
 +       */
 +      int (*get_noa)(void *priv, u8 *buf, size_t buf_len);
 +
 +      /**
 +       * set_noa - Set Notice of Absence parameters for GO (testing)
 +       * @priv: Private driver interface data
 +       * @count: Count
 +       * @start: Start time in ms from next TBTT
 +       * @duration: Duration in ms
 +       * Returns: 0 on success or -1 on failure
 +       *
 +       * This function is used to set Notice of Absence parameters for GO. It
 +       * is used only for testing. To disable NoA, all parameters are set to
 +       * 0.
 +       */
 +      int (*set_noa)(void *priv, u8 count, int start, int duration);
 +
 +      /**
 +       * set_p2p_powersave - Set P2P power save options
 +       * @priv: Private driver interface data
 +       * @legacy_ps: 0 = disable, 1 = enable, 2 = maximum PS, -1 = no change
 +       * @opp_ps: 0 = disable, 1 = enable, -1 = no change
 +       * @ctwindow: 0.. = change (msec), -1 = no change
 +       * Returns: 0 on success or -1 on failure
 +       */
 +      int (*set_p2p_powersave)(void *priv, int legacy_ps, int opp_ps,
 +                               int ctwindow);
 +
 +      /**
 +       * ampdu - Enable/disable aggregation
 +       * @priv: Private driver interface data
 +       * @ampdu: 1/0 = enable/disable A-MPDU aggregation
 +       * Returns: 0 on success or -1 on failure
 +       */
 +      int (*ampdu)(void *priv, int ampdu);
 +
 +      /**
 +       * get_radio_name - Get physical radio name for the device
 +       * @priv: Private driver interface data
 +       * Returns: Radio name or %NULL if not known
 +       *
 +       * The returned data must not be modified by the caller. It is assumed
 +       * that any interface that has the same radio name as another is
 +       * sharing the same physical radio. This information can be used to
 +       * share scan results etc. information between the virtual interfaces
 +       * to speed up various operations.
 +       */
 +      const char * (*get_radio_name)(void *priv);
 +
 +      /**
 +       * 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
 +       *
 +       * This optional function can be used to send packet to driver which is
 +       * 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, u32 peer_capab,
 +                            int initiator, const u8 *buf, size_t len);
 +
 +      /**
 +       * tdls_oper - Ask the driver to perform high-level TDLS operations
 +       * @priv: Private driver interface data
 +       * @oper: TDLS high-level operation. See %enum tdls_oper
 +       * @peer: Destination (peer) MAC address
 +       * Returns: 0 on success, negative (<0) on failure
 +       *
 +       * This optional function can be used to send high-level TDLS commands
 +       * to the driver.
 +       */
 +      int (*tdls_oper)(void *priv, enum tdls_oper oper, const u8 *peer);
 +
 +      /**
 +       * wnm_oper - Notify driver of the WNM frame reception
 +       * @priv: Private driver interface data
 +       * @oper: WNM operation. See %enum wnm_oper
 +       * @peer: Destination (peer) MAC address
 +       * @buf: Buffer for the driver to fill in (for getting IE)
 +       * @buf_len: Return the len of buf
 +       * Returns: 0 on success, negative (<0) on failure
 +       */
 +      int (*wnm_oper)(void *priv, enum wnm_oper oper, const u8 *peer,
 +                      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);
 +
 +      /**
 +       * set_authmode - Set authentication algorithm(s) for static WEP
 +       * @priv: Private driver interface data
 +       * @authmode: 1=Open System, 2=Shared Key, 3=both
 +       * Returns: 0 on success, -1 on failure
 +       *
 +       * This function can be used to set authentication algorithms for AP
 +       * mode when static WEP is used. If the driver uses user space MLME/SME
 +       * implementation, there is no need to implement this function.
 +       *
 +       * DEPRECATED - use set_ap() instead
 +       */
 +      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, size_t kek_len,
 +                             const u8 *kck, size_t kck_len,
 +                             const u8 *replay_ctr);
 +
 +      /**
 +       * sta_assoc - Station association indication
 +       * @priv: Private driver interface data
 +       * @own_addr: Source address and BSSID for association frame
 +       * @addr: MAC address of the station to associate
 +       * @reassoc: flag to indicate re-association
 +       * @status: association response status code
 +       * @ie: assoc response ie buffer
 +       * @len: ie buffer length
 +       * Returns: 0 on success, -1 on failure
 +       *
 +       * This function indicates the driver to send (Re)Association
 +       * Response frame to the station.
 +       */
 +       int (*sta_assoc)(void *priv, const u8 *own_addr, const u8 *addr,
 +                        int reassoc, u16 status, const u8 *ie, size_t len);
 +
 +      /**
 +       * sta_auth - Station authentication indication
 +       * @priv: Private driver interface data
 +       * @own_addr: Source address and BSSID for authentication frame
 +       * @addr: MAC address of the station to associate
 +       * @seq: authentication sequence number
 +       * @status: authentication response status code
 +       * @ie: authentication frame ie buffer
 +       * @len: ie buffer length
 +       *
 +       * This function indicates the driver to send Authentication frame
 +       * to the station.
 +       */
 +       int (*sta_auth)(void *priv, const u8 *own_addr, const u8 *addr,
 +                       u16 seq, u16 status, const u8 *ie, size_t len);
 +
 +      /**
 +       * add_tspec - Add traffic stream
 +       * @priv: Private driver interface data
 +       * @addr: MAC address of the station to associate
 +       * @tspec_ie: tspec ie buffer
 +       * @tspec_ielen: tspec ie length
 +       * Returns: 0 on success, -1 on failure
 +       *
 +       * This function adds the traffic steam for the station
 +       * and fills the medium_time in tspec_ie.
 +       */
 +       int (*add_tspec)(void *priv, const u8 *addr, u8 *tspec_ie,
 +                        size_t tspec_ielen);
 +
 +      /**
 +       * add_sta_node - Add a station node in the driver
 +       * @priv: Private driver interface data
 +       * @addr: MAC address of the station to add
 +       * @auth_alg: authentication algorithm used by the station
 +       * Returns: 0 on success, -1 on failure
 +       *
 +       * This function adds the station node in the driver, when
 +       * the station gets added by FT-over-DS.
 +       */
 +      int (*add_sta_node)(void *priv, const u8 *addr, u16 auth_alg);
 +
 +      /**
 +       * sched_scan - Request the driver to initiate scheduled scan
 +       * @priv: Private driver interface data
 +       * @params: Scan parameters
 +       * @interval: Interval between scan cycles in milliseconds
 +       * Returns: 0 on success, -1 on failure
 +       *
 +       * This operation should be used for scheduled scan offload to
 +       * the hardware. Every time scan results are available, the
 +       * driver should report scan results event for wpa_supplicant
 +       * which will eventually request the results with
 +       * wpa_driver_get_scan_results2(). This operation is optional
 +       * and if not provided or if it returns -1, we fall back to
 +       * normal host-scheduled scans.
 +       */
 +      int (*sched_scan)(void *priv, struct wpa_driver_scan_params *params,
 +                        u32 interval);
 +
 +      /**
 +       * stop_sched_scan - Request the driver to stop a scheduled scan
 +       * @priv: Private driver interface data
 +       * Returns: 0 on success, -1 on failure
 +       *
 +       * This should cause the scheduled scan to be stopped and
 +       * results should stop being sent. Must be supported if
 +       * sched_scan is supported.
 +       */
 +      int (*stop_sched_scan)(void *priv);
 +
 +      /**
 +       * poll_client - Probe (null data or such) the given station
 +       * @priv: Private driver interface data
 +       * @own_addr: MAC address of sending interface
 +       * @addr: MAC address of the station to probe
 +       * @qos: Indicates whether station is QoS station
 +       *
 +       * This function is used to verify whether an associated station is
 +       * still present. This function does not need to be implemented if the
 +       * driver provides such inactivity polling mechanism.
 +       */
 +      void (*poll_client)(void *priv, const u8 *own_addr,
 +                          const u8 *addr, int qos);
 +
 +      /**
 +       * radio_disable - Disable/enable radio
 +       * @priv: Private driver interface data
 +       * @disabled: 1=disable 0=enable radio
 +       * Returns: 0 on success, -1 on failure
 +       *
 +       * This optional command is for testing purposes. It can be used to
 +       * disable the radio on a testbed device to simulate out-of-radio-range
 +       * conditions.
 +       */
 +      int (*radio_disable)(void *priv, int disabled);
 +
 +      /**
 +       * switch_channel - Announce channel switch and migrate the GO to the
 +       * given frequency
 +       * @priv: Private driver interface data
 +       * @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, 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: Channel parameters
 +       * Returns: 0 on success, -1 on failure
 +       */
 +      int (*start_dfs_cac)(void *priv, struct hostapd_freq_params *freq);
 +
 +      /**
 +       * stop_ap - Removes beacon from AP
 +       * @priv: Private driver interface data
 +       * Returns: 0 on success, -1 on failure (or if not supported)
 +       *
 +       * This optional function can be used to disable AP mode related
 +       * configuration. Unlike deinit_ap, it does not change to station
 +       * mode.
 +       */
 +      int (*stop_ap)(void *priv);
 +
 +      /**
 +       * 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);
++
++      /**
++       * set_band - Notify driver of band selection
++       * @priv: Private driver interface data
++       * @band: The selected band(s)
++       * Returns 0 on success, -1 on failure
++       */
++      int (*set_band)(void *priv, enum set_band band);
++
++      /**
++       * get_pref_freq_list - Get preferred frequency list for an interface
++       * @priv: Private driver interface data
++       * @if_type: Interface type
++       * @num: Number of channels
++       * @freq_list: Preferred channel frequency list encoded in MHz values
++       * Returns 0 on success, -1 on failure
++       *
++       * This command can be used to query the preferred frequency list from
++       * the driver specific to a particular interface type.
++       */
++      int (*get_pref_freq_list)(void *priv, enum wpa_driver_if_type if_type,
++                                unsigned int *num, unsigned int *freq_list);
++
++      /**
++       * set_prob_oper_freq - Indicate probable P2P operating channel
++       * @priv: Private driver interface data
++       * @freq: Channel frequency in MHz
++       * Returns 0 on success, -1 on failure
++       *
++       * This command can be used to inform the driver of the operating
++       * frequency that an ongoing P2P group formation is likely to come up
++       * on. Local device is assuming P2P Client role.
++       */
++      int (*set_prob_oper_freq)(void *priv, unsigned int freq);
 +};
 +
 +
 +/**
 + * enum wpa_event_type - Event type for wpa_supplicant_event() calls
 + */
 +enum wpa_event_type {
 +      /**
 +       * EVENT_ASSOC - Association completed
 +       *
 +       * This event needs to be delivered when the driver completes IEEE
 +       * 802.11 association or reassociation successfully.
 +       * wpa_driver_ops::get_bssid() is expected to provide the current BSSID
 +       * after this event has been generated. In addition, optional
 +       * EVENT_ASSOCINFO may be generated just before EVENT_ASSOC to provide
 +       * more information about the association. If the driver interface gets
 +       * both of these events at the same time, it can also include the
 +       * assoc_info data in EVENT_ASSOC call.
 +       */
 +      EVENT_ASSOC,
 +
 +      /**
 +       * EVENT_DISASSOC - Association lost
 +       *
 +       * This event should be called when association is lost either due to
 +       * receiving deauthenticate or disassociate frame from the AP or when
 +       * sending either of these frames to the current AP. If the driver
 +       * supports separate deauthentication event, EVENT_DISASSOC should only
 +       * be used for disassociation and EVENT_DEAUTH for deauthentication.
 +       * In AP mode, union wpa_event_data::disassoc_info is required.
 +       */
 +      EVENT_DISASSOC,
 +
 +      /**
 +       * EVENT_MICHAEL_MIC_FAILURE - Michael MIC (TKIP) detected
 +       *
 +       * This event must be delivered when a Michael MIC error is detected by
 +       * the local driver. Additional data for event processing is
 +       * provided with union wpa_event_data::michael_mic_failure. This
 +       * information is used to request new encyption key and to initiate
 +       * TKIP countermeasures if needed.
 +       */
 +      EVENT_MICHAEL_MIC_FAILURE,
 +
 +      /**
 +       * EVENT_SCAN_RESULTS - Scan results available
 +       *
 +       * This event must be called whenever scan results are available to be
 +       * fetched with struct wpa_driver_ops::get_scan_results(). This event
 +       * is expected to be used some time after struct wpa_driver_ops::scan()
 +       * is called. If the driver provides an unsolicited event when the scan
 +       * has been completed, this event can be used to trigger
 +       * EVENT_SCAN_RESULTS call. If such event is not available from the
 +       * driver, the driver wrapper code is expected to use a registered
 +       * timeout to generate EVENT_SCAN_RESULTS call after the time that the
 +       * scan is expected to be completed. Optional information about
 +       * completed scan can be provided with union wpa_event_data::scan_info.
 +       */
 +      EVENT_SCAN_RESULTS,
 +
 +      /**
 +       * EVENT_ASSOCINFO - Report optional extra information for association
 +       *
 +       * This event can be used to report extra association information for
 +       * EVENT_ASSOC processing. This extra information includes IEs from
 +       * association frames and Beacon/Probe Response frames in union
 +       * wpa_event_data::assoc_info. EVENT_ASSOCINFO must be send just before
 +       * EVENT_ASSOC. Alternatively, the driver interface can include
 +       * assoc_info data in the EVENT_ASSOC call if it has all the
 +       * information available at the same point.
 +       */
 +      EVENT_ASSOCINFO,
 +
 +      /**
 +       * EVENT_INTERFACE_STATUS - Report interface status changes
 +       *
 +       * This optional event can be used to report changes in interface
 +       * status (interface added/removed) using union
 +       * wpa_event_data::interface_status. This can be used to trigger
 +       * wpa_supplicant to stop and re-start processing for the interface,
 +       * e.g., when a cardbus card is ejected/inserted.
 +       */
 +      EVENT_INTERFACE_STATUS,
 +
 +      /**
 +       * EVENT_PMKID_CANDIDATE - Report a candidate AP for pre-authentication
 +       *
 +       * This event can be used to inform wpa_supplicant about candidates for
 +       * RSN (WPA2) pre-authentication. If wpa_supplicant is not responsible
 +       * for scan request (ap_scan=2 mode), this event is required for
 +       * pre-authentication. If wpa_supplicant is performing scan request
 +       * (ap_scan=1), this event is optional since scan results can be used
 +       * to add pre-authentication candidates. union
 +       * wpa_event_data::pmkid_candidate is used to report the BSSID of the
 +       * candidate and priority of the candidate, e.g., based on the signal
 +       * strength, in order to try to pre-authenticate first with candidates
 +       * that are most likely targets for re-association.
 +       *
 +       * EVENT_PMKID_CANDIDATE can be called whenever the driver has updates
 +       * on the candidate list. In addition, it can be called for the current
 +       * AP and APs that have existing PMKSA cache entries. wpa_supplicant
 +       * will automatically skip pre-authentication in cases where a valid
 +       * PMKSA exists. When more than one candidate exists, this event should
 +       * be generated once for each candidate.
 +       *
 +       * Driver will be notified about successful pre-authentication with
 +       * struct wpa_driver_ops::add_pmkid() calls.
 +       */
 +      EVENT_PMKID_CANDIDATE,
 +
 +      /**
 +       * EVENT_STKSTART - Request STK handshake (MLME-STKSTART.request)
 +       *
 +       * This event can be used to inform wpa_supplicant about desire to set
 +       * up secure direct link connection between two stations as defined in
 +       * IEEE 802.11e with a new PeerKey mechanism that replaced the original
 +       * STAKey negotiation. The caller will need to set peer address for the
 +       * event.
 +       */
 +      EVENT_STKSTART,
 +
 +      /**
 +       * EVENT_TDLS - Request TDLS operation
 +       *
 +       * This event can be used to request a TDLS operation to be performed.
 +       */
 +      EVENT_TDLS,
 +
 +      /**
 +       * EVENT_FT_RESPONSE - Report FT (IEEE 802.11r) response IEs
 +       *
 +       * The driver is expected to report the received FT IEs from
 +       * FT authentication sequence from the AP. The FT IEs are included in
 +       * the extra information in union wpa_event_data::ft_ies.
 +       */
 +      EVENT_FT_RESPONSE,
 +
 +      /**
 +       * EVENT_IBSS_RSN_START - Request RSN authentication in IBSS
 +       *
 +       * The driver can use this event to inform wpa_supplicant about a STA
 +       * in an IBSS with which protected frames could be exchanged. This
 +       * event starts RSN authentication with the other STA to authenticate
 +       * the STA and set up encryption keys with it.
 +       */
 +      EVENT_IBSS_RSN_START,
 +
 +      /**
 +       * EVENT_AUTH - Authentication result
 +       *
 +       * This event should be called when authentication attempt has been
 +       * completed. This is only used if the driver supports separate
 +       * authentication step (struct wpa_driver_ops::authenticate).
 +       * Information about authentication result is included in
 +       * union wpa_event_data::auth.
 +       */
 +      EVENT_AUTH,
 +
 +      /**
 +       * EVENT_DEAUTH - Authentication lost
 +       *
 +       * This event should be called when authentication is lost either due
 +       * to receiving deauthenticate frame from the AP or when sending that
 +       * frame to the current AP.
 +       * In AP mode, union wpa_event_data::deauth_info is required.
 +       */
 +      EVENT_DEAUTH,
 +
 +      /**
 +       * EVENT_ASSOC_REJECT - Association rejected
 +       *
 +       * This event should be called when (re)association attempt has been
 +       * rejected by the AP. Information about the association response is
 +       * included in union wpa_event_data::assoc_reject.
 +       */
 +      EVENT_ASSOC_REJECT,
 +
 +      /**
 +       * EVENT_AUTH_TIMED_OUT - Authentication timed out
 +       */
 +      EVENT_AUTH_TIMED_OUT,
 +
 +      /**
 +       * EVENT_ASSOC_TIMED_OUT - Association timed out
 +       */
 +      EVENT_ASSOC_TIMED_OUT,
 +
 +      /**
 +       * EVENT_WPS_BUTTON_PUSHED - Report hardware push button press for WPS
 +       */
 +      EVENT_WPS_BUTTON_PUSHED,
 +
 +      /**
 +       * EVENT_TX_STATUS - Report TX status
 +       */
 +      EVENT_TX_STATUS,
 +
 +      /**
 +       * EVENT_RX_FROM_UNKNOWN - Report RX from unknown STA
 +       */
 +      EVENT_RX_FROM_UNKNOWN,
 +
 +      /**
 +       * EVENT_RX_MGMT - Report RX of a management frame
 +       */
 +      EVENT_RX_MGMT,
 +
 +      /**
 +       * EVENT_REMAIN_ON_CHANNEL - Remain-on-channel duration started
 +       *
 +       * This event is used to indicate when the driver has started the
 +       * requested remain-on-channel duration. Information about the
 +       * operation is included in union wpa_event_data::remain_on_channel.
 +       */
 +      EVENT_REMAIN_ON_CHANNEL,
 +
 +      /**
 +       * EVENT_CANCEL_REMAIN_ON_CHANNEL - Remain-on-channel timed out
 +       *
 +       * This event is used to indicate when the driver has completed
 +       * remain-on-channel duration, i.e., may noot be available on the
 +       * requested channel anymore. Information about the
 +       * operation is included in union wpa_event_data::remain_on_channel.
 +       */
 +      EVENT_CANCEL_REMAIN_ON_CHANNEL,
 +
 +      /**
 +       * EVENT_RX_PROBE_REQ - Indicate received Probe Request frame
 +       *
 +       * This event is used to indicate when a Probe Request frame has been
 +       * received. Information about the received frame is included in
 +       * union wpa_event_data::rx_probe_req. The driver is required to report
 +       * these events only after successfully completed probe_req_report()
 +       * commands to request the events (i.e., report parameter is non-zero)
 +       * in station mode. In AP mode, Probe Request frames should always be
 +       * reported.
 +       */
 +      EVENT_RX_PROBE_REQ,
 +
 +      /**
 +       * EVENT_NEW_STA - New wired device noticed
 +       *
 +       * This event is used to indicate that a new device has been detected
 +       * in a network that does not use association-like functionality (i.e.,
 +       * mainly wired Ethernet). This can be used to start EAPOL
 +       * authenticator when receiving a frame from a device. The address of
 +       * the device is included in union wpa_event_data::new_sta.
 +       */
 +      EVENT_NEW_STA,
 +
 +      /**
 +       * 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.
 +       */
 +      EVENT_EAPOL_RX,
 +
 +      /**
 +       * EVENT_SIGNAL_CHANGE - Indicate change in signal strength
 +       *
 +       * This event is used to indicate changes in the signal strength
 +       * observed in frames received from the current AP if signal strength
 +       * monitoring has been enabled with signal_monitor().
 +       */
 +      EVENT_SIGNAL_CHANGE,
 +
 +      /**
 +       * EVENT_INTERFACE_ENABLED - Notify that interface was enabled
 +       *
 +       * This event is used to indicate that the interface was enabled after
 +       * having been previously disabled, e.g., due to rfkill.
 +       */
 +      EVENT_INTERFACE_ENABLED,
 +
 +      /**
 +       * EVENT_INTERFACE_DISABLED - Notify that interface was disabled
 +       *
 +       * This event is used to indicate that the interface was disabled,
 +       * e.g., due to rfkill.
 +       */
 +      EVENT_INTERFACE_DISABLED,
 +
 +      /**
 +       * EVENT_CHANNEL_LIST_CHANGED - Channel list changed
 +       *
 +       * This event is used to indicate that the channel list has changed,
 +       * e.g., because of a regulatory domain change triggered by scan
 +       * results including an AP advertising a country code.
 +       */
 +      EVENT_CHANNEL_LIST_CHANGED,
 +
 +      /**
 +       * EVENT_INTERFACE_UNAVAILABLE - Notify that interface is unavailable
 +       *
 +       * This event is used to indicate that the driver cannot maintain this
 +       * interface in its operation mode anymore. The most likely use for
 +       * this is to indicate that AP mode operation is not available due to
 +       * operating channel would need to be changed to a DFS channel when
 +       * 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. This event can be propagated when channel switching
 +       * fails.
 +       */
 +      EVENT_INTERFACE_UNAVAILABLE,
 +
 +      /**
 +       * EVENT_BEST_CHANNEL
 +       *
 +       * Driver generates this event whenever it detects a better channel
 +       * (e.g., based on RSSI or channel use). This information can be used
 +       * to improve channel selection for a new AP/P2P group.
 +       */
 +      EVENT_BEST_CHANNEL,
 +
 +      /**
 +       * EVENT_UNPROT_DEAUTH - Unprotected Deauthentication frame received
 +       *
 +       * This event should be called when a Deauthentication frame is dropped
 +       * due to it not being protected (MFP/IEEE 802.11w).
 +       * union wpa_event_data::unprot_deauth is required to provide more
 +       * details of the frame.
 +       */
 +      EVENT_UNPROT_DEAUTH,
 +
 +      /**
 +       * EVENT_UNPROT_DISASSOC - Unprotected Disassociation frame received
 +       *
 +       * This event should be called when a Disassociation frame is dropped
 +       * due to it not being protected (MFP/IEEE 802.11w).
 +       * union wpa_event_data::unprot_disassoc is required to provide more
 +       * details of the frame.
 +       */
 +      EVENT_UNPROT_DISASSOC,
 +
 +      /**
 +       * EVENT_STATION_LOW_ACK
 +       *
 +       * Driver generates this event whenever it detected that a particular
 +       * station was lost. Detection can be through massive transmission
 +       * failures for example.
 +       */
 +      EVENT_STATION_LOW_ACK,
 +
 +      /**
 +       * EVENT_IBSS_PEER_LOST - IBSS peer not reachable anymore
 +       */
 +      EVENT_IBSS_PEER_LOST,
 +
 +      /**
 +       * EVENT_DRIVER_GTK_REKEY - Device/driver did GTK rekey
 +       *
 +       * This event carries the new replay counter to notify wpa_supplicant
 +       * of the current EAPOL-Key Replay Counter in case the driver/firmware
 +       * completed Group Key Handshake while the host (including
 +       * wpa_supplicant was sleeping).
 +       */
 +      EVENT_DRIVER_GTK_REKEY,
 +
 +      /**
 +       * EVENT_SCHED_SCAN_STOPPED - Scheduled scan was stopped
 +       */
 +      EVENT_SCHED_SCAN_STOPPED,
 +
 +      /**
 +       * EVENT_DRIVER_CLIENT_POLL_OK - Station responded to poll
 +       *
 +       * This event indicates that the station responded to the poll
 +       * initiated with @poll_client.
 +       */
 +      EVENT_DRIVER_CLIENT_POLL_OK,
 +
 +      /**
 +       * EVENT_EAPOL_TX_STATUS - notify of EAPOL TX status
 +       */
 +      EVENT_EAPOL_TX_STATUS,
 +
 +      /**
 +       * EVENT_CH_SWITCH - AP or GO decided to switch channels
 +       *
 +       * Described in wpa_event_data.ch_switch
 +       * */
 +      EVENT_CH_SWITCH,
 +
 +      /**
 +       * EVENT_WNM - Request WNM operation
 +       *
 +       * This event can be used to request a WNM operation to be performed.
 +       */
 +      EVENT_WNM,
 +
 +      /**
 +       * EVENT_CONNECT_FAILED_REASON - Connection failure reason in AP mode
 +       *
 +       * This event indicates that the driver reported a connection failure
 +       * with the specified client (for example, max client reached, etc.) in
 +       * AP mode.
 +       */
 +      EVENT_CONNECT_FAILED_REASON,
 +
 +      /**
 +       * 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).
 +       */
 +      EVENT_DFS_RADAR_DETECTED,
 +
 +      /**
 +       * 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_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
 +       * external influences.
 +       */
 +      EVENT_DFS_CAC_ABORTED,
 +
 +      /**
 +       * 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_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 {
 +      /**
 +       * struct assoc_info - Data for EVENT_ASSOC and EVENT_ASSOCINFO events
 +       *
 +       * This structure is optional for EVENT_ASSOC calls and required for
 +       * EVENT_ASSOCINFO calls. By using EVENT_ASSOC with this data, the
 +       * driver interface does not need to generate separate EVENT_ASSOCINFO
 +       * calls.
 +       */
 +      struct assoc_info {
 +              /**
 +               * reassoc - Flag to indicate association or reassociation
 +               */
 +              int reassoc;
 +
 +              /**
 +               * req_ies - (Re)Association Request IEs
 +               *
 +               * If the driver generates WPA/RSN IE, this event data must be
 +               * returned for WPA handshake to have needed information. If
 +               * wpa_supplicant-generated WPA/RSN IE is used, this
 +               * information event is optional.
 +               *
 +               * This should start with the first IE (fixed fields before IEs
 +               * are not included).
 +               */
 +              const u8 *req_ies;
 +
 +              /**
 +               * req_ies_len - Length of req_ies in bytes
 +               */
 +              size_t req_ies_len;
 +
 +              /**
 +               * resp_ies - (Re)Association Response IEs
 +               *
 +               * Optional association data from the driver. This data is not
 +               * required WPA, but may be useful for some protocols and as
 +               * such, should be reported if this is available to the driver
 +               * interface.
 +               *
 +               * This should start with the first IE (fixed fields before IEs
 +               * are not included).
 +               */
 +              const u8 *resp_ies;
 +
 +              /**
 +               * resp_ies_len - Length of resp_ies in bytes
 +               */
 +              size_t resp_ies_len;
 +
 +              /**
 +               * beacon_ies - Beacon or Probe Response IEs
 +               *
 +               * Optional Beacon/ProbeResp data: IEs included in Beacon or
 +               * Probe Response frames from the current AP (i.e., the one
 +               * that the client just associated with). This information is
 +               * used to update WPA/RSN IE for the AP. If this field is not
 +               * set, the results from previous scan will be used. If no
 +               * data for the new AP is found, scan results will be requested
 +               * again (without scan request). At this point, the driver is
 +               * expected to provide WPA/RSN IE for the AP (if WPA/WPA2 is
 +               * used).
 +               *
 +               * This should start with the first IE (fixed fields before IEs
 +               * are not included).
 +               */
 +              const u8 *beacon_ies;
 +
 +              /**
 +               * beacon_ies_len - Length of beacon_ies */
 +              size_t beacon_ies_len;
 +
 +              /**
 +               * freq - Frequency of the operational channel in MHz
 +               */
 +              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;
 +
 +      /**
 +       * struct disassoc_info - Data for EVENT_DISASSOC events
 +       */
 +      struct disassoc_info {
 +              /**
 +               * addr - Station address (for AP mode)
 +               */
 +              const u8 *addr;
 +
 +              /**
 +               * reason_code - Reason Code (host byte order) used in
 +               *      Deauthentication frame
 +               */
 +              u16 reason_code;
 +
 +              /**
 +               * ie - Optional IE(s) in Disassociation frame
 +               */
 +              const u8 *ie;
 +
 +              /**
 +               * ie_len - Length of ie buffer in octets
 +               */
 +              size_t ie_len;
 +
 +              /**
 +               * locally_generated - Whether the frame was locally generated
 +               */
 +              int locally_generated;
 +      } disassoc_info;
 +
 +      /**
 +       * struct deauth_info - Data for EVENT_DEAUTH events
 +       */
 +      struct deauth_info {
 +              /**
 +               * addr - Station address (for AP mode)
 +               */
 +              const u8 *addr;
 +
 +              /**
 +               * reason_code - Reason Code (host byte order) used in
 +               *      Deauthentication frame
 +               */
 +              u16 reason_code;
 +
 +              /**
 +               * ie - Optional IE(s) in Deauthentication frame
 +               */
 +              const u8 *ie;
 +
 +              /**
 +               * ie_len - Length of ie buffer in octets
 +               */
 +              size_t ie_len;
 +
 +              /**
 +               * locally_generated - Whether the frame was locally generated
 +               */
 +              int locally_generated;
 +      } deauth_info;
 +
 +      /**
 +       * struct michael_mic_failure - Data for EVENT_MICHAEL_MIC_FAILURE
 +       */
 +      struct michael_mic_failure {
 +              int unicast;
 +              const u8 *src;
 +      } michael_mic_failure;
 +
 +      /**
 +       * struct interface_status - Data for EVENT_INTERFACE_STATUS
 +       */
 +      struct interface_status {
 +              char ifname[100];
 +              enum {
 +                      EVENT_INTERFACE_ADDED, EVENT_INTERFACE_REMOVED
 +              } ievent;
 +      } interface_status;
 +
 +      /**
 +       * struct pmkid_candidate - Data for EVENT_PMKID_CANDIDATE
 +       */
 +      struct pmkid_candidate {
 +              /** BSSID of the PMKID candidate */
 +              u8 bssid[ETH_ALEN];
 +              /** Smaller the index, higher the priority */
 +              int index;
 +              /** Whether RSN IE includes pre-authenticate flag */
 +              int preauth;
 +      } pmkid_candidate;
 +
 +      /**
 +       * struct stkstart - Data for EVENT_STKSTART
 +       */
 +      struct stkstart {
 +              u8 peer[ETH_ALEN];
 +      } stkstart;
 +
 +      /**
 +       * struct tdls - Data for EVENT_TDLS
 +       */
 +      struct tdls {
 +              u8 peer[ETH_ALEN];
 +              enum {
 +                      TDLS_REQUEST_SETUP,
 +                      TDLS_REQUEST_TEARDOWN,
 +                      TDLS_REQUEST_DISCOVER,
 +              } oper;
 +              u16 reason_code; /* for teardown */
 +      } tdls;
 +
 +      /**
 +       * struct wnm - Data for EVENT_WNM
 +       */
 +      struct wnm {
 +              u8 addr[ETH_ALEN];
 +              enum {
 +                      WNM_OPER_SLEEP,
 +              } oper;
 +              enum {
 +                      WNM_SLEEP_ENTER,
 +                      WNM_SLEEP_EXIT
 +              } sleep_action;
 +              int sleep_intval;
 +              u16 reason_code;
 +              u8 *buf;
 +              u16 buf_len;
 +      } wnm;
 +
 +      /**
 +       * struct ft_ies - FT information elements (EVENT_FT_RESPONSE)
 +       *
 +       * During FT (IEEE 802.11r) authentication sequence, the driver is
 +       * expected to use this event to report received FT IEs (MDIE, FTIE,
 +       * RSN IE, TIE, possible resource request) to the supplicant. The FT
 +       * IEs for the next message will be delivered through the
 +       * struct wpa_driver_ops::update_ft_ies() callback.
 +       */
 +      struct ft_ies {
 +              const u8 *ies;
 +              size_t ies_len;
 +              int ft_action;
 +              u8 target_ap[ETH_ALEN];
 +              /** Optional IE(s), e.g., WMM TSPEC(s), for RIC-Request */
 +              const u8 *ric_ies;
 +              /** Length of ric_ies buffer in octets */
 +              size_t ric_ies_len;
 +      } ft_ies;
 +
 +      /**
 +       * struct ibss_rsn_start - Data for EVENT_IBSS_RSN_START
 +       */
 +      struct ibss_rsn_start {
 +              u8 peer[ETH_ALEN];
 +      } ibss_rsn_start;
 +
 +      /**
 +       * struct auth_info - Data for EVENT_AUTH events
 +       */
 +      struct auth_info {
 +              u8 peer[ETH_ALEN];
 +              u8 bssid[ETH_ALEN];
 +              u16 auth_type;
 +              u16 auth_transaction;
 +              u16 status_code;
 +              const u8 *ies;
 +              size_t ies_len;
 +      } auth;
 +
 +      /**
 +       * struct assoc_reject - Data for EVENT_ASSOC_REJECT events
 +       */
 +      struct assoc_reject {
 +              /**
 +               * bssid - BSSID of the AP that rejected association
 +               */
 +              const u8 *bssid;
 +
 +              /**
 +               * resp_ies - (Re)Association Response IEs
 +               *
 +               * Optional association data from the driver. This data is not
 +               * required WPA, but may be useful for some protocols and as
 +               * such, should be reported if this is available to the driver
 +               * interface.
 +               *
 +               * This should start with the first IE (fixed fields before IEs
 +               * are not included).
 +               */
 +              const u8 *resp_ies;
 +
 +              /**
 +               * resp_ies_len - Length of resp_ies in bytes
 +               */
 +              size_t resp_ies_len;
 +
 +              /**
 +               * status_code - Status Code from (Re)association Response
 +               */
 +              u16 status_code;
 +      } assoc_reject;
 +
 +      struct timeout_event {
 +              u8 addr[ETH_ALEN];
 +      } timeout_event;
 +
 +      /**
 +       * struct tx_status - Data for EVENT_TX_STATUS events
 +       */
 +      struct tx_status {
 +              u16 type;
 +              u16 stype;
 +              const u8 *dst;
 +              const u8 *data;
 +              size_t data_len;
 +              int ack;
 +      } tx_status;
 +
 +      /**
 +       * struct rx_from_unknown - Data for EVENT_RX_FROM_UNKNOWN events
 +       */
 +      struct rx_from_unknown {
 +              const u8 *bssid;
 +              const u8 *addr;
 +              int wds;
 +      } rx_from_unknown;
 +
 +      /**
 +       * struct rx_mgmt - Data for EVENT_RX_MGMT events
 +       */
 +      struct rx_mgmt {
 +              const u8 *frame;
 +              size_t frame_len;
 +              u32 datarate;
 +
 +              /**
 +               * 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.
 +               */
 +              void *drv_priv;
 +
 +              /**
 +               * freq - Frequency (in MHz) on which the frame was received
 +               */
 +              int freq;
 +
 +              /**
 +               * ssi_signal - Signal strength in dBm (or 0 if not available)
 +               */
 +              int ssi_signal;
 +      } rx_mgmt;
 +
 +      /**
 +       * struct remain_on_channel - Data for EVENT_REMAIN_ON_CHANNEL events
 +       *
 +       * This is also used with EVENT_CANCEL_REMAIN_ON_CHANNEL events.
 +       */
 +      struct remain_on_channel {
 +              /**
 +               * freq - Channel frequency in MHz
 +               */
 +              unsigned int freq;
 +
 +              /**
 +               * duration - Duration to remain on the channel in milliseconds
 +               */
 +              unsigned int duration;
 +      } remain_on_channel;
 +
 +      /**
 +       * struct scan_info - Optional data for EVENT_SCAN_RESULTS events
 +       * @aborted: Whether the scan was aborted
 +       * @freqs: Scanned frequencies in MHz (%NULL = all channels scanned)
 +       * @num_freqs: Number of entries in freqs array
 +       * @ssids: Scanned SSIDs (%NULL or zero-length SSID indicates wildcard
 +       *      SSID)
 +       * @num_ssids: Number of entries in ssids array
 +       */
 +      struct scan_info {
 +              int aborted;
 +              const int *freqs;
 +              size_t num_freqs;
 +              struct wpa_driver_scan_ssid ssids[WPAS_MAX_SCAN_SSIDS];
 +              size_t num_ssids;
 +      } scan_info;
 +
 +      /**
 +       * struct rx_probe_req - Data for EVENT_RX_PROBE_REQ events
 +       */
 +      struct rx_probe_req {
 +              /**
 +               * sa - Source address of the received Probe Request frame
 +               */
 +              const u8 *sa;
 +
 +              /**
 +               * da - Destination address of the received Probe Request frame
 +               *      or %NULL if not available
 +               */
 +              const u8 *da;
 +
 +              /**
 +               * bssid - BSSID of the received Probe Request frame or %NULL
 +               *      if not available
 +               */
 +              const u8 *bssid;
 +
 +              /**
 +               * ie - IEs from the Probe Request body
 +               */
 +              const u8 *ie;
 +
 +              /**
 +               * ie_len - Length of ie buffer in octets
 +               */
 +              size_t ie_len;
 +
 +              /**
 +               * signal - signal strength in dBm (or 0 if not available)
 +               */
 +              int ssi_signal;
 +      } rx_probe_req;
 +
 +      /**
 +       * struct new_sta - Data for EVENT_NEW_STA events
 +       */
 +      struct new_sta {
 +              const u8 *addr;
 +      } new_sta;
 +
 +      /**
 +       * struct eapol_rx - Data for EVENT_EAPOL_RX events
 +       */
 +      struct eapol_rx {
 +              const u8 *src;
 +              const u8 *data;
 +              size_t data_len;
 +      } eapol_rx;
 +
 +      /**
 +       * signal_change - Data for EVENT_SIGNAL_CHANGE events
 +       */
 +      struct wpa_signal_info signal_change;
 +
 +      /**
 +       * struct best_channel - Data for EVENT_BEST_CHANNEL events
 +       * @freq_24: Best 2.4 GHz band channel frequency in MHz
 +       * @freq_5: Best 5 GHz band channel frequency in MHz
 +       * @freq_overall: Best channel frequency in MHz
 +       *
 +       * 0 can be used to indicate no preference in either band.
 +       */
 +      struct best_channel {
 +              int freq_24;
 +              int freq_5;
 +              int freq_overall;
 +      } best_chan;
 +
 +      struct unprot_deauth {
 +              const u8 *sa;
 +              const u8 *da;
 +              u16 reason_code;
 +      } unprot_deauth;
 +
 +      struct unprot_disassoc {
 +              const u8 *sa;
 +              const u8 *da;
 +              u16 reason_code;
 +      } unprot_disassoc;
 +
 +      /**
 +       * struct low_ack - Data for EVENT_STATION_LOW_ACK events
 +       * @addr: station address
 +       */
 +      struct low_ack {
 +              u8 addr[ETH_ALEN];
 +      } low_ack;
 +
 +      /**
 +       * struct ibss_peer_lost - Data for EVENT_IBSS_PEER_LOST
 +       */
 +      struct ibss_peer_lost {
 +              u8 peer[ETH_ALEN];
 +      } ibss_peer_lost;
 +
 +      /**
 +       * struct driver_gtk_rekey - Data for EVENT_DRIVER_GTK_REKEY
 +       */
 +      struct driver_gtk_rekey {
 +              const u8 *bssid;
 +              const u8 *replay_ctr;
 +      } driver_gtk_rekey;
 +
 +      /**
 +       * struct client_poll - Data for EVENT_DRIVER_CLIENT_POLL_OK events
 +       * @addr: station address
 +       */
 +      struct client_poll {
 +              u8 addr[ETH_ALEN];
 +      } client_poll;
 +
 +      /**
 +       * struct eapol_tx_status
 +       * @dst: Original destination
 +       * @data: Data starting with IEEE 802.1X header (!)
 +       * @data_len: Length of data
 +       * @ack: Indicates ack or lost frame
 +       *
 +       * This corresponds to hapd_send_eapol if the frame sent
 +       * there isn't just reported as EVENT_TX_STATUS.
 +       */
 +      struct eapol_tx_status {
 +              const u8 *dst;
 +              const u8 *data;
 +              int data_len;
 +              int ack;
 +      } eapol_tx_status;
 +
 +      /**
 +       * struct ch_switch
 +       * @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;
 +
 +      /**
 +       * struct connect_failed - Data for EVENT_CONNECT_FAILED_REASON
 +       * @addr: Remote client address
 +       * @code: Reason code for connection failure
 +       */
 +      struct connect_failed_reason {
 +              u8 addr[ETH_ALEN];
 +              enum {
 +                      MAX_CLIENT_REACHED,
 +                      BLOCKED_CLIENT
 +              } code;
 +      } connect_failed_reason;
 +
 +      /**
 +       * struct dfs_event - Data for radar detected events
 +       * @freq: Frequency of the channel in MHz
 +       */
 +      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
++       * @vht_seg0_center_ch: VHT mode Segment0 center channel
++       * @vht_seg1_center_ch: VHT mode Segment1 center channel
++       * @ch_width: Selected Channel width by driver. Driver may choose to
++       *      change hostapd configured ACS channel width due driver internal
++       *      channel restrictions.
++       * hw_mode: Selected band (used with hw_mode=any)
 +       */
 +      struct acs_selected_channels {
 +              u8 pri_channel;
 +              u8 sec_channel;
++              u8 vht_seg0_center_ch;
++              u8 vht_seg1_center_ch;
++              u16 ch_width;
++              enum hostapd_hw_mode hw_mode;
 +      } acs_selected_channels;
 +};
 +
 +/**
 + * wpa_supplicant_event - Report a driver event for wpa_supplicant
 + * @ctx: Context pointer (wpa_s); this is the ctx variable registered
 + *    with struct wpa_driver_ops::init()
 + * @event: event type (defined above)
 + * @data: possible extra data for the event
 + *
 + * Driver wrapper code should call this function whenever an event is received
 + * from the driver.
 + */
 +void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
 +                        union wpa_event_data *data);
 +
 +
 +/*
 + * The following inline functions are provided for convenience to simplify
 + * event indication for some of the common events.
 + */
 +
 +static inline void drv_event_assoc(void *ctx, const u8 *addr, const u8 *ie,
 +                                 size_t ielen, int reassoc)
 +{
 +      union wpa_event_data event;
 +      os_memset(&event, 0, sizeof(event));
 +      event.assoc_info.reassoc = reassoc;
 +      event.assoc_info.req_ies = ie;
 +      event.assoc_info.req_ies_len = ielen;
 +      event.assoc_info.addr = addr;
 +      wpa_supplicant_event(ctx, EVENT_ASSOC, &event);
 +}
 +
 +static inline void drv_event_disassoc(void *ctx, const u8 *addr)
 +{
 +      union wpa_event_data event;
 +      os_memset(&event, 0, sizeof(event));
 +      event.disassoc_info.addr = addr;
 +      wpa_supplicant_event(ctx, EVENT_DISASSOC, &event);
 +}
 +
 +static inline void drv_event_eapol_rx(void *ctx, const u8 *src, const u8 *data,
 +                                    size_t data_len)
 +{
 +      union wpa_event_data event;
 +      os_memset(&event, 0, sizeof(event));
 +      event.eapol_rx.src = src;
 +      event.eapol_rx.data = data;
 +      event.eapol_rx.data_len = data_len;
 +      wpa_supplicant_event(ctx, EVENT_EAPOL_RX, &event);
 +}
 +
 +/* driver_common.c */
 +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 const struct wpa_driver_ops *const wpa_drivers[];
 +
 +#endif /* DRIVER_H */
index 63b0e19e78cccfdc8cb227188c87a6e6c509f8f2,0000000000000000000000000000000000000000..b57dd03c9ca9f2417f3baaa2e52d7215029870f0
mode 100644,000000..100644
--- /dev/null
@@@ -1,1657 -1,0 +1,1657 @@@
-       if (drv != NULL)
-               os_free(drv);
 +/*
 + * WPA Supplicant - driver interaction with BSD net80211 layer
 + * Copyright (c) 2004, Sam Leffler <sam@errno.com>
 + * Copyright (c) 2004, 2Wire, 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 <sys/sysctl.h>
 +
 +#include "common.h"
 +#include "driver.h"
 +#include "eloop.h"
 +#include "common/ieee802_11_defs.h"
 +#include "common/wpa_common.h"
 +
 +#include <net/if.h>
 +#include <net/if_media.h>
 +
 +#ifdef __NetBSD__
 +#include <net/if_ether.h>
 +#else
 +#include <net/ethernet.h>
 +#endif
 +#include <net/route.h>
 +
 +#ifdef __DragonFly__
 +#include <netproto/802_11/ieee80211_ioctl.h>
 +#include <netproto/802_11/ieee80211_dragonfly.h>
 +#else /* __DragonFly__ */
 +#ifdef __GLIBC__
 +#include <netinet/ether.h>
 +#endif /* __GLIBC__ */
 +#include <net80211/ieee80211.h>
 +#include <net80211/ieee80211_ioctl.h>
 +#include <net80211/ieee80211_crypto.h>
 +#endif /* __DragonFly__ || __GLIBC__ */
 +#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
 +#include <net80211/ieee80211_freebsd.h>
 +#endif
 +#if __NetBSD__
 +#include <net80211/ieee80211_netbsd.h>
 +#endif
 +
 +#include "l2_packet/l2_packet.h"
 +
 +struct bsd_driver_data {
 +      struct hostapd_data *hapd;      /* back pointer */
 +
 +      int     sock;                   /* open socket for 802.11 ioctls */
 +      struct l2_packet_data *sock_xmit;/* raw packet xmit socket */
 +      int     route;                  /* routing socket for events */
 +      char    ifname[IFNAMSIZ+1];     /* interface name */
 +      unsigned int ifindex;           /* interface index */
 +      void    *ctx;
 +      struct wpa_driver_capa capa;    /* driver capability */
 +      int     is_ap;                  /* Access point mode */
 +      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 */
 +static int
 +bsd_set80211(void *priv, int op, int val, const void *arg, int arg_len)
 +{
 +      struct bsd_driver_data *drv = priv;
 +      struct ieee80211req ireq;
 +
 +      os_memset(&ireq, 0, sizeof(ireq));
 +      os_strlcpy(ireq.i_name, drv->ifname, sizeof(ireq.i_name));
 +      ireq.i_type = op;
 +      ireq.i_val = val;
 +      ireq.i_data = (void *) arg;
 +      ireq.i_len = arg_len;
 +
 +      if (ioctl(drv->sock, SIOCS80211, &ireq) < 0) {
 +              wpa_printf(MSG_ERROR, "ioctl[SIOCS80211, op=%u, val=%u, "
 +                         "arg_len=%u]: %s", op, val, arg_len,
 +                         strerror(errno));
 +              return -1;
 +      }
 +      return 0;
 +}
 +
 +static int
 +bsd_get80211(void *priv, struct ieee80211req *ireq, int op, void *arg,
 +           int arg_len)
 +{
 +      struct bsd_driver_data *drv = priv;
 +
 +      os_memset(ireq, 0, sizeof(*ireq));
 +      os_strlcpy(ireq->i_name, drv->ifname, sizeof(ireq->i_name));
 +      ireq->i_type = op;
 +      ireq->i_len = arg_len;
 +      ireq->i_data = arg;
 +
 +      if (ioctl(drv->sock, SIOCG80211, ireq) < 0) {
 +              wpa_printf(MSG_ERROR, "ioctl[SIOCS80211, op=%u, "
 +                         "arg_len=%u]: %s", op, arg_len, strerror(errno));
 +              return -1;
 +      }
 +      return 0;
 +}
 +
 +static int
 +get80211var(struct bsd_driver_data *drv, int op, void *arg, int arg_len)
 +{
 +      struct ieee80211req ireq;
 +
 +      if (bsd_get80211(drv, &ireq, op, arg, arg_len) < 0)
 +              return -1;
 +      return ireq.i_len;
 +}
 +
 +static int
 +set80211var(struct bsd_driver_data *drv, int op, const void *arg, int arg_len)
 +{
 +      return bsd_set80211(drv, op, 0, arg, arg_len);
 +}
 +
 +static int
 +set80211param(struct bsd_driver_data *drv, int op, int arg)
 +{
 +      return bsd_set80211(drv, op, arg, NULL, 0);
 +}
 +
 +static int
 +bsd_get_ssid(void *priv, u8 *ssid, int len)
 +{
 +      struct bsd_driver_data *drv = priv;
 +#ifdef SIOCG80211NWID
 +      struct ieee80211_nwid nwid;
 +      struct ifreq ifr;
 +
 +      os_memset(&ifr, 0, sizeof(ifr));
 +      os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name));
 +      ifr.ifr_data = (void *)&nwid;
 +      if (ioctl(drv->sock, SIOCG80211NWID, &ifr) < 0 ||
 +          nwid.i_len > IEEE80211_NWID_LEN)
 +              return -1;
 +      os_memcpy(ssid, nwid.i_nwid, nwid.i_len);
 +      return nwid.i_len;
 +#else
 +      return get80211var(drv, IEEE80211_IOC_SSID, ssid, IEEE80211_NWID_LEN);
 +#endif
 +}
 +
 +static int
 +bsd_set_ssid(void *priv, const u8 *ssid, int ssid_len)
 +{
 +      struct bsd_driver_data *drv = priv;
 +#ifdef SIOCS80211NWID
 +      struct ieee80211_nwid nwid;
 +      struct ifreq ifr;
 +
 +      os_memcpy(nwid.i_nwid, ssid, ssid_len);
 +      nwid.i_len = ssid_len;
 +      os_memset(&ifr, 0, sizeof(ifr));
 +      os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name));
 +      ifr.ifr_data = (void *)&nwid;
 +      return ioctl(drv->sock, SIOCS80211NWID, &ifr);
 +#else
 +      return set80211var(drv, IEEE80211_IOC_SSID, ssid, ssid_len);
 +#endif
 +}
 +
 +static int
 +bsd_get_if_media(void *priv)
 +{
 +      struct bsd_driver_data *drv = priv;
 +      struct ifmediareq ifmr;
 +
 +      os_memset(&ifmr, 0, sizeof(ifmr));
 +      os_strlcpy(ifmr.ifm_name, drv->ifname, sizeof(ifmr.ifm_name));
 +
 +      if (ioctl(drv->sock, SIOCGIFMEDIA, &ifmr) < 0) {
 +              wpa_printf(MSG_ERROR, "%s: SIOCGIFMEDIA %s", __func__,
 +                         strerror(errno));
 +              return -1;
 +      }
 +
 +      return ifmr.ifm_current;
 +}
 +
 +static int
 +bsd_set_if_media(void *priv, int media)
 +{
 +      struct bsd_driver_data *drv = priv;
 +      struct ifreq ifr;
 +
 +      os_memset(&ifr, 0, sizeof(ifr));
 +      os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name));
 +      ifr.ifr_media = media;
 +
 +      if (ioctl(drv->sock, SIOCSIFMEDIA, &ifr) < 0) {
 +              wpa_printf(MSG_ERROR, "%s: SIOCSIFMEDIA %s", __func__,
 +                         strerror(errno));
 +              return -1;
 +      }
 +
 +      return 0;
 +}
 +
 +static int
 +bsd_set_mediaopt(void *priv, uint32_t mask, uint32_t mode)
 +{
 +      int media = bsd_get_if_media(priv);
 +
 +      if (media < 0)
 +              return -1;
 +      media &= ~mask;
 +      media |= mode;
 +      if (bsd_set_if_media(priv, media) < 0)
 +              return -1;
 +      return 0;
 +}
 +
 +static int
 +bsd_del_key(void *priv, const u8 *addr, int key_idx)
 +{
 +      struct ieee80211req_del_key wk;
 +
 +      os_memset(&wk, 0, sizeof(wk));
 +      if (addr == NULL) {
 +              wpa_printf(MSG_DEBUG, "%s: key_idx=%d", __func__, key_idx);
 +              wk.idk_keyix = key_idx;
 +      } else {
 +              wpa_printf(MSG_DEBUG, "%s: addr=" MACSTR, __func__,
 +                         MAC2STR(addr));
 +              os_memcpy(wk.idk_macaddr, addr, IEEE80211_ADDR_LEN);
 +              wk.idk_keyix = (u_int8_t) IEEE80211_KEYIX_NONE; /* XXX */
 +      }
 +
 +      return set80211var(priv, IEEE80211_IOC_DELKEY, &wk, sizeof(wk));
 +}
 +
 +static int
 +bsd_send_mlme_param(void *priv, const u8 op, const u16 reason, const u8 *addr)
 +{
 +      struct ieee80211req_mlme mlme;
 +
 +      os_memset(&mlme, 0, sizeof(mlme));
 +      mlme.im_op = op;
 +      mlme.im_reason = reason;
 +      os_memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN);
 +      return set80211var(priv, IEEE80211_IOC_MLME, &mlme, sizeof(mlme));
 +}
 +
 +static int
 +bsd_ctrl_iface(void *priv, int enable)
 +{
 +      struct bsd_driver_data *drv = priv;
 +      struct ifreq ifr;
 +
 +      os_memset(&ifr, 0, sizeof(ifr));
 +      os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name));
 +
 +      if (ioctl(drv->sock, SIOCGIFFLAGS, &ifr) < 0) {
 +              wpa_printf(MSG_ERROR, "ioctl[SIOCGIFFLAGS]: %s",
 +                         strerror(errno));
 +              return -1;
 +      }
 +
 +      if (enable) {
 +              if (ifr.ifr_flags & IFF_UP)
 +                      return 0;
 +              ifr.ifr_flags |= IFF_UP;
 +      } else {
 +              if (!(ifr.ifr_flags & IFF_UP))
 +                      return 0;
 +              ifr.ifr_flags &= ~IFF_UP;
 +      }
 +
 +      if (ioctl(drv->sock, SIOCSIFFLAGS, &ifr) < 0) {
 +              wpa_printf(MSG_ERROR, "ioctl[SIOCSIFFLAGS]: %s",
 +                         strerror(errno));
 +              return -1;
 +      }
 +
 +      return 0;
 +}
 +
 +static int
 +bsd_set_key(const char *ifname, void *priv, enum wpa_alg alg,
 +          const unsigned char *addr, int key_idx, int set_tx, const u8 *seq,
 +          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,
 +                 set_tx, seq_len, key_len);
 +
 +      if (alg == WPA_ALG_NONE) {
 +#ifndef HOSTAPD
 +              if (addr == NULL || is_broadcast_ether_addr(addr))
 +                      return bsd_del_key(priv, NULL, key_idx);
 +              else
 +#endif /* HOSTAPD */
 +                      return bsd_del_key(priv, addr, key_idx);
 +      }
 +
 +      os_memset(&wk, 0, sizeof(wk));
 +      switch (alg) {
 +      case WPA_ALG_WEP:
 +              wk.ik_type = IEEE80211_CIPHER_WEP;
 +              break;
 +      case WPA_ALG_TKIP:
 +              wk.ik_type = IEEE80211_CIPHER_TKIP;
 +              break;
 +      case WPA_ALG_CCMP:
 +              wk.ik_type = IEEE80211_CIPHER_AES_CCM;
 +              break;
 +      default:
 +              wpa_printf(MSG_ERROR, "%s: unknown alg=%d", __func__, alg);
 +              return -1;
 +      }
 +
 +      wk.ik_flags = IEEE80211_KEY_RECV;
 +      if (set_tx)
 +              wk.ik_flags |= IEEE80211_KEY_XMIT;
 +
 +      if (addr == NULL) {
 +              os_memset(wk.ik_macaddr, 0xff, IEEE80211_ADDR_LEN);
 +              wk.ik_keyix = key_idx;
 +      } else {
 +              os_memcpy(wk.ik_macaddr, addr, IEEE80211_ADDR_LEN);
 +              /*
 +               * Deduce whether group/global or unicast key by checking
 +               * the address (yech).  Note also that we can only mark global
 +               * keys default; doing this for a unicast key is an error.
 +               */
 +              if (is_broadcast_ether_addr(addr)) {
 +                      wk.ik_flags |= IEEE80211_KEY_GROUP;
 +                      wk.ik_keyix = key_idx;
 +              } else {
 +                      wk.ik_keyix = key_idx == 0 ? IEEE80211_KEYIX_NONE :
 +                              key_idx;
 +              }
 +      }
 +      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
 +              /*
 +               * wk.ik_keyrsc is in host byte order (big endian), need to
 +               * swap it to match with the byte order used in WPA.
 +               */
 +              int i;
 +              u8 *keyrsc = (u8 *) &wk.ik_keyrsc;
 +              for (i = 0; i < seq_len; i++)
 +                      keyrsc[WPA_KEY_RSC_LEN - i - 1] = seq[i];
 +#else /* WORDS_BIGENDIAN */
 +              os_memcpy(&wk.ik_keyrsc, seq, seq_len);
 +#endif /* WORDS_BIGENDIAN */
 +      }
 +      os_memcpy(wk.ik_keydata, key, key_len);
 +
 +      return set80211var(priv, IEEE80211_IOC_WPAKEY, &wk, sizeof(wk));
 +}
 +
 +static int
 +bsd_configure_wpa(void *priv, struct wpa_bss_params *params)
 +{
 +#ifndef IEEE80211_IOC_APPIE
 +      static const char *ciphernames[] =
 +              { "WEP", "TKIP", "AES-OCB", "AES-CCM", "CKIP", "NONE" };
 +      int v;
 +
 +      switch (params->wpa_group) {
 +      case WPA_CIPHER_CCMP:
 +              v = IEEE80211_CIPHER_AES_CCM;
 +              break;
 +      case WPA_CIPHER_TKIP:
 +              v = IEEE80211_CIPHER_TKIP;
 +              break;
 +      case WPA_CIPHER_WEP104:
 +              v = IEEE80211_CIPHER_WEP;
 +              break;
 +      case WPA_CIPHER_WEP40:
 +              v = IEEE80211_CIPHER_WEP;
 +              break;
 +      case WPA_CIPHER_NONE:
 +              v = IEEE80211_CIPHER_NONE;
 +              break;
 +      default:
 +              wpa_printf(MSG_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)) {
 +              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)) {
 +                      wpa_printf(MSG_INFO,
 +                                 "Unable to set group key length to %u", v);
 +                      return -1;
 +              }
 +      }
 +
 +      v = 0;
 +      if (params->wpa_pairwise & WPA_CIPHER_CCMP)
 +              v |= 1<<IEEE80211_CIPHER_AES_CCM;
 +      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(priv, IEEE80211_IOC_UCASTCIPHERS, v)) {
 +              wpa_printf(MSG_INFO,
 +                         "Unable to set pairwise key ciphers to 0x%x", v);
 +              return -1;
 +      }
 +
 +      wpa_printf(MSG_DEBUG, "%s: key management algorithms=0x%x",
 +                 __func__, params->wpa_key_mgmt);
 +      if (set80211param(priv, IEEE80211_IOC_KEYMGTALGS,
 +                        params->wpa_key_mgmt)) {
 +              wpa_printf(MSG_INFO,
 +                         "Unable to set key management algorithms to 0x%x",
 +                         params->wpa_key_mgmt);
 +              return -1;
 +      }
 +
 +      v = 0;
 +      if (params->rsn_preauth)
 +              v |= BIT(0);
 +      wpa_printf(MSG_DEBUG, "%s: rsn capabilities=0x%x",
 +                 __func__, params->rsn_preauth);
 +      if (set80211param(priv, IEEE80211_IOC_RSNCAPS, 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)) {
 +              wpa_printf(MSG_INFO, "Unable to set WPA to %u", params->wpa);
 +              return -1;
 +      }
 +      return 0;
 +}
 +
 +static int
 +bsd_set_ieee8021x(void *priv, struct wpa_bss_params *params)
 +{
 +      wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, params->enabled);
 +
 +      if (!params->enabled) {
 +              /* XXX restore state */
 +              return set80211param(priv, IEEE80211_IOC_AUTHMODE,
 +                                   IEEE80211_AUTH_AUTO);
 +      }
 +      if (!params->wpa && !params->ieee802_1x) {
 +              wpa_printf(MSG_ERROR, "%s: No 802.1X or WPA enabled",
 +                         __func__);
 +              return -1;
 +      }
 +      if (params->wpa && bsd_configure_wpa(priv, params) != 0) {
 +              wpa_printf(MSG_ERROR, "%s: Failed to configure WPA state",
 +                         __func__);
 +              return -1;
 +      }
 +      if (set80211param(priv, IEEE80211_IOC_AUTHMODE,
 +              (params->wpa ? IEEE80211_AUTH_WPA : IEEE80211_AUTH_8021X))) {
 +              wpa_printf(MSG_ERROR, "%s: Failed to enable WPA/802.1X",
 +                         __func__);
 +              return -1;
 +      }
 +      return bsd_ctrl_iface(priv, 1);
 +}
 +
 +static void
 +bsd_new_sta(void *priv, void *ctx, u8 addr[IEEE80211_ADDR_LEN])
 +{
 +      struct ieee80211req_wpaie ie;
 +      int ielen = 0;
 +      u8 *iebuf = NULL;
 +
 +      /*
 +       * Fetch and validate any negotiated WPA/RSN parameters.
 +       */
 +      memset(&ie, 0, sizeof(ie));
 +      memcpy(ie.wpa_macaddr, addr, IEEE80211_ADDR_LEN);
 +      if (get80211var(priv, IEEE80211_IOC_WPAIE, &ie, sizeof(ie)) < 0) {
 +              wpa_printf(MSG_INFO,
 +                         "Failed to get WPA/RSN information element");
 +              goto no_ie;
 +      }
 +      iebuf = ie.wpa_ie;
 +      ielen = ie.wpa_ie[1];
 +      if (ielen == 0)
 +              iebuf = NULL;
 +      else
 +              ielen += 2;
 +
 +no_ie:
 +      drv_event_assoc(ctx, addr, iebuf, ielen, 0);
 +}
 +
 +static int
 +bsd_send_eapol(void *priv, const u8 *addr, const u8 *data, size_t data_len,
 +             int encrypt, const u8 *own_addr, u32 flags)
 +{
 +      struct bsd_driver_data *drv = priv;
 +
 +      wpa_hexdump(MSG_MSGDUMP, "TX EAPOL", data, data_len);
 +
 +      return l2_packet_send(drv->sock_xmit, addr, ETH_P_EAPOL, data,
 +                            data_len);
 +}
 +
 +static int
 +bsd_set_freq(void *priv, struct hostapd_freq_params *freq)
 +{
 +      struct bsd_driver_data *drv = priv;
 +#ifdef SIOCS80211CHANNEL
 +      struct ieee80211chanreq creq;
 +#endif /* SIOCS80211CHANNEL */
 +      u32 mode;
 +      int channel = freq->channel;
 +
 +      if (channel < 14) {
 +              mode =
 +#ifdef CONFIG_IEEE80211N
 +                      freq->ht_enabled ? IFM_IEEE80211_11NG :
 +#endif /* CONFIG_IEEE80211N */
 +                      IFM_IEEE80211_11G;
 +      } else if (channel == 14) {
 +              mode = IFM_IEEE80211_11B;
 +      } else {
 +              mode =
 +#ifdef CONFIG_IEEE80211N
 +                      freq->ht_enabled ? IFM_IEEE80211_11NA :
 +#endif /* CONFIG_IEEE80211N */
 +                      IFM_IEEE80211_11A;
 +      }
 +      if (bsd_set_mediaopt(drv, IFM_MMASK, mode) < 0) {
 +              wpa_printf(MSG_ERROR, "%s: failed to set modulation mode",
 +                         __func__);
 +              return -1;
 +      }
 +
 +#ifdef SIOCS80211CHANNEL
 +      os_memset(&creq, 0, sizeof(creq));
 +      os_strlcpy(creq.i_name, drv->ifname, sizeof(creq.i_name));
 +      creq.i_channel = (u_int16_t)channel;
 +      return ioctl(drv->sock, SIOCS80211CHANNEL, &creq);
 +#else /* SIOCS80211CHANNEL */
 +      return set80211param(priv, IEEE80211_IOC_CHANNEL, channel);
 +#endif /* SIOCS80211CHANNEL */
 +}
 +
 +static int
 +bsd_set_opt_ie(void *priv, const u8 *ie, size_t ie_len)
 +{
 +#ifdef IEEE80211_IOC_APPIE
 +      wpa_printf(MSG_DEBUG, "%s: set WPA+RSN ie (len %lu)", __func__,
 +                 (unsigned long)ie_len);
 +      return bsd_set80211(priv, IEEE80211_IOC_APPIE, IEEE80211_APPIE_WPA,
 +                          ie, ie_len);
 +#endif /* IEEE80211_IOC_APPIE */
 +      return 0;
 +}
 +
 +static size_t
 +rtbuf_len(void)
 +{
 +      size_t len;
 +
 +      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", __func__,
 +                         strerror(errno));
 +              len = 2048;
 +      }
 +
 +      return len;
 +}
 +
 +#ifdef HOSTAPD
 +
 +/*
 + * Avoid conflicts with hostapd definitions by undefining couple of defines
 + * from net80211 header files.
 + */
 +#undef RSN_VERSION
 +#undef WPA_VERSION
 +#undef WPA_OUI_TYPE
 +
 +static int bsd_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr,
 +                        int reason_code);
 +
 +static const char *
 +ether_sprintf(const u8 *addr)
 +{
 +      static char buf[sizeof(MACSTR)];
 +
 +      if (addr != NULL)
 +              snprintf(buf, sizeof(buf), MACSTR, MAC2STR(addr));
 +      else
 +              snprintf(buf, sizeof(buf), MACSTR, 0,0,0,0,0,0);
 +      return buf;
 +}
 +
 +static int
 +bsd_set_privacy(void *priv, int enabled)
 +{
 +      wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled);
 +
 +      return set80211param(priv, IEEE80211_IOC_PRIVACY, enabled);
 +}
 +
 +static int
 +bsd_get_seqnum(const char *ifname, void *priv, const u8 *addr, int idx,
 +             u8 *seq)
 +{
 +      struct ieee80211req_key wk;
 +
 +      wpa_printf(MSG_DEBUG, "%s: addr=%s idx=%d",
 +                 __func__, ether_sprintf(addr), idx);
 +
 +      memset(&wk, 0, sizeof(wk));
 +      if (addr == NULL)
 +              memset(wk.ik_macaddr, 0xff, IEEE80211_ADDR_LEN);
 +      else
 +              memcpy(wk.ik_macaddr, addr, IEEE80211_ADDR_LEN);
 +      wk.ik_keyix = idx;
 +
 +      if (get80211var(priv, IEEE80211_IOC_WPAKEY, &wk, sizeof(wk)) < 0) {
 +              wpa_printf(MSG_INFO, "Failed to get encryption");
 +              return -1;
 +      }
 +
 +#ifdef WORDS_BIGENDIAN
 +      {
 +              /*
 +               * wk.ik_keytsc is in host byte order (big endian), need to
 +               * swap it to match with the byte order used in WPA.
 +               */
 +              int i;
 +              u8 tmp[WPA_KEY_RSC_LEN];
 +              memcpy(tmp, &wk.ik_keytsc, sizeof(wk.ik_keytsc));
 +              for (i = 0; i < WPA_KEY_RSC_LEN; i++) {
 +                      seq[i] = tmp[WPA_KEY_RSC_LEN - i - 1];
 +              }
 +      }
 +#else /* WORDS_BIGENDIAN */
 +      memcpy(seq, &wk.ik_keytsc, sizeof(wk.ik_keytsc));
 +#endif /* WORDS_BIGENDIAN */
 +      return 0;
 +}
 +
 +
 +static int
 +bsd_flush(void *priv)
 +{
 +      u8 allsta[IEEE80211_ADDR_LEN];
 +
 +      memset(allsta, 0xff, IEEE80211_ADDR_LEN);
 +      return bsd_sta_deauth(priv, NULL, allsta, IEEE80211_REASON_AUTH_LEAVE);
 +}
 +
 +
 +static int
 +bsd_read_sta_driver_data(void *priv, struct hostap_sta_driver_data *data,
 +                       const u8 *addr)
 +{
 +      struct ieee80211req_sta_stats stats;
 +
 +      memcpy(stats.is_u.macaddr, addr, IEEE80211_ADDR_LEN);
 +      if (get80211var(priv, IEEE80211_IOC_STA_STATS, &stats, sizeof(stats))
 +          > 0) {
 +              /* XXX? do packets counts include non-data frames? */
 +              data->rx_packets = stats.is_stats.ns_rx_data;
 +              data->rx_bytes = stats.is_stats.ns_rx_bytes;
 +              data->tx_packets = stats.is_stats.ns_tx_data;
 +              data->tx_bytes = stats.is_stats.ns_tx_bytes;
 +      }
 +      return 0;
 +}
 +
 +static int
 +bsd_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr, int reason_code)
 +{
 +      return bsd_send_mlme_param(priv, IEEE80211_MLME_DEAUTH, reason_code,
 +                                 addr);
 +}
 +
 +static int
 +bsd_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr,
 +               int reason_code)
 +{
 +      return bsd_send_mlme_param(priv, IEEE80211_MLME_DISASSOC, reason_code,
 +                                 addr);
 +}
 +
 +static void
 +bsd_wireless_event_receive(int sock, void *ctx, void *sock_ctx)
 +{
 +      struct bsd_driver_data *drv = ctx;
 +      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;
 +      union wpa_event_data data;
 +
 +      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",
 +                                 __func__, strerror(errno));
 +              return;
 +      }
 +
 +      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);
 +              return;
 +      }
 +      ifan = (struct if_announcemsghdr *) rtm;
 +      switch (rtm->rtm_type) {
 +      case RTM_IEEE80211:
 +              switch (ifan->ifan_what) {
 +              case RTM_IEEE80211_ASSOC:
 +              case RTM_IEEE80211_REASSOC:
 +              case RTM_IEEE80211_DISASSOC:
 +              case RTM_IEEE80211_SCAN:
 +                      break;
 +              case RTM_IEEE80211_LEAVE:
 +                      leave = (struct ieee80211_leave_event *) &ifan[1];
 +                      drv_event_disassoc(drv->hapd, leave->iev_addr);
 +                      break;
 +              case RTM_IEEE80211_JOIN:
 +#ifdef RTM_IEEE80211_REJOIN
 +              case RTM_IEEE80211_REJOIN:
 +#endif
 +                      join = (struct ieee80211_join_event *) &ifan[1];
 +                      bsd_new_sta(drv, drv->hapd, join->iev_addr);
 +                      break;
 +              case RTM_IEEE80211_REPLAY:
 +                      /* ignore */
 +                      break;
 +              case RTM_IEEE80211_MICHAEL:
 +                      mic = (struct ieee80211_michael_event *) &ifan[1];
 +                      wpa_printf(MSG_DEBUG,
 +                              "Michael MIC failure wireless event: "
 +                              "keyix=%u src_addr=" MACSTR, mic->iev_keyix,
 +                              MAC2STR(mic->iev_src));
 +                      os_memset(&data, 0, sizeof(data));
 +                      data.michael_mic_failure.unicast = 1;
 +                      data.michael_mic_failure.src = mic->iev_src;
 +                      wpa_supplicant_event(drv->hapd,
 +                                           EVENT_MICHAEL_MIC_FAILURE, &data);
 +                      break;
 +              }
 +              break;
 +      }
 +}
 +
 +static void
 +handle_read(void *ctx, const u8 *src_addr, const u8 *buf, size_t len)
 +{
 +      struct bsd_driver_data *drv = ctx;
 +      drv_event_eapol_rx(drv->hapd, src_addr, buf, len);
 +}
 +
 +static void *
 +bsd_init(struct hostapd_data *hapd, struct wpa_init_params *params)
 +{
 +      struct bsd_driver_data *drv;
 +
 +      drv = os_zalloc(sizeof(struct bsd_driver_data));
 +      if (drv == NULL) {
 +              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) {
 +              wpa_printf(MSG_ERROR, "socket[PF_INET,SOCK_DGRAM]: %s",
 +                         strerror(errno));
 +              goto bad;
 +      }
 +      os_strlcpy(drv->ifname, params->ifname, sizeof(drv->ifname));
 +
 +      drv->sock_xmit = l2_packet_init(drv->ifname, NULL, ETH_P_EAPOL,
 +                                      handle_read, drv, 0);
 +      if (drv->sock_xmit == NULL)
 +              goto bad;
 +      if (l2_packet_get_own_addr(drv->sock_xmit, params->own_addr))
 +              goto bad;
 +
 +      /* mark down during setup */
 +      if (bsd_ctrl_iface(drv, 0) < 0)
 +              goto bad;
 +
 +      drv->route = socket(PF_ROUTE, SOCK_RAW, 0);
 +      if (drv->route < 0) {
 +              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,
 +                               NULL);
 +
 +      if (bsd_set_mediaopt(drv, IFM_OMASK, IFM_IEEE80211_HOSTAP) < 0) {
 +              wpa_printf(MSG_ERROR, "%s: failed to set operation mode",
 +                         __func__);
 +              goto bad;
 +      }
 +
 +      return drv;
 +bad:
 +      if (drv->sock_xmit != NULL)
 +              l2_packet_deinit(drv->sock_xmit);
 +      if (drv->sock >= 0)
 +              close(drv->sock);
 +      os_free(drv->event_buf);
-                      int total_flags, int flags_or, int flags_and)
++      os_free(drv);
 +      return NULL;
 +}
 +
 +
 +static void
 +bsd_deinit(void *priv)
 +{
 +      struct bsd_driver_data *drv = priv;
 +
 +      if (drv->route >= 0) {
 +              eloop_unregister_read_sock(drv->route);
 +              close(drv->route);
 +      }
 +      bsd_ctrl_iface(drv, 0);
 +      if (drv->sock >= 0)
 +              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,
++                     unsigned int total_flags, unsigned int flags_or,
++                     unsigned 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
 +get80211param(struct bsd_driver_data *drv, int op)
 +{
 +      struct ieee80211req ireq;
 +
 +      if (bsd_get80211(drv, &ireq, op, NULL, 0) < 0)
 +              return -1;
 +      return ireq.i_val;
 +}
 +
 +static int
 +wpa_driver_bsd_get_bssid(void *priv, u8 *bssid)
 +{
 +      struct bsd_driver_data *drv = priv;
 +#ifdef SIOCG80211BSSID
 +      struct ieee80211_bssid bs;
 +
 +      os_strlcpy(bs.i_name, drv->ifname, sizeof(bs.i_name));
 +      if (ioctl(drv->sock, SIOCG80211BSSID, &bs) < 0)
 +              return -1;
 +      os_memcpy(bssid, bs.i_bssid, sizeof(bs.i_bssid));
 +      return 0;
 +#else
 +      return get80211var(drv, IEEE80211_IOC_BSSID,
 +              bssid, IEEE80211_ADDR_LEN) < 0 ? -1 : 0;
 +#endif
 +}
 +
 +static int
 +wpa_driver_bsd_get_ssid(void *priv, u8 *ssid)
 +{
 +      struct bsd_driver_data *drv = priv;
 +      return bsd_get_ssid(drv, ssid, 0);
 +}
 +
 +static int
 +wpa_driver_bsd_set_wpa_ie(struct bsd_driver_data *drv, const u8 *wpa_ie,
 +                        size_t wpa_ie_len)
 +{
 +#ifdef IEEE80211_IOC_APPIE
 +      return bsd_set_opt_ie(drv, wpa_ie, wpa_ie_len);
 +#else /* IEEE80211_IOC_APPIE */
 +      return set80211var(drv, IEEE80211_IOC_OPTIE, wpa_ie, wpa_ie_len);
 +#endif /* IEEE80211_IOC_APPIE */
 +}
 +
 +static int
 +wpa_driver_bsd_set_wpa_internal(void *priv, int wpa, int privacy)
 +{
 +      int ret = 0;
 +
 +      wpa_printf(MSG_DEBUG, "%s: wpa=%d privacy=%d",
 +              __FUNCTION__, wpa, privacy);
 +
 +      if (!wpa && wpa_driver_bsd_set_wpa_ie(priv, NULL, 0) < 0)
 +              ret = -1;
 +      if (set80211param(priv, IEEE80211_IOC_PRIVACY, privacy) < 0)
 +              ret = -1;
 +      if (set80211param(priv, IEEE80211_IOC_WPA, wpa) < 0)
 +              ret = -1;
 +
 +      return ret;
 +}
 +
 +static int
 +wpa_driver_bsd_set_wpa(void *priv, int enabled)
 +{
 +      wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled);
 +
 +      return wpa_driver_bsd_set_wpa_internal(priv, enabled ? 3 : 0, enabled);
 +}
 +
 +static int
 +wpa_driver_bsd_set_countermeasures(void *priv, int enabled)
 +{
 +      wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled);
 +      return set80211param(priv, IEEE80211_IOC_COUNTERMEASURES, enabled);
 +}
 +
 +
 +static int
 +wpa_driver_bsd_set_drop_unencrypted(void *priv, int enabled)
 +{
 +      wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled);
 +      return set80211param(priv, IEEE80211_IOC_DROPUNENCRYPTED, enabled);
 +}
 +
 +static int
 +wpa_driver_bsd_deauthenticate(void *priv, const u8 *addr, int reason_code)
 +{
 +      return bsd_send_mlme_param(priv, IEEE80211_MLME_DEAUTH, reason_code,
 +                                 addr);
 +}
 +
 +static int
 +wpa_driver_bsd_set_auth_alg(void *priv, int auth_alg)
 +{
 +      int authmode;
 +
 +      if ((auth_alg & WPA_AUTH_ALG_OPEN) &&
 +          (auth_alg & WPA_AUTH_ALG_SHARED))
 +              authmode = IEEE80211_AUTH_AUTO;
 +      else if (auth_alg & WPA_AUTH_ALG_SHARED)
 +              authmode = IEEE80211_AUTH_SHARED;
 +      else
 +              authmode = IEEE80211_AUTH_OPEN;
 +
 +      return set80211param(priv, IEEE80211_IOC_AUTHMODE, authmode);
 +}
 +
 +static void
 +handle_read(void *ctx, const u8 *src_addr, const u8 *buf, size_t len)
 +{
 +      struct bsd_driver_data *drv = ctx;
 +
 +      drv_event_eapol_rx(drv->ctx, src_addr, buf, len);
 +}
 +
 +static int
 +wpa_driver_bsd_associate(void *priv, struct wpa_driver_associate_params *params)
 +{
 +      struct bsd_driver_data *drv = priv;
 +      struct ieee80211req_mlme mlme;
 +      u32 mode;
 +      int privacy;
 +      int ret = 0;
 +
 +      wpa_printf(MSG_DEBUG,
 +              "%s: ssid '%.*s' wpa ie len %u pairwise %u group %u key mgmt %u"
 +              , __func__
 +                 , (unsigned int) params->ssid_len, params->ssid
 +              , (unsigned int) params->wpa_ie_len
 +              , params->pairwise_suite
 +              , params->group_suite
 +              , params->key_mgmt_suite
 +      );
 +
 +      switch (params->mode) {
 +      case IEEE80211_MODE_INFRA:
 +              mode = 0 /* STA */;
 +              break;
 +      case IEEE80211_MODE_IBSS:
 +              mode = IFM_IEEE80211_IBSS;
 +              break;
 +      case IEEE80211_MODE_AP:
 +              mode = IFM_IEEE80211_HOSTAP;
 +              break;
 +      default:
 +              wpa_printf(MSG_ERROR, "%s: unknown operation mode", __func__);
 +              return -1;
 +      }
 +      if (bsd_set_mediaopt(drv, IFM_OMASK, mode) < 0) {
 +              wpa_printf(MSG_ERROR, "%s: failed to set operation mode",
 +                         __func__);
 +              return -1;
 +      }
 +
 +      if (params->mode == IEEE80211_MODE_AP) {
 +              drv->sock_xmit = l2_packet_init(drv->ifname, NULL, ETH_P_EAPOL,
 +                                              handle_read, drv, 0);
 +              if (drv->sock_xmit == NULL)
 +                      return -1;
 +              drv->is_ap = 1;
 +              return 0;
 +      }
 +
 +      if (wpa_driver_bsd_set_drop_unencrypted(drv, params->drop_unencrypted)
 +          < 0)
 +              ret = -1;
 +      if (wpa_driver_bsd_set_auth_alg(drv, params->auth_alg) < 0)
 +              ret = -1;
 +      /* XXX error handling is wrong but unclear what to do... */
 +      if (wpa_driver_bsd_set_wpa_ie(drv, params->wpa_ie, params->wpa_ie_len) < 0)
 +              return -1;
 +
 +      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);
 +
 +      if (set80211param(drv, IEEE80211_IOC_PRIVACY, privacy) < 0)
 +              return -1;
 +
 +      if (params->wpa_ie_len &&
 +          set80211param(drv, IEEE80211_IOC_WPA,
 +                        params->wpa_ie[0] == WLAN_EID_RSN ? 2 : 1) < 0)
 +              return -1;
 +
 +      os_memset(&mlme, 0, sizeof(mlme));
 +      mlme.im_op = IEEE80211_MLME_ASSOC;
 +      if (params->ssid != NULL)
 +              os_memcpy(mlme.im_ssid, params->ssid, params->ssid_len);
 +      mlme.im_ssid_len = params->ssid_len;
 +      if (params->bssid != NULL)
 +              os_memcpy(mlme.im_macaddr, params->bssid, IEEE80211_ADDR_LEN);
 +      if (set80211var(drv, IEEE80211_IOC_MLME, &mlme, sizeof(mlme)) < 0)
 +              return -1;
 +      return ret;
 +}
 +
 +static int
 +wpa_driver_bsd_scan(void *priv, struct wpa_driver_scan_params *params)
 +{
 +      struct bsd_driver_data *drv = priv;
 +#ifdef IEEE80211_IOC_SCAN_MAX_SSID
 +      struct ieee80211_scan_req sr;
 +      int i;
 +#endif /* IEEE80211_IOC_SCAN_MAX_SSID */
 +
 +      if (bsd_set_mediaopt(drv, IFM_OMASK, 0 /* STA */) < 0) {
 +              wpa_printf(MSG_ERROR, "%s: failed to set operation mode",
 +                         __func__);
 +              return -1;
 +      }
 +
 +      if (set80211param(drv, IEEE80211_IOC_ROAMING,
 +                        IEEE80211_ROAMING_MANUAL) < 0) {
 +              wpa_printf(MSG_ERROR, "%s: failed to set "
 +                         "wpa_supplicant-based roaming: %s", __func__,
 +                         strerror(errno));
 +              return -1;
 +      }
 +
 +      if (wpa_driver_bsd_set_wpa(drv, 1) < 0) {
 +              wpa_printf(MSG_ERROR, "%s: failed to set wpa: %s", __func__,
 +                         strerror(errno));
 +              return -1;
 +      }
 +
 +      /* NB: interface must be marked UP to do a scan */
 +      if (bsd_ctrl_iface(drv, 1) < 0)
 +              return -1;
 +
 +#ifdef IEEE80211_IOC_SCAN_MAX_SSID
 +      os_memset(&sr, 0, sizeof(sr));
 +      sr.sr_flags = IEEE80211_IOC_SCAN_ACTIVE | IEEE80211_IOC_SCAN_ONCE |
 +              IEEE80211_IOC_SCAN_NOJOIN;
 +      sr.sr_duration = IEEE80211_IOC_SCAN_FOREVER;
 +      if (params->num_ssids > 0) {
 +              sr.sr_nssid = params->num_ssids;
 +#if 0
 +              /* Boundary check is done by upper layer */
 +              if (sr.sr_nssid > IEEE80211_IOC_SCAN_MAX_SSID)
 +                      sr.sr_nssid = IEEE80211_IOC_SCAN_MAX_SSID;
 +#endif
 +
 +              /* NB: check scan cache first */
 +              sr.sr_flags |= IEEE80211_IOC_SCAN_CHECK;
 +      }
 +      for (i = 0; i < sr.sr_nssid; i++) {
 +              sr.sr_ssid[i].len = params->ssids[i].ssid_len;
 +              os_memcpy(sr.sr_ssid[i].ssid, params->ssids[i].ssid,
 +                        sr.sr_ssid[i].len);
 +      }
 +
 +      /* NB: net80211 delivers a scan complete event so no need to poll */
 +      return set80211var(drv, IEEE80211_IOC_SCAN_REQ, &sr, sizeof(sr));
 +#else /* IEEE80211_IOC_SCAN_MAX_SSID */
 +      /* set desired ssid before scan */
 +      if (bsd_set_ssid(drv, params->ssids[0].ssid,
 +                       params->ssids[0].ssid_len) < 0)
 +              return -1;
 +
 +      /* NB: net80211 delivers a scan complete event so no need to poll */
 +      return set80211param(drv, IEEE80211_IOC_SCAN_REQ, 0);
 +#endif /* IEEE80211_IOC_SCAN_MAX_SSID */
 +}
 +
 +static void
 +wpa_driver_bsd_event_receive(int sock, void *ctx, void *sock_ctx)
 +{
 +      struct bsd_driver_data *drv = sock_ctx;
 +      struct if_announcemsghdr *ifan;
 +      struct if_msghdr *ifm;
 +      struct rt_msghdr *rtm;
 +      union wpa_event_data event;
 +      struct ieee80211_michael_event *mic;
 +      struct ieee80211_leave_event *leave;
 +      struct ieee80211_join_event *join;
 +      int n;
 +
 +      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",
 +                                 __func__, strerror(errno));
 +              return;
 +      }
 +
 +      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);
 +              return;
 +      }
 +      os_memset(&event, 0, sizeof(event));
 +      switch (rtm->rtm_type) {
 +      case RTM_IFANNOUNCE:
 +              ifan = (struct if_announcemsghdr *) rtm;
 +              if (ifan->ifan_index != drv->ifindex)
 +                      break;
 +              os_strlcpy(event.interface_status.ifname, drv->ifname,
 +                         sizeof(event.interface_status.ifname));
 +              switch (ifan->ifan_what) {
 +              case IFAN_DEPARTURE:
 +                      event.interface_status.ievent = EVENT_INTERFACE_REMOVED;
 +              default:
 +                      return;
 +              }
 +              wpa_printf(MSG_DEBUG, "RTM_IFANNOUNCE: Interface '%s' %s",
 +                         event.interface_status.ifname,
 +                         ifan->ifan_what == IFAN_DEPARTURE ?
 +                              "removed" : "added");
 +              wpa_supplicant_event(ctx, EVENT_INTERFACE_STATUS, &event);
 +              break;
 +      case RTM_IEEE80211:
 +              ifan = (struct if_announcemsghdr *) rtm;
 +              if (ifan->ifan_index != drv->ifindex)
 +                      break;
 +              switch (ifan->ifan_what) {
 +              case RTM_IEEE80211_ASSOC:
 +              case RTM_IEEE80211_REASSOC:
 +                      if (drv->is_ap)
 +                              break;
 +                      wpa_supplicant_event(ctx, EVENT_ASSOC, NULL);
 +                      break;
 +              case RTM_IEEE80211_DISASSOC:
 +                      if (drv->is_ap)
 +                              break;
 +                      wpa_supplicant_event(ctx, EVENT_DISASSOC, NULL);
 +                      break;
 +              case RTM_IEEE80211_SCAN:
 +                      if (drv->is_ap)
 +                              break;
 +                      wpa_supplicant_event(ctx, EVENT_SCAN_RESULTS, NULL);
 +                      break;
 +              case RTM_IEEE80211_LEAVE:
 +                      leave = (struct ieee80211_leave_event *) &ifan[1];
 +                      drv_event_disassoc(ctx, leave->iev_addr);
 +                      break;
 +              case RTM_IEEE80211_JOIN:
 +#ifdef RTM_IEEE80211_REJOIN
 +              case RTM_IEEE80211_REJOIN:
 +#endif
 +                      join = (struct ieee80211_join_event *) &ifan[1];
 +                      bsd_new_sta(drv, ctx, join->iev_addr);
 +                      break;
 +              case RTM_IEEE80211_REPLAY:
 +                      /* ignore */
 +                      break;
 +              case RTM_IEEE80211_MICHAEL:
 +                      mic = (struct ieee80211_michael_event *) &ifan[1];
 +                      wpa_printf(MSG_DEBUG,
 +                              "Michael MIC failure wireless event: "
 +                              "keyix=%u src_addr=" MACSTR, mic->iev_keyix,
 +                              MAC2STR(mic->iev_src));
 +
 +                      os_memset(&event, 0, sizeof(event));
 +                      event.michael_mic_failure.unicast =
 +                              !IEEE80211_IS_MULTICAST(mic->iev_dst);
 +                      wpa_supplicant_event(ctx, EVENT_MICHAEL_MIC_FAILURE,
 +                              &event);
 +                      break;
 +              }
 +              break;
 +      case RTM_IFINFO:
 +              ifm = (struct if_msghdr *) rtm;
 +              if (ifm->ifm_index != drv->ifindex)
 +                      break;
 +              if ((rtm->rtm_flags & RTF_UP) == 0) {
 +                      os_strlcpy(event.interface_status.ifname, drv->ifname,
 +                                 sizeof(event.interface_status.ifname));
 +                      event.interface_status.ievent = EVENT_INTERFACE_REMOVED;
 +                      wpa_printf(MSG_DEBUG, "RTM_IFINFO: Interface '%s' DOWN",
 +                                 event.interface_status.ifname);
 +                      wpa_supplicant_event(ctx, EVENT_INTERFACE_STATUS, &event);
 +              }
 +              break;
 +      }
 +}
 +
 +static void
 +wpa_driver_bsd_add_scan_entry(struct wpa_scan_results *res,
 +                            struct ieee80211req_scan_result *sr)
 +{
 +      struct wpa_scan_res *result, **tmp;
 +      size_t extra_len;
 +      u8 *pos;
 +
 +      extra_len = 2 + sr->isr_ssid_len;
 +      extra_len += 2 + sr->isr_nrates;
 +      extra_len += 3; /* ERP IE */
 +      extra_len += sr->isr_ie_len;
 +
 +      result = os_zalloc(sizeof(*result) + extra_len);
 +      if (result == NULL)
 +              return;
 +      os_memcpy(result->bssid, sr->isr_bssid, ETH_ALEN);
 +      result->freq = sr->isr_freq;
 +      result->beacon_int = sr->isr_intval;
 +      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);
 +
 +      *pos++ = WLAN_EID_SSID;
 +      *pos++ = sr->isr_ssid_len;
 +      os_memcpy(pos, sr + 1, sr->isr_ssid_len);
 +      pos += sr->isr_ssid_len;
 +
 +      /*
 +       * Deal all rates as supported rate.
 +       * Because net80211 doesn't report extended supported rate or not.
 +       */
 +      *pos++ = WLAN_EID_SUPP_RATES;
 +      *pos++ = sr->isr_nrates;
 +      os_memcpy(pos, sr->isr_rates, sr->isr_nrates);
 +      pos += sr->isr_nrates;
 +
 +      *pos++ = WLAN_EID_ERP_INFO;
 +      *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);
 +
 +      tmp = os_realloc_array(res->res, res->num + 1,
 +                             sizeof(struct wpa_scan_res *));
 +      if (tmp == NULL) {
 +              os_free(result);
 +              return;
 +      }
 +      tmp[res->num++] = result;
 +      res->res = tmp;
 +}
 +
 +struct wpa_scan_results *
 +wpa_driver_bsd_get_scan_results2(void *priv)
 +{
 +      struct ieee80211req_scan_result *sr;
 +      struct wpa_scan_results *res;
 +      int len, rest;
 +      uint8_t buf[24*1024], *pos;
 +
 +      len = get80211var(priv, IEEE80211_IOC_SCAN_RESULTS, buf, 24*1024);
 +      if (len < 0)
 +              return NULL;
 +
 +      res = os_zalloc(sizeof(*res));
 +      if (res == NULL)
 +              return NULL;
 +
 +      pos = buf;
 +      rest = len;
 +      while (rest >= sizeof(struct ieee80211req_scan_result)) {
 +              sr = (struct ieee80211req_scan_result *)pos;
 +              wpa_driver_bsd_add_scan_entry(res, sr);
 +              pos += sr->isr_len;
 +              rest -= sr->isr_len;
 +      }
 +
 +      wpa_printf(MSG_DEBUG, "Received %d bytes of scan results (%lu BSSes)",
 +                 len, (unsigned long)res->num);
 +
 +      return res;
 +}
 +
 +static int wpa_driver_bsd_capa(struct bsd_driver_data *drv)
 +{
 +#ifdef IEEE80211_IOC_DEVCAPS
 +/* kernel definitions copied from net80211/ieee80211_var.h */
 +#define IEEE80211_CIPHER_WEP            0
 +#define IEEE80211_CIPHER_TKIP           1
 +#define IEEE80211_CIPHER_AES_CCM        3
 +#define IEEE80211_CRYPTO_WEP            (1<<IEEE80211_CIPHER_WEP)
 +#define IEEE80211_CRYPTO_TKIP           (1<<IEEE80211_CIPHER_TKIP)
 +#define IEEE80211_CRYPTO_AES_CCM        (1<<IEEE80211_CIPHER_AES_CCM)
 +#define IEEE80211_C_HOSTAP      0x00000400      /* CAPABILITY: HOSTAP avail */
 +#define IEEE80211_C_WPA1        0x00800000      /* CAPABILITY: WPA1 avail */
 +#define IEEE80211_C_WPA2        0x01000000      /* CAPABILITY: WPA2 avail */
 +      struct ieee80211_devcaps_req devcaps;
 +
 +      if (get80211var(drv, IEEE80211_IOC_DEVCAPS, &devcaps,
 +                      sizeof(devcaps)) < 0) {
 +              wpa_printf(MSG_ERROR, "failed to IEEE80211_IOC_DEVCAPS: %s",
 +                         strerror(errno));
 +              return -1;
 +      }
 +
 +      wpa_printf(MSG_DEBUG, "%s: drivercaps=0x%08x,cryptocaps=0x%08x",
 +                 __func__, devcaps.dc_drivercaps, devcaps.dc_cryptocaps);
 +
 +      if (devcaps.dc_drivercaps & IEEE80211_C_WPA1)
 +              drv->capa.key_mgmt = WPA_DRIVER_CAPA_KEY_MGMT_WPA |
 +                      WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK;
 +      if (devcaps.dc_drivercaps & IEEE80211_C_WPA2)
 +              drv->capa.key_mgmt = WPA_DRIVER_CAPA_KEY_MGMT_WPA2 |
 +                      WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK;
 +
 +#ifdef __FreeBSD__
 +      drv->capa.enc |= WPA_DRIVER_CAPA_ENC_WEP40 |
 +          WPA_DRIVER_CAPA_ENC_WEP104 |
 +          WPA_DRIVER_CAPA_ENC_TKIP |
 +          WPA_DRIVER_CAPA_ENC_CCMP;
 +#else
 +      /*
 +       * XXX
 +       * FreeBSD exports hardware cryptocaps.  These have no meaning for wpa
 +       * since net80211 performs software crypto.
 +       */
 +      if (devcaps.dc_cryptocaps & IEEE80211_CRYPTO_WEP)
 +              drv->capa.enc |= WPA_DRIVER_CAPA_ENC_WEP40 |
 +                      WPA_DRIVER_CAPA_ENC_WEP104;
 +      if (devcaps.dc_cryptocaps & IEEE80211_CRYPTO_TKIP)
 +              drv->capa.enc |= WPA_DRIVER_CAPA_ENC_TKIP;
 +      if (devcaps.dc_cryptocaps & IEEE80211_CRYPTO_AES_CCM)
 +              drv->capa.enc |= WPA_DRIVER_CAPA_ENC_CCMP;
 +#endif
 +
 +      if (devcaps.dc_drivercaps & IEEE80211_C_HOSTAP)
 +              drv->capa.flags |= WPA_DRIVER_FLAGS_AP;
 +#undef IEEE80211_CIPHER_WEP
 +#undef IEEE80211_CIPHER_TKIP
 +#undef IEEE80211_CIPHER_AES_CCM
 +#undef IEEE80211_CRYPTO_WEP
 +#undef IEEE80211_CRYPTO_TKIP
 +#undef IEEE80211_CRYPTO_AES_CCM
 +#undef IEEE80211_C_HOSTAP
 +#undef IEEE80211_C_WPA1
 +#undef IEEE80211_C_WPA2
 +#else /* IEEE80211_IOC_DEVCAPS */
 +      /* 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.flags |= WPA_DRIVER_FLAGS_AP;
 +#endif /* IEEE80211_IOC_DEVCAPS */
 +#ifdef IEEE80211_IOC_SCAN_MAX_SSID
 +      drv->capa.max_scan_ssids = IEEE80211_IOC_SCAN_MAX_SSID;
 +#else /* IEEE80211_IOC_SCAN_MAX_SSID */
 +      drv->capa.max_scan_ssids = 1;
 +#endif /* IEEE80211_IOC_SCAN_MAX_SSID */
 +      drv->capa.auth = WPA_DRIVER_AUTH_OPEN |
 +              WPA_DRIVER_AUTH_SHARED |
 +              WPA_DRIVER_AUTH_LEAP;
 +      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)
 +{
 +#define       GETPARAM(drv, param, v) \
 +      (((v) = get80211param(drv, param)) != -1)
 +      struct bsd_driver_data *drv;
 +
 +      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
 +       *     wait for an interface to appear.  This seems ok; that
 +       *     doesn't belong here; it's really the job of devd.
 +       */
 +      drv->ifindex = if_nametoindex(ifname);
 +      if (drv->ifindex == 0) {
 +              wpa_printf(MSG_DEBUG, "%s: interface %s does not exist",
 +                         __func__, ifname);
 +              goto fail1;
 +      }
 +      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;
 +      eloop_register_read_sock(drv->route,
 +              wpa_driver_bsd_event_receive, ctx, drv);
 +
 +      drv->ctx = ctx;
 +
 +      if (!GETPARAM(drv, IEEE80211_IOC_ROAMING, drv->prev_roaming)) {
 +              wpa_printf(MSG_DEBUG, "%s: failed to get roaming state: %s",
 +                      __func__, strerror(errno));
 +              goto fail;
 +      }
 +      if (!GETPARAM(drv, IEEE80211_IOC_PRIVACY, drv->prev_privacy)) {
 +              wpa_printf(MSG_DEBUG, "%s: failed to get privacy state: %s",
 +                      __func__, strerror(errno));
 +              goto fail;
 +      }
 +      if (!GETPARAM(drv, IEEE80211_IOC_WPA, drv->prev_wpa)) {
 +              wpa_printf(MSG_DEBUG, "%s: failed to get wpa state: %s",
 +                      __func__, strerror(errno));
 +              goto fail;
 +      }
 +
 +      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
 +}
 +
 +static void
 +wpa_driver_bsd_deinit(void *priv)
 +{
 +      struct bsd_driver_data *drv = priv;
 +
 +      wpa_driver_bsd_set_wpa(drv, 0);
 +      eloop_unregister_read_sock(drv->route);
 +
 +      /* NB: mark interface down */
 +      bsd_ctrl_iface(drv, 0);
 +
 +      wpa_driver_bsd_set_wpa_internal(drv, drv->prev_wpa, drv->prev_privacy);
 +      if (set80211param(drv, IEEE80211_IOC_ROAMING, drv->prev_roaming) < 0)
 +              wpa_printf(MSG_DEBUG, "%s: failed to restore roaming state",
 +                      __func__);
 +
 +      if (drv->sock_xmit != NULL)
 +              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);
 +}
 +
 +static int
 +wpa_driver_bsd_get_capa(void *priv, struct wpa_driver_capa *capa)
 +{
 +      struct bsd_driver_data *drv = priv;
 +
 +      os_memcpy(capa, &drv->capa, sizeof(*capa));
 +      return 0;
 +}
 +#endif /* HOSTAPD */
 +
 +
 +const struct wpa_driver_ops wpa_driver_bsd_ops = {
 +      .name                   = "bsd",
 +      .desc                   = "BSD 802.11 support",
 +#ifdef HOSTAPD
 +      .hapd_init              = bsd_init,
 +      .hapd_deinit            = bsd_deinit,
 +      .set_privacy            = bsd_set_privacy,
 +      .get_seqnum             = bsd_get_seqnum,
 +      .flush                  = bsd_flush,
 +      .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,
 +      .get_bssid              = wpa_driver_bsd_get_bssid,
 +      .get_ssid               = wpa_driver_bsd_get_ssid,
 +      .set_countermeasures    = wpa_driver_bsd_set_countermeasures,
 +      .scan2                  = wpa_driver_bsd_scan,
 +      .get_scan_results2      = wpa_driver_bsd_get_scan_results2,
 +      .deauthenticate         = wpa_driver_bsd_deauthenticate,
 +      .associate              = wpa_driver_bsd_associate,
 +      .get_capa               = wpa_driver_bsd_get_capa,
 +#endif /* HOSTAPD */
 +      .set_freq               = bsd_set_freq,
 +      .set_key                = bsd_set_key,
 +      .set_ieee8021x          = bsd_set_ieee8021x,
 +      .hapd_set_ssid          = bsd_set_ssid,
 +      .hapd_get_ssid          = bsd_get_ssid,
 +      .hapd_send_eapol        = bsd_send_eapol,
 +      .set_generic_elem       = bsd_set_opt_ie,
 +};
index bf19a5842d9891012dee2e7d022973f1e3b6d8e0,0000000000000000000000000000000000000000..2f5ade68194e65e5e0a6857dcc9a456cb4f32422
mode 100644,000000..100644
--- /dev/null
@@@ -1,3219 -1,0 +1,3219 @@@
-       char ssid[32];
 +/*
 + * WPA Supplicant - Windows/NDIS driver interface
 + * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
 + *
 + * This software may be distributed under the terms of the BSD license.
 + * See README for more details.
 + */
 +
 +#ifdef __CYGWIN__
 +/* Avoid some header file conflicts by not including standard headers for
 + * cygwin builds when Packet32.h is included. */
 +#include "build_config.h"
 +int close(int fd);
 +#else /* __CYGWIN__ */
 +#include "includes.h"
 +#endif /* __CYGWIN__ */
 +#ifdef CONFIG_USE_NDISUIO
 +#include <winsock2.h>
 +#else /* CONFIG_USE_NDISUIO */
 +#include <Packet32.h>
 +#endif /* CONFIG_USE_NDISUIO */
 +#ifdef __MINGW32_VERSION
 +#include <ddk/ntddndis.h>
 +#else /* __MINGW32_VERSION */
 +#include <ntddndis.h>
 +#endif /* __MINGW32_VERSION */
 +
 +#ifdef _WIN32_WCE
 +#include <winioctl.h>
 +#include <nuiouser.h>
 +#include <devload.h>
 +#endif /* _WIN32_WCE */
 +
 +#include "common.h"
 +#include "driver.h"
 +#include "eloop.h"
 +#include "common/ieee802_11_defs.h"
 +#include "driver_ndis.h"
 +
 +int wpa_driver_register_event_cb(struct wpa_driver_ndis_data *drv);
 +#ifdef CONFIG_NDIS_EVENTS_INTEGRATED
 +void wpa_driver_ndis_event_pipe_cb(void *eloop_data, void *user_data);
 +#endif /* CONFIG_NDIS_EVENTS_INTEGRATED */
 +
 +static void wpa_driver_ndis_deinit(void *priv);
 +static void wpa_driver_ndis_poll(void *drv);
 +static void wpa_driver_ndis_poll_timeout(void *eloop_ctx, void *timeout_ctx);
 +static int wpa_driver_ndis_adapter_init(struct wpa_driver_ndis_data *drv);
 +static int wpa_driver_ndis_adapter_open(struct wpa_driver_ndis_data *drv);
 +static void wpa_driver_ndis_adapter_close(struct wpa_driver_ndis_data *drv);
 +
 +
 +static const u8 pae_group_addr[ETH_ALEN] =
 +{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 };
 +
 +
 +/* FIX: to be removed once this can be compiled with the complete NDIS
 + * header files */
 +#ifndef OID_802_11_BSSID
 +#define OID_802_3_MULTICAST_LIST                0x01010103
 +#define OID_802_11_BSSID                      0x0d010101
 +#define OID_802_11_SSID                       0x0d010102
 +#define OID_802_11_INFRASTRUCTURE_MODE                0x0d010108
 +#define OID_802_11_ADD_WEP                    0x0D010113
 +#define OID_802_11_REMOVE_WEP                 0x0D010114
 +#define OID_802_11_DISASSOCIATE                       0x0D010115
 +#define OID_802_11_BSSID_LIST                         0x0d010217
 +#define OID_802_11_AUTHENTICATION_MODE                0x0d010118
 +#define OID_802_11_PRIVACY_FILTER             0x0d010119
 +#define OID_802_11_BSSID_LIST_SCAN            0x0d01011A
 +#define OID_802_11_WEP_STATUS                 0x0d01011B
 +#define OID_802_11_ENCRYPTION_STATUS OID_802_11_WEP_STATUS
 +#define OID_802_11_ADD_KEY                    0x0d01011D
 +#define OID_802_11_REMOVE_KEY                         0x0d01011E
 +#define OID_802_11_ASSOCIATION_INFORMATION    0x0d01011F
 +#define OID_802_11_TEST                       0x0d010120
 +#define OID_802_11_CAPABILITY                         0x0d010122
 +#define OID_802_11_PMKID                      0x0d010123
 +
 +#define NDIS_802_11_LENGTH_SSID 32
 +#define NDIS_802_11_LENGTH_RATES 8
 +#define NDIS_802_11_LENGTH_RATES_EX 16
 +
 +typedef UCHAR NDIS_802_11_MAC_ADDRESS[6];
 +
 +typedef struct NDIS_802_11_SSID {
 +      ULONG SsidLength;
 +      UCHAR Ssid[NDIS_802_11_LENGTH_SSID];
 +} NDIS_802_11_SSID;
 +
 +typedef LONG NDIS_802_11_RSSI;
 +
 +typedef enum NDIS_802_11_NETWORK_TYPE {
 +      Ndis802_11FH,
 +      Ndis802_11DS,
 +      Ndis802_11OFDM5,
 +      Ndis802_11OFDM24,
 +      Ndis802_11NetworkTypeMax
 +} NDIS_802_11_NETWORK_TYPE;
 +
 +typedef struct NDIS_802_11_CONFIGURATION_FH {
 +      ULONG Length;
 +      ULONG HopPattern;
 +      ULONG HopSet;
 +      ULONG DwellTime;
 +} NDIS_802_11_CONFIGURATION_FH;
 +
 +typedef struct NDIS_802_11_CONFIGURATION {
 +      ULONG Length;
 +      ULONG BeaconPeriod;
 +      ULONG ATIMWindow;
 +      ULONG DSConfig;
 +      NDIS_802_11_CONFIGURATION_FH FHConfig;
 +} NDIS_802_11_CONFIGURATION;
 +
 +typedef enum NDIS_802_11_NETWORK_INFRASTRUCTURE {
 +      Ndis802_11IBSS,
 +      Ndis802_11Infrastructure,
 +      Ndis802_11AutoUnknown,
 +      Ndis802_11InfrastructureMax
 +} NDIS_802_11_NETWORK_INFRASTRUCTURE;
 +
 +typedef enum NDIS_802_11_AUTHENTICATION_MODE {
 +      Ndis802_11AuthModeOpen,
 +      Ndis802_11AuthModeShared,
 +      Ndis802_11AuthModeAutoSwitch,
 +      Ndis802_11AuthModeWPA,
 +      Ndis802_11AuthModeWPAPSK,
 +      Ndis802_11AuthModeWPANone,
 +      Ndis802_11AuthModeWPA2,
 +      Ndis802_11AuthModeWPA2PSK,
 +      Ndis802_11AuthModeMax
 +} NDIS_802_11_AUTHENTICATION_MODE;
 +
 +typedef enum NDIS_802_11_WEP_STATUS {
 +      Ndis802_11WEPEnabled,
 +      Ndis802_11Encryption1Enabled = Ndis802_11WEPEnabled,
 +      Ndis802_11WEPDisabled,
 +      Ndis802_11EncryptionDisabled = Ndis802_11WEPDisabled,
 +      Ndis802_11WEPKeyAbsent,
 +      Ndis802_11Encryption1KeyAbsent = Ndis802_11WEPKeyAbsent,
 +      Ndis802_11WEPNotSupported,
 +      Ndis802_11EncryptionNotSupported = Ndis802_11WEPNotSupported,
 +      Ndis802_11Encryption2Enabled,
 +      Ndis802_11Encryption2KeyAbsent,
 +      Ndis802_11Encryption3Enabled,
 +      Ndis802_11Encryption3KeyAbsent
 +} NDIS_802_11_WEP_STATUS, NDIS_802_11_ENCRYPTION_STATUS;
 +
 +typedef enum NDIS_802_11_PRIVACY_FILTER {
 +      Ndis802_11PrivFilterAcceptAll,
 +      Ndis802_11PrivFilter8021xWEP
 +} NDIS_802_11_PRIVACY_FILTER;
 +
 +typedef UCHAR NDIS_802_11_RATES[NDIS_802_11_LENGTH_RATES];
 +typedef UCHAR NDIS_802_11_RATES_EX[NDIS_802_11_LENGTH_RATES_EX];
 +
 +typedef struct NDIS_WLAN_BSSID_EX {
 +      ULONG Length;
 +      NDIS_802_11_MAC_ADDRESS MacAddress; /* BSSID */
 +      UCHAR Reserved[2];
 +      NDIS_802_11_SSID Ssid;
 +      ULONG Privacy;
 +      NDIS_802_11_RSSI Rssi;
 +      NDIS_802_11_NETWORK_TYPE NetworkTypeInUse;
 +      NDIS_802_11_CONFIGURATION Configuration;
 +      NDIS_802_11_NETWORK_INFRASTRUCTURE InfrastructureMode;
 +      NDIS_802_11_RATES_EX SupportedRates;
 +      ULONG IELength;
 +      UCHAR IEs[1];
 +} NDIS_WLAN_BSSID_EX;
 +
 +typedef struct NDIS_802_11_BSSID_LIST_EX {
 +      ULONG NumberOfItems;
 +      NDIS_WLAN_BSSID_EX Bssid[1];
 +} NDIS_802_11_BSSID_LIST_EX;
 +
 +typedef struct NDIS_802_11_FIXED_IEs {
 +      UCHAR Timestamp[8];
 +      USHORT BeaconInterval;
 +      USHORT Capabilities;
 +} NDIS_802_11_FIXED_IEs;
 +
 +typedef struct NDIS_802_11_WEP {
 +      ULONG Length;
 +      ULONG KeyIndex;
 +      ULONG KeyLength;
 +      UCHAR KeyMaterial[1];
 +} NDIS_802_11_WEP;
 +
 +typedef ULONG NDIS_802_11_KEY_INDEX;
 +typedef ULONGLONG NDIS_802_11_KEY_RSC;
 +
 +typedef struct NDIS_802_11_KEY {
 +      ULONG Length;
 +      ULONG KeyIndex;
 +      ULONG KeyLength;
 +      NDIS_802_11_MAC_ADDRESS BSSID;
 +      NDIS_802_11_KEY_RSC KeyRSC;
 +      UCHAR KeyMaterial[1];
 +} NDIS_802_11_KEY;
 +
 +typedef struct NDIS_802_11_REMOVE_KEY {
 +      ULONG Length;
 +      ULONG KeyIndex;
 +      NDIS_802_11_MAC_ADDRESS BSSID;
 +} NDIS_802_11_REMOVE_KEY;
 +
 +typedef struct NDIS_802_11_AI_REQFI {
 +      USHORT Capabilities;
 +      USHORT ListenInterval;
 +      NDIS_802_11_MAC_ADDRESS CurrentAPAddress;
 +} NDIS_802_11_AI_REQFI;
 +
 +typedef struct NDIS_802_11_AI_RESFI {
 +      USHORT Capabilities;
 +      USHORT StatusCode;
 +      USHORT AssociationId;
 +} NDIS_802_11_AI_RESFI;
 +
 +typedef struct NDIS_802_11_ASSOCIATION_INFORMATION {
 +      ULONG Length;
 +      USHORT AvailableRequestFixedIEs;
 +      NDIS_802_11_AI_REQFI RequestFixedIEs;
 +      ULONG RequestIELength;
 +      ULONG OffsetRequestIEs;
 +      USHORT AvailableResponseFixedIEs;
 +      NDIS_802_11_AI_RESFI ResponseFixedIEs;
 +      ULONG ResponseIELength;
 +      ULONG OffsetResponseIEs;
 +} NDIS_802_11_ASSOCIATION_INFORMATION;
 +
 +typedef struct NDIS_802_11_AUTHENTICATION_ENCRYPTION {
 +      NDIS_802_11_AUTHENTICATION_MODE AuthModeSupported;
 +      NDIS_802_11_ENCRYPTION_STATUS EncryptStatusSupported;
 +} NDIS_802_11_AUTHENTICATION_ENCRYPTION;
 +
 +typedef struct NDIS_802_11_CAPABILITY {
 +      ULONG Length;
 +      ULONG Version;
 +      ULONG NoOfPMKIDs;
 +      ULONG NoOfAuthEncryptPairsSupported;
 +      NDIS_802_11_AUTHENTICATION_ENCRYPTION
 +              AuthenticationEncryptionSupported[1];
 +} NDIS_802_11_CAPABILITY;
 +
 +typedef UCHAR NDIS_802_11_PMKID_VALUE[16];
 +
 +typedef struct BSSID_INFO {
 +      NDIS_802_11_MAC_ADDRESS BSSID;
 +      NDIS_802_11_PMKID_VALUE PMKID;
 +} BSSID_INFO;
 +
 +typedef struct NDIS_802_11_PMKID {
 +      ULONG Length;
 +      ULONG BSSIDInfoCount;
 +      BSSID_INFO BSSIDInfo[1];
 +} NDIS_802_11_PMKID;
 +
 +typedef enum NDIS_802_11_STATUS_TYPE {
 +      Ndis802_11StatusType_Authentication,
 +      Ndis802_11StatusType_PMKID_CandidateList = 2,
 +      Ndis802_11StatusTypeMax
 +} NDIS_802_11_STATUS_TYPE;
 +
 +typedef struct NDIS_802_11_STATUS_INDICATION {
 +      NDIS_802_11_STATUS_TYPE StatusType;
 +} NDIS_802_11_STATUS_INDICATION;
 +
 +typedef struct PMKID_CANDIDATE {
 +      NDIS_802_11_MAC_ADDRESS BSSID;
 +      ULONG Flags;
 +} PMKID_CANDIDATE;
 +
 +#define NDIS_802_11_PMKID_CANDIDATE_PREAUTH_ENABLED 0x01
 +
 +typedef struct NDIS_802_11_PMKID_CANDIDATE_LIST {
 +      ULONG Version;
 +      ULONG NumCandidates;
 +      PMKID_CANDIDATE CandidateList[1];
 +} NDIS_802_11_PMKID_CANDIDATE_LIST;
 +
 +typedef struct NDIS_802_11_AUTHENTICATION_REQUEST {
 +      ULONG Length;
 +      NDIS_802_11_MAC_ADDRESS Bssid;
 +      ULONG Flags;
 +} NDIS_802_11_AUTHENTICATION_REQUEST;
 +
 +#define NDIS_802_11_AUTH_REQUEST_REAUTH                       0x01
 +#define NDIS_802_11_AUTH_REQUEST_KEYUPDATE            0x02
 +#define NDIS_802_11_AUTH_REQUEST_PAIRWISE_ERROR               0x06
 +#define NDIS_802_11_AUTH_REQUEST_GROUP_ERROR          0x0E
 +
 +#endif /* OID_802_11_BSSID */
 +
 +
 +#ifndef OID_802_11_PMKID
 +/* Platform SDK for XP did not include WPA2, so add needed definitions */
 +
 +#define OID_802_11_CAPABILITY                         0x0d010122
 +#define OID_802_11_PMKID                      0x0d010123
 +
 +#define Ndis802_11AuthModeWPA2 6
 +#define Ndis802_11AuthModeWPA2PSK 7
 +
 +#define Ndis802_11StatusType_PMKID_CandidateList 2
 +
 +typedef struct NDIS_802_11_AUTHENTICATION_ENCRYPTION {
 +      NDIS_802_11_AUTHENTICATION_MODE AuthModeSupported;
 +      NDIS_802_11_ENCRYPTION_STATUS EncryptStatusSupported;
 +} NDIS_802_11_AUTHENTICATION_ENCRYPTION;
 +
 +typedef struct NDIS_802_11_CAPABILITY {
 +      ULONG Length;
 +      ULONG Version;
 +      ULONG NoOfPMKIDs;
 +      ULONG NoOfAuthEncryptPairsSupported;
 +      NDIS_802_11_AUTHENTICATION_ENCRYPTION
 +              AuthenticationEncryptionSupported[1];
 +} NDIS_802_11_CAPABILITY;
 +
 +typedef UCHAR NDIS_802_11_PMKID_VALUE[16];
 +
 +typedef struct BSSID_INFO {
 +      NDIS_802_11_MAC_ADDRESS BSSID;
 +      NDIS_802_11_PMKID_VALUE PMKID;
 +} BSSID_INFO;
 +
 +typedef struct NDIS_802_11_PMKID {
 +      ULONG Length;
 +      ULONG BSSIDInfoCount;
 +      BSSID_INFO BSSIDInfo[1];
 +} NDIS_802_11_PMKID;
 +
 +typedef struct PMKID_CANDIDATE {
 +      NDIS_802_11_MAC_ADDRESS BSSID;
 +      ULONG Flags;
 +} PMKID_CANDIDATE;
 +
 +#define NDIS_802_11_PMKID_CANDIDATE_PREAUTH_ENABLED 0x01
 +
 +typedef struct NDIS_802_11_PMKID_CANDIDATE_LIST {
 +      ULONG Version;
 +      ULONG NumCandidates;
 +      PMKID_CANDIDATE CandidateList[1];
 +} NDIS_802_11_PMKID_CANDIDATE_LIST;
 +
 +#endif /* OID_802_11_CAPABILITY */
 +
 +
 +#ifndef OID_DOT11_CURRENT_OPERATION_MODE
 +/* Native 802.11 OIDs */
 +#define OID_DOT11_NDIS_START 0x0D010300
 +#define OID_DOT11_CURRENT_OPERATION_MODE (OID_DOT11_NDIS_START + 8)
 +#define OID_DOT11_SCAN_REQUEST (OID_DOT11_NDIS_START + 11)
 +
 +typedef enum _DOT11_BSS_TYPE {
 +      dot11_BSS_type_infrastructure = 1,
 +      dot11_BSS_type_independent = 2,
 +      dot11_BSS_type_any = 3
 +} DOT11_BSS_TYPE, * PDOT11_BSS_TYPE;
 +
 +typedef UCHAR DOT11_MAC_ADDRESS[6];
 +typedef DOT11_MAC_ADDRESS * PDOT11_MAC_ADDRESS;
 +
 +typedef enum _DOT11_SCAN_TYPE {
 +      dot11_scan_type_active = 1,
 +      dot11_scan_type_passive = 2,
 +      dot11_scan_type_auto = 3,
 +      dot11_scan_type_forced = 0x80000000
 +} DOT11_SCAN_TYPE, * PDOT11_SCAN_TYPE;
 +
 +typedef struct _DOT11_SCAN_REQUEST_V2 {
 +      DOT11_BSS_TYPE dot11BSSType;
 +      DOT11_MAC_ADDRESS dot11BSSID;
 +      DOT11_SCAN_TYPE dot11ScanType;
 +      BOOLEAN bRestrictedScan;
 +      ULONG udot11SSIDsOffset;
 +      ULONG uNumOfdot11SSIDs;
 +      BOOLEAN bUseRequestIE;
 +      ULONG uRequestIDsOffset;
 +      ULONG uNumOfRequestIDs;
 +      ULONG uPhyTypeInfosOffset;
 +      ULONG uNumOfPhyTypeInfos;
 +      ULONG uIEsOffset;
 +      ULONG uIEsLength;
 +      UCHAR ucBuffer[1];
 +} DOT11_SCAN_REQUEST_V2, * PDOT11_SCAN_REQUEST_V2;
 +
 +#endif /* OID_DOT11_CURRENT_OPERATION_MODE */
 +
 +#ifdef CONFIG_USE_NDISUIO
 +#ifndef _WIN32_WCE
 +#ifdef __MINGW32_VERSION
 +typedef ULONG NDIS_OID;
 +#endif /* __MINGW32_VERSION */
 +/* from nuiouser.h */
 +#define FSCTL_NDISUIO_BASE      FILE_DEVICE_NETWORK
 +
 +#define _NDISUIO_CTL_CODE(_Function, _Method, _Access) \
 +      CTL_CODE(FSCTL_NDISUIO_BASE, _Function, _Method, _Access)
 +
 +#define IOCTL_NDISUIO_OPEN_DEVICE \
 +      _NDISUIO_CTL_CODE(0x200, METHOD_BUFFERED, \
 +                        FILE_READ_ACCESS | FILE_WRITE_ACCESS)
 +
 +#define IOCTL_NDISUIO_QUERY_OID_VALUE \
 +      _NDISUIO_CTL_CODE(0x201, METHOD_BUFFERED, \
 +                        FILE_READ_ACCESS | FILE_WRITE_ACCESS)
 +
 +#define IOCTL_NDISUIO_SET_OID_VALUE \
 +      _NDISUIO_CTL_CODE(0x205, METHOD_BUFFERED, \
 +                        FILE_READ_ACCESS | FILE_WRITE_ACCESS)
 +
 +#define IOCTL_NDISUIO_SET_ETHER_TYPE \
 +      _NDISUIO_CTL_CODE(0x202, METHOD_BUFFERED, \
 +                        FILE_READ_ACCESS | FILE_WRITE_ACCESS)
 +
 +#define IOCTL_NDISUIO_QUERY_BINDING \
 +      _NDISUIO_CTL_CODE(0x203, METHOD_BUFFERED, \
 +                        FILE_READ_ACCESS | FILE_WRITE_ACCESS)
 +
 +#define IOCTL_NDISUIO_BIND_WAIT \
 +      _NDISUIO_CTL_CODE(0x204, METHOD_BUFFERED, \
 +                        FILE_READ_ACCESS | FILE_WRITE_ACCESS)
 +
 +typedef struct _NDISUIO_QUERY_OID
 +{
 +    NDIS_OID Oid;
 +    UCHAR Data[sizeof(ULONG)];
 +} NDISUIO_QUERY_OID, *PNDISUIO_QUERY_OID;
 +
 +typedef struct _NDISUIO_SET_OID
 +{
 +    NDIS_OID Oid;
 +    UCHAR Data[sizeof(ULONG)];
 +} NDISUIO_SET_OID, *PNDISUIO_SET_OID;
 +
 +typedef struct _NDISUIO_QUERY_BINDING
 +{
 +      ULONG BindingIndex;
 +      ULONG DeviceNameOffset;
 +      ULONG DeviceNameLength;
 +      ULONG DeviceDescrOffset;
 +      ULONG DeviceDescrLength;
 +} NDISUIO_QUERY_BINDING, *PNDISUIO_QUERY_BINDING;
 +#endif /* _WIN32_WCE */
 +#endif /* CONFIG_USE_NDISUIO */
 +
 +
 +static int ndis_get_oid(struct wpa_driver_ndis_data *drv, unsigned int oid,
 +                      char *data, size_t len)
 +{
 +#ifdef CONFIG_USE_NDISUIO
 +      NDISUIO_QUERY_OID *o;
 +      size_t buflen = sizeof(*o) + len;
 +      DWORD written;
 +      int ret;
 +      size_t hdrlen;
 +
 +      o = os_zalloc(buflen);
 +      if (o == NULL)
 +              return -1;
 +      o->Oid = oid;
 +#ifdef _WIN32_WCE
 +      o->ptcDeviceName = drv->adapter_name;
 +#endif /* _WIN32_WCE */
 +      if (!DeviceIoControl(drv->ndisuio, IOCTL_NDISUIO_QUERY_OID_VALUE,
 +                           o, sizeof(NDISUIO_QUERY_OID), o, buflen, &written,
 +                           NULL)) {
 +              wpa_printf(MSG_DEBUG, "NDIS: IOCTL_NDISUIO_QUERY_OID_VALUE "
 +                         "failed (oid=%08x): %d", oid, (int) GetLastError());
 +              os_free(o);
 +              return -1;
 +      }
 +      hdrlen = sizeof(NDISUIO_QUERY_OID) - sizeof(o->Data);
 +      if (written < hdrlen) {
 +              wpa_printf(MSG_DEBUG, "NDIS: query oid=%08x written (%d); "
 +                         "too short", oid, (unsigned int) written);
 +              os_free(o);
 +              return -1;
 +      }
 +      written -= hdrlen;
 +      if (written > len) {
 +              wpa_printf(MSG_DEBUG, "NDIS: query oid=%08x written (%d) > "
 +                         "len (%d)",oid, (unsigned int) written, len);
 +              os_free(o);
 +              return -1;
 +      }
 +      os_memcpy(data, o->Data, written);
 +      ret = written;
 +      os_free(o);
 +      return ret;
 +#else /* CONFIG_USE_NDISUIO */
 +      char *buf;
 +      PACKET_OID_DATA *o;
 +      int ret;
 +
 +      buf = os_zalloc(sizeof(*o) + len);
 +      if (buf == NULL)
 +              return -1;
 +      o = (PACKET_OID_DATA *) buf;
 +      o->Oid = oid;
 +      o->Length = len;
 +
 +      if (!PacketRequest(drv->adapter, FALSE, o)) {
 +              wpa_printf(MSG_DEBUG, "%s: oid=0x%x len (%d) failed",
 +                         __func__, oid, len);
 +              os_free(buf);
 +              return -1;
 +      }
 +      if (o->Length > len) {
 +              wpa_printf(MSG_DEBUG, "%s: oid=0x%x Length (%d) > len (%d)",
 +                         __func__, oid, (unsigned int) o->Length, len);
 +              os_free(buf);
 +              return -1;
 +      }
 +      os_memcpy(data, o->Data, o->Length);
 +      ret = o->Length;
 +      os_free(buf);
 +      return ret;
 +#endif /* CONFIG_USE_NDISUIO */
 +}
 +
 +
 +static int ndis_set_oid(struct wpa_driver_ndis_data *drv, unsigned int oid,
 +                      const char *data, size_t len)
 +{
 +#ifdef CONFIG_USE_NDISUIO
 +      NDISUIO_SET_OID *o;
 +      size_t buflen, reallen;
 +      DWORD written;
 +      char txt[50];
 +
 +      os_snprintf(txt, sizeof(txt), "NDIS: Set OID %08x", oid);
 +      wpa_hexdump_key(MSG_MSGDUMP, txt, (const u8 *) data, len);
 +
 +      buflen = sizeof(*o) + len;
 +      reallen = buflen - sizeof(o->Data);
 +      o = os_zalloc(buflen);
 +      if (o == NULL)
 +              return -1;
 +      o->Oid = oid;
 +#ifdef _WIN32_WCE
 +      o->ptcDeviceName = drv->adapter_name;
 +#endif /* _WIN32_WCE */
 +      if (data)
 +              os_memcpy(o->Data, data, len);
 +      if (!DeviceIoControl(drv->ndisuio, IOCTL_NDISUIO_SET_OID_VALUE,
 +                           o, reallen, NULL, 0, &written, NULL)) {
 +              wpa_printf(MSG_DEBUG, "NDIS: IOCTL_NDISUIO_SET_OID_VALUE "
 +                         "(oid=%08x) failed: %d", oid, (int) GetLastError());
 +              os_free(o);
 +              return -1;
 +      }
 +      os_free(o);
 +      return 0;
 +#else /* CONFIG_USE_NDISUIO */
 +      char *buf;
 +      PACKET_OID_DATA *o;
 +      char txt[50];
 +
 +      os_snprintf(txt, sizeof(txt), "NDIS: Set OID %08x", oid);
 +      wpa_hexdump_key(MSG_MSGDUMP, txt, (const u8 *) data, len);
 +
 +      buf = os_zalloc(sizeof(*o) + len);
 +      if (buf == NULL)
 +              return -1;
 +      o = (PACKET_OID_DATA *) buf;
 +      o->Oid = oid;
 +      o->Length = len;
 +      if (data)
 +              os_memcpy(o->Data, data, len);
 +
 +      if (!PacketRequest(drv->adapter, TRUE, o)) {
 +              wpa_printf(MSG_DEBUG, "%s: oid=0x%x len (%d) failed",
 +                         __func__, oid, len);
 +              os_free(buf);
 +              return -1;
 +      }
 +      os_free(buf);
 +      return 0;
 +#endif /* CONFIG_USE_NDISUIO */
 +}
 +
 +
 +static int ndis_set_auth_mode(struct wpa_driver_ndis_data *drv, int mode)
 +{
 +      u32 auth_mode = mode;
 +      if (ndis_set_oid(drv, OID_802_11_AUTHENTICATION_MODE,
 +                       (char *) &auth_mode, sizeof(auth_mode)) < 0) {
 +              wpa_printf(MSG_DEBUG, "NDIS: Failed to set "
 +                         "OID_802_11_AUTHENTICATION_MODE (%d)",
 +                         (int) auth_mode);
 +              return -1;
 +      }
 +      return 0;
 +}
 +
 +
 +static int ndis_get_auth_mode(struct wpa_driver_ndis_data *drv)
 +{
 +      u32 auth_mode;
 +      int res;
 +      res = ndis_get_oid(drv, OID_802_11_AUTHENTICATION_MODE,
 +                         (char *) &auth_mode, sizeof(auth_mode));
 +      if (res != sizeof(auth_mode)) {
 +              wpa_printf(MSG_DEBUG, "NDIS: Failed to get "
 +                         "OID_802_11_AUTHENTICATION_MODE");
 +              return -1;
 +      }
 +      return auth_mode;
 +}
 +
 +
 +static int ndis_set_encr_status(struct wpa_driver_ndis_data *drv, int encr)
 +{
 +      u32 encr_status = encr;
 +      if (ndis_set_oid(drv, OID_802_11_ENCRYPTION_STATUS,
 +                       (char *) &encr_status, sizeof(encr_status)) < 0) {
 +              wpa_printf(MSG_DEBUG, "NDIS: Failed to set "
 +                         "OID_802_11_ENCRYPTION_STATUS (%d)", encr);
 +              return -1;
 +      }
 +      return 0;
 +}
 +
 +
 +static int ndis_get_encr_status(struct wpa_driver_ndis_data *drv)
 +{
 +      u32 encr;
 +      int res;
 +      res = ndis_get_oid(drv, OID_802_11_ENCRYPTION_STATUS,
 +                         (char *) &encr, sizeof(encr));
 +      if (res != sizeof(encr)) {
 +              wpa_printf(MSG_DEBUG, "NDIS: Failed to get "
 +                         "OID_802_11_ENCRYPTION_STATUS");
 +              return -1;
 +      }
 +      return encr;
 +}
 +
 +
 +static int wpa_driver_ndis_get_bssid(void *priv, u8 *bssid)
 +{
 +      struct wpa_driver_ndis_data *drv = priv;
 +
 +      if (drv->wired) {
 +              /*
 +               * Report PAE group address as the "BSSID" for wired
 +               * connection.
 +               */
 +              os_memcpy(bssid, pae_group_addr, ETH_ALEN);
 +              return 0;
 +      }
 +
 +      return ndis_get_oid(drv, OID_802_11_BSSID, (char *) bssid, ETH_ALEN) <
 +              0 ? -1 : 0;
 +}
 +
 +
 +static int wpa_driver_ndis_get_ssid(void *priv, u8 *ssid)
 +{
 +      struct wpa_driver_ndis_data *drv = priv;
 +      NDIS_802_11_SSID buf;
 +      int res;
 +
 +      res = ndis_get_oid(drv, OID_802_11_SSID, (char *) &buf, sizeof(buf));
 +      if (res < 4) {
 +              wpa_printf(MSG_DEBUG, "NDIS: Failed to get SSID");
 +              if (drv->wired) {
 +                      wpa_printf(MSG_DEBUG, "NDIS: Allow get_ssid failure "
 +                                 "with a wired interface");
 +                      return 0;
 +              }
 +              return -1;
 +      }
 +      os_memcpy(ssid, buf.Ssid, buf.SsidLength);
 +      return buf.SsidLength;
 +}
 +
 +
 +static int wpa_driver_ndis_set_ssid(struct wpa_driver_ndis_data *drv,
 +                                  const u8 *ssid, size_t ssid_len)
 +{
 +      NDIS_802_11_SSID buf;
 +
 +      os_memset(&buf, 0, sizeof(buf));
 +      buf.SsidLength = ssid_len;
 +      os_memcpy(buf.Ssid, ssid, ssid_len);
 +      /*
 +       * Make sure radio is marked enabled here so that scan request will not
 +       * force SSID to be changed to a random one in order to enable radio at
 +       * that point.
 +       */
 +      drv->radio_enabled = 1;
 +      return ndis_set_oid(drv, OID_802_11_SSID, (char *) &buf, sizeof(buf));
 +}
 +
 +
 +/* Disconnect using OID_802_11_DISASSOCIATE. This will also turn the radio off.
 + */
 +static int wpa_driver_ndis_radio_off(struct wpa_driver_ndis_data *drv)
 +{
 +      drv->radio_enabled = 0;
 +      return ndis_set_oid(drv, OID_802_11_DISASSOCIATE, "    ", 4);
 +}
 +
 +
 +/* Disconnect by setting SSID to random (i.e., likely not used). */
 +static int wpa_driver_ndis_disconnect(struct wpa_driver_ndis_data *drv)
 +{
-       for (i = 0; i < 32; i++)
++      char ssid[SSID_MAX_LEN];
 +      int i;
-       return wpa_driver_ndis_set_ssid(drv, (u8 *) ssid, 32);
++      for (i = 0; i < SSID_MAX_LEN; i++)
 +              ssid[i] = rand() & 0xff;
-       if (ssid->SsidLength == 0 || ssid->SsidLength > 32)
++      return wpa_driver_ndis_set_ssid(drv, (u8 *) ssid, SSID_MAX_LEN);
 +}
 +
 +
 +static int wpa_driver_ndis_deauthenticate(void *priv, const u8 *addr,
 +                                        int reason_code)
 +{
 +      struct wpa_driver_ndis_data *drv = priv;
 +      return wpa_driver_ndis_disconnect(drv);
 +}
 +
 +
 +static void wpa_driver_ndis_scan_timeout(void *eloop_ctx, void *timeout_ctx)
 +{
 +      wpa_printf(MSG_DEBUG, "Scan timeout - try to get results");
 +      wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL);
 +}
 +
 +
 +static int wpa_driver_ndis_scan_native80211(
 +      struct wpa_driver_ndis_data *drv,
 +      struct wpa_driver_scan_params *params)
 +{
 +      DOT11_SCAN_REQUEST_V2 req;
 +      int res;
 +
 +      os_memset(&req, 0, sizeof(req));
 +      req.dot11BSSType = dot11_BSS_type_any;
 +      os_memset(req.dot11BSSID, 0xff, ETH_ALEN);
 +      req.dot11ScanType = dot11_scan_type_auto;
 +      res = ndis_set_oid(drv, OID_DOT11_SCAN_REQUEST, (char *) &req,
 +                         sizeof(req));
 +      eloop_cancel_timeout(wpa_driver_ndis_scan_timeout, drv, drv->ctx);
 +      eloop_register_timeout(7, 0, wpa_driver_ndis_scan_timeout, drv,
 +                             drv->ctx);
 +      return res;
 +}
 +
 +
 +static int wpa_driver_ndis_scan(void *priv,
 +                              struct wpa_driver_scan_params *params)
 +{
 +      struct wpa_driver_ndis_data *drv = priv;
 +      int res;
 +
 +      if (drv->native80211)
 +              return wpa_driver_ndis_scan_native80211(drv, params);
 +
 +      if (!drv->radio_enabled) {
 +              wpa_printf(MSG_DEBUG, "NDIS: turning radio on before the first"
 +                         " scan");
 +              if (wpa_driver_ndis_disconnect(drv) < 0) {
 +                      wpa_printf(MSG_DEBUG, "NDIS: failed to enable radio");
 +              }
 +              drv->radio_enabled = 1;
 +      }
 +
 +      res = ndis_set_oid(drv, OID_802_11_BSSID_LIST_SCAN, "    ", 4);
 +      eloop_cancel_timeout(wpa_driver_ndis_scan_timeout, drv, drv->ctx);
 +      eloop_register_timeout(7, 0, wpa_driver_ndis_scan_timeout, drv,
 +                             drv->ctx);
 +      return res;
 +}
 +
 +
 +static const u8 * wpa_scan_get_ie(const struct wpa_scan_res *res, u8 ie)
 +{
 +      const u8 *end, *pos;
 +
 +      pos = (const u8 *) (res + 1);
 +      end = pos + res->ie_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 struct wpa_scan_res * wpa_driver_ndis_add_scan_ssid(
 +      struct wpa_scan_res *r, NDIS_802_11_SSID *ssid)
 +{
 +      struct wpa_scan_res *nr;
 +      u8 *pos;
 +
 +      if (wpa_scan_get_ie(r, WLAN_EID_SSID))
 +              return r; /* SSID IE already present */
 +
++      if (ssid->SsidLength == 0 || ssid->SsidLength > SSID_MAX_LEN)
 +              return r; /* No valid SSID inside scan data */
 +
 +      nr = os_realloc(r, sizeof(*r) + r->ie_len + 2 + ssid->SsidLength);
 +      if (nr == NULL)
 +              return r;
 +
 +      pos = ((u8 *) (nr + 1)) + nr->ie_len;
 +      *pos++ = WLAN_EID_SSID;
 +      *pos++ = ssid->SsidLength;
 +      os_memcpy(pos, ssid->Ssid, ssid->SsidLength);
 +      nr->ie_len += 2 + ssid->SsidLength;
 +
 +      return nr;
 +}
 +
 +
 +static struct wpa_scan_results * wpa_driver_ndis_get_scan_results(void *priv)
 +{
 +      struct wpa_driver_ndis_data *drv = priv;
 +      NDIS_802_11_BSSID_LIST_EX *b;
 +      size_t blen, count, i;
 +      int len;
 +      char *pos;
 +      struct wpa_scan_results *results;
 +      struct wpa_scan_res *r;
 +
 +      blen = 65535;
 +      b = os_zalloc(blen);
 +      if (b == NULL)
 +              return NULL;
 +      len = ndis_get_oid(drv, OID_802_11_BSSID_LIST, (char *) b, blen);
 +      if (len < 0) {
 +              wpa_printf(MSG_DEBUG, "NDIS: failed to get scan results");
 +              os_free(b);
 +              return NULL;
 +      }
 +      count = b->NumberOfItems;
 +
 +      results = os_zalloc(sizeof(*results));
 +      if (results == NULL) {
 +              os_free(b);
 +              return NULL;
 +      }
 +      results->res = os_calloc(count, sizeof(struct wpa_scan_res *));
 +      if (results->res == NULL) {
 +              os_free(results);
 +              os_free(b);
 +              return NULL;
 +      }
 +
 +      pos = (char *) &b->Bssid[0];
 +      for (i = 0; i < count; i++) {
 +              NDIS_WLAN_BSSID_EX *bss = (NDIS_WLAN_BSSID_EX *) pos;
 +              NDIS_802_11_FIXED_IEs *fixed;
 +
 +              if (bss->IELength < sizeof(NDIS_802_11_FIXED_IEs)) {
 +                      wpa_printf(MSG_DEBUG, "NDIS: too small IELength=%d",
 +                                 (int) bss->IELength);
 +                      break;
 +              }
 +              if (((char *) bss->IEs) + bss->IELength  > (char *) b + blen) {
 +                      /*
 +                       * Some NDIS drivers have been reported to include an
 +                       * entry with an invalid IELength in scan results and
 +                       * this has crashed wpa_supplicant, so validate the
 +                       * returned value before using it.
 +                       */
 +                      wpa_printf(MSG_DEBUG, "NDIS: skipped invalid scan "
 +                                 "result IE (BSSID=" MACSTR ") IELength=%d",
 +                                 MAC2STR(bss->MacAddress),
 +                                 (int) bss->IELength);
 +                      break;
 +              }
 +
 +              r = os_zalloc(sizeof(*r) + bss->IELength -
 +                            sizeof(NDIS_802_11_FIXED_IEs));
 +              if (r == NULL)
 +                      break;
 +
 +              os_memcpy(r->bssid, bss->MacAddress, ETH_ALEN);
 +              r->level = (int) bss->Rssi;
 +              r->freq = bss->Configuration.DSConfig / 1000;
 +              fixed = (NDIS_802_11_FIXED_IEs *) bss->IEs;
 +              r->beacon_int = WPA_GET_LE16((u8 *) &fixed->BeaconInterval);
 +              r->caps = WPA_GET_LE16((u8 *) &fixed->Capabilities);
 +              r->tsf = WPA_GET_LE64(fixed->Timestamp);
 +              os_memcpy(r + 1, bss->IEs + sizeof(NDIS_802_11_FIXED_IEs),
 +                        bss->IELength - sizeof(NDIS_802_11_FIXED_IEs));
 +              r->ie_len = bss->IELength - sizeof(NDIS_802_11_FIXED_IEs);
 +              r = wpa_driver_ndis_add_scan_ssid(r, &bss->Ssid);
 +
 +              results->res[results->num++] = r;
 +
 +              pos += bss->Length;
 +              if (pos > (char *) b + blen)
 +                      break;
 +      }
 +
 +      os_free(b);
 +
 +      return results;
 +}
 +
 +
 +static int wpa_driver_ndis_remove_key(struct wpa_driver_ndis_data *drv,
 +                                    int key_idx, const u8 *addr,
 +                                    const u8 *bssid, int pairwise)
 +{
 +      NDIS_802_11_REMOVE_KEY rkey;
 +      NDIS_802_11_KEY_INDEX index;
 +      int res, res2;
 +
 +      os_memset(&rkey, 0, sizeof(rkey));
 +
 +      rkey.Length = sizeof(rkey);
 +      rkey.KeyIndex = key_idx;
 +      if (pairwise)
 +              rkey.KeyIndex |= 1 << 30;
 +      os_memcpy(rkey.BSSID, bssid, ETH_ALEN);
 +
 +      res = ndis_set_oid(drv, OID_802_11_REMOVE_KEY, (char *) &rkey,
 +                         sizeof(rkey));
 +      if (!pairwise) {
 +              index = key_idx;
 +              res2 = ndis_set_oid(drv, OID_802_11_REMOVE_WEP,
 +                                  (char *) &index, sizeof(index));
 +      } else
 +              res2 = 0;
 +
 +      if (res < 0 && res2 < 0)
 +              return -1;
 +      return 0;
 +}
 +
 +
 +static int wpa_driver_ndis_add_wep(struct wpa_driver_ndis_data *drv,
 +                                 int pairwise, int key_idx, int set_tx,
 +                                 const u8 *key, size_t key_len)
 +{
 +      NDIS_802_11_WEP *wep;
 +      size_t len;
 +      int res;
 +
 +      len = 12 + key_len;
 +      wep = os_zalloc(len);
 +      if (wep == NULL)
 +              return -1;
 +      wep->Length = len;
 +      wep->KeyIndex = key_idx;
 +      if (set_tx)
 +              wep->KeyIndex |= 1 << 31;
 +#if 0 /* Setting bit30 does not seem to work with some NDIS drivers */
 +      if (pairwise)
 +              wep->KeyIndex |= 1 << 30;
 +#endif
 +      wep->KeyLength = key_len;
 +      os_memcpy(wep->KeyMaterial, key, key_len);
 +
 +      wpa_hexdump_key(MSG_MSGDUMP, "NDIS: OID_802_11_ADD_WEP",
 +                      (u8 *) wep, len);
 +      res = ndis_set_oid(drv, OID_802_11_ADD_WEP, (char *) wep, len);
 +
 +      os_free(wep);
 +
 +      return res;
 +}
 +
 +
 +static int wpa_driver_ndis_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 wpa_driver_ndis_data *drv = priv;
 +      size_t len, i;
 +      NDIS_802_11_KEY *nkey;
 +      int res, pairwise;
 +      u8 bssid[ETH_ALEN];
 +
 +      if (addr == NULL || is_broadcast_ether_addr(addr)) {
 +              /* Group Key */
 +              pairwise = 0;
 +              if (wpa_driver_ndis_get_bssid(drv, bssid) < 0)
 +                      os_memset(bssid, 0xff, ETH_ALEN);
 +      } else {
 +              /* Pairwise Key */
 +              pairwise = 1;
 +              os_memcpy(bssid, addr, ETH_ALEN);
 +      }
 +
 +      if (alg == WPA_ALG_NONE || key_len == 0) {
 +              return wpa_driver_ndis_remove_key(drv, key_idx, addr, bssid,
 +                                                pairwise);
 +      }
 +
 +      if (alg == WPA_ALG_WEP) {
 +              return wpa_driver_ndis_add_wep(drv, pairwise, key_idx, set_tx,
 +                                             key, key_len);
 +      }
 +
 +      len = 12 + 6 + 6 + 8 + key_len;
 +
 +      nkey = os_zalloc(len);
 +      if (nkey == NULL)
 +              return -1;
 +
 +      nkey->Length = len;
 +      nkey->KeyIndex = key_idx;
 +      if (set_tx)
 +              nkey->KeyIndex |= 1 << 31;
 +      if (pairwise)
 +              nkey->KeyIndex |= 1 << 30;
 +      if (seq && seq_len)
 +              nkey->KeyIndex |= 1 << 29;
 +      nkey->KeyLength = key_len;
 +      os_memcpy(nkey->BSSID, bssid, ETH_ALEN);
 +      if (seq && seq_len) {
 +              for (i = 0; i < seq_len; i++)
 +                      nkey->KeyRSC |= (ULONGLONG) seq[i] << (i * 8);
 +      }
 +      if (alg == WPA_ALG_TKIP && key_len == 32) {
 +              os_memcpy(nkey->KeyMaterial, key, 16);
 +              os_memcpy(nkey->KeyMaterial + 16, key + 24, 8);
 +              os_memcpy(nkey->KeyMaterial + 24, key + 16, 8);
 +      } else {
 +              os_memcpy(nkey->KeyMaterial, key, key_len);
 +      }
 +
 +      wpa_hexdump_key(MSG_MSGDUMP, "NDIS: OID_802_11_ADD_KEY",
 +                      (u8 *) nkey, len);
 +      res = ndis_set_oid(drv, OID_802_11_ADD_KEY, (char *) nkey, len);
 +      os_free(nkey);
 +
 +      return res;
 +}
 +
 +
 +static int
 +wpa_driver_ndis_associate(void *priv,
 +                        struct wpa_driver_associate_params *params)
 +{
 +      struct wpa_driver_ndis_data *drv = priv;
 +      u32 auth_mode, encr, priv_mode, mode;
 +      u8 bcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
 +
 +      drv->mode = params->mode;
 +
 +      /* Note: Setting OID_802_11_INFRASTRUCTURE_MODE clears current keys,
 +       * so static WEP keys needs to be set again after this. */
 +      if (params->mode == IEEE80211_MODE_IBSS) {
 +              mode = Ndis802_11IBSS;
 +              /* Need to make sure that BSSID polling is enabled for
 +               * IBSS mode. */
 +              eloop_cancel_timeout(wpa_driver_ndis_poll_timeout, drv, NULL);
 +              eloop_register_timeout(1, 0, wpa_driver_ndis_poll_timeout,
 +                                     drv, NULL);
 +      } else
 +              mode = Ndis802_11Infrastructure;
 +      if (ndis_set_oid(drv, OID_802_11_INFRASTRUCTURE_MODE,
 +                       (char *) &mode, sizeof(mode)) < 0) {
 +              wpa_printf(MSG_DEBUG, "NDIS: Failed to set "
 +                         "OID_802_11_INFRASTRUCTURE_MODE (%d)",
 +                         (int) mode);
 +              /* Try to continue anyway */
 +      }
 +
 +      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++) {
 +                      if (!params->wep_key[i])
 +                              continue;
 +                      wpa_printf(MSG_DEBUG, "NDIS: Re-setting static WEP "
 +                                 "key %d", i);
 +                      wpa_driver_ndis_set_key(drv->ifname, drv, WPA_ALG_WEP,
 +                                              bcast, i,
 +                                              i == params->wep_tx_keyidx,
 +                                              NULL, 0, params->wep_key[i],
 +                                              params->wep_key_len[i]);
 +              }
 +      }
 +
 +      if (params->wpa_ie == NULL || params->wpa_ie_len == 0) {
 +              if (params->auth_alg & WPA_AUTH_ALG_SHARED) {
 +                      if (params->auth_alg & WPA_AUTH_ALG_OPEN)
 +                              auth_mode = Ndis802_11AuthModeAutoSwitch;
 +                      else
 +                              auth_mode = Ndis802_11AuthModeShared;
 +              } else
 +                      auth_mode = Ndis802_11AuthModeOpen;
 +              priv_mode = Ndis802_11PrivFilterAcceptAll;
 +      } else if (params->wpa_ie[0] == WLAN_EID_RSN) {
 +              priv_mode = Ndis802_11PrivFilter8021xWEP;
 +              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 == WPA_KEY_MGMT_WPS) {
 +              auth_mode = Ndis802_11AuthModeOpen;
 +              priv_mode = Ndis802_11PrivFilterAcceptAll;
 +              if (params->wps == WPS_MODE_PRIVACY) {
 +                      u8 dummy_key[5] = { 0x11, 0x22, 0x33, 0x44, 0x55 };
 +                      /*
 +                       * Some NDIS drivers refuse to associate in open mode
 +                       * configuration due to Privacy field mismatch, so use
 +                       * a workaround to make the configuration look like
 +                       * matching one for WPS provisioning.
 +                       */
 +                      wpa_printf(MSG_DEBUG, "NDIS: Set dummy WEP key as a "
 +                                 "workaround to allow driver to associate "
 +                                 "for WPS");
 +                      wpa_driver_ndis_set_key(drv->ifname, drv, WPA_ALG_WEP,
 +                                              bcast, 0, 1,
 +                                              NULL, 0, dummy_key,
 +                                              sizeof(dummy_key));
 +              }
 +#endif /* CONFIG_WPS */
 +      } else {
 +              priv_mode = Ndis802_11PrivFilter8021xWEP;
 +              if (params->key_mgmt_suite == WPA_KEY_MGMT_WPA_NONE)
 +                      auth_mode = Ndis802_11AuthModeWPANone;
 +              else if (params->key_mgmt_suite == WPA_KEY_MGMT_PSK)
 +                      auth_mode = Ndis802_11AuthModeWPAPSK;
 +              else
 +                      auth_mode = Ndis802_11AuthModeWPA;
 +      }
 +
 +      switch (params->pairwise_suite) {
 +      case WPA_CIPHER_CCMP:
 +              encr = Ndis802_11Encryption3Enabled;
 +              break;
 +      case WPA_CIPHER_TKIP:
 +              encr = Ndis802_11Encryption2Enabled;
 +              break;
 +      case WPA_CIPHER_WEP40:
 +      case WPA_CIPHER_WEP104:
 +              encr = Ndis802_11Encryption1Enabled;
 +              break;
 +      case WPA_CIPHER_NONE:
 +#ifdef CONFIG_WPS
 +              if (params->wps == WPS_MODE_PRIVACY) {
 +                      encr = Ndis802_11Encryption1Enabled;
 +                      break;
 +              }
 +#endif /* CONFIG_WPS */
 +              if (params->group_suite == WPA_CIPHER_CCMP)
 +                      encr = Ndis802_11Encryption3Enabled;
 +              else if (params->group_suite == WPA_CIPHER_TKIP)
 +                      encr = Ndis802_11Encryption2Enabled;
 +              else
 +                      encr = Ndis802_11EncryptionDisabled;
 +              break;
 +      default:
 +#ifdef CONFIG_WPS
 +              if (params->wps == WPS_MODE_PRIVACY) {
 +                      encr = Ndis802_11Encryption1Enabled;
 +                      break;
 +              }
 +#endif /* CONFIG_WPS */
 +              encr = Ndis802_11EncryptionDisabled;
 +              break;
 +      };
 +
 +      if (ndis_set_oid(drv, OID_802_11_PRIVACY_FILTER,
 +                       (char *) &priv_mode, sizeof(priv_mode)) < 0) {
 +              wpa_printf(MSG_DEBUG, "NDIS: Failed to set "
 +                         "OID_802_11_PRIVACY_FILTER (%d)",
 +                         (int) priv_mode);
 +              /* Try to continue anyway */
 +      }
 +
 +      ndis_set_auth_mode(drv, auth_mode);
 +      ndis_set_encr_status(drv, encr);
 +
 +      if (params->bssid) {
 +              ndis_set_oid(drv, OID_802_11_BSSID, (char *) params->bssid,
 +                           ETH_ALEN);
 +              drv->oid_bssid_set = 1;
 +      } else if (drv->oid_bssid_set) {
 +              ndis_set_oid(drv, OID_802_11_BSSID, "\xff\xff\xff\xff\xff\xff",
 +                           ETH_ALEN);
 +              drv->oid_bssid_set = 0;
 +      }
 +
 +      return wpa_driver_ndis_set_ssid(drv, params->ssid, params->ssid_len);
 +}
 +
 +
 +static int wpa_driver_ndis_set_pmkid(struct wpa_driver_ndis_data *drv)
 +{
 +      int len, count, i, ret;
 +      struct ndis_pmkid_entry *entry;
 +      NDIS_802_11_PMKID *p;
 +
 +      count = 0;
 +      entry = drv->pmkid;
 +      while (entry) {
 +              count++;
 +              if (count >= drv->no_of_pmkid)
 +                      break;
 +              entry = entry->next;
 +      }
 +      len = 8 + count * sizeof(BSSID_INFO);
 +      p = os_zalloc(len);
 +      if (p == NULL)
 +              return -1;
 +
 +      p->Length = len;
 +      p->BSSIDInfoCount = count;
 +      entry = drv->pmkid;
 +      for (i = 0; i < count; i++) {
 +              os_memcpy(&p->BSSIDInfo[i].BSSID, entry->bssid, ETH_ALEN);
 +              os_memcpy(&p->BSSIDInfo[i].PMKID, entry->pmkid, 16);
 +              entry = entry->next;
 +      }
 +      wpa_hexdump(MSG_MSGDUMP, "NDIS: OID_802_11_PMKID", (u8 *) p, len);
 +      ret = ndis_set_oid(drv, OID_802_11_PMKID, (char *) p, len);
 +      os_free(p);
 +      return ret;
 +}
 +
 +
 +static int wpa_driver_ndis_add_pmkid(void *priv, const u8 *bssid,
 +                                   const u8 *pmkid)
 +{
 +      struct wpa_driver_ndis_data *drv = priv;
 +      struct ndis_pmkid_entry *entry, *prev;
 +
 +      if (drv->no_of_pmkid == 0)
 +              return 0;
 +
 +      prev = NULL;
 +      entry = drv->pmkid;
 +      while (entry) {
 +              if (os_memcmp(entry->bssid, bssid, ETH_ALEN) == 0)
 +                      break;
 +              prev = entry;
 +              entry = entry->next;
 +      }
 +
 +      if (entry) {
 +              /* Replace existing entry for this BSSID and move it into the
 +               * beginning of the list. */
 +              os_memcpy(entry->pmkid, pmkid, 16);
 +              if (prev) {
 +                      prev->next = entry->next;
 +                      entry->next = drv->pmkid;
 +                      drv->pmkid = entry;
 +              }
 +      } else {
 +              entry = os_malloc(sizeof(*entry));
 +              if (entry) {
 +                      os_memcpy(entry->bssid, bssid, ETH_ALEN);
 +                      os_memcpy(entry->pmkid, pmkid, 16);
 +                      entry->next = drv->pmkid;
 +                      drv->pmkid = entry;
 +              }
 +      }
 +
 +      return wpa_driver_ndis_set_pmkid(drv);
 +}
 +
 +
 +static int wpa_driver_ndis_remove_pmkid(void *priv, const u8 *bssid,
 +                                      const u8 *pmkid)
 +{
 +      struct wpa_driver_ndis_data *drv = priv;
 +      struct ndis_pmkid_entry *entry, *prev;
 +
 +      if (drv->no_of_pmkid == 0)
 +              return 0;
 +
 +      entry = drv->pmkid;
 +      prev = NULL;
 +      while (entry) {
 +              if (os_memcmp(entry->bssid, bssid, ETH_ALEN) == 0 &&
 +                  os_memcmp(entry->pmkid, pmkid, 16) == 0) {
 +                      if (prev)
 +                              prev->next = entry->next;
 +                      else
 +                              drv->pmkid = entry->next;
 +                      os_free(entry);
 +                      break;
 +              }
 +              prev = entry;
 +              entry = entry->next;
 +      }
 +      return wpa_driver_ndis_set_pmkid(drv);
 +}
 +
 +
 +static int wpa_driver_ndis_flush_pmkid(void *priv)
 +{
 +      struct wpa_driver_ndis_data *drv = priv;
 +      NDIS_802_11_PMKID p;
 +      struct ndis_pmkid_entry *pmkid, *prev;
 +      int prev_authmode, ret;
 +
 +      if (drv->no_of_pmkid == 0)
 +              return 0;
 +
 +      pmkid = drv->pmkid;
 +      drv->pmkid = NULL;
 +      while (pmkid) {
 +              prev = pmkid;
 +              pmkid = pmkid->next;
 +              os_free(prev);
 +      }
 +
 +      /*
 +       * Some drivers may refuse OID_802_11_PMKID if authMode is not set to
 +       * WPA2, so change authMode temporarily, if needed.
 +       */
 +      prev_authmode = ndis_get_auth_mode(drv);
 +      if (prev_authmode != Ndis802_11AuthModeWPA2)
 +              ndis_set_auth_mode(drv, Ndis802_11AuthModeWPA2);
 +
 +      os_memset(&p, 0, sizeof(p));
 +      p.Length = 8;
 +      p.BSSIDInfoCount = 0;
 +      wpa_hexdump(MSG_MSGDUMP, "NDIS: OID_802_11_PMKID (flush)",
 +                  (u8 *) &p, 8);
 +      ret = ndis_set_oid(drv, OID_802_11_PMKID, (char *) &p, 8);
 +
 +      if (prev_authmode != Ndis802_11AuthModeWPA2)
 +              ndis_set_auth_mode(drv, prev_authmode);
 +
 +      return ret;
 +}
 +
 +
 +static int wpa_driver_ndis_get_associnfo(struct wpa_driver_ndis_data *drv)
 +{
 +      char buf[512], *pos;
 +      NDIS_802_11_ASSOCIATION_INFORMATION *ai;
 +      int len;
 +      union wpa_event_data data;
 +      NDIS_802_11_BSSID_LIST_EX *b;
 +      size_t blen, i;
 +
 +      len = ndis_get_oid(drv, OID_802_11_ASSOCIATION_INFORMATION, buf,
 +                         sizeof(buf));
 +      if (len < 0) {
 +              wpa_printf(MSG_DEBUG, "NDIS: failed to get association "
 +                         "information");
 +              return -1;
 +      }
 +      if (len > sizeof(buf)) {
 +              /* Some drivers seem to be producing incorrect length for this
 +               * data. Limit the length to the current buffer size to avoid
 +               * crashing in hexdump. The data seems to be otherwise valid,
 +               * so better try to use it. */
 +              wpa_printf(MSG_DEBUG, "NDIS: ignored bogus association "
 +                         "information length %d", len);
 +              len = ndis_get_oid(drv, OID_802_11_ASSOCIATION_INFORMATION,
 +                                 buf, sizeof(buf));
 +              if (len < -1) {
 +                      wpa_printf(MSG_DEBUG, "NDIS: re-reading association "
 +                                 "information failed");
 +                      return -1;
 +              }
 +              if (len > sizeof(buf)) {
 +                      wpa_printf(MSG_DEBUG, "NDIS: ignored bogus association"
 +                                 " information length %d (re-read)", len);
 +                      len = sizeof(buf);
 +              }
 +      }
 +      wpa_hexdump(MSG_MSGDUMP, "NDIS: association information",
 +                  (u8 *) buf, len);
 +      if (len < sizeof(*ai)) {
 +              wpa_printf(MSG_DEBUG, "NDIS: too short association "
 +                         "information");
 +              return -1;
 +      }
 +      ai = (NDIS_802_11_ASSOCIATION_INFORMATION *) buf;
 +      wpa_printf(MSG_DEBUG, "NDIS: ReqFixed=0x%x RespFixed=0x%x off_req=%d "
 +                 "off_resp=%d len_req=%d len_resp=%d",
 +                 ai->AvailableRequestFixedIEs, ai->AvailableResponseFixedIEs,
 +                 (int) ai->OffsetRequestIEs, (int) ai->OffsetResponseIEs,
 +                 (int) ai->RequestIELength, (int) ai->ResponseIELength);
 +
 +      if (ai->OffsetRequestIEs + ai->RequestIELength > (unsigned) len ||
 +          ai->OffsetResponseIEs + ai->ResponseIELength > (unsigned) len) {
 +              wpa_printf(MSG_DEBUG, "NDIS: association information - "
 +                         "IE overflow");
 +              return -1;
 +      }
 +
 +      wpa_hexdump(MSG_MSGDUMP, "NDIS: Request IEs",
 +                  (u8 *) buf + ai->OffsetRequestIEs, ai->RequestIELength);
 +      wpa_hexdump(MSG_MSGDUMP, "NDIS: Response IEs",
 +                  (u8 *) buf + ai->OffsetResponseIEs, ai->ResponseIELength);
 +
 +      os_memset(&data, 0, sizeof(data));
 +      data.assoc_info.req_ies = (u8 *) buf + ai->OffsetRequestIEs;
 +      data.assoc_info.req_ies_len = ai->RequestIELength;
 +      data.assoc_info.resp_ies = (u8 *) buf + ai->OffsetResponseIEs;
 +      data.assoc_info.resp_ies_len = ai->ResponseIELength;
 +
 +      blen = 65535;
 +      b = os_zalloc(blen);
 +      if (b == NULL)
 +              goto skip_scan_results;
 +      len = ndis_get_oid(drv, OID_802_11_BSSID_LIST, (char *) b, blen);
 +      if (len < 0) {
 +              wpa_printf(MSG_DEBUG, "NDIS: failed to get scan results");
 +              os_free(b);
 +              b = NULL;
 +              goto skip_scan_results;
 +      }
 +      wpa_printf(MSG_DEBUG, "NDIS: %d BSSID items to process for AssocInfo",
 +                 (unsigned int) b->NumberOfItems);
 +
 +      pos = (char *) &b->Bssid[0];
 +      for (i = 0; i < b->NumberOfItems; i++) {
 +              NDIS_WLAN_BSSID_EX *bss = (NDIS_WLAN_BSSID_EX *) pos;
 +              if (os_memcmp(drv->bssid, bss->MacAddress, ETH_ALEN) == 0 &&
 +                  bss->IELength > sizeof(NDIS_802_11_FIXED_IEs)) {
 +                      data.assoc_info.beacon_ies =
 +                              ((u8 *) bss->IEs) +
 +                              sizeof(NDIS_802_11_FIXED_IEs);
 +                      data.assoc_info.beacon_ies_len =
 +                              bss->IELength - sizeof(NDIS_802_11_FIXED_IEs);
 +                      wpa_hexdump(MSG_MSGDUMP, "NDIS: Beacon IEs",
 +                                  data.assoc_info.beacon_ies,
 +                                  data.assoc_info.beacon_ies_len);
 +                      break;
 +              }
 +              pos += bss->Length;
 +              if (pos > (char *) b + blen)
 +                      break;
 +      }
 +
 +skip_scan_results:
 +      wpa_supplicant_event(drv->ctx, EVENT_ASSOCINFO, &data);
 +
 +      os_free(b);
 +
 +      return 0;
 +}
 +
 +
 +static void wpa_driver_ndis_poll_timeout(void *eloop_ctx, void *timeout_ctx)
 +{
 +      struct wpa_driver_ndis_data *drv = eloop_ctx;
 +      u8 bssid[ETH_ALEN];
 +      int poll;
 +
 +      if (drv->wired)
 +              return;
 +
 +      if (wpa_driver_ndis_get_bssid(drv, bssid)) {
 +              /* Disconnected */
 +              if (!is_zero_ether_addr(drv->bssid)) {
 +                      os_memset(drv->bssid, 0, ETH_ALEN);
 +                      wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, NULL);
 +              }
 +      } else {
 +              /* Connected */
 +              if (os_memcmp(drv->bssid, bssid, ETH_ALEN) != 0) {
 +                      os_memcpy(drv->bssid, bssid, ETH_ALEN);
 +                      wpa_driver_ndis_get_associnfo(drv);
 +                      wpa_supplicant_event(drv->ctx, EVENT_ASSOC, NULL);
 +              }
 +      }
 +
 +      /* When using integrated NDIS event receiver, we can skip BSSID
 +       * polling when using infrastructure network. However, when using
 +       * IBSS mode, many driver do not seem to generate connection event,
 +       * so we need to enable BSSID polling to figure out when IBSS network
 +       * has been formed.
 +       */
 +      poll = drv->mode == IEEE80211_MODE_IBSS;
 +#ifndef CONFIG_NDIS_EVENTS_INTEGRATED
 +#ifndef _WIN32_WCE
 +      poll = 1;
 +#endif /* _WIN32_WCE */
 +#endif /* CONFIG_NDIS_EVENTS_INTEGRATED */
 +
 +      if (poll) {
 +              eloop_register_timeout(1, 0, wpa_driver_ndis_poll_timeout,
 +                                      drv, NULL);
 +      }
 +}
 +
 +
 +static void wpa_driver_ndis_poll(void *priv)
 +{
 +      struct wpa_driver_ndis_data *drv = priv;
 +      eloop_cancel_timeout(wpa_driver_ndis_poll_timeout, drv, NULL);
 +      wpa_driver_ndis_poll_timeout(drv, NULL);
 +}
 +
 +
 +/* Called when driver generates Media Connect Event by calling
 + * NdisMIndicateStatus() with NDIS_STATUS_MEDIA_CONNECT */
 +void wpa_driver_ndis_event_connect(struct wpa_driver_ndis_data *drv)
 +{
 +      wpa_printf(MSG_DEBUG, "NDIS: Media Connect Event");
 +      if (wpa_driver_ndis_get_bssid(drv, drv->bssid) == 0) {
 +              wpa_driver_ndis_get_associnfo(drv);
 +              wpa_supplicant_event(drv->ctx, EVENT_ASSOC, NULL);
 +      }
 +}
 +
 +
 +/* Called when driver generates Media Disconnect Event by calling
 + * NdisMIndicateStatus() with NDIS_STATUS_MEDIA_DISCONNECT */
 +void wpa_driver_ndis_event_disconnect(struct wpa_driver_ndis_data *drv)
 +{
 +      wpa_printf(MSG_DEBUG, "NDIS: Media Disconnect Event");
 +      os_memset(drv->bssid, 0, ETH_ALEN);
 +      wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, NULL);
 +}
 +
 +
 +static void wpa_driver_ndis_event_auth(struct wpa_driver_ndis_data *drv,
 +                                     const u8 *data, size_t data_len)
 +{
 +      NDIS_802_11_AUTHENTICATION_REQUEST *req;
 +      int pairwise = 0, group = 0;
 +      union wpa_event_data event;
 +
 +      if (data_len < sizeof(*req)) {
 +              wpa_printf(MSG_DEBUG, "NDIS: Too short Authentication Request "
 +                         "Event (len=%d)", data_len);
 +              return;
 +      }
 +      req = (NDIS_802_11_AUTHENTICATION_REQUEST *) data;
 +
 +      wpa_printf(MSG_DEBUG, "NDIS: Authentication Request Event: "
 +                 "Bssid " MACSTR " Flags 0x%x",
 +                 MAC2STR(req->Bssid), (int) req->Flags);
 +
 +      if ((req->Flags & NDIS_802_11_AUTH_REQUEST_PAIRWISE_ERROR) ==
 +          NDIS_802_11_AUTH_REQUEST_PAIRWISE_ERROR)
 +              pairwise = 1;
 +      else if ((req->Flags & NDIS_802_11_AUTH_REQUEST_GROUP_ERROR) ==
 +          NDIS_802_11_AUTH_REQUEST_GROUP_ERROR)
 +              group = 1;
 +
 +      if (pairwise || group) {
 +              os_memset(&event, 0, sizeof(event));
 +              event.michael_mic_failure.unicast = pairwise;
 +              wpa_supplicant_event(drv->ctx, EVENT_MICHAEL_MIC_FAILURE,
 +                                   &event);
 +      }
 +}
 +
 +
 +static void wpa_driver_ndis_event_pmkid(struct wpa_driver_ndis_data *drv,
 +                                      const u8 *data, size_t data_len)
 +{
 +      NDIS_802_11_PMKID_CANDIDATE_LIST *pmkid;
 +      size_t i;
 +      union wpa_event_data event;
 +
 +      if (data_len < 8) {
 +              wpa_printf(MSG_DEBUG, "NDIS: Too short PMKID Candidate List "
 +                         "Event (len=%d)", data_len);
 +              return;
 +      }
 +      pmkid = (NDIS_802_11_PMKID_CANDIDATE_LIST *) data;
 +      wpa_printf(MSG_DEBUG, "NDIS: PMKID Candidate List Event - Version %d "
 +                 "NumCandidates %d",
 +                 (int) pmkid->Version, (int) pmkid->NumCandidates);
 +
 +      if (pmkid->Version != 1) {
 +              wpa_printf(MSG_DEBUG, "NDIS: Unsupported PMKID Candidate List "
 +                         "Version %d", (int) pmkid->Version);
 +              return;
 +      }
 +
 +      if (data_len < 8 + pmkid->NumCandidates * sizeof(PMKID_CANDIDATE)) {
 +              wpa_printf(MSG_DEBUG, "NDIS: PMKID Candidate List underflow");
 +              return;
 +      }
 +
 +      os_memset(&event, 0, sizeof(event));
 +      for (i = 0; i < pmkid->NumCandidates; i++) {
 +              PMKID_CANDIDATE *p = &pmkid->CandidateList[i];
 +              wpa_printf(MSG_DEBUG, "NDIS: %d: " MACSTR " Flags 0x%x",
 +                         i, MAC2STR(p->BSSID), (int) p->Flags);
 +              os_memcpy(event.pmkid_candidate.bssid, p->BSSID, ETH_ALEN);
 +              event.pmkid_candidate.index = i;
 +              event.pmkid_candidate.preauth =
 +                      p->Flags & NDIS_802_11_PMKID_CANDIDATE_PREAUTH_ENABLED;
 +              wpa_supplicant_event(drv->ctx, EVENT_PMKID_CANDIDATE,
 +                                   &event);
 +      }
 +}
 +
 +
 +/* Called when driver calls NdisMIndicateStatus() with
 + * NDIS_STATUS_MEDIA_SPECIFIC_INDICATION */
 +void wpa_driver_ndis_event_media_specific(struct wpa_driver_ndis_data *drv,
 +                                        const u8 *data, size_t data_len)
 +{
 +      NDIS_802_11_STATUS_INDICATION *status;
 +
 +      if (data == NULL || data_len < sizeof(*status))
 +              return;
 +
 +      wpa_hexdump(MSG_DEBUG, "NDIS: Media Specific Indication",
 +                  data, data_len);
 +
 +      status = (NDIS_802_11_STATUS_INDICATION *) data;
 +      data += sizeof(status);
 +      data_len -= sizeof(status);
 +
 +      switch (status->StatusType) {
 +      case Ndis802_11StatusType_Authentication:
 +              wpa_driver_ndis_event_auth(drv, data, data_len);
 +              break;
 +      case Ndis802_11StatusType_PMKID_CandidateList:
 +              wpa_driver_ndis_event_pmkid(drv, data, data_len);
 +              break;
 +      default:
 +              wpa_printf(MSG_DEBUG, "NDIS: Unknown StatusType %d",
 +                         (int) status->StatusType);
 +              break;
 +      }
 +}
 +
 +
 +/* Called when an adapter is added */
 +void wpa_driver_ndis_event_adapter_arrival(struct wpa_driver_ndis_data *drv)
 +{
 +      union wpa_event_data event;
 +      int i;
 +
 +      wpa_printf(MSG_DEBUG, "NDIS: Notify Adapter Arrival");
 +
 +      for (i = 0; i < 30; i++) {
 +              /* Re-open Packet32/NDISUIO connection */
 +              wpa_driver_ndis_adapter_close(drv);
 +              if (wpa_driver_ndis_adapter_init(drv) < 0 ||
 +                  wpa_driver_ndis_adapter_open(drv) < 0) {
 +                      wpa_printf(MSG_DEBUG, "NDIS: Driver re-initialization "
 +                                 "(%d) failed", i);
 +                      os_sleep(1, 0);
 +              } else {
 +                      wpa_printf(MSG_DEBUG, "NDIS: Driver re-initialized");
 +                      break;
 +              }
 +      }
 +
 +      os_memset(&event, 0, sizeof(event));
 +      os_strlcpy(event.interface_status.ifname, drv->ifname,
 +                 sizeof(event.interface_status.ifname));
 +      event.interface_status.ievent = EVENT_INTERFACE_ADDED;
 +      wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS, &event);
 +}
 +
 +
 +/* Called when an adapter is removed */
 +void wpa_driver_ndis_event_adapter_removal(struct wpa_driver_ndis_data *drv)
 +{
 +      union wpa_event_data event;
 +
 +      wpa_printf(MSG_DEBUG, "NDIS: Notify Adapter Removal");
 +      os_memset(&event, 0, sizeof(event));
 +      os_strlcpy(event.interface_status.ifname, drv->ifname,
 +                 sizeof(event.interface_status.ifname));
 +      event.interface_status.ievent = EVENT_INTERFACE_REMOVED;
 +      wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS, &event);
 +}
 +
 +
 +static void
 +wpa_driver_ndis_get_wpa_capability(struct wpa_driver_ndis_data *drv)
 +{
 +      wpa_printf(MSG_DEBUG, "NDIS: verifying driver WPA capability");
 +
 +      if (ndis_set_auth_mode(drv, Ndis802_11AuthModeWPA) == 0 &&
 +          ndis_get_auth_mode(drv) == Ndis802_11AuthModeWPA) {
 +              wpa_printf(MSG_DEBUG, "NDIS: WPA key management supported");
 +              drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA;
 +      }
 +
 +      if (ndis_set_auth_mode(drv, Ndis802_11AuthModeWPAPSK) == 0 &&
 +          ndis_get_auth_mode(drv) == Ndis802_11AuthModeWPAPSK) {
 +              wpa_printf(MSG_DEBUG, "NDIS: WPA-PSK key management "
 +                         "supported");
 +              drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK;
 +      }
 +
 +      if (ndis_set_encr_status(drv, Ndis802_11Encryption3Enabled) == 0 &&
 +          ndis_get_encr_status(drv) == Ndis802_11Encryption3KeyAbsent) {
 +              wpa_printf(MSG_DEBUG, "NDIS: CCMP encryption supported");
 +              drv->capa.enc |= WPA_DRIVER_CAPA_ENC_CCMP;
 +      }
 +
 +      if (ndis_set_encr_status(drv, Ndis802_11Encryption2Enabled) == 0 &&
 +          ndis_get_encr_status(drv) == Ndis802_11Encryption2KeyAbsent) {
 +              wpa_printf(MSG_DEBUG, "NDIS: TKIP encryption supported");
 +              drv->capa.enc |= WPA_DRIVER_CAPA_ENC_TKIP;
 +      }
 +
 +      if (ndis_set_encr_status(drv, Ndis802_11Encryption1Enabled) == 0 &&
 +          ndis_get_encr_status(drv) == Ndis802_11Encryption1KeyAbsent) {
 +              wpa_printf(MSG_DEBUG, "NDIS: WEP encryption supported");
 +              drv->capa.enc |= WPA_DRIVER_CAPA_ENC_WEP40 |
 +                      WPA_DRIVER_CAPA_ENC_WEP104;
 +      }
 +
 +      if (ndis_set_auth_mode(drv, Ndis802_11AuthModeShared) == 0 &&
 +          ndis_get_auth_mode(drv) == Ndis802_11AuthModeShared) {
 +              drv->capa.auth |= WPA_DRIVER_AUTH_SHARED;
 +      }
 +
 +      if (ndis_set_auth_mode(drv, Ndis802_11AuthModeOpen) == 0 &&
 +          ndis_get_auth_mode(drv) == Ndis802_11AuthModeOpen) {
 +              drv->capa.auth |= WPA_DRIVER_AUTH_OPEN;
 +      }
 +
 +      ndis_set_encr_status(drv, Ndis802_11EncryptionDisabled);
 +
 +      /* Could also verify OID_802_11_ADD_KEY error reporting and
 +       * support for OID_802_11_ASSOCIATION_INFORMATION. */
 +
 +      if (drv->capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA &&
 +          drv->capa.enc & (WPA_DRIVER_CAPA_ENC_TKIP |
 +                           WPA_DRIVER_CAPA_ENC_CCMP)) {
 +              wpa_printf(MSG_DEBUG, "NDIS: driver supports WPA");
 +              drv->has_capability = 1;
 +      } else {
 +              wpa_printf(MSG_DEBUG, "NDIS: no WPA support found");
 +      }
 +
 +      wpa_printf(MSG_DEBUG, "NDIS: driver capabilities: key_mgmt 0x%x "
 +                 "enc 0x%x auth 0x%x",
 +                 drv->capa.key_mgmt, drv->capa.enc, drv->capa.auth);
 +}
 +
 +
 +static void wpa_driver_ndis_get_capability(struct wpa_driver_ndis_data *drv)
 +{
 +      char buf[512];
 +      int len;
 +      size_t i;
 +      NDIS_802_11_CAPABILITY *c;
 +
 +      drv->capa.flags = WPA_DRIVER_FLAGS_DRIVER_IE;
 +
 +      len = ndis_get_oid(drv, OID_802_11_CAPABILITY, buf, sizeof(buf));
 +      if (len < 0) {
 +              wpa_driver_ndis_get_wpa_capability(drv);
 +              return;
 +      }
 +
 +      wpa_hexdump(MSG_MSGDUMP, "OID_802_11_CAPABILITY", (u8 *) buf, len);
 +      c = (NDIS_802_11_CAPABILITY *) buf;
 +      if (len < sizeof(*c) || c->Version != 2) {
 +              wpa_printf(MSG_DEBUG, "NDIS: unsupported "
 +                         "OID_802_11_CAPABILITY data");
 +              return;
 +      }
 +      wpa_printf(MSG_DEBUG, "NDIS: Driver supports OID_802_11_CAPABILITY - "
 +                 "NoOfPMKIDs %d NoOfAuthEncrPairs %d",
 +                 (int) c->NoOfPMKIDs,
 +                 (int) c->NoOfAuthEncryptPairsSupported);
 +      drv->has_capability = 1;
 +      drv->no_of_pmkid = c->NoOfPMKIDs;
 +      for (i = 0; i < c->NoOfAuthEncryptPairsSupported; i++) {
 +              NDIS_802_11_AUTHENTICATION_ENCRYPTION *ae;
 +              ae = &c->AuthenticationEncryptionSupported[i];
 +              if ((char *) (ae + 1) > buf + len) {
 +                      wpa_printf(MSG_DEBUG, "NDIS: auth/encr pair list "
 +                                 "overflow");
 +                      break;
 +              }
 +              wpa_printf(MSG_MSGDUMP, "NDIS: %d - auth %d encr %d",
 +                         i, (int) ae->AuthModeSupported,
 +                         (int) ae->EncryptStatusSupported);
 +              switch (ae->AuthModeSupported) {
 +              case Ndis802_11AuthModeOpen:
 +                      drv->capa.auth |= WPA_DRIVER_AUTH_OPEN;
 +                      break;
 +              case Ndis802_11AuthModeShared:
 +                      drv->capa.auth |= WPA_DRIVER_AUTH_SHARED;
 +                      break;
 +              case Ndis802_11AuthModeWPA:
 +                      drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA;
 +                      break;
 +              case Ndis802_11AuthModeWPAPSK:
 +                      drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK;
 +                      break;
 +              case Ndis802_11AuthModeWPA2:
 +                      drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA2;
 +                      break;
 +              case Ndis802_11AuthModeWPA2PSK:
 +                      drv->capa.key_mgmt |=
 +                              WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK;
 +                      break;
 +              case Ndis802_11AuthModeWPANone:
 +                      drv->capa.key_mgmt |=
 +                              WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE;
 +                      break;
 +              default:
 +                      break;
 +              }
 +              switch (ae->EncryptStatusSupported) {
 +              case Ndis802_11Encryption1Enabled:
 +                      drv->capa.enc |= WPA_DRIVER_CAPA_ENC_WEP40;
 +                      drv->capa.enc |= WPA_DRIVER_CAPA_ENC_WEP104;
 +                      break;
 +              case Ndis802_11Encryption2Enabled:
 +                      drv->capa.enc |= WPA_DRIVER_CAPA_ENC_TKIP;
 +                      break;
 +              case Ndis802_11Encryption3Enabled:
 +                      drv->capa.enc |= WPA_DRIVER_CAPA_ENC_CCMP;
 +                      break;
 +              default:
 +                      break;
 +              }
 +      }
 +
 +      wpa_printf(MSG_DEBUG, "NDIS: driver capabilities: key_mgmt 0x%x "
 +                 "enc 0x%x auth 0x%x",
 +                 drv->capa.key_mgmt, drv->capa.enc, drv->capa.auth);
 +}
 +
 +
 +static int wpa_driver_ndis_get_capa(void *priv, struct wpa_driver_capa *capa)
 +{
 +      struct wpa_driver_ndis_data *drv = priv;
 +      if (!drv->has_capability)
 +              return -1;
 +      os_memcpy(capa, &drv->capa, sizeof(*capa));
 +      return 0;
 +}
 +
 +
 +static const char * wpa_driver_ndis_get_ifname(void *priv)
 +{
 +      struct wpa_driver_ndis_data *drv = priv;
 +      return drv->ifname;
 +}
 +
 +
 +static const u8 * wpa_driver_ndis_get_mac_addr(void *priv)
 +{
 +      struct wpa_driver_ndis_data *drv = priv;
 +      return drv->own_addr;
 +}
 +
 +
 +#ifdef _WIN32_WCE
 +
 +#define NDISUIO_MSG_SIZE (sizeof(NDISUIO_DEVICE_NOTIFICATION) + 512)
 +
 +static void ndisuio_notification_receive(void *eloop_data, void *user_ctx)
 +{
 +      struct wpa_driver_ndis_data *drv = eloop_data;
 +      NDISUIO_DEVICE_NOTIFICATION *hdr;
 +      u8 buf[NDISUIO_MSG_SIZE];
 +      DWORD len, flags;
 +
 +      if (!ReadMsgQueue(drv->event_queue, buf, NDISUIO_MSG_SIZE, &len, 0,
 +                        &flags)) {
 +              wpa_printf(MSG_DEBUG, "ndisuio_notification_receive: "
 +                         "ReadMsgQueue failed: %d", (int) GetLastError());
 +              return;
 +      }
 +
 +      if (len < sizeof(NDISUIO_DEVICE_NOTIFICATION)) {
 +              wpa_printf(MSG_DEBUG, "ndisuio_notification_receive: "
 +                         "Too short message (len=%d)", (int) len);
 +              return;
 +      }
 +
 +      hdr = (NDISUIO_DEVICE_NOTIFICATION *) buf;
 +      wpa_printf(MSG_DEBUG, "NDIS: Notification received: len=%d type=0x%x",
 +                 (int) len, hdr->dwNotificationType);
 +
 +      switch (hdr->dwNotificationType) {
 +#ifdef NDISUIO_NOTIFICATION_ADAPTER_ARRIVAL
 +      case NDISUIO_NOTIFICATION_ADAPTER_ARRIVAL:
 +              wpa_printf(MSG_DEBUG, "NDIS: ADAPTER_ARRIVAL");
 +              wpa_driver_ndis_event_adapter_arrival(drv);
 +              break;
 +#endif
 +#ifdef NDISUIO_NOTIFICATION_ADAPTER_REMOVAL
 +      case NDISUIO_NOTIFICATION_ADAPTER_REMOVAL:
 +              wpa_printf(MSG_DEBUG, "NDIS: ADAPTER_REMOVAL");
 +              wpa_driver_ndis_event_adapter_removal(drv);
 +              break;
 +#endif
 +      case NDISUIO_NOTIFICATION_MEDIA_CONNECT:
 +              wpa_printf(MSG_DEBUG, "NDIS: MEDIA_CONNECT");
 +              SetEvent(drv->connected_event);
 +              wpa_driver_ndis_event_connect(drv);
 +              break;
 +      case NDISUIO_NOTIFICATION_MEDIA_DISCONNECT:
 +              ResetEvent(drv->connected_event);
 +              wpa_printf(MSG_DEBUG, "NDIS: MEDIA_DISCONNECT");
 +              wpa_driver_ndis_event_disconnect(drv);
 +              break;
 +      case NDISUIO_NOTIFICATION_MEDIA_SPECIFIC_NOTIFICATION:
 +              wpa_printf(MSG_DEBUG, "NDIS: MEDIA_SPECIFIC_NOTIFICATION");
 +#if _WIN32_WCE == 420 || _WIN32_WCE == 0x420
 +              wpa_driver_ndis_event_media_specific(
 +                      drv, hdr->pvStatusBuffer, hdr->uiStatusBufferSize);
 +#else
 +              wpa_driver_ndis_event_media_specific(
 +                      drv, ((const u8 *) hdr) + hdr->uiOffsetToStatusBuffer,
 +                      (size_t) hdr->uiStatusBufferSize);
 +#endif
 +              break;
 +      default:
 +              wpa_printf(MSG_DEBUG, "NDIS: Unknown notification type 0x%x",
 +                         hdr->dwNotificationType);
 +              break;
 +      }
 +}
 +
 +
 +static void ndisuio_notification_deinit(struct wpa_driver_ndis_data *drv)
 +{
 +      NDISUIO_REQUEST_NOTIFICATION req;
 +
 +      memset(&req, 0, sizeof(req));
 +      req.hMsgQueue = drv->event_queue;
 +      req.dwNotificationTypes = 0;
 +
 +      if (!DeviceIoControl(drv->ndisuio, IOCTL_NDISUIO_REQUEST_NOTIFICATION,
 +                           &req, sizeof(req), NULL, 0, NULL, NULL)) {
 +              wpa_printf(MSG_INFO, "ndisuio_notification_deinit: "
 +                         "IOCTL_NDISUIO_REQUEST_NOTIFICATION failed: %d",
 +                         (int) GetLastError());
 +      }
 +
 +      if (!DeviceIoControl(drv->ndisuio, IOCTL_NDISUIO_CANCEL_NOTIFICATION,
 +                           NULL, 0, NULL, 0, NULL, NULL)) {
 +              wpa_printf(MSG_INFO, "ndisuio_notification_deinit: "
 +                         "IOCTL_NDISUIO_CANCEL_NOTIFICATION failed: %d",
 +                         (int) GetLastError());
 +      }
 +
 +      if (drv->event_queue) {
 +              eloop_unregister_event(drv->event_queue,
 +                                     sizeof(drv->event_queue));
 +              CloseHandle(drv->event_queue);
 +              drv->event_queue = NULL;
 +      }
 +
 +      if (drv->connected_event) {
 +              CloseHandle(drv->connected_event);
 +              drv->connected_event = NULL;
 +      }
 +}
 +
 +
 +static int ndisuio_notification_init(struct wpa_driver_ndis_data *drv)
 +{
 +      MSGQUEUEOPTIONS opt;
 +      NDISUIO_REQUEST_NOTIFICATION req;
 +
 +      drv->connected_event =
 +              CreateEvent(NULL, TRUE, FALSE, TEXT("WpaSupplicantConnected"));
 +      if (drv->connected_event == NULL) {
 +              wpa_printf(MSG_INFO, "ndisuio_notification_init: "
 +                         "CreateEvent failed: %d",
 +                         (int) GetLastError());
 +              return -1;
 +      }
 +
 +      memset(&opt, 0, sizeof(opt));
 +      opt.dwSize = sizeof(opt);
 +      opt.dwMaxMessages = 5;
 +      opt.cbMaxMessage = NDISUIO_MSG_SIZE;
 +      opt.bReadAccess = TRUE;
 +
 +      drv->event_queue = CreateMsgQueue(NULL, &opt);
 +      if (drv->event_queue == NULL) {
 +              wpa_printf(MSG_INFO, "ndisuio_notification_init: "
 +                         "CreateMsgQueue failed: %d",
 +                         (int) GetLastError());
 +              ndisuio_notification_deinit(drv);
 +              return -1;
 +      }
 +
 +      memset(&req, 0, sizeof(req));
 +      req.hMsgQueue = drv->event_queue;
 +      req.dwNotificationTypes =
 +#ifdef NDISUIO_NOTIFICATION_ADAPTER_ARRIVAL
 +              NDISUIO_NOTIFICATION_ADAPTER_ARRIVAL |
 +#endif
 +#ifdef NDISUIO_NOTIFICATION_ADAPTER_REMOVAL
 +              NDISUIO_NOTIFICATION_ADAPTER_REMOVAL |
 +#endif
 +              NDISUIO_NOTIFICATION_MEDIA_CONNECT |
 +              NDISUIO_NOTIFICATION_MEDIA_DISCONNECT |
 +              NDISUIO_NOTIFICATION_MEDIA_SPECIFIC_NOTIFICATION;
 +
 +      if (!DeviceIoControl(drv->ndisuio, IOCTL_NDISUIO_REQUEST_NOTIFICATION,
 +                           &req, sizeof(req), NULL, 0, NULL, NULL)) {
 +              wpa_printf(MSG_INFO, "ndisuio_notification_init: "
 +                         "IOCTL_NDISUIO_REQUEST_NOTIFICATION failed: %d",
 +                         (int) GetLastError());
 +              ndisuio_notification_deinit(drv);
 +              return -1;
 +      }
 +
 +      eloop_register_event(drv->event_queue, sizeof(drv->event_queue),
 +                           ndisuio_notification_receive, drv, NULL);
 +
 +      return 0;
 +}
 +#endif /* _WIN32_WCE */
 +
 +
 +static int wpa_driver_ndis_get_names(struct wpa_driver_ndis_data *drv)
 +{
 +#ifdef CONFIG_USE_NDISUIO
 +      NDISUIO_QUERY_BINDING *b;
 +      size_t blen = sizeof(*b) + 1024;
 +      int i, error, found = 0;
 +      DWORD written;
 +      char name[256], desc[256], *dpos;
 +      WCHAR *pos;
 +      size_t j, len, dlen;
 +
 +      b = os_malloc(blen);
 +      if (b == NULL)
 +              return -1;
 +
 +      for (i = 0; ; i++) {
 +              os_memset(b, 0, blen);
 +              b->BindingIndex = i;
 +              if (!DeviceIoControl(drv->ndisuio, IOCTL_NDISUIO_QUERY_BINDING,
 +                                   b, sizeof(NDISUIO_QUERY_BINDING), b, blen,
 +                                   &written, NULL)) {
 +                      error = (int) GetLastError();
 +                      if (error == ERROR_NO_MORE_ITEMS)
 +                              break;
 +                      wpa_printf(MSG_DEBUG, "IOCTL_NDISUIO_QUERY_BINDING "
 +                                 "failed: %d", error);
 +                      break;
 +              }
 +
 +              pos = (WCHAR *) ((char *) b + b->DeviceNameOffset);
 +              len = b->DeviceNameLength;
 +              if (len >= sizeof(name))
 +                      len = sizeof(name) - 1;
 +              for (j = 0; j < len; j++)
 +                      name[j] = (char) pos[j];
 +              name[len] = '\0';
 +
 +              pos = (WCHAR *) ((char *) b + b->DeviceDescrOffset);
 +              len = b->DeviceDescrLength;
 +              if (len >= sizeof(desc))
 +                      len = sizeof(desc) - 1;
 +              for (j = 0; j < len; j++)
 +                      desc[j] = (char) pos[j];
 +              desc[len] = '\0';
 +
 +              wpa_printf(MSG_DEBUG, "NDIS: %d - %s - %s", i, name, desc);
 +
 +              if (os_strstr(name, drv->ifname)) {
 +                      wpa_printf(MSG_DEBUG, "NDIS: Interface name match");
 +                      found = 1;
 +                      break;
 +              }
 +
 +              if (os_strncmp(desc, drv->ifname, os_strlen(drv->ifname)) == 0)
 +              {
 +                      wpa_printf(MSG_DEBUG, "NDIS: Interface description "
 +                                 "match");
 +                      found = 1;
 +                      break;
 +              }
 +      }
 +
 +      if (!found) {
 +              wpa_printf(MSG_DEBUG, "NDIS: Could not find interface '%s'",
 +                         drv->ifname);
 +              os_free(b);
 +              return -1;
 +      }
 +
 +      os_strlcpy(drv->ifname,
 +                 os_strncmp(name, "\\DEVICE\\", 8) == 0 ? name + 8 : name,
 +                 sizeof(drv->ifname));
 +#ifdef _WIN32_WCE
 +      drv->adapter_name = wpa_strdup_tchar(drv->ifname);
 +      if (drv->adapter_name == NULL) {
 +              wpa_printf(MSG_ERROR, "NDIS: Failed to allocate memory for "
 +                         "adapter name");
 +              os_free(b);
 +              return -1;
 +      }
 +#endif /* _WIN32_WCE */
 +
 +      dpos = os_strstr(desc, " - ");
 +      if (dpos)
 +              dlen = dpos - desc;
 +      else
 +              dlen = os_strlen(desc);
 +      drv->adapter_desc = dup_binstr(desc, dlen);
 +      os_free(b);
 +      if (drv->adapter_desc == NULL)
 +              return -1;
 +
 +      wpa_printf(MSG_DEBUG, "NDIS: Adapter description prefix '%s'",
 +                 drv->adapter_desc);
 +
 +      return 0;
 +#else /* CONFIG_USE_NDISUIO */
 +      PTSTR _names;
 +      char *names, *pos, *pos2;
 +      ULONG len;
 +      BOOLEAN res;
 +#define MAX_ADAPTERS 32
 +      char *name[MAX_ADAPTERS];
 +      char *desc[MAX_ADAPTERS];
 +      int num_name, num_desc, i, found_name, found_desc;
 +      size_t dlen;
 +
 +      wpa_printf(MSG_DEBUG, "NDIS: Packet.dll version: %s",
 +                 PacketGetVersion());
 +
 +      len = 8192;
 +      _names = os_zalloc(len);
 +      if (_names == NULL)
 +              return -1;
 +
 +      res = PacketGetAdapterNames(_names, &len);
 +      if (!res && len > 8192) {
 +              os_free(_names);
 +              _names = os_zalloc(len);
 +              if (_names == NULL)
 +                      return -1;
 +              res = PacketGetAdapterNames(_names, &len);
 +      }
 +
 +      if (!res) {
 +              wpa_printf(MSG_ERROR, "NDIS: Failed to get adapter list "
 +                         "(PacketGetAdapterNames)");
 +              os_free(_names);
 +              return -1;
 +      }
 +
 +      names = (char *) _names;
 +      if (names[0] && names[1] == '\0' && names[2] && names[3] == '\0') {
 +              wpa_printf(MSG_DEBUG, "NDIS: Looks like adapter names are in "
 +                         "UNICODE");
 +              /* Convert to ASCII */
 +              pos2 = pos = names;
 +              while (pos2 < names + len) {
 +                      if (pos2[0] == '\0' && pos2[1] == '\0' &&
 +                          pos2[2] == '\0' && pos2[3] == '\0') {
 +                              pos2 += 4;
 +                              break;
 +                      }
 +                      *pos++ = pos2[0];
 +                      pos2 += 2;
 +              }
 +              os_memcpy(pos + 2, names, pos - names);
 +              pos += 2;
 +      } else
 +              pos = names;
 +
 +      num_name = 0;
 +      while (pos < names + len) {
 +              name[num_name] = pos;
 +              while (*pos && pos < names + len)
 +                      pos++;
 +              if (pos + 1 >= names + len) {
 +                      os_free(names);
 +                      return -1;
 +              }
 +              pos++;
 +              num_name++;
 +              if (num_name >= MAX_ADAPTERS) {
 +                      wpa_printf(MSG_DEBUG, "NDIS: Too many adapters");
 +                      os_free(names);
 +                      return -1;
 +              }
 +              if (*pos == '\0') {
 +                      wpa_printf(MSG_DEBUG, "NDIS: %d adapter names found",
 +                                 num_name);
 +                      pos++;
 +                      break;
 +              }
 +      }
 +
 +      num_desc = 0;
 +      while (pos < names + len) {
 +              desc[num_desc] = pos;
 +              while (*pos && pos < names + len)
 +                      pos++;
 +              if (pos + 1 >= names + len) {
 +                      os_free(names);
 +                      return -1;
 +              }
 +              pos++;
 +              num_desc++;
 +              if (num_desc >= MAX_ADAPTERS) {
 +                      wpa_printf(MSG_DEBUG, "NDIS: Too many adapter "
 +                                 "descriptions");
 +                      os_free(names);
 +                      return -1;
 +              }
 +              if (*pos == '\0') {
 +                      wpa_printf(MSG_DEBUG, "NDIS: %d adapter descriptions "
 +                                 "found", num_name);
 +                      pos++;
 +                      break;
 +              }
 +      }
 +
 +      /*
 +       * Windows 98 with Packet.dll 3.0 alpha3 does not include adapter
 +       * descriptions. Fill in dummy descriptors to work around this.
 +       */
 +      while (num_desc < num_name)
 +              desc[num_desc++] = "dummy description";
 +
 +      if (num_name != num_desc) {
 +              wpa_printf(MSG_DEBUG, "NDIS: mismatch in adapter name and "
 +                         "description counts (%d != %d)",
 +                         num_name, num_desc);
 +              os_free(names);
 +              return -1;
 +      }
 +
 +      found_name = found_desc = -1;
 +      for (i = 0; i < num_name; i++) {
 +              wpa_printf(MSG_DEBUG, "NDIS: %d - %s - %s",
 +                         i, name[i], desc[i]);
 +              if (found_name == -1 && os_strstr(name[i], drv->ifname))
 +                      found_name = i;
 +              if (found_desc == -1 &&
 +                  os_strncmp(desc[i], drv->ifname, os_strlen(drv->ifname)) ==
 +                  0)
 +                      found_desc = i;
 +      }
 +
 +      if (found_name < 0 && found_desc >= 0) {
 +              wpa_printf(MSG_DEBUG, "NDIS: Matched interface '%s' based on "
 +                         "description '%s'",
 +                         name[found_desc], desc[found_desc]);
 +              found_name = found_desc;
 +              os_strlcpy(drv->ifname,
 +                         os_strncmp(name[found_desc], "\\Device\\NPF_", 12)
 +                         == 0 ? name[found_desc] + 12 : name[found_desc],
 +                         sizeof(drv->ifname));
 +      }
 +
 +      if (found_name < 0) {
 +              wpa_printf(MSG_DEBUG, "NDIS: Could not find interface '%s'",
 +                         drv->ifname);
 +              os_free(names);
 +              return -1;
 +      }
 +
 +      i = found_name;
 +      pos = os_strrchr(desc[i], '(');
 +      if (pos) {
 +              dlen = pos - desc[i];
 +              pos--;
 +              if (pos > desc[i] && *pos == ' ')
 +                      dlen--;
 +      } else {
 +              dlen = os_strlen(desc[i]);
 +      }
 +      drv->adapter_desc = dup_binstr(desc[i], dlen);
 +      os_free(names);
 +      if (drv->adapter_desc == NULL)
 +              return -1;
 +
 +      wpa_printf(MSG_DEBUG, "NDIS: Adapter description prefix '%s'",
 +                 drv->adapter_desc);
 +
 +      return 0;
 +#endif /* CONFIG_USE_NDISUIO */
 +}
 +
 +
 +#if defined(CONFIG_NATIVE_WINDOWS) || defined(__CYGWIN__)
 +#ifndef _WIN32_WCE
 +/*
 + * These structures are undocumented for WinXP; only WinCE version is
 + * documented. These would be included wzcsapi.h if it were available. Some
 + * changes here have been needed to make the structures match with WinXP SP2.
 + * It is unclear whether these work with any other version.
 + */
 +
 +typedef struct {
 +      LPWSTR wszGuid;
 +} INTF_KEY_ENTRY, *PINTF_KEY_ENTRY;
 +
 +typedef struct {
 +      DWORD dwNumIntfs;
 +      PINTF_KEY_ENTRY pIntfs;
 +} INTFS_KEY_TABLE, *PINTFS_KEY_TABLE;
 +
 +typedef struct {
 +      DWORD dwDataLen;
 +      LPBYTE pData;
 +} RAW_DATA, *PRAW_DATA;
 +
 +typedef struct {
 +      LPWSTR wszGuid;
 +      LPWSTR wszDescr;
 +      ULONG ulMediaState;
 +      ULONG ulMediaType;
 +      ULONG ulPhysicalMediaType;
 +      INT nInfraMode;
 +      INT nAuthMode;
 +      INT nWepStatus;
 +#ifndef _WIN32_WCE
 +      u8 pad[2]; /* why is this needed? */
 +#endif /* _WIN32_WCE */
 +      DWORD dwCtlFlags;
 +      DWORD dwCapabilities; /* something added for WinXP SP2(?) */
 +      RAW_DATA rdSSID;
 +      RAW_DATA rdBSSID;
 +      RAW_DATA rdBSSIDList;
 +      RAW_DATA rdStSSIDList;
 +      RAW_DATA rdCtrlData;
 +#ifdef UNDER_CE
 +      BOOL bInitialized;
 +#endif
 +      DWORD nWPAMCastCipher;
 +      /* add some extra buffer for later additions since this interface is
 +       * far from stable */
 +      u8 later_additions[100];
 +} INTF_ENTRY, *PINTF_ENTRY;
 +
 +#define INTF_ALL 0xffffffff
 +#define INTF_ALL_FLAGS 0x0000ffff
 +#define INTF_CTLFLAGS 0x00000010
 +#define INTFCTL_ENABLED 0x8000
 +#endif /* _WIN32_WCE */
 +
 +
 +#ifdef _WIN32_WCE
 +static int wpa_driver_ndis_rebind_adapter(struct wpa_driver_ndis_data *drv)
 +{
 +      HANDLE ndis;
 +      TCHAR multi[100];
 +      int len;
 +
 +      len = _tcslen(drv->adapter_name);
 +      if (len > 80)
 +              return -1;
 +
 +      ndis = CreateFile(DD_NDIS_DEVICE_NAME, GENERIC_READ | GENERIC_WRITE,
 +                        0, NULL, OPEN_EXISTING, 0, NULL);
 +      if (ndis == INVALID_HANDLE_VALUE) {
 +              wpa_printf(MSG_DEBUG, "NDIS: Failed to open file to NDIS "
 +                         "device: %d", (int) GetLastError());
 +              return -1;
 +      }
 +
 +      len++;
 +      memcpy(multi, drv->adapter_name, len * sizeof(TCHAR));
 +      memcpy(&multi[len], TEXT("NDISUIO\0"), 9 * sizeof(TCHAR));
 +      len += 9;
 +
 +      if (!DeviceIoControl(ndis, IOCTL_NDIS_REBIND_ADAPTER,
 +                           multi, len * sizeof(TCHAR), NULL, 0, NULL, NULL))
 +      {
 +              wpa_printf(MSG_DEBUG, "NDIS: IOCTL_NDIS_REBIND_ADAPTER "
 +                         "failed: 0x%x", (int) GetLastError());
 +              wpa_hexdump_ascii(MSG_DEBUG, "NDIS: rebind multi_sz",
 +                                (u8 *) multi, len * sizeof(TCHAR));
 +              CloseHandle(ndis);
 +              return -1;
 +      }
 +
 +      CloseHandle(ndis);
 +
 +      wpa_printf(MSG_DEBUG, "NDIS: Requested NDIS rebind of NDISUIO "
 +                 "protocol");
 +
 +      return 0;
 +}
 +#endif /* _WIN32_WCE */
 +
 +
 +static int wpa_driver_ndis_set_wzc(struct wpa_driver_ndis_data *drv,
 +                                 int enable)
 +{
 +#ifdef _WIN32_WCE
 +      HKEY hk, hk2;
 +      LONG ret;
 +      DWORD i, hnd, len;
 +      TCHAR keyname[256], devname[256];
 +
 +#define WZC_DRIVER TEXT("Drivers\\BuiltIn\\ZeroConfig")
 +
 +      if (enable) {
 +              HANDLE h;
 +              h = ActivateDeviceEx(WZC_DRIVER, NULL, 0, NULL);
 +              if (h == INVALID_HANDLE_VALUE || h == 0) {
 +                      wpa_printf(MSG_DEBUG, "NDIS: Failed to re-enable WZC "
 +                                 "- ActivateDeviceEx failed: %d",
 +                                 (int) GetLastError());
 +                      return -1;
 +              }
 +
 +              wpa_printf(MSG_DEBUG, "NDIS: WZC re-enabled");
 +              return wpa_driver_ndis_rebind_adapter(drv);
 +      }
 +
 +      /*
 +       * Unfortunately, just disabling the WZC for an interface is not enough
 +       * to free NDISUIO for us, so need to disable and unload WZC completely
 +       * for now when using WinCE with NDISUIO. In addition, must request
 +       * NDISUIO protocol to be rebound to the adapter in order to free the
 +       * NDISUIO binding that WZC hold before us.
 +       */
 +
 +      /* Enumerate HKLM\Drivers\Active\* to find a handle to WZC. */
 +      ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, DEVLOAD_ACTIVE_KEY, 0, 0, &hk);
 +      if (ret != ERROR_SUCCESS) {
 +              wpa_printf(MSG_DEBUG, "NDIS: RegOpenKeyEx(DEVLOAD_ACTIVE_KEY) "
 +                         "failed: %d %d", (int) ret, (int) GetLastError());
 +              return -1;
 +      }
 +
 +      for (i = 0; ; i++) {
 +              len = sizeof(keyname);
 +              ret = RegEnumKeyEx(hk, i, keyname, &len, NULL, NULL, NULL,
 +                                 NULL);
 +              if (ret != ERROR_SUCCESS) {
 +                      wpa_printf(MSG_DEBUG, "NDIS: Could not find active "
 +                                 "WZC - assuming it is not running.");
 +                      RegCloseKey(hk);
 +                      return -1;
 +              }
 +
 +              ret = RegOpenKeyEx(hk, keyname, 0, 0, &hk2);
 +              if (ret != ERROR_SUCCESS) {
 +                      wpa_printf(MSG_DEBUG, "NDIS: RegOpenKeyEx(active dev) "
 +                                 "failed: %d %d",
 +                                 (int) ret, (int) GetLastError());
 +                      continue;
 +              }
 +
 +              len = sizeof(devname);
 +              ret = RegQueryValueEx(hk2, DEVLOAD_DEVKEY_VALNAME, NULL, NULL,
 +                                    (LPBYTE) devname, &len);
 +              if (ret != ERROR_SUCCESS) {
 +                      wpa_printf(MSG_DEBUG, "NDIS: RegQueryValueEx("
 +                                 "DEVKEY_VALNAME) failed: %d %d",
 +                                 (int) ret, (int) GetLastError());
 +                      RegCloseKey(hk2);
 +                      continue;
 +              }
 +
 +              if (_tcscmp(devname, WZC_DRIVER) == 0)
 +                      break;
 +
 +              RegCloseKey(hk2);
 +      }
 +
 +      RegCloseKey(hk);
 +
 +      /* Found WZC - get handle to it. */
 +      len = sizeof(hnd);
 +      ret = RegQueryValueEx(hk2, DEVLOAD_HANDLE_VALNAME, NULL, NULL,
 +                            (PUCHAR) &hnd, &len);
 +      if (ret != ERROR_SUCCESS) {
 +              wpa_printf(MSG_DEBUG, "NDIS: RegQueryValueEx(HANDLE_VALNAME) "
 +                         "failed: %d %d", (int) ret, (int) GetLastError());
 +              RegCloseKey(hk2);
 +              return -1;
 +      }
 +
 +      RegCloseKey(hk2);
 +
 +      /* Deactivate WZC */
 +      if (!DeactivateDevice((HANDLE) hnd)) {
 +              wpa_printf(MSG_DEBUG, "NDIS: DeactivateDevice failed: %d",
 +                         (int) GetLastError());
 +              return -1;
 +      }
 +
 +      wpa_printf(MSG_DEBUG, "NDIS: Disabled WZC temporarily");
 +      drv->wzc_disabled = 1;
 +      return wpa_driver_ndis_rebind_adapter(drv);
 +
 +#else /* _WIN32_WCE */
 +
 +      HMODULE hm;
 +      DWORD (WINAPI *wzc_enum_interf)(LPWSTR pSrvAddr,
 +                                      PINTFS_KEY_TABLE pIntfs);
 +      DWORD (WINAPI *wzc_query_interf)(LPWSTR pSrvAddr, DWORD dwInFlags,
 +                                       PINTF_ENTRY pIntf,
 +                                       LPDWORD pdwOutFlags);
 +      DWORD (WINAPI *wzc_set_interf)(LPWSTR pSrvAddr, DWORD dwInFlags,
 +                                     PINTF_ENTRY pIntf, LPDWORD pdwOutFlags);
 +      int ret = -1, j;
 +      DWORD res;
 +      INTFS_KEY_TABLE guids;
 +      INTF_ENTRY intf;
 +      char guid[128];
 +      WCHAR *pos;
 +      DWORD flags, i;
 +
 +      hm = LoadLibrary(TEXT("wzcsapi.dll"));
 +      if (hm == NULL) {
 +              wpa_printf(MSG_DEBUG, "NDIS: Failed to load wzcsapi.dll (%u) "
 +                         "- WZC probably not running",
 +                         (unsigned int) GetLastError());
 +              return -1;
 +      }
 +
 +#ifdef _WIN32_WCE
 +      wzc_enum_interf = (void *) GetProcAddressA(hm, "WZCEnumInterfaces");
 +      wzc_query_interf = (void *) GetProcAddressA(hm, "WZCQueryInterface");
 +      wzc_set_interf = (void *) GetProcAddressA(hm, "WZCSetInterface");
 +#else /* _WIN32_WCE */
 +      wzc_enum_interf = (void *) GetProcAddress(hm, "WZCEnumInterfaces");
 +      wzc_query_interf = (void *) GetProcAddress(hm, "WZCQueryInterface");
 +      wzc_set_interf = (void *) GetProcAddress(hm, "WZCSetInterface");
 +#endif /* _WIN32_WCE */
 +
 +      if (wzc_enum_interf == NULL || wzc_query_interf == NULL ||
 +          wzc_set_interf == NULL) {
 +              wpa_printf(MSG_DEBUG, "NDIS: WZCEnumInterfaces, "
 +                         "WZCQueryInterface, or WZCSetInterface not found "
 +                         "in wzcsapi.dll");
 +              goto fail;
 +      }
 +
 +      os_memset(&guids, 0, sizeof(guids));
 +      res = wzc_enum_interf(NULL, &guids);
 +      if (res != 0) {
 +              wpa_printf(MSG_DEBUG, "NDIS: WZCEnumInterfaces failed: %d; "
 +                         "WZC service is apparently not running",
 +                         (int) res);
 +              goto fail;
 +      }
 +
 +      wpa_printf(MSG_DEBUG, "NDIS: WZCEnumInterfaces: %d interfaces",
 +                 (int) guids.dwNumIntfs);
 +
 +      for (i = 0; i < guids.dwNumIntfs; i++) {
 +              pos = guids.pIntfs[i].wszGuid;
 +              for (j = 0; j < sizeof(guid); j++) {
 +                      guid[j] = (char) *pos;
 +                      if (*pos == 0)
 +                              break;
 +                      pos++;
 +              }
 +              guid[sizeof(guid) - 1] = '\0';
 +              wpa_printf(MSG_DEBUG, "NDIS: intfs %d GUID '%s'",
 +                         (int) i, guid);
 +              if (os_strstr(drv->ifname, guid) == NULL)
 +                      continue;
 +
 +              wpa_printf(MSG_DEBUG, "NDIS: Current interface found from "
 +                         "WZC");
 +              break;
 +      }
 +
 +      if (i >= guids.dwNumIntfs) {
 +              wpa_printf(MSG_DEBUG, "NDIS: Current interface not found from "
 +                         "WZC");
 +              goto fail;
 +      }
 +
 +      os_memset(&intf, 0, sizeof(intf));
 +      intf.wszGuid = guids.pIntfs[i].wszGuid;
 +      /* Set flags to verify that the structure has not changed. */
 +      intf.dwCtlFlags = -1;
 +      flags = 0;
 +      res = wzc_query_interf(NULL, INTFCTL_ENABLED, &intf, &flags);
 +      if (res != 0) {
 +              wpa_printf(MSG_DEBUG, "NDIS: Could not query flags for the "
 +                         "WZC interface: %d (0x%x)",
 +                         (int) res, (int) res);
 +              wpa_printf(MSG_DEBUG, "NDIS: GetLastError: %u",
 +                         (unsigned int) GetLastError());
 +              goto fail;
 +      }
 +
 +      wpa_printf(MSG_DEBUG, "NDIS: WZC interface flags 0x%x dwCtlFlags 0x%x",
 +                 (int) flags, (int) intf.dwCtlFlags);
 +
 +      if (intf.dwCtlFlags == -1) {
 +              wpa_printf(MSG_DEBUG, "NDIS: Looks like wzcsapi has changed "
 +                         "again - could not disable WZC");
 +              wpa_hexdump(MSG_MSGDUMP, "NDIS: intf",
 +                          (u8 *) &intf, sizeof(intf));
 +              goto fail;
 +      }
 +
 +      if (enable) {
 +              if (!(intf.dwCtlFlags & INTFCTL_ENABLED)) {
 +                      wpa_printf(MSG_DEBUG, "NDIS: Enabling WZC for this "
 +                                 "interface");
 +                      intf.dwCtlFlags |= INTFCTL_ENABLED;
 +                      res = wzc_set_interf(NULL, INTFCTL_ENABLED, &intf,
 +                                           &flags);
 +                      if (res != 0) {
 +                              wpa_printf(MSG_DEBUG, "NDIS: Failed to enable "
 +                                         "WZC: %d (0x%x)",
 +                                         (int) res, (int) res);
 +                              wpa_printf(MSG_DEBUG, "NDIS: GetLastError: %u",
 +                                         (unsigned int) GetLastError());
 +                              goto fail;
 +                      }
 +                      wpa_printf(MSG_DEBUG, "NDIS: Re-enabled WZC for this "
 +                                 "interface");
 +                      drv->wzc_disabled = 0;
 +              }
 +      } else {
 +              if (intf.dwCtlFlags & INTFCTL_ENABLED) {
 +                      wpa_printf(MSG_DEBUG, "NDIS: Disabling WZC for this "
 +                                 "interface");
 +                      intf.dwCtlFlags &= ~INTFCTL_ENABLED;
 +                      res = wzc_set_interf(NULL, INTFCTL_ENABLED, &intf,
 +                                           &flags);
 +                      if (res != 0) {
 +                              wpa_printf(MSG_DEBUG, "NDIS: Failed to "
 +                                         "disable WZC: %d (0x%x)",
 +                                         (int) res, (int) res);
 +                              wpa_printf(MSG_DEBUG, "NDIS: GetLastError: %u",
 +                                         (unsigned int) GetLastError());
 +                              goto fail;
 +                      }
 +                      wpa_printf(MSG_DEBUG, "NDIS: Disabled WZC temporarily "
 +                                 "for this interface");
 +                      drv->wzc_disabled = 1;
 +              } else {
 +                      wpa_printf(MSG_DEBUG, "NDIS: WZC was not enabled for "
 +                                 "this interface");
 +              }
 +      }
 +
 +      ret = 0;
 +
 +fail:
 +      FreeLibrary(hm);
 +
 +      return ret;
 +#endif /* _WIN32_WCE */
 +}
 +
 +#else /* CONFIG_NATIVE_WINDOWS || __CYGWIN__ */
 +
 +static int wpa_driver_ndis_set_wzc(struct wpa_driver_ndis_data *drv,
 +                                 int enable)
 +{
 +      return 0;
 +}
 +
 +#endif /* CONFIG_NATIVE_WINDOWS || __CYGWIN__ */
 +
 +
 +#ifdef CONFIG_USE_NDISUIO
 +/*
 + * l2_packet_ndis.c is sharing the same handle to NDISUIO, so we must be able
 + * to export this handle. This is somewhat ugly, but there is no better
 + * mechanism available to pass data from driver interface to l2_packet wrapper.
 + */
 +static HANDLE driver_ndis_ndisuio_handle = INVALID_HANDLE_VALUE;
 +
 +HANDLE driver_ndis_get_ndisuio_handle(void)
 +{
 +      return driver_ndis_ndisuio_handle;
 +}
 +#endif /* CONFIG_USE_NDISUIO */
 +
 +
 +static int wpa_driver_ndis_adapter_init(struct wpa_driver_ndis_data *drv)
 +{
 +#ifdef CONFIG_USE_NDISUIO
 +#ifndef _WIN32_WCE
 +#define NDISUIO_DEVICE_NAME TEXT("\\\\.\\\\Ndisuio")
 +      DWORD written;
 +#endif /* _WIN32_WCE */
 +      drv->ndisuio = CreateFile(NDISUIO_DEVICE_NAME,
 +                                GENERIC_READ | GENERIC_WRITE, 0, NULL,
 +                                OPEN_EXISTING,
 +                                FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
 +                                INVALID_HANDLE_VALUE);
 +      if (drv->ndisuio == INVALID_HANDLE_VALUE) {
 +              wpa_printf(MSG_ERROR, "NDIS: Failed to open connection to "
 +                         "NDISUIO: %d", (int) GetLastError());
 +              return -1;
 +      }
 +      driver_ndis_ndisuio_handle = drv->ndisuio;
 +
 +#ifndef _WIN32_WCE
 +      if (!DeviceIoControl(drv->ndisuio, IOCTL_NDISUIO_BIND_WAIT, NULL, 0,
 +                           NULL, 0, &written, NULL)) {
 +              wpa_printf(MSG_ERROR, "NDIS: IOCTL_NDISUIO_BIND_WAIT failed: "
 +                         "%d", (int) GetLastError());
 +              CloseHandle(drv->ndisuio);
 +              drv->ndisuio = INVALID_HANDLE_VALUE;
 +              return -1;
 +      }
 +#endif /* _WIN32_WCE */
 +
 +      return 0;
 +#else /* CONFIG_USE_NDISUIO */
 +      return 0;
 +#endif /* CONFIG_USE_NDISUIO */
 +}
 +
 +
 +static int wpa_driver_ndis_adapter_open(struct wpa_driver_ndis_data *drv)
 +{
 +#ifdef CONFIG_USE_NDISUIO
 +      DWORD written;
 +#define MAX_NDIS_DEVICE_NAME_LEN 256
 +      WCHAR ifname[MAX_NDIS_DEVICE_NAME_LEN];
 +      size_t len, i, pos;
 +      const char *prefix = "\\DEVICE\\";
 +
 +#ifdef _WIN32_WCE
 +      pos = 0;
 +#else /* _WIN32_WCE */
 +      pos = 8;
 +#endif /* _WIN32_WCE */
 +      len = pos + os_strlen(drv->ifname);
 +      if (len >= MAX_NDIS_DEVICE_NAME_LEN)
 +              return -1;
 +      for (i = 0; i < pos; i++)
 +              ifname[i] = (WCHAR) prefix[i];
 +      for (i = pos; i < len; i++)
 +              ifname[i] = (WCHAR) drv->ifname[i - pos];
 +      ifname[i] = L'\0';
 +
 +      if (!DeviceIoControl(drv->ndisuio, IOCTL_NDISUIO_OPEN_DEVICE,
 +                           ifname, len * sizeof(WCHAR), NULL, 0, &written,
 +                           NULL)) {
 +              wpa_printf(MSG_ERROR, "NDIS: IOCTL_NDISUIO_OPEN_DEVICE "
 +                         "failed: %d", (int) GetLastError());
 +              wpa_hexdump_ascii(MSG_DEBUG, "NDIS: ifname",
 +                                (const u8 *) ifname, len * sizeof(WCHAR));
 +              CloseHandle(drv->ndisuio);
 +              drv->ndisuio = INVALID_HANDLE_VALUE;
 +              return -1;
 +      }
 +
 +      wpa_printf(MSG_DEBUG, "NDIS: Opened NDISUIO device successfully");
 +
 +      return 0;
 +#else /* CONFIG_USE_NDISUIO */
 +      char ifname[128];
 +      os_snprintf(ifname, sizeof(ifname), "\\Device\\NPF_%s", drv->ifname);
 +      drv->adapter = PacketOpenAdapter(ifname);
 +      if (drv->adapter == NULL) {
 +              wpa_printf(MSG_DEBUG, "NDIS: PacketOpenAdapter failed for "
 +                         "'%s'", ifname);
 +              return -1;
 +      }
 +      return 0;
 +#endif /* CONFIG_USE_NDISUIO */
 +}
 +
 +
 +static void wpa_driver_ndis_adapter_close(struct wpa_driver_ndis_data *drv)
 +{
 +#ifdef CONFIG_USE_NDISUIO
 +      driver_ndis_ndisuio_handle = INVALID_HANDLE_VALUE;
 +      if (drv->ndisuio != INVALID_HANDLE_VALUE)
 +              CloseHandle(drv->ndisuio);
 +#else /* CONFIG_USE_NDISUIO */
 +      if (drv->adapter)
 +              PacketCloseAdapter(drv->adapter);
 +#endif /* CONFIG_USE_NDISUIO */
 +}
 +
 +
 +static int ndis_add_multicast(struct wpa_driver_ndis_data *drv)
 +{
 +      if (ndis_set_oid(drv, OID_802_3_MULTICAST_LIST,
 +                       (const char *) pae_group_addr, ETH_ALEN) < 0) {
 +              wpa_printf(MSG_DEBUG, "NDIS: Failed to add PAE group address "
 +                         "to the multicast list");
 +              return -1;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +static void * wpa_driver_ndis_init(void *ctx, const char *ifname)
 +{
 +      struct wpa_driver_ndis_data *drv;
 +      u32 mode;
 +
 +      drv = os_zalloc(sizeof(*drv));
 +      if (drv == NULL)
 +              return NULL;
 +      drv->ctx = ctx;
 +      /*
 +       * Compatibility code to strip possible prefix from the GUID. Previous
 +       * versions include \Device\NPF_ prefix for all names, but the internal
 +       * interface name is now only the GUI. Both Packet32 and NDISUIO
 +       * prefixes are supported.
 +       */
 +      if (os_strncmp(ifname, "\\Device\\NPF_", 12) == 0)
 +              ifname += 12;
 +      else if (os_strncmp(ifname, "\\DEVICE\\", 8) == 0)
 +              ifname += 8;
 +      os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname));
 +
 +      if (wpa_driver_ndis_adapter_init(drv) < 0) {
 +              os_free(drv);
 +              return NULL;
 +      }
 +
 +      if (wpa_driver_ndis_get_names(drv) < 0) {
 +              wpa_driver_ndis_adapter_close(drv);
 +              os_free(drv);
 +              return NULL;
 +      }
 +
 +      wpa_driver_ndis_set_wzc(drv, 0);
 +
 +      if (wpa_driver_ndis_adapter_open(drv) < 0) {
 +              wpa_driver_ndis_adapter_close(drv);
 +              os_free(drv);
 +              return NULL;
 +      }
 +
 +      if (ndis_get_oid(drv, OID_802_3_CURRENT_ADDRESS,
 +                       (char *) drv->own_addr, ETH_ALEN) < 0) {
 +              wpa_printf(MSG_DEBUG, "NDIS: Get OID_802_3_CURRENT_ADDRESS "
 +                         "failed");
 +              wpa_driver_ndis_adapter_close(drv);
 +              os_free(drv);
 +              return NULL;
 +      }
 +      wpa_driver_ndis_get_capability(drv);
 +
 +      /* Make sure that the driver does not have any obsolete PMKID entries.
 +       */
 +      wpa_driver_ndis_flush_pmkid(drv);
 +
 +      /*
 +       * Disconnect to make sure that driver re-associates if it was
 +       * connected.
 +       */
 +      wpa_driver_ndis_disconnect(drv);
 +
 +      eloop_register_timeout(1, 0, wpa_driver_ndis_poll_timeout, drv, NULL);
 +
 +#ifdef CONFIG_NDIS_EVENTS_INTEGRATED
 +      drv->events = ndis_events_init(&drv->events_pipe, &drv->event_avail,
 +                                     drv->ifname, drv->adapter_desc);
 +      if (drv->events == NULL) {
 +              wpa_driver_ndis_deinit(drv);
 +              return NULL;
 +      }
 +      eloop_register_event(drv->event_avail, sizeof(drv->event_avail),
 +                           wpa_driver_ndis_event_pipe_cb, drv, NULL);
 +#endif /* CONFIG_NDIS_EVENTS_INTEGRATED */
 +
 +#ifdef _WIN32_WCE
 +      if (ndisuio_notification_init(drv) < 0) {
 +              wpa_driver_ndis_deinit(drv);
 +              return NULL;
 +      }
 +#endif /* _WIN32_WCE */
 +
 +      /* Set mode here in case card was configured for ad-hoc mode
 +       * previously. */
 +      mode = Ndis802_11Infrastructure;
 +      if (ndis_set_oid(drv, OID_802_11_INFRASTRUCTURE_MODE,
 +                       (char *) &mode, sizeof(mode)) < 0) {
 +              char buf[8];
 +              int res;
 +              wpa_printf(MSG_DEBUG, "NDIS: Failed to set "
 +                         "OID_802_11_INFRASTRUCTURE_MODE (%d)",
 +                         (int) mode);
 +              /* Try to continue anyway */
 +
 +              res = ndis_get_oid(drv, OID_DOT11_CURRENT_OPERATION_MODE, buf,
 +                                 sizeof(buf));
 +              if (res > 0) {
 +                      wpa_printf(MSG_INFO, "NDIS: The driver seems to use "
 +                                 "Native 802.11 OIDs. These are not yet "
 +                                 "fully supported.");
 +                      drv->native80211 = 1;
 +              } else if (!drv->has_capability || drv->capa.enc == 0) {
 +                      /*
 +                       * Note: This will also happen with NDIS 6 drivers with
 +                       * Vista.
 +                       */
 +                      wpa_printf(MSG_DEBUG, "NDIS: Driver did not provide "
 +                                 "any wireless capabilities - assume it is "
 +                                 "a wired interface");
 +                      drv->wired = 1;
 +                      drv->capa.flags |= WPA_DRIVER_FLAGS_WIRED;
 +                      drv->has_capability = 1;
 +                      ndis_add_multicast(drv);
 +              }
 +      }
 +
 +      return drv;
 +}
 +
 +
 +static void wpa_driver_ndis_deinit(void *priv)
 +{
 +      struct wpa_driver_ndis_data *drv = priv;
 +
 +#ifdef CONFIG_NDIS_EVENTS_INTEGRATED
 +      if (drv->events) {
 +              eloop_unregister_event(drv->event_avail,
 +                                     sizeof(drv->event_avail));
 +              ndis_events_deinit(drv->events);
 +      }
 +#endif /* CONFIG_NDIS_EVENTS_INTEGRATED */
 +
 +#ifdef _WIN32_WCE
 +      ndisuio_notification_deinit(drv);
 +#endif /* _WIN32_WCE */
 +
 +      eloop_cancel_timeout(wpa_driver_ndis_scan_timeout, drv, drv->ctx);
 +      eloop_cancel_timeout(wpa_driver_ndis_poll_timeout, drv, NULL);
 +      wpa_driver_ndis_flush_pmkid(drv);
 +      wpa_driver_ndis_disconnect(drv);
 +      if (wpa_driver_ndis_radio_off(drv) < 0) {
 +              wpa_printf(MSG_DEBUG, "NDIS: failed to disassociate and turn "
 +                         "radio off");
 +      }
 +
 +      wpa_driver_ndis_adapter_close(drv);
 +
 +      if (drv->wzc_disabled)
 +              wpa_driver_ndis_set_wzc(drv, 1);
 +
 +#ifdef _WIN32_WCE
 +      os_free(drv->adapter_name);
 +#endif /* _WIN32_WCE */
 +      os_free(drv->adapter_desc);
 +      os_free(drv);
 +}
 +
 +
 +static struct wpa_interface_info *
 +wpa_driver_ndis_get_interfaces(void *global_priv)
 +{
 +      struct wpa_interface_info *iface = NULL, *niface;
 +
 +#ifdef CONFIG_USE_NDISUIO
 +      NDISUIO_QUERY_BINDING *b;
 +      size_t blen = sizeof(*b) + 1024;
 +      int i, error;
 +      DWORD written;
 +      char name[256], desc[256];
 +      WCHAR *pos;
 +      size_t j, len;
 +      HANDLE ndisuio;
 +
 +      ndisuio = CreateFile(NDISUIO_DEVICE_NAME,
 +                           GENERIC_READ | GENERIC_WRITE, 0, NULL,
 +                           OPEN_EXISTING,
 +                           FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
 +                           INVALID_HANDLE_VALUE);
 +      if (ndisuio == INVALID_HANDLE_VALUE) {
 +              wpa_printf(MSG_ERROR, "NDIS: Failed to open connection to "
 +                         "NDISUIO: %d", (int) GetLastError());
 +              return NULL;
 +      }
 +
 +#ifndef _WIN32_WCE
 +      if (!DeviceIoControl(ndisuio, IOCTL_NDISUIO_BIND_WAIT, NULL, 0,
 +                           NULL, 0, &written, NULL)) {
 +              wpa_printf(MSG_ERROR, "NDIS: IOCTL_NDISUIO_BIND_WAIT failed: "
 +                         "%d", (int) GetLastError());
 +              CloseHandle(ndisuio);
 +              return NULL;
 +      }
 +#endif /* _WIN32_WCE */
 +
 +      b = os_malloc(blen);
 +      if (b == NULL) {
 +              CloseHandle(ndisuio);
 +              return NULL;
 +      }
 +
 +      for (i = 0; ; i++) {
 +              os_memset(b, 0, blen);
 +              b->BindingIndex = i;
 +              if (!DeviceIoControl(ndisuio, IOCTL_NDISUIO_QUERY_BINDING,
 +                                   b, sizeof(NDISUIO_QUERY_BINDING), b, blen,
 +                                   &written, NULL)) {
 +                      error = (int) GetLastError();
 +                      if (error == ERROR_NO_MORE_ITEMS)
 +                              break;
 +                      wpa_printf(MSG_DEBUG, "IOCTL_NDISUIO_QUERY_BINDING "
 +                                 "failed: %d", error);
 +                      break;
 +              }
 +
 +              pos = (WCHAR *) ((char *) b + b->DeviceNameOffset);
 +              len = b->DeviceNameLength;
 +              if (len >= sizeof(name))
 +                      len = sizeof(name) - 1;
 +              for (j = 0; j < len; j++)
 +                      name[j] = (char) pos[j];
 +              name[len] = '\0';
 +
 +              pos = (WCHAR *) ((char *) b + b->DeviceDescrOffset);
 +              len = b->DeviceDescrLength;
 +              if (len >= sizeof(desc))
 +                      len = sizeof(desc) - 1;
 +              for (j = 0; j < len; j++)
 +                      desc[j] = (char) pos[j];
 +              desc[len] = '\0';
 +
 +              wpa_printf(MSG_DEBUG, "NDIS: %d - %s - %s", i, name, desc);
 +
 +              niface = os_zalloc(sizeof(*niface));
 +              if (niface == NULL)
 +                      break;
 +              niface->drv_name = "ndis";
 +              if (os_strncmp(name, "\\DEVICE\\", 8) == 0)
 +                      niface->ifname = os_strdup(name + 8);
 +              else
 +                      niface->ifname = os_strdup(name);
 +              if (niface->ifname == NULL) {
 +                      os_free(niface);
 +                      break;
 +              }
 +              niface->desc = os_strdup(desc);
 +              niface->next = iface;
 +              iface = niface;
 +      }
 +
 +      os_free(b);
 +      CloseHandle(ndisuio);
 +#else /* CONFIG_USE_NDISUIO */
 +      PTSTR _names;
 +      char *names, *pos, *pos2;
 +      ULONG len;
 +      BOOLEAN res;
 +      char *name[MAX_ADAPTERS];
 +      char *desc[MAX_ADAPTERS];
 +      int num_name, num_desc, i;
 +
 +      wpa_printf(MSG_DEBUG, "NDIS: Packet.dll version: %s",
 +                 PacketGetVersion());
 +
 +      len = 8192;
 +      _names = os_zalloc(len);
 +      if (_names == NULL)
 +              return NULL;
 +
 +      res = PacketGetAdapterNames(_names, &len);
 +      if (!res && len > 8192) {
 +              os_free(_names);
 +              _names = os_zalloc(len);
 +              if (_names == NULL)
 +                      return NULL;
 +              res = PacketGetAdapterNames(_names, &len);
 +      }
 +
 +      if (!res) {
 +              wpa_printf(MSG_ERROR, "NDIS: Failed to get adapter list "
 +                         "(PacketGetAdapterNames)");
 +              os_free(_names);
 +              return NULL;
 +      }
 +
 +      names = (char *) _names;
 +      if (names[0] && names[1] == '\0' && names[2] && names[3] == '\0') {
 +              wpa_printf(MSG_DEBUG, "NDIS: Looks like adapter names are in "
 +                         "UNICODE");
 +              /* Convert to ASCII */
 +              pos2 = pos = names;
 +              while (pos2 < names + len) {
 +                      if (pos2[0] == '\0' && pos2[1] == '\0' &&
 +                          pos2[2] == '\0' && pos2[3] == '\0') {
 +                              pos2 += 4;
 +                              break;
 +                      }
 +                      *pos++ = pos2[0];
 +                      pos2 += 2;
 +              }
 +              os_memcpy(pos + 2, names, pos - names);
 +              pos += 2;
 +      } else
 +              pos = names;
 +
 +      num_name = 0;
 +      while (pos < names + len) {
 +              name[num_name] = pos;
 +              while (*pos && pos < names + len)
 +                      pos++;
 +              if (pos + 1 >= names + len) {
 +                      os_free(names);
 +                      return NULL;
 +              }
 +              pos++;
 +              num_name++;
 +              if (num_name >= MAX_ADAPTERS) {
 +                      wpa_printf(MSG_DEBUG, "NDIS: Too many adapters");
 +                      os_free(names);
 +                      return NULL;
 +              }
 +              if (*pos == '\0') {
 +                      wpa_printf(MSG_DEBUG, "NDIS: %d adapter names found",
 +                                 num_name);
 +                      pos++;
 +                      break;
 +              }
 +      }
 +
 +      num_desc = 0;
 +      while (pos < names + len) {
 +              desc[num_desc] = pos;
 +              while (*pos && pos < names + len)
 +                      pos++;
 +              if (pos + 1 >= names + len) {
 +                      os_free(names);
 +                      return NULL;
 +              }
 +              pos++;
 +              num_desc++;
 +              if (num_desc >= MAX_ADAPTERS) {
 +                      wpa_printf(MSG_DEBUG, "NDIS: Too many adapter "
 +                                 "descriptions");
 +                      os_free(names);
 +                      return NULL;
 +              }
 +              if (*pos == '\0') {
 +                      wpa_printf(MSG_DEBUG, "NDIS: %d adapter descriptions "
 +                                 "found", num_name);
 +                      pos++;
 +                      break;
 +              }
 +      }
 +
 +      /*
 +       * Windows 98 with Packet.dll 3.0 alpha3 does not include adapter
 +       * descriptions. Fill in dummy descriptors to work around this.
 +       */
 +      while (num_desc < num_name)
 +              desc[num_desc++] = "dummy description";
 +
 +      if (num_name != num_desc) {
 +              wpa_printf(MSG_DEBUG, "NDIS: mismatch in adapter name and "
 +                         "description counts (%d != %d)",
 +                         num_name, num_desc);
 +              os_free(names);
 +              return NULL;
 +      }
 +
 +      for (i = 0; i < num_name; i++) {
 +              niface = os_zalloc(sizeof(*niface));
 +              if (niface == NULL)
 +                      break;
 +              niface->drv_name = "ndis";
 +              if (os_strncmp(name[i], "\\Device\\NPF_", 12) == 0)
 +                      niface->ifname = os_strdup(name[i] + 12);
 +              else
 +                      niface->ifname = os_strdup(name[i]);
 +              if (niface->ifname == NULL) {
 +                      os_free(niface);
 +                      break;
 +              }
 +              niface->desc = os_strdup(desc[i]);
 +              niface->next = iface;
 +              iface = niface;
 +      }
 +
 +#endif /* CONFIG_USE_NDISUIO */
 +
 +      return iface;
 +}
 +
 +
 +static const char *ndis_drv_name = "ndis";
 +static const char *ndis_drv_desc = "Windows NDIS driver";
 +
 +struct wpa_driver_ops wpa_driver_ndis_ops;
 +
 +void driver_ndis_init_ops(void)
 +{
 +      os_memset(&wpa_driver_ndis_ops, 0, sizeof(wpa_driver_ndis_ops));
 +      wpa_driver_ndis_ops.name = ndis_drv_name;
 +      wpa_driver_ndis_ops.desc = ndis_drv_desc;
 +      wpa_driver_ndis_ops.get_bssid = wpa_driver_ndis_get_bssid;
 +      wpa_driver_ndis_ops.get_ssid = wpa_driver_ndis_get_ssid;
 +      wpa_driver_ndis_ops.set_key = wpa_driver_ndis_set_key;
 +      wpa_driver_ndis_ops.init = wpa_driver_ndis_init;
 +      wpa_driver_ndis_ops.deinit = wpa_driver_ndis_deinit;
 +      wpa_driver_ndis_ops.deauthenticate = wpa_driver_ndis_deauthenticate;
 +      wpa_driver_ndis_ops.associate = wpa_driver_ndis_associate;
 +      wpa_driver_ndis_ops.add_pmkid = wpa_driver_ndis_add_pmkid;
 +      wpa_driver_ndis_ops.remove_pmkid = wpa_driver_ndis_remove_pmkid;
 +      wpa_driver_ndis_ops.flush_pmkid = wpa_driver_ndis_flush_pmkid;
 +      wpa_driver_ndis_ops.get_capa = wpa_driver_ndis_get_capa;
 +      wpa_driver_ndis_ops.poll = wpa_driver_ndis_poll;
 +      wpa_driver_ndis_ops.get_ifname = wpa_driver_ndis_get_ifname;
 +      wpa_driver_ndis_ops.get_mac_addr = wpa_driver_ndis_get_mac_addr;
 +      wpa_driver_ndis_ops.get_scan_results2 =
 +              wpa_driver_ndis_get_scan_results;
 +      wpa_driver_ndis_ops.get_interfaces = wpa_driver_ndis_get_interfaces;
 +      wpa_driver_ndis_ops.scan2 = wpa_driver_ndis_scan;
 +}
index 802589aa7581212a6902f8305a96634828f71b88,0000000000000000000000000000000000000000..5c21e0faf55cd86a603d359bdc3543e5f374fa96
mode 100644,000000..100644
--- /dev/null
@@@ -1,274 -1,0 +1,277 @@@
-       u8 ssid[32];
 +/*
 + * 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];
 +
 +      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 auth_ssid[32];
++      u8 ssid[SSID_MAX_LEN];
 +      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;
++      unsigned int setband_vendor_cmd_avail:1;
++      unsigned int get_pref_freq_list:1;
++      unsigned int set_prob_oper_freq: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];
- int android_genl_ctrl_resolve(struct nl_handle *handle, const char *name);
++      u8 auth_ssid[SSID_MAX_LEN];
 +      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_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);
++const u8 * nl80211_get_ie(const u8 *ies, size_t ies_len, u8 ie);
 +
 +#endif /* DRIVER_NL80211_H */
index 3cc9a65867f64fa3bdc6bfcc8787657be90e6475,0000000000000000000000000000000000000000..ba47888843bbb01873c022a8b9fccbef686bf9d9
mode 100644,000000..100644
--- /dev/null
@@@ -1,220 -1,0 +1,190 @@@
- #ifdef ANDROID_P2P_STUB
 +/*
 + * 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
- #endif /* ANDROID_P2P_STUB */
++#ifdef ANDROID_LIB_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;
 +}
 +
- 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;
- }
++#endif /* ANDROID_LIB_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);
 +}
 +
 +
index e0d1d233e2ad5548eb0c737ee69656c32acf30e9,0000000000000000000000000000000000000000..4cf31238aeb70703ea5e30d4376de57aeb71d6f4
mode 100644,000000..100644
--- /dev/null
@@@ -1,1532 -1,0 +1,1590 @@@
-                       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;
 +/*
 + * 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 int ext_feature_isset(const u8 *ext_features, int ext_features_len,
++                           enum nl80211_ext_feature_index ftidx)
++{
++      u8 ft_byte;
++
++      if ((int) ftidx / 8 >= ext_features_len)
++              return 0;
++
++      ft_byte = ext_features[ftidx / 8];
++      return (ft_byte & BIT(ftidx % 8)) != 0;
++}
++
++
++static void wiphy_info_ext_feature_flags(struct wiphy_info_data *info,
++                                       struct nlattr *tb)
++{
++      struct wpa_driver_capa *capa = info->capa;
++
++      if (tb == NULL)
++              return;
++
++      if (ext_feature_isset(nla_data(tb), nla_len(tb),
++                            NL80211_EXT_FEATURE_VHT_IBSS))
++              capa->flags |= WPA_DRIVER_FLAGS_VHT_IBSS;
++}
++
++
 +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_ext_feature_flags(info, tb[NL80211_ATTR_EXT_FEATURES]);
 +      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);
++                      if (vinfo->vendor_id == OUI_QCA) {
++                              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_GET_PREFERRED_FREQ_LIST:
++                                      drv->get_pref_freq_list = 1;
++                                      break;
++                              case QCA_NL80211_VENDOR_SUBCMD_SET_PROBABLE_OPER_CHANNEL:
++                                      drv->set_prob_oper_freq = 1;
++                                      break;
++                              case QCA_NL80211_VENDOR_SUBCMD_DO_ACS:
++                                      drv->capa.flags |=
++                                              WPA_DRIVER_FLAGS_ACS_OFFLOAD;
++                                      break;
++                              case QCA_NL80211_VENDOR_SUBCMD_SETBAND:
++                                      drv->setband_vendor_cmd_avail = 1;
++                                      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;
 +
 +      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;
++      struct wpa_driver_capa *capa;
 +};
 +
 +
 +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);
 +              }
++              attr = tb_vendor[QCA_WLAN_VENDOR_ATTR_CONCURRENCY_CAPA];
++              if (attr)
++                      info->capa->conc_capab = nla_get_u32(attr);
++
++              attr = tb_vendor[
++                      QCA_WLAN_VENDOR_ATTR_MAX_CONCURRENT_CHANNELS_2_4_BAND];
++              if (attr)
++                      info->capa->max_conc_chan_2_4 = nla_get_u32(attr);
++
++              attr = tb_vendor[
++                      QCA_WLAN_VENDOR_ATTR_MAX_CONCURRENT_CHANNELS_5_0_BAND];
++              if (attr)
++                      info->capa->max_conc_chan_5_0 = nla_get_u32(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));
++      info.capa = &drv->capa;
 +      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;
++
++      if (check_feature(QCA_WLAN_VENDOR_FEATURE_SUPPORT_HW_MODE_ANY, &info))
++              drv->capa.flags |= WPA_DRIVER_FLAGS_SUPPORT_HW_MODE_ANY;
 +}
 +
 +
 +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.
 +       */
 +      drv->use_monitor = !info.poll_command_supported || !info.data_tx_status;
 +
 +      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;
 +}
index 87e412dc596a56d4018a1de37f7ae50c8971b81b,0000000000000000000000000000000000000000..7b0f721e65842aac3bcfb5de58aab670a7153989
mode 100644,000000..100644
--- /dev/null
@@@ -1,2029 -1,0 +1,2092 @@@
 +/*
 + * 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"
 +
 +
 +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;
 +      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;
 +
 +      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;
++      const u8 *ssid;
 +      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 (cmd == NL80211_CMD_ROAM) {
++                      ssid = nl80211_get_ie(event.assoc_info.req_ies,
++                                            event.assoc_info.req_ies_len,
++                                            WLAN_EID_SSID);
++                      if (ssid && ssid[1] > 0 && ssid[1] <= 32) {
++                              drv->ssid_len = ssid[1];
++                              os_memcpy(drv->ssid, ssid + 2, ssid[1]);
++                      }
++              }
 +      }
 +      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);
 +}
 +
 +
 +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;
 +      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);
 +              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");
 +              }
 +              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));
 +      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;
 +      }
 +      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 enum hostapd_hw_mode get_qca_hw_mode(u8 hw_mode)
++{
++      switch (hw_mode) {
++      case QCA_ACS_MODE_IEEE80211B:
++              return HOSTAPD_MODE_IEEE80211B;
++      case QCA_ACS_MODE_IEEE80211G:
++              return HOSTAPD_MODE_IEEE80211G;
++      case QCA_ACS_MODE_IEEE80211A:
++              return HOSTAPD_MODE_IEEE80211A;
++      case QCA_ACS_MODE_IEEE80211AD:
++              return HOSTAPD_MODE_IEEE80211AD;
++      case QCA_ACS_MODE_IEEE80211ANY:
++              return HOSTAPD_MODE_IEEE80211ANY;
++      default:
++              return NUM_HOSTAPD_MODES;
++      }
++}
++
++
 +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]);
++      if (tb[QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG0_CENTER_CHANNEL])
++              event.acs_selected_channels.vht_seg0_center_ch =
++                      nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG0_CENTER_CHANNEL]);
++      if (tb[QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG0_CENTER_CHANNEL])
++              event.acs_selected_channels.vht_seg1_center_ch =
++                      nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG1_CENTER_CHANNEL]);
++      if (tb[QCA_WLAN_VENDOR_ATTR_ACS_CHWIDTH])
++              event.acs_selected_channels.ch_width =
++                      nla_get_u16(tb[QCA_WLAN_VENDOR_ATTR_ACS_CHWIDTH]);
++      if (tb[QCA_WLAN_VENDOR_ATTR_ACS_HW_MODE]) {
++              u8 hw_mode = nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_ACS_HW_MODE]);
++
++              event.acs_selected_channels.hw_mode = get_qca_hw_mode(hw_mode);
++              if (event.acs_selected_channels.hw_mode == NUM_HOSTAPD_MODES ||
++                  event.acs_selected_channels.hw_mode ==
++                  HOSTAPD_MODE_IEEE80211ANY) {
++                      wpa_printf(MSG_DEBUG,
++                                 "nl80211: Invalid hw_mode %d in ACS selection event",
++                                 hw_mode);
++                      return;
++              }
++      }
++
++      wpa_printf(MSG_INFO,
++                 "nl80211: ACS Results: PCH: %d SCH: %d BW: %d VHT0: %d VHT1: %d HW_MODE: %d",
++                 event.acs_selected_channels.pri_channel,
++                 event.acs_selected_channels.sec_channel,
++                 event.acs_selected_channels.ch_width,
++                 event.acs_selected_channels.vht_seg0_center_ch,
++                 event.acs_selected_channels.vht_seg1_center_ch,
++                 event.acs_selected_channels.hw_mode);
++
++      /* Ignore ACS channel list check for backwards compatibility */
 +
 +      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;
 +}
index 3911f485f72e436ca8557190d0a920ddf5b17726,0000000000000000000000000000000000000000..4b762eafbe8aebcb05cab46d680a94653da999c2
mode 100644,000000..100644
--- /dev/null
@@@ -1,775 -1,0 +1,783 @@@
- static const u8 * nl80211_get_ie(const u8 *ies, size_t ies_len, u8 ie)
 +/*
 + * 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;
 +
++      if (TEST_FAIL())
++              return -1;
++
 +      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;
 +}
 +
 +
++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_BEACON_TSF]) {
++              u64 tsf = nla_get_u64(bss[NL80211_BSS_BEACON_TSF]);
++              if (tsf > r->tsf)
++                      r->tsf = 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 de23fbd2b9fe11c47942fa2195d7502c190525bb,0000000000000000000000000000000000000000..1f1676a20ac5868f68b4e13f66a360da53c70593
mode 100644,000000..100644
--- /dev/null
@@@ -1,749 -1,0 +1,837 @@@
-       u8 reply[sizeof(int) + 32];
 +/*
 + * WPA Supplicant - privilege separated driver interface
 + * Copyright (c) 2007-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 <sys/un.h>
 +
 +#include "common.h"
 +#include "driver.h"
 +#include "eloop.h"
 +#include "common/privsep_commands.h"
 +
 +
 +struct wpa_driver_privsep_data {
 +      void *ctx;
 +      u8 own_addr[ETH_ALEN];
 +      int priv_socket;
 +      char *own_socket_path;
 +      int cmd_socket;
 +      char *own_cmd_path;
 +      struct sockaddr_un priv_addr;
 +      char ifname[16];
 +};
 +
 +
 +static int wpa_priv_reg_cmd(struct wpa_driver_privsep_data *drv, int cmd)
 +{
 +      int res;
 +
 +      res = sendto(drv->priv_socket, &cmd, sizeof(cmd), 0,
 +                   (struct sockaddr *) &drv->priv_addr,
 +                   sizeof(drv->priv_addr));
 +      if (res < 0)
 +              wpa_printf(MSG_ERROR, "sendto: %s", strerror(errno));
 +      return res < 0 ? -1 : 0;
 +}
 +
 +
 +static int wpa_priv_cmd(struct wpa_driver_privsep_data *drv, int cmd,
 +                      const void *data, size_t data_len,
 +                      void *reply, size_t *reply_len)
 +{
 +      struct msghdr msg;
 +      struct iovec io[2];
 +
 +      io[0].iov_base = &cmd;
 +      io[0].iov_len = sizeof(cmd);
 +      io[1].iov_base = (u8 *) data;
 +      io[1].iov_len = data_len;
 +
 +      os_memset(&msg, 0, sizeof(msg));
 +      msg.msg_iov = io;
 +      msg.msg_iovlen = data ? 2 : 1;
 +      msg.msg_name = &drv->priv_addr;
 +      msg.msg_namelen = sizeof(drv->priv_addr);
 +
 +      if (sendmsg(drv->cmd_socket, &msg, 0) < 0) {
 +              wpa_printf(MSG_ERROR, "sendmsg(cmd_socket): %s",
 +                         strerror(errno));
 +              return -1;
 +      }
 +
 +      if (reply) {
 +              fd_set rfds;
 +              struct timeval tv;
 +              int res;
 +
 +              FD_ZERO(&rfds);
 +              FD_SET(drv->cmd_socket, &rfds);
 +              tv.tv_sec = 5;
 +              tv.tv_usec = 0;
 +              res = select(drv->cmd_socket + 1, &rfds, NULL, NULL, &tv);
 +              if (res < 0 && errno != EINTR) {
 +                      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) {
 +                              wpa_printf(MSG_ERROR, "recv: %s",
 +                                         strerror(errno));
 +                              return -1;
 +                      }
 +                      *reply_len = res;
 +              } else {
 +                      wpa_printf(MSG_DEBUG, "PRIVSEP: Timeout while waiting "
 +                                 "for reply (cmd=%d)", cmd);
 +                      return -1;
 +              }
 +      }
 +
 +      return 0;
 +}
 +
 +                           
 +static int wpa_driver_privsep_scan(void *priv,
 +                                 struct wpa_driver_scan_params *params)
 +{
 +      struct wpa_driver_privsep_data *drv = priv;
 +      const u8 *ssid = params->ssids[0].ssid;
 +      size_t ssid_len = params->ssids[0].ssid_len;
 +      wpa_printf(MSG_DEBUG, "%s: priv=%p", __func__, priv);
 +      return wpa_priv_cmd(drv, PRIVSEP_CMD_SCAN, ssid, ssid_len,
 +                          NULL, NULL);
 +}
 +
 +
 +static struct wpa_scan_results *
 +wpa_driver_privsep_get_scan_results2(void *priv)
 +{
 +      struct wpa_driver_privsep_data *drv = priv;
 +      int res, num;
 +      u8 *buf, *pos, *end;
 +      size_t reply_len = 60000;
 +      struct wpa_scan_results *results;
 +      struct wpa_scan_res *r;
 +
 +      buf = os_malloc(reply_len);
 +      if (buf == NULL)
 +              return NULL;
 +      res = wpa_priv_cmd(drv, PRIVSEP_CMD_GET_SCAN_RESULTS,
 +                         NULL, 0, buf, &reply_len);
 +      if (res < 0) {
 +              os_free(buf);
 +              return NULL;
 +      }
 +
 +      wpa_printf(MSG_DEBUG, "privsep: Received %lu bytes of scan results",
 +                 (unsigned long) reply_len);
 +      if (reply_len < sizeof(int)) {
 +              wpa_printf(MSG_DEBUG, "privsep: Invalid scan result len %lu",
 +                         (unsigned long) reply_len);
 +              os_free(buf);
 +              return NULL;
 +      }
 +
 +      pos = buf;
 +      end = buf + reply_len;
 +      os_memcpy(&num, pos, sizeof(int));
 +      if (num < 0 || num > 1000) {
 +              os_free(buf);
 +              return NULL;
 +      }
 +      pos += sizeof(int);
 +
 +      results = os_zalloc(sizeof(*results));
 +      if (results == NULL) {
 +              os_free(buf);
 +              return NULL;
 +      }
 +
 +      results->res = os_calloc(num, sizeof(struct wpa_scan_res *));
 +      if (results->res == NULL) {
 +              os_free(results);
 +              os_free(buf);
 +              return NULL;
 +      }
 +
 +      while (results->num < (size_t) num && pos + sizeof(int) < end) {
 +              int len;
 +              os_memcpy(&len, pos, sizeof(int));
 +              pos += sizeof(int);
 +              if (len < 0 || len > 10000 || pos + len > end)
 +                      break;
 +
 +              r = os_malloc(len);
 +              if (r == NULL)
 +                      break;
 +              os_memcpy(r, pos, len);
 +              pos += len;
 +              if (sizeof(*r) + r->ie_len > (size_t) len) {
 +                      os_free(r);
 +                      break;
 +              }
 +
 +              results->res[results->num++] = r;
 +      }
 +
 +      os_free(buf);
 +      return results;
 +}
 +
 +
 +static int wpa_driver_privsep_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 wpa_driver_privsep_data *drv = priv;
 +      struct privsep_cmd_set_key cmd;
 +
 +      wpa_printf(MSG_DEBUG, "%s: priv=%p alg=%d key_idx=%d set_tx=%d",
 +                 __func__, priv, alg, key_idx, set_tx);
 +
 +      os_memset(&cmd, 0, sizeof(cmd));
 +      cmd.alg = alg;
 +      if (addr)
 +              os_memcpy(cmd.addr, addr, ETH_ALEN);
 +      else
 +              os_memset(cmd.addr, 0xff, ETH_ALEN);
 +      cmd.key_idx = key_idx;
 +      cmd.set_tx = set_tx;
 +      if (seq && seq_len > 0 && seq_len < sizeof(cmd.seq)) {
 +              os_memcpy(cmd.seq, seq, seq_len);
 +              cmd.seq_len = seq_len;
 +      }
 +      if (key && key_len > 0 && key_len < sizeof(cmd.key)) {
 +              os_memcpy(cmd.key, key, key_len);
 +              cmd.key_len = key_len;
 +      }
 +
 +      return wpa_priv_cmd(drv, PRIVSEP_CMD_SET_KEY, &cmd, sizeof(cmd),
 +                          NULL, NULL);
 +}
 +
 +
++static int wpa_driver_privsep_authenticate(
++      void *priv, struct wpa_driver_auth_params *params)
++{
++      struct wpa_driver_privsep_data *drv = priv;
++      struct privsep_cmd_authenticate *data;
++      int i, res;
++      size_t buflen;
++      u8 *pos;
++
++      wpa_printf(MSG_DEBUG, "%s: priv=%p freq=%d bssid=" MACSTR
++                 " auth_alg=%d local_state_change=%d p2p=%d",
++                 __func__, priv, params->freq, MAC2STR(params->bssid),
++                 params->auth_alg, params->local_state_change, params->p2p);
++
++      buflen = sizeof(*data) + params->ie_len + params->sae_data_len;
++      data = os_zalloc(buflen);
++      if (data == NULL)
++              return -1;
++
++      data->freq = params->freq;
++      os_memcpy(data->bssid, params->bssid, ETH_ALEN);
++      os_memcpy(data->ssid, params->ssid, params->ssid_len);
++      data->ssid_len = params->ssid_len;
++      data->auth_alg = params->auth_alg;
++      data->ie_len = params->ie_len;
++      for (i = 0; i < 4; i++) {
++              if (params->wep_key[i])
++                      os_memcpy(data->wep_key[i], params->wep_key[i],
++                                params->wep_key_len[i]);
++              data->wep_key_len[i] = params->wep_key_len[i];
++      }
++      data->wep_tx_keyidx = params->wep_tx_keyidx;
++      data->local_state_change = params->local_state_change;
++      data->p2p = params->p2p;
++      pos = (u8 *) (data + 1);
++      if (params->ie_len) {
++              os_memcpy(pos, params->ie, params->ie_len);
++              pos += params->ie_len;
++      }
++      if (params->sae_data_len)
++              os_memcpy(pos, params->sae_data, params->sae_data_len);
++
++      res = wpa_priv_cmd(drv, PRIVSEP_CMD_AUTHENTICATE, data, buflen,
++                         NULL, NULL);
++      os_free(data);
++
++      return res;
++}
++
++
 +static int wpa_driver_privsep_associate(
 +      void *priv, struct wpa_driver_associate_params *params)
 +{
 +      struct wpa_driver_privsep_data *drv = priv;
 +      struct privsep_cmd_associate *data;
 +      int res;
 +      size_t buflen;
 +
 +      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.freq, params->pairwise_suite,
 +                 params->group_suite, params->key_mgmt_suite,
 +                 params->auth_alg, params->mode);
 +
 +      buflen = sizeof(*data) + params->wpa_ie_len;
 +      data = os_zalloc(buflen);
 +      if (data == NULL)
 +              return -1;
 +
 +      if (params->bssid)
 +              os_memcpy(data->bssid, params->bssid, ETH_ALEN);
 +      os_memcpy(data->ssid, params->ssid, params->ssid_len);
 +      data->ssid_len = params->ssid_len;
 +      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;
 +      data->auth_alg = params->auth_alg;
 +      data->mode = params->mode;
 +      data->wpa_ie_len = params->wpa_ie_len;
 +      if (params->wpa_ie)
 +              os_memcpy(data + 1, params->wpa_ie, params->wpa_ie_len);
 +      /* TODO: add support for other assoc parameters */
 +
 +      res = wpa_priv_cmd(drv, PRIVSEP_CMD_ASSOCIATE, data, buflen,
 +                         NULL, NULL);
 +      os_free(data);
 +
 +      return res;
 +}
 +
 +
 +static int wpa_driver_privsep_get_bssid(void *priv, u8 *bssid)
 +{
 +      struct wpa_driver_privsep_data *drv = priv;
 +      int res;
 +      size_t len = ETH_ALEN;
 +
 +      res = wpa_priv_cmd(drv, PRIVSEP_CMD_GET_BSSID, NULL, 0, bssid, &len);
 +      if (res < 0 || len != ETH_ALEN)
 +              return -1;
 +      return 0;
 +}
 +
 +
 +static int wpa_driver_privsep_get_ssid(void *priv, u8 *ssid)
 +{
 +      struct wpa_driver_privsep_data *drv = priv;
 +      int res, ssid_len;
-       if (ssid_len < 0 || ssid_len > 32 || sizeof(int) + ssid_len > len) {
++      u8 reply[sizeof(int) + SSID_MAX_LEN];
 +      size_t len = sizeof(reply);
 +
 +      res = wpa_priv_cmd(drv, PRIVSEP_CMD_GET_SSID, NULL, 0, reply, &len);
 +      if (res < 0 || len < sizeof(int))
 +              return -1;
 +      os_memcpy(&ssid_len, reply, sizeof(int));
- struct wpa_driver_ops *wpa_drivers[] =
++      if (ssid_len < 0 || ssid_len > SSID_MAX_LEN ||
++          sizeof(int) + ssid_len > len) {
 +              wpa_printf(MSG_DEBUG, "privsep: Invalid get SSID reply");
 +              return -1;
 +      }
 +      os_memcpy(ssid, &reply[sizeof(int)], ssid_len);
 +      return ssid_len;
 +}
 +
 +
 +static int wpa_driver_privsep_deauthenticate(void *priv, const u8 *addr,
 +                                        int reason_code)
 +{
 +      //struct wpa_driver_privsep_data *drv = priv;
 +      wpa_printf(MSG_DEBUG, "%s addr=" MACSTR " reason_code=%d",
 +                 __func__, MAC2STR(addr), reason_code);
 +      wpa_printf(MSG_DEBUG, "%s - TODO", __func__);
 +      return 0;
 +}
 +
 +
++static void wpa_driver_privsep_event_auth(void *ctx, u8 *buf, size_t len)
++{
++      union wpa_event_data data;
++      struct privsep_event_auth *auth;
++
++      os_memset(&data, 0, sizeof(data));
++      if (len < sizeof(*auth))
++              return;
++      auth = (struct privsep_event_auth *) buf;
++      if (len < sizeof(*auth) + auth->ies_len)
++              return;
++
++      os_memcpy(data.auth.peer, auth->peer, ETH_ALEN);
++      os_memcpy(data.auth.bssid, auth->bssid, ETH_ALEN);
++      data.auth.auth_type = auth->auth_type;
++      data.auth.auth_transaction = auth->auth_transaction;
++      data.auth.status_code = auth->status_code;
++      if (auth->ies_len) {
++              data.auth.ies = (u8 *) (auth + 1);
++              data.auth.ies_len = auth->ies_len;
++      }
++
++      wpa_supplicant_event(ctx, EVENT_AUTH, &data);
++}
++
++
 +static void wpa_driver_privsep_event_assoc(void *ctx,
 +                                         enum wpa_event_type event,
 +                                         u8 *buf, size_t len)
 +{
 +      union wpa_event_data data;
 +      int inc_data = 0;
 +      u8 *pos, *end;
 +      int ie_len;
 +
 +      os_memset(&data, 0, sizeof(data));
 +
 +      pos = buf;
 +      end = buf + len;
 +
 +      if (end - pos < (int) sizeof(int))
 +              return;
 +      os_memcpy(&ie_len, pos, sizeof(int));
 +      pos += sizeof(int);
 +      if (ie_len < 0 || ie_len > end - pos)
 +              return;
 +      if (ie_len) {
 +              data.assoc_info.req_ies = pos;
 +              data.assoc_info.req_ies_len = ie_len;
 +              pos += ie_len;
 +              inc_data = 1;
 +      }
 +
 +      wpa_supplicant_event(ctx, event, inc_data ? &data : NULL);
 +}
 +
 +
 +static void wpa_driver_privsep_event_interface_status(void *ctx, u8 *buf,
 +                                                    size_t len)
 +{
 +      union wpa_event_data data;
 +      int ievent;
 +
 +      if (len < sizeof(int) ||
 +          len - sizeof(int) > sizeof(data.interface_status.ifname))
 +              return;
 +
 +      os_memcpy(&ievent, buf, sizeof(int));
 +
 +      os_memset(&data, 0, sizeof(data));
 +      data.interface_status.ievent = ievent;
 +      os_memcpy(data.interface_status.ifname, buf + sizeof(int),
 +                len - sizeof(int));
 +      wpa_supplicant_event(ctx, EVENT_INTERFACE_STATUS, &data);
 +}
 +
 +
 +static void wpa_driver_privsep_event_michael_mic_failure(
 +      void *ctx, u8 *buf, size_t len)
 +{
 +      union wpa_event_data data;
 +
 +      if (len != sizeof(int))
 +              return;
 +
 +      os_memset(&data, 0, sizeof(data));
 +      os_memcpy(&data.michael_mic_failure.unicast, buf, sizeof(int));
 +      wpa_supplicant_event(ctx, EVENT_MICHAEL_MIC_FAILURE, &data);
 +}
 +
 +
 +static void wpa_driver_privsep_event_pmkid_candidate(void *ctx, u8 *buf,
 +                                                   size_t len)
 +{
 +      union wpa_event_data data;
 +
 +      if (len != sizeof(struct pmkid_candidate))
 +              return;
 +
 +      os_memset(&data, 0, sizeof(data));
 +      os_memcpy(&data.pmkid_candidate, buf, len);
 +      wpa_supplicant_event(ctx, EVENT_PMKID_CANDIDATE, &data);
 +}
 +
 +
 +static void wpa_driver_privsep_event_stkstart(void *ctx, u8 *buf, size_t len)
 +{
 +      union wpa_event_data data;
 +
 +      if (len != ETH_ALEN)
 +              return;
 +
 +      os_memset(&data, 0, sizeof(data));
 +      os_memcpy(data.stkstart.peer, buf, ETH_ALEN);
 +      wpa_supplicant_event(ctx, EVENT_STKSTART, &data);
 +}
 +
 +
 +static void wpa_driver_privsep_event_ft_response(void *ctx, u8 *buf,
 +                                               size_t len)
 +{
 +      union wpa_event_data data;
 +
 +      if (len < sizeof(int) + ETH_ALEN)
 +              return;
 +
 +      os_memset(&data, 0, sizeof(data));
 +      os_memcpy(&data.ft_ies.ft_action, buf, sizeof(int));
 +      os_memcpy(data.ft_ies.target_ap, buf + sizeof(int), ETH_ALEN);
 +      data.ft_ies.ies = buf + sizeof(int) + ETH_ALEN;
 +      data.ft_ies.ies_len = len - sizeof(int) - ETH_ALEN;
 +      wpa_supplicant_event(ctx, EVENT_FT_RESPONSE, &data);
 +}
 +
 +
 +static void wpa_driver_privsep_event_rx_eapol(void *ctx, u8 *buf, size_t len)
 +{
 +      if (len < ETH_ALEN)
 +              return;
 +      drv_event_eapol_rx(ctx, buf, buf + ETH_ALEN, len - ETH_ALEN);
 +}
 +
 +
 +static void wpa_driver_privsep_receive(int sock, void *eloop_ctx,
 +                                     void *sock_ctx)
 +{
 +      struct wpa_driver_privsep_data *drv = eloop_ctx;
 +      u8 *buf, *event_buf;
 +      size_t event_len;
 +      int res, event;
 +      enum privsep_event e;
 +      struct sockaddr_un from;
 +      socklen_t fromlen = sizeof(from);
 +      const size_t buflen = 2000;
 +
 +      buf = os_malloc(buflen);
 +      if (buf == NULL)
 +              return;
 +      res = recvfrom(sock, buf, buflen, 0,
 +                     (struct sockaddr *) &from, &fromlen);
 +      if (res < 0) {
 +              wpa_printf(MSG_ERROR, "recvfrom(priv_socket): %s",
 +                         strerror(errno));
 +              os_free(buf);
 +              return;
 +      }
 +
 +      wpa_printf(MSG_DEBUG, "privsep_driver: received %u bytes", res);
 +
 +      if (res < (int) sizeof(int)) {
 +              wpa_printf(MSG_DEBUG, "Too short event message (len=%d)", res);
 +              return;
 +      }
 +
 +      os_memcpy(&event, buf, sizeof(int));
 +      event_buf = &buf[sizeof(int)];
 +      event_len = res - sizeof(int);
 +      wpa_printf(MSG_DEBUG, "privsep: Event %d received (len=%lu)",
 +                 event, (unsigned long) event_len);
 +
 +      e = event;
 +      switch (e) {
 +      case PRIVSEP_EVENT_SCAN_RESULTS:
 +              wpa_supplicant_event(drv->ctx, EVENT_SCAN_RESULTS, NULL);
 +              break;
++      case PRIVSEP_EVENT_SCAN_STARTED:
++              wpa_supplicant_event(drv->ctx, EVENT_SCAN_STARTED, NULL);
++              break;
 +      case PRIVSEP_EVENT_ASSOC:
 +              wpa_driver_privsep_event_assoc(drv->ctx, EVENT_ASSOC,
 +                                             event_buf, event_len);
 +              break;
 +      case PRIVSEP_EVENT_DISASSOC:
 +              wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, NULL);
 +              break;
 +      case PRIVSEP_EVENT_ASSOCINFO:
 +              wpa_driver_privsep_event_assoc(drv->ctx, EVENT_ASSOCINFO,
 +                                             event_buf, event_len);
 +              break;
 +      case PRIVSEP_EVENT_MICHAEL_MIC_FAILURE:
 +              wpa_driver_privsep_event_michael_mic_failure(
 +                      drv->ctx, event_buf, event_len);
 +              break;
 +      case PRIVSEP_EVENT_INTERFACE_STATUS:
 +              wpa_driver_privsep_event_interface_status(drv->ctx, event_buf,
 +                                                        event_len);
 +              break;
 +      case PRIVSEP_EVENT_PMKID_CANDIDATE:
 +              wpa_driver_privsep_event_pmkid_candidate(drv->ctx, event_buf,
 +                                                       event_len);
 +              break;
 +      case PRIVSEP_EVENT_STKSTART:
 +              wpa_driver_privsep_event_stkstart(drv->ctx, event_buf,
 +                                                event_len);
 +              break;
 +      case PRIVSEP_EVENT_FT_RESPONSE:
 +              wpa_driver_privsep_event_ft_response(drv->ctx, event_buf,
 +                                                   event_len);
 +              break;
 +      case PRIVSEP_EVENT_RX_EAPOL:
 +              wpa_driver_privsep_event_rx_eapol(drv->ctx, event_buf,
 +                                                event_len);
 +              break;
++      case PRIVSEP_EVENT_AUTH:
++              wpa_driver_privsep_event_auth(drv->ctx, event_buf, event_len);
++              break;
 +      }
 +
 +      os_free(buf);
 +}
 +
 +
 +static void * wpa_driver_privsep_init(void *ctx, const char *ifname)
 +{
 +      struct wpa_driver_privsep_data *drv;
 +
 +      drv = os_zalloc(sizeof(*drv));
 +      if (drv == NULL)
 +              return NULL;
 +      drv->ctx = ctx;
 +      drv->priv_socket = -1;
 +      drv->cmd_socket = -1;
 +      os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname));
 +
 +      return drv;
 +}
 +
 +
 +static void wpa_driver_privsep_deinit(void *priv)
 +{
 +      struct wpa_driver_privsep_data *drv = priv;
 +
 +      if (drv->priv_socket >= 0) {
 +              wpa_priv_reg_cmd(drv, PRIVSEP_CMD_UNREGISTER);
 +              eloop_unregister_read_sock(drv->priv_socket);
 +              close(drv->priv_socket);
 +      }
 +
 +      if (drv->own_socket_path) {
 +              unlink(drv->own_socket_path);
 +              os_free(drv->own_socket_path);
 +      }
 +
 +      if (drv->cmd_socket >= 0) {
 +              eloop_unregister_read_sock(drv->cmd_socket);
 +              close(drv->cmd_socket);
 +      }
 +
 +      if (drv->own_cmd_path) {
 +              unlink(drv->own_cmd_path);
 +              os_free(drv->own_cmd_path);
 +      }
 +
 +      os_free(drv);
 +}
 +
 +
 +static int wpa_driver_privsep_set_param(void *priv, const char *param)
 +{
 +      struct wpa_driver_privsep_data *drv = priv;
 +      const char *pos;
 +      char *own_dir, *priv_dir;
 +      static unsigned int counter = 0;
 +      size_t len;
 +      struct sockaddr_un addr;
 +
 +      wpa_printf(MSG_DEBUG, "%s: param='%s'", __func__, param);
 +      if (param == NULL)
 +              pos = NULL;
 +      else
 +              pos = os_strstr(param, "own_dir=");
 +      if (pos) {
 +              char *end;
 +              own_dir = os_strdup(pos + 8);
 +              if (own_dir == NULL)
 +                      return -1;
 +              end = os_strchr(own_dir, ' ');
 +              if (end)
 +                      *end = '\0';
 +      } else {
 +              own_dir = os_strdup("/tmp");
 +              if (own_dir == NULL)
 +                      return -1;
 +      }
 +
 +      if (param == NULL)
 +              pos = NULL;
 +      else
 +              pos = os_strstr(param, "priv_dir=");
 +      if (pos) {
 +              char *end;
 +              priv_dir = os_strdup(pos + 9);
 +              if (priv_dir == NULL) {
 +                      os_free(own_dir);
 +                      return -1;
 +              }
 +              end = os_strchr(priv_dir, ' ');
 +              if (end)
 +                      *end = '\0';
 +      } else {
 +              priv_dir = os_strdup("/var/run/wpa_priv");
 +              if (priv_dir == NULL) {
 +                      os_free(own_dir);
 +                      return -1;
 +              }
 +      }
 +
 +      len = os_strlen(own_dir) + 50;
 +      drv->own_socket_path = os_malloc(len);
 +      if (drv->own_socket_path == NULL) {
 +              os_free(priv_dir);
 +              os_free(own_dir);
 +              return -1;
 +      }
 +      os_snprintf(drv->own_socket_path, len, "%s/wpa_privsep-%d-%d",
 +                  own_dir, getpid(), counter++);
 +
 +      len = os_strlen(own_dir) + 50;
 +      drv->own_cmd_path = os_malloc(len);
 +      if (drv->own_cmd_path == NULL) {
 +              os_free(drv->own_socket_path);
 +              drv->own_socket_path = NULL;
 +              os_free(priv_dir);
 +              os_free(own_dir);
 +              return -1;
 +      }
 +      os_snprintf(drv->own_cmd_path, len, "%s/wpa_privsep-%d-%d",
 +                  own_dir, getpid(), counter++);
 +
 +      os_free(own_dir);
 +
 +      drv->priv_addr.sun_family = AF_UNIX;
 +      os_snprintf(drv->priv_addr.sun_path, sizeof(drv->priv_addr.sun_path),
 +                  "%s/%s", priv_dir, drv->ifname);
 +      os_free(priv_dir);
 +
 +      drv->priv_socket = socket(PF_UNIX, SOCK_DGRAM, 0);
 +      if (drv->priv_socket < 0) {
 +              wpa_printf(MSG_ERROR, "socket(PF_UNIX): %s", strerror(errno));
 +              os_free(drv->own_socket_path);
 +              drv->own_socket_path = NULL;
 +              return -1;
 +      }
 +
 +      os_memset(&addr, 0, sizeof(addr));
 +      addr.sun_family = AF_UNIX;
 +      os_strlcpy(addr.sun_path, drv->own_socket_path, sizeof(addr.sun_path));
 +      if (bind(drv->priv_socket, (struct sockaddr *) &addr, sizeof(addr)) <
 +          0) {
 +              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);
 +              os_free(drv->own_socket_path);
 +              drv->own_socket_path = NULL;
 +              return -1;
 +      }
 +
 +      eloop_register_read_sock(drv->priv_socket, wpa_driver_privsep_receive,
 +                               drv, NULL);
 +
 +      drv->cmd_socket = socket(PF_UNIX, SOCK_DGRAM, 0);
 +      if (drv->cmd_socket < 0) {
 +              wpa_printf(MSG_ERROR, "socket(PF_UNIX): %s", strerror(errno));
 +              os_free(drv->own_cmd_path);
 +              drv->own_cmd_path = NULL;
 +              return -1;
 +      }
 +
 +      os_memset(&addr, 0, sizeof(addr));
 +      addr.sun_family = AF_UNIX;
 +      os_strlcpy(addr.sun_path, drv->own_cmd_path, sizeof(addr.sun_path));
 +      if (bind(drv->cmd_socket, (struct sockaddr *) &addr, sizeof(addr)) < 0)
 +      {
 +              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);
 +              os_free(drv->own_cmd_path);
 +              drv->own_cmd_path = NULL;
 +              return -1;
 +      }
 +
 +      if (wpa_priv_reg_cmd(drv, PRIVSEP_CMD_REGISTER) < 0) {
 +              wpa_printf(MSG_ERROR, "Failed to register with wpa_priv");
 +              return -1;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +static int wpa_driver_privsep_get_capa(void *priv,
 +                                     struct wpa_driver_capa *capa)
 +{
 +      struct wpa_driver_privsep_data *drv = priv;
 +      int res;
 +      size_t len = sizeof(*capa);
 +
 +      res = wpa_priv_cmd(drv, PRIVSEP_CMD_GET_CAPA, NULL, 0, capa, &len);
 +      if (res < 0 || len != sizeof(*capa))
 +              return -1;
++      /* For now, no support for passing extended_capa pointers */
++      capa->extended_capa = NULL;
++      capa->extended_capa_mask = NULL;
++      capa->extended_capa_len = 0;
 +      return 0;
 +}
 +
 +
 +static const u8 * wpa_driver_privsep_get_mac_addr(void *priv)
 +{
 +      struct wpa_driver_privsep_data *drv = priv;
 +      wpa_printf(MSG_DEBUG, "%s", __func__);
 +      return drv->own_addr;
 +}
 +
 +
 +static int wpa_driver_privsep_set_country(void *priv, const char *alpha2)
 +{
 +      struct wpa_driver_privsep_data *drv = priv;
 +      wpa_printf(MSG_DEBUG, "%s country='%s'", __func__, alpha2);
 +      return wpa_priv_cmd(drv, PRIVSEP_CMD_SET_COUNTRY, alpha2,
 +                          os_strlen(alpha2), NULL, NULL);
 +}
 +
 +
 +struct wpa_driver_ops wpa_driver_privsep_ops = {
 +      "privsep",
 +      "wpa_supplicant privilege separated driver",
 +      .get_bssid = wpa_driver_privsep_get_bssid,
 +      .get_ssid = wpa_driver_privsep_get_ssid,
 +      .set_key = wpa_driver_privsep_set_key,
 +      .init = wpa_driver_privsep_init,
 +      .deinit = wpa_driver_privsep_deinit,
 +      .set_param = wpa_driver_privsep_set_param,
 +      .scan2 = wpa_driver_privsep_scan,
 +      .deauthenticate = wpa_driver_privsep_deauthenticate,
++      .authenticate = wpa_driver_privsep_authenticate,
 +      .associate = wpa_driver_privsep_associate,
 +      .get_capa = wpa_driver_privsep_get_capa,
 +      .get_mac_addr = wpa_driver_privsep_get_mac_addr,
 +      .get_scan_results2 = wpa_driver_privsep_get_scan_results2,
 +      .set_country = wpa_driver_privsep_set_country,
 +};
 +
 +
++const struct wpa_driver_ops *const wpa_drivers[] =
 +{
 +      &wpa_driver_privsep_ops,
 +      NULL
 +};
index f0c3bb3c63ba002cf5dc25c2b706fbafd9652ece,0000000000000000000000000000000000000000..a98af9ac7d711a33e1c5904dfb76d938dc673993
mode 100644,000000..100644
--- /dev/null
@@@ -1,86 -1,0 +1,86 @@@
- struct wpa_driver_ops *wpa_drivers[] =
 +/*
 + * Driver interface list
 + * Copyright (c) 2004-2005, Jouni Malinen <j@w1.fi>
 + *
 + * This software may be distributed under the terms of the BSD license.
 + * See README for more details.
 + */
 +
 +#include "utils/includes.h"
 +#include "utils/common.h"
 +#include "driver.h"
 +
 +#ifdef CONFIG_DRIVER_WEXT
 +extern struct wpa_driver_ops wpa_driver_wext_ops; /* driver_wext.c */
 +#endif /* CONFIG_DRIVER_WEXT */
 +#ifdef CONFIG_DRIVER_NL80211
 +extern struct wpa_driver_ops wpa_driver_nl80211_ops; /* driver_nl80211.c */
 +#endif /* CONFIG_DRIVER_NL80211 */
 +#ifdef CONFIG_DRIVER_HOSTAP
 +extern struct wpa_driver_ops wpa_driver_hostap_ops; /* driver_hostap.c */
 +#endif /* CONFIG_DRIVER_HOSTAP */
 +#ifdef CONFIG_DRIVER_BSD
 +extern struct wpa_driver_ops wpa_driver_bsd_ops; /* driver_bsd.c */
 +#endif /* CONFIG_DRIVER_BSD */
 +#ifdef CONFIG_DRIVER_OPENBSD
 +extern struct wpa_driver_ops wpa_driver_openbsd_ops; /* driver_openbsd.c */
 +#endif /* CONFIG_DRIVER_OPENBSD */
 +#ifdef CONFIG_DRIVER_NDIS
 +extern struct wpa_driver_ops wpa_driver_ndis_ops; /* driver_ndis.c */
 +#endif /* CONFIG_DRIVER_NDIS */
 +#ifdef CONFIG_DRIVER_WIRED
 +extern struct wpa_driver_ops wpa_driver_wired_ops; /* driver_wired.c */
 +#endif /* CONFIG_DRIVER_WIRED */
 +#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;
 +#endif /* CONFIG_DRIVER_ROBOSWITCH */
 +#ifdef CONFIG_DRIVER_ATHEROS
 +extern struct wpa_driver_ops wpa_driver_atheros_ops; /* driver_atheros.c */
 +#endif /* CONFIG_DRIVER_ATHEROS */
 +#ifdef CONFIG_DRIVER_NONE
 +extern struct wpa_driver_ops wpa_driver_none_ops; /* driver_none.c */
 +#endif /* CONFIG_DRIVER_NONE */
 +
 +
++const struct wpa_driver_ops *const wpa_drivers[] =
 +{
 +#ifdef CONFIG_DRIVER_NL80211
 +      &wpa_driver_nl80211_ops,
 +#endif /* CONFIG_DRIVER_NL80211 */
 +#ifdef CONFIG_DRIVER_WEXT
 +      &wpa_driver_wext_ops,
 +#endif /* CONFIG_DRIVER_WEXT */
 +#ifdef CONFIG_DRIVER_HOSTAP
 +      &wpa_driver_hostap_ops,
 +#endif /* CONFIG_DRIVER_HOSTAP */
 +#ifdef CONFIG_DRIVER_BSD
 +      &wpa_driver_bsd_ops,
 +#endif /* CONFIG_DRIVER_BSD */
 +#ifdef CONFIG_DRIVER_OPENBSD
 +      &wpa_driver_openbsd_ops,
 +#endif /* CONFIG_DRIVER_OPENBSD */
 +#ifdef CONFIG_DRIVER_NDIS
 +      &wpa_driver_ndis_ops,
 +#endif /* CONFIG_DRIVER_NDIS */
 +#ifdef CONFIG_DRIVER_WIRED
 +      &wpa_driver_wired_ops,
 +#endif /* CONFIG_DRIVER_WIRED */
 +#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 */
 +#ifdef CONFIG_DRIVER_ATHEROS
 +      &wpa_driver_atheros_ops,
 +#endif /* CONFIG_DRIVER_ATHEROS */
 +#ifdef CONFIG_DRIVER_NONE
 +      &wpa_driver_none_ops,
 +#endif /* CONFIG_DRIVER_NONE */
 +      NULL
 +};
index 1de13281c5115fe807f01c70a290b02f2970f42b,0000000000000000000000000000000000000000..51a15d75bc938b1d403168c57e13254f83f86ba5
mode 100644,000000..100644
--- /dev/null
@@@ -1,288 -1,0 +1,288 @@@
-  * eap_get_id - Get EAP Type from wpabuf
 +/*
 + * EAP common peer/server definitions
 + * 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 "includes.h"
 +
 +#include "common.h"
 +#include "eap_defs.h"
 +#include "eap_common.h"
 +
 +/**
 + * eap_hdr_len_valid - Validate EAP header length field
 + * @msg: EAP frame (starting with EAP header)
 + * @min_payload: Minimum payload length needed
 + * Returns: 1 for valid header, 0 for invalid
 + *
 + * This is a helper function that does minimal validation of EAP messages. The
 + * length field is verified to be large enough to include the header and not
 + * too large to go beyond the end of the buffer.
 + */
 +int eap_hdr_len_valid(const struct wpabuf *msg, size_t min_payload)
 +{
 +      const struct eap_hdr *hdr;
 +      size_t len;
 +
 +      if (msg == NULL)
 +              return 0;
 +
 +      hdr = wpabuf_head(msg);
 +
 +      if (wpabuf_len(msg) < sizeof(*hdr)) {
 +              wpa_printf(MSG_INFO, "EAP: Too short EAP frame");
 +              return 0;
 +      }
 +
 +      len = be_to_host16(hdr->length);
 +      if (len < sizeof(*hdr) + min_payload || len > wpabuf_len(msg)) {
 +              wpa_printf(MSG_INFO, "EAP: Invalid EAP length");
 +              return 0;
 +      }
 +
 +      return 1;
 +}
 +
 +
 +/**
 + * eap_hdr_validate - Validate EAP header
 + * @vendor: Expected EAP Vendor-Id (0 = IETF)
 + * @eap_type: Expected EAP type number
 + * @msg: EAP frame (starting with EAP header)
 + * @plen: Pointer to variable to contain the returned payload length
 + * Returns: Pointer to EAP payload (after type field), or %NULL on failure
 + *
 + * This is a helper function for EAP method implementations. This is usually
 + * called in the beginning of struct eap_method::process() function to verify
 + * that the received EAP request packet has a valid header. This function is
 + * able to process both legacy and expanded EAP headers and in most cases, the
 + * caller can just use the returned payload pointer (into *plen) for processing
 + * the payload regardless of whether the packet used the expanded EAP header or
 + * not.
 + */
 +const u8 * eap_hdr_validate(int vendor, EapType eap_type,
 +                          const struct wpabuf *msg, size_t *plen)
 +{
 +      const struct eap_hdr *hdr;
 +      const u8 *pos;
 +      size_t len;
 +
 +      if (!eap_hdr_len_valid(msg, 1))
 +              return NULL;
 +
 +      hdr = wpabuf_head(msg);
 +      len = be_to_host16(hdr->length);
 +      pos = (const u8 *) (hdr + 1);
 +
 +      if (*pos == EAP_TYPE_EXPANDED) {
 +              int exp_vendor;
 +              u32 exp_type;
 +              if (len < sizeof(*hdr) + 8) {
 +                      wpa_printf(MSG_INFO, "EAP: Invalid expanded EAP "
 +                                 "length");
 +                      return NULL;
 +              }
 +              pos++;
 +              exp_vendor = WPA_GET_BE24(pos);
 +              pos += 3;
 +              exp_type = WPA_GET_BE32(pos);
 +              pos += 4;
 +              if (exp_vendor != vendor || exp_type != (u32) eap_type) {
 +                      wpa_printf(MSG_INFO, "EAP: Invalid expanded frame "
 +                                 "type");
 +                      return NULL;
 +              }
 +
 +              *plen = len - sizeof(*hdr) - 8;
 +              return pos;
 +      } else {
 +              if (vendor != EAP_VENDOR_IETF || *pos != eap_type) {
 +                      wpa_printf(MSG_INFO, "EAP: Invalid frame type");
 +                      return NULL;
 +              }
 +              *plen = len - sizeof(*hdr) - 1;
 +              return pos + 1;
 +      }
 +}
 +
 +
 +/**
 + * eap_msg_alloc - Allocate a buffer for an EAP message
 + * @vendor: Vendor-Id (0 = IETF)
 + * @type: EAP type
 + * @payload_len: Payload length in bytes (data after Type)
 + * @code: Message Code (EAP_CODE_*)
 + * @identifier: Identifier
 + * Returns: Pointer to the allocated message buffer or %NULL on error
 + *
 + * This function can be used to allocate a buffer for an EAP message and fill
 + * in the EAP header. This function is automatically using expanded EAP header
 + * if the selected Vendor-Id is not IETF. In other words, most EAP methods do
 + * not need to separately select which header type to use when using this
 + * function to allocate the message buffers. The returned buffer has room for
 + * payload_len bytes and has the EAP header and Type field already filled in.
 + */
 +struct wpabuf * eap_msg_alloc(int vendor, EapType type, size_t payload_len,
 +                            u8 code, u8 identifier)
 +{
 +      struct wpabuf *buf;
 +      struct eap_hdr *hdr;
 +      size_t len;
 +
 +      len = sizeof(struct eap_hdr) + (vendor == EAP_VENDOR_IETF ? 1 : 8) +
 +              payload_len;
 +      buf = wpabuf_alloc(len);
 +      if (buf == NULL)
 +              return NULL;
 +
 +      hdr = wpabuf_put(buf, sizeof(*hdr));
 +      hdr->code = code;
 +      hdr->identifier = identifier;
 +      hdr->length = host_to_be16(len);
 +
 +      if (vendor == EAP_VENDOR_IETF) {
 +              wpabuf_put_u8(buf, type);
 +      } else {
 +              wpabuf_put_u8(buf, EAP_TYPE_EXPANDED);
 +              wpabuf_put_be24(buf, vendor);
 +              wpabuf_put_be32(buf, type);
 +      }
 +
 +      return buf;
 +}
 +
 +
 +/**
 + * eap_update_len - Update EAP header length
 + * @msg: EAP message from eap_msg_alloc
 + *
 + * This function updates the length field in the EAP header to match with the
 + * current length for the buffer. This allows eap_msg_alloc() to be used to
 + * allocate a larger buffer than the exact message length (e.g., if exact
 + * message length is not yet known).
 + */
 +void eap_update_len(struct wpabuf *msg)
 +{
 +      struct eap_hdr *hdr;
 +      hdr = wpabuf_mhead(msg);
 +      if (wpabuf_len(msg) < sizeof(*hdr))
 +              return;
 +      hdr->length = host_to_be16(wpabuf_len(msg));
 +}
 +
 +
 +/**
 + * eap_get_id - Get EAP Identifier from wpabuf
 + * @msg: Buffer starting with an EAP header
 + * Returns: The Identifier field from the EAP header
 + */
 +u8 eap_get_id(const struct wpabuf *msg)
 +{
 +      const struct eap_hdr *eap;
 +
 +      if (wpabuf_len(msg) < sizeof(*eap))
 +              return 0;
 +
 +      eap = wpabuf_head(msg);
 +      return eap->identifier;
 +}
 +
 +
 +/**
++ * eap_get_type - Get EAP Type from wpabuf
 + * @msg: Buffer starting with an EAP header
 + * Returns: The EAP Type after the EAP header
 + */
 +EapType eap_get_type(const struct wpabuf *msg)
 +{
 +      if (wpabuf_len(msg) < sizeof(struct eap_hdr) + 1)
 +              return EAP_TYPE_NONE;
 +
 +      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 fceb1b0adc1c22ec6eced6b8ee06ae09a60ea8a1,0000000000000000000000000000000000000000..151cc7859c5da8cf8e9972930da992530d382707
mode 100644,000000..100644
--- /dev/null
@@@ -1,298 -1,0 +1,267 @@@
-       struct tls_keys keys;
-       u8 *rnd = NULL, *out;
-       int block_size;
 +/*
 + * EAP-FAST common helper functions (RFC 4851)
 + * Copyright (c) 2008, 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/sha1.h"
 +#include "crypto/tls.h"
 +#include "eap_defs.h"
 +#include "eap_tlv_common.h"
 +#include "eap_fast_common.h"
 +
 +
 +void eap_fast_put_tlv_hdr(struct wpabuf *buf, u16 type, u16 len)
 +{
 +      struct pac_tlv_hdr hdr;
 +      hdr.type = host_to_be16(type);
 +      hdr.len = host_to_be16(len);
 +      wpabuf_put_data(buf, &hdr, sizeof(hdr));
 +}
 +
 +
 +void eap_fast_put_tlv(struct wpabuf *buf, u16 type, const void *data,
 +                           u16 len)
 +{
 +      eap_fast_put_tlv_hdr(buf, type, len);
 +      wpabuf_put_data(buf, data, len);
 +}
 +
 +
 +void eap_fast_put_tlv_buf(struct wpabuf *buf, u16 type,
 +                               const struct wpabuf *data)
 +{
 +      eap_fast_put_tlv_hdr(buf, type, wpabuf_len(data));
 +      wpabuf_put_buf(buf, data);
 +}
 +
 +
 +struct wpabuf * eap_fast_tlv_eap_payload(struct wpabuf *buf)
 +{
 +      struct wpabuf *e;
 +
 +      if (buf == NULL)
 +              return NULL;
 +
 +      /* Encapsulate EAP packet in EAP-Payload TLV */
 +      wpa_printf(MSG_DEBUG, "EAP-FAST: Add EAP-Payload TLV");
 +      e = wpabuf_alloc(sizeof(struct pac_tlv_hdr) + wpabuf_len(buf));
 +      if (e == NULL) {
 +              wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to allocate memory "
 +                         "for TLV encapsulation");
 +              wpabuf_free(buf);
 +              return NULL;
 +      }
 +      eap_fast_put_tlv_buf(e,
 +                           EAP_TLV_TYPE_MANDATORY | EAP_TLV_EAP_PAYLOAD_TLV,
 +                           buf);
 +      wpabuf_free(buf);
 +      return e;
 +}
 +
 +
 +void eap_fast_derive_master_secret(const u8 *pac_key, const u8 *server_random,
 +                                 const u8 *client_random, u8 *master_secret)
 +{
 +#define TLS_RANDOM_LEN 32
 +#define TLS_MASTER_SECRET_LEN 48
 +      u8 seed[2 * TLS_RANDOM_LEN];
 +
 +      wpa_hexdump(MSG_DEBUG, "EAP-FAST: client_random",
 +                  client_random, TLS_RANDOM_LEN);
 +      wpa_hexdump(MSG_DEBUG, "EAP-FAST: server_random",
 +                  server_random, TLS_RANDOM_LEN);
 +
 +      /*
 +       * RFC 4851, Section 5.1:
 +       * master_secret = T-PRF(PAC-Key, "PAC to master secret label hash", 
 +       *                       server_random + client_random, 48)
 +       */
 +      os_memcpy(seed, server_random, TLS_RANDOM_LEN);
 +      os_memcpy(seed + TLS_RANDOM_LEN, client_random, TLS_RANDOM_LEN);
 +      sha1_t_prf(pac_key, EAP_FAST_PAC_KEY_LEN,
 +                 "PAC to master secret label hash",
 +                 seed, sizeof(seed), master_secret, TLS_MASTER_SECRET_LEN);
 +
 +      wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: master_secret",
 +                      master_secret, TLS_MASTER_SECRET_LEN);
 +}
 +
 +
 +u8 * eap_fast_derive_key(void *ssl_ctx, struct tls_connection *conn,
 +                       const char *label, size_t len)
 +{
-       block_size = tls_connection_get_keyblock_size(ssl_ctx, conn);
-       if (block_size < 0)
-               return NULL;
-       out = os_malloc(block_size + len);
++      u8 *out;
 +
-       if (tls_connection_prf(ssl_ctx, conn, label, 1, out, block_size + len)
-           == 0) {
-               os_memmove(out, out + block_size, len);
-               return out;
++      out = os_malloc(len);
 +      if (out == NULL)
 +              return NULL;
 +
-       if (tls_connection_get_keys(ssl_ctx, conn, &keys))
-               goto fail;
-       rnd = os_malloc(keys.client_random_len + keys.server_random_len);
-       if (rnd == NULL)
-               goto fail;
-       os_memcpy(rnd, keys.server_random, keys.server_random_len);
-       os_memcpy(rnd + keys.server_random_len, keys.client_random,
-                 keys.client_random_len);
-       wpa_hexdump_key(MSG_MSGDUMP, "EAP-FAST: master_secret for key "
-                       "expansion", keys.master_key, keys.master_key_len);
-       if (tls_prf_sha1_md5(keys.master_key, keys.master_key_len,
-                            label, rnd, keys.client_random_len +
-                            keys.server_random_len, out, block_size + len))
-               goto fail;
-       os_free(rnd);
-       os_memmove(out, out + block_size, len);
++      if (tls_connection_prf(ssl_ctx, conn, label, 1, 1, out, len)) {
++              os_free(out);
++              return NULL;
 +      }
 +
- fail:
-       os_free(rnd);
-       os_free(out);
-       return NULL;
 +      return out;
 +}
 +
 +
 +void eap_fast_derive_eap_msk(const u8 *simck, u8 *msk)
 +{
 +      /*
 +       * RFC 4851, Section 5.4: EAP Master Session Key Generation
 +       * MSK = T-PRF(S-IMCK[j], "Session Key Generating Function", 64)
 +       */
 +
 +      sha1_t_prf(simck, EAP_FAST_SIMCK_LEN,
 +                 "Session Key Generating Function", (u8 *) "", 0,
 +                 msk, EAP_FAST_KEY_LEN);
 +      wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: Derived key (MSK)",
 +                      msk, EAP_FAST_KEY_LEN);
 +}
 +
 +
 +void eap_fast_derive_eap_emsk(const u8 *simck, u8 *emsk)
 +{
 +      /*
 +       * RFC 4851, Section 5.4: EAP Master Session Key Genreration
 +       * EMSK = T-PRF(S-IMCK[j],
 +       *        "Extended Session Key Generating Function", 64)
 +       */
 +
 +      sha1_t_prf(simck, EAP_FAST_SIMCK_LEN,
 +                 "Extended Session Key Generating Function", (u8 *) "", 0,
 +                 emsk, EAP_EMSK_LEN);
 +      wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: Derived key (EMSK)",
 +                      emsk, EAP_EMSK_LEN);
 +}
 +
 +
 +int eap_fast_parse_tlv(struct eap_fast_tlv_parse *tlv,
 +                     int tlv_type, u8 *pos, size_t len)
 +{
 +      switch (tlv_type) {
 +      case EAP_TLV_EAP_PAYLOAD_TLV:
 +              wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: EAP-Payload TLV",
 +                          pos, len);
 +              if (tlv->eap_payload_tlv) {
 +                      wpa_printf(MSG_DEBUG, "EAP-FAST: More than one "
 +                                 "EAP-Payload TLV in the message");
 +                      tlv->iresult = EAP_TLV_RESULT_FAILURE;
 +                      return -2;
 +              }
 +              tlv->eap_payload_tlv = pos;
 +              tlv->eap_payload_tlv_len = len;
 +              break;
 +      case EAP_TLV_RESULT_TLV:
 +              wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Result TLV", pos, len);
 +              if (tlv->result) {
 +                      wpa_printf(MSG_DEBUG, "EAP-FAST: More than one "
 +                                 "Result TLV in the message");
 +                      tlv->result = EAP_TLV_RESULT_FAILURE;
 +                      return -2;
 +              }
 +              if (len < 2) {
 +                      wpa_printf(MSG_DEBUG, "EAP-FAST: Too short "
 +                                 "Result TLV");
 +                      tlv->result = EAP_TLV_RESULT_FAILURE;
 +                      break;
 +              }
 +              tlv->result = WPA_GET_BE16(pos);
 +              if (tlv->result != EAP_TLV_RESULT_SUCCESS &&
 +                  tlv->result != EAP_TLV_RESULT_FAILURE) {
 +                      wpa_printf(MSG_DEBUG, "EAP-FAST: Unknown Result %d",
 +                                 tlv->result);
 +                      tlv->result = EAP_TLV_RESULT_FAILURE;
 +              }
 +              wpa_printf(MSG_DEBUG, "EAP-FAST: Result: %s",
 +                         tlv->result == EAP_TLV_RESULT_SUCCESS ?
 +                         "Success" : "Failure");
 +              break;
 +      case EAP_TLV_INTERMEDIATE_RESULT_TLV:
 +              wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Intermediate Result TLV",
 +                          pos, len);
 +              if (len < 2) {
 +                      wpa_printf(MSG_DEBUG, "EAP-FAST: Too short "
 +                                 "Intermediate-Result TLV");
 +                      tlv->iresult = EAP_TLV_RESULT_FAILURE;
 +                      break;
 +              }
 +              if (tlv->iresult) {
 +                      wpa_printf(MSG_DEBUG, "EAP-FAST: More than one "
 +                                 "Intermediate-Result TLV in the message");
 +                      tlv->iresult = EAP_TLV_RESULT_FAILURE;
 +                      return -2;
 +              }
 +              tlv->iresult = WPA_GET_BE16(pos);
 +              if (tlv->iresult != EAP_TLV_RESULT_SUCCESS &&
 +                  tlv->iresult != EAP_TLV_RESULT_FAILURE) {
 +                      wpa_printf(MSG_DEBUG, "EAP-FAST: Unknown Intermediate "
 +                                 "Result %d", tlv->iresult);
 +                      tlv->iresult = EAP_TLV_RESULT_FAILURE;
 +              }
 +              wpa_printf(MSG_DEBUG, "EAP-FAST: Intermediate Result: %s",
 +                         tlv->iresult == EAP_TLV_RESULT_SUCCESS ?
 +                         "Success" : "Failure");
 +              break;
 +      case EAP_TLV_CRYPTO_BINDING_TLV:
 +              wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Crypto-Binding TLV",
 +                          pos, len);
 +              if (tlv->crypto_binding) {
 +                      wpa_printf(MSG_DEBUG, "EAP-FAST: More than one "
 +                                 "Crypto-Binding TLV in the message");
 +                      tlv->iresult = EAP_TLV_RESULT_FAILURE;
 +                      return -2;
 +              }
 +              tlv->crypto_binding_len = sizeof(struct eap_tlv_hdr) + len;
 +              if (tlv->crypto_binding_len < sizeof(*tlv->crypto_binding)) {
 +                      wpa_printf(MSG_DEBUG, "EAP-FAST: Too short "
 +                                 "Crypto-Binding TLV");
 +                      tlv->iresult = EAP_TLV_RESULT_FAILURE;
 +                      return -2;
 +              }
 +              tlv->crypto_binding = (struct eap_tlv_crypto_binding_tlv *)
 +                      (pos - sizeof(struct eap_tlv_hdr));
 +              break;
 +      case EAP_TLV_REQUEST_ACTION_TLV:
 +              wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Request-Action TLV",
 +                          pos, len);
 +              if (tlv->request_action) {
 +                      wpa_printf(MSG_DEBUG, "EAP-FAST: More than one "
 +                                 "Request-Action TLV in the message");
 +                      tlv->iresult = EAP_TLV_RESULT_FAILURE;
 +                      return -2;
 +              }
 +              if (len < 2) {
 +                      wpa_printf(MSG_DEBUG, "EAP-FAST: Too short "
 +                                 "Request-Action TLV");
 +                      tlv->iresult = EAP_TLV_RESULT_FAILURE;
 +                      break;
 +              }
 +              tlv->request_action = WPA_GET_BE16(pos);
 +              wpa_printf(MSG_DEBUG, "EAP-FAST: Request-Action: %d",
 +                         tlv->request_action);
 +              break;
 +      case EAP_TLV_PAC_TLV:
 +              wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: PAC TLV", pos, len);
 +              if (tlv->pac) {
 +                      wpa_printf(MSG_DEBUG, "EAP-FAST: More than one "
 +                                 "PAC TLV in the message");
 +                      tlv->iresult = EAP_TLV_RESULT_FAILURE;
 +                      return -2;
 +              }
 +              tlv->pac = pos;
 +              tlv->pac_len = len;
 +              break;
 +      default:
 +              /* Unknown TLV */
 +              return -1;
 +      }
 +
 +      return 0;
 +}
index 631c363fb7c9f7e292fc9a5a84772413263e0ab7,0000000000000000000000000000000000000000..4d27623f87bfdbbfced4b3916c188c7b8d9ff129
mode 100644,000000..100644
--- /dev/null
@@@ -1,346 -1,0 +1,347 @@@
-                            u8 *password, int password_len,
-                            u8 *id_server, int id_server_len,
-                            u8 *id_peer, int id_peer_len, u8 *token)
 +/*
 + * EAP server/peer: EAP-pwd shared routines
 + * Copyright (c) 2010, Dan Harkins <dharkins@lounge.org>
 + *
 + * 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/sha256.h"
 +#include "crypto/crypto.h"
 +#include "eap_defs.h"
 +#include "eap_pwd_common.h"
 +
 +/* The random function H(x) = HMAC-SHA256(0^32, x) */
 +struct crypto_hash * eap_pwd_h_init(void)
 +{
 +      u8 allzero[SHA256_MAC_LEN];
 +      os_memset(allzero, 0, SHA256_MAC_LEN);
 +      return crypto_hash_init(CRYPTO_HASH_ALG_HMAC_SHA256, allzero,
 +                              SHA256_MAC_LEN);
 +}
 +
 +
 +void eap_pwd_h_update(struct crypto_hash *hash, const u8 *data, size_t len)
 +{
 +      crypto_hash_update(hash, data, len);
 +}
 +
 +
 +void eap_pwd_h_final(struct crypto_hash *hash, u8 *digest)
 +{
 +      size_t len = SHA256_MAC_LEN;
 +      crypto_hash_finish(hash, digest, &len);
 +}
 +
 +
 +/* a counter-based KDF based on NIST SP800-108 */
 +static int eap_pwd_kdf(const u8 *key, size_t keylen, const u8 *label,
 +                     size_t labellen, u8 *result, size_t resultbitlen)
 +{
 +      struct crypto_hash *hash;
 +      u8 digest[SHA256_MAC_LEN];
 +      u16 i, ctr, L;
 +      size_t resultbytelen, len = 0, mdlen;
 +
 +      resultbytelen = (resultbitlen + 7) / 8;
 +      ctr = 0;
 +      L = htons(resultbitlen);
 +      while (len < resultbytelen) {
 +              ctr++;
 +              i = htons(ctr);
 +              hash = crypto_hash_init(CRYPTO_HASH_ALG_HMAC_SHA256,
 +                                      key, keylen);
 +              if (hash == NULL)
 +                      return -1;
 +              if (ctr > 1)
 +                      crypto_hash_update(hash, digest, SHA256_MAC_LEN);
 +              crypto_hash_update(hash, (u8 *) &i, sizeof(u16));
 +              crypto_hash_update(hash, label, labellen);
 +              crypto_hash_update(hash, (u8 *) &L, sizeof(u16));
 +              mdlen = SHA256_MAC_LEN;
 +              if (crypto_hash_finish(hash, digest, &mdlen) < 0)
 +                      return -1;
 +              if ((len + mdlen) > resultbytelen)
 +                      os_memcpy(result + len, digest, resultbytelen - len);
 +              else
 +                      os_memcpy(result + len, digest, mdlen);
 +              len += mdlen;
 +      }
 +
 +      /* since we're expanding to a bit length, mask off the excess */
 +      if (resultbitlen % 8) {
 +              u8 mask = 0xff;
 +              mask <<= (8 - (resultbitlen % 8));
 +              result[resultbytelen - 1] &= mask;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +/*
 + * compute a "random" secret point on an elliptic curve based
 + * on the password and identities.
 + */
 +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, u8 *session_id)
++                           const u8 *password, size_t password_len,
++                           const u8 *id_server, size_t id_server_len,
++                           const u8 *id_peer, size_t id_peer_len,
++                           const u8 *token)
 +{
 +      BIGNUM *x_candidate = NULL, *rnd = NULL, *cofactor = NULL;
 +      struct crypto_hash *hash;
 +      unsigned char pwe_digest[SHA256_MAC_LEN], *prfbuf = NULL, ctr;
 +      int nid, is_odd, ret = 0;
 +      size_t primebytelen, primebitlen;
 +
 +      switch (num) { /* from IANA registry for IKE D-H groups */
 +        case 19:
 +              nid = NID_X9_62_prime256v1;
 +              break;
 +        case 20:
 +              nid = NID_secp384r1;
 +              break;
 +        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;
 +        default:
 +              wpa_printf(MSG_INFO, "EAP-pwd: unsupported group %d", num);
 +              return -1;
 +      }
 +
 +      grp->pwe = NULL;
 +      grp->order = NULL;
 +      grp->prime = NULL;
 +
 +      if ((grp->group = EC_GROUP_new_by_curve_name(nid)) == NULL) {
 +              wpa_printf(MSG_INFO, "EAP-pwd: unable to create EC_GROUP");
 +              goto fail;
 +      }
 +
 +      if (((rnd = BN_new()) == NULL) ||
 +          ((cofactor = BN_new()) == NULL) ||
 +          ((grp->pwe = EC_POINT_new(grp->group)) == NULL) ||
 +          ((grp->order = BN_new()) == NULL) ||
 +          ((grp->prime = BN_new()) == NULL) ||
 +          ((x_candidate = BN_new()) == NULL)) {
 +              wpa_printf(MSG_INFO, "EAP-pwd: unable to create bignums");
 +              goto fail;
 +      }
 +
 +      if (!EC_GROUP_get_curve_GFp(grp->group, grp->prime, NULL, NULL, NULL))
 +      {
 +              wpa_printf(MSG_INFO, "EAP-pwd: unable to get prime for GFp "
 +                         "curve");
 +              goto fail;
 +      }
 +      if (!EC_GROUP_get_order(grp->group, grp->order, NULL)) {
 +              wpa_printf(MSG_INFO, "EAP-pwd: unable to get order for curve");
 +              goto fail;
 +      }
 +      if (!EC_GROUP_get_cofactor(grp->group, cofactor, NULL)) {
 +              wpa_printf(MSG_INFO, "EAP-pwd: unable to get cofactor for "
 +                         "curve");
 +              goto fail;
 +      }
 +      primebitlen = BN_num_bits(grp->prime);
 +      primebytelen = BN_num_bytes(grp->prime);
 +      if ((prfbuf = os_malloc(primebytelen)) == NULL) {
 +              wpa_printf(MSG_INFO, "EAP-pwd: unable to malloc space for prf "
 +                         "buffer");
 +              goto fail;
 +      }
 +      os_memset(prfbuf, 0, primebytelen);
 +      ctr = 0;
 +      while (1) {
 +              if (ctr > 30) {
 +                      wpa_printf(MSG_INFO, "EAP-pwd: unable to find random "
 +                                 "point on curve for group %d, something's "
 +                                 "fishy", num);
 +                      goto fail;
 +              }
 +              ctr++;
 +
 +              /*
 +               * compute counter-mode password value and stretch to prime
 +               *    pwd-seed = H(token | peer-id | server-id | password |
 +               *                 counter)
 +               */
 +              hash = eap_pwd_h_init();
 +              if (hash == NULL)
 +                      goto fail;
 +              eap_pwd_h_update(hash, token, sizeof(u32));
 +              eap_pwd_h_update(hash, id_peer, id_peer_len);
 +              eap_pwd_h_update(hash, id_server, id_server_len);
 +              eap_pwd_h_update(hash, password, password_len);
 +              eap_pwd_h_update(hash, &ctr, sizeof(ctr));
 +              eap_pwd_h_final(hash, pwe_digest);
 +
 +              BN_bin2bn(pwe_digest, SHA256_MAC_LEN, rnd);
 +
 +              if (eap_pwd_kdf(pwe_digest, SHA256_MAC_LEN,
 +                              (u8 *) "EAP-pwd Hunting And Pecking",
 +                              os_strlen("EAP-pwd Hunting And Pecking"),
 +                              prfbuf, primebitlen) < 0)
 +                      goto fail;
 +
 +              BN_bin2bn(prfbuf, primebytelen, x_candidate);
 +
 +              /*
 +               * eap_pwd_kdf() returns a string of bits 0..primebitlen but
 +               * BN_bin2bn will treat that string of bits as a big endian
 +               * number. If the primebitlen is not an even multiple of 8
 +               * then excessive bits-- those _after_ primebitlen-- so now
 +               * we have to shift right the amount we masked off.
 +               */
 +              if (primebitlen % 8)
 +                      BN_rshift(x_candidate, x_candidate,
 +                                (8 - (primebitlen % 8)));
 +
 +              if (BN_ucmp(x_candidate, grp->prime) >= 0)
 +                      continue;
 +
 +              wpa_hexdump(MSG_DEBUG, "EAP-pwd: x_candidate",
 +                          prfbuf, primebytelen);
 +
 +              /*
 +               * need to unambiguously identify the solution, if there is
 +               * one...
 +               */
 +              if (BN_is_odd(rnd))
 +                      is_odd = 1;
 +              else
 +                      is_odd = 0;
 +
 +              /*
 +               * solve the quadratic equation, if it's not solvable then we
 +               * don't have a point
 +               */
 +              if (!EC_POINT_set_compressed_coordinates_GFp(grp->group,
 +                                                           grp->pwe,
 +                                                           x_candidate,
 +                                                           is_odd, NULL))
 +                      continue;
 +              /*
 +               * If there's a solution to the equation then the point must be
 +               * on the curve so why check again explicitly? OpenSSL code
 +               * says this is required by X9.62. We're not X9.62 but it can't
 +               * hurt just to be sure.
 +               */
 +              if (!EC_POINT_is_on_curve(grp->group, grp->pwe, NULL)) {
 +                      wpa_printf(MSG_INFO, "EAP-pwd: point is not on curve");
 +                      continue;
 +              }
 +
 +              if (BN_cmp(cofactor, BN_value_one())) {
 +                      /* make sure the point is not in a small sub-group */
 +                      if (!EC_POINT_mul(grp->group, grp->pwe, NULL, grp->pwe,
 +                                        cofactor, NULL)) {
 +                              wpa_printf(MSG_INFO, "EAP-pwd: cannot "
 +                                         "multiply generator by order");
 +                              continue;
 +                      }
 +                      if (EC_POINT_is_at_infinity(grp->group, grp->pwe)) {
 +                              wpa_printf(MSG_INFO, "EAP-pwd: point is at "
 +                                         "infinity");
 +                              continue;
 +                      }
 +              }
 +              /* if we got here then we have a new generator. */
 +              break;
 +      }
 +      wpa_printf(MSG_DEBUG, "EAP-pwd: found a PWE in %d tries", ctr);
 +      grp->group_num = num;
 +      if (0) {
 + fail:
 +              EC_GROUP_free(grp->group);
 +              grp->group = NULL;
 +              EC_POINT_clear_free(grp->pwe);
 +              grp->pwe = NULL;
 +              BN_clear_free(grp->order);
 +              grp->order = NULL;
 +              BN_clear_free(grp->prime);
 +              grp->prime = NULL;
 +              ret = 1;
 +      }
 +      /* cleanliness and order.... */
 +      BN_clear_free(cofactor);
 +      BN_clear_free(x_candidate);
 +      BN_clear_free(rnd);
 +      os_free(prfbuf);
 +
 +      return ret;
 +}
 +
 +
-       eap_pwd_h_update(hash, (u8 *) ciphersuite, sizeof(u32));
++int compute_keys(EAP_PWD_group *grp, BN_CTX *bnctx, const BIGNUM *k,
++               const BIGNUM *peer_scalar, const BIGNUM *server_scalar,
++               const u8 *confirm_peer, const u8 *confirm_server,
++               const u32 *ciphersuite, u8 *msk, u8 *emsk, u8 *session_id)
 +{
 +      struct crypto_hash *hash;
 +      u8 mk[SHA256_MAC_LEN], *cruft;
 +      u8 msk_emsk[EAP_MSK_LEN + EAP_EMSK_LEN];
 +      int offset;
 +
 +      if ((cruft = os_malloc(BN_num_bytes(grp->prime))) == NULL)
 +              return -1;
 +
 +      /*
 +       * first compute the session-id = TypeCode | H(ciphersuite | scal_p |
 +       *      scal_s)
 +       */
 +      session_id[0] = EAP_TYPE_PWD;
 +      hash = eap_pwd_h_init();
 +      if (hash == NULL) {
 +              os_free(cruft);
 +              return -1;
 +      }
++      eap_pwd_h_update(hash, (const u8 *) ciphersuite, sizeof(u32));
 +      offset = BN_num_bytes(grp->order) - BN_num_bytes(peer_scalar);
 +      os_memset(cruft, 0, BN_num_bytes(grp->prime));
 +      BN_bn2bin(peer_scalar, cruft + offset);
 +      eap_pwd_h_update(hash, cruft, BN_num_bytes(grp->order));
 +      offset = BN_num_bytes(grp->order) - BN_num_bytes(server_scalar);
 +      os_memset(cruft, 0, BN_num_bytes(grp->prime));
 +      BN_bn2bin(server_scalar, cruft + offset);
 +      eap_pwd_h_update(hash, cruft, BN_num_bytes(grp->order));
 +      eap_pwd_h_final(hash, &session_id[1]);
 +
 +      /* then compute MK = H(k | confirm-peer | confirm-server) */
 +      hash = eap_pwd_h_init();
 +      if (hash == NULL) {
 +              os_free(cruft);
 +              return -1;
 +      }
 +      offset = BN_num_bytes(grp->prime) - BN_num_bytes(k);
 +      os_memset(cruft, 0, BN_num_bytes(grp->prime));
 +      BN_bn2bin(k, cruft + offset);
 +      eap_pwd_h_update(hash, cruft, BN_num_bytes(grp->prime));
 +      os_free(cruft);
 +      eap_pwd_h_update(hash, confirm_peer, SHA256_MAC_LEN);
 +      eap_pwd_h_update(hash, confirm_server, SHA256_MAC_LEN);
 +      eap_pwd_h_final(hash, mk);
 +
 +      /* stretch the mk with the session-id to get MSK | EMSK */
 +      if (eap_pwd_kdf(mk, SHA256_MAC_LEN,
 +                      session_id, SHA256_MAC_LEN + 1,
 +                      msk_emsk, (EAP_MSK_LEN + EAP_EMSK_LEN) * 8) < 0) {
 +              return -1;
 +      }
 +
 +      os_memcpy(msk, msk_emsk, EAP_MSK_LEN);
 +      os_memcpy(emsk, msk_emsk + EAP_MSK_LEN, EAP_EMSK_LEN);
 +
 +      return 1;
 +}
index c54c4414f11f36425a3fd12753f4a526483963a2,0000000000000000000000000000000000000000..a0d717edfe7a83c337d41dcc91b8625421eb4c53
mode 100644,000000..100644
--- /dev/null
@@@ -1,67 -1,0 +1,72 @@@
- 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 *);
 +/*
 + * EAP server/peer: EAP-pwd shared definitions
 + * Copyright (c) 2009, Dan Harkins <dharkins@lounge.org>
 + *
 + * This software may be distributed under the terms of the BSD license.
 + * See README for more details.
 + */
 +
 +#ifndef EAP_PWD_COMMON_H
 +#define EAP_PWD_COMMON_H
 +
 +#include <openssl/bn.h>
 +#include <openssl/ec.h>
 +#include <openssl/evp.h>
 +
 +/*
 + * definition of a finite cyclic group
 + * TODO: support one based on a prime field
 + */
 +typedef struct group_definition_ {
 +      u16 group_num;
 +      EC_GROUP *group;
 +      EC_POINT *pwe;
 +      BIGNUM *order;
 +      BIGNUM *prime;
 +} EAP_PWD_group;
 +
 +/*
 + * EAP-pwd header, included on all payloads
 + * L(1 bit) | M(1 bit) | exch(6 bits) | total_length(if L is set)
 + */
 +#define EAP_PWD_HDR_SIZE                1
 +
 +#define EAP_PWD_OPCODE_ID_EXCH          1
 +#define EAP_PWD_OPCODE_COMMIT_EXCH      2
 +#define EAP_PWD_OPCODE_CONFIRM_EXCH     3
 +#define EAP_PWD_GET_LENGTH_BIT(x)       ((x) & 0x80)
 +#define EAP_PWD_SET_LENGTH_BIT(x)       ((x) |= 0x80)
 +#define EAP_PWD_GET_MORE_BIT(x)         ((x) & 0x40)
 +#define EAP_PWD_SET_MORE_BIT(x)         ((x) |= 0x40)
 +#define EAP_PWD_GET_EXCHANGE(x)         ((x) & 0x3f)
 +#define EAP_PWD_SET_EXCHANGE(x,y)       ((x) |= (y))
 +
 +/* EAP-pwd-ID payload */
 +struct eap_pwd_id {
 +      be16 group_num;
 +      u8 random_function;
 +#define EAP_PWD_DEFAULT_RAND_FUNC       1
 +      u8 prf;
 +#define EAP_PWD_DEFAULT_PRF             1
 +      u8 token[4];
 +      u8 prep;
 +#define EAP_PWD_PREP_NONE               0
 +#define EAP_PWD_PREP_MS                 1
 +      u8 identity[0];     /* length inferred from payload */
 +} STRUCT_PACKED;
 +
 +/* common routines */
++int compute_password_element(EAP_PWD_group *grp, u16 num,
++                           const u8 *password, size_t password_len,
++                           const u8 *id_server, size_t id_server_len,
++                           const u8 *id_peer, size_t id_peer_len,
++                           const u8 *token);
++int compute_keys(EAP_PWD_group *grp, BN_CTX *bnctx, const BIGNUM *k,
++               const BIGNUM *peer_scalar, const BIGNUM *server_scalar,
++               const u8 *confirm_peer, const u8 *confirm_server,
++               const u32 *ciphersuite, u8 *msk, u8 *emsk, u8 *session_id);
 +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);
 +
 +#endif  /* EAP_PWD_COMMON_H */
index a76253d00f606a0721a5009eaddf3523f53218cc,0000000000000000000000000000000000000000..c22e43ed84b60f88463501a75684063d3dfaf21b
mode 100644,000000..100644
--- /dev/null
@@@ -1,387 -1,0 +1,387 @@@
-                                  const u8 *pos)
 +/*
 + * EAP server/peer: EAP-SAKE shared routines
 + * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
 + *
 + * This software may be distributed under the terms of the BSD license.
 + * See README for more details.
 + */
 +
 +#include "includes.h"
 +
 +#include "common.h"
 +#include "wpabuf.h"
 +#include "crypto/sha1.h"
 +#include "eap_defs.h"
 +#include "eap_sake_common.h"
 +
 +
 +static int eap_sake_parse_add_attr(struct eap_sake_parse_attr *attr,
-       switch (pos[0]) {
++                                 u8 attr_id, u8 len, const u8 *data)
 +{
 +      size_t i;
 +
-               if (pos[1] != 2 + EAP_SAKE_RAND_LEN) {
++      switch (attr_id) {
 +      case EAP_SAKE_AT_RAND_S:
 +              wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_RAND_S");
-                                  "invalid length %d", pos[1]);
++              if (len != EAP_SAKE_RAND_LEN) {
 +                      wpa_printf(MSG_DEBUG, "EAP-SAKE: AT_RAND_S with "
-               attr->rand_s = pos + 2;
++                                 "invalid payload length %d", len);
 +                      return -1;
 +              }
-               if (pos[1] != 2 + EAP_SAKE_RAND_LEN) {
++              attr->rand_s = data;
 +              break;
 +      case EAP_SAKE_AT_RAND_P:
 +              wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_RAND_P");
-                                  "invalid length %d", pos[1]);
++              if (len != EAP_SAKE_RAND_LEN) {
 +                      wpa_printf(MSG_DEBUG, "EAP-SAKE: AT_RAND_P with "
-               attr->rand_p = pos + 2;
++                                 "invalid payload length %d", len);
 +                      return -1;
 +              }
-               if (pos[1] != 2 + EAP_SAKE_MIC_LEN) {
++              attr->rand_p = data;
 +              break;
 +      case EAP_SAKE_AT_MIC_S:
 +              wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_MIC_S");
-                                  "invalid length %d", pos[1]);
++              if (len != EAP_SAKE_MIC_LEN) {
 +                      wpa_printf(MSG_DEBUG, "EAP-SAKE: AT_MIC_S with "
-               attr->mic_s = pos + 2;
++                                 "invalid payload length %d", len);
 +                      return -1;
 +              }
-               if (pos[1] != 2 + EAP_SAKE_MIC_LEN) {
++              attr->mic_s = data;
 +              break;
 +      case EAP_SAKE_AT_MIC_P:
 +              wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_MIC_P");
-                                  "invalid length %d", pos[1]);
++              if (len != EAP_SAKE_MIC_LEN) {
 +                      wpa_printf(MSG_DEBUG, "EAP-SAKE: AT_MIC_P with "
-               attr->mic_p = pos + 2;
++                                 "invalid payload length %d", len);
 +                      return -1;
 +              }
-               attr->serverid = pos + 2;
-               attr->serverid_len = pos[1] - 2;
++              attr->mic_p = data;
 +              break;
 +      case EAP_SAKE_AT_SERVERID:
 +              wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_SERVERID");
-               attr->peerid = pos + 2;
-               attr->peerid_len = pos[1] - 2;
++              attr->serverid = data;
++              attr->serverid_len = len;
 +              break;
 +      case EAP_SAKE_AT_PEERID:
 +              wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_PEERID");
-               attr->spi_s = pos + 2;
-               attr->spi_s_len = pos[1] - 2;
++              attr->peerid = data;
++              attr->peerid_len = len;
 +              break;
 +      case EAP_SAKE_AT_SPI_S:
 +              wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_SPI_S");
-               attr->spi_p = pos + 2;
-               attr->spi_p_len = pos[1] - 2;
++              attr->spi_s = data;
++              attr->spi_s_len = len;
 +              break;
 +      case EAP_SAKE_AT_SPI_P:
 +              wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_SPI_P");
-               if (pos[1] != 4) {
++              attr->spi_p = data;
++              attr->spi_p_len = len;
 +              break;
 +      case EAP_SAKE_AT_ANY_ID_REQ:
 +              wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_ANY_ID_REQ");
-                                  " length %d", pos[1]);
++              if (len != 2) {
 +                      wpa_printf(MSG_DEBUG, "EAP-SAKE: Invalid AT_ANY_ID_REQ"
-               attr->any_id_req = pos + 2;
++                                 " payload length %d", len);
 +                      return -1;
 +              }
-               if (pos[1] != 4) {
++              attr->any_id_req = data;
 +              break;
 +      case EAP_SAKE_AT_PERM_ID_REQ:
 +              wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_PERM_ID_REQ");
-                                  "AT_PERM_ID_REQ length %d", pos[1]);
++              if (len != 2) {
 +                      wpa_printf(MSG_DEBUG, "EAP-SAKE: Invalid "
-               attr->perm_id_req = pos + 2;
++                                 "AT_PERM_ID_REQ payload length %d", len);
 +                      return -1;
 +              }
-               attr->encr_data = pos + 2;
-               attr->encr_data_len = pos[1] - 2;
++              attr->perm_id_req = data;
 +              break;
 +      case EAP_SAKE_AT_ENCR_DATA:
 +              wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_ENCR_DATA");
-               attr->iv = pos + 2;
-               attr->iv_len = pos[1] - 2;
++              attr->encr_data = data;
++              attr->encr_data_len = len;
 +              break;
 +      case EAP_SAKE_AT_IV:
 +              wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_IV");
-               for (i = 2; i < pos[1]; i++) {
-                       if (pos[i]) {
++              attr->iv = data;
++              attr->iv_len = len;
 +              break;
 +      case EAP_SAKE_AT_PADDING:
 +              wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_PADDING");
-               attr->next_tmpid = pos + 2;
-               attr->next_tmpid_len = pos[1] - 2;
++              for (i = 0; i < len; i++) {
++                      if (data[i]) {
 +                              wpa_printf(MSG_DEBUG, "EAP-SAKE: AT_PADDING "
 +                                         "with non-zero pad byte");
 +                              return -1;
 +                      }
 +              }
 +              break;
 +      case EAP_SAKE_AT_NEXT_TMPID:
 +              wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_NEXT_TMPID");
-               if (pos[1] != 6) {
++              attr->next_tmpid = data;
++              attr->next_tmpid_len = len;
 +              break;
 +      case EAP_SAKE_AT_MSK_LIFE:
 +              wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_IV");
-                                  "AT_MSK_LIFE length %d", pos[1]);
++              if (len != 4) {
 +                      wpa_printf(MSG_DEBUG, "EAP-SAKE: Invalid "
-               attr->msk_life = pos + 2;
++                                 "AT_MSK_LIFE payload length %d", len);
 +                      return -1;
 +              }
-               if (pos[0] < 128) {
++              attr->msk_life = data;
 +              break;
 +      default:
-                                  " attribute %d", pos[0]);
++              if (attr_id < 128) {
 +                      wpa_printf(MSG_DEBUG, "EAP-SAKE: Unknown non-skippable"
-                          "attribute %d", pos[0]);
++                                 " attribute %d", attr_id);
 +                      return -1;
 +              }
 +              wpa_printf(MSG_DEBUG, "EAP-SAKE: Ignoring unknown skippable "
-               if (eap_sake_parse_add_attr(attr, pos))
++                         "attribute %d", attr_id);
 +              break;
 +      }
 +
 +      if (attr->iv && !attr->encr_data) {
 +              wpa_printf(MSG_DEBUG, "EAP-SAKE: AT_IV included without "
 +                         "AT_ENCR_DATA");
 +              return -1;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +/**
 + * eap_sake_parse_attributes - Parse EAP-SAKE attributes
 + * @buf: Packet payload (starting with the first attribute)
 + * @len: Payload length
 + * @attr: Structure to be filled with found attributes
 + * Returns: 0 on success or -1 on failure
 + */
 +int eap_sake_parse_attributes(const u8 *buf, size_t len,
 +                            struct eap_sake_parse_attr *attr)
 +{
 +      const u8 *pos = buf, *end = buf + len;
 +
 +      os_memset(attr, 0, sizeof(*attr));
 +      while (pos < end) {
 +              if (end - pos < 2) {
 +                      wpa_printf(MSG_DEBUG, "EAP-SAKE: Too short attribute");
 +                      return -1;
 +              }
 +
 +              if (pos[1] < 2) {
 +                      wpa_printf(MSG_DEBUG, "EAP-SAKE: Invalid attribute "
 +                                 "length (%d)", pos[1]);
 +                      return -1;
 +              }
 +
 +              if (pos + pos[1] > end) {
 +                      wpa_printf(MSG_DEBUG, "EAP-SAKE: Attribute underflow");
 +                      return -1;
 +              }
 +
++              if (eap_sake_parse_add_attr(attr, pos[0], pos[1] - 2, pos + 2))
 +                      return -1;
 +
 +              pos += pos[1];
 +      }
 +
 +      return 0;
 +}
 +
 +
 +/**
 + * eap_sake_kdf - EAP-SAKE Key Derivation Function (KDF)
 + * @key: Key for KDF
 + * @key_len: Length of the key in bytes
 + * @label: A unique label for each purpose of the KDF
 + * @data: Extra data (start) to bind into the key
 + * @data_len: Length of the data
 + * @data2: Extra data (end) to bind into the key
 + * @data2_len: Length of the data2
 + * @buf: Buffer for the generated pseudo-random key
 + * @buf_len: Number of bytes of key to generate
 + *
 + * This function is used to derive new, cryptographically separate keys from a
 + * given key (e.g., SMS). This is identical to the PRF used in IEEE 802.11i.
 + */
 +static void eap_sake_kdf(const u8 *key, size_t key_len, const char *label,
 +                       const u8 *data, size_t data_len,
 +                       const u8 *data2, size_t data2_len,
 +                       u8 *buf, size_t buf_len)
 +{
 +      u8 counter = 0;
 +      size_t pos, plen;
 +      u8 hash[SHA1_MAC_LEN];
 +      size_t label_len = os_strlen(label) + 1;
 +      const unsigned char *addr[4];
 +      size_t len[4];
 +
 +      addr[0] = (u8 *) label; /* Label | Y */
 +      len[0] = label_len;
 +      addr[1] = data; /* Msg[start] */
 +      len[1] = data_len;
 +      addr[2] = data2; /* Msg[end] */
 +      len[2] = data2_len;
 +      addr[3] = &counter; /* Length */
 +      len[3] = 1;
 +
 +      pos = 0;
 +      while (pos < buf_len) {
 +              plen = buf_len - pos;
 +              if (plen >= SHA1_MAC_LEN) {
 +                      hmac_sha1_vector(key, key_len, 4, addr, len,
 +                                       &buf[pos]);
 +                      pos += SHA1_MAC_LEN;
 +              } else {
 +                      hmac_sha1_vector(key, key_len, 4, addr, len,
 +                                       hash);
 +                      os_memcpy(&buf[pos], hash, plen);
 +                      break;
 +              }
 +              counter++;
 +      }
 +}
 +
 +
 +/**
 + * eap_sake_derive_keys - Derive EAP-SAKE keys
 + * @root_secret_a: 16-byte Root-Secret-A
 + * @root_secret_b: 16-byte Root-Secret-B
 + * @rand_s: 16-byte RAND_S
 + * @rand_p: 16-byte RAND_P
 + * @tek: Buffer for Temporary EAK Keys (TEK-Auth[16] | TEK-Cipher[16])
 + * @msk: Buffer for 64-byte MSK
 + * @emsk: Buffer for 64-byte EMSK
 + *
 + * This function derives EAP-SAKE keys as defined in RFC 4763, section 3.2.6.
 + */
 +void eap_sake_derive_keys(const u8 *root_secret_a, const u8 *root_secret_b,
 +                        const u8 *rand_s, const u8 *rand_p, u8 *tek, u8 *msk,
 +                        u8 *emsk)
 +{
 +      u8 sms_a[EAP_SAKE_SMS_LEN];
 +      u8 sms_b[EAP_SAKE_SMS_LEN];
 +      u8 key_buf[EAP_MSK_LEN + EAP_EMSK_LEN];
 +
 +      wpa_printf(MSG_DEBUG, "EAP-SAKE: Deriving keys");
 +
 +      wpa_hexdump_key(MSG_DEBUG, "EAP-SAKE: Root-Secret-A",
 +                      root_secret_a, EAP_SAKE_ROOT_SECRET_LEN);
 +      eap_sake_kdf(root_secret_a, EAP_SAKE_ROOT_SECRET_LEN,
 +                   "SAKE Master Secret A",
 +                   rand_p, EAP_SAKE_RAND_LEN, rand_s, EAP_SAKE_RAND_LEN,
 +                   sms_a, EAP_SAKE_SMS_LEN);
 +      wpa_hexdump_key(MSG_DEBUG, "EAP-SAKE: SMS-A", sms_a, EAP_SAKE_SMS_LEN);
 +      eap_sake_kdf(sms_a, EAP_SAKE_SMS_LEN, "Transient EAP Key",
 +                   rand_s, EAP_SAKE_RAND_LEN, rand_p, EAP_SAKE_RAND_LEN,
 +                   tek, EAP_SAKE_TEK_LEN);
 +      wpa_hexdump_key(MSG_DEBUG, "EAP-SAKE: TEK-Auth",
 +                      tek, EAP_SAKE_TEK_AUTH_LEN);
 +      wpa_hexdump_key(MSG_DEBUG, "EAP-SAKE: TEK-Cipher",
 +                      tek + EAP_SAKE_TEK_AUTH_LEN, EAP_SAKE_TEK_CIPHER_LEN);
 +
 +      wpa_hexdump_key(MSG_DEBUG, "EAP-SAKE: Root-Secret-B",
 +                      root_secret_b, EAP_SAKE_ROOT_SECRET_LEN);
 +      eap_sake_kdf(root_secret_b, EAP_SAKE_ROOT_SECRET_LEN,
 +                   "SAKE Master Secret B",
 +                   rand_p, EAP_SAKE_RAND_LEN, rand_s, EAP_SAKE_RAND_LEN,
 +                   sms_b, EAP_SAKE_SMS_LEN);
 +      wpa_hexdump_key(MSG_DEBUG, "EAP-SAKE: SMS-B", sms_b, EAP_SAKE_SMS_LEN);
 +      eap_sake_kdf(sms_b, EAP_SAKE_SMS_LEN, "Master Session Key",
 +                   rand_s, EAP_SAKE_RAND_LEN, rand_p, EAP_SAKE_RAND_LEN,
 +                   key_buf, sizeof(key_buf));
 +      os_memcpy(msk, key_buf, EAP_MSK_LEN);
 +      os_memcpy(emsk, key_buf + EAP_MSK_LEN, EAP_EMSK_LEN);
 +      wpa_hexdump_key(MSG_DEBUG, "EAP-SAKE: MSK", msk, EAP_MSK_LEN);
 +      wpa_hexdump_key(MSG_DEBUG, "EAP-SAKE: EMSK", emsk, EAP_EMSK_LEN);
 +}
 +
 +
 +/**
 + * eap_sake_compute_mic - Compute EAP-SAKE MIC for an EAP packet
 + * @tek_auth: 16-byte TEK-Auth
 + * @rand_s: 16-byte RAND_S
 + * @rand_p: 16-byte RAND_P
 + * @serverid: SERVERID
 + * @serverid_len: SERVERID length
 + * @peerid: PEERID
 + * @peerid_len: PEERID length
 + * @peer: MIC calculation for 0 = Server, 1 = Peer message
 + * @eap: EAP packet
 + * @eap_len: EAP packet length
 + * @mic_pos: MIC position in the EAP packet (must be [eap .. eap + eap_len])
 + * @mic: Buffer for the computed 16-byte MIC
 + */
 +int eap_sake_compute_mic(const u8 *tek_auth,
 +                       const u8 *rand_s, const u8 *rand_p,
 +                       const u8 *serverid, size_t serverid_len,
 +                       const u8 *peerid, size_t peerid_len,
 +                       int peer, const u8 *eap, size_t eap_len,
 +                       const u8 *mic_pos, u8 *mic)
 +{
 +      u8 _rand[2 * EAP_SAKE_RAND_LEN];
 +      u8 *tmp, *pos;
 +      size_t tmplen;
 +
 +      tmplen = serverid_len + 1 + peerid_len + 1 + eap_len;
 +      tmp = os_malloc(tmplen);
 +      if (tmp == NULL)
 +              return -1;
 +      pos = tmp;
 +      if (peer) {
 +              if (peerid) {
 +                      os_memcpy(pos, peerid, peerid_len);
 +                      pos += peerid_len;
 +              }
 +              *pos++ = 0x00;
 +              if (serverid) {
 +                      os_memcpy(pos, serverid, serverid_len);
 +                      pos += serverid_len;
 +              }
 +              *pos++ = 0x00;
 +
 +              os_memcpy(_rand, rand_s, EAP_SAKE_RAND_LEN);
 +              os_memcpy(_rand + EAP_SAKE_RAND_LEN, rand_p,
 +                        EAP_SAKE_RAND_LEN);
 +      } else {
 +              if (serverid) {
 +                      os_memcpy(pos, serverid, serverid_len);
 +                      pos += serverid_len;
 +              }
 +              *pos++ = 0x00;
 +              if (peerid) {
 +                      os_memcpy(pos, peerid, peerid_len);
 +                      pos += peerid_len;
 +              }
 +              *pos++ = 0x00;
 +
 +              os_memcpy(_rand, rand_p, EAP_SAKE_RAND_LEN);
 +              os_memcpy(_rand + EAP_SAKE_RAND_LEN, rand_s,
 +                        EAP_SAKE_RAND_LEN);
 +      }
 +
 +      os_memcpy(pos, eap, eap_len);
 +      os_memset(pos + (mic_pos - eap), 0, EAP_SAKE_MIC_LEN);
 +
 +      eap_sake_kdf(tek_auth, EAP_SAKE_TEK_AUTH_LEN,
 +                   peer ? "Peer MIC" : "Server MIC",
 +                   _rand, 2 * EAP_SAKE_RAND_LEN, tmp, tmplen,
 +                   mic, EAP_SAKE_MIC_LEN);
 +
 +      os_free(tmp);
 +
 +      return 0;
 +}
 +
 +
 +void eap_sake_add_attr(struct wpabuf *buf, u8 type, const u8 *data,
 +                     size_t len)
 +{
 +      wpabuf_put_u8(buf, type);
 +      wpabuf_put_u8(buf, 2 + len); /* Length; including attr header */
 +      if (data)
 +              wpabuf_put_data(buf, data, len);
 +      else
 +              os_memset(wpabuf_put(buf, len), 0, len);
 +}
index 4f9e64eced08a83dcb9855589e98a1f6eebc256a,0000000000000000000000000000000000000000..d60358c733f0b3d853414ad32b9802aca51379f1
mode 100644,000000..100644
--- /dev/null
@@@ -1,726 -1,0 +1,726 @@@
- static struct ikev2_integ_alg ikev2_integ_algs[] = {
 +/*
 + * IKEv2 common routines for initiator and responder
 + * Copyright (c) 2007, Jouni Malinen <j@w1.fi>
 + *
 + * This software may be distributed under the terms of the BSD license.
 + * See README for more details.
 + */
 +
 +#include "includes.h"
 +
 +#include "common.h"
 +#include "crypto/crypto.h"
 +#include "crypto/md5.h"
 +#include "crypto/sha1.h"
 +#include "crypto/random.h"
 +#include "ikev2_common.h"
 +
 +
- static struct ikev2_prf_alg ikev2_prf_algs[] = {
++static const struct ikev2_integ_alg ikev2_integ_algs[] = {
 +      { AUTH_HMAC_SHA1_96, 20, 12 },
 +      { AUTH_HMAC_MD5_96, 16, 12 }
 +};
 +
 +#define NUM_INTEG_ALGS ARRAY_SIZE(ikev2_integ_algs)
 +
 +
- static struct ikev2_encr_alg ikev2_encr_algs[] = {
++static const struct ikev2_prf_alg ikev2_prf_algs[] = {
 +      { PRF_HMAC_SHA1, 20, 20 },
 +      { PRF_HMAC_MD5, 16, 16 }
 +};
 +
 +#define NUM_PRF_ALGS ARRAY_SIZE(ikev2_prf_algs)
 +
 +
++static const struct ikev2_encr_alg ikev2_encr_algs[] = {
 +      { ENCR_AES_CBC, 16, 16 }, /* only 128-bit keys supported for now */
 +      { ENCR_3DES, 24, 8 }
 +};
 +
 +#define NUM_ENCR_ALGS ARRAY_SIZE(ikev2_encr_algs)
 +
 +
 +const struct ikev2_integ_alg * ikev2_get_integ(int id)
 +{
 +      size_t i;
 +
 +      for (i = 0; i < NUM_INTEG_ALGS; i++) {
 +              if (ikev2_integ_algs[i].id == id)
 +                      return &ikev2_integ_algs[i];
 +      }
 +
 +      return NULL;
 +}
 +
 +
 +int ikev2_integ_hash(int alg, const u8 *key, size_t key_len, const u8 *data,
 +                   size_t data_len, u8 *hash)
 +{
 +      u8 tmphash[IKEV2_MAX_HASH_LEN];
 +
 +      switch (alg) {
 +      case AUTH_HMAC_SHA1_96:
 +              if (key_len != 20)
 +                      return -1;
 +              hmac_sha1(key, key_len, data, data_len, tmphash);
 +              os_memcpy(hash, tmphash, 12);
 +              break;
 +      case AUTH_HMAC_MD5_96:
 +              if (key_len != 16)
 +                      return -1;
 +              hmac_md5(key, key_len, data, data_len, tmphash);
 +              os_memcpy(hash, tmphash, 12);
 +              break;
 +      default:
 +              return -1;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +const struct ikev2_prf_alg * ikev2_get_prf(int id)
 +{
 +      size_t i;
 +
 +      for (i = 0; i < NUM_PRF_ALGS; i++) {
 +              if (ikev2_prf_algs[i].id == id)
 +                      return &ikev2_prf_algs[i];
 +      }
 +
 +      return NULL;
 +}
 +
 +
 +int ikev2_prf_hash(int alg, const u8 *key, size_t key_len,
 +                 size_t num_elem, const u8 *addr[], const size_t *len,
 +                 u8 *hash)
 +{
 +      switch (alg) {
 +      case PRF_HMAC_SHA1:
 +              hmac_sha1_vector(key, key_len, num_elem, addr, len, hash);
 +              break;
 +      case PRF_HMAC_MD5:
 +              hmac_md5_vector(key, key_len, num_elem, addr, len, hash);
 +              break;
 +      default:
 +              return -1;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +int ikev2_prf_plus(int alg, const u8 *key, size_t key_len,
 +                 const u8 *data, size_t data_len,
 +                 u8 *out, size_t out_len)
 +{
 +      u8 hash[IKEV2_MAX_HASH_LEN];
 +      size_t hash_len;
 +      u8 iter, *pos, *end;
 +      const u8 *addr[3];
 +      size_t len[3];
 +      const struct ikev2_prf_alg *prf;
 +      int res;
 +
 +      prf = ikev2_get_prf(alg);
 +      if (prf == NULL)
 +              return -1;
 +      hash_len = prf->hash_len;
 +
 +      addr[0] = hash;
 +      len[0] = hash_len;
 +      addr[1] = data;
 +      len[1] = data_len;
 +      addr[2] = &iter;
 +      len[2] = 1;
 +
 +      pos = out;
 +      end = out + out_len;
 +      iter = 1;
 +      while (pos < end) {
 +              size_t clen;
 +              if (iter == 1)
 +                      res = ikev2_prf_hash(alg, key, key_len, 2, &addr[1],
 +                                           &len[1], hash);
 +              else
 +                      res = ikev2_prf_hash(alg, key, key_len, 3, addr, len,
 +                                           hash);
 +              if (res < 0)
 +                      return -1;
 +              clen = hash_len;
 +              if ((int) clen > end - pos)
 +                      clen = end - pos;
 +              os_memcpy(pos, hash, clen);
 +              pos += clen;
 +              iter++;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +const struct ikev2_encr_alg * ikev2_get_encr(int id)
 +{
 +      size_t i;
 +
 +      for (i = 0; i < NUM_ENCR_ALGS; i++) {
 +              if (ikev2_encr_algs[i].id == id)
 +                      return &ikev2_encr_algs[i];
 +      }
 +
 +      return NULL;
 +}
 +
 +
 +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;
 +
 +      switch (alg) {
 +      case ENCR_3DES:
 +              encr_alg = CRYPTO_CIPHER_ALG_3DES;
 +              break;
 +      case ENCR_AES_CBC:
 +              encr_alg = CRYPTO_CIPHER_ALG_AES;
 +              break;
 +      default:
 +              wpa_printf(MSG_DEBUG, "IKEV2: Unsupported encr alg %d", alg);
 +              return -1;
 +      }
 +
 +      cipher = crypto_cipher_init(encr_alg, iv, key, key_len);
 +      if (cipher == NULL) {
 +              wpa_printf(MSG_INFO, "IKEV2: Failed to initialize cipher");
 +              return -1;
 +      }
 +
 +      if (crypto_cipher_encrypt(cipher, plain, crypt, len) < 0) {
 +              wpa_printf(MSG_INFO, "IKEV2: Encryption failed");
 +              crypto_cipher_deinit(cipher);
 +              return -1;
 +      }
 +      crypto_cipher_deinit(cipher);
 +
 +      return 0;
 +}
 +
 +
 +int ikev2_encr_decrypt(int alg, const u8 *key, size_t key_len, const u8 *iv,
 +                     const u8 *crypt, u8 *plain, size_t len)
 +{
 +      struct crypto_cipher *cipher;
 +      int encr_alg;
 +
 +      switch (alg) {
 +      case ENCR_3DES:
 +              encr_alg = CRYPTO_CIPHER_ALG_3DES;
 +              break;
 +      case ENCR_AES_CBC:
 +              encr_alg = CRYPTO_CIPHER_ALG_AES;
 +              break;
 +      default:
 +              wpa_printf(MSG_DEBUG, "IKEV2: Unsupported encr alg %d", alg);
 +              return -1;
 +      }
 +
 +      cipher = crypto_cipher_init(encr_alg, iv, key, key_len);
 +      if (cipher == NULL) {
 +              wpa_printf(MSG_INFO, "IKEV2: Failed to initialize cipher");
 +              return -1;
 +      }
 +
 +      if (crypto_cipher_decrypt(cipher, crypt, plain, len) < 0) {
 +              wpa_printf(MSG_INFO, "IKEV2: Decryption failed");
 +              crypto_cipher_deinit(cipher);
 +              return -1;
 +      }
 +      crypto_cipher_deinit(cipher);
 +
 +      return 0;
 +}
 +
 +
 +int ikev2_parse_payloads(struct ikev2_payloads *payloads,
 +                       u8 next_payload, const u8 *pos, const u8 *end)
 +{
 +      const struct ikev2_payload_hdr *phdr;
 +
 +      os_memset(payloads, 0, sizeof(*payloads));
 +
 +      while (next_payload != IKEV2_PAYLOAD_NO_NEXT_PAYLOAD) {
 +              unsigned int plen, pdatalen, left;
 +              const u8 *pdata;
 +              wpa_printf(MSG_DEBUG, "IKEV2: Processing payload %u",
 +                         next_payload);
 +              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 < 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: %u",
 +                         phdr->next_payload, phdr->flags, plen);
 +
 +              pdata = (const u8 *) (phdr + 1);
 +              pdatalen = plen - sizeof(*phdr);
 +
 +              switch (next_payload) {
 +              case IKEV2_PAYLOAD_SA:
 +                      wpa_printf(MSG_DEBUG, "IKEV2:   Payload: Security "
 +                                 "Association");
 +                      payloads->sa = pdata;
 +                      payloads->sa_len = pdatalen;
 +                      break;
 +              case IKEV2_PAYLOAD_KEY_EXCHANGE:
 +                      wpa_printf(MSG_DEBUG, "IKEV2:   Payload: Key "
 +                                 "Exchange");
 +                      payloads->ke = pdata;
 +                      payloads->ke_len = pdatalen;
 +                      break;
 +              case IKEV2_PAYLOAD_IDi:
 +                      wpa_printf(MSG_DEBUG, "IKEV2:   Payload: IDi");
 +                      payloads->idi = pdata;
 +                      payloads->idi_len = pdatalen;
 +                      break;
 +              case IKEV2_PAYLOAD_IDr:
 +                      wpa_printf(MSG_DEBUG, "IKEV2:   Payload: IDr");
 +                      payloads->idr = pdata;
 +                      payloads->idr_len = pdatalen;
 +                      break;
 +              case IKEV2_PAYLOAD_CERTIFICATE:
 +                      wpa_printf(MSG_DEBUG, "IKEV2:   Payload: Certificate");
 +                      payloads->cert = pdata;
 +                      payloads->cert_len = pdatalen;
 +                      break;
 +              case IKEV2_PAYLOAD_AUTHENTICATION:
 +                      wpa_printf(MSG_DEBUG, "IKEV2:   Payload: "
 +                                 "Authentication");
 +                      payloads->auth = pdata;
 +                      payloads->auth_len = pdatalen;
 +                      break;
 +              case IKEV2_PAYLOAD_NONCE:
 +                      wpa_printf(MSG_DEBUG, "IKEV2:   Payload: Nonce");
 +                      payloads->nonce = pdata;
 +                      payloads->nonce_len = pdatalen;
 +                      break;
 +              case IKEV2_PAYLOAD_ENCRYPTED:
 +                      wpa_printf(MSG_DEBUG, "IKEV2:   Payload: Encrypted");
 +                      payloads->encrypted = pdata;
 +                      payloads->encrypted_len = pdatalen;
 +                      break;
 +              case IKEV2_PAYLOAD_NOTIFICATION:
 +                      wpa_printf(MSG_DEBUG, "IKEV2:   Payload: "
 +                                 "Notification");
 +                      payloads->notification = pdata;
 +                      payloads->notification_len = pdatalen;
 +                      break;
 +              default:
 +                      if (phdr->flags & IKEV2_PAYLOAD_FLAGS_CRITICAL) {
 +                              wpa_printf(MSG_INFO, "IKEV2:   Unsupported "
 +                                         "critical payload %u - reject the "
 +                                         "entire message", next_payload);
 +                              return -1;
 +                      } else {
 +                              wpa_printf(MSG_DEBUG, "IKEV2:   Skipped "
 +                                         "unsupported payload %u",
 +                                         next_payload);
 +                      }
 +              }
 +
 +              if (next_payload == IKEV2_PAYLOAD_ENCRYPTED &&
 +                  pos + plen == end) {
 +                      /*
 +                       * Next Payload in the case of Encrypted Payload is
 +                       * actually the payload type for the first embedded
 +                       * payload.
 +                       */
 +                      payloads->encr_next_payload = phdr->next_payload;
 +                      next_payload = IKEV2_PAYLOAD_NO_NEXT_PAYLOAD;
 +              } else
 +                      next_payload = phdr->next_payload;
 +
 +              pos += plen;
 +      }
 +
 +      if (pos != end) {
 +              wpa_printf(MSG_INFO, "IKEV2: Unexpected extra data after "
 +                         "payloads");
 +              return -1;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +int ikev2_derive_auth_data(int prf_alg, const struct wpabuf *sign_msg,
 +                         const u8 *ID, size_t ID_len, u8 ID_type,
 +                         struct ikev2_keys *keys, int initiator,
 +                         const u8 *shared_secret, size_t shared_secret_len,
 +                         const u8 *nonce, size_t nonce_len,
 +                         const u8 *key_pad, size_t key_pad_len,
 +                         u8 *auth_data)
 +{
 +      size_t sign_len, buf_len;
 +      u8 *sign_data, *pos, *buf, hash[IKEV2_MAX_HASH_LEN];
 +      const struct ikev2_prf_alg *prf;
 +      const u8 *SK_p = initiator ? keys->SK_pi : keys->SK_pr;
 +
 +      prf = ikev2_get_prf(prf_alg);
 +      if (sign_msg == NULL || ID == NULL || SK_p == NULL ||
 +          shared_secret == NULL || nonce == NULL || prf == NULL)
 +              return -1;
 +
 +      /* prf(SK_pi/r,IDi/r') */
 +      buf_len = 4 + ID_len;
 +      buf = os_zalloc(buf_len);
 +      if (buf == NULL)
 +              return -1;
 +      buf[0] = ID_type;
 +      os_memcpy(buf + 4, ID, ID_len);
 +      if (ikev2_prf_hash(prf->id, SK_p, keys->SK_prf_len,
 +                         1, (const u8 **) &buf, &buf_len, hash) < 0) {
 +              os_free(buf);
 +              return -1;
 +      }
 +      os_free(buf);
 +
 +      /* sign_data = msg | Nr/i | prf(SK_pi/r,IDi/r') */
 +      sign_len = wpabuf_len(sign_msg) + nonce_len + prf->hash_len;
 +      sign_data = os_malloc(sign_len);
 +      if (sign_data == NULL)
 +              return -1;
 +      pos = sign_data;
 +      os_memcpy(pos, wpabuf_head(sign_msg), wpabuf_len(sign_msg));
 +      pos += wpabuf_len(sign_msg);
 +      os_memcpy(pos, nonce, nonce_len);
 +      pos += nonce_len;
 +      os_memcpy(pos, hash, prf->hash_len);
 +
 +      /* AUTH = prf(prf(Shared Secret, key pad, sign_data) */
 +      if (ikev2_prf_hash(prf->id, shared_secret, shared_secret_len, 1,
 +                         &key_pad, &key_pad_len, hash) < 0 ||
 +          ikev2_prf_hash(prf->id, hash, prf->hash_len, 1,
 +                         (const u8 **) &sign_data, &sign_len, auth_data) < 0)
 +      {
 +              os_free(sign_data);
 +              return -1;
 +      }
 +      os_free(sign_data);
 +
 +      return 0;
 +}
 +
 +
 +u8 * ikev2_decrypt_payload(int encr_id, int integ_id,
 +                         struct ikev2_keys *keys, int initiator,
 +                         const struct ikev2_hdr *hdr,
 +                         const u8 *encrypted, size_t encrypted_len,
 +                         size_t *res_len)
 +{
 +      size_t iv_len;
 +      const u8 *pos, *end, *iv, *integ;
 +      u8 hash[IKEV2_MAX_HASH_LEN], *decrypted;
 +      size_t decrypted_len, pad_len;
 +      const struct ikev2_integ_alg *integ_alg;
 +      const struct ikev2_encr_alg *encr_alg;
 +      const u8 *SK_e = initiator ? keys->SK_ei : keys->SK_er;
 +      const u8 *SK_a = initiator ? keys->SK_ai : keys->SK_ar;
 +
 +      if (encrypted == NULL) {
 +              wpa_printf(MSG_INFO, "IKEV2: No Encrypted payload in SA_AUTH");
 +              return NULL;
 +      }
 +
 +      encr_alg = ikev2_get_encr(encr_id);
 +      if (encr_alg == NULL) {
 +              wpa_printf(MSG_INFO, "IKEV2: Unsupported encryption type");
 +              return NULL;
 +      }
 +      iv_len = encr_alg->block_size;
 +
 +      integ_alg = ikev2_get_integ(integ_id);
 +      if (integ_alg == NULL) {
 +              wpa_printf(MSG_INFO, "IKEV2: Unsupported intergrity type");
 +              return NULL;
 +      }
 +
 +      if (encrypted_len < iv_len + 1 + integ_alg->hash_len) {
 +              wpa_printf(MSG_INFO, "IKEV2: No room for IV or Integrity "
 +                        "Checksum");
 +              return NULL;
 +      }
 +
 +      iv = encrypted;
 +      pos = iv + iv_len;
 +      end = encrypted + encrypted_len;
 +      integ = end - integ_alg->hash_len;
 +
 +      if (SK_a == NULL) {
 +              wpa_printf(MSG_INFO, "IKEV2: No SK_a available");
 +              return NULL;
 +      }
 +      if (ikev2_integ_hash(integ_id, SK_a, keys->SK_integ_len,
 +                           (const u8 *) hdr,
 +                           integ - (const u8 *) hdr, hash) < 0) {
 +              wpa_printf(MSG_INFO, "IKEV2: Failed to calculate integrity "
 +                         "hash");
 +              return NULL;
 +      }
 +      if (os_memcmp_const(integ, hash, integ_alg->hash_len) != 0) {
 +              wpa_printf(MSG_INFO, "IKEV2: Incorrect Integrity Checksum "
 +                         "Data");
 +              return NULL;
 +      }
 +
 +      if (SK_e == NULL) {
 +              wpa_printf(MSG_INFO, "IKEV2: No SK_e available");
 +              return NULL;
 +      }
 +
 +      decrypted_len = integ - pos;
 +      decrypted = os_malloc(decrypted_len);
 +      if (decrypted == NULL)
 +              return NULL;
 +
 +      if (ikev2_encr_decrypt(encr_alg->id, SK_e, keys->SK_encr_len, iv, pos,
 +                             decrypted, decrypted_len) < 0) {
 +              os_free(decrypted);
 +              return NULL;
 +      }
 +
 +      pad_len = decrypted[decrypted_len - 1];
 +      if (decrypted_len < pad_len + 1) {
 +              wpa_printf(MSG_INFO, "IKEV2: Invalid padding in encrypted "
 +                         "payload");
 +              os_free(decrypted);
 +              return NULL;
 +      }
 +
 +      decrypted_len -= pad_len + 1;
 +
 +      *res_len = decrypted_len;
 +      return decrypted;
 +}
 +
 +
 +void ikev2_update_hdr(struct wpabuf *msg)
 +{
 +      struct ikev2_hdr *hdr;
 +
 +      /* Update lenth field in HDR */
 +      hdr = wpabuf_mhead(msg);
 +      WPA_PUT_BE32(hdr->length, wpabuf_len(msg));
 +}
 +
 +
 +int ikev2_build_encrypted(int encr_id, int integ_id, struct ikev2_keys *keys,
 +                        int initiator, struct wpabuf *msg,
 +                        struct wpabuf *plain, u8 next_payload)
 +{
 +      struct ikev2_payload_hdr *phdr;
 +      size_t plen;
 +      size_t iv_len, pad_len;
 +      u8 *icv, *iv;
 +      const struct ikev2_integ_alg *integ_alg;
 +      const struct ikev2_encr_alg *encr_alg;
 +      const u8 *SK_e = initiator ? keys->SK_ei : keys->SK_er;
 +      const u8 *SK_a = initiator ? keys->SK_ai : keys->SK_ar;
 +
 +      wpa_printf(MSG_DEBUG, "IKEV2: Adding Encrypted payload");
 +
 +      /* Encr - RFC 4306, Sect. 3.14 */
 +
 +      encr_alg = ikev2_get_encr(encr_id);
 +      if (encr_alg == NULL) {
 +              wpa_printf(MSG_INFO, "IKEV2: Unsupported encryption type");
 +              return -1;
 +      }
 +      iv_len = encr_alg->block_size;
 +
 +      integ_alg = ikev2_get_integ(integ_id);
 +      if (integ_alg == NULL) {
 +              wpa_printf(MSG_INFO, "IKEV2: Unsupported intergrity type");
 +              return -1;
 +      }
 +
 +      if (SK_e == NULL) {
 +              wpa_printf(MSG_INFO, "IKEV2: No SK_e available");
 +              return -1;
 +      }
 +
 +      if (SK_a == NULL) {
 +              wpa_printf(MSG_INFO, "IKEV2: No SK_a available");
 +              return -1;
 +      }
 +
 +      phdr = wpabuf_put(msg, sizeof(*phdr));
 +      phdr->next_payload = next_payload;
 +      phdr->flags = 0;
 +
 +      iv = wpabuf_put(msg, iv_len);
 +      if (random_get_bytes(iv, iv_len)) {
 +              wpa_printf(MSG_INFO, "IKEV2: Could not generate IV");
 +              return -1;
 +      }
 +
 +      pad_len = iv_len - (wpabuf_len(plain) + 1) % iv_len;
 +      if (pad_len == iv_len)
 +              pad_len = 0;
 +      wpabuf_put(plain, pad_len);
 +      wpabuf_put_u8(plain, pad_len);
 +
 +      if (ikev2_encr_encrypt(encr_alg->id, SK_e, keys->SK_encr_len, iv,
 +                             wpabuf_head(plain), wpabuf_mhead(plain),
 +                             wpabuf_len(plain)) < 0)
 +              return -1;
 +
 +      wpabuf_put_buf(msg, plain);
 +
 +      /* Need to update all headers (Length fields) prior to hash func */
 +      icv = wpabuf_put(msg, integ_alg->hash_len);
 +      plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) phdr;
 +      WPA_PUT_BE16(phdr->payload_length, plen);
 +
 +      ikev2_update_hdr(msg);
 +
 +      return ikev2_integ_hash(integ_id, SK_a, keys->SK_integ_len,
 +                              wpabuf_head(msg),
 +                              wpabuf_len(msg) - integ_alg->hash_len, icv);
 +
 +      return 0;
 +}
 +
 +
 +int ikev2_keys_set(struct ikev2_keys *keys)
 +{
 +      return keys->SK_d && keys->SK_ai && keys->SK_ar && keys->SK_ei &&
 +              keys->SK_er && keys->SK_pi && keys->SK_pr;
 +}
 +
 +
 +void ikev2_free_keys(struct ikev2_keys *keys)
 +{
 +      os_free(keys->SK_d);
 +      os_free(keys->SK_ai);
 +      os_free(keys->SK_ar);
 +      os_free(keys->SK_ei);
 +      os_free(keys->SK_er);
 +      os_free(keys->SK_pi);
 +      os_free(keys->SK_pr);
 +      keys->SK_d = keys->SK_ai = keys->SK_ar = keys->SK_ei = keys->SK_er =
 +              keys->SK_pi = keys->SK_pr = NULL;
 +}
 +
 +
 +int ikev2_derive_sk_keys(const struct ikev2_prf_alg *prf,
 +                       const struct ikev2_integ_alg *integ,
 +                       const struct ikev2_encr_alg *encr,
 +                       const u8 *skeyseed, const u8 *data, size_t data_len,
 +                       struct ikev2_keys *keys)
 +{
 +      u8 *keybuf, *pos;
 +      size_t keybuf_len;
 +
 +      /*
 +       * {SK_d | SK_ai | SK_ar | SK_ei | SK_er | SK_pi | SK_pr } =
 +       *      prf+(SKEYSEED, Ni | Nr | SPIi | SPIr )
 +       */
 +      ikev2_free_keys(keys);
 +      keys->SK_d_len = prf->key_len;
 +      keys->SK_integ_len = integ->key_len;
 +      keys->SK_encr_len = encr->key_len;
 +      keys->SK_prf_len = prf->key_len;
 +
 +      keybuf_len = keys->SK_d_len + 2 * keys->SK_integ_len +
 +              2 * keys->SK_encr_len + 2 * keys->SK_prf_len;
 +      keybuf = os_malloc(keybuf_len);
 +      if (keybuf == NULL)
 +              return -1;
 +
 +      if (ikev2_prf_plus(prf->id, skeyseed, prf->hash_len,
 +                         data, data_len, keybuf, keybuf_len)) {
 +              os_free(keybuf);
 +              return -1;
 +      }
 +
 +      pos = keybuf;
 +
 +      keys->SK_d = os_malloc(keys->SK_d_len);
 +      if (keys->SK_d) {
 +              os_memcpy(keys->SK_d, pos, keys->SK_d_len);
 +              wpa_hexdump_key(MSG_DEBUG, "IKEV2: SK_d",
 +                              keys->SK_d, keys->SK_d_len);
 +      }
 +      pos += keys->SK_d_len;
 +
 +      keys->SK_ai = os_malloc(keys->SK_integ_len);
 +      if (keys->SK_ai) {
 +              os_memcpy(keys->SK_ai, pos, keys->SK_integ_len);
 +              wpa_hexdump_key(MSG_DEBUG, "IKEV2: SK_ai",
 +                              keys->SK_ai, keys->SK_integ_len);
 +      }
 +      pos += keys->SK_integ_len;
 +
 +      keys->SK_ar = os_malloc(keys->SK_integ_len);
 +      if (keys->SK_ar) {
 +              os_memcpy(keys->SK_ar, pos, keys->SK_integ_len);
 +              wpa_hexdump_key(MSG_DEBUG, "IKEV2: SK_ar",
 +                              keys->SK_ar, keys->SK_integ_len);
 +      }
 +      pos += keys->SK_integ_len;
 +
 +      keys->SK_ei = os_malloc(keys->SK_encr_len);
 +      if (keys->SK_ei) {
 +              os_memcpy(keys->SK_ei, pos, keys->SK_encr_len);
 +              wpa_hexdump_key(MSG_DEBUG, "IKEV2: SK_ei",
 +                              keys->SK_ei, keys->SK_encr_len);
 +      }
 +      pos += keys->SK_encr_len;
 +
 +      keys->SK_er = os_malloc(keys->SK_encr_len);
 +      if (keys->SK_er) {
 +              os_memcpy(keys->SK_er, pos, keys->SK_encr_len);
 +              wpa_hexdump_key(MSG_DEBUG, "IKEV2: SK_er",
 +                              keys->SK_er, keys->SK_encr_len);
 +      }
 +      pos += keys->SK_encr_len;
 +
 +      keys->SK_pi = os_malloc(keys->SK_prf_len);
 +      if (keys->SK_pi) {
 +              os_memcpy(keys->SK_pi, pos, keys->SK_prf_len);
 +              wpa_hexdump_key(MSG_DEBUG, "IKEV2: SK_pi",
 +                              keys->SK_pi, keys->SK_prf_len);
 +      }
 +      pos += keys->SK_prf_len;
 +
 +      keys->SK_pr = os_malloc(keys->SK_prf_len);
 +      if (keys->SK_pr) {
 +              os_memcpy(keys->SK_pr, pos, keys->SK_prf_len);
 +              wpa_hexdump_key(MSG_DEBUG, "IKEV2: SK_pr",
 +                              keys->SK_pr, keys->SK_prf_len);
 +      }
 +
 +      os_free(keybuf);
 +
 +      if (!ikev2_keys_set(keys)) {
 +              ikev2_free_keys(keys);
 +              return -1;
 +      }
 +
 +      return 0;
 +}
index 35433f3bd8e41b7f8645553435ec1c43723d2db9,0000000000000000000000000000000000000000..56c24b5503200b2f06099c2f7886887759a49efb
mode 100644,000000..100644
--- /dev/null
@@@ -1,2950 -1,0 +1,2950 @@@
-       msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_ERP_TYPE_REAUTH,
 +/*
 + * EAP peer state machines (RFC 4137)
 + * 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.
 + *
 + * This file implements the Peer State Machine as defined in RFC 4137. The used
 + * states and state transitions match mostly with the RFC. However, there are
 + * couple of additional transitions for working around small issues noticed
 + * during testing. These exceptions are explained in comments within the
 + * functions in this file. The method functions, m.func(), are similar to the
 + * ones used in RFC 4137, but some small changes have used here to optimize
 + * operations and to add functionality needed for fast re-authentication
 + * (session resumption).
 + */
 +
 +#include "includes.h"
 +
 +#include "common.h"
 +#include "pcsc_funcs.h"
 +#include "state_machine.h"
 +#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"
 +#include "eap_config.h"
 +
 +#define STATE_MACHINE_DATA struct eap_sm
 +#define STATE_MACHINE_DEBUG_PREFIX "EAP"
 +
 +#define EAP_MAX_AUTH_ROUNDS 50
 +#define EAP_CLIENT_TIMEOUT_DEFAULT 60
 +
 +
 +static Boolean eap_sm_allowMethod(struct eap_sm *sm, int vendor,
 +                                EapType method);
 +static struct wpabuf * eap_sm_buildNak(struct eap_sm *sm, int id);
 +static void eap_sm_processIdentity(struct eap_sm *sm,
 +                                 const struct wpabuf *req);
 +static void eap_sm_processNotify(struct eap_sm *sm, const struct wpabuf *req);
 +static struct wpabuf * eap_sm_buildNotify(int id);
 +static void eap_sm_parseEapReq(struct eap_sm *sm, const struct wpabuf *req);
 +#if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG)
 +static const char * eap_sm_method_state_txt(EapMethodState state);
 +static const char * eap_sm_decision_txt(EapDecision decision);
 +#endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */
 +
 +
 +
 +static Boolean eapol_get_bool(struct eap_sm *sm, enum eapol_bool_var var)
 +{
 +      return sm->eapol_cb->get_bool(sm->eapol_ctx, var);
 +}
 +
 +
 +static void eapol_set_bool(struct eap_sm *sm, enum eapol_bool_var var,
 +                         Boolean value)
 +{
 +      sm->eapol_cb->set_bool(sm->eapol_ctx, var, value);
 +}
 +
 +
 +static unsigned int eapol_get_int(struct eap_sm *sm, enum eapol_int_var var)
 +{
 +      return sm->eapol_cb->get_int(sm->eapol_ctx, var);
 +}
 +
 +
 +static void eapol_set_int(struct eap_sm *sm, enum eapol_int_var var,
 +                        unsigned int value)
 +{
 +      sm->eapol_cb->set_int(sm->eapol_ctx, var, value);
 +}
 +
 +
 +static struct wpabuf * eapol_get_eapReqData(struct eap_sm *sm)
 +{
 +      return sm->eapol_cb->get_eapReqData(sm->eapol_ctx);
 +}
 +
 +
 +static void eap_notify_status(struct eap_sm *sm, const char *status,
 +                                    const char *parameter)
 +{
 +      wpa_printf(MSG_DEBUG, "EAP: Status notification: %s (param=%s)",
 +                 status, parameter);
 +      if (sm->eapol_cb->notify_status)
 +              sm->eapol_cb->notify_status(sm->eapol_ctx, status, parameter);
 +}
 +
 +
 +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);
 +      sm->ext_pw_buf = NULL;
 +
 +      if (sm->m == NULL || sm->eap_method_priv == NULL)
 +              return;
 +
 +      wpa_printf(MSG_DEBUG, "EAP: deinitialize previously used EAP method "
 +                 "(%d, %s) at %s", sm->selectedMethod, sm->m->name, txt);
 +      sm->m->deinit(sm, sm->eap_method_priv);
 +      sm->eap_method_priv = NULL;
 +      sm->m = NULL;
 +}
 +
 +
 +/**
 + * eap_allowed_method - Check whether EAP method is allowed
 + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
 + * @vendor: Vendor-Id for expanded types or 0 = IETF for legacy types
 + * @method: EAP type
 + * Returns: 1 = allowed EAP method, 0 = not allowed
 + */
 +int eap_allowed_method(struct eap_sm *sm, int vendor, u32 method)
 +{
 +      struct eap_peer_config *config = eap_get_config(sm);
 +      int i;
 +      struct eap_method_type *m;
 +
 +      if (config == NULL || config->eap_methods == NULL)
 +              return 1;
 +
 +      m = config->eap_methods;
 +      for (i = 0; m[i].vendor != EAP_VENDOR_IETF ||
 +                   m[i].method != EAP_TYPE_NONE; i++) {
 +              if (m[i].vendor == vendor && m[i].method == method)
 +                      return 1;
 +      }
 +      return 0;
 +}
 +
 +
 +/*
 + * This state initializes state machine variables when the machine is
 + * activated (portEnabled = TRUE). This is also used when re-starting
 + * authentication (eapRestart == TRUE).
 + */
 +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->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;
 +      sm->methodState = METHOD_NONE;
 +      sm->allowNotifications = TRUE;
 +      sm->decision = DECISION_FAIL;
 +      sm->ClientTimeout = EAP_CLIENT_TIMEOUT_DEFAULT;
 +      eapol_set_int(sm, EAPOL_idleWhile, sm->ClientTimeout);
 +      eapol_set_bool(sm, EAPOL_eapSuccess, FALSE);
 +      eapol_set_bool(sm, EAPOL_eapFail, FALSE);
 +      eap_sm_free_key(sm);
 +      os_free(sm->eapSessionId);
 +      sm->eapSessionId = NULL;
 +      sm->eapKeyAvailable = FALSE;
 +      eapol_set_bool(sm, EAPOL_eapRestart, FALSE);
 +      sm->lastId = -1; /* new session - make sure this does not match with
 +                        * the first EAP-Packet */
 +      /*
 +       * RFC 4137 does not reset eapResp and eapNoResp here. However, this
 +       * seemed to be able to trigger cases where both were set and if EAPOL
 +       * state machine uses eapNoResp first, it may end up not sending a real
 +       * reply correctly. This occurred when the workaround in FAIL state set
 +       * eapNoResp = TRUE.. Maybe that workaround needs to be fixed to do
 +       * something else(?)
 +       */
 +      eapol_set_bool(sm, EAPOL_eapResp, FALSE);
 +      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;
 +}
 +
 +
 +/*
 + * This state is reached whenever service from the lower layer is interrupted
 + * or unavailable (portEnabled == FALSE). Immediate transition to INITIALIZE
 + * occurs when the port becomes enabled.
 + */
 +SM_STATE(EAP, DISABLED)
 +{
 +      SM_ENTRY(EAP, DISABLED);
 +      sm->num_rounds = 0;
 +      /*
 +       * RFC 4137 does not describe clearing of idleWhile here, but doing so
 +       * allows the timer tick to be stopped more quickly when EAP is not in
 +       * use.
 +       */
 +      eapol_set_int(sm, EAPOL_idleWhile, 0);
 +}
 +
 +
 +/*
 + * The state machine spends most of its time here, waiting for something to
 + * happen. This state is entered unconditionally from INITIALIZE, DISCARD, and
 + * SEND_RESPONSE states.
 + */
 +SM_STATE(EAP, IDLE)
 +{
 +      SM_ENTRY(EAP, IDLE);
 +}
 +
 +
 +/*
 + * This state is entered when an EAP packet is received (eapReq == TRUE) to
 + * parse the packet header.
 + */
 +SM_STATE(EAP, RECEIVED)
 +{
 +      const struct wpabuf *eapReqData;
 +
 +      SM_ENTRY(EAP, RECEIVED);
 +      eapReqData = eapol_get_eapReqData(sm);
 +      /* parse rxReq, rxSuccess, rxFailure, reqId, reqMethod */
 +      eap_sm_parseEapReq(sm, eapReqData);
 +      sm->num_rounds++;
 +}
 +
 +
 +/*
 + * This state is entered when a request for a new type comes in. Either the
 + * correct method is started, or a Nak response is built.
 + */
 +SM_STATE(EAP, GET_METHOD)
 +{
 +      int reinit;
 +      EapType method;
 +      const struct eap_method *eap_method;
 +
 +      SM_ENTRY(EAP, GET_METHOD);
 +
 +      if (sm->reqMethod == EAP_TYPE_EXPANDED)
 +              method = sm->reqVendorMethod;
 +      else
 +              method = sm->reqMethod;
 +
 +      eap_method = eap_peer_get_eap_method(sm->reqVendor, method);
 +
 +      if (!eap_sm_allowMethod(sm, sm->reqVendor, method)) {
 +              wpa_printf(MSG_DEBUG, "EAP: vendor %u method %u not allowed",
 +                         sm->reqVendor, method);
 +              wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_PROPOSED_METHOD
 +                      "vendor=%u method=%u -> NAK",
 +                      sm->reqVendor, method);
 +              eap_notify_status(sm, "refuse proposed method",
 +                                eap_method ?  eap_method->name : "unknown");
 +              goto nak;
 +      }
 +
 +      wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_PROPOSED_METHOD
 +              "vendor=%u method=%u", sm->reqVendor, method);
 +
 +      eap_notify_status(sm, "accept proposed method",
 +                        eap_method ?  eap_method->name : "unknown");
 +      /*
 +       * RFC 4137 does not define specific operation for fast
 +       * re-authentication (session resumption). The design here is to allow
 +       * the previously used method data to be maintained for
 +       * re-authentication if the method support session resumption.
 +       * Otherwise, the previously used method data is freed and a new method
 +       * is allocated here.
 +       */
 +      if (sm->fast_reauth &&
 +          sm->m && sm->m->vendor == sm->reqVendor &&
 +          sm->m->method == method &&
 +          sm->m->has_reauth_data &&
 +          sm->m->has_reauth_data(sm, sm->eap_method_priv)) {
 +              wpa_printf(MSG_DEBUG, "EAP: Using previous method data"
 +                         " for fast re-authentication");
 +              reinit = 1;
 +      } else {
 +              eap_deinit_prev_method(sm, "GET_METHOD");
 +              reinit = 0;
 +      }
 +
 +      sm->selectedMethod = sm->reqMethod;
 +      if (sm->m == NULL)
 +              sm->m = eap_method;
 +      if (!sm->m) {
 +              wpa_printf(MSG_DEBUG, "EAP: Could not find selected method: "
 +                         "vendor %d method %d",
 +                         sm->reqVendor, method);
 +              goto nak;
 +      }
 +
 +      sm->ClientTimeout = EAP_CLIENT_TIMEOUT_DEFAULT;
 +
 +      wpa_printf(MSG_DEBUG, "EAP: Initialize selected EAP method: "
 +                 "vendor %u method %u (%s)",
 +                 sm->reqVendor, method, sm->m->name);
 +      if (reinit)
 +              sm->eap_method_priv = sm->m->init_for_reauth(
 +                      sm, sm->eap_method_priv);
 +      else
 +              sm->eap_method_priv = sm->m->init(sm);
 +
 +      if (sm->eap_method_priv == NULL) {
 +              struct eap_peer_config *config = eap_get_config(sm);
 +              wpa_msg(sm->msg_ctx, MSG_INFO,
 +                      "EAP: Failed to initialize EAP method: vendor %u "
 +                      "method %u (%s)",
 +                      sm->reqVendor, method, sm->m->name);
 +              sm->m = NULL;
 +              sm->methodState = METHOD_NONE;
 +              sm->selectedMethod = EAP_TYPE_NONE;
 +              if (sm->reqMethod == EAP_TYPE_TLS && config &&
 +                  (config->pending_req_pin ||
 +                   config->pending_req_passphrase)) {
 +                      /*
 +                       * Return without generating Nak in order to allow
 +                       * entering of PIN code or passphrase to retry the
 +                       * current EAP packet.
 +                       */
 +                      wpa_printf(MSG_DEBUG, "EAP: Pending PIN/passphrase "
 +                                 "request - skip Nak");
 +                      return;
 +              }
 +
 +              goto nak;
 +      }
 +
 +      sm->methodState = METHOD_INIT;
 +      wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_METHOD
 +              "EAP vendor %u method %u (%s) selected",
 +              sm->reqVendor, method, sm->m->name);
 +      return;
 +
 +nak:
 +      wpabuf_free(sm->eapRespData);
 +      sm->eapRespData = NULL;
 +      sm->eapRespData = eap_sm_buildNak(sm, sm->reqId);
 +}
 +
 +
 +#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);
 +
-                       os_memcpy(sm->last_md5, sm->req_md5, 16);
++      msg = eap_msg_alloc(EAP_VENDOR_IETF, (EapType) 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.
 + */
 +SM_STATE(EAP, METHOD)
 +{
 +      struct wpabuf *eapReqData;
 +      struct eap_method_ret ret;
 +      int min_len = 1;
 +
 +      SM_ENTRY(EAP, METHOD);
 +      if (sm->m == NULL) {
 +              wpa_printf(MSG_WARNING, "EAP::METHOD - method not selected");
 +              return;
 +      }
 +
 +      eapReqData = eapol_get_eapReqData(sm);
 +      if (sm->m->vendor == EAP_VENDOR_IETF && sm->m->method == EAP_TYPE_LEAP)
 +              min_len = 0; /* LEAP uses EAP-Success without payload */
 +      if (!eap_hdr_len_valid(eapReqData, min_len))
 +              return;
 +
 +      /*
 +       * Get ignore, methodState, decision, allowNotifications, and
 +       * eapRespData. RFC 4137 uses three separate method procedure (check,
 +       * process, and buildResp) in this state. These have been combined into
 +       * a single function call to m->process() in order to optimize EAP
 +       * method implementation interface a bit. These procedures are only
 +       * used from within this METHOD state, so there is no need to keep
 +       * these as separate C functions.
 +       *
 +       * The RFC 4137 procedures return values as follows:
 +       * ignore = m.check(eapReqData)
 +       * (methodState, decision, allowNotifications) = m.process(eapReqData)
 +       * eapRespData = m.buildResp(reqId)
 +       */
 +      os_memset(&ret, 0, sizeof(ret));
 +      ret.ignore = sm->ignore;
 +      ret.methodState = sm->methodState;
 +      ret.decision = sm->decision;
 +      ret.allowNotifications = sm->allowNotifications;
 +      wpabuf_free(sm->eapRespData);
 +      sm->eapRespData = NULL;
 +      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 eapRespData=%p",
 +                 ret.ignore ? "TRUE" : "FALSE",
 +                 eap_sm_method_state_txt(ret.methodState),
 +                 eap_sm_decision_txt(ret.decision),
 +                 sm->eapRespData);
 +
 +      sm->ignore = ret.ignore;
 +      if (sm->ignore)
 +              return;
 +      sm->methodState = ret.methodState;
 +      sm->decision = ret.decision;
 +      sm->allowNotifications = ret.allowNotifications;
 +
 +      if (sm->m->isKeyAvailable && sm->m->getKey &&
 +          sm->m->isKeyAvailable(sm, sm->eap_method_priv)) {
 +              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);
 +              sm->eapSessionId = NULL;
 +              if (sm->m->getSessionId) {
 +                      sm->eapSessionId = sm->m->getSessionId(
 +                              sm, sm->eap_method_priv,
 +                              &sm->eapSessionIdLen);
 +                      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);
 +      }
 +}
 +
 +
 +/*
 + * This state signals the lower layer that a response packet is ready to be
 + * sent.
 + */
 +SM_STATE(EAP, SEND_RESPONSE)
 +{
 +      SM_ENTRY(EAP, SEND_RESPONSE);
 +      wpabuf_free(sm->lastRespData);
 +      if (sm->eapRespData) {
 +              if (sm->workaround)
-           os_memcmp(sm->req_md5, sm->last_md5, 16) != 0) {
++                      os_memcpy(sm->last_sha1, sm->req_sha1, 20);
 +              sm->lastId = sm->reqId;
 +              sm->lastRespData = wpabuf_dup(sm->eapRespData);
 +              eapol_set_bool(sm, EAPOL_eapResp, TRUE);
 +      } 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;
 +}
 +
 +
 +/*
 + * This state signals the lower layer that the request was discarded, and no
 + * response packet will be sent at this time.
 + */
 +SM_STATE(EAP, DISCARD)
 +{
 +      SM_ENTRY(EAP, DISCARD);
 +      eapol_set_bool(sm, EAPOL_eapReq, FALSE);
 +      eapol_set_bool(sm, EAPOL_eapNoResp, TRUE);
 +}
 +
 +
 +/*
 + * Handles requests for Identity method and builds a response.
 + */
 +SM_STATE(EAP, IDENTITY)
 +{
 +      const struct wpabuf *eapReqData;
 +
 +      SM_ENTRY(EAP, IDENTITY);
 +      eapReqData = eapol_get_eapReqData(sm);
 +      if (!eap_hdr_len_valid(eapReqData, 1))
 +              return;
 +      eap_sm_processIdentity(sm, eapReqData);
 +      wpabuf_free(sm->eapRespData);
 +      sm->eapRespData = NULL;
 +      sm->eapRespData = eap_sm_buildIdentity(sm, sm->reqId, 0);
 +}
 +
 +
 +/*
 + * Handles requests for Notification method and builds a response.
 + */
 +SM_STATE(EAP, NOTIFICATION)
 +{
 +      const struct wpabuf *eapReqData;
 +
 +      SM_ENTRY(EAP, NOTIFICATION);
 +      eapReqData = eapol_get_eapReqData(sm);
 +      if (!eap_hdr_len_valid(eapReqData, 1))
 +              return;
 +      eap_sm_processNotify(sm, eapReqData);
 +      wpabuf_free(sm->eapRespData);
 +      sm->eapRespData = NULL;
 +      sm->eapRespData = eap_sm_buildNotify(sm->reqId);
 +}
 +
 +
 +/*
 + * This state retransmits the previous response packet.
 + */
 +SM_STATE(EAP, RETRANSMIT)
 +{
 +      SM_ENTRY(EAP, RETRANSMIT);
 +      wpabuf_free(sm->eapRespData);
 +      if (sm->lastRespData)
 +              sm->eapRespData = wpabuf_dup(sm->lastRespData);
 +      else
 +              sm->eapRespData = NULL;
 +}
 +
 +
 +/*
 + * This state is entered in case of a successful completion of authentication
 + * and state machine waits here until port is disabled or EAP authentication is
 + * restarted.
 + */
 +SM_STATE(EAP, SUCCESS)
 +{
 +      SM_ENTRY(EAP, SUCCESS);
 +      if (sm->eapKeyData != NULL)
 +              sm->eapKeyAvailable = TRUE;
 +      eapol_set_bool(sm, EAPOL_eapSuccess, TRUE);
 +
 +      /*
 +       * RFC 4137 does not clear eapReq here, but this seems to be required
 +       * to avoid processing the same request twice when state machine is
 +       * initialized.
 +       */
 +      eapol_set_bool(sm, EAPOL_eapReq, FALSE);
 +
 +      /*
 +       * RFC 4137 does not set eapNoResp here, but this seems to be required
 +       * to get EAPOL Supplicant backend state machine into SUCCESS state. In
 +       * addition, either eapResp or eapNoResp is required to be set after
 +       * processing the received EAP frame.
 +       */
 +      eapol_set_bool(sm, EAPOL_eapNoResp, TRUE);
 +
 +      wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_SUCCESS
 +              "EAP authentication completed successfully");
 +}
 +
 +
 +/*
 + * This state is entered in case of a failure and state machine waits here
 + * until port is disabled or EAP authentication is restarted.
 + */
 +SM_STATE(EAP, FAILURE)
 +{
 +      SM_ENTRY(EAP, FAILURE);
 +      eapol_set_bool(sm, EAPOL_eapFail, TRUE);
 +
 +      /*
 +       * RFC 4137 does not clear eapReq here, but this seems to be required
 +       * to avoid processing the same request twice when state machine is
 +       * initialized.
 +       */
 +      eapol_set_bool(sm, EAPOL_eapReq, FALSE);
 +
 +      /*
 +       * RFC 4137 does not set eapNoResp here. However, either eapResp or
 +       * eapNoResp is required to be set after processing the received EAP
 +       * frame.
 +       */
 +      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;
 +}
 +
 +
 +static int eap_success_workaround(struct eap_sm *sm, int reqId, int lastId)
 +{
 +      /*
 +       * At least Microsoft IAS and Meetinghouse Aegis seem to be sending
 +       * EAP-Success/Failure with lastId + 1 even though RFC 3748 and
 +       * RFC 4137 require that reqId == lastId. In addition, it looks like
 +       * Ringmaster v2.1.2.0 would be using lastId + 2 in EAP-Success.
 +       *
 +       * Accept this kind of Id if EAP workarounds are enabled. These are
 +       * unauthenticated plaintext messages, so this should have minimal
 +       * security implications (bit easier to fake EAP-Success/Failure).
 +       */
 +      if (sm->workaround && (reqId == ((lastId + 1) & 0xff) ||
 +                             reqId == ((lastId + 2) & 0xff))) {
 +              wpa_printf(MSG_DEBUG, "EAP: Workaround for unexpected "
 +                         "identifier field in EAP Success: "
 +                         "reqId=%d lastId=%d (these are supposed to be "
 +                         "same)", reqId, lastId);
 +              return 1;
 +      }
 +      wpa_printf(MSG_DEBUG, "EAP: EAP-Success Id mismatch - reqId=%d "
 +                 "lastId=%d", reqId, lastId);
 +      return 0;
 +}
 +
 +
 +/*
 + * RFC 4137 - Appendix A.1: EAP Peer State Machine - State transitions
 + */
 +
 +static void eap_peer_sm_step_idle(struct eap_sm *sm)
 +{
 +      /*
 +       * The first three transitions are from RFC 4137. The last two are
 +       * local additions to handle special cases with LEAP and PEAP server
 +       * not sending EAP-Success in some cases.
 +       */
 +      if (eapol_get_bool(sm, EAPOL_eapReq))
 +              SM_ENTER(EAP, RECEIVED);
 +      else if ((eapol_get_bool(sm, EAPOL_altAccept) &&
 +                sm->decision != DECISION_FAIL) ||
 +               (eapol_get_int(sm, EAPOL_idleWhile) == 0 &&
 +                sm->decision == DECISION_UNCOND_SUCC))
 +              SM_ENTER(EAP, SUCCESS);
 +      else if (eapol_get_bool(sm, EAPOL_altReject) ||
 +               (eapol_get_int(sm, EAPOL_idleWhile) == 0 &&
 +                sm->decision != DECISION_UNCOND_SUCC) ||
 +               (eapol_get_bool(sm, EAPOL_altAccept) &&
 +                sm->methodState != METHOD_CONT &&
 +                sm->decision == DECISION_FAIL))
 +              SM_ENTER(EAP, FAILURE);
 +      else if (sm->selectedMethod == EAP_TYPE_LEAP &&
 +               sm->leap_done && sm->decision != DECISION_FAIL &&
 +               sm->methodState == METHOD_DONE)
 +              SM_ENTER(EAP, SUCCESS);
 +      else if (sm->selectedMethod == EAP_TYPE_PEAP &&
 +               sm->peap_done && sm->decision != DECISION_FAIL &&
 +               sm->methodState == METHOD_DONE)
 +              SM_ENTER(EAP, SUCCESS);
 +}
 +
 +
 +static int eap_peer_req_is_duplicate(struct eap_sm *sm)
 +{
 +      int duplicate;
 +
 +      duplicate = (sm->reqId == sm->lastId) && sm->rxReq;
 +      if (sm->workaround && duplicate &&
-                * unfortunately, such implementations exist. Use MD5 hash as
++          os_memcmp(sm->req_sha1, sm->last_sha1, 20) != 0) {
 +              /*
 +               * RFC 4137 uses (reqId == lastId) as the only verification for
 +               * duplicate EAP requests. However, this misses cases where the
 +               * AS is incorrectly using the same id again; and
-               md5_vector(1, addr, &plen, sm->req_md5);
++               * unfortunately, such implementations exist. Use SHA1 hash as
 +               * an extra verification for the packets being duplicate to
 +               * workaround these issues.
 +               */
 +              wpa_printf(MSG_DEBUG, "EAP: AS used the same Id again, but "
 +                         "EAP packets were not identical");
 +              wpa_printf(MSG_DEBUG, "EAP: workaround - assume this is not a "
 +                         "duplicate packet");
 +              duplicate = 0;
 +      }
 +
 +      return duplicate;
 +}
 +
 +
 +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);
 +
 +      /*
 +       * Two special cases below for LEAP are local additions to work around
 +       * odd LEAP behavior (EAP-Success in the middle of authentication and
 +       * then swapped roles). Other transitions are based on RFC 4137.
 +       */
 +      if (sm->rxSuccess && sm->decision != DECISION_FAIL &&
 +          (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) ||
 +                (sm->rxSuccess && sm->decision == DECISION_FAIL &&
 +                 (sm->selectedMethod != EAP_TYPE_LEAP ||
 +                  sm->methodState != METHOD_MAY_CONT))) &&
 +               (sm->reqId == sm->lastId ||
 +                eap_success_workaround(sm, sm->reqId, sm->lastId)))
 +              SM_ENTER(EAP, FAILURE);
 +      else if (sm->rxReq && duplicate)
 +              SM_ENTER(EAP, RETRANSMIT);
 +      else if (sm->rxReq && !duplicate &&
 +               sm->reqMethod == EAP_TYPE_NOTIFICATION &&
 +               sm->allowNotifications)
 +              SM_ENTER(EAP, NOTIFICATION);
 +      else if (sm->rxReq && !duplicate &&
 +               sm->selectedMethod == EAP_TYPE_NONE &&
 +               sm->reqMethod == EAP_TYPE_IDENTITY)
 +              SM_ENTER(EAP, IDENTITY);
 +      else if (sm->rxReq && !duplicate &&
 +               sm->selectedMethod == EAP_TYPE_NONE &&
 +               sm->reqMethod != EAP_TYPE_IDENTITY &&
 +               sm->reqMethod != EAP_TYPE_NOTIFICATION)
 +              SM_ENTER(EAP, GET_METHOD);
 +      else if (sm->rxReq && !duplicate &&
 +               sm->reqMethod == sm->selectedMethod &&
 +               sm->methodState != METHOD_DONE)
 +              SM_ENTER(EAP, METHOD);
 +      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);
 +}
 +
 +
 +static void eap_peer_sm_step_local(struct eap_sm *sm)
 +{
 +      switch (sm->EAP_state) {
 +      case EAP_INITIALIZE:
 +              SM_ENTER(EAP, IDLE);
 +              break;
 +      case EAP_DISABLED:
 +              if (eapol_get_bool(sm, EAPOL_portEnabled) &&
 +                  !sm->force_disabled)
 +                      SM_ENTER(EAP, INITIALIZE);
 +              break;
 +      case EAP_IDLE:
 +              eap_peer_sm_step_idle(sm);
 +              break;
 +      case EAP_RECEIVED:
 +              eap_peer_sm_step_received(sm);
 +              break;
 +      case EAP_GET_METHOD:
 +              if (sm->selectedMethod == sm->reqMethod)
 +                      SM_ENTER(EAP, METHOD);
 +              else
 +                      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;
 +      case EAP_SEND_RESPONSE:
 +              SM_ENTER(EAP, IDLE);
 +              break;
 +      case EAP_DISCARD:
 +              SM_ENTER(EAP, IDLE);
 +              break;
 +      case EAP_IDENTITY:
 +              SM_ENTER(EAP, SEND_RESPONSE);
 +              break;
 +      case EAP_NOTIFICATION:
 +              SM_ENTER(EAP, SEND_RESPONSE);
 +              break;
 +      case EAP_RETRANSMIT:
 +              SM_ENTER(EAP, SEND_RESPONSE);
 +              break;
 +      case EAP_SUCCESS:
 +              break;
 +      case EAP_FAILURE:
 +              break;
 +      }
 +}
 +
 +
 +SM_STEP(EAP)
 +{
 +      /* Global transitions */
 +      if (eapol_get_bool(sm, EAPOL_eapRestart) &&
 +          eapol_get_bool(sm, EAPOL_portEnabled))
 +              SM_ENTER_GLOBAL(EAP, INITIALIZE);
 +      else if (!eapol_get_bool(sm, EAPOL_portEnabled) || sm->force_disabled)
 +              SM_ENTER_GLOBAL(EAP, DISABLED);
 +      else if (sm->num_rounds > EAP_MAX_AUTH_ROUNDS) {
 +              /* RFC 4137 does not place any limit on number of EAP messages
 +               * in an authentication session. However, some error cases have
 +               * ended up in a state were EAP messages were sent between the
 +               * peer and server in a loop (e.g., TLS ACK frame in both
 +               * direction). Since this is quite undesired outcome, limit the
 +               * total number of EAP round-trips and abort authentication if
 +               * this limit is exceeded.
 +               */
 +              if (sm->num_rounds == EAP_MAX_AUTH_ROUNDS + 1) {
 +                      wpa_msg(sm->msg_ctx, MSG_INFO, "EAP: more than %d "
 +                              "authentication rounds - abort",
 +                              EAP_MAX_AUTH_ROUNDS);
 +                      sm->num_rounds++;
 +                      SM_ENTER_GLOBAL(EAP, FAILURE);
 +              }
 +      } else {
 +              /* Local transitions */
 +              eap_peer_sm_step_local(sm);
 +      }
 +}
 +
 +
 +static Boolean eap_sm_allowMethod(struct eap_sm *sm, int vendor,
 +                                EapType method)
 +{
 +      if (!eap_allowed_method(sm, vendor, method)) {
 +              wpa_printf(MSG_DEBUG, "EAP: configuration does not allow: "
 +                         "vendor %u method %u", vendor, method);
 +              return FALSE;
 +      }
 +      if (eap_peer_get_eap_method(vendor, method))
 +              return TRUE;
 +      wpa_printf(MSG_DEBUG, "EAP: not included in build: "
 +                 "vendor %u method %u", vendor, method);
 +      return FALSE;
 +}
 +
 +
 +static struct wpabuf * eap_sm_build_expanded_nak(
 +      struct eap_sm *sm, int id, const struct eap_method *methods,
 +      size_t count)
 +{
 +      struct wpabuf *resp;
 +      int found = 0;
 +      const struct eap_method *m;
 +
 +      wpa_printf(MSG_DEBUG, "EAP: Building expanded EAP-Nak");
 +
 +      /* RFC 3748 - 5.3.2: Expanded Nak */
 +      resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_EXPANDED,
 +                           8 + 8 * (count + 1), EAP_CODE_RESPONSE, id);
 +      if (resp == NULL)
 +              return NULL;
 +
 +      wpabuf_put_be24(resp, EAP_VENDOR_IETF);
 +      wpabuf_put_be32(resp, EAP_TYPE_NAK);
 +
 +      for (m = methods; m; m = m->next) {
 +              if (sm->reqVendor == m->vendor &&
 +                  sm->reqVendorMethod == m->method)
 +                      continue; /* do not allow the current method again */
 +              if (eap_allowed_method(sm, m->vendor, m->method)) {
 +                      wpa_printf(MSG_DEBUG, "EAP: allowed type: "
 +                                 "vendor=%u method=%u",
 +                                 m->vendor, m->method);
 +                      wpabuf_put_u8(resp, EAP_TYPE_EXPANDED);
 +                      wpabuf_put_be24(resp, m->vendor);
 +                      wpabuf_put_be32(resp, m->method);
 +
 +                      found++;
 +              }
 +      }
 +      if (!found) {
 +              wpa_printf(MSG_DEBUG, "EAP: no more allowed methods");
 +              wpabuf_put_u8(resp, EAP_TYPE_EXPANDED);
 +              wpabuf_put_be24(resp, EAP_VENDOR_IETF);
 +              wpabuf_put_be32(resp, EAP_TYPE_NONE);
 +      }
 +
 +      eap_update_len(resp);
 +
 +      return resp;
 +}
 +
 +
 +static struct wpabuf * eap_sm_buildNak(struct eap_sm *sm, int id)
 +{
 +      struct wpabuf *resp;
 +      u8 *start;
 +      int found = 0, expanded_found = 0;
 +      size_t count;
 +      const struct eap_method *methods, *m;
 +
 +      wpa_printf(MSG_DEBUG, "EAP: Building EAP-Nak (requested type %u "
 +                 "vendor=%u method=%u not allowed)", sm->reqMethod,
 +                 sm->reqVendor, sm->reqVendorMethod);
 +      methods = eap_peer_get_methods(&count);
 +      if (methods == NULL)
 +              return NULL;
 +      if (sm->reqMethod == EAP_TYPE_EXPANDED)
 +              return eap_sm_build_expanded_nak(sm, id, methods, count);
 +
 +      /* RFC 3748 - 5.3.1: Legacy Nak */
 +      resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_NAK,
 +                           sizeof(struct eap_hdr) + 1 + count + 1,
 +                           EAP_CODE_RESPONSE, id);
 +      if (resp == NULL)
 +              return NULL;
 +
 +      start = wpabuf_put(resp, 0);
 +      for (m = methods; m; m = m->next) {
 +              if (m->vendor == EAP_VENDOR_IETF && m->method == sm->reqMethod)
 +                      continue; /* do not allow the current method again */
 +              if (eap_allowed_method(sm, m->vendor, m->method)) {
 +                      if (m->vendor != EAP_VENDOR_IETF) {
 +                              if (expanded_found)
 +                                      continue;
 +                              expanded_found = 1;
 +                              wpabuf_put_u8(resp, EAP_TYPE_EXPANDED);
 +                      } else
 +                              wpabuf_put_u8(resp, m->method);
 +                      found++;
 +              }
 +      }
 +      if (!found)
 +              wpabuf_put_u8(resp, EAP_TYPE_NONE);
 +      wpa_hexdump(MSG_DEBUG, "EAP: allowed methods", start, found);
 +
 +      eap_update_len(resp);
 +
 +      return resp;
 +}
 +
 +
 +static void eap_sm_processIdentity(struct eap_sm *sm, const struct wpabuf *req)
 +{
 +      const u8 *pos;
 +      size_t msg_len;
 +
 +      wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_STARTED
 +              "EAP authentication started");
 +      eap_notify_status(sm, "started", "");
 +
 +      pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_IDENTITY, req,
 +                             &msg_len);
 +      if (pos == NULL)
 +              return;
 +
 +      /*
 +       * RFC 3748 - 5.1: Identity
 +       * Data field may contain a displayable message in UTF-8. If this
 +       * includes NUL-character, only the data before that should be
 +       * displayed. Some EAP implementasitons may piggy-back additional
 +       * options after the NUL.
 +       */
 +      /* TODO: could save displayable message so that it can be shown to the
 +       * user in case of interaction is required */
 +      wpa_hexdump_ascii(MSG_DEBUG, "EAP: EAP-Request Identity data",
 +                        pos, msg_len);
 +}
 +
 +
 +#ifdef PCSC_FUNCS
 +
 +/*
 + * Rules for figuring out MNC length based on IMSI for SIM cards that do not
 + * include MNC length field.
 + */
 +static int mnc_len_from_imsi(const char *imsi)
 +{
 +      char mcc_str[4];
 +      unsigned int mcc;
 +
 +      os_memcpy(mcc_str, imsi, 3);
 +      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 */
 +
 +      return -1;
 +}
 +
 +
 +static int eap_sm_append_3gpp_realm(struct eap_sm *sm, char *imsi,
 +                                  size_t max_len, size_t *imsi_len)
 +{
 +      int mnc_len;
 +      char *pos, mnc[4];
 +
 +      if (*imsi_len + 36 > max_len) {
 +              wpa_printf(MSG_WARNING, "No room for realm in IMSI buffer");
 +              return -1;
 +      }
 +
 +      /* MNC (2 or 3 digits) */
 +      mnc_len = scard_get_mnc_len(sm->scard_ctx);
 +      if (mnc_len < 0)
 +              mnc_len = mnc_len_from_imsi(imsi);
 +      if (mnc_len < 0) {
 +              wpa_printf(MSG_INFO, "Failed to get MNC length from (U)SIM "
 +                         "assuming 3");
 +              mnc_len = 3;
 +      }
 +
 +      if (mnc_len == 2) {
 +              mnc[0] = '0';
 +              mnc[1] = imsi[3];
 +              mnc[2] = imsi[4];
 +      } else if (mnc_len == 3) {
 +              mnc[0] = imsi[3];
 +              mnc[1] = imsi[4];
 +              mnc[2] = imsi[5];
 +      }
 +      mnc[3] = '\0';
 +
 +      pos = imsi + *imsi_len;
 +      pos += os_snprintf(pos, imsi + max_len - pos,
 +                         "@wlan.mnc%s.mcc%c%c%c.3gppnetwork.org",
 +                         mnc, imsi[0], imsi[1], imsi[2]);
 +      *imsi_len = pos - imsi;
 +
 +      return 0;
 +}
 +
 +
 +static int eap_sm_imsi_identity(struct eap_sm *sm,
 +                              struct eap_peer_config *conf)
 +{
 +      enum { EAP_SM_SIM, EAP_SM_AKA, EAP_SM_AKA_PRIME } method = EAP_SM_SIM;
 +      char imsi[100];
 +      size_t imsi_len;
 +      struct eap_method_type *m = conf->eap_methods;
 +      int i;
 +
 +      imsi_len = sizeof(imsi);
 +      if (scard_get_imsi(sm->scard_ctx, imsi, &imsi_len)) {
 +              wpa_printf(MSG_WARNING, "Failed to get IMSI from SIM");
 +              return -1;
 +      }
 +
 +      wpa_hexdump_ascii(MSG_DEBUG, "IMSI", (u8 *) imsi, imsi_len);
 +
 +      if (imsi_len < 7) {
 +              wpa_printf(MSG_WARNING, "Too short IMSI for SIM identity");
 +              return -1;
 +      }
 +
 +      if (eap_sm_append_3gpp_realm(sm, imsi, sizeof(imsi), &imsi_len) < 0) {
 +              wpa_printf(MSG_WARNING, "Could not add realm to SIM identity");
 +              return -1;
 +      }
 +      wpa_hexdump_ascii(MSG_DEBUG, "IMSI + realm", (u8 *) imsi, imsi_len);
 +
 +      for (i = 0; m && (m[i].vendor != EAP_VENDOR_IETF ||
 +                        m[i].method != EAP_TYPE_NONE); i++) {
 +              if (m[i].vendor == EAP_VENDOR_IETF &&
 +                  m[i].method == EAP_TYPE_AKA_PRIME) {
 +                      method = EAP_SM_AKA_PRIME;
 +                      break;
 +              }
 +
 +              if (m[i].vendor == EAP_VENDOR_IETF &&
 +                  m[i].method == EAP_TYPE_AKA) {
 +                      method = EAP_SM_AKA;
 +                      break;
 +              }
 +      }
 +
 +      os_free(conf->identity);
 +      conf->identity = os_malloc(1 + imsi_len);
 +      if (conf->identity == NULL) {
 +              wpa_printf(MSG_WARNING, "Failed to allocate buffer for "
 +                         "IMSI-based identity");
 +              return -1;
 +      }
 +
 +      switch (method) {
 +      case EAP_SM_SIM:
 +              conf->identity[0] = '1';
 +              break;
 +      case EAP_SM_AKA:
 +              conf->identity[0] = '0';
 +              break;
 +      case EAP_SM_AKA_PRIME:
 +              conf->identity[0] = '6';
 +              break;
 +      }
 +      os_memcpy(conf->identity + 1, imsi, imsi_len);
 +      conf->identity_len = 1 + imsi_len;
 +
 +      return 0;
 +}
 +
 +#endif /* PCSC_FUNCS */
 +
 +
 +static int eap_sm_set_scard_pin(struct eap_sm *sm,
 +                              struct eap_peer_config *conf)
 +{
 +#ifdef PCSC_FUNCS
 +      if (scard_set_pin(sm->scard_ctx, conf->pin)) {
 +              /*
 +               * Make sure the same PIN is not tried again in order to avoid
 +               * blocking SIM.
 +               */
 +              os_free(conf->pin);
 +              conf->pin = NULL;
 +
 +              wpa_printf(MSG_WARNING, "PIN validation failed");
 +              eap_sm_request_pin(sm);
 +              return -1;
 +      }
 +      return 0;
 +#else /* PCSC_FUNCS */
 +      return -1;
 +#endif /* PCSC_FUNCS */
 +}
 +
 +static int eap_sm_get_scard_identity(struct eap_sm *sm,
 +                                   struct eap_peer_config *conf)
 +{
 +#ifdef PCSC_FUNCS
 +      if (eap_sm_set_scard_pin(sm, conf))
 +              return -1;
 +
 +      return eap_sm_imsi_identity(sm, conf);
 +#else /* PCSC_FUNCS */
 +      return -1;
 +#endif /* PCSC_FUNCS */
 +}
 +
 +
 +/**
 + * eap_sm_buildIdentity - Build EAP-Identity/Response for the current network
 + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
 + * @id: EAP identifier for the packet
 + * @encrypted: Whether the packet is for encrypted tunnel (EAP phase 2)
 + * Returns: Pointer to the allocated EAP-Identity/Response packet or %NULL on
 + * failure
 + *
 + * This function allocates and builds an EAP-Identity/Response packet for the
 + * current network. The caller is responsible for freeing the returned data.
 + */
 +struct wpabuf * eap_sm_buildIdentity(struct eap_sm *sm, int id, int encrypted)
 +{
 +      struct eap_peer_config *config = eap_get_config(sm);
 +      struct wpabuf *resp;
 +      const u8 *identity;
 +      size_t identity_len;
 +
 +      if (config == NULL) {
 +              wpa_printf(MSG_WARNING, "EAP: buildIdentity: configuration "
 +                         "was not available");
 +              return NULL;
 +      }
 +
 +      if (sm->m && sm->m->get_identity &&
 +          (identity = sm->m->get_identity(sm, sm->eap_method_priv,
 +                                          &identity_len)) != NULL) {
 +              wpa_hexdump_ascii(MSG_DEBUG, "EAP: using method re-auth "
 +                                "identity", identity, identity_len);
 +      } else if (!encrypted && config->anonymous_identity) {
 +              identity = config->anonymous_identity;
 +              identity_len = config->anonymous_identity_len;
 +              wpa_hexdump_ascii(MSG_DEBUG, "EAP: using anonymous identity",
 +                                identity, identity_len);
 +      } else {
 +              identity = config->identity;
 +              identity_len = config->identity_len;
 +              wpa_hexdump_ascii(MSG_DEBUG, "EAP: using real identity",
 +                                identity, identity_len);
 +      }
 +
 +      if (identity == NULL) {
 +              wpa_printf(MSG_WARNING, "EAP: buildIdentity: identity "
 +                         "configuration was not available");
 +              if (config->pcsc) {
 +                      if (eap_sm_get_scard_identity(sm, config) < 0)
 +                              return NULL;
 +                      identity = config->identity;
 +                      identity_len = config->identity_len;
 +                      wpa_hexdump_ascii(MSG_DEBUG, "permanent identity from "
 +                                        "IMSI", identity, identity_len);
 +              } else {
 +                      eap_sm_request_identity(sm);
 +                      return NULL;
 +              }
 +      } else if (config->pcsc) {
 +              if (eap_sm_set_scard_pin(sm, config) < 0)
 +                      return NULL;
 +      }
 +
 +      resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_IDENTITY, identity_len,
 +                           EAP_CODE_RESPONSE, id);
 +      if (resp == NULL)
 +              return NULL;
 +
 +      wpabuf_put_data(resp, identity, identity_len);
 +
 +      return resp;
 +}
 +
 +
 +static void eap_sm_processNotify(struct eap_sm *sm, const struct wpabuf *req)
 +{
 +      const u8 *pos;
 +      char *msg;
 +      size_t i, msg_len;
 +
 +      pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_NOTIFICATION, req,
 +                             &msg_len);
 +      if (pos == NULL)
 +              return;
 +      wpa_hexdump_ascii(MSG_DEBUG, "EAP: EAP-Request Notification data",
 +                        pos, msg_len);
 +
 +      msg = os_malloc(msg_len + 1);
 +      if (msg == NULL)
 +              return;
 +      for (i = 0; i < msg_len; i++)
 +              msg[i] = isprint(pos[i]) ? (char) pos[i] : '_';
 +      msg[msg_len] = '\0';
 +      wpa_msg(sm->msg_ctx, MSG_INFO, "%s%s",
 +              WPA_EVENT_EAP_NOTIFICATION, msg);
 +      os_free(msg);
 +}
 +
 +
 +static struct wpabuf * eap_sm_buildNotify(int id)
 +{
 +      struct wpabuf *resp;
 +
 +      wpa_printf(MSG_DEBUG, "EAP: Generating EAP-Response Notification");
 +      resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_NOTIFICATION, 0,
 +                           EAP_CODE_RESPONSE, id);
 +      if (resp == NULL)
 +              return NULL;
 +
 +      return resp;
 +}
 +
 +
 +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;
 +      size_t plen;
 +      const u8 *pos;
 +
 +      sm->rxReq = sm->rxResp = sm->rxSuccess = sm->rxFailure = FALSE;
 +      sm->reqId = 0;
 +      sm->reqMethod = EAP_TYPE_NONE;
 +      sm->reqVendor = EAP_VENDOR_IETF;
 +      sm->reqVendorMethod = EAP_TYPE_NONE;
 +
 +      if (req == NULL || wpabuf_len(req) < sizeof(*hdr))
 +              return;
 +
 +      hdr = wpabuf_head(req);
 +      plen = be_to_host16(hdr->length);
 +      if (plen > wpabuf_len(req)) {
 +              wpa_printf(MSG_DEBUG, "EAP: Ignored truncated EAP-Packet "
 +                         "(len=%lu plen=%lu)",
 +                         (unsigned long) wpabuf_len(req),
 +                         (unsigned long) plen);
 +              return;
 +      }
 +
 +      sm->reqId = hdr->identifier;
 +
 +      if (sm->workaround) {
 +              const u8 *addr[1];
 +              addr[0] = wpabuf_head(req);
-                                struct eapol_callbacks *eapol_cb,
++              sha1_vector(1, addr, &plen, sm->req_sha1);
 +      }
 +
 +      switch (hdr->code) {
 +      case EAP_CODE_REQUEST:
 +              if (plen < sizeof(*hdr) + 1) {
 +                      wpa_printf(MSG_DEBUG, "EAP: Too short EAP-Request - "
 +                                 "no Type field");
 +                      return;
 +              }
 +              sm->rxReq = TRUE;
 +              pos = (const u8 *) (hdr + 1);
 +              sm->reqMethod = *pos++;
 +              if (sm->reqMethod == EAP_TYPE_EXPANDED) {
 +                      if (plen < sizeof(*hdr) + 8) {
 +                              wpa_printf(MSG_DEBUG, "EAP: Ignored truncated "
 +                                         "expanded EAP-Packet (plen=%lu)",
 +                                         (unsigned long) plen);
 +                              return;
 +                      }
 +                      sm->reqVendor = WPA_GET_BE24(pos);
 +                      pos += 3;
 +                      sm->reqVendorMethod = WPA_GET_BE32(pos);
 +              }
 +              wpa_printf(MSG_DEBUG, "EAP: Received EAP-Request id=%d "
 +                         "method=%u vendor=%u vendorMethod=%u",
 +                         sm->reqId, sm->reqMethod, sm->reqVendor,
 +                         sm->reqVendorMethod);
 +              break;
 +      case EAP_CODE_RESPONSE:
 +              if (sm->selectedMethod == EAP_TYPE_LEAP) {
 +                      /*
 +                       * LEAP differs from RFC 4137 by using reversed roles
 +                       * for mutual authentication and because of this, we
 +                       * need to accept EAP-Response frames if LEAP is used.
 +                       */
 +                      if (plen < sizeof(*hdr) + 1) {
 +                              wpa_printf(MSG_DEBUG, "EAP: Too short "
 +                                         "EAP-Response - no Type field");
 +                              return;
 +                      }
 +                      sm->rxResp = TRUE;
 +                      pos = (const u8 *) (hdr + 1);
 +                      sm->reqMethod = *pos;
 +                      wpa_printf(MSG_DEBUG, "EAP: Received EAP-Response for "
 +                                 "LEAP method=%d id=%d",
 +                                 sm->reqMethod, sm->reqId);
 +                      break;
 +              }
 +              wpa_printf(MSG_DEBUG, "EAP: Ignored EAP-Response");
 +              break;
 +      case EAP_CODE_SUCCESS:
 +              wpa_printf(MSG_DEBUG, "EAP: Received EAP-Success");
 +              eap_notify_status(sm, "completion", "success");
 +              sm->rxSuccess = TRUE;
 +              break;
 +      case EAP_CODE_FAILURE:
 +              wpa_printf(MSG_DEBUG, "EAP: Received EAP-Failure");
 +              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);
 +              break;
 +      }
 +}
 +
 +
 +static void eap_peer_sm_tls_event(void *ctx, enum tls_event ev,
 +                                union tls_event_data *data)
 +{
 +      struct eap_sm *sm = ctx;
 +      char *hash_hex = NULL;
 +
 +      switch (ev) {
 +      case TLS_CERT_CHAIN_SUCCESS:
 +              eap_notify_status(sm, "remote certificate verification",
 +                                "success");
 +              break;
 +      case TLS_CERT_CHAIN_FAILURE:
 +              wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_TLS_CERT_ERROR
 +                      "reason=%d depth=%d subject='%s' err='%s'",
 +                      data->cert_fail.reason,
 +                      data->cert_fail.depth,
 +                      data->cert_fail.subject,
 +                      data->cert_fail.reason_txt);
 +              eap_notify_status(sm, "remote certificate verification",
 +                                data->cert_fail.reason_txt);
 +              break;
 +      case TLS_PEER_CERTIFICATE:
 +              if (!sm->eapol_cb->notify_cert)
 +                      break;
 +
 +              if (data->peer_cert.hash) {
 +                      size_t len = data->peer_cert.hash_len * 2 + 1;
 +                      hash_hex = os_malloc(len);
 +                      if (hash_hex) {
 +                              wpa_snprintf_hex(hash_hex, len,
 +                                               data->peer_cert.hash,
 +                                               data->peer_cert.hash_len);
 +                      }
 +              }
 +
 +              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:
 +              if (data->alert.is_local)
 +                      eap_notify_status(sm, "local TLS alert",
 +                                        data->alert.description);
 +              else
 +                      eap_notify_status(sm, "remote TLS alert",
 +                                        data->alert.description);
 +              break;
 +      }
 +
 +      os_free(hash_hex);
 +}
 +
 +
 +/**
 + * eap_peer_sm_init - Allocate and initialize EAP peer state machine
 + * @eapol_ctx: Context data to be used with eapol_cb calls
 + * @eapol_cb: Pointer to EAPOL callback functions
 + * @msg_ctx: Context data for wpa_msg() calls
 + * @conf: EAP configuration
 + * Returns: Pointer to the allocated EAP state machine or %NULL on failure
 + *
 + * This function allocates and initializes an EAP state machine. In addition,
 + * this initializes TLS library for the new EAP state machine. eapol_cb pointer
 + * will be in use until eap_peer_sm_deinit() is used to deinitialize this EAP
 + * state machine. Consequently, the caller must make sure that this data
 + * structure remains alive while the EAP state machine is active.
 + */
 +struct eap_sm * eap_peer_sm_init(void *eapol_ctx,
-       u8 type = eap_peer_get_type(name, &v);
++                               const struct eapol_callbacks *eapol_cb,
 +                               void *msg_ctx, struct eap_config *conf)
 +{
 +      struct eap_sm *sm;
 +      struct tls_config tlsconf;
 +
 +      sm = os_zalloc(sizeof(*sm));
 +      if (sm == NULL)
 +              return NULL;
 +      sm->eapol_ctx = eapol_ctx;
 +      sm->eapol_cb = eapol_cb;
 +      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 */
 +      tlsconf.event_cb = eap_peer_sm_tls_event;
 +      tlsconf.cb_ctx = sm;
 +      tlsconf.cert_in_cb = conf->cert_in_cb;
 +      sm->ssl_ctx = tls_init(&tlsconf);
 +      if (sm->ssl_ctx == NULL) {
 +              wpa_printf(MSG_WARNING, "SSL: Failed to initialize TLS "
 +                         "context.");
 +              os_free(sm);
 +              return NULL;
 +      }
 +
 +      sm->ssl_ctx2 = tls_init(&tlsconf);
 +      if (sm->ssl_ctx2 == NULL) {
 +              wpa_printf(MSG_INFO, "SSL: Failed to initialize TLS "
 +                         "context (2).");
 +              /* Run without separate TLS context within TLS tunnel */
 +      }
 +
 +      return sm;
 +}
 +
 +
 +/**
 + * eap_peer_sm_deinit - Deinitialize and free an EAP peer state machine
 + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
 + *
 + * This function deinitializes EAP state machine and frees all allocated
 + * resources.
 + */
 +void eap_peer_sm_deinit(struct eap_sm *sm)
 +{
 +      if (sm == NULL)
 +              return;
 +      eap_deinit_prev_method(sm, "EAP deinit");
 +      eap_sm_abort(sm);
 +      if (sm->ssl_ctx2)
 +              tls_deinit(sm->ssl_ctx2);
 +      tls_deinit(sm->ssl_ctx);
 +      eap_peer_erp_free_keys(sm);
 +      os_free(sm);
 +}
 +
 +
 +/**
 + * eap_peer_sm_step - Step EAP peer state machine
 + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
 + * Returns: 1 if EAP state was changed or 0 if not
 + *
 + * This function advances EAP state machine to a new state to match with the
 + * current variables. This should be called whenever variables used by the EAP
 + * state machine have changed.
 + */
 +int eap_peer_sm_step(struct eap_sm *sm)
 +{
 +      int res = 0;
 +      do {
 +              sm->changed = FALSE;
 +              SM_STEP_RUN(EAP);
 +              if (sm->changed)
 +                      res = 1;
 +      } while (sm->changed);
 +      return res;
 +}
 +
 +
 +/**
 + * eap_sm_abort - Abort EAP authentication
 + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
 + *
 + * Release system resources that have been allocated for the authentication
 + * session without fully deinitializing the EAP state machine.
 + */
 +void eap_sm_abort(struct eap_sm *sm)
 +{
 +      wpabuf_free(sm->lastRespData);
 +      sm->lastRespData = NULL;
 +      wpabuf_free(sm->eapRespData);
 +      sm->eapRespData = NULL;
 +      eap_sm_free_key(sm);
 +      os_free(sm->eapSessionId);
 +      sm->eapSessionId = NULL;
 +
 +      /* This is not clearly specified in the EAP statemachines draft, but
 +       * it seems necessary to make sure that some of the EAPOL variables get
 +       * cleared for the next authentication. */
 +      eapol_set_bool(sm, EAPOL_eapSuccess, FALSE);
 +}
 +
 +
 +#ifdef CONFIG_CTRL_IFACE
 +static const char * eap_sm_state_txt(int state)
 +{
 +      switch (state) {
 +      case EAP_INITIALIZE:
 +              return "INITIALIZE";
 +      case EAP_DISABLED:
 +              return "DISABLED";
 +      case EAP_IDLE:
 +              return "IDLE";
 +      case EAP_RECEIVED:
 +              return "RECEIVED";
 +      case EAP_GET_METHOD:
 +              return "GET_METHOD";
 +      case EAP_METHOD:
 +              return "METHOD";
 +      case EAP_SEND_RESPONSE:
 +              return "SEND_RESPONSE";
 +      case EAP_DISCARD:
 +              return "DISCARD";
 +      case EAP_IDENTITY:
 +              return "IDENTITY";
 +      case EAP_NOTIFICATION:
 +              return "NOTIFICATION";
 +      case EAP_RETRANSMIT:
 +              return "RETRANSMIT";
 +      case EAP_SUCCESS:
 +              return "SUCCESS";
 +      case EAP_FAILURE:
 +              return "FAILURE";
 +      default:
 +              return "UNKNOWN";
 +      }
 +}
 +#endif /* CONFIG_CTRL_IFACE */
 +
 +
 +#if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG)
 +static const char * eap_sm_method_state_txt(EapMethodState state)
 +{
 +      switch (state) {
 +      case METHOD_NONE:
 +              return "NONE";
 +      case METHOD_INIT:
 +              return "INIT";
 +      case METHOD_CONT:
 +              return "CONT";
 +      case METHOD_MAY_CONT:
 +              return "MAY_CONT";
 +      case METHOD_DONE:
 +              return "DONE";
 +      default:
 +              return "UNKNOWN";
 +      }
 +}
 +
 +
 +static const char * eap_sm_decision_txt(EapDecision decision)
 +{
 +      switch (decision) {
 +      case DECISION_FAIL:
 +              return "FAIL";
 +      case DECISION_COND_SUCC:
 +              return "COND_SUCC";
 +      case DECISION_UNCOND_SUCC:
 +              return "UNCOND_SUCC";
 +      default:
 +              return "UNKNOWN";
 +      }
 +}
 +#endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */
 +
 +
 +#ifdef CONFIG_CTRL_IFACE
 +
 +/**
 + * eap_sm_get_status - Get EAP state machine status
 + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
 + * @buf: Buffer for status information
 + * @buflen: Maximum buffer length
 + * @verbose: Whether to include verbose status information
 + * Returns: Number of bytes written to buf.
 + *
 + * Query EAP state machine for status information. This function fills in a
 + * text area with current status information from the EAPOL state machine. If
 + * the buffer (buf) is not large enough, status information will be truncated
 + * to fit the buffer.
 + */
 +int eap_sm_get_status(struct eap_sm *sm, char *buf, size_t buflen, int verbose)
 +{
 +      int len, ret;
 +
 +      if (sm == NULL)
 +              return 0;
 +
 +      len = os_snprintf(buf, buflen,
 +                        "EAP state=%s\n",
 +                        eap_sm_state_txt(sm->EAP_state));
 +      if (os_snprintf_error(buflen, len))
 +              return 0;
 +
 +      if (sm->selectedMethod != EAP_TYPE_NONE) {
 +              const char *name;
 +              if (sm->m) {
 +                      name = sm->m->name;
 +              } else {
 +                      const struct eap_method *m =
 +                              eap_peer_get_eap_method(EAP_VENDOR_IETF,
 +                                                      sm->selectedMethod);
 +                      if (m)
 +                              name = m->name;
 +                      else
 +                              name = "?";
 +              }
 +              ret = os_snprintf(buf + len, buflen - len,
 +                                "selectedMethod=%d (EAP-%s)\n",
 +                                sm->selectedMethod, name);
 +              if (os_snprintf_error(buflen - len, ret))
 +                      return len;
 +              len += ret;
 +
 +              if (sm->m && sm->m->get_status) {
 +                      len += sm->m->get_status(sm, sm->eap_method_priv,
 +                                               buf + len, buflen - len,
 +                                               verbose);
 +              }
 +      }
 +
 +      if (verbose) {
 +              ret = os_snprintf(buf + len, buflen - len,
 +                                "reqMethod=%d\n"
 +                                "methodState=%s\n"
 +                                "decision=%s\n"
 +                                "ClientTimeout=%d\n",
 +                                sm->reqMethod,
 +                                eap_sm_method_state_txt(sm->methodState),
 +                                eap_sm_decision_txt(sm->decision),
 +                                sm->ClientTimeout);
 +              if (os_snprintf_error(buflen - len, ret))
 +                      return len;
 +              len += ret;
 +      }
 +
 +      return len;
 +}
 +#endif /* CONFIG_CTRL_IFACE */
 +
 +
 +#if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG)
 +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;
 +      const char *txt = NULL;
 +      char *tmp;
 +
 +      if (sm == NULL)
 +              return;
 +      config = eap_get_config(sm);
 +      if (config == NULL)
 +              return;
 +
 +      switch (field) {
 +      case WPA_CTRL_REQ_EAP_IDENTITY:
 +              config->pending_req_identity++;
 +              break;
 +      case WPA_CTRL_REQ_EAP_PASSWORD:
 +              config->pending_req_password++;
 +              break;
 +      case WPA_CTRL_REQ_EAP_NEW_PASSWORD:
 +              config->pending_req_new_password++;
 +              break;
 +      case WPA_CTRL_REQ_EAP_PIN:
 +              config->pending_req_pin++;
 +              break;
 +      case WPA_CTRL_REQ_EAP_OTP:
 +              if (msg) {
 +                      tmp = os_malloc(msglen + 3);
 +                      if (tmp == NULL)
 +                              return;
 +                      tmp[0] = '[';
 +                      os_memcpy(tmp + 1, msg, msglen);
 +                      tmp[msglen + 1] = ']';
 +                      tmp[msglen + 2] = '\0';
 +                      txt = tmp;
 +                      os_free(config->pending_req_otp);
 +                      config->pending_req_otp = tmp;
 +                      config->pending_req_otp_len = msglen + 3;
 +              } else {
 +                      if (config->pending_req_otp == NULL)
 +                              return;
 +                      txt = config->pending_req_otp;
 +              }
 +              break;
 +      case WPA_CTRL_REQ_EAP_PASSPHRASE:
 +              config->pending_req_passphrase++;
 +              break;
 +      case WPA_CTRL_REQ_SIM:
 +              txt = msg;
 +              break;
 +      default:
 +              return;
 +      }
 +
 +      if (sm->eapol_cb->eap_param_needed)
 +              sm->eapol_cb->eap_param_needed(sm->eapol_ctx, field, txt);
 +}
 +#else /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */
 +#define eap_sm_request(sm, type, msg, msglen) do { } while (0)
 +#endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */
 +
 +const char * eap_sm_get_method_name(struct eap_sm *sm)
 +{
 +      if (sm->m == NULL)
 +              return "UNKNOWN";
 +      return sm->m->name;
 +}
 +
 +
 +/**
 + * eap_sm_request_identity - Request identity from user (ctrl_iface)
 + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
 + *
 + * EAP methods can call this function to request identity information for the
 + * current network. This is normally called when the identity is not included
 + * in the network configuration. The request will be sent to monitor programs
 + * through the control interface.
 + */
 +void eap_sm_request_identity(struct eap_sm *sm)
 +{
 +      eap_sm_request(sm, WPA_CTRL_REQ_EAP_IDENTITY, NULL, 0);
 +}
 +
 +
 +/**
 + * eap_sm_request_password - Request password from user (ctrl_iface)
 + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
 + *
 + * EAP methods can call this function to request password information for the
 + * current network. This is normally called when the password is not included
 + * in the network configuration. The request will be sent to monitor programs
 + * through the control interface.
 + */
 +void eap_sm_request_password(struct eap_sm *sm)
 +{
 +      eap_sm_request(sm, WPA_CTRL_REQ_EAP_PASSWORD, NULL, 0);
 +}
 +
 +
 +/**
 + * eap_sm_request_new_password - Request new password from user (ctrl_iface)
 + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
 + *
 + * EAP methods can call this function to request new password information for
 + * the current network. This is normally called when the EAP method indicates
 + * that the current password has expired and password change is required. The
 + * request will be sent to monitor programs through the control interface.
 + */
 +void eap_sm_request_new_password(struct eap_sm *sm)
 +{
 +      eap_sm_request(sm, WPA_CTRL_REQ_EAP_NEW_PASSWORD, NULL, 0);
 +}
 +
 +
 +/**
 + * eap_sm_request_pin - Request SIM or smart card PIN from user (ctrl_iface)
 + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
 + *
 + * EAP methods can call this function to request SIM or smart card PIN
 + * information for the current network. This is normally called when the PIN is
 + * not included in the network configuration. The request will be sent to
 + * monitor programs through the control interface.
 + */
 +void eap_sm_request_pin(struct eap_sm *sm)
 +{
 +      eap_sm_request(sm, WPA_CTRL_REQ_EAP_PIN, NULL, 0);
 +}
 +
 +
 +/**
 + * eap_sm_request_otp - Request one time password from user (ctrl_iface)
 + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
 + * @msg: Message to be displayed to the user when asking for OTP
 + * @msg_len: Length of the user displayable message
 + *
 + * EAP methods can call this function to request open time password (OTP) for
 + * the current network. The request will be sent to monitor programs through
 + * the control interface.
 + */
 +void eap_sm_request_otp(struct eap_sm *sm, const char *msg, size_t msg_len)
 +{
 +      eap_sm_request(sm, WPA_CTRL_REQ_EAP_OTP, msg, msg_len);
 +}
 +
 +
 +/**
 + * eap_sm_request_passphrase - Request passphrase from user (ctrl_iface)
 + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
 + *
 + * EAP methods can call this function to request passphrase for a private key
 + * for the current network. This is normally called when the passphrase is not
 + * included in the network configuration. The request will be sent to monitor
 + * programs through the control interface.
 + */
 +void eap_sm_request_passphrase(struct eap_sm *sm)
 +{
 +      eap_sm_request(sm, WPA_CTRL_REQ_EAP_PASSPHRASE, NULL, 0);
 +}
 +
 +
 +/**
 + * 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()
 + *
 + * Notify EAP state machines that a monitor was attached to the control
 + * interface to trigger re-sending of pending requests for user input.
 + */
 +void eap_sm_notify_ctrl_attached(struct eap_sm *sm)
 +{
 +      struct eap_peer_config *config = eap_get_config(sm);
 +
 +      if (config == NULL)
 +              return;
 +
 +      /* Re-send any pending requests for user data since a new control
 +       * interface was added. This handles cases where the EAP authentication
 +       * starts immediately after system startup when the user interface is
 +       * not yet running. */
 +      if (config->pending_req_identity)
 +              eap_sm_request_identity(sm);
 +      if (config->pending_req_password)
 +              eap_sm_request_password(sm);
 +      if (config->pending_req_new_password)
 +              eap_sm_request_new_password(sm);
 +      if (config->pending_req_otp)
 +              eap_sm_request_otp(sm, NULL, 0);
 +      if (config->pending_req_pin)
 +              eap_sm_request_pin(sm);
 +      if (config->pending_req_passphrase)
 +              eap_sm_request_passphrase(sm);
 +}
 +
 +
 +static int eap_allowed_phase2_type(int vendor, int type)
 +{
 +      if (vendor != EAP_VENDOR_IETF)
 +              return 0;
 +      return type != EAP_TYPE_PEAP && type != EAP_TYPE_TTLS &&
 +              type != EAP_TYPE_FAST;
 +}
 +
 +
 +/**
 + * eap_get_phase2_type - Get EAP type for the given EAP phase 2 method name
 + * @name: EAP method name, e.g., MD5
 + * @vendor: Buffer for returning EAP Vendor-Id
 + * Returns: EAP method type or %EAP_TYPE_NONE if not found
 + *
 + * This function maps EAP type names into EAP type numbers that are allowed for
 + * Phase 2, i.e., for tunneled authentication. Phase 2 is used, e.g., with
 + * EAP-PEAP, EAP-TTLS, and EAP-FAST.
 + */
 +u32 eap_get_phase2_type(const char *name, int *vendor)
 +{
 +      int v;
++      u32 type = eap_peer_get_type(name, &v);
 +      if (eap_allowed_phase2_type(v, type)) {
 +              *vendor = v;
 +              return type;
 +      }
 +      *vendor = EAP_VENDOR_IETF;
 +      return EAP_TYPE_NONE;
 +}
 +
 +
 +/**
 + * eap_get_phase2_types - Get list of allowed EAP phase 2 types
 + * @config: Pointer to a network configuration
 + * @count: Pointer to a variable to be filled with number of returned EAP types
 + * Returns: Pointer to allocated type list or %NULL on failure
 + *
 + * This function generates an array of allowed EAP phase 2 (tunneled) types for
 + * the given network configuration.
 + */
 +struct eap_method_type * eap_get_phase2_types(struct eap_peer_config *config,
 +                                            size_t *count)
 +{
 +      struct eap_method_type *buf;
 +      u32 method;
 +      int vendor;
 +      size_t mcount;
 +      const struct eap_method *methods, *m;
 +
 +      methods = eap_peer_get_methods(&mcount);
 +      if (methods == NULL)
 +              return NULL;
 +      *count = 0;
 +      buf = os_malloc(mcount * sizeof(struct eap_method_type));
 +      if (buf == NULL)
 +              return NULL;
 +
 +      for (m = methods; m; m = m->next) {
 +              vendor = m->vendor;
 +              method = m->method;
 +              if (eap_allowed_phase2_type(vendor, method)) {
 +                      if (vendor == EAP_VENDOR_IETF &&
 +                          method == EAP_TYPE_TLS && config &&
 +                          config->private_key2 == NULL)
 +                              continue;
 +                      buf[*count].vendor = vendor;
 +                      buf[*count].method = method;
 +                      (*count)++;
 +              }
 +      }
 +
 +      return buf;
 +}
 +
 +
 +/**
 + * eap_set_fast_reauth - Update fast_reauth setting
 + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
 + * @enabled: 1 = Fast reauthentication is enabled, 0 = Disabled
 + */
 +void eap_set_fast_reauth(struct eap_sm *sm, int enabled)
 +{
 +      sm->fast_reauth = enabled;
 +}
 +
 +
 +/**
 + * eap_set_workaround - Update EAP workarounds setting
 + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
 + * @workaround: 1 = Enable EAP workarounds, 0 = Disable EAP workarounds
 + */
 +void eap_set_workaround(struct eap_sm *sm, unsigned int workaround)
 +{
 +      sm->workaround = workaround;
 +}
 +
 +
 +/**
 + * eap_get_config - Get current network configuration
 + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
 + * Returns: Pointer to the current network configuration or %NULL if not found
 + *
 + * EAP peer methods should avoid using this function if they can use other
 + * access functions, like eap_get_config_identity() and
 + * eap_get_config_password(), that do not require direct access to
 + * struct eap_peer_config.
 + */
 +struct eap_peer_config * eap_get_config(struct eap_sm *sm)
 +{
 +      return sm->eapol_cb->get_config(sm->eapol_ctx);
 +}
 +
 +
 +/**
 + * eap_get_config_identity - Get identity from the network configuration
 + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
 + * @len: Buffer for the length of the identity
 + * Returns: Pointer to the identity or %NULL if not found
 + */
 +const u8 * eap_get_config_identity(struct eap_sm *sm, size_t *len)
 +{
 +      struct eap_peer_config *config = eap_get_config(sm);
 +      if (config == NULL)
 +              return NULL;
 +      *len = config->identity_len;
 +      return config->identity;
 +}
 +
 +
 +static int eap_get_ext_password(struct eap_sm *sm,
 +                              struct eap_peer_config *config)
 +{
 +      char *name;
 +
 +      if (config->password == NULL)
 +              return -1;
 +
 +      name = os_zalloc(config->password_len + 1);
 +      if (name == NULL)
 +              return -1;
 +      os_memcpy(name, config->password, config->password_len);
 +
 +      ext_password_free(sm->ext_pw_buf);
 +      sm->ext_pw_buf = ext_password_get(sm->ext_pw, name);
 +      os_free(name);
 +
 +      return sm->ext_pw_buf == NULL ? -1 : 0;
 +}
 +
 +
 +/**
 + * eap_get_config_password - Get password from the network configuration
 + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
 + * @len: Buffer for the length of the password
 + * Returns: Pointer to the password or %NULL if not found
 + */
 +const u8 * eap_get_config_password(struct eap_sm *sm, size_t *len)
 +{
 +      struct eap_peer_config *config = eap_get_config(sm);
 +      if (config == NULL)
 +              return NULL;
 +
 +      if (config->flags & EAP_CONFIG_FLAGS_EXT_PASSWORD) {
 +              if (eap_get_ext_password(sm, config) < 0)
 +                      return NULL;
 +              *len = wpabuf_len(sm->ext_pw_buf);
 +              return wpabuf_head(sm->ext_pw_buf);
 +      }
 +
 +      *len = config->password_len;
 +      return config->password;
 +}
 +
 +
 +/**
 + * eap_get_config_password2 - Get password from the network configuration
 + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
 + * @len: Buffer for the length of the password
 + * @hash: Buffer for returning whether the password is stored as a
 + * NtPasswordHash instead of plaintext password; can be %NULL if this
 + * information is not needed
 + * Returns: Pointer to the password or %NULL if not found
 + */
 +const u8 * eap_get_config_password2(struct eap_sm *sm, size_t *len, int *hash)
 +{
 +      struct eap_peer_config *config = eap_get_config(sm);
 +      if (config == NULL)
 +              return NULL;
 +
 +      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);
 +      }
 +
 +      *len = config->password_len;
 +      if (hash)
 +              *hash = !!(config->flags & EAP_CONFIG_FLAGS_PASSWORD_NTHASH);
 +      return config->password;
 +}
 +
 +
 +/**
 + * eap_get_config_new_password - Get new password from network configuration
 + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
 + * @len: Buffer for the length of the new password
 + * Returns: Pointer to the new password or %NULL if not found
 + */
 +const u8 * eap_get_config_new_password(struct eap_sm *sm, size_t *len)
 +{
 +      struct eap_peer_config *config = eap_get_config(sm);
 +      if (config == NULL)
 +              return NULL;
 +      *len = config->new_password_len;
 +      return config->new_password;
 +}
 +
 +
 +/**
 + * eap_get_config_otp - Get one-time password from the network configuration
 + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
 + * @len: Buffer for the length of the one-time password
 + * Returns: Pointer to the one-time password or %NULL if not found
 + */
 +const u8 * eap_get_config_otp(struct eap_sm *sm, size_t *len)
 +{
 +      struct eap_peer_config *config = eap_get_config(sm);
 +      if (config == NULL)
 +              return NULL;
 +      *len = config->otp_len;
 +      return config->otp;
 +}
 +
 +
 +/**
 + * eap_clear_config_otp - Clear used one-time password
 + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
 + *
 + * This function clears a used one-time password (OTP) from the current network
 + * configuration. This should be called when the OTP has been used and is not
 + * needed anymore.
 + */
 +void eap_clear_config_otp(struct eap_sm *sm)
 +{
 +      struct eap_peer_config *config = eap_get_config(sm);
 +      if (config == NULL)
 +              return;
 +      os_memset(config->otp, 0, config->otp_len);
 +      os_free(config->otp);
 +      config->otp = NULL;
 +      config->otp_len = 0;
 +}
 +
 +
 +/**
 + * eap_get_config_phase1 - Get phase1 data from the network configuration
 + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
 + * Returns: Pointer to the phase1 data or %NULL if not found
 + */
 +const char * eap_get_config_phase1(struct eap_sm *sm)
 +{
 +      struct eap_peer_config *config = eap_get_config(sm);
 +      if (config == NULL)
 +              return NULL;
 +      return config->phase1;
 +}
 +
 +
 +/**
 + * eap_get_config_phase2 - Get phase2 data from the network configuration
 + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
 + * Returns: Pointer to the phase1 data or %NULL if not found
 + */
 +const char * eap_get_config_phase2(struct eap_sm *sm)
 +{
 +      struct eap_peer_config *config = eap_get_config(sm);
 +      if (config == NULL)
 +              return NULL;
 +      return config->phase2;
 +}
 +
 +
 +int eap_get_config_fragment_size(struct eap_sm *sm)
 +{
 +      struct eap_peer_config *config = eap_get_config(sm);
 +      if (config == NULL)
 +              return -1;
 +      return config->fragment_size;
 +}
 +
 +
 +/**
 + * eap_key_available - Get key availability (eapKeyAvailable variable)
 + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
 + * Returns: 1 if EAP keying material is available, 0 if not
 + */
 +int eap_key_available(struct eap_sm *sm)
 +{
 +      return sm ? sm->eapKeyAvailable : 0;
 +}
 +
 +
 +/**
 + * eap_notify_success - Notify EAP state machine about external success trigger
 + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
 + *
 + * This function is called when external event, e.g., successful completion of
 + * WPA-PSK key handshake, is indicating that EAP state machine should move to
 + * success state. This is mainly used with security modes that do not use EAP
 + * state machine (e.g., WPA-PSK).
 + */
 +void eap_notify_success(struct eap_sm *sm)
 +{
 +      if (sm) {
 +              sm->decision = DECISION_COND_SUCC;
 +              sm->EAP_state = EAP_SUCCESS;
 +      }
 +}
 +
 +
 +/**
 + * eap_notify_lower_layer_success - Notification of lower layer success
 + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
 + *
 + * Notify EAP state machines that a lower layer has detected a successful
 + * authentication. This is used to recover from dropped EAP-Success messages.
 + */
 +void eap_notify_lower_layer_success(struct eap_sm *sm)
 +{
 +      if (sm == NULL)
 +              return;
 +
 +      if (eapol_get_bool(sm, EAPOL_eapSuccess) ||
 +          sm->decision == DECISION_FAIL ||
 +          (sm->methodState != METHOD_MAY_CONT &&
 +           sm->methodState != METHOD_DONE))
 +              return;
 +
 +      if (sm->eapKeyData != NULL)
 +              sm->eapKeyAvailable = TRUE;
 +      eapol_set_bool(sm, EAPOL_eapSuccess, TRUE);
 +      wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_SUCCESS
 +              "EAP authentication completed successfully (based on lower "
 +              "layer success)");
 +}
 +
 +
 +/**
 + * eap_get_eapSessionId - Get Session-Id from EAP state machine
 + * @sm: Pointer to EAP state machine allocated with eap_peer_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
 + *
 + * Fetch EAP Session-Id from the EAP state machine. The Session-Id is available
 + * only after a successful authentication. EAP state machine continues to manage
 + * the Session-Id and the caller must not change or free the returned data.
 + */
 +const u8 * eap_get_eapSessionId(struct eap_sm *sm, size_t *len)
 +{
 +      if (sm == NULL || sm->eapSessionId == NULL) {
 +              *len = 0;
 +              return NULL;
 +      }
 +
 +      *len = sm->eapSessionIdLen;
 +      return sm->eapSessionId;
 +}
 +
 +
 +/**
 + * eap_get_eapKeyData - Get master session key (MSK) from EAP state machine
 + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
 + * @len: Pointer to variable that will be set to number of bytes in the key
 + * Returns: Pointer to the EAP keying data or %NULL on failure
 + *
 + * Fetch EAP keying material (MSK, eapKeyData) from the EAP state machine. The
 + * key is available only after a successful authentication. EAP state machine
 + * continues to manage the key data and the caller must not change or free the
 + * returned data.
 + */
 +const u8 * eap_get_eapKeyData(struct eap_sm *sm, size_t *len)
 +{
 +      if (sm == NULL || sm->eapKeyData == NULL) {
 +              *len = 0;
 +              return NULL;
 +      }
 +
 +      *len = sm->eapKeyDataLen;
 +      return sm->eapKeyData;
 +}
 +
 +
 +/**
 + * eap_get_eapKeyData - Get EAP response data
 + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
 + * Returns: Pointer to the EAP response (eapRespData) or %NULL on failure
 + *
 + * Fetch EAP response (eapRespData) from the EAP state machine. This data is
 + * available when EAP state machine has processed an incoming EAP request. The
 + * EAP state machine does not maintain a reference to the response after this
 + * function is called and the caller is responsible for freeing the data.
 + */
 +struct wpabuf * eap_get_eapRespData(struct eap_sm *sm)
 +{
 +      struct wpabuf *resp;
 +
 +      if (sm == NULL || sm->eapRespData == NULL)
 +              return NULL;
 +
 +      resp = sm->eapRespData;
 +      sm->eapRespData = NULL;
 +
 +      return resp;
 +}
 +
 +
 +/**
 + * eap_sm_register_scard_ctx - Notification of smart card context
 + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
 + * @ctx: Context data for smart card operations
 + *
 + * Notify EAP state machines of context data for smart card operations. This
 + * context data will be used as a parameter for scard_*() functions.
 + */
 +void eap_register_scard_ctx(struct eap_sm *sm, void *ctx)
 +{
 +      if (sm)
 +              sm->scard_ctx = ctx;
 +}
 +
 +
 +/**
 + * eap_set_config_blob - Set or add a named configuration blob
 + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
 + * @blob: New value for the blob
 + *
 + * Adds a new configuration blob or replaces the current value of an existing
 + * blob.
 + */
 +void eap_set_config_blob(struct eap_sm *sm, struct wpa_config_blob *blob)
 +{
 +#ifndef CONFIG_NO_CONFIG_BLOBS
 +      sm->eapol_cb->set_config_blob(sm->eapol_ctx, blob);
 +#endif /* CONFIG_NO_CONFIG_BLOBS */
 +}
 +
 +
 +/**
 + * eap_get_config_blob - Get a named configuration blob
 + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
 + * @name: Name of the blob
 + * Returns: Pointer to blob data or %NULL if not found
 + */
 +const struct wpa_config_blob * eap_get_config_blob(struct eap_sm *sm,
 +                                                 const char *name)
 +{
 +#ifndef CONFIG_NO_CONFIG_BLOBS
 +      return sm->eapol_cb->get_config_blob(sm->eapol_ctx, name);
 +#else /* CONFIG_NO_CONFIG_BLOBS */
 +      return NULL;
 +#endif /* CONFIG_NO_CONFIG_BLOBS */
 +}
 +
 +
 +/**
 + * eap_set_force_disabled - Set force_disabled flag
 + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
 + * @disabled: 1 = EAP disabled, 0 = EAP enabled
 + *
 + * This function is used to force EAP state machine to be disabled when it is
 + * not in use (e.g., with WPA-PSK or plaintext connections).
 + */
 +void eap_set_force_disabled(struct eap_sm *sm, int disabled)
 +{
 +      sm->force_disabled = 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()
 + *
 + * An EAP method can perform a pending operation (e.g., to get a response from
 + * an external process). Once the response is available, this function can be
 + * used to request EAPOL state machine to retry delivering the previously
 + * received (and still unanswered) EAP request to EAP state machine.
 + */
 +void eap_notify_pending(struct eap_sm *sm)
 +{
 +      sm->eapol_cb->notify_pending(sm->eapol_ctx);
 +}
 +
 +
 +/**
 + * eap_invalidate_cached_session - Mark cached session data invalid
 + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
 + */
 +void eap_invalidate_cached_session(struct eap_sm *sm)
 +{
 +      if (sm)
 +              eap_deinit_prev_method(sm, "invalidate");
 +}
 +
 +
 +int eap_is_wps_pbc_enrollee(struct eap_peer_config *conf)
 +{
 +      if (conf->identity_len != WSC_ID_ENROLLEE_LEN ||
 +          os_memcmp(conf->identity, WSC_ID_ENROLLEE, WSC_ID_ENROLLEE_LEN))
 +              return 0; /* Not a WPS Enrollee */
 +
 +      if (conf->phase1 == NULL || os_strstr(conf->phase1, "pbc=1") == NULL)
 +              return 0; /* Not using PBC */
 +
 +      return 1;
 +}
 +
 +
 +int eap_is_wps_pin_enrollee(struct eap_peer_config *conf)
 +{
 +      if (conf->identity_len != WSC_ID_ENROLLEE_LEN ||
 +          os_memcmp(conf->identity, WSC_ID_ENROLLEE, WSC_ID_ENROLLEE_LEN))
 +              return 0; /* Not a WPS Enrollee */
 +
 +      if (conf->phase1 == NULL || os_strstr(conf->phase1, "pin=") == NULL)
 +              return 0; /* Not using PIN */
 +
 +      return 1;
 +}
 +
 +
 +void eap_sm_set_ext_pw_ctx(struct eap_sm *sm, struct ext_password_data *ext)
 +{
 +      ext_password_free(sm->ext_pw_buf);
 +      sm->ext_pw_buf = NULL;
 +      sm->ext_pw = ext;
 +}
 +
 +
 +/**
 + * eap_set_anon_id - Set or add anonymous identity
 + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
 + * @id: Anonymous identity (e.g., EAP-SIM pseudonym) or %NULL to clear
 + * @len: Length of anonymous identity in octets
 + */
 +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 702463b9d5146ea1e99548f95bdc0eb1258d6abe,0000000000000000000000000000000000000000..1a645af8b20034196b765acc389ed4621aa3a2a0
mode 100644,000000..100644
--- /dev/null
@@@ -1,354 -1,0 +1,354 @@@
-                                struct eapol_callbacks *eapol_cb,
 +/*
 + * EAP peer state machine functions (RFC 4137)
 + * Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi>
 + *
 + * This software may be distributed under the terms of the BSD license.
 + * See README for more details.
 + */
 +
 +#ifndef EAP_H
 +#define EAP_H
 +
 +#include "common/defs.h"
 +#include "eap_common/eap_defs.h"
 +#include "eap_peer/eap_methods.h"
 +
 +struct eap_sm;
 +struct wpa_config_blob;
 +struct wpabuf;
 +
 +struct eap_method_type {
 +      int vendor;
 +      u32 method;
 +};
 +
 +#ifdef IEEE8021X_EAPOL
 +
 +/**
 + * enum eapol_bool_var - EAPOL boolean state variables for EAP state machine
 + *
 + * These variables are used in the interface between EAP peer state machine and
 + * lower layer. These are defined in RFC 4137, Sect. 4.1. Lower layer code is
 + * expected to maintain these variables and register a callback functions for
 + * EAP state machine to get and set the variables.
 + */
 +enum eapol_bool_var {
 +      /**
 +       * EAPOL_eapSuccess - EAP SUCCESS state reached
 +       *
 +       * EAP state machine reads and writes this value.
 +       */
 +      EAPOL_eapSuccess,
 +
 +      /**
 +       * EAPOL_eapRestart - Lower layer request to restart authentication
 +       *
 +       * Set to TRUE in lower layer, FALSE in EAP state machine.
 +       */
 +      EAPOL_eapRestart,
 +
 +      /**
 +       * EAPOL_eapFail - EAP FAILURE state reached
 +       *
 +       * EAP state machine writes this value.
 +       */
 +      EAPOL_eapFail,
 +
 +      /**
 +       * EAPOL_eapResp - Response to send
 +       *
 +       * Set to TRUE in EAP state machine, FALSE in lower layer.
 +       */
 +      EAPOL_eapResp,
 +
 +      /**
 +       * EAPOL_eapNoResp - Request has been process; no response to send
 +       *
 +       * Set to TRUE in EAP state machine, FALSE in lower layer.
 +       */
 +      EAPOL_eapNoResp,
 +
 +      /**
 +       * EAPOL_eapReq - EAP request available from lower layer
 +       *
 +       * Set to TRUE in lower layer, FALSE in EAP state machine.
 +       */
 +      EAPOL_eapReq,
 +
 +      /**
 +       * EAPOL_portEnabled - Lower layer is ready for communication
 +       *
 +       * EAP state machines reads this value.
 +       */
 +      EAPOL_portEnabled,
 +
 +      /**
 +       * EAPOL_altAccept - Alternate indication of success (RFC3748)
 +       *
 +       * EAP state machines reads this value.
 +       */
 +      EAPOL_altAccept,
 +
 +      /**
 +       * EAPOL_altReject - Alternate indication of failure (RFC3748)
 +       *
 +       * EAP state machines reads this value.
 +       */
 +      EAPOL_altReject,
 +
 +      /**
 +       * EAPOL_eapTriggerStart - EAP-based trigger to send EAPOL-Start
 +       *
 +       * EAP state machine writes this value.
 +       */
 +      EAPOL_eapTriggerStart
 +};
 +
 +/**
 + * enum eapol_int_var - EAPOL integer state variables for EAP state machine
 + *
 + * These variables are used in the interface between EAP peer state machine and
 + * lower layer. These are defined in RFC 4137, Sect. 4.1. Lower layer code is
 + * expected to maintain these variables and register a callback functions for
 + * EAP state machine to get and set the variables.
 + */
 +enum eapol_int_var {
 +      /**
 +       * EAPOL_idleWhile - Outside time for EAP peer timeout
 +       *
 +       * This integer variable is used to provide an outside timer that the
 +       * external (to EAP state machine) code must decrement by one every
 +       * second until the value reaches zero. This is used in the same way as
 +       * EAPOL state machine timers. EAP state machine reads and writes this
 +       * value.
 +       */
 +      EAPOL_idleWhile
 +};
 +
 +/**
 + * struct eapol_callbacks - Callback functions from EAP to lower layer
 + *
 + * This structure defines the callback functions that EAP state machine
 + * requires from the lower layer (usually EAPOL state machine) for updating
 + * state variables and requesting information. eapol_ctx from
 + * eap_peer_sm_init() call will be used as the ctx parameter for these
 + * callback functions.
 + */
 +struct eapol_callbacks {
 +      /**
 +       * get_config - Get pointer to the current network configuration
 +       * @ctx: eapol_ctx from eap_peer_sm_init() call
 +       */
 +      struct eap_peer_config * (*get_config)(void *ctx);
 +
 +      /**
 +       * get_bool - Get a boolean EAPOL state variable
 +       * @variable: EAPOL boolean variable to get
 +       * Returns: Value of the EAPOL variable
 +       */
 +      Boolean (*get_bool)(void *ctx, enum eapol_bool_var variable);
 +
 +      /**
 +       * set_bool - Set a boolean EAPOL state variable
 +       * @ctx: eapol_ctx from eap_peer_sm_init() call
 +       * @variable: EAPOL boolean variable to set
 +       * @value: Value for the EAPOL variable
 +       */
 +      void (*set_bool)(void *ctx, enum eapol_bool_var variable,
 +                       Boolean value);
 +
 +      /**
 +       * get_int - Get an integer EAPOL state variable
 +       * @ctx: eapol_ctx from eap_peer_sm_init() call
 +       * @variable: EAPOL integer variable to get
 +       * Returns: Value of the EAPOL variable
 +       */
 +      unsigned int (*get_int)(void *ctx, enum eapol_int_var variable);
 +
 +      /**
 +       * set_int - Set an integer EAPOL state variable
 +       * @ctx: eapol_ctx from eap_peer_sm_init() call
 +       * @variable: EAPOL integer variable to set
 +       * @value: Value for the EAPOL variable
 +       */
 +      void (*set_int)(void *ctx, enum eapol_int_var variable,
 +                      unsigned int value);
 +
 +      /**
 +       * get_eapReqData - Get EAP-Request data
 +       * @ctx: eapol_ctx from eap_peer_sm_init() call
 +       * @len: Pointer to variable that will be set to eapReqDataLen
 +       * Returns: Reference to eapReqData (EAP state machine will not free
 +       * this) or %NULL if eapReqData not available.
 +       */
 +      struct wpabuf * (*get_eapReqData)(void *ctx);
 +
 +      /**
 +       * set_config_blob - Set named configuration blob
 +       * @ctx: eapol_ctx from eap_peer_sm_init() call
 +       * @blob: New value for the blob
 +       *
 +       * Adds a new configuration blob or replaces the current value of an
 +       * existing blob.
 +       */
 +      void (*set_config_blob)(void *ctx, struct wpa_config_blob *blob);
 +
 +      /**
 +       * get_config_blob - Get a named configuration blob
 +       * @ctx: eapol_ctx from eap_peer_sm_init() call
 +       * @name: Name of the blob
 +       * Returns: Pointer to blob data or %NULL if not found
 +       */
 +      const struct wpa_config_blob * (*get_config_blob)(void *ctx,
 +                                                        const char *name);
 +
 +      /**
 +       * notify_pending - Notify that a pending request can be retried
 +       * @ctx: eapol_ctx from eap_peer_sm_init() call
 +       *
 +       * An EAP method can perform a pending operation (e.g., to get a
 +       * response from an external process). Once the response is available,
 +       * this callback function can be used to request EAPOL state machine to
 +       * retry delivering the previously received (and still unanswered) EAP
 +       * request to EAP state machine.
 +       */
 +      void (*notify_pending)(void *ctx);
 +
 +      /**
 +       * eap_param_needed - Notify that EAP parameter is needed
 +       * @ctx: eapol_ctx from eap_peer_sm_init() call
 +       * @field: Field indicator (e.g., WPA_CTRL_REQ_EAP_IDENTITY)
 +       * @txt: User readable text describing the required parameter
 +       */
 +      void (*eap_param_needed)(void *ctx, enum wpa_ctrl_req_type field,
 +                               const char *txt);
 +
 +      /**
 +       * notify_cert - Notification of a peer certificate
 +       * @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);
 +
 +      /**
 +       * notify_status - Notification of the current EAP state
 +       * @ctx: eapol_ctx from eap_peer_sm_init() call
 +       * @status: Step in the process of EAP authentication
 +       * @parameter: Step-specific parameter, e.g., EAP method name
 +       */
 +      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
 +       * @id: Anonymous identity (e.g., EAP-SIM pseudonym) or %NULL to clear
 +       * @len: Length of anonymous identity in octets
 +       */
 +      void (*set_anon_id)(void *ctx, const u8 *id, size_t len);
 +};
 +
 +/**
 + * struct eap_config - Configuration for EAP state machine
 + */
 +struct eap_config {
 +      /**
 +       * opensc_engine_path - OpenSC engine for OpenSSL engine support
 +       *
 +       * Usually, path to engine_opensc.so.
 +       */
 +      const char *opensc_engine_path;
 +      /**
 +       * pkcs11_engine_path - PKCS#11 engine for OpenSSL engine support
 +       *
 +       * Usually, path to engine_pkcs11.so.
 +       */
 +      const char *pkcs11_engine_path;
 +      /**
 +       * pkcs11_module_path - OpenSC PKCS#11 module for OpenSSL engine
 +       *
 +       * Usually, path to opensc-pkcs11.so.
 +       */
 +      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.
 +       */
 +      struct wps_context *wps;
 +
 +      /**
 +       * cert_in_cb - Include server certificates in callback
 +       */
 +      int cert_in_cb;
 +};
 +
 +struct eap_sm * eap_peer_sm_init(void *eapol_ctx,
++                               const struct eapol_callbacks *eapol_cb,
 +                               void *msg_ctx, struct eap_config *conf);
 +void eap_peer_sm_deinit(struct eap_sm *sm);
 +int eap_peer_sm_step(struct eap_sm *sm);
 +void eap_sm_abort(struct eap_sm *sm);
 +int eap_sm_get_status(struct eap_sm *sm, char *buf, size_t buflen,
 +                    int verbose);
 +const char * eap_sm_get_method_name(struct eap_sm *sm);
 +struct wpabuf * eap_sm_buildIdentity(struct eap_sm *sm, int id, int encrypted);
 +void eap_sm_request_identity(struct eap_sm *sm);
 +void eap_sm_request_password(struct eap_sm *sm);
 +void eap_sm_request_new_password(struct eap_sm *sm);
 +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,
 +                                            size_t *count);
 +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);
 +const u8 * eap_get_eapSessionId(struct eap_sm *sm, size_t *len);
 +const u8 * eap_get_eapKeyData(struct eap_sm *sm, size_t *len);
 +struct wpabuf * eap_get_eapRespData(struct eap_sm *sm);
 +void eap_register_scard_ctx(struct eap_sm *sm, void *ctx);
 +void eap_invalidate_cached_session(struct eap_sm *sm);
 +
 +int eap_is_wps_pbc_enrollee(struct eap_peer_config *conf);
 +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 */
 +
 +#endif /* EAP_H */
index 0662ae738367447325ba9e1b116a452ba1340ab4,0000000000000000000000000000000000000000..dc9e8cc34d4afc9e37fece6fc666e0d992e43e39
mode 100644,000000..100644
--- /dev/null
@@@ -1,1551 -1,0 +1,1551 @@@
-       if (pos == NULL || len < 1) {
 +/*
 + * EAP peer method: EAP-AKA (RFC 4187) and EAP-AKA' (RFC 5448)
 + * Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi>
 + *
 + * This software may be distributed under the terms of the BSD license.
 + * See README for more details.
 + */
 +
 +#include "includes.h"
 +
 +#include "common.h"
 +#include "pcsc_funcs.h"
 +#include "crypto/crypto.h"
 +#include "crypto/sha1.h"
 +#include "crypto/sha256.h"
 +#include "crypto/milenage.h"
 +#include "eap_common/eap_sim_common.h"
 +#include "eap_config.h"
 +#include "eap_i.h"
 +
 +
 +struct eap_aka_data {
 +      u8 ik[EAP_AKA_IK_LEN], ck[EAP_AKA_CK_LEN], res[EAP_AKA_RES_MAX_LEN];
 +      size_t res_len;
 +      u8 nonce_s[EAP_SIM_NONCE_S_LEN];
 +      u8 mk[EAP_SIM_MK_LEN];
 +      u8 k_aut[EAP_AKA_PRIME_K_AUT_LEN];
 +      u8 k_encr[EAP_SIM_K_ENCR_LEN];
 +      u8 k_re[EAP_AKA_PRIME_K_RE_LEN]; /* EAP-AKA' only */
 +      u8 msk[EAP_SIM_KEYING_DATA_LEN];
 +      u8 emsk[EAP_EMSK_LEN];
 +      u8 rand[EAP_AKA_RAND_LEN], autn[EAP_AKA_AUTN_LEN];
 +      u8 auts[EAP_AKA_AUTS_LEN];
 +
 +      int num_id_req, num_notification;
 +      u8 *pseudonym;
 +      size_t pseudonym_len;
 +      u8 *reauth_id;
 +      size_t reauth_id_len;
 +      int reauth;
 +      unsigned int counter, counter_too_small;
 +      u8 *last_eap_identity;
 +      size_t last_eap_identity_len;
 +      enum {
 +              CONTINUE, RESULT_SUCCESS, SUCCESS, FAILURE
 +      } state;
 +
 +      struct wpabuf *id_msgs;
 +      int prev_id;
 +      int result_ind, use_result_ind;
 +      u8 eap_method;
 +      u8 *network_name;
 +      size_t network_name_len;
 +      u16 kdf;
 +      int kdf_negotiation;
 +};
 +
 +
 +#ifndef CONFIG_NO_STDOUT_DEBUG
 +static const char * eap_aka_state_txt(int state)
 +{
 +      switch (state) {
 +      case CONTINUE:
 +              return "CONTINUE";
 +      case RESULT_SUCCESS:
 +              return "RESULT_SUCCESS";
 +      case SUCCESS:
 +              return "SUCCESS";
 +      case FAILURE:
 +              return "FAILURE";
 +      default:
 +              return "?";
 +      }
 +}
 +#endif /* CONFIG_NO_STDOUT_DEBUG */
 +
 +
 +static void eap_aka_state(struct eap_aka_data *data, int state)
 +{
 +      wpa_printf(MSG_DEBUG, "EAP-AKA: %s -> %s",
 +                 eap_aka_state_txt(data->state),
 +                 eap_aka_state_txt(state));
 +      data->state = state;
 +}
 +
 +
 +static void * eap_aka_init(struct eap_sm *sm)
 +{
 +      struct eap_aka_data *data;
 +      const char *phase1 = eap_get_config_phase1(sm);
 +      struct eap_peer_config *config = eap_get_config(sm);
 +
 +      data = os_zalloc(sizeof(*data));
 +      if (data == NULL)
 +              return NULL;
 +
 +      data->eap_method = EAP_TYPE_AKA;
 +
 +      eap_aka_state(data, CONTINUE);
 +      data->prev_id = -1;
 +
 +      data->result_ind = phase1 && os_strstr(phase1, "result_ind=1") != NULL;
 +
 +      if (config && config->anonymous_identity) {
 +              data->pseudonym = os_malloc(config->anonymous_identity_len);
 +              if (data->pseudonym) {
 +                      os_memcpy(data->pseudonym, config->anonymous_identity,
 +                                config->anonymous_identity_len);
 +                      data->pseudonym_len = config->anonymous_identity_len;
 +              }
 +      }
 +
 +      return data;
 +}
 +
 +
 +#ifdef EAP_AKA_PRIME
 +static void * eap_aka_prime_init(struct eap_sm *sm)
 +{
 +      struct eap_aka_data *data = eap_aka_init(sm);
 +      if (data == NULL)
 +              return NULL;
 +      data->eap_method = EAP_TYPE_AKA_PRIME;
 +      return data;
 +}
 +#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;
 +      if (data) {
 +              os_free(data->pseudonym);
 +              os_free(data->reauth_id);
 +              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;
 +
 +      wpa_printf(MSG_DEBUG, "EAP-AKA: UMTS authentication algorithm");
 +
 +      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,
 +                                     data->ik, data->ck, data->auts);
 +      }
 +
 +#ifdef CONFIG_USIM_SIMULATOR
 +      if (conf->password) {
 +              u8 opc[16], k[16], sqn[6];
 +              const char *pos;
 +              wpa_printf(MSG_DEBUG, "EAP-AKA: Use internal Milenage "
 +                         "implementation for UMTS authentication");
 +              if (conf->password_len < 78) {
 +                      wpa_printf(MSG_DEBUG, "EAP-AKA: invalid Milenage "
 +                                 "password");
 +                      return -1;
 +              }
 +              pos = (const char *) conf->password;
 +              if (hexstr2bin(pos, k, 16))
 +                      return -1;
 +              pos += 32;
 +              if (*pos != ':')
 +                      return -1;
 +              pos++;
 +
 +              if (hexstr2bin(pos, opc, 16))
 +                      return -1;
 +              pos += 32;
 +              if (*pos != ':')
 +                      return -1;
 +              pos++;
 +
 +              if (hexstr2bin(pos, sqn, 6))
 +                      return -1;
 +
 +              return milenage_check(opc, k, sqn, data->rand, data->autn,
 +                                    data->ik, data->ck,
 +                                    data->res, &data->res_len, data->auts);
 +      }
 +#endif /* CONFIG_USIM_SIMULATOR */
 +
 +#ifdef CONFIG_USIM_HARDCODED
 +      wpa_printf(MSG_DEBUG, "EAP-AKA: Use hardcoded Kc and SRES values for "
 +                 "testing");
 +
 +      /* These hardcoded Kc and SRES values are used for testing.
 +       * Could consider making them configurable. */
 +      os_memset(data->res, '2', EAP_AKA_RES_MAX_LEN);
 +      data->res_len = EAP_AKA_RES_MAX_LEN;
 +      os_memset(data->ik, '3', EAP_AKA_IK_LEN);
 +      os_memset(data->ck, '4', EAP_AKA_CK_LEN);
 +      {
 +              u8 autn[EAP_AKA_AUTN_LEN];
 +              os_memset(autn, '1', EAP_AKA_AUTN_LEN);
 +              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;
 +              }
 +      }
 +#if 0
 +      {
 +              static int test_resync = 1;
 +              if (test_resync) {
 +                      /* Test Resynchronization */
 +                      test_resync = 0;
 +                      return -2;
 +              }
 +      }
 +#endif
 +      return 0;
 +
 +#else /* CONFIG_USIM_HARDCODED */
 +
 +      wpa_printf(MSG_DEBUG, "EAP-AKA: No UMTS authentication algorithm "
 +                 "enabled");
 +      return -1;
 +
 +#endif /* CONFIG_USIM_HARDCODED */
 +}
 +
 +
 +#define CLEAR_PSEUDONYM       0x01
 +#define CLEAR_REAUTH_ID       0x02
 +#define CLEAR_EAP_ID  0x04
 +
 +static void eap_aka_clear_identities(struct eap_sm *sm,
 +                                   struct eap_aka_data *data, int id)
 +{
 +      if ((id & CLEAR_PSEUDONYM) && data->pseudonym) {
 +              wpa_printf(MSG_DEBUG, "EAP-AKA: forgetting old pseudonym");
 +              os_free(data->pseudonym);
 +              data->pseudonym = NULL;
 +              data->pseudonym_len = 0;
 +              eap_set_anon_id(sm, NULL, 0);
 +      }
 +      if ((id & CLEAR_REAUTH_ID) && data->reauth_id) {
 +              wpa_printf(MSG_DEBUG, "EAP-AKA: forgetting old reauth_id");
 +              os_free(data->reauth_id);
 +              data->reauth_id = NULL;
 +              data->reauth_id_len = 0;
 +      }
 +      if ((id & CLEAR_EAP_ID) && data->last_eap_identity) {
 +              wpa_printf(MSG_DEBUG, "EAP-AKA: forgetting old eap_id");
 +              os_free(data->last_eap_identity);
 +              data->last_eap_identity = NULL;
 +              data->last_eap_identity_len = 0;
 +      }
 +}
 +
 +
 +static int eap_aka_learn_ids(struct eap_sm *sm, struct eap_aka_data *data,
 +                           struct eap_sim_attrs *attr)
 +{
 +      if (attr->next_pseudonym) {
 +              const u8 *identity = NULL;
 +              size_t identity_len = 0;
 +              const u8 *realm = NULL;
 +              size_t realm_len = 0;
 +
 +              wpa_hexdump_ascii(MSG_DEBUG,
 +                                "EAP-AKA: (encr) AT_NEXT_PSEUDONYM",
 +                                attr->next_pseudonym,
 +                                attr->next_pseudonym_len);
 +              os_free(data->pseudonym);
 +              /* Look for the realm of the permanent identity */
 +              identity = eap_get_config_identity(sm, &identity_len);
 +              if (identity) {
 +                      for (realm = identity, realm_len = identity_len;
 +                           realm_len > 0; realm_len--, realm++) {
 +                              if (*realm == '@')
 +                                      break;
 +                      }
 +              }
 +              data->pseudonym = os_malloc(attr->next_pseudonym_len +
 +                                          realm_len);
 +              if (data->pseudonym == NULL) {
 +                      wpa_printf(MSG_INFO, "EAP-AKA: (encr) No memory for "
 +                                 "next pseudonym");
 +                      data->pseudonym_len = 0;
 +                      return -1;
 +              }
 +              os_memcpy(data->pseudonym, attr->next_pseudonym,
 +                        attr->next_pseudonym_len);
 +              if (realm_len) {
 +                      os_memcpy(data->pseudonym + attr->next_pseudonym_len,
 +                                realm, realm_len);
 +              }
 +              data->pseudonym_len = attr->next_pseudonym_len + realm_len;
 +              eap_set_anon_id(sm, data->pseudonym, data->pseudonym_len);
 +      }
 +
 +      if (attr->next_reauth_id) {
 +              os_free(data->reauth_id);
 +              data->reauth_id = os_malloc(attr->next_reauth_id_len);
 +              if (data->reauth_id == NULL) {
 +                      wpa_printf(MSG_INFO, "EAP-AKA: (encr) No memory for "
 +                                 "next reauth_id");
 +                      data->reauth_id_len = 0;
 +                      return -1;
 +              }
 +              os_memcpy(data->reauth_id, attr->next_reauth_id,
 +                        attr->next_reauth_id_len);
 +              data->reauth_id_len = attr->next_reauth_id_len;
 +              wpa_hexdump_ascii(MSG_DEBUG,
 +                                "EAP-AKA: (encr) AT_NEXT_REAUTH_ID",
 +                                data->reauth_id,
 +                                data->reauth_id_len);
 +      }
 +
 +      return 0;
 +}
 +
 +
 +static int eap_aka_add_id_msg(struct eap_aka_data *data,
 +                            const struct wpabuf *msg)
 +{
 +      if (msg == NULL)
 +              return -1;
 +
 +      if (data->id_msgs == NULL) {
 +              data->id_msgs = wpabuf_dup(msg);
 +              return data->id_msgs == NULL ? -1 : 0;
 +      }
 +
 +      if (wpabuf_resize(&data->id_msgs, wpabuf_len(msg)) < 0)
 +              return -1;
 +      wpabuf_put_buf(data->id_msgs, msg);
 +
 +      return 0;
 +}
 +
 +
 +static void eap_aka_add_checkcode(struct eap_aka_data *data,
 +                                struct eap_sim_msg *msg)
 +{
 +      const u8 *addr;
 +      size_t len;
 +      u8 hash[SHA256_MAC_LEN];
 +
 +      wpa_printf(MSG_DEBUG, "   AT_CHECKCODE");
 +
 +      if (data->id_msgs == NULL) {
 +              /*
 +               * No EAP-AKA/Identity packets were exchanged - send empty
 +               * checkcode.
 +               */
 +              eap_sim_msg_add(msg, EAP_SIM_AT_CHECKCODE, 0, NULL, 0);
 +              return;
 +      }
 +
 +      /* Checkcode is SHA1/SHA256 hash over all EAP-AKA/Identity packets. */
 +      addr = wpabuf_head(data->id_msgs);
 +      len = wpabuf_len(data->id_msgs);
 +      wpa_hexdump(MSG_MSGDUMP, "EAP-AKA: AT_CHECKCODE data", addr, len);
 +#ifdef EAP_AKA_PRIME
 +      if (data->eap_method == EAP_TYPE_AKA_PRIME)
 +              sha256_vector(1, &addr, &len, hash);
 +      else
 +#endif /* EAP_AKA_PRIME */
 +              sha1_vector(1, &addr, &len, hash);
 +
 +      eap_sim_msg_add(msg, EAP_SIM_AT_CHECKCODE, 0, hash,
 +                      data->eap_method == EAP_TYPE_AKA_PRIME ?
 +                      EAP_AKA_PRIME_CHECKCODE_LEN : EAP_AKA_CHECKCODE_LEN);
 +}
 +
 +
 +static int eap_aka_verify_checkcode(struct eap_aka_data *data,
 +                                  const u8 *checkcode, size_t checkcode_len)
 +{
 +      const u8 *addr;
 +      size_t len;
 +      u8 hash[SHA256_MAC_LEN];
 +      size_t hash_len;
 +
 +      if (checkcode == NULL)
 +              return -1;
 +
 +      if (data->id_msgs == NULL) {
 +              if (checkcode_len != 0) {
 +                      wpa_printf(MSG_DEBUG, "EAP-AKA: Checkcode from server "
 +                                 "indicates that AKA/Identity messages were "
 +                                 "used, but they were not");
 +                      return -1;
 +              }
 +              return 0;
 +      }
 +
 +      hash_len = data->eap_method == EAP_TYPE_AKA_PRIME ?
 +              EAP_AKA_PRIME_CHECKCODE_LEN : EAP_AKA_CHECKCODE_LEN;
 +
 +      if (checkcode_len != hash_len) {
 +              wpa_printf(MSG_DEBUG, "EAP-AKA: Checkcode from server "
 +                         "indicates that AKA/Identity message were not "
 +                         "used, but they were");
 +              return -1;
 +      }
 +
 +      /* Checkcode is SHA1/SHA256 hash over all EAP-AKA/Identity packets. */
 +      addr = wpabuf_head(data->id_msgs);
 +      len = wpabuf_len(data->id_msgs);
 +#ifdef EAP_AKA_PRIME
 +      if (data->eap_method == EAP_TYPE_AKA_PRIME)
 +              sha256_vector(1, &addr, &len, hash);
 +      else
 +#endif /* EAP_AKA_PRIME */
 +              sha1_vector(1, &addr, &len, hash);
 +
 +      if (os_memcmp_const(hash, checkcode, hash_len) != 0) {
 +              wpa_printf(MSG_DEBUG, "EAP-AKA: Mismatch in AT_CHECKCODE");
 +              return -1;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +static struct wpabuf * eap_aka_client_error(struct eap_aka_data *data, u8 id,
 +                                          int err)
 +{
 +      struct eap_sim_msg *msg;
 +
 +      eap_aka_state(data, FAILURE);
 +      data->num_id_req = 0;
 +      data->num_notification = 0;
 +
 +      wpa_printf(MSG_DEBUG, "EAP-AKA: Send Client-Error (error code %d)",
 +                 err);
 +      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, data->eap_method, NULL, NULL, 0);
 +}
 +
 +
 +static struct wpabuf * eap_aka_authentication_reject(struct eap_aka_data *data,
 +                                                   u8 id)
 +{
 +      struct eap_sim_msg *msg;
 +
 +      eap_aka_state(data, FAILURE);
 +      data->num_id_req = 0;
 +      data->num_notification = 0;
 +
 +      wpa_printf(MSG_DEBUG, "Generating EAP-AKA Authentication-Reject "
 +                 "(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, data->eap_method, NULL, NULL, 0);
 +}
 +
 +
 +static struct wpabuf * eap_aka_synchronization_failure(
 +      struct eap_aka_data *data, u8 id)
 +{
 +      struct eap_sim_msg *msg;
 +
 +      data->num_id_req = 0;
 +      data->num_notification = 0;
 +
 +      wpa_printf(MSG_DEBUG, "Generating EAP-AKA Synchronization-Failure "
 +                 "(id=%d)", id);
 +      msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method,
 +                             EAP_AKA_SUBTYPE_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, data->eap_method, NULL, NULL, 0);
 +}
 +
 +
 +static struct wpabuf * eap_aka_response_identity(struct eap_sm *sm,
 +                                               struct eap_aka_data *data,
 +                                               u8 id,
 +                                               enum eap_sim_id_req id_req)
 +{
 +      const u8 *identity = NULL;
 +      size_t identity_len = 0;
 +      struct eap_sim_msg *msg;
 +
 +      data->reauth = 0;
 +      if (id_req == ANY_ID && data->reauth_id) {
 +              identity = data->reauth_id;
 +              identity_len = data->reauth_id_len;
 +              data->reauth = 1;
 +      } else if ((id_req == ANY_ID || id_req == FULLAUTH_ID) &&
 +                 data->pseudonym) {
 +              identity = data->pseudonym;
 +              identity_len = data->pseudonym_len;
 +              eap_aka_clear_identities(sm, data, CLEAR_REAUTH_ID);
 +      } else if (id_req != NO_ID_REQ) {
 +              identity = eap_get_config_identity(sm, &identity_len);
 +              if (identity) {
 +                      eap_aka_clear_identities(sm, data, CLEAR_PSEUDONYM |
 +                                               CLEAR_REAUTH_ID);
 +              }
 +      }
 +      if (id_req != NO_ID_REQ)
 +              eap_aka_clear_identities(sm, data, CLEAR_EAP_ID);
 +
 +      wpa_printf(MSG_DEBUG, "Generating EAP-AKA Identity (id=%d)", id);
 +      msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method,
 +                             EAP_AKA_SUBTYPE_IDENTITY);
 +
 +      if (identity) {
 +              wpa_hexdump_ascii(MSG_DEBUG, "   AT_IDENTITY",
 +                                identity, identity_len);
 +              eap_sim_msg_add(msg, EAP_SIM_AT_IDENTITY, identity_len,
 +                              identity, identity_len);
 +      }
 +
 +      return eap_sim_msg_finish(msg, data->eap_method, NULL, NULL, 0);
 +}
 +
 +
 +static struct wpabuf * eap_aka_response_challenge(struct eap_aka_data *data,
 +                                                u8 id)
 +{
 +      struct eap_sim_msg *msg;
 +
 +      wpa_printf(MSG_DEBUG, "Generating EAP-AKA Challenge (id=%d)", id);
 +      msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method,
 +                             EAP_AKA_SUBTYPE_CHALLENGE);
 +      wpa_printf(MSG_DEBUG, "   AT_RES");
 +      eap_sim_msg_add(msg, EAP_SIM_AT_RES, data->res_len * 8,
 +                      data->res, data->res_len);
 +      eap_aka_add_checkcode(data, msg);
 +      if (data->use_result_ind) {
 +              wpa_printf(MSG_DEBUG, "   AT_RESULT_IND");
 +              eap_sim_msg_add(msg, EAP_SIM_AT_RESULT_IND, 0, NULL, 0);
 +      }
 +      wpa_printf(MSG_DEBUG, "   AT_MAC");
 +      eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
 +      return eap_sim_msg_finish(msg, data->eap_method, data->k_aut, (u8 *) "",
 +                                0);
 +}
 +
 +
 +static struct wpabuf * eap_aka_response_reauth(struct eap_aka_data *data,
 +                                             u8 id, int counter_too_small,
 +                                             const u8 *nonce_s)
 +{
 +      struct eap_sim_msg *msg;
 +      unsigned int counter;
 +
 +      wpa_printf(MSG_DEBUG, "Generating EAP-AKA Reauthentication (id=%d)",
 +                 id);
 +      msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method,
 +                             EAP_AKA_SUBTYPE_REAUTHENTICATION);
 +      wpa_printf(MSG_DEBUG, "   AT_IV");
 +      wpa_printf(MSG_DEBUG, "   AT_ENCR_DATA");
 +      eap_sim_msg_add_encr_start(msg, EAP_SIM_AT_IV, EAP_SIM_AT_ENCR_DATA);
 +
 +      if (counter_too_small) {
 +              wpa_printf(MSG_DEBUG, "   *AT_COUNTER_TOO_SMALL");
 +              eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER_TOO_SMALL, 0, NULL, 0);
 +              counter = data->counter_too_small;
 +      } else
 +              counter = data->counter;
 +
 +      wpa_printf(MSG_DEBUG, "   *AT_COUNTER %d", counter);
 +      eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER, counter, NULL, 0);
 +
 +      if (eap_sim_msg_add_encr_end(msg, data->k_encr, EAP_SIM_AT_PADDING)) {
 +              wpa_printf(MSG_WARNING, "EAP-AKA: Failed to encrypt "
 +                         "AT_ENCR_DATA");
 +              eap_sim_msg_free(msg);
 +              return NULL;
 +      }
 +      eap_aka_add_checkcode(data, msg);
 +      if (data->use_result_ind) {
 +              wpa_printf(MSG_DEBUG, "   AT_RESULT_IND");
 +              eap_sim_msg_add(msg, EAP_SIM_AT_RESULT_IND, 0, NULL, 0);
 +      }
 +      wpa_printf(MSG_DEBUG, "   AT_MAC");
 +      eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
 +      return eap_sim_msg_finish(msg, data->eap_method, data->k_aut, nonce_s,
 +                                EAP_SIM_NONCE_S_LEN);
 +}
 +
 +
 +static struct wpabuf * eap_aka_response_notification(struct eap_aka_data *data,
 +                                                   u8 id, u16 notification)
 +{
 +      struct eap_sim_msg *msg;
 +      u8 *k_aut = (notification & 0x4000) == 0 ? data->k_aut : NULL;
 +
 +      wpa_printf(MSG_DEBUG, "Generating EAP-AKA Notification (id=%d)", id);
 +      msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method,
 +                             EAP_AKA_SUBTYPE_NOTIFICATION);
 +      if (k_aut && data->reauth) {
 +              wpa_printf(MSG_DEBUG, "   AT_IV");
 +              wpa_printf(MSG_DEBUG, "   AT_ENCR_DATA");
 +              eap_sim_msg_add_encr_start(msg, EAP_SIM_AT_IV,
 +                                         EAP_SIM_AT_ENCR_DATA);
 +              wpa_printf(MSG_DEBUG, "   *AT_COUNTER %d", data->counter);
 +              eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER, data->counter,
 +                              NULL, 0);
 +              if (eap_sim_msg_add_encr_end(msg, data->k_encr,
 +                                           EAP_SIM_AT_PADDING)) {
 +                      wpa_printf(MSG_WARNING, "EAP-AKA: Failed to encrypt "
 +                                 "AT_ENCR_DATA");
 +                      eap_sim_msg_free(msg);
 +                      return NULL;
 +              }
 +      }
 +      if (k_aut) {
 +              wpa_printf(MSG_DEBUG, "   AT_MAC");
 +              eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
 +      }
 +      return eap_sim_msg_finish(msg, data->eap_method, k_aut, (u8 *) "", 0);
 +}
 +
 +
 +static struct wpabuf * eap_aka_process_identity(struct eap_sm *sm,
 +                                              struct eap_aka_data *data,
 +                                              u8 id,
 +                                              const struct wpabuf *reqData,
 +                                              struct eap_sim_attrs *attr)
 +{
 +      int id_error;
 +      struct wpabuf *buf;
 +
 +      wpa_printf(MSG_DEBUG, "EAP-AKA: subtype Identity");
 +
 +      id_error = 0;
 +      switch (attr->id_req) {
 +      case NO_ID_REQ:
 +              break;
 +      case ANY_ID:
 +              if (data->num_id_req > 0)
 +                      id_error++;
 +              data->num_id_req++;
 +              break;
 +      case FULLAUTH_ID:
 +              if (data->num_id_req > 1)
 +                      id_error++;
 +              data->num_id_req++;
 +              break;
 +      case PERMANENT_ID:
 +              if (data->num_id_req > 2)
 +                      id_error++;
 +              data->num_id_req++;
 +              break;
 +      }
 +      if (id_error) {
 +              wpa_printf(MSG_INFO, "EAP-AKA: Too many ID requests "
 +                         "used within one authentication");
 +              return eap_aka_client_error(data, id,
 +                                          EAP_AKA_UNABLE_TO_PROCESS_PACKET);
 +      }
 +
 +      buf = eap_aka_response_identity(sm, data, id, attr->id_req);
 +
 +      if (data->prev_id != id) {
 +              eap_aka_add_id_msg(data, reqData);
 +              eap_aka_add_id_msg(data, buf);
 +              data->prev_id = id;
 +      }
 +
 +      return buf;
 +}
 +
 +
 +static int eap_aka_verify_mac(struct eap_aka_data *data,
 +                            const struct wpabuf *req,
 +                            const u8 *mac, const u8 *extra,
 +                            size_t extra_len)
 +{
 +      if (data->eap_method == EAP_TYPE_AKA_PRIME)
 +              return eap_sim_verify_mac_sha256(data->k_aut, req, mac, extra,
 +                                               extra_len);
 +      return eap_sim_verify_mac(data->k_aut, req, mac, extra, extra_len);
 +}
 +
 +
 +#ifdef EAP_AKA_PRIME
 +static struct wpabuf * eap_aka_prime_kdf_select(struct eap_aka_data *data,
 +                                              u8 id, u16 kdf)
 +{
 +      struct eap_sim_msg *msg;
 +
 +      data->kdf_negotiation = 1;
 +      data->kdf = kdf;
 +      wpa_printf(MSG_DEBUG, "Generating EAP-AKA Challenge (id=%d) (KDF "
 +                 "select)", id);
 +      msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method,
 +                             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, data->eap_method, NULL, NULL, 0);
 +}
 +
 +
 +static struct wpabuf * eap_aka_prime_kdf_neg(struct eap_aka_data *data,
 +                                           u8 id, struct eap_sim_attrs *attr)
 +{
 +      size_t i;
 +
 +      for (i = 0; i < attr->kdf_count; i++) {
 +              if (attr->kdf[i] == EAP_AKA_PRIME_KDF)
 +                      return eap_aka_prime_kdf_select(data, id,
 +                                                      EAP_AKA_PRIME_KDF);
 +      }
 +
 +      /* No matching KDF found - fail authentication as if AUTN had been
 +       * incorrect */
 +      return eap_aka_authentication_reject(data, id);
 +}
 +
 +
 +static int eap_aka_prime_kdf_valid(struct eap_aka_data *data,
 +                                 struct eap_sim_attrs *attr)
 +{
 +      size_t i, j;
 +
 +      if (attr->kdf_count == 0)
 +              return 0;
 +
 +      /* The only allowed (and required) duplication of a KDF is the addition
 +       * of the selected KDF into the beginning of the list. */
 +
 +      if (data->kdf_negotiation) {
 +              if (attr->kdf[0] != data->kdf) {
 +                      wpa_printf(MSG_WARNING, "EAP-AKA': The server did not "
 +                                 "accept the selected KDF");
 +                      return 0;
 +              }
 +
 +              for (i = 1; i < attr->kdf_count; i++) {
 +                      if (attr->kdf[i] == data->kdf)
 +                              break;
 +              }
 +              if (i == attr->kdf_count &&
 +                  attr->kdf_count < EAP_AKA_PRIME_KDF_MAX) {
 +                      wpa_printf(MSG_WARNING, "EAP-AKA': The server did not "
 +                                 "duplicate the selected KDF");
 +                      return 0;
 +              }
 +
 +              /* TODO: should check that the list is identical to the one
 +               * used in the previous Challenge message apart from the added
 +               * entry in the beginning. */
 +      }
 +
 +      for (i = data->kdf ? 1 : 0; i < attr->kdf_count; i++) {
 +              for (j = i + 1; j < attr->kdf_count; j++) {
 +                      if (attr->kdf[i] == attr->kdf[j]) {
 +                              wpa_printf(MSG_WARNING, "EAP-AKA': The server "
 +                                         "included a duplicated KDF");
 +                              return 0;
 +                      }
 +              }
 +      }
 +
 +      return 1;
 +}
 +#endif /* EAP_AKA_PRIME */
 +
 +
 +static struct wpabuf * eap_aka_process_challenge(struct eap_sm *sm,
 +                                               struct eap_aka_data *data,
 +                                               u8 id,
 +                                               const struct wpabuf *reqData,
 +                                               struct eap_sim_attrs *attr)
 +{
 +      const u8 *identity;
 +      size_t identity_len;
 +      int res;
 +      struct eap_sim_attrs eattr;
 +
 +      wpa_printf(MSG_DEBUG, "EAP-AKA: subtype Challenge");
 +
 +      if (attr->checkcode &&
 +          eap_aka_verify_checkcode(data, attr->checkcode,
 +                                   attr->checkcode_len)) {
 +              wpa_printf(MSG_WARNING, "EAP-AKA: Invalid AT_CHECKCODE in the "
 +                         "message");
 +              return eap_aka_client_error(data, id,
 +                                          EAP_AKA_UNABLE_TO_PROCESS_PACKET);
 +      }
 +
 +#ifdef EAP_AKA_PRIME
 +      if (data->eap_method == EAP_TYPE_AKA_PRIME) {
 +              if (!attr->kdf_input || attr->kdf_input_len == 0) {
 +                      wpa_printf(MSG_WARNING, "EAP-AKA': Challenge message "
 +                                 "did not include non-empty AT_KDF_INPUT");
 +                      /* Fail authentication as if AUTN had been incorrect */
 +                      return eap_aka_authentication_reject(data, id);
 +              }
 +              os_free(data->network_name);
 +              data->network_name = os_malloc(attr->kdf_input_len);
 +              if (data->network_name == NULL) {
 +                      wpa_printf(MSG_WARNING, "EAP-AKA': No memory for "
 +                                 "storing Network Name");
 +                      return eap_aka_authentication_reject(data, id);
 +              }
 +              os_memcpy(data->network_name, attr->kdf_input,
 +                        attr->kdf_input_len);
 +              data->network_name_len = attr->kdf_input_len;
 +              wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA': Network Name "
 +                                "(AT_KDF_INPUT)",
 +                                data->network_name, data->network_name_len);
 +              /* TODO: check Network Name per 3GPP.33.402 */
 +
 +              if (!eap_aka_prime_kdf_valid(data, attr))
 +                      return eap_aka_authentication_reject(data, id);
 +
 +              if (attr->kdf[0] != EAP_AKA_PRIME_KDF)
 +                      return eap_aka_prime_kdf_neg(data, id, attr);
 +
 +              data->kdf = EAP_AKA_PRIME_KDF;
 +              wpa_printf(MSG_DEBUG, "EAP-AKA': KDF %d selected", data->kdf);
 +      }
 +
 +      if (data->eap_method == EAP_TYPE_AKA && attr->bidding) {
 +              u16 flags = WPA_GET_BE16(attr->bidding);
 +              if ((flags & EAP_AKA_BIDDING_FLAG_D) &&
 +                  eap_allowed_method(sm, EAP_VENDOR_IETF,
 +                                     EAP_TYPE_AKA_PRIME)) {
 +                      wpa_printf(MSG_WARNING, "EAP-AKA: Bidding down from "
 +                                 "AKA' to AKA detected");
 +                      /* Fail authentication as if AUTN had been incorrect */
 +                      return eap_aka_authentication_reject(data, id);
 +              }
 +      }
 +#endif /* EAP_AKA_PRIME */
 +
 +      data->reauth = 0;
 +      if (!attr->mac || !attr->rand || !attr->autn) {
 +              wpa_printf(MSG_WARNING, "EAP-AKA: Challenge message "
 +                         "did not include%s%s%s",
 +                         !attr->mac ? " AT_MAC" : "",
 +                         !attr->rand ? " AT_RAND" : "",
 +                         !attr->autn ? " AT_AUTN" : "");
 +              return eap_aka_client_error(data, id,
 +                                          EAP_AKA_UNABLE_TO_PROCESS_PACKET);
 +      }
 +      os_memcpy(data->rand, attr->rand, EAP_AKA_RAND_LEN);
 +      os_memcpy(data->autn, attr->autn, EAP_AKA_AUTN_LEN);
 +
 +      res = eap_aka_umts_auth(sm, data);
 +      if (res == -1) {
 +              wpa_printf(MSG_WARNING, "EAP-AKA: UMTS authentication "
 +                         "failed (AUTN)");
 +              return eap_aka_authentication_reject(data, id);
 +      } else if (res == -2) {
 +              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,
 +                                          EAP_AKA_UNABLE_TO_PROCESS_PACKET);
 +      }
 +#ifdef EAP_AKA_PRIME
 +      if (data->eap_method == EAP_TYPE_AKA_PRIME) {
 +              /* Note: AUTN = (SQN ^ AK) || AMF || MAC which gives us the
 +               * needed 6-octet SQN ^ AK for CK',IK' derivation */
 +              u16 amf = WPA_GET_BE16(data->autn + 6);
 +              if (!(amf & 0x8000)) {
 +                      wpa_printf(MSG_WARNING, "EAP-AKA': AMF separation bit "
 +                                 "not set (AMF=0x%4x)", amf);
 +                      return eap_aka_authentication_reject(data, id);
 +              }
 +              eap_aka_prime_derive_ck_ik_prime(data->ck, data->ik,
 +                                               data->autn,
 +                                               data->network_name,
 +                                               data->network_name_len);
 +      }
 +#endif /* EAP_AKA_PRIME */
 +      if (data->last_eap_identity) {
 +              identity = data->last_eap_identity;
 +              identity_len = data->last_eap_identity_len;
 +      } else if (data->pseudonym) {
 +              identity = data->pseudonym;
 +              identity_len = data->pseudonym_len;
 +      } else
 +              identity = eap_get_config_identity(sm, &identity_len);
 +      wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA: Selected identity for MK "
 +                        "derivation", identity, identity_len);
 +      if (data->eap_method == EAP_TYPE_AKA_PRIME) {
 +              eap_aka_prime_derive_keys(identity, identity_len, data->ik,
 +                                        data->ck, data->k_encr, data->k_aut,
 +                                        data->k_re, data->msk, data->emsk);
 +      } else {
 +              eap_aka_derive_mk(identity, identity_len, data->ik, data->ck,
 +                                data->mk);
 +              eap_sim_derive_keys(data->mk, data->k_encr, data->k_aut,
 +                                  data->msk, data->emsk);
 +      }
 +      if (eap_aka_verify_mac(data, reqData, attr->mac, (u8 *) "", 0)) {
 +              wpa_printf(MSG_WARNING, "EAP-AKA: Challenge message "
 +                         "used invalid AT_MAC");
 +              return eap_aka_client_error(data, id,
 +                                          EAP_AKA_UNABLE_TO_PROCESS_PACKET);
 +      }
 +
 +      /* Old reauthentication identity must not be used anymore. In
 +       * other words, if no new identities are received, full
 +       * authentication will be used on next reauthentication (using
 +       * pseudonym identity or permanent identity). */
 +      eap_aka_clear_identities(sm, data, CLEAR_REAUTH_ID | CLEAR_EAP_ID);
 +
 +      if (attr->encr_data) {
 +              u8 *decrypted;
 +              decrypted = eap_sim_parse_encr(data->k_encr, attr->encr_data,
 +                                             attr->encr_data_len, attr->iv,
 +                                             &eattr, 0);
 +              if (decrypted == NULL) {
 +                      return eap_aka_client_error(
 +                              data, id, EAP_AKA_UNABLE_TO_PROCESS_PACKET);
 +              }
 +              eap_aka_learn_ids(sm, data, &eattr);
 +              os_free(decrypted);
 +      }
 +
 +      if (data->result_ind && attr->result_ind)
 +              data->use_result_ind = 1;
 +
 +      if (data->state != FAILURE) {
 +              eap_aka_state(data, data->use_result_ind ?
 +                            RESULT_SUCCESS : SUCCESS);
 +      }
 +
 +      data->num_id_req = 0;
 +      data->num_notification = 0;
 +      /* RFC 4187 specifies that counter is initialized to one after
 +       * fullauth, but initializing it to zero makes it easier to implement
 +       * reauth verification. */
 +      data->counter = 0;
 +      return eap_aka_response_challenge(data, id);
 +}
 +
 +
 +static int eap_aka_process_notification_reauth(struct eap_aka_data *data,
 +                                             struct eap_sim_attrs *attr)
 +{
 +      struct eap_sim_attrs eattr;
 +      u8 *decrypted;
 +
 +      if (attr->encr_data == NULL || attr->iv == NULL) {
 +              wpa_printf(MSG_WARNING, "EAP-AKA: Notification message after "
 +                         "reauth did not include encrypted data");
 +              return -1;
 +      }
 +
 +      decrypted = eap_sim_parse_encr(data->k_encr, attr->encr_data,
 +                                     attr->encr_data_len, attr->iv, &eattr,
 +                                     0);
 +      if (decrypted == NULL) {
 +              wpa_printf(MSG_WARNING, "EAP-AKA: Failed to parse encrypted "
 +                         "data from notification message");
 +              return -1;
 +      }
 +
 +      if (eattr.counter < 0 || (size_t) eattr.counter != data->counter) {
 +              wpa_printf(MSG_WARNING, "EAP-AKA: Counter in notification "
 +                         "message does not match with counter in reauth "
 +                         "message");
 +              os_free(decrypted);
 +              return -1;
 +      }
 +
 +      os_free(decrypted);
 +      return 0;
 +}
 +
 +
 +static int eap_aka_process_notification_auth(struct eap_aka_data *data,
 +                                           const struct wpabuf *reqData,
 +                                           struct eap_sim_attrs *attr)
 +{
 +      if (attr->mac == NULL) {
 +              wpa_printf(MSG_INFO, "EAP-AKA: no AT_MAC in after_auth "
 +                         "Notification message");
 +              return -1;
 +      }
 +
 +      if (eap_aka_verify_mac(data, reqData, attr->mac, (u8 *) "", 0)) {
 +              wpa_printf(MSG_WARNING, "EAP-AKA: Notification message "
 +                         "used invalid AT_MAC");
 +              return -1;
 +      }
 +
 +      if (data->reauth &&
 +          eap_aka_process_notification_reauth(data, attr)) {
 +              wpa_printf(MSG_WARNING, "EAP-AKA: Invalid notification "
 +                         "message after reauth");
 +              return -1;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +static struct wpabuf * eap_aka_process_notification(
 +      struct eap_sm *sm, struct eap_aka_data *data, u8 id,
 +      const struct wpabuf *reqData, struct eap_sim_attrs *attr)
 +{
 +      wpa_printf(MSG_DEBUG, "EAP-AKA: subtype Notification");
 +      if (data->num_notification > 0) {
 +              wpa_printf(MSG_INFO, "EAP-AKA: too many notification "
 +                         "rounds (only one allowed)");
 +              return eap_aka_client_error(data, id,
 +                                          EAP_AKA_UNABLE_TO_PROCESS_PACKET);
 +      }
 +      data->num_notification++;
 +      if (attr->notification == -1) {
 +              wpa_printf(MSG_INFO, "EAP-AKA: no AT_NOTIFICATION in "
 +                         "Notification message");
 +              return eap_aka_client_error(data, id,
 +                                          EAP_AKA_UNABLE_TO_PROCESS_PACKET);
 +      }
 +
 +      if ((attr->notification & 0x4000) == 0 &&
 +          eap_aka_process_notification_auth(data, reqData, attr)) {
 +              return eap_aka_client_error(data, id,
 +                                          EAP_AKA_UNABLE_TO_PROCESS_PACKET);
 +      }
 +
 +      eap_sim_report_notification(sm->msg_ctx, attr->notification, 1);
 +      if (attr->notification >= 0 && attr->notification < 32768) {
 +              eap_aka_state(data, FAILURE);
 +      } else if (attr->notification == EAP_SIM_SUCCESS &&
 +                 data->state == RESULT_SUCCESS)
 +              eap_aka_state(data, SUCCESS);
 +      return eap_aka_response_notification(data, id, attr->notification);
 +}
 +
 +
 +static struct wpabuf * eap_aka_process_reauthentication(
 +      struct eap_sm *sm, struct eap_aka_data *data, u8 id,
 +      const struct wpabuf *reqData, struct eap_sim_attrs *attr)
 +{
 +      struct eap_sim_attrs eattr;
 +      u8 *decrypted;
 +
 +      wpa_printf(MSG_DEBUG, "EAP-AKA: subtype Reauthentication");
 +
 +      if (attr->checkcode &&
 +          eap_aka_verify_checkcode(data, attr->checkcode,
 +                                   attr->checkcode_len)) {
 +              wpa_printf(MSG_WARNING, "EAP-AKA: Invalid AT_CHECKCODE in the "
 +                         "message");
 +              return eap_aka_client_error(data, id,
 +                                          EAP_AKA_UNABLE_TO_PROCESS_PACKET);
 +      }
 +
 +      if (data->reauth_id == NULL) {
 +              wpa_printf(MSG_WARNING, "EAP-AKA: Server is trying "
 +                         "reauthentication, but no reauth_id available");
 +              return eap_aka_client_error(data, id,
 +                                          EAP_AKA_UNABLE_TO_PROCESS_PACKET);
 +      }
 +
 +      data->reauth = 1;
 +      if (eap_aka_verify_mac(data, reqData, attr->mac, (u8 *) "", 0)) {
 +              wpa_printf(MSG_WARNING, "EAP-AKA: Reauthentication "
 +                         "did not have valid AT_MAC");
 +              return eap_aka_client_error(data, id,
 +                                          EAP_AKA_UNABLE_TO_PROCESS_PACKET);
 +      }
 +
 +      if (attr->encr_data == NULL || attr->iv == NULL) {
 +              wpa_printf(MSG_WARNING, "EAP-AKA: Reauthentication "
 +                         "message did not include encrypted data");
 +              return eap_aka_client_error(data, id,
 +                                          EAP_AKA_UNABLE_TO_PROCESS_PACKET);
 +      }
 +
 +      decrypted = eap_sim_parse_encr(data->k_encr, attr->encr_data,
 +                                     attr->encr_data_len, attr->iv, &eattr,
 +                                     0);
 +      if (decrypted == NULL) {
 +              wpa_printf(MSG_WARNING, "EAP-AKA: Failed to parse encrypted "
 +                         "data from reauthentication message");
 +              return eap_aka_client_error(data, id,
 +                                          EAP_AKA_UNABLE_TO_PROCESS_PACKET);
 +      }
 +
 +      if (eattr.nonce_s == NULL || eattr.counter < 0) {
 +              wpa_printf(MSG_INFO, "EAP-AKA: (encr) No%s%s in reauth packet",
 +                         !eattr.nonce_s ? " AT_NONCE_S" : "",
 +                         eattr.counter < 0 ? " AT_COUNTER" : "");
 +              os_free(decrypted);
 +              return eap_aka_client_error(data, id,
 +                                          EAP_AKA_UNABLE_TO_PROCESS_PACKET);
 +      }
 +
 +      if (eattr.counter < 0 || (size_t) eattr.counter <= data->counter) {
 +              struct wpabuf *res;
 +              wpa_printf(MSG_INFO, "EAP-AKA: (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
 +               * packet, it has to saved for the following fullauth to be
 +               * used in MK derivation. */
 +              os_free(data->last_eap_identity);
 +              data->last_eap_identity = data->reauth_id;
 +              data->last_eap_identity_len = data->reauth_id_len;
 +              data->reauth_id = NULL;
 +              data->reauth_id_len = 0;
 +
 +              res = eap_aka_response_reauth(data, id, 1, eattr.nonce_s);
 +              os_free(decrypted);
 +
 +              return res;
 +      }
 +      data->counter = eattr.counter;
 +
 +      os_memcpy(data->nonce_s, eattr.nonce_s, EAP_SIM_NONCE_S_LEN);
 +      wpa_hexdump(MSG_DEBUG, "EAP-AKA: (encr) AT_NONCE_S",
 +                  data->nonce_s, EAP_SIM_NONCE_S_LEN);
 +
 +      if (data->eap_method == EAP_TYPE_AKA_PRIME) {
 +              eap_aka_prime_derive_keys_reauth(data->k_re, data->counter,
 +                                               data->reauth_id,
 +                                               data->reauth_id_len,
 +                                               data->nonce_s,
 +                                               data->msk, data->emsk);
 +      } else {
 +              eap_sim_derive_keys_reauth(data->counter, data->reauth_id,
 +                                         data->reauth_id_len,
 +                                         data->nonce_s, data->mk,
 +                                         data->msk, data->emsk);
 +      }
 +      eap_aka_clear_identities(sm, data, CLEAR_REAUTH_ID | CLEAR_EAP_ID);
 +      eap_aka_learn_ids(sm, data, &eattr);
 +
 +      if (data->result_ind && attr->result_ind)
 +              data->use_result_ind = 1;
 +
 +      if (data->state != FAILURE) {
 +              eap_aka_state(data, data->use_result_ind ?
 +                            RESULT_SUCCESS : SUCCESS);
 +      }
 +
 +      data->num_id_req = 0;
 +      data->num_notification = 0;
 +      if (data->counter > EAP_AKA_MAX_FAST_REAUTHS) {
 +              wpa_printf(MSG_DEBUG, "EAP-AKA: Maximum number of "
 +                         "fast reauths performed - force fullauth");
 +              eap_aka_clear_identities(sm, data,
 +                                       CLEAR_REAUTH_ID | CLEAR_EAP_ID);
 +      }
 +      os_free(decrypted);
 +      return eap_aka_response_reauth(data, id, 0, data->nonce_s);
 +}
 +
 +
 +static struct wpabuf * eap_aka_process(struct eap_sm *sm, void *priv,
 +                                     struct eap_method_ret *ret,
 +                                     const struct wpabuf *reqData)
 +{
 +      struct eap_aka_data *data = priv;
 +      const struct eap_hdr *req;
 +      u8 subtype, id;
 +      struct wpabuf *res;
 +      const u8 *pos;
 +      struct eap_sim_attrs attr;
 +      size_t len;
 +
 +      wpa_hexdump_buf(MSG_DEBUG, "EAP-AKA: EAP data", reqData);
 +      if (eap_get_config_identity(sm, &len) == NULL) {
 +              wpa_printf(MSG_INFO, "EAP-AKA: Identity not configured");
 +              eap_sm_request_identity(sm);
 +              ret->ignore = TRUE;
 +              return NULL;
 +      }
 +
 +      pos = eap_hdr_validate(EAP_VENDOR_IETF, data->eap_method, reqData,
 +                             &len);
++      if (pos == NULL || len < 3) {
 +              ret->ignore = TRUE;
 +              return NULL;
 +      }
 +      req = wpabuf_head(reqData);
 +      id = req->identifier;
 +      len = be_to_host16(req->length);
 +
 +      ret->ignore = FALSE;
 +      ret->methodState = METHOD_MAY_CONT;
 +      ret->decision = DECISION_FAIL;
 +      ret->allowNotifications = TRUE;
 +
 +      subtype = *pos++;
 +      wpa_printf(MSG_DEBUG, "EAP-AKA: Subtype=%d", subtype);
 +      pos += 2; /* Reserved */
 +
 +      if (eap_sim_parse_attr(pos, wpabuf_head_u8(reqData) + len, &attr,
 +                             data->eap_method == EAP_TYPE_AKA_PRIME ? 2 : 1,
 +                             0)) {
 +              res = eap_aka_client_error(data, id,
 +                                         EAP_AKA_UNABLE_TO_PROCESS_PACKET);
 +              goto done;
 +      }
 +
 +      switch (subtype) {
 +      case EAP_AKA_SUBTYPE_IDENTITY:
 +              res = eap_aka_process_identity(sm, data, id, reqData, &attr);
 +              break;
 +      case EAP_AKA_SUBTYPE_CHALLENGE:
 +              res = eap_aka_process_challenge(sm, data, id, reqData, &attr);
 +              break;
 +      case EAP_AKA_SUBTYPE_NOTIFICATION:
 +              res = eap_aka_process_notification(sm, data, id, reqData,
 +                                                 &attr);
 +              break;
 +      case EAP_AKA_SUBTYPE_REAUTHENTICATION:
 +              res = eap_aka_process_reauthentication(sm, data, id, reqData,
 +                                                     &attr);
 +              break;
 +      case EAP_AKA_SUBTYPE_CLIENT_ERROR:
 +              wpa_printf(MSG_DEBUG, "EAP-AKA: subtype Client-Error");
 +              res = eap_aka_client_error(data, id,
 +                                         EAP_AKA_UNABLE_TO_PROCESS_PACKET);
 +              break;
 +      default:
 +              wpa_printf(MSG_DEBUG, "EAP-AKA: Unknown subtype=%d", subtype);
 +              res = eap_aka_client_error(data, id,
 +                                         EAP_AKA_UNABLE_TO_PROCESS_PACKET);
 +              break;
 +      }
 +
 +done:
 +      if (data->state == FAILURE) {
 +              ret->decision = DECISION_FAIL;
 +              ret->methodState = METHOD_DONE;
 +      } else if (data->state == SUCCESS) {
 +              ret->decision = data->use_result_ind ?
 +                      DECISION_UNCOND_SUCC : DECISION_COND_SUCC;
 +              /*
 +               * It is possible for the server to reply with AKA
 +               * Notification, so we must allow the method to continue and
 +               * not only accept EAP-Success at this point.
 +               */
 +              ret->methodState = data->use_result_ind ?
 +                      METHOD_DONE : METHOD_MAY_CONT;
 +      } else if (data->state == RESULT_SUCCESS)
 +              ret->methodState = METHOD_CONT;
 +
 +      if (ret->methodState == METHOD_DONE) {
 +              ret->allowNotifications = FALSE;
 +      }
 +
 +      return res;
 +}
 +
 +
 +static Boolean eap_aka_has_reauth_data(struct eap_sm *sm, void *priv)
 +{
 +      struct eap_aka_data *data = priv;
 +      return data->pseudonym || data->reauth_id;
 +}
 +
 +
 +static void eap_aka_deinit_for_reauth(struct eap_sm *sm, void *priv)
 +{
 +      struct eap_aka_data *data = priv;
 +      eap_aka_clear_identities(sm, data, CLEAR_EAP_ID);
 +      data->prev_id = -1;
 +      wpabuf_free(data->id_msgs);
 +      data->id_msgs = NULL;
 +      data->use_result_ind = 0;
 +      data->kdf_negotiation = 0;
 +      eap_aka_clear_keys(data, 1);
 +}
 +
 +
 +static void * eap_aka_init_for_reauth(struct eap_sm *sm, void *priv)
 +{
 +      struct eap_aka_data *data = priv;
 +      data->num_id_req = 0;
 +      data->num_notification = 0;
 +      eap_aka_state(data, CONTINUE);
 +      return priv;
 +}
 +
 +
 +static const u8 * eap_aka_get_identity(struct eap_sm *sm, void *priv,
 +                                     size_t *len)
 +{
 +      struct eap_aka_data *data = priv;
 +
 +      if (data->reauth_id) {
 +              *len = data->reauth_id_len;
 +              return data->reauth_id;
 +      }
 +
 +      if (data->pseudonym) {
 +              *len = data->pseudonym_len;
 +              return data->pseudonym;
 +      }
 +
 +      return NULL;
 +}
 +
 +
 +static Boolean eap_aka_isKeyAvailable(struct eap_sm *sm, void *priv)
 +{
 +      struct eap_aka_data *data = priv;
 +      return data->state == SUCCESS;
 +}
 +
 +
 +static u8 * eap_aka_getKey(struct eap_sm *sm, void *priv, size_t *len)
 +{
 +      struct eap_aka_data *data = priv;
 +      u8 *key;
 +
 +      if (data->state != SUCCESS)
 +              return NULL;
 +
 +      key = os_malloc(EAP_SIM_KEYING_DATA_LEN);
 +      if (key == NULL)
 +              return NULL;
 +
 +      *len = EAP_SIM_KEYING_DATA_LEN;
 +      os_memcpy(key, data->msk, EAP_SIM_KEYING_DATA_LEN);
 +
 +      return key;
 +}
 +
 +
 +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;
 +}
 +
 +
 +static u8 * eap_aka_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
 +{
 +      struct eap_aka_data *data = priv;
 +      u8 *key;
 +
 +      if (data->state != SUCCESS)
 +              return NULL;
 +
 +      key = os_malloc(EAP_EMSK_LEN);
 +      if (key == NULL)
 +              return NULL;
 +
 +      *len = EAP_EMSK_LEN;
 +      os_memcpy(key, data->emsk, EAP_EMSK_LEN);
 +
 +      return key;
 +}
 +
 +
 +int eap_peer_aka_register(void)
 +{
 +      struct eap_method *eap;
 +      int ret;
 +
 +      eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
 +                                  EAP_VENDOR_IETF, EAP_TYPE_AKA, "AKA");
 +      if (eap == NULL)
 +              return -1;
 +
 +      eap->init = eap_aka_init;
 +      eap->deinit = eap_aka_deinit;
 +      eap->process = eap_aka_process;
 +      eap->isKeyAvailable = eap_aka_isKeyAvailable;
 +      eap->getKey = eap_aka_getKey;
 +      eap->getSessionId = eap_aka_get_session_id;
 +      eap->has_reauth_data = eap_aka_has_reauth_data;
 +      eap->deinit_for_reauth = eap_aka_deinit_for_reauth;
 +      eap->init_for_reauth = eap_aka_init_for_reauth;
 +      eap->get_identity = eap_aka_get_identity;
 +      eap->get_emsk = eap_aka_get_emsk;
 +
 +      ret = eap_peer_method_register(eap);
 +      if (ret)
 +              eap_peer_method_free(eap);
 +      return ret;
 +}
 +
 +
 +#ifdef EAP_AKA_PRIME
 +int eap_peer_aka_prime_register(void)
 +{
 +      struct eap_method *eap;
 +      int ret;
 +
 +      eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
 +                                  EAP_VENDOR_IETF, EAP_TYPE_AKA_PRIME,
 +                                  "AKA'");
 +      if (eap == NULL)
 +              return -1;
 +
 +      eap->init = eap_aka_prime_init;
 +      eap->deinit = eap_aka_deinit;
 +      eap->process = eap_aka_process;
 +      eap->isKeyAvailable = eap_aka_isKeyAvailable;
 +      eap->getKey = eap_aka_getKey;
 +      eap->getSessionId = eap_aka_get_session_id;
 +      eap->has_reauth_data = eap_aka_has_reauth_data;
 +      eap->deinit_for_reauth = eap_aka_deinit_for_reauth;
 +      eap->init_for_reauth = eap_aka_init_for_reauth;
 +      eap->get_identity = eap_aka_get_identity;
 +      eap->get_emsk = eap_aka_get_emsk;
 +
 +      ret = eap_peer_method_register(eap);
 +      if (ret)
 +              eap_peer_method_free(eap);
 +
 +      return ret;
 +}
 +#endif /* EAP_AKA_PRIME */
index 9fec66c0686795b0d93051ed036e6d8c56a107df,0000000000000000000000000000000000000000..dfbda5644f6fcaa271cf4f0c49f07c10670e5e2f
mode 100644,000000..100644
--- /dev/null
@@@ -1,765 -1,0 +1,792 @@@
-                                         const struct wpabuf *reqData,
-                                         u32 failure_code)
 +/*
 + * 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,
-       resp = eap_eke_build_msg(data, eap_get_id(reqData), 4, EAP_EKE_FAILURE);
++                                        u8 id, u32 failure_code)
 +{
 +      struct wpabuf *resp;
 +
 +      wpa_printf(MSG_DEBUG, "EAP-EKE: Sending EAP-EKE-Failure/Response - code=0x%x",
 +                 failure_code);
 +
-               return eap_eke_build_fail(data, ret, reqData,
++      resp = eap_eke_build_msg(data, id, 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;
++      u8 id = eap_get_id(reqData);
 +
 +      if (data->state != IDENTITY) {
-               return eap_eke_build_fail(data, ret, reqData,
++              return eap_eke_build_fail(data, ret, id,
 +                                        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,
++              return eap_eke_build_fail(data, ret, id,
 +                                        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,
++              return eap_eke_build_fail(data, ret, id,
 +                                        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,
++              return eap_eke_build_fail(data, ret, id,
 +                                        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,
++              return eap_eke_build_fail(data, ret, id,
 +                                        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) {
-       resp = eap_eke_build_msg(data, eap_get_id(reqData),
++              return eap_eke_build_fail(data, ret, id,
 +                                        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");
 +
-               return eap_eke_build_fail(data, ret, reqData,
++      resp = eap_eke_build_msg(data, id,
 +                               2 + 4 + 1 + data->peerid_len,
 +                               EAP_EKE_ID);
 +      if (resp == NULL) {
-               return eap_eke_build_fail(data, ret, reqData,
++              return eap_eke_build_fail(data, ret, id,
 +                                        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,
++              return eap_eke_build_fail(data, ret, id,
 +                                        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;
++      u8 id = eap_get_id(reqData);
 +
 +      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,
++              return eap_eke_build_fail(data, ret, id,
 +                                        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,
++              return eap_eke_build_fail(data, ret, id,
 +                                        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,
++              return eap_eke_build_fail(data, ret, id,
 +                                        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,
++              return eap_eke_build_fail(data, ret, id,
 +                                        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,
++              return eap_eke_build_fail(data, ret, id,
 +                                        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,
++              return eap_eke_build_fail(data, ret, id,
 +                                        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));
-       resp = eap_eke_build_msg(data, eap_get_id(reqData),
++              return eap_eke_build_fail(data, ret, id,
 +                                        EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
 +      }
 +
 +      wpa_printf(MSG_DEBUG, "EAP-EKE: Sending EAP-EKE-Commit/Response");
 +
-               return eap_eke_build_fail(data, ret, reqData,
++      resp = eap_eke_build_msg(data, id,
 +                               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,
++              return eap_eke_build_fail(data, ret, id,
 +                                        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,
++              return eap_eke_build_fail(data, ret, id,
 +                                        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,
++              return eap_eke_build_fail(data, ret, id,
 +                                        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,
++              return eap_eke_build_fail(data, ret, id,
 +                                        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,
++              return eap_eke_build_fail(data, ret, id,
 +                                        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;
++      u8 id = eap_get_id(reqData);
 +
 +      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,
++              return eap_eke_build_fail(data, ret, id,
 +                                        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,
++              return eap_eke_build_fail(data, ret, id,
 +                                        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,
++              return eap_eke_build_fail(data, ret, id,
 +                                        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,
++              return eap_eke_build_fail(data, ret, id,
 +                                        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,
++              return eap_eke_build_fail(data, ret, id,
 +                                        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,
++              return eap_eke_build_fail(data, ret, id,
 +                                        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,
++              return eap_eke_build_fail(data, ret, id,
 +                                        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");
-       resp = eap_eke_build_msg(data, eap_get_id(reqData),
++              return eap_eke_build_fail(data, ret, id,
 +                                        EAP_EKE_FAIL_AUTHENTICATION_FAIL);
 +      }
 +
 +      wpa_printf(MSG_DEBUG, "EAP-EKE: Sending EAP-EKE-Confirm/Response");
 +
-               return eap_eke_build_fail(data, ret, reqData,
++      resp = eap_eke_build_msg(data, id,
 +                               data->sess.pnonce_len + data->sess.prf_len,
 +                               EAP_EKE_CONFIRM);
 +      if (resp == NULL) {
-               return eap_eke_build_fail(data, ret, reqData,
++              return eap_eke_build_fail(data, ret, id,
 +                                        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,
++              return eap_eke_build_fail(data, ret, id,
 +                                        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,
++              return eap_eke_build_fail(data, ret, id,
 +                                        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_NO_ERROR);
++              return eap_eke_build_fail(data, ret, id,
 +                                        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, eap_get_id(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;
 +}
 +
 +
++static u8 * eap_eke_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
++{
++      struct eap_eke_data *data = priv;
++      u8 *sid;
++      size_t sid_len;
++
++      if (data->state != SUCCESS)
++              return NULL;
++
++      sid_len = 1 + 2 * data->sess.nonce_len;
++      sid = os_malloc(sid_len);
++      if (sid == NULL)
++              return NULL;
++      sid[0] = EAP_TYPE_EKE;
++      os_memcpy(sid + 1, data->nonce_p, data->sess.nonce_len);
++      os_memcpy(sid + 1 + data->sess.nonce_len, data->nonce_s,
++                data->sess.nonce_len);
++      *len = sid_len;
++
++      return sid;
++}
++
++
 +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;
++      eap->getSessionId = eap_eke_get_session_id;
 +
 +      ret = eap_peer_method_register(eap);
 +      if (ret)
 +              eap_peer_method_free(eap);
 +      return ret;
 +}
index 68d7fba8892efd4136f43254dff09f199c24a088,0000000000000000000000000000000000000000..4cbe3bacb0a61a72d8e58ca9b78742c58d6b6b0f
mode 100644,000000..100644
--- /dev/null
@@@ -1,1769 -1,0 +1,1785 @@@
- static void eap_fast_derive_key_auth(struct eap_sm *sm,
-                                    struct eap_fast_data *data)
 +/*
 + * EAP peer method: EAP-FAST (RFC 4851)
 + * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
 + *
 + * This software may be distributed under the terms of the BSD license.
 + * See README for more details.
 + */
 +
 +#include "includes.h"
 +
 +#include "common.h"
 +#include "crypto/tls.h"
 +#include "crypto/sha1.h"
 +#include "eap_common/eap_tlv_common.h"
 +#include "eap_i.h"
 +#include "eap_tls_common.h"
 +#include "eap_config.h"
 +#include "eap_fast_pac.h"
 +
 +#ifdef EAP_FAST_DYNAMIC
 +#include "eap_fast_pac.c"
 +#endif /* EAP_FAST_DYNAMIC */
 +
 +/* TODO:
 + * - test session resumption and enable it if it interoperates
 + * - password change (pending mschapv2 packet; replay decrypted packet)
 + */
 +
 +
 +static void eap_fast_deinit(struct eap_sm *sm, void *priv);
 +
 +
 +struct eap_fast_data {
 +      struct eap_ssl_data ssl;
 +
 +      int fast_version;
 +
 +      const struct eap_method *phase2_method;
 +      void *phase2_priv;
 +      int phase2_success;
 +
 +      struct eap_method_type phase2_type;
 +      struct eap_method_type *phase2_types;
 +      size_t num_phase2_types;
 +      int resuming; /* starting a resumed session */
 +      struct eap_fast_key_block_provisioning *key_block_p;
 +#define EAP_FAST_PROV_UNAUTH 1
 +#define EAP_FAST_PROV_AUTH 2
 +      int provisioning_allowed; /* Allowed PAC provisioning modes */
 +      int provisioning; /* doing PAC provisioning (not the normal auth) */
 +      int anon_provisioning; /* doing anonymous (unauthenticated)
 +                              * provisioning */
 +      int session_ticket_used;
 +
 +      u8 key_data[EAP_FAST_KEY_LEN];
 +      u8 *session_id;
 +      size_t id_len;
 +      u8 emsk[EAP_EMSK_LEN];
 +      int success;
 +
 +      struct eap_fast_pac *pac;
 +      struct eap_fast_pac *current_pac;
 +      size_t max_pac_list_len;
 +      int use_pac_binary_format;
 +
 +      u8 simck[EAP_FAST_SIMCK_LEN];
 +      int simck_idx;
 +
 +      struct wpabuf *pending_phase2_req;
 +};
 +
 +
 +static int eap_fast_session_ticket_cb(void *ctx, const u8 *ticket, size_t len,
 +                                    const u8 *client_random,
 +                                    const u8 *server_random,
 +                                    u8 *master_secret)
 +{
 +      struct eap_fast_data *data = ctx;
 +
 +      wpa_printf(MSG_DEBUG, "EAP-FAST: SessionTicket callback");
 +
 +      if (client_random == NULL || server_random == NULL ||
 +          master_secret == NULL) {
 +              wpa_printf(MSG_DEBUG, "EAP-FAST: SessionTicket failed - fall "
 +                         "back to full TLS handshake");
 +              data->session_ticket_used = 0;
 +              if (data->provisioning_allowed) {
 +                      wpa_printf(MSG_DEBUG, "EAP-FAST: Try to provision a "
 +                                 "new PAC-Key");
 +                      data->provisioning = 1;
 +                      data->current_pac = NULL;
 +              }
 +              return 0;
 +      }
 +
 +      wpa_hexdump(MSG_DEBUG, "EAP-FAST: SessionTicket", ticket, len);
 +
 +      if (data->current_pac == NULL) {
 +              wpa_printf(MSG_DEBUG, "EAP-FAST: No PAC-Key available for "
 +                         "using SessionTicket");
 +              data->session_ticket_used = 0;
 +              return 0;
 +      }
 +
 +      eap_fast_derive_master_secret(data->current_pac->pac_key,
 +                                    server_random, client_random,
 +                                    master_secret);
 +
 +      data->session_ticket_used = 1;
 +
 +      return 1;
 +}
 +
 +
 +static int eap_fast_parse_phase1(struct eap_fast_data *data,
 +                               const char *phase1)
 +{
 +      const char *pos;
 +
 +      pos = os_strstr(phase1, "fast_provisioning=");
 +      if (pos) {
 +              data->provisioning_allowed = atoi(pos + 18);
 +              wpa_printf(MSG_DEBUG, "EAP-FAST: Automatic PAC provisioning "
 +                         "mode: %d", data->provisioning_allowed);
 +      }
 +
 +      pos = os_strstr(phase1, "fast_max_pac_list_len=");
 +      if (pos) {
 +              data->max_pac_list_len = atoi(pos + 22);
 +              if (data->max_pac_list_len == 0)
 +                      data->max_pac_list_len = 1;
 +              wpa_printf(MSG_DEBUG, "EAP-FAST: Maximum PAC list length: %lu",
 +                         (unsigned long) data->max_pac_list_len);
 +      }
 +
 +      pos = os_strstr(phase1, "fast_pac_format=binary");
 +      if (pos) {
 +              data->use_pac_binary_format = 1;
 +              wpa_printf(MSG_DEBUG, "EAP-FAST: Using binary format for PAC "
 +                         "list");
 +      }
 +
 +      return 0;
 +}
 +
 +
 +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->phase1 && eap_fast_parse_phase1(data, config->phase1) < 0) {
 +              eap_fast_deinit(sm, data);
 +              return NULL;
 +      }
 +
 +      if (eap_peer_select_phase2_methods(config, "auth=",
 +                                         &data->phase2_types,
 +                                         &data->num_phase2_types) < 0) {
 +              eap_fast_deinit(sm, data);
 +              return NULL;
 +      }
 +
 +      data->phase2_type.vendor = EAP_VENDOR_IETF;
 +      data->phase2_type.method = EAP_TYPE_NONE;
 +
 +      if (eap_peer_tls_ssl_init(sm, &data->ssl, config, EAP_TYPE_FAST)) {
 +              wpa_printf(MSG_INFO, "EAP-FAST: Failed to initialize SSL.");
 +              eap_fast_deinit(sm, data);
 +              return NULL;
 +      }
 +
 +      if (tls_connection_set_session_ticket_cb(sm->ssl_ctx, data->ssl.conn,
 +                                               eap_fast_session_ticket_cb,
 +                                               data) < 0) {
 +              wpa_printf(MSG_INFO, "EAP-FAST: Failed to set SessionTicket "
 +                         "callback");
 +              eap_fast_deinit(sm, data);
 +              return NULL;
 +      }
 +
 +      /*
 +       * The local RADIUS server in a Cisco AP does not seem to like empty
 +       * fragments before data, so disable that workaround for CBC.
 +       * TODO: consider making this configurable
 +       */
 +      if (tls_connection_enable_workaround(sm->ssl_ctx, data->ssl.conn)) {
 +              wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to enable TLS "
 +                         "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;
 +      }
 +      eap_fast_pac_list_truncate(data->pac, data->max_pac_list_len);
 +
 +      if (data->pac == NULL && !data->provisioning_allowed) {
 +              wpa_printf(MSG_INFO, "EAP-FAST: No PAC configured and "
 +                         "provisioning disabled");
 +              eap_fast_deinit(sm, data);
 +              return NULL;
 +      }
 +
 +      return data;
 +}
 +
 +
 +static void eap_fast_deinit(struct eap_sm *sm, void *priv)
 +{
 +      struct eap_fast_data *data = priv;
 +      struct eap_fast_pac *pac, *prev;
 +
 +      if (data == NULL)
 +              return;
 +      if (data->phase2_priv && data->phase2_method)
 +              data->phase2_method->deinit(sm, data->phase2_priv);
 +      os_free(data->phase2_types);
 +      os_free(data->key_block_p);
 +      eap_peer_tls_ssl_deinit(sm, &data->ssl);
 +
 +      pac = data->pac;
 +      prev = NULL;
 +      while (pac) {
 +              prev = pac;
 +              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);
 +}
 +
 +
 +static int eap_fast_derive_msk(struct eap_fast_data *data)
 +{
 +      eap_fast_derive_eap_msk(data->simck, data->key_data);
 +      eap_fast_derive_eap_emsk(data->simck, data->emsk);
 +      data->success = 1;
 +      return 0;
 +}
 +
 +
-               return;
++static int eap_fast_derive_key_auth(struct eap_sm *sm,
++                                  struct eap_fast_data *data)
 +{
 +      u8 *sks;
 +
 +      /* RFC 4851, Section 5.1:
 +       * Extra key material after TLS key_block: session_key_seed[40]
 +       */
 +
 +      sks = eap_fast_derive_key(sm->ssl_ctx, data->ssl.conn, "key expansion",
 +                                EAP_FAST_SKS_LEN);
 +      if (sks == NULL) {
 +              wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to derive "
 +                         "session_key_seed");
- static void eap_fast_derive_key_provisioning(struct eap_sm *sm,
-                                            struct eap_fast_data *data)
++              return -1;
 +      }
 +
 +      /*
 +       * RFC 4851, Section 5.2:
 +       * S-IMCK[0] = session_key_seed
 +       */
 +      wpa_hexdump_key(MSG_DEBUG,
 +                      "EAP-FAST: session_key_seed (SKS = S-IMCK[0])",
 +                      sks, EAP_FAST_SKS_LEN);
 +      data->simck_idx = 0;
 +      os_memcpy(data->simck, sks, EAP_FAST_SIMCK_LEN);
 +      os_free(sks);
++      return 0;
 +}
 +
 +
-               return;
++static int eap_fast_derive_key_provisioning(struct eap_sm *sm,
++                                          struct eap_fast_data *data)
 +{
 +      os_free(data->key_block_p);
 +      data->key_block_p = (struct eap_fast_key_block_provisioning *)
 +              eap_fast_derive_key(sm->ssl_ctx, data->ssl.conn,
 +                                  "key expansion",
 +                                  sizeof(*data->key_block_p));
 +      if (data->key_block_p == NULL) {
 +              wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to derive key block");
- static void eap_fast_derive_keys(struct eap_sm *sm, struct eap_fast_data *data)
++              return -1;
 +      }
 +      /*
 +       * RFC 4851, Section 5.2:
 +       * S-IMCK[0] = session_key_seed
 +       */
 +      wpa_hexdump_key(MSG_DEBUG,
 +                      "EAP-FAST: session_key_seed (SKS = S-IMCK[0])",
 +                      data->key_block_p->session_key_seed,
 +                      sizeof(data->key_block_p->session_key_seed));
 +      data->simck_idx = 0;
 +      os_memcpy(data->simck, data->key_block_p->session_key_seed,
 +                EAP_FAST_SIMCK_LEN);
 +      wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: server_challenge",
 +                      data->key_block_p->server_challenge,
 +                      sizeof(data->key_block_p->server_challenge));
 +      wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: client_challenge",
 +                      data->key_block_p->client_challenge,
 +                      sizeof(data->key_block_p->client_challenge));
++      return 0;
 +}
 +
 +
-               eap_fast_derive_key_provisioning(sm, data);
++static int eap_fast_derive_keys(struct eap_sm *sm, struct eap_fast_data *data)
 +{
++      int res;
++
 +      if (data->anon_provisioning)
-               eap_fast_derive_key_auth(sm, data);
++              res = eap_fast_derive_key_provisioning(sm, data);
 +      else
-                                     const struct eap_hdr *req,
++              res = eap_fast_derive_key_auth(sm, data);
++      return res;
 +}
 +
 +
 +static int eap_fast_init_phase2_method(struct eap_sm *sm,
 +                                     struct eap_fast_data *data)
 +{
 +      data->phase2_method =
 +              eap_peer_get_eap_method(data->phase2_type.vendor,
 +                                      data->phase2_type.method);
 +      if (data->phase2_method == NULL)
 +              return -1;
 +
 +      if (data->key_block_p) {
 +              sm->auth_challenge = data->key_block_p->server_challenge;
 +              sm->peer_challenge = data->key_block_p->client_challenge;
 +      }
 +      sm->init_phase2 = 1;
 +      data->phase2_priv = data->phase2_method->init(sm);
 +      sm->init_phase2 = 0;
 +      sm->auth_challenge = NULL;
 +      sm->peer_challenge = NULL;
 +
 +      return data->phase2_priv == NULL ? -1 : 0;
 +}
 +
 +
 +static int eap_fast_select_phase2_method(struct eap_fast_data *data, u8 type)
 +{
 +      size_t i;
 +
 +      /* TODO: TNC with anonymous provisioning; need to require both
 +       * completed MSCHAPv2 and TNC */
 +
 +      if (data->anon_provisioning && type != EAP_TYPE_MSCHAPV2) {
 +              wpa_printf(MSG_INFO, "EAP-FAST: Only EAP-MSCHAPv2 is allowed "
 +                         "during unauthenticated provisioning; reject phase2"
 +                         " type %d", type);
 +              return -1;
 +      }
 +
 +#ifdef EAP_TNC
 +      if (type == EAP_TYPE_TNC) {
 +              data->phase2_type.vendor = EAP_VENDOR_IETF;
 +              data->phase2_type.method = EAP_TYPE_TNC;
 +              wpa_printf(MSG_DEBUG, "EAP-FAST: Selected Phase 2 EAP "
 +                         "vendor %d method %d for TNC",
 +                         data->phase2_type.vendor,
 +                         data->phase2_type.method);
 +              return 0;
 +      }
 +#endif /* EAP_TNC */
 +
 +      for (i = 0; i < data->num_phase2_types; i++) {
 +              if (data->phase2_types[i].vendor != EAP_VENDOR_IETF ||
 +                  data->phase2_types[i].method != type)
 +                      continue;
 +
 +              data->phase2_type.vendor = data->phase2_types[i].vendor;
 +              data->phase2_type.method = data->phase2_types[i].method;
 +              wpa_printf(MSG_DEBUG, "EAP-FAST: Selected Phase 2 EAP "
 +                         "vendor %d method %d",
 +                         data->phase2_type.vendor,
 +                         data->phase2_type.method);
 +              break;
 +      }
 +
 +      if (type != data->phase2_type.method || type == EAP_TYPE_NONE)
 +              return -1;
 +
 +      return 0;
 +}
 +
 +
 +static int eap_fast_phase2_request(struct eap_sm *sm,
 +                                 struct eap_fast_data *data,
 +                                 struct eap_method_ret *ret,
 +                                 struct eap_hdr *hdr,
 +                                 struct wpabuf **resp)
 +{
 +      size_t len = be_to_host16(hdr->length);
 +      u8 *pos;
 +      struct eap_method_ret iret;
 +      struct eap_peer_config *config = eap_get_config(sm);
 +      struct wpabuf msg;
 +
 +      if (len <= sizeof(struct eap_hdr)) {
 +              wpa_printf(MSG_INFO, "EAP-FAST: too short "
 +                         "Phase 2 request (len=%lu)", (unsigned long) len);
 +              return -1;
 +      }
 +      pos = (u8 *) (hdr + 1);
 +      wpa_printf(MSG_DEBUG, "EAP-FAST: Phase 2 Request: type=%d", *pos);
 +      if (*pos == EAP_TYPE_IDENTITY) {
 +              *resp = eap_sm_buildIdentity(sm, hdr->identifier, 1);
 +              return 0;
 +      }
 +
 +      if (data->phase2_priv && data->phase2_method &&
 +          *pos != data->phase2_type.method) {
 +              wpa_printf(MSG_DEBUG, "EAP-FAST: Phase 2 EAP sequence - "
 +                         "deinitialize previous method");
 +              data->phase2_method->deinit(sm, data->phase2_priv);
 +              data->phase2_method = NULL;
 +              data->phase2_priv = NULL;
 +              data->phase2_type.vendor = EAP_VENDOR_IETF;
 +              data->phase2_type.method = EAP_TYPE_NONE;
 +      }
 +
 +      if (data->phase2_type.vendor == EAP_VENDOR_IETF &&
 +          data->phase2_type.method == EAP_TYPE_NONE &&
 +          eap_fast_select_phase2_method(data, *pos) < 0) {
 +              if (eap_peer_tls_phase2_nak(data->phase2_types,
 +                                          data->num_phase2_types,
 +                                          hdr, resp))
 +                      return -1;
 +              return 0;
 +      }
 +
 +      if ((data->phase2_priv == NULL &&
 +           eap_fast_init_phase2_method(sm, data) < 0) ||
 +          data->phase2_method == NULL) {
 +              wpa_printf(MSG_INFO, "EAP-FAST: Failed to initialize "
 +                         "Phase 2 EAP method %d", *pos);
 +              ret->methodState = METHOD_DONE;
 +              ret->decision = DECISION_FAIL;
 +              return -1;
 +      }
 +
 +      os_memset(&iret, 0, sizeof(iret));
 +      wpabuf_set(&msg, hdr, len);
 +      *resp = data->phase2_method->process(sm, data->phase2_priv, &iret,
 +                                           &msg);
 +      if (*resp == NULL ||
 +          (iret.methodState == METHOD_DONE &&
 +           iret.decision == DECISION_FAIL)) {
 +              ret->methodState = METHOD_DONE;
 +              ret->decision = DECISION_FAIL;
 +      } else if ((iret.methodState == METHOD_DONE ||
 +                  iret.methodState == METHOD_MAY_CONT) &&
 +                 (iret.decision == DECISION_UNCOND_SUCC ||
 +                  iret.decision == DECISION_COND_SUCC)) {
 +              data->phase2_success = 1;
 +      }
 +
 +      if (*resp == NULL && config &&
 +          (config->pending_req_identity || config->pending_req_password ||
 +           config->pending_req_otp || config->pending_req_new_password)) {
 +              wpabuf_free(data->pending_phase2_req);
 +              data->pending_phase2_req = wpabuf_alloc_copy(hdr, len);
 +      } else if (*resp == NULL)
 +              return -1;
 +
 +      return 0;
 +}
 +
 +
 +static struct wpabuf * eap_fast_tlv_nak(int vendor_id, int tlv_type)
 +{
 +      struct wpabuf *buf;
 +      struct eap_tlv_nak_tlv *nak;
 +      buf = wpabuf_alloc(sizeof(*nak));
 +      if (buf == NULL)
 +              return NULL;
 +      nak = wpabuf_put(buf, sizeof(*nak));
 +      nak->tlv_type = host_to_be16(EAP_TLV_TYPE_MANDATORY | EAP_TLV_NAK_TLV);
 +      nak->length = host_to_be16(6);
 +      nak->vendor_id = host_to_be32(vendor_id);
 +      nak->nak_type = host_to_be16(tlv_type);
 +      return buf;
 +}
 +
 +
 +static struct wpabuf * eap_fast_tlv_result(int status, int intermediate)
 +{
 +      struct wpabuf *buf;
 +      struct eap_tlv_intermediate_result_tlv *result;
 +      buf = wpabuf_alloc(sizeof(*result));
 +      if (buf == NULL)
 +              return NULL;
 +      wpa_printf(MSG_DEBUG, "EAP-FAST: Add %sResult TLV(status=%d)",
 +                 intermediate ? "Intermediate " : "", status);
 +      result = wpabuf_put(buf, sizeof(*result));
 +      result->tlv_type = host_to_be16(EAP_TLV_TYPE_MANDATORY |
 +                                      (intermediate ?
 +                                       EAP_TLV_INTERMEDIATE_RESULT_TLV :
 +                                       EAP_TLV_RESULT_TLV));
 +      result->length = host_to_be16(2);
 +      result->status = host_to_be16(status);
 +      return buf;
 +}
 +
 +
 +static struct wpabuf * eap_fast_tlv_pac_ack(void)
 +{
 +      struct wpabuf *buf;
 +      struct eap_tlv_result_tlv *res;
 +      struct eap_tlv_pac_ack_tlv *ack;
 +
 +      buf = wpabuf_alloc(sizeof(*res) + sizeof(*ack));
 +      if (buf == NULL)
 +              return NULL;
 +
 +      wpa_printf(MSG_DEBUG, "EAP-FAST: Add PAC TLV (ack)");
 +      ack = wpabuf_put(buf, sizeof(*ack));
 +      ack->tlv_type = host_to_be16(EAP_TLV_PAC_TLV |
 +                                   EAP_TLV_TYPE_MANDATORY);
 +      ack->length = host_to_be16(sizeof(*ack) - sizeof(struct eap_tlv_hdr));
 +      ack->pac_type = host_to_be16(PAC_TYPE_PAC_ACKNOWLEDGEMENT);
 +      ack->pac_len = host_to_be16(2);
 +      ack->result = host_to_be16(EAP_TLV_RESULT_SUCCESS);
 +
 +      return buf;
 +}
 +
 +
 +static struct wpabuf * eap_fast_process_eap_payload_tlv(
 +      struct eap_sm *sm, struct eap_fast_data *data,
 +      struct eap_method_ret *ret,
 +      u8 *eap_payload_tlv, size_t eap_payload_tlv_len)
 +{
 +      struct eap_hdr *hdr;
 +      struct wpabuf *resp = NULL;
 +
 +      if (eap_payload_tlv_len < sizeof(*hdr)) {
 +              wpa_printf(MSG_DEBUG, "EAP-FAST: too short EAP "
 +                         "Payload TLV (len=%lu)",
 +                         (unsigned long) eap_payload_tlv_len);
 +              return NULL;
 +      }
 +
 +      hdr = (struct eap_hdr *) eap_payload_tlv;
 +      if (be_to_host16(hdr->length) > eap_payload_tlv_len) {
 +              wpa_printf(MSG_DEBUG, "EAP-FAST: EAP packet overflow in "
 +                         "EAP Payload TLV");
 +              return NULL;
 +      }
 +
 +      if (hdr->code != EAP_CODE_REQUEST) {
 +              wpa_printf(MSG_INFO, "EAP-FAST: Unexpected code=%d in "
 +                         "Phase 2 EAP header", hdr->code);
 +              return NULL;
 +      }
 +
 +      if (eap_fast_phase2_request(sm, data, ret, hdr, &resp)) {
 +              wpa_printf(MSG_INFO, "EAP-FAST: Phase2 Request processing "
 +                         "failed");
 +              return NULL;
 +      }
 +
 +      return eap_fast_tlv_eap_payload(resp);
 +}
 +
 +
 +static int eap_fast_validate_crypto_binding(
 +      struct eap_tlv_crypto_binding_tlv *_bind)
 +{
 +      wpa_printf(MSG_DEBUG, "EAP-FAST: Crypto-Binding TLV: Version %d "
 +                 "Received Version %d SubType %d",
 +                 _bind->version, _bind->received_version, _bind->subtype);
 +      wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: NONCE",
 +                  _bind->nonce, sizeof(_bind->nonce));
 +      wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Compound MAC",
 +                  _bind->compound_mac, sizeof(_bind->compound_mac));
 +
 +      if (_bind->version != EAP_FAST_VERSION ||
 +          _bind->received_version != EAP_FAST_VERSION ||
 +          _bind->subtype != EAP_TLV_CRYPTO_BINDING_SUBTYPE_REQUEST) {
 +              wpa_printf(MSG_INFO, "EAP-FAST: Invalid version/subtype in "
 +                         "Crypto-Binding TLV: Version %d "
 +                         "Received Version %d SubType %d",
 +                         _bind->version, _bind->received_version,
 +                         _bind->subtype);
 +              return -1;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +static void eap_fast_write_crypto_binding(
 +      struct eap_tlv_crypto_binding_tlv *rbind,
 +      struct eap_tlv_crypto_binding_tlv *_bind, const u8 *cmk)
 +{
 +      rbind->tlv_type = host_to_be16(EAP_TLV_TYPE_MANDATORY |
 +                                     EAP_TLV_CRYPTO_BINDING_TLV);
 +      rbind->length = host_to_be16(sizeof(*rbind) -
 +                                   sizeof(struct eap_tlv_hdr));
 +      rbind->version = EAP_FAST_VERSION;
 +      rbind->received_version = _bind->version;
 +      rbind->subtype = EAP_TLV_CRYPTO_BINDING_SUBTYPE_RESPONSE;
 +      os_memcpy(rbind->nonce, _bind->nonce, sizeof(_bind->nonce));
 +      inc_byte_array(rbind->nonce, sizeof(rbind->nonce));
 +      hmac_sha1(cmk, EAP_FAST_CMK_LEN, (u8 *) rbind, sizeof(*rbind),
 +                rbind->compound_mac);
 +
 +      wpa_printf(MSG_DEBUG, "EAP-FAST: Reply Crypto-Binding TLV: Version %d "
 +                 "Received Version %d SubType %d",
 +                 rbind->version, rbind->received_version, rbind->subtype);
 +      wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: NONCE",
 +                  rbind->nonce, sizeof(rbind->nonce));
 +      wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Compound MAC",
 +                  rbind->compound_mac, sizeof(rbind->compound_mac));
 +}
 +
 +
 +static int eap_fast_get_phase2_key(struct eap_sm *sm,
 +                                 struct eap_fast_data *data,
 +                                 u8 *isk, size_t isk_len)
 +{
 +      u8 *key;
 +      size_t key_len;
 +
 +      os_memset(isk, 0, isk_len);
 +
 +      if (data->phase2_method == NULL || data->phase2_priv == NULL) {
 +              wpa_printf(MSG_DEBUG, "EAP-FAST: Phase 2 method not "
 +                         "available");
 +              return -1;
 +      }
 +
 +      if (data->phase2_method->isKeyAvailable == NULL ||
 +          data->phase2_method->getKey == NULL)
 +              return 0;
 +
 +      if (!data->phase2_method->isKeyAvailable(sm, data->phase2_priv) ||
 +          (key = data->phase2_method->getKey(sm, data->phase2_priv,
 +                                             &key_len)) == NULL) {
 +              wpa_printf(MSG_DEBUG, "EAP-FAST: Could not get key material "
 +                         "from Phase 2");
 +              return -1;
 +      }
 +
 +      if (key_len > isk_len)
 +              key_len = isk_len;
 +      if (key_len == 32 &&
 +          data->phase2_method->vendor == EAP_VENDOR_IETF &&
 +          data->phase2_method->method == EAP_TYPE_MSCHAPV2) {
 +              /*
 +               * EAP-FAST uses reverse order for MS-MPPE keys when deriving
 +               * MSK from EAP-MSCHAPv2. Swap the keys here to get the correct
 +               * ISK for EAP-FAST cryptobinding.
 +               */
 +              os_memcpy(isk, key + 16, 16);
 +              os_memcpy(isk + 16, key, 16);
 +      } else
 +              os_memcpy(isk, key, key_len);
 +      os_free(key);
 +
 +      return 0;
 +}
 +
 +
 +static int eap_fast_get_cmk(struct eap_sm *sm, struct eap_fast_data *data,
 +                          u8 *cmk)
 +{
 +      u8 isk[32], imck[60];
 +
 +      wpa_printf(MSG_DEBUG, "EAP-FAST: Determining CMK[%d] for Compound MIC "
 +                 "calculation", data->simck_idx + 1);
 +
 +      /*
 +       * RFC 4851, Section 5.2:
 +       * IMCK[j] = T-PRF(S-IMCK[j-1], "Inner Methods Compound Keys",
 +       *                 MSK[j], 60)
 +       * S-IMCK[j] = first 40 octets of IMCK[j]
 +       * CMK[j] = last 20 octets of IMCK[j]
 +       */
 +
 +      if (eap_fast_get_phase2_key(sm, data, isk, sizeof(isk)) < 0)
 +              return -1;
 +      wpa_hexdump_key(MSG_MSGDUMP, "EAP-FAST: ISK[j]", isk, sizeof(isk));
 +      sha1_t_prf(data->simck, EAP_FAST_SIMCK_LEN,
 +                 "Inner Methods Compound Keys",
 +                 isk, sizeof(isk), imck, sizeof(imck));
 +      data->simck_idx++;
 +      os_memcpy(data->simck, imck, EAP_FAST_SIMCK_LEN);
 +      wpa_hexdump_key(MSG_MSGDUMP, "EAP-FAST: S-IMCK[j]",
 +                      data->simck, EAP_FAST_SIMCK_LEN);
 +      os_memcpy(cmk, imck + EAP_FAST_SIMCK_LEN, EAP_FAST_CMK_LEN);
 +      wpa_hexdump_key(MSG_MSGDUMP, "EAP-FAST: CMK[j]",
 +                      cmk, EAP_FAST_CMK_LEN);
 +
 +      return 0;
 +}
 +
 +
 +static u8 * eap_fast_write_pac_request(u8 *pos, u16 pac_type)
 +{
 +      struct eap_tlv_hdr *pac;
 +      struct eap_tlv_request_action_tlv *act;
 +      struct eap_tlv_pac_type_tlv *type;
 +
 +      act = (struct eap_tlv_request_action_tlv *) pos;
 +      act->tlv_type = host_to_be16(EAP_TLV_REQUEST_ACTION_TLV);
 +      act->length = host_to_be16(2);
 +      act->action = host_to_be16(EAP_TLV_ACTION_PROCESS_TLV);
 +
 +      pac = (struct eap_tlv_hdr *) (act + 1);
 +      pac->tlv_type = host_to_be16(EAP_TLV_PAC_TLV);
 +      pac->length = host_to_be16(sizeof(*type));
 +
 +      type = (struct eap_tlv_pac_type_tlv *) (pac + 1);
 +      type->tlv_type = host_to_be16(PAC_TYPE_PAC_TYPE);
 +      type->length = host_to_be16(2);
 +      type->pac_type = host_to_be16(pac_type);
 +
 +      return (u8 *) (type + 1);
 +}
 +
 +
 +static struct wpabuf * eap_fast_process_crypto_binding(
 +      struct eap_sm *sm, struct eap_fast_data *data,
 +      struct eap_method_ret *ret,
 +      struct eap_tlv_crypto_binding_tlv *_bind, size_t bind_len)
 +{
 +      struct wpabuf *resp;
 +      u8 *pos;
 +      u8 cmk[EAP_FAST_CMK_LEN], cmac[SHA1_MAC_LEN];
 +      int res;
 +      size_t len;
 +
 +      if (eap_fast_validate_crypto_binding(_bind) < 0)
 +              return NULL;
 +
 +      if (eap_fast_get_cmk(sm, data, cmk) < 0)
 +              return NULL;
 +
 +      /* Validate received Compound MAC */
 +      os_memcpy(cmac, _bind->compound_mac, sizeof(cmac));
 +      os_memset(_bind->compound_mac, 0, sizeof(cmac));
 +      wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Crypto-Binding TLV for Compound "
 +                  "MAC calculation", (u8 *) _bind, bind_len);
 +      hmac_sha1(cmk, EAP_FAST_CMK_LEN, (u8 *) _bind, bind_len,
 +                _bind->compound_mac);
 +      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",
 +                  _bind->compound_mac, sizeof(cmac));
 +      if (res != 0) {
 +              wpa_printf(MSG_INFO, "EAP-FAST: Compound MAC did not match");
 +              os_memcpy(_bind->compound_mac, cmac, sizeof(cmac));
 +              return NULL;
 +      }
 +
 +      /*
 +       * Compound MAC was valid, so authentication succeeded. Reply with
 +       * crypto binding to allow server to complete authentication.
 +       */
 +
 +      len = sizeof(struct eap_tlv_crypto_binding_tlv);
 +      resp = wpabuf_alloc(len);
 +      if (resp == NULL)
 +              return NULL;
 +
 +      if (!data->anon_provisioning && data->phase2_success &&
 +          eap_fast_derive_msk(data) < 0) {
 +              wpa_printf(MSG_INFO, "EAP-FAST: Failed to generate MSK");
 +              ret->methodState = METHOD_DONE;
 +              ret->decision = DECISION_FAIL;
 +              data->phase2_success = 0;
 +              wpabuf_free(resp);
 +              return NULL;
 +      }
 +
 +      if (!data->anon_provisioning && data->phase2_success) {
 +              os_free(data->session_id);
 +              data->session_id = eap_peer_tls_derive_session_id(
 +                      sm, &data->ssl, EAP_TYPE_FAST, &data->id_len);
 +              if (data->session_id) {
 +                      wpa_hexdump(MSG_DEBUG, "EAP-FAST: Derived Session-Id",
 +                                  data->session_id, data->id_len);
 +              } else {
 +                      wpa_printf(MSG_ERROR, "EAP-FAST: Failed to derive "
 +                                 "Session-Id");
 +                      wpabuf_free(resp);
 +                      return NULL;
 +              }
 +      }
 +
 +      pos = wpabuf_put(resp, sizeof(struct eap_tlv_crypto_binding_tlv));
 +      eap_fast_write_crypto_binding((struct eap_tlv_crypto_binding_tlv *)
 +                                    pos, _bind, cmk);
 +
 +      return resp;
 +}
 +
 +
 +static void eap_fast_parse_pac_tlv(struct eap_fast_pac *entry, int type,
 +                                 u8 *pos, size_t len, int *pac_key_found)
 +{
 +      switch (type & 0x7fff) {
 +      case PAC_TYPE_PAC_KEY:
 +              wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: PAC-Key", pos, len);
 +              if (len != EAP_FAST_PAC_KEY_LEN) {
 +                      wpa_printf(MSG_DEBUG, "EAP-FAST: Invalid PAC-Key "
 +                                 "length %lu", (unsigned long) len);
 +                      break;
 +              }
 +              *pac_key_found = 1;
 +              os_memcpy(entry->pac_key, pos, len);
 +              break;
 +      case PAC_TYPE_PAC_OPAQUE:
 +              wpa_hexdump(MSG_DEBUG, "EAP-FAST: PAC-Opaque", pos, len);
 +              entry->pac_opaque = pos;
 +              entry->pac_opaque_len = len;
 +              break;
 +      case PAC_TYPE_PAC_INFO:
 +              wpa_hexdump(MSG_DEBUG, "EAP-FAST: PAC-Info", pos, len);
 +              entry->pac_info = pos;
 +              entry->pac_info_len = len;
 +              break;
 +      default:
 +              wpa_printf(MSG_DEBUG, "EAP-FAST: Ignored unknown PAC type %d",
 +                         type);
 +              break;
 +      }
 +}
 +
 +
 +static int eap_fast_process_pac_tlv(struct eap_fast_pac *entry,
 +                                  u8 *pac, size_t pac_len)
 +{
 +      struct pac_tlv_hdr *hdr;
 +      u8 *pos;
 +      size_t left, len;
 +      int type, pac_key_found = 0;
 +
 +      pos = pac;
 +      left = pac_len;
 +
 +      while (left > sizeof(*hdr)) {
 +              hdr = (struct pac_tlv_hdr *) pos;
 +              type = be_to_host16(hdr->type);
 +              len = be_to_host16(hdr->len);
 +              pos += sizeof(*hdr);
 +              left -= sizeof(*hdr);
 +              if (len > left) {
 +                      wpa_printf(MSG_DEBUG, "EAP-FAST: PAC TLV overrun "
 +                                 "(type=%d len=%lu left=%lu)",
 +                                 type, (unsigned long) len,
 +                                 (unsigned long) left);
 +                      return -1;
 +              }
 +
 +              eap_fast_parse_pac_tlv(entry, type, pos, len, &pac_key_found);
 +
 +              pos += len;
 +              left -= len;
 +      }
 +
 +      if (!pac_key_found || !entry->pac_opaque || !entry->pac_info) {
 +              wpa_printf(MSG_DEBUG, "EAP-FAST: PAC TLV does not include "
 +                         "all the required fields");
 +              return -1;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +static int eap_fast_parse_pac_info(struct eap_fast_pac *entry, int type,
 +                                 u8 *pos, size_t len)
 +{
 +      u16 pac_type;
 +      u32 lifetime;
 +      struct os_time now;
 +
 +      switch (type & 0x7fff) {
 +      case PAC_TYPE_CRED_LIFETIME:
 +              if (len != 4) {
 +                      wpa_hexdump(MSG_DEBUG, "EAP-FAST: PAC-Info - "
 +                                  "Invalid CRED_LIFETIME length - ignored",
 +                                  pos, len);
 +                      return 0;
 +              }
 +
 +              /*
 +               * This is not currently saved separately in PAC files since
 +               * the server can automatically initiate PAC update when
 +               * needed. Anyway, the information is available from PAC-Info
 +               * dump if it is needed for something in the future.
 +               */
 +              lifetime = WPA_GET_BE32(pos);
 +              os_get_time(&now);
 +              wpa_printf(MSG_DEBUG, "EAP-FAST: PAC-Info - CRED_LIFETIME %d "
 +                         "(%d days)",
 +                         lifetime, (lifetime - (u32) now.sec) / 86400);
 +              break;
 +      case PAC_TYPE_A_ID:
 +              wpa_hexdump_ascii(MSG_DEBUG, "EAP-FAST: PAC-Info - A-ID",
 +                                pos, len);
 +              entry->a_id = pos;
 +              entry->a_id_len = len;
 +              break;
 +      case PAC_TYPE_I_ID:
 +              wpa_hexdump_ascii(MSG_DEBUG, "EAP-FAST: PAC-Info - I-ID",
 +                                pos, len);
 +              entry->i_id = pos;
 +              entry->i_id_len = len;
 +              break;
 +      case PAC_TYPE_A_ID_INFO:
 +              wpa_hexdump_ascii(MSG_DEBUG, "EAP-FAST: PAC-Info - A-ID-Info",
 +                                pos, len);
 +              entry->a_id_info = pos;
 +              entry->a_id_info_len = len;
 +              break;
 +      case PAC_TYPE_PAC_TYPE:
 +              /* RFC 5422, Section 4.2.6 - PAC-Type TLV */
 +              if (len != 2) {
 +                      wpa_printf(MSG_INFO, "EAP-FAST: Invalid PAC-Type "
 +                                 "length %lu (expected 2)",
 +                                 (unsigned long) len);
 +                      wpa_hexdump_ascii(MSG_DEBUG,
 +                                        "EAP-FAST: PAC-Info - PAC-Type",
 +                                        pos, len);
 +                      return -1;
 +              }
 +              pac_type = WPA_GET_BE16(pos);
 +              if (pac_type != PAC_TYPE_TUNNEL_PAC &&
 +                  pac_type != PAC_TYPE_USER_AUTHORIZATION &&
 +                  pac_type != PAC_TYPE_MACHINE_AUTHENTICATION) {
 +                      wpa_printf(MSG_INFO, "EAP-FAST: Unsupported PAC Type "
 +                                 "%d", pac_type);
 +                      return -1;
 +              }
 +
 +              wpa_printf(MSG_DEBUG, "EAP-FAST: PAC-Info - PAC-Type %d",
 +                         pac_type);
 +              entry->pac_type = pac_type;
 +              break;
 +      default:
 +              wpa_printf(MSG_DEBUG, "EAP-FAST: Ignored unknown PAC-Info "
 +                         "type %d", type);
 +              break;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +static int eap_fast_process_pac_info(struct eap_fast_pac *entry)
 +{
 +      struct pac_tlv_hdr *hdr;
 +      u8 *pos;
 +      size_t left, len;
 +      int type;
 +
 +      /* RFC 5422, Section 4.2.4 */
 +
 +      /* PAC-Type defaults to Tunnel PAC (Type 1) */
 +      entry->pac_type = PAC_TYPE_TUNNEL_PAC;
 +
 +      pos = entry->pac_info;
 +      left = entry->pac_info_len;
 +      while (left > sizeof(*hdr)) {
 +              hdr = (struct pac_tlv_hdr *) pos;
 +              type = be_to_host16(hdr->type);
 +              len = be_to_host16(hdr->len);
 +              pos += sizeof(*hdr);
 +              left -= sizeof(*hdr);
 +              if (len > left) {
 +                      wpa_printf(MSG_DEBUG, "EAP-FAST: PAC-Info overrun "
 +                                 "(type=%d len=%lu left=%lu)",
 +                                 type, (unsigned long) len,
 +                                 (unsigned long) left);
 +                      return -1;
 +              }
 +
 +              if (eap_fast_parse_pac_info(entry, type, pos, len) < 0)
 +                      return -1;
 +
 +              pos += len;
 +              left -= len;
 +      }
 +
 +      if (entry->a_id == NULL || entry->a_id_info == NULL) {
 +              wpa_printf(MSG_DEBUG, "EAP-FAST: PAC-Info does not include "
 +                         "all the required fields");
 +              return -1;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +static struct wpabuf * eap_fast_process_pac(struct eap_sm *sm,
 +                                          struct eap_fast_data *data,
 +                                          struct eap_method_ret *ret,
 +                                          u8 *pac, size_t pac_len)
 +{
 +      struct eap_peer_config *config = eap_get_config(sm);
 +      struct eap_fast_pac entry;
 +
 +      os_memset(&entry, 0, sizeof(entry));
 +      if (eap_fast_process_pac_tlv(&entry, pac, pac_len) ||
 +          eap_fast_process_pac_info(&entry))
 +              return NULL;
 +
 +      eap_fast_add_pac(&data->pac, &data->current_pac, &entry);
 +      eap_fast_pac_list_truncate(data->pac, data->max_pac_list_len);
 +      if (data->use_pac_binary_format)
 +              eap_fast_save_pac_bin(sm, data->pac, config->pac_file);
 +      else
 +              eap_fast_save_pac(sm, data->pac, config->pac_file);
 +
 +      if (data->provisioning) {
 +              if (data->anon_provisioning) {
 +                      /*
 +                       * Unauthenticated provisioning does not provide keying
 +                       * material and must end with an EAP-Failure.
 +                       * Authentication will be done separately after this.
 +                       */
 +                      data->success = 0;
 +                      ret->decision = DECISION_FAIL;
 +              } else {
 +                      /*
 +                       * Server may or may not allow authenticated
 +                       * provisioning also for key generation.
 +                       */
 +                      ret->decision = DECISION_COND_SUCC;
 +              }
 +              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
 +               * expected to be completed with an EAP-Success. However,
 +               * RFC 5422, Section 3.5 allows EAP-Failure to be sent even
 +               * after protected success exchange in case of EAP-Fast
 +               * provisioning, so we better use DECISION_COND_SUCC here
 +               * instead of DECISION_UNCOND_SUCC.
 +               */
 +              wpa_printf(MSG_DEBUG, "EAP-FAST: Send PAC-Acknowledgement TLV "
 +                         "- PAC refreshing completed successfully");
 +              ret->decision = DECISION_COND_SUCC;
 +      }
 +      ret->methodState = METHOD_DONE;
 +      return eap_fast_tlv_pac_ack();
 +}
 +
 +
 +static int eap_fast_parse_decrypted(struct wpabuf *decrypted,
 +                                  struct eap_fast_tlv_parse *tlv,
 +                                  struct wpabuf **resp)
 +{
 +      int mandatory, tlv_type, res;
 +      size_t len;
 +      u8 *pos, *end;
 +
 +      os_memset(tlv, 0, sizeof(*tlv));
 +
 +      /* Parse TLVs from the decrypted Phase 2 data */
 +      pos = wpabuf_mhead(decrypted);
 +      end = pos + wpabuf_len(decrypted);
 +      while (pos + 4 < end) {
 +              mandatory = pos[0] & 0x80;
 +              tlv_type = WPA_GET_BE16(pos) & 0x3fff;
 +              pos += 2;
 +              len = WPA_GET_BE16(pos);
 +              pos += 2;
 +              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 %u%s",
 +                         tlv_type, (unsigned int) len,
 +                         mandatory ? " (mandatory)" : "");
 +
 +              res = eap_fast_parse_tlv(tlv, tlv_type, pos, len);
 +              if (res == -2)
 +                      break;
 +              if (res < 0) {
 +                      if (mandatory) {
 +                              wpa_printf(MSG_DEBUG, "EAP-FAST: Nak unknown "
 +                                         "mandatory TLV type %d", tlv_type);
 +                              *resp = eap_fast_tlv_nak(0, tlv_type);
 +                              break;
 +                      } else {
 +                              wpa_printf(MSG_DEBUG, "EAP-FAST: ignored "
 +                                         "unknown optional TLV type %d",
 +                                         tlv_type);
 +                      }
 +              }
 +
 +              pos += len;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +static int eap_fast_encrypt_response(struct eap_sm *sm,
 +                                   struct eap_fast_data *data,
 +                                   struct wpabuf *resp,
 +                                   u8 identifier, struct wpabuf **out_data)
 +{
 +      if (resp == NULL)
 +              return 0;
 +
 +      wpa_hexdump_buf(MSG_DEBUG, "EAP-FAST: Encrypting Phase 2 data",
 +                      resp);
 +      if (eap_peer_tls_encrypt(sm, &data->ssl, EAP_TYPE_FAST,
 +                               data->fast_version, identifier,
 +                               resp, out_data)) {
 +              wpa_printf(MSG_INFO, "EAP-FAST: Failed to encrypt a Phase 2 "
 +                         "frame");
 +      }
 +      wpabuf_free(resp);
 +
 +      return 0;
 +}
 +
 +
 +static struct wpabuf * eap_fast_pac_request(void)
 +{
 +      struct wpabuf *tmp;
 +      u8 *pos, *pos2;
 +
 +      tmp = wpabuf_alloc(sizeof(struct eap_tlv_hdr) +
 +                         sizeof(struct eap_tlv_request_action_tlv) +
 +                         sizeof(struct eap_tlv_pac_type_tlv));
 +      if (tmp == NULL)
 +              return NULL;
 +
 +      pos = wpabuf_put(tmp, 0);
 +      pos2 = eap_fast_write_pac_request(pos, PAC_TYPE_TUNNEL_PAC);
 +      wpabuf_put(tmp, pos2 - pos);
 +      return tmp;
 +}
 +
 +
 +static int eap_fast_process_decrypted(struct eap_sm *sm,
 +                                    struct eap_fast_data *data,
 +                                    struct eap_method_ret *ret,
-                                                req->identifier, out_data);
++                                    u8 identifier,
 +                                    struct wpabuf *decrypted,
 +                                    struct wpabuf **out_data)
 +{
 +      struct wpabuf *resp = NULL, *tmp;
 +      struct eap_fast_tlv_parse tlv;
 +      int failed = 0;
 +
 +      if (eap_fast_parse_decrypted(decrypted, &tlv, &resp) < 0)
 +              return 0;
 +      if (resp)
 +              return eap_fast_encrypt_response(sm, data, resp,
-                                                req->identifier, out_data);
++                                               identifier, out_data);
 +
 +      if (tlv.result == EAP_TLV_RESULT_FAILURE) {
 +              resp = eap_fast_tlv_result(EAP_TLV_RESULT_FAILURE, 0);
 +              return eap_fast_encrypt_response(sm, data, resp,
-                                                req->identifier, out_data);
++                                               identifier, out_data);
 +      }
 +
 +      if (tlv.iresult == EAP_TLV_RESULT_FAILURE) {
 +              resp = eap_fast_tlv_result(EAP_TLV_RESULT_FAILURE, 1);
 +              return eap_fast_encrypt_response(sm, data, resp,
-       return eap_fast_encrypt_response(sm, data, resp, req->identifier,
++                                               identifier, out_data);
 +      }
 +
 +      if (tlv.crypto_binding) {
 +              tmp = eap_fast_process_crypto_binding(sm, data, ret,
 +                                                    tlv.crypto_binding,
 +                                                    tlv.crypto_binding_len);
 +              if (tmp == NULL)
 +                      failed = 1;
 +              else
 +                      resp = wpabuf_concat(resp, tmp);
 +      }
 +
 +      if (tlv.iresult == EAP_TLV_RESULT_SUCCESS) {
 +              tmp = eap_fast_tlv_result(failed ? EAP_TLV_RESULT_FAILURE :
 +                                        EAP_TLV_RESULT_SUCCESS, 1);
 +              resp = wpabuf_concat(resp, tmp);
 +      }
 +
 +      if (tlv.eap_payload_tlv) {
 +              tmp = eap_fast_process_eap_payload_tlv(
 +                      sm, data, ret, tlv.eap_payload_tlv,
 +                      tlv.eap_payload_tlv_len);
 +              resp = wpabuf_concat(resp, tmp);
 +      }
 +
 +      if (tlv.pac && tlv.result != EAP_TLV_RESULT_SUCCESS) {
 +              wpa_printf(MSG_DEBUG, "EAP-FAST: PAC TLV without Result TLV "
 +                         "acknowledging success");
 +              failed = 1;
 +      } else if (tlv.pac && tlv.result == EAP_TLV_RESULT_SUCCESS) {
 +              tmp = eap_fast_process_pac(sm, data, ret, tlv.pac,
 +                                         tlv.pac_len);
 +              resp = wpabuf_concat(resp, tmp);
 +      }
 +
 +      if (data->current_pac == NULL && data->provisioning &&
 +          !data->anon_provisioning && !tlv.pac &&
 +          (tlv.iresult == EAP_TLV_RESULT_SUCCESS ||
 +           tlv.result == EAP_TLV_RESULT_SUCCESS)) {
 +              /*
 +               * Need to request Tunnel PAC when using authenticated
 +               * provisioning.
 +               */
 +              wpa_printf(MSG_DEBUG, "EAP-FAST: Request Tunnel PAC");
 +              tmp = eap_fast_pac_request();
 +              resp = wpabuf_concat(resp, tmp);
 +      }
 +
 +      if (tlv.result == EAP_TLV_RESULT_SUCCESS && !failed) {
 +              tmp = eap_fast_tlv_result(EAP_TLV_RESULT_SUCCESS, 0);
 +              resp = wpabuf_concat(tmp, resp);
 +      } else if (failed) {
 +              tmp = eap_fast_tlv_result(EAP_TLV_RESULT_FAILURE, 0);
 +              resp = wpabuf_concat(tmp, resp);
 +      }
 +
 +      if (resp && tlv.result == EAP_TLV_RESULT_SUCCESS && !failed &&
 +          tlv.crypto_binding && data->phase2_success) {
 +              if (data->anon_provisioning) {
 +                      wpa_printf(MSG_DEBUG, "EAP-FAST: Unauthenticated "
 +                                 "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.");
 +                      if (data->provisioning)
 +                              ret->methodState = METHOD_MAY_CONT;
 +                      else
 +                              ret->methodState = METHOD_DONE;
 +                      ret->decision = DECISION_UNCOND_SUCC;
 +              }
 +      }
 +
 +      if (resp == NULL) {
 +              wpa_printf(MSG_DEBUG, "EAP-FAST: No recognized TLVs - send "
 +                         "empty response packet");
 +              resp = wpabuf_alloc(1);
 +      }
 +
-                           struct eap_method_ret *ret,
-                           const struct eap_hdr *req,
++      return eap_fast_encrypt_response(sm, data, resp, identifier,
 +                                       out_data);
 +}
 +
 +
 +static int eap_fast_decrypt(struct eap_sm *sm, struct eap_fast_data *data,
-                                           req->identifier, NULL, out_data);
++                          struct eap_method_ret *ret, u8 identifier,
 +                          const struct wpabuf *in_data,
 +                          struct wpabuf **out_data)
 +{
 +      struct wpabuf *in_decrypted;
 +      int res;
 +
 +      wpa_printf(MSG_DEBUG, "EAP-FAST: Received %lu bytes encrypted data for"
 +                 " Phase 2", (unsigned long) wpabuf_len(in_data));
 +
 +      if (data->pending_phase2_req) {
 +              wpa_printf(MSG_DEBUG, "EAP-FAST: Pending Phase 2 request - "
 +                         "skip decryption and use old data");
 +              /* Clear TLS reassembly state. */
 +              eap_peer_tls_reset_input(&data->ssl);
 +
 +              in_decrypted = data->pending_phase2_req;
 +              data->pending_phase2_req = NULL;
 +              goto continue_req;
 +      }
 +
 +      if (wpabuf_len(in_data) == 0) {
 +              /* Received TLS ACK - requesting more fragments */
 +              return eap_peer_tls_encrypt(sm, &data->ssl, EAP_TYPE_FAST,
 +                                          data->fast_version,
-       res = eap_fast_process_decrypted(sm, data, ret, req,
++                                          identifier, NULL, out_data);
 +      }
 +
 +      res = eap_peer_tls_decrypt(sm, &data->ssl, in_data, &in_decrypted);
 +      if (res)
 +              return res;
 +
 +continue_req:
 +      wpa_hexdump_buf(MSG_MSGDUMP, "EAP-FAST: Decrypted Phase 2 TLV(s)",
 +                      in_decrypted);
 +
 +      if (wpabuf_len(in_decrypted) < 4) {
 +              wpa_printf(MSG_INFO, "EAP-FAST: Too short Phase 2 "
 +                         "TLV frame (len=%lu)",
 +                         (unsigned long) wpabuf_len(in_decrypted));
 +              wpabuf_free(in_decrypted);
 +              return -1;
 +      }
 +
-       struct pac_tlv_hdr *hdr;
++      res = eap_fast_process_decrypted(sm, data, ret, identifier,
 +                                       in_decrypted, out_data);
 +
 +      wpabuf_free(in_decrypted);
 +
 +      return res;
 +}
 +
 +
 +static const u8 * eap_fast_get_a_id(const u8 *buf, size_t len, size_t *id_len)
 +{
 +      const u8 *a_id;
-               hdr = (struct pac_tlv_hdr *) buf;
++      const struct pac_tlv_hdr *hdr;
 +
 +      /*
 +       * Parse authority identity (A-ID) from the EAP-FAST/Start. This
 +       * supports both raw A-ID and one inside an A-ID TLV.
 +       */
 +      a_id = buf;
 +      *id_len = len;
 +      if (len > sizeof(*hdr)) {
 +              int tlen;
-                       a_id = (u8 *) (hdr + 1);
++              hdr = (const struct pac_tlv_hdr *) buf;
 +              tlen = be_to_host16(hdr->len);
 +              if (be_to_host16(hdr->type) == PAC_TYPE_A_ID &&
 +                  sizeof(*hdr) + tlen <= len) {
 +                      wpa_printf(MSG_DEBUG, "EAP-FAST: A-ID was in TLV "
 +                                 "(Start)");
-               struct wpabuf msg;
-               wpabuf_set(&msg, pos, left);
-               res = eap_fast_decrypt(sm, data, ret, req, &msg, &resp);
++                      a_id = (const u8 *) (hdr + 1);
 +                      *id_len = tlen;
 +              }
 +      }
 +      wpa_hexdump_ascii(MSG_DEBUG, "EAP-FAST: A-ID", a_id, *id_len);
 +
 +      return a_id;
 +}
 +
 +
 +static void eap_fast_select_pac(struct eap_fast_data *data,
 +                              const u8 *a_id, size_t a_id_len)
 +{
 +      data->current_pac = eap_fast_get_pac(data->pac, a_id, a_id_len,
 +                                           PAC_TYPE_TUNNEL_PAC);
 +      if (data->current_pac == NULL) {
 +              /*
 +               * Tunnel PAC was not available for this A-ID. Try to use
 +               * Machine Authentication PAC, if one is available.
 +               */
 +              data->current_pac = eap_fast_get_pac(
 +                      data->pac, a_id, a_id_len,
 +                      PAC_TYPE_MACHINE_AUTHENTICATION);
 +      }
 +
 +      if (data->current_pac) {
 +              wpa_printf(MSG_DEBUG, "EAP-FAST: PAC found for this A-ID "
 +                         "(PAC-Type %d)", data->current_pac->pac_type);
 +              wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-FAST: A-ID-Info",
 +                                data->current_pac->a_id_info,
 +                                data->current_pac->a_id_info_len);
 +      }
 +}
 +
 +
 +static int eap_fast_use_pac_opaque(struct eap_sm *sm,
 +                                 struct eap_fast_data *data,
 +                                 struct eap_fast_pac *pac)
 +{
 +      u8 *tlv;
 +      size_t tlv_len, olen;
 +      struct eap_tlv_hdr *ehdr;
 +
 +      olen = pac->pac_opaque_len;
 +      tlv_len = sizeof(*ehdr) + olen;
 +      tlv = os_malloc(tlv_len);
 +      if (tlv) {
 +              ehdr = (struct eap_tlv_hdr *) tlv;
 +              ehdr->tlv_type = host_to_be16(PAC_TYPE_PAC_OPAQUE);
 +              ehdr->length = host_to_be16(olen);
 +              os_memcpy(ehdr + 1, pac->pac_opaque, olen);
 +      }
 +      if (tlv == NULL ||
 +          tls_connection_client_hello_ext(sm->ssl_ctx, data->ssl.conn,
 +                                          TLS_EXT_PAC_OPAQUE,
 +                                          tlv, tlv_len) < 0) {
 +              wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to add PAC-Opaque TLS "
 +                         "extension");
 +              os_free(tlv);
 +              return -1;
 +      }
 +      os_free(tlv);
 +
 +      return 0;
 +}
 +
 +
 +static int eap_fast_clear_pac_opaque_ext(struct eap_sm *sm,
 +                                       struct eap_fast_data *data)
 +{
 +      if (tls_connection_client_hello_ext(sm->ssl_ctx, data->ssl.conn,
 +                                          TLS_EXT_PAC_OPAQUE, NULL, 0) < 0) {
 +              wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to remove PAC-Opaque "
 +                         "TLS extension");
 +              return -1;
 +      }
 +      return 0;
 +}
 +
 +
 +static int eap_fast_set_provisioning_ciphers(struct eap_sm *sm,
 +                                           struct eap_fast_data *data)
 +{
 +      u8 ciphers[5];
 +      int count = 0;
 +
 +      if (data->provisioning_allowed & EAP_FAST_PROV_UNAUTH) {
 +              wpa_printf(MSG_DEBUG, "EAP-FAST: Enabling unauthenticated "
 +                         "provisioning TLS cipher suites");
 +              ciphers[count++] = TLS_CIPHER_ANON_DH_AES128_SHA;
 +      }
 +
 +      if (data->provisioning_allowed & EAP_FAST_PROV_AUTH) {
 +              wpa_printf(MSG_DEBUG, "EAP-FAST: Enabling authenticated "
 +                         "provisioning TLS cipher suites");
 +              ciphers[count++] = TLS_CIPHER_RSA_DHE_AES128_SHA;
 +              ciphers[count++] = TLS_CIPHER_AES128_SHA;
 +              ciphers[count++] = TLS_CIPHER_RC4_SHA;
 +      }
 +
 +      ciphers[count++] = TLS_CIPHER_NONE;
 +
 +      if (tls_connection_set_cipher_list(sm->ssl_ctx, data->ssl.conn,
 +                                         ciphers)) {
 +              wpa_printf(MSG_INFO, "EAP-FAST: Could not configure TLS "
 +                         "cipher suites for provisioning");
 +              return -1;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +static int eap_fast_process_start(struct eap_sm *sm,
 +                                struct eap_fast_data *data, u8 flags,
 +                                const u8 *pos, size_t left)
 +{
 +      const u8 *a_id;
 +      size_t a_id_len;
 +
 +      /* EAP-FAST Version negotiation (section 3.1) */
 +      wpa_printf(MSG_DEBUG, "EAP-FAST: Start (server ver=%d, own ver=%d)",
 +                 flags & EAP_TLS_VERSION_MASK, data->fast_version);
 +      if ((flags & EAP_TLS_VERSION_MASK) < data->fast_version)
 +              data->fast_version = flags & EAP_TLS_VERSION_MASK;
 +      wpa_printf(MSG_DEBUG, "EAP-FAST: Using FAST version %d",
 +                 data->fast_version);
 +
 +      a_id = eap_fast_get_a_id(pos, left, &a_id_len);
 +      eap_fast_select_pac(data, a_id, a_id_len);
 +
 +      if (data->resuming && data->current_pac) {
 +              wpa_printf(MSG_DEBUG, "EAP-FAST: Trying to resume session - "
 +                         "do not add PAC-Opaque to TLS ClientHello");
 +              if (eap_fast_clear_pac_opaque_ext(sm, data) < 0)
 +                      return -1;
 +      } else if (data->current_pac) {
 +              /*
 +               * PAC found for the A-ID and we are not resuming an old
 +               * session, so add PAC-Opaque extension to ClientHello.
 +               */
 +              if (eap_fast_use_pac_opaque(sm, data, data->current_pac) < 0)
 +                      return -1;
 +      } else {
 +              /* No PAC found, so we must provision one. */
 +              if (!data->provisioning_allowed) {
 +                      wpa_printf(MSG_DEBUG, "EAP-FAST: No PAC found and "
 +                                 "provisioning disabled");
 +                      return -1;
 +              }
 +              wpa_printf(MSG_DEBUG, "EAP-FAST: No PAC found - "
 +                         "starting provisioning");
 +              if (eap_fast_set_provisioning_ciphers(sm, data) < 0 ||
 +                  eap_fast_clear_pac_opaque_ext(sm, data) < 0)
 +                      return -1;
 +              data->provisioning = 1;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +static struct wpabuf * eap_fast_process(struct eap_sm *sm, void *priv,
 +                                      struct eap_method_ret *ret,
 +                                      const struct wpabuf *reqData)
 +{
 +      const struct eap_hdr *req;
 +      size_t left;
 +      int res;
 +      u8 flags, id;
 +      struct wpabuf *resp;
 +      const u8 *pos;
 +      struct eap_fast_data *data = priv;
++      struct wpabuf msg;
 +
 +      pos = eap_peer_tls_process_init(sm, &data->ssl, EAP_TYPE_FAST, ret,
 +                                      reqData, &left, &flags);
 +      if (pos == NULL)
 +              return NULL;
 +
 +      req = wpabuf_head(reqData);
 +      id = req->identifier;
 +
 +      if (flags & EAP_TLS_FLAGS_START) {
 +              if (eap_fast_process_start(sm, data, flags, pos, left) < 0)
 +                      return NULL;
 +
 +              left = 0; /* A-ID is not used in further packet processing */
 +      }
 +
++      wpabuf_set(&msg, pos, left);
++
 +      resp = NULL;
 +      if (tls_connection_established(sm->ssl_ctx, data->ssl.conn) &&
 +          !data->resuming) {
 +              /* Process tunneled (encrypted) phase 2 data. */
-                                                 data->fast_version, id, pos,
-                                                 left, &resp);
++              res = eap_fast_decrypt(sm, data, ret, id, &msg, &resp);
 +              if (res < 0) {
 +                      ret->methodState = METHOD_DONE;
 +                      ret->decision = DECISION_FAIL;
 +                      /*
 +                       * Ack possible Alert that may have caused failure in
 +                       * decryption.
 +                       */
 +                      res = 1;
 +              }
 +      } else {
 +              /* Continue processing TLS handshake (phase 1). */
 +              res = eap_peer_tls_process_helper(sm, &data->ssl,
 +                                                EAP_TYPE_FAST,
-                       eap_fast_derive_keys(sm, data);
++                                                data->fast_version, id, &msg,
++                                                &resp);
++              if (res < 0) {
++                      wpa_printf(MSG_DEBUG,
++                                 "EAP-FAST: TLS processing failed");
++                      ret->methodState = METHOD_DONE;
++                      ret->decision = DECISION_FAIL;
++                      return resp;
++              }
 +
 +              if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) {
 +                      char cipher[80];
 +                      wpa_printf(MSG_DEBUG,
 +                                 "EAP-FAST: TLS done, proceed to Phase 2");
 +                      if (data->provisioning &&
 +                          (!(data->provisioning_allowed &
 +                             EAP_FAST_PROV_AUTH) ||
 +                           tls_get_cipher(sm->ssl_ctx, data->ssl.conn,
 +                                          cipher, sizeof(cipher)) < 0 ||
 +                           os_strstr(cipher, "ADH-") ||
 +                           os_strstr(cipher, "anon"))) {
 +                              wpa_printf(MSG_DEBUG, "EAP-FAST: Using "
 +                                         "anonymous (unauthenticated) "
 +                                         "provisioning");
 +                              data->anon_provisioning = 1;
 +                      } else
 +                              data->anon_provisioning = 0;
 +                      data->resuming = 0;
-                       struct wpabuf msg;
++                      if (eap_fast_derive_keys(sm, data) < 0) {
++                              wpa_printf(MSG_DEBUG,
++                                         "EAP-FAST: Could not derive keys");
++                              ret->methodState = METHOD_DONE;
++                              ret->decision = DECISION_FAIL;
++                              wpabuf_free(resp);
++                              return NULL;
++                      }
 +              }
 +
 +              if (res == 2) {
-                       wpabuf_set(&msg, pos, left);
-                       res = eap_fast_decrypt(sm, data, ret, req, &msg,
-                                              &resp);
 +                      /*
 +                       * Application data included in the handshake message.
 +                       */
 +                      wpabuf_free(data->pending_phase2_req);
 +                      data->pending_phase2_req = resp;
 +                      resp = NULL;
++                      res = eap_fast_decrypt(sm, data, ret, id, &msg, &resp);
 +              }
 +      }
 +
 +      if (res == 1) {
 +              wpabuf_free(resp);
 +              return eap_peer_tls_build_ack(id, EAP_TYPE_FAST,
 +                                            data->fast_version);
 +      }
 +
 +      return resp;
 +}
 +
 +
 +#if 0 /* FIX */
 +static Boolean eap_fast_has_reauth_data(struct eap_sm *sm, void *priv)
 +{
 +      struct eap_fast_data *data = priv;
 +      return tls_connection_established(sm->ssl_ctx, data->ssl.conn);
 +}
 +
 +
 +static void eap_fast_deinit_for_reauth(struct eap_sm *sm, void *priv)
 +{
 +      struct eap_fast_data *data = priv;
 +      os_free(data->key_block_p);
 +      data->key_block_p = NULL;
 +      wpabuf_free(data->pending_phase2_req);
 +      data->pending_phase2_req = NULL;
 +}
 +
 +
 +static void * eap_fast_init_for_reauth(struct eap_sm *sm, void *priv)
 +{
 +      struct eap_fast_data *data = priv;
 +      if (eap_peer_tls_reauth_init(sm, &data->ssl)) {
 +              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 &&
 +          data->phase2_method->init_for_reauth)
 +              data->phase2_method->init_for_reauth(sm, data->phase2_priv);
 +      data->phase2_success = 0;
 +      data->resuming = 1;
 +      data->provisioning = 0;
 +      data->anon_provisioning = 0;
 +      data->simck_idx = 0;
 +      return priv;
 +}
 +#endif
 +
 +
 +static int eap_fast_get_status(struct eap_sm *sm, void *priv, char *buf,
 +                             size_t buflen, int verbose)
 +{
 +      struct eap_fast_data *data = priv;
 +      int len, ret;
 +
 +      len = eap_peer_tls_status(sm, &data->ssl, buf, buflen, verbose);
 +      if (data->phase2_method) {
 +              ret = os_snprintf(buf + len, buflen - len,
 +                                "EAP-FAST Phase2 method=%s\n",
 +                                data->phase2_method->name);
 +              if (os_snprintf_error(buflen - len, ret))
 +                      return len;
 +              len += ret;
 +      }
 +      return len;
 +}
 +
 +
 +static Boolean eap_fast_isKeyAvailable(struct eap_sm *sm, void *priv)
 +{
 +      struct eap_fast_data *data = priv;
 +      return data->success;
 +}
 +
 +
 +static u8 * eap_fast_getKey(struct eap_sm *sm, void *priv, size_t *len)
 +{
 +      struct eap_fast_data *data = priv;
 +      u8 *key;
 +
 +      if (!data->success)
 +              return NULL;
 +
 +      key = os_malloc(EAP_FAST_KEY_LEN);
 +      if (key == NULL)
 +              return NULL;
 +
 +      *len = EAP_FAST_KEY_LEN;
 +      os_memcpy(key, data->key_data, EAP_FAST_KEY_LEN);
 +
 +      return key;
 +}
 +
 +
 +static u8 * eap_fast_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
 +{
 +      struct eap_fast_data *data = priv;
 +      u8 *id;
 +
 +      if (!data->success)
 +              return NULL;
 +
 +      id = os_malloc(data->id_len);
 +      if (id == NULL)
 +              return NULL;
 +
 +      *len = data->id_len;
 +      os_memcpy(id, data->session_id, data->id_len);
 +
 +      return id;
 +}
 +
 +
 +static u8 * eap_fast_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
 +{
 +      struct eap_fast_data *data = priv;
 +      u8 *key;
 +
 +      if (!data->success)
 +              return NULL;
 +
 +      key = os_malloc(EAP_EMSK_LEN);
 +      if (key == NULL)
 +              return NULL;
 +
 +      *len = EAP_EMSK_LEN;
 +      os_memcpy(key, data->emsk, EAP_EMSK_LEN);
 +
 +      return key;
 +}
 +
 +
 +int eap_peer_fast_register(void)
 +{
 +      struct eap_method *eap;
 +      int ret;
 +
 +      eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
 +                                  EAP_VENDOR_IETF, EAP_TYPE_FAST, "FAST");
 +      if (eap == NULL)
 +              return -1;
 +
 +      eap->init = eap_fast_init;
 +      eap->deinit = eap_fast_deinit;
 +      eap->process = eap_fast_process;
 +      eap->isKeyAvailable = eap_fast_isKeyAvailable;
 +      eap->getKey = eap_fast_getKey;
 +      eap->getSessionId = eap_fast_get_session_id;
 +      eap->get_status = eap_fast_get_status;
 +#if 0
 +      eap->has_reauth_data = eap_fast_has_reauth_data;
 +      eap->deinit_for_reauth = eap_fast_deinit_for_reauth;
 +      eap->init_for_reauth = eap_fast_init_for_reauth;
 +#endif
 +      eap->get_emsk = eap_fast_get_emsk;
 +
 +      ret = eap_peer_method_register(eap);
 +      if (ret)
 +              eap_peer_method_free(eap);
 +      return ret;
 +}
index c54bf116477c8a76f058b33c34f2edb5b72f2256,0000000000000000000000000000000000000000..902b4ba26d6e46b6a10019bd8c958e4deb7fc985
mode 100644,000000..100644
--- /dev/null
@@@ -1,790 -1,0 +1,793 @@@
-                                              const struct wpabuf *reqData,
 +/*
 + * EAP peer method: EAP-GPSK (RFC 5433)
 + * 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 "includes.h"
 +
 +#include "common.h"
 +#include "crypto/random.h"
 +#include "eap_peer/eap_i.h"
 +#include "eap_common/eap_gpsk_common.h"
 +
 +struct eap_gpsk_data {
 +      enum { GPSK_1, GPSK_3, SUCCESS, FAILURE } state;
 +      u8 rand_server[EAP_GPSK_RAND_LEN];
 +      u8 rand_peer[EAP_GPSK_RAND_LEN];
 +      u8 msk[EAP_MSK_LEN];
 +      u8 emsk[EAP_EMSK_LEN];
 +      u8 sk[EAP_GPSK_MAX_SK_LEN];
 +      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;
 +      int vendor; /* CSuite/Specifier */
 +      int specifier; /* CSuite/Specifier */
 +      u8 *psk;
 +      size_t psk_len;
 +      u16 forced_cipher; /* force cipher or 0 to allow all supported */
 +};
 +
 +
 +static struct wpabuf * eap_gpsk_send_gpsk_2(struct eap_gpsk_data *data,
 +                                          u8 identifier,
 +                                          const u8 *csuite_list,
 +                                          size_t csuite_list_len);
 +static struct wpabuf * eap_gpsk_send_gpsk_4(struct eap_gpsk_data *data,
 +                                          u8 identifier);
 +
 +
 +#ifndef CONFIG_NO_STDOUT_DEBUG
 +static const char * eap_gpsk_state_txt(int state)
 +{
 +      switch (state) {
 +      case GPSK_1:
 +              return "GPSK-1";
 +      case GPSK_3:
 +              return "GPSK-3";
 +      case SUCCESS:
 +              return "SUCCESS";
 +      case FAILURE:
 +              return "FAILURE";
 +      default:
 +              return "?";
 +      }
 +}
 +#endif /* CONFIG_NO_STDOUT_DEBUG */
 +
 +
 +static void eap_gpsk_state(struct eap_gpsk_data *data, int state)
 +{
 +      wpa_printf(MSG_DEBUG, "EAP-GPSK: %s -> %s",
 +                 eap_gpsk_state_txt(data->state),
 +                 eap_gpsk_state_txt(state));
 +      data->state = state;
 +}
 +
 +
 +static void eap_gpsk_deinit(struct eap_sm *sm, void *priv);
 +
 +
 +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) {
 +              wpa_printf(MSG_INFO, "EAP-GPSK: No key (password) configured");
 +              return NULL;
 +      }
 +
 +      data = os_zalloc(sizeof(*data));
 +      if (data == NULL)
 +              return NULL;
 +      data->state = GPSK_1;
 +
 +      identity = eap_get_config_identity(sm, &identity_len);
 +      if (identity) {
 +              data->id_peer = os_malloc(identity_len);
 +              if (data->id_peer == NULL) {
 +                      eap_gpsk_deinit(sm, data);
 +                      return NULL;
 +              }
 +              os_memcpy(data->id_peer, identity, identity_len);
 +              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);
 +              return NULL;
 +      }
 +      os_memcpy(data->psk, password, password_len);
 +      data->psk_len = password_len;
 +
 +      return data;
 +}
 +
 +
 +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);
 +      if (data->psk) {
 +              os_memset(data->psk, 0, data->psk_len);
 +              os_free(data->psk);
 +      }
 +      bin_clear_free(data, sizeof(*data));
 +}
 +
 +
 +static const u8 * eap_gpsk_process_id_server(struct eap_gpsk_data *data,
 +                                           const u8 *pos, const u8 *end)
 +{
 +      u16 alen;
 +
 +      if (end - pos < 2) {
 +              wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short GPSK-1 packet");
 +              return NULL;
 +      }
 +      alen = WPA_GET_BE16(pos);
 +      pos += 2;
 +      if (end - pos < alen) {
 +              wpa_printf(MSG_DEBUG, "EAP-GPSK: ID_Server overflow");
 +              return NULL;
 +      }
 +      os_free(data->id_server);
 +      data->id_server = os_malloc(alen);
 +      if (data->id_server == NULL) {
 +              wpa_printf(MSG_DEBUG, "EAP-GPSK: No memory for ID_Server");
 +              return NULL;
 +      }
 +      os_memcpy(data->id_server, pos, alen);
 +      data->id_server_len = alen;
 +      wpa_hexdump_ascii(MSG_DEBUG, "EAP-GPSK: ID_Server",
 +                        data->id_server, data->id_server_len);
 +      pos += alen;
 +
 +      return pos;
 +}
 +
 +
 +static const u8 * eap_gpsk_process_rand_server(struct eap_gpsk_data *data,
 +                                             const u8 *pos, const u8 *end)
 +{
 +      if (pos == NULL)
 +              return NULL;
 +
 +      if (end - pos < EAP_GPSK_RAND_LEN) {
 +              wpa_printf(MSG_DEBUG, "EAP-GPSK: RAND_Server overflow");
 +              return NULL;
 +      }
 +      os_memcpy(data->rand_server, pos, EAP_GPSK_RAND_LEN);
 +      wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Server",
 +                  data->rand_server, EAP_GPSK_RAND_LEN);
 +      pos += EAP_GPSK_RAND_LEN;
 +
 +      return pos;
 +}
 +
 +
 +static int eap_gpsk_select_csuite(struct eap_sm *sm,
 +                                struct eap_gpsk_data *data,
 +                                const u8 *csuite_list,
 +                                size_t csuite_list_len)
 +{
 +      struct eap_gpsk_csuite *csuite;
 +      int i, count;
 +
 +      count = csuite_list_len / sizeof(struct eap_gpsk_csuite);
 +      data->vendor = EAP_GPSK_VENDOR_IETF;
 +      data->specifier = EAP_GPSK_CIPHER_RESERVED;
 +      csuite = (struct eap_gpsk_csuite *) csuite_list;
 +      for (i = 0; i < count; i++) {
 +              int vendor, specifier;
 +              vendor = WPA_GET_BE32(csuite->vendor);
 +              specifier = WPA_GET_BE16(csuite->specifier);
 +              wpa_printf(MSG_DEBUG, "EAP-GPSK: CSuite[%d]: %d:%d",
 +                         i, vendor, specifier);
 +              if (data->vendor == EAP_GPSK_VENDOR_IETF &&
 +                  data->specifier == EAP_GPSK_CIPHER_RESERVED &&
 +                  eap_gpsk_supported_ciphersuite(vendor, specifier) &&
 +                  (!data->forced_cipher || data->forced_cipher == specifier))
 +              {
 +                      data->vendor = vendor;
 +                      data->specifier = specifier;
 +              }
 +              csuite++;
 +      }
 +      if (data->vendor == EAP_GPSK_VENDOR_IETF &&
 +          data->specifier == EAP_GPSK_CIPHER_RESERVED) {
 +              wpa_msg(sm->msg_ctx, MSG_INFO, "EAP-GPSK: No supported "
 +                      "ciphersuite found");
 +              return -1;
 +      }
 +      wpa_printf(MSG_DEBUG, "EAP-GPSK: Selected ciphersuite %d:%d",
 +                 data->vendor, data->specifier);
 +
 +      return 0;
 +}
 +
 +
 +static const u8 * eap_gpsk_process_csuite_list(struct eap_sm *sm,
 +                                             struct eap_gpsk_data *data,
 +                                             const u8 **list,
 +                                             size_t *list_len,
 +                                             const u8 *pos, const u8 *end)
 +{
 +      size_t len;
 +
 +      if (pos == NULL)
 +              return NULL;
 +
 +      if (end - pos < 2) {
 +              wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short GPSK-1 packet");
 +              return NULL;
 +      }
 +      len = WPA_GET_BE16(pos);
 +      pos += 2;
 +      if (len > (size_t) (end - pos)) {
 +              wpa_printf(MSG_DEBUG, "EAP-GPSK: CSuite_List overflow");
 +              return NULL;
 +      }
 +      if (len == 0 || (len % sizeof(struct eap_gpsk_csuite))) {
 +              wpa_printf(MSG_DEBUG, "EAP-GPSK: Invalid CSuite_List len %lu",
 +                         (unsigned long) len);
 +              return NULL;
 +      }
 +
 +      if (eap_gpsk_select_csuite(sm, data, pos, len) < 0)
 +              return NULL;
 +
 +      *list = pos;
 +      *list_len = len;
 +      pos += len;
 +
 +      return pos;
 +}
 +
 +
 +static struct wpabuf * eap_gpsk_process_gpsk_1(struct eap_sm *sm,
 +                                             struct eap_gpsk_data *data,
 +                                             struct eap_method_ret *ret,
-       resp = eap_gpsk_send_gpsk_2(data, eap_get_id(reqData),
++                                             u8 identifier,
 +                                             const u8 *payload,
 +                                             size_t payload_len)
 +{
 +      size_t csuite_list_len;
 +      const u8 *csuite_list, *pos, *end;
 +      struct wpabuf *resp;
 +
 +      if (data->state != GPSK_1) {
 +              ret->ignore = TRUE;
 +              return NULL;
 +      }
 +
 +      wpa_printf(MSG_DEBUG, "EAP-GPSK: Received Request/GPSK-1");
 +
 +      end = payload + payload_len;
 +
 +      pos = eap_gpsk_process_id_server(data, payload, end);
 +      pos = eap_gpsk_process_rand_server(data, pos, end);
 +      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;
 +      }
 +
-                                              const struct wpabuf *reqData,
++      resp = eap_gpsk_send_gpsk_2(data, identifier,
 +                                  csuite_list, csuite_list_len);
 +      if (resp == NULL)
 +              return NULL;
 +
 +      eap_gpsk_state(data, GPSK_3);
 +
 +      return resp;
 +}
 +
 +
 +static struct wpabuf * eap_gpsk_send_gpsk_2(struct eap_gpsk_data *data,
 +                                          u8 identifier,
 +                                          const u8 *csuite_list,
 +                                          size_t csuite_list_len)
 +{
 +      struct wpabuf *resp;
 +      size_t len, miclen;
 +      u8 *rpos, *start;
 +      struct eap_gpsk_csuite *csuite;
 +
 +      wpa_printf(MSG_DEBUG, "EAP-GPSK: Sending Response/GPSK-2");
 +
 +      miclen = eap_gpsk_mic_len(data->vendor, data->specifier);
 +      len = 1 + 2 + data->id_peer_len + 2 + data->id_server_len +
 +              2 * EAP_GPSK_RAND_LEN + 2 + csuite_list_len +
 +              sizeof(struct eap_gpsk_csuite) + 2 + miclen;
 +
 +      resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_GPSK, len,
 +                           EAP_CODE_RESPONSE, identifier);
 +      if (resp == NULL)
 +              return NULL;
 +
 +      wpabuf_put_u8(resp, EAP_GPSK_OPCODE_GPSK_2);
 +      start = wpabuf_put(resp, 0);
 +
 +      wpa_hexdump_ascii(MSG_DEBUG, "EAP-GPSK: ID_Peer",
 +                        data->id_peer, data->id_peer_len);
 +      wpabuf_put_be16(resp, data->id_peer_len);
 +      wpabuf_put_data(resp, data->id_peer, data->id_peer_len);
 +
 +      wpabuf_put_be16(resp, data->id_server_len);
 +      wpabuf_put_data(resp, data->id_server, data->id_server_len);
 +
 +      if (random_get_bytes(data->rand_peer, EAP_GPSK_RAND_LEN)) {
 +              wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to get random data "
 +                         "for RAND_Peer");
 +              eap_gpsk_state(data, FAILURE);
 +              wpabuf_free(resp);
 +              return NULL;
 +      }
 +      wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Peer",
 +                  data->rand_peer, EAP_GPSK_RAND_LEN);
 +      wpabuf_put_data(resp, data->rand_peer, EAP_GPSK_RAND_LEN);
 +      wpabuf_put_data(resp, data->rand_server, EAP_GPSK_RAND_LEN);
 +
 +      wpabuf_put_be16(resp, csuite_list_len);
 +      wpabuf_put_data(resp, csuite_list, csuite_list_len);
 +
 +      csuite = wpabuf_put(resp, sizeof(*csuite));
 +      WPA_PUT_BE32(csuite->vendor, data->vendor);
 +      WPA_PUT_BE16(csuite->specifier, data->specifier);
 +
 +      if (eap_gpsk_derive_keys(data->psk, data->psk_len,
 +                               data->vendor, data->specifier,
 +                               data->rand_peer, data->rand_server,
 +                               data->id_peer, data->id_peer_len,
 +                               data->id_server, data->id_server_len,
 +                               data->msk, data->emsk,
 +                               data->sk, &data->sk_len,
 +                               data->pk, &data->pk_len) < 0) {
 +              wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to derive keys");
 +              eap_gpsk_state(data, FAILURE);
 +              wpabuf_free(resp);
 +              return NULL;
 +      }
 +
 +      if (eap_gpsk_derive_session_id(data->psk, data->psk_len,
 +                                     data->vendor, data->specifier,
 +                                     data->rand_peer, data->rand_server,
 +                                     data->id_peer, data->id_peer_len,
 +                                     data->id_server, data->id_server_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);
 +              wpabuf_free(resp);
 +              return NULL;
 +      }
 +      wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Derived Session-Id",
 +                  data->session_id, data->id_len);
 +
 +      /* No PD_Payload_1 */
 +      wpabuf_put_be16(resp, 0);
 +
 +      rpos = wpabuf_put(resp, miclen);
 +      if (eap_gpsk_compute_mic(data->sk, data->sk_len, data->vendor,
 +                               data->specifier, start, rpos - start, rpos) <
 +          0) {
 +              eap_gpsk_state(data, FAILURE);
 +              wpabuf_free(resp);
 +              return NULL;
 +      }
 +
 +      return resp;
 +}
 +
 +
 +static const u8 * eap_gpsk_validate_rand(struct eap_gpsk_data *data,
 +                                       const u8 *pos, const u8 *end)
 +{
 +      if (end - pos < EAP_GPSK_RAND_LEN) {
 +              wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for "
 +                         "RAND_Peer");
 +              return NULL;
 +      }
 +      if (os_memcmp(pos, data->rand_peer, EAP_GPSK_RAND_LEN) != 0) {
 +              wpa_printf(MSG_DEBUG, "EAP-GPSK: RAND_Peer in GPSK-2 and "
 +                         "GPSK-3 did not match");
 +              wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Peer in GPSK-2",
 +                          data->rand_peer, EAP_GPSK_RAND_LEN);
 +              wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Peer in GPSK-3",
 +                          pos, EAP_GPSK_RAND_LEN);
 +              return NULL;
 +      }
 +      pos += EAP_GPSK_RAND_LEN;
 +
 +      if (end - pos < EAP_GPSK_RAND_LEN) {
 +              wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for "
 +                         "RAND_Server");
 +              return NULL;
 +      }
 +      if (os_memcmp(pos, data->rand_server, EAP_GPSK_RAND_LEN) != 0) {
 +              wpa_printf(MSG_DEBUG, "EAP-GPSK: RAND_Server in GPSK-1 and "
 +                         "GPSK-3 did not match");
 +              wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Server in GPSK-1",
 +                          data->rand_server, EAP_GPSK_RAND_LEN);
 +              wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Server in GPSK-3",
 +                          pos, EAP_GPSK_RAND_LEN);
 +              return NULL;
 +      }
 +      pos += EAP_GPSK_RAND_LEN;
 +
 +      return pos;
 +}
 +
 +
 +static const u8 * eap_gpsk_validate_id_server(struct eap_gpsk_data *data,
 +                                            const u8 *pos, const u8 *end)
 +{
 +      size_t len;
 +
 +      if (pos == NULL)
 +              return NULL;
 +
 +      if (end - pos < (int) 2) {
 +              wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for "
 +                         "length(ID_Server)");
 +              return NULL;
 +      }
 +
 +      len = WPA_GET_BE16(pos);
 +      pos += 2;
 +
 +      if (end - pos < (int) len) {
 +              wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for "
 +                         "ID_Server");
 +              return NULL;
 +      }
 +
 +      if (len != data->id_server_len ||
 +          os_memcmp(pos, data->id_server, len) != 0) {
 +              wpa_printf(MSG_INFO, "EAP-GPSK: ID_Server did not match with "
 +                         "the one used in GPSK-1");
 +              wpa_hexdump_ascii(MSG_DEBUG, "EAP-GPSK: ID_Server in GPSK-1",
 +                                data->id_server, data->id_server_len);
 +              wpa_hexdump_ascii(MSG_DEBUG, "EAP-GPSK: ID_Server in GPSK-3",
 +                                pos, len);
 +              return NULL;
 +      }
 +
 +      pos += len;
 +
 +      return pos;
 +}
 +
 +
 +static const u8 * eap_gpsk_validate_csuite(struct eap_gpsk_data *data,
 +                                         const u8 *pos, const u8 *end)
 +{
 +      int vendor, specifier;
 +      const struct eap_gpsk_csuite *csuite;
 +
 +      if (pos == NULL)
 +              return NULL;
 +
 +      if (end - pos < (int) sizeof(*csuite)) {
 +              wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for "
 +                         "CSuite_Sel");
 +              return NULL;
 +      }
 +      csuite = (const struct eap_gpsk_csuite *) pos;
 +      vendor = WPA_GET_BE32(csuite->vendor);
 +      specifier = WPA_GET_BE16(csuite->specifier);
 +      pos += sizeof(*csuite);
 +      if (vendor != data->vendor || specifier != data->specifier) {
 +              wpa_printf(MSG_DEBUG, "EAP-GPSK: CSuite_Sel (%d:%d) does not "
 +                         "match with the one sent in GPSK-2 (%d:%d)",
 +                         vendor, specifier, data->vendor, data->specifier);
 +              return NULL;
 +      }
 +
 +      return pos;
 +}
 +
 +
 +static const u8 * eap_gpsk_validate_pd_payload_2(struct eap_gpsk_data *data,
 +                                               const u8 *pos, const u8 *end)
 +{
 +      u16 alen;
 +
 +      if (pos == NULL)
 +              return NULL;
 +
 +      if (end - pos < 2) {
 +              wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for "
 +                         "PD_Payload_2 length");
 +              return NULL;
 +      }
 +      alen = WPA_GET_BE16(pos);
 +      pos += 2;
 +      if (end - pos < alen) {
 +              wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for "
 +                         "%d-octet PD_Payload_2", alen);
 +              return NULL;
 +      }
 +      wpa_hexdump(MSG_DEBUG, "EAP-GPSK: PD_Payload_2", pos, alen);
 +      pos += alen;
 +
 +      return pos;
 +}
 +
 +
 +static const u8 * eap_gpsk_validate_gpsk_3_mic(struct eap_gpsk_data *data,
 +                                             const u8 *payload,
 +                                             const u8 *pos, const u8 *end)
 +{
 +      size_t miclen;
 +      u8 mic[EAP_GPSK_MAX_MIC_LEN];
 +
 +      if (pos == NULL)
 +              return NULL;
 +
 +      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 "
 +                         "(left=%lu miclen=%lu)",
 +                         (unsigned long) (end - pos),
 +                         (unsigned long) miclen);
 +              return NULL;
 +      }
 +      if (eap_gpsk_compute_mic(data->sk, data->sk_len, data->vendor,
 +                               data->specifier, payload, pos - payload, mic)
 +          < 0) {
 +              wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to compute MIC");
 +              return NULL;
 +      }
 +      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);
 +              return NULL;
 +      }
 +      pos += miclen;
 +
 +      return pos;
 +}
 +
 +
 +static struct wpabuf * eap_gpsk_process_gpsk_3(struct eap_sm *sm,
 +                                             struct eap_gpsk_data *data,
 +                                             struct eap_method_ret *ret,
-       resp = eap_gpsk_send_gpsk_4(data, eap_get_id(reqData));
++                                             u8 identifier,
 +                                             const u8 *payload,
 +                                             size_t payload_len)
 +{
 +      struct wpabuf *resp;
 +      const u8 *pos, *end;
 +
 +      if (data->state != GPSK_3) {
 +              ret->ignore = TRUE;
 +              return NULL;
 +      }
 +
 +      wpa_printf(MSG_DEBUG, "EAP-GPSK: Received Request/GPSK-3");
 +
 +      end = payload + payload_len;
 +
 +      pos = eap_gpsk_validate_rand(data, payload, end);
 +      pos = eap_gpsk_validate_id_server(data, pos, end);
 +      pos = eap_gpsk_validate_csuite(data, pos, end);
 +      pos = eap_gpsk_validate_pd_payload_2(data, pos, end);
 +      pos = eap_gpsk_validate_gpsk_3_mic(data, payload, pos, end);
 +
 +      if (pos == NULL) {
 +              eap_gpsk_state(data, FAILURE);
 +              return NULL;
 +      }
 +      if (pos != end) {
 +              wpa_printf(MSG_DEBUG, "EAP-GPSK: Ignored %lu bytes of extra "
 +                         "data in the end of GPSK-2",
 +                         (unsigned long) (end - pos));
 +      }
 +
-       wpa_printf(MSG_DEBUG, "EAP-GPSK: Received frame: opcode %d", *pos);
++      resp = eap_gpsk_send_gpsk_4(data, identifier);
 +      if (resp == NULL)
 +              return NULL;
 +
 +      eap_gpsk_state(data, SUCCESS);
 +      ret->methodState = METHOD_DONE;
 +      ret->decision = DECISION_UNCOND_SUCC;
 +
 +      return resp;
 +}
 +
 +
 +static struct wpabuf * eap_gpsk_send_gpsk_4(struct eap_gpsk_data *data,
 +                                          u8 identifier)
 +{
 +      struct wpabuf *resp;
 +      u8 *rpos, *start;
 +      size_t mlen;
 +
 +      wpa_printf(MSG_DEBUG, "EAP-GPSK: Sending Response/GPSK-4");
 +
 +      mlen = eap_gpsk_mic_len(data->vendor, data->specifier);
 +
 +      resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_GPSK, 1 + 2 + mlen,
 +                           EAP_CODE_RESPONSE, identifier);
 +      if (resp == NULL)
 +              return NULL;
 +
 +      wpabuf_put_u8(resp, EAP_GPSK_OPCODE_GPSK_4);
 +      start = wpabuf_put(resp, 0);
 +
 +      /* No PD_Payload_3 */
 +      wpabuf_put_be16(resp, 0);
 +
 +      rpos = wpabuf_put(resp, mlen);
 +      if (eap_gpsk_compute_mic(data->sk, data->sk_len, data->vendor,
 +                               data->specifier, start, rpos - start, rpos) <
 +          0) {
 +              eap_gpsk_state(data, FAILURE);
 +              wpabuf_free(resp);
 +              return NULL;
 +      }
 +
 +      return resp;
 +}
 +
 +
 +static struct wpabuf * eap_gpsk_process(struct eap_sm *sm, void *priv,
 +                                      struct eap_method_ret *ret,
 +                                      const struct wpabuf *reqData)
 +{
 +      struct eap_gpsk_data *data = priv;
 +      struct wpabuf *resp;
 +      const u8 *pos;
 +      size_t len;
++      u8 opcode, id;
 +
 +      pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_GPSK, reqData, &len);
 +      if (pos == NULL || len < 1) {
 +              ret->ignore = TRUE;
 +              return NULL;
 +      }
 +
-       switch (*pos) {
++      id = eap_get_id(reqData);
++      opcode = *pos++;
++      len--;
++      wpa_printf(MSG_DEBUG, "EAP-GPSK: Received frame: opcode %d", opcode);
 +
 +      ret->ignore = FALSE;
 +      ret->methodState = METHOD_MAY_CONT;
 +      ret->decision = DECISION_FAIL;
 +      ret->allowNotifications = FALSE;
 +
-               resp = eap_gpsk_process_gpsk_1(sm, data, ret, reqData,
-                                              pos + 1, len - 1);
++      switch (opcode) {
 +      case EAP_GPSK_OPCODE_GPSK_1:
-               resp = eap_gpsk_process_gpsk_3(sm, data, ret, reqData,
-                                              pos + 1, len - 1);
++              resp = eap_gpsk_process_gpsk_1(sm, data, ret, id, pos, len);
 +              break;
 +      case EAP_GPSK_OPCODE_GPSK_3:
-               wpa_printf(MSG_DEBUG, "EAP-GPSK: Ignoring message with "
-                          "unknown opcode %d", *pos);
++              resp = eap_gpsk_process_gpsk_3(sm, data, ret, id, pos, len);
 +              break;
 +      default:
++              wpa_printf(MSG_DEBUG,
++                         "EAP-GPSK: Ignoring message with unknown opcode %d",
++                         opcode);
 +              ret->ignore = TRUE;
 +              return NULL;
 +      }
 +
 +      return resp;
 +}
 +
 +
 +static Boolean eap_gpsk_isKeyAvailable(struct eap_sm *sm, void *priv)
 +{
 +      struct eap_gpsk_data *data = priv;
 +      return data->state == SUCCESS;
 +}
 +
 +
 +static u8 * eap_gpsk_getKey(struct eap_sm *sm, void *priv, size_t *len)
 +{
 +      struct eap_gpsk_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_gpsk_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
 +{
 +      struct eap_gpsk_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 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_peer_gpsk_register(void)
 +{
 +      struct eap_method *eap;
 +      int ret;
 +
 +      eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
 +                                  EAP_VENDOR_IETF, EAP_TYPE_GPSK, "GPSK");
 +      if (eap == NULL)
 +              return -1;
 +
 +      eap->init = eap_gpsk_init;
 +      eap->deinit = eap_gpsk_deinit;
 +      eap->process = eap_gpsk_process;
 +      eap->isKeyAvailable = eap_gpsk_isKeyAvailable;
 +      eap->getKey = eap_gpsk_getKey;
 +      eap->get_emsk = eap_gpsk_get_emsk;
 +      eap->getSessionId = eap_gpsk_get_session_id;
 +
 +      ret = eap_peer_method_register(eap);
 +      if (ret)
 +              eap_peer_method_free(eap);
 +      return ret;
 +}
index 2d7fdea227747ed135648de0fcfcff86314eb698,0000000000000000000000000000000000000000..99b44dae4e34ea9a2e41c0a968dc8972d5d31dc3
mode 100644,000000..100644
--- /dev/null
@@@ -1,389 -1,0 +1,389 @@@
-       struct eapol_callbacks *eapol_cb;
 +/*
 + * EAP peer state machines internal structures (RFC 4137)
 + * 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.
 + */
 +
 +#ifndef EAP_I_H
 +#define EAP_I_H
 +
 +#include "wpabuf.h"
 +#include "utils/list.h"
 +#include "eap_peer/eap.h"
 +#include "eap_common/eap_common.h"
 +
 +/* RFC 4137 - EAP Peer state machine */
 +
 +typedef enum {
 +      DECISION_FAIL, DECISION_COND_SUCC, DECISION_UNCOND_SUCC
 +} EapDecision;
 +
 +typedef enum {
 +      METHOD_NONE, METHOD_INIT, METHOD_CONT, METHOD_MAY_CONT, METHOD_DONE
 +} EapMethodState;
 +
 +/**
 + * struct eap_method_ret - EAP return values from struct eap_method::process()
 + *
 + * These structure contains OUT variables for the interface between peer state
 + * machine and methods (RFC 4137, Sect. 4.2). eapRespData will be returned as
 + * the return value of struct eap_method::process() so it is not included in
 + * this structure.
 + */
 +struct eap_method_ret {
 +      /**
 +       * ignore - Whether method decided to drop the current packed (OUT)
 +       */
 +      Boolean ignore;
 +
 +      /**
 +       * methodState - Method-specific state (IN/OUT)
 +       */
 +      EapMethodState methodState;
 +
 +      /**
 +       * decision - Authentication decision (OUT)
 +       */
 +      EapDecision decision;
 +
 +      /**
 +       * allowNotifications - Whether method allows notifications (OUT)
 +       */
 +      Boolean allowNotifications;
 +};
 +
 +
 +/**
 + * struct eap_method - EAP method interface
 + * This structure defines the EAP method interface. Each method will need to
 + * register its own EAP type, EAP name, and set of function pointers for method
 + * specific operations. This interface is based on section 4.4 of RFC 4137.
 + */
 +struct eap_method {
 +      /**
 +       * vendor - EAP Vendor-ID (EAP_VENDOR_*) (0 = IETF)
 +       */
 +      int vendor;
 +
 +      /**
 +       * method - EAP type number (EAP_TYPE_*)
 +       */
 +      EapType method;
 +
 +      /**
 +       * name - Name of the method (e.g., "TLS")
 +       */
 +      const char *name;
 +
 +      /**
 +       * init - Initialize an EAP method
 +       * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
 +       * Returns: Pointer to allocated private data, or %NULL on failure
 +       *
 +       * This function is used to initialize the EAP method explicitly
 +       * instead of using METHOD_INIT state as specific in RFC 4137. The
 +       * method is expected to initialize it method-specific state and return
 +       * a pointer that will be used as the priv argument to other calls.
 +       */
 +      void * (*init)(struct eap_sm *sm);
 +
 +      /**
 +       * deinit - Deinitialize an EAP method
 +       * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
 +       * @priv: Pointer to private EAP method data from eap_method::init()
 +       *
 +       * Deinitialize the EAP method and free any allocated private data.
 +       */
 +      void (*deinit)(struct eap_sm *sm, void *priv);
 +
 +      /**
 +       * process - Process an EAP request
 +       * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
 +       * @priv: Pointer to private EAP method data from eap_method::init()
 +       * @ret: Return values from EAP request validation and processing
 +       * @reqData: EAP request to be processed (eapReqData)
 +       * Returns: Pointer to allocated EAP response packet (eapRespData)
 +       *
 +       * This function is a combination of m.check(), m.process(), and
 +       * m.buildResp() procedures defined in section 4.4 of RFC 4137 In other
 +       * words, this function validates the incoming request, processes it,
 +       * and build a response packet. m.check() and m.process() return values
 +       * are returned through struct eap_method_ret *ret variable. Caller is
 +       * responsible for freeing the returned EAP response packet.
 +       */
 +      struct wpabuf * (*process)(struct eap_sm *sm, void *priv,
 +                                 struct eap_method_ret *ret,
 +                                 const struct wpabuf *reqData);
 +
 +      /**
 +       * isKeyAvailable - Find out whether EAP method has keying material
 +       * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
 +       * @priv: Pointer to private EAP method data from eap_method::init()
 +       * Returns: %TRUE if key material (eapKeyData) is available
 +       */
 +      Boolean (*isKeyAvailable)(struct eap_sm *sm, void *priv);
 +
 +      /**
 +       * getKey - Get EAP method specific keying material (eapKeyData)
 +       * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
 +       * @priv: Pointer to private EAP method data from eap_method::init()
 +       * @len: Pointer to variable to store key length (eapKeyDataLen)
 +       * Returns: Keying material (eapKeyData) or %NULL if not available
 +       *
 +       * This function can be used to get the keying material from the EAP
 +       * method. The key may already be stored in the method-specific private
 +       * data or this function may derive the key.
 +       */
 +      u8 * (*getKey)(struct eap_sm *sm, void *priv, size_t *len);
 +
 +      /**
 +       * get_status - Get EAP method status
 +       * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
 +       * @priv: Pointer to private EAP method data from eap_method::init()
 +       * @buf: Buffer for status information
 +       * @buflen: Maximum buffer length
 +       * @verbose: Whether to include verbose status information
 +       * Returns: Number of bytes written to buf
 +       *
 +       * Query EAP method for status information. This function fills in a
 +       * text area with current status information from the EAP method. If
 +       * the buffer (buf) is not large enough, status information will be
 +       * truncated to fit the buffer.
 +       */
 +      int (*get_status)(struct eap_sm *sm, void *priv, char *buf,
 +                        size_t buflen, int verbose);
 +
 +      /**
 +       * has_reauth_data - Whether method is ready for fast reauthentication
 +       * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
 +       * @priv: Pointer to private EAP method data from eap_method::init()
 +       * Returns: %TRUE or %FALSE based on whether fast reauthentication is
 +       * possible
 +       *
 +       * This function is an optional handler that only EAP methods
 +       * supporting fast re-authentication need to implement.
 +       */
 +      Boolean (*has_reauth_data)(struct eap_sm *sm, void *priv);
 +
 +      /**
 +       * deinit_for_reauth - Release data that is not needed for fast re-auth
 +       * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
 +       * @priv: Pointer to private EAP method data from eap_method::init()
 +       *
 +       * This function is an optional handler that only EAP methods
 +       * supporting fast re-authentication need to implement. This is called
 +       * when authentication has been completed and EAP state machine is
 +       * requesting that enough state information is maintained for fast
 +       * re-authentication
 +       */
 +      void (*deinit_for_reauth)(struct eap_sm *sm, void *priv);
 +
 +      /**
 +       * init_for_reauth - Prepare for start of fast re-authentication
 +       * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
 +       * @priv: Pointer to private EAP method data from eap_method::init()
 +       *
 +       * This function is an optional handler that only EAP methods
 +       * supporting fast re-authentication need to implement. This is called
 +       * when EAP authentication is started and EAP state machine is
 +       * requesting fast re-authentication to be used.
 +       */
 +      void * (*init_for_reauth)(struct eap_sm *sm, void *priv);
 +
 +      /**
 +       * get_identity - Get method specific identity for re-authentication
 +       * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
 +       * @priv: Pointer to private EAP method data from eap_method::init()
 +       * @len: Length of the returned identity
 +       * Returns: Pointer to the method specific identity or %NULL if default
 +       * identity is to be used
 +       *
 +       * This function is an optional handler that only EAP methods
 +       * that use method specific identity need to implement.
 +       */
 +      const u8 * (*get_identity)(struct eap_sm *sm, void *priv, size_t *len);
 +
 +      /**
 +       * free - Free EAP method data
 +       * @method: Pointer to the method data registered with
 +       * eap_peer_method_register().
 +       *
 +       * This function will be called when the EAP method is being
 +       * unregistered. If the EAP method allocated resources during
 +       * registration (e.g., allocated struct eap_method), they should be
 +       * freed in this function. No other method functions will be called
 +       * after this call. If this function is not defined (i.e., function
 +       * pointer is %NULL), a default handler is used to release the method
 +       * data with free(method). This is suitable for most cases.
 +       */
 +      void (*free)(struct eap_method *method);
 +
 +#define EAP_PEER_METHOD_INTERFACE_VERSION 1
 +      /**
 +       * version - Version of the EAP peer method interface
 +       *
 +       * The EAP peer method implementation should set this variable to
 +       * EAP_PEER_METHOD_INTERFACE_VERSION. This is used to verify that the
 +       * EAP method is using supported API version when using dynamically
 +       * loadable EAP methods.
 +       */
 +      int version;
 +
 +      /**
 +       * next - Pointer to the next EAP method
 +       *
 +       * This variable is used internally in the EAP method registration code
 +       * to create a linked list of registered EAP methods.
 +       */
 +      struct eap_method *next;
 +
 +#ifdef CONFIG_DYNAMIC_EAP_METHODS
 +      /**
 +       * dl_handle - Handle for the dynamic library
 +       *
 +       * This variable is used internally in the EAP method registration code
 +       * to store a handle for the dynamic library. If the method is linked
 +       * in statically, this is %NULL.
 +       */
 +      void *dl_handle;
 +#endif /* CONFIG_DYNAMIC_EAP_METHODS */
 +
 +      /**
 +       * get_emsk - Get EAP method specific keying extended material (EMSK)
 +       * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
 +       * @priv: Pointer to private EAP method data from eap_method::init()
 +       * @len: Pointer to a variable to store EMSK length
 +       * Returns: EMSK or %NULL if not available
 +       *
 +       * This function can be used to get the extended keying material from
 +       * the EAP method. The key may already be stored in the method-specific
 +       * 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_peer_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);
 +};
 +
 +
 +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
 + */
 +struct eap_sm {
 +      enum {
 +              EAP_INITIALIZE, EAP_DISABLED, EAP_IDLE, EAP_RECEIVED,
 +              EAP_GET_METHOD, EAP_METHOD, EAP_SEND_RESPONSE, EAP_DISCARD,
 +              EAP_IDENTITY, EAP_NOTIFICATION, EAP_RETRANSMIT, EAP_SUCCESS,
 +              EAP_FAILURE
 +      } EAP_state;
 +      /* Long-term local variables */
 +      EapType selectedMethod;
 +      EapMethodState methodState;
 +      int lastId;
 +      struct wpabuf *lastRespData;
 +      EapDecision decision;
 +      /* Short-term local variables */
 +      Boolean rxReq;
 +      Boolean rxSuccess;
 +      Boolean rxFailure;
 +      int reqId;
 +      EapType reqMethod;
 +      int reqVendor;
 +      u32 reqVendorMethod;
 +      Boolean ignore;
 +      /* Constants */
 +      int ClientTimeout;
 +
 +      /* Miscellaneous variables */
 +      Boolean allowNotifications; /* peer state machine <-> methods */
 +      struct wpabuf *eapRespData; /* peer to lower layer */
 +      Boolean eapKeyAvailable; /* peer to lower layer */
 +      u8 *eapKeyData; /* peer to lower layer */
 +      size_t eapKeyDataLen; /* peer to lower layer */
 +      u8 *eapSessionId; /* peer to lower layer */
 +      size_t eapSessionIdLen; /* peer to lower layer */
 +      const struct eap_method *m; /* selected EAP method */
 +      /* not defined in RFC 4137 */
 +      Boolean changed;
 +      void *eapol_ctx;
-       u8 req_md5[16]; /* MD5() of the current EAP packet */
-       u8 last_md5[16]; /* MD5() of the previously received EAP packet; used
-                         * in duplicate request detection. */
++      const struct eapol_callbacks *eapol_cb;
 +      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;
 +      Boolean peap_done;
++      u8 req_sha1[20]; /* SHA1() of the current EAP packet */
++      u8 last_sha1[20]; /* SHA1() of the previously received EAP packet; used
++                         * in duplicate request detection. */
 +
 +      void *msg_ctx;
 +      void *scard_ctx;
 +      void *ssl_ctx;
 +      void *ssl_ctx2;
 +
 +      unsigned int workaround;
 +
 +      /* Optional challenges generated in Phase 1 (EAP-FAST) */
 +      u8 *peer_challenge, *auth_challenge;
 +
 +      int num_rounds;
 +      int force_disabled;
 +
 +      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);
 +const u8 * eap_get_config_password(struct eap_sm *sm, size_t *len);
 +const u8 * eap_get_config_password2(struct eap_sm *sm, size_t *len, int *hash);
 +const u8 * eap_get_config_new_password(struct eap_sm *sm, size_t *len);
 +const u8 * eap_get_config_otp(struct eap_sm *sm, size_t *len);
 +void eap_clear_config_otp(struct eap_sm *sm);
 +const char * eap_get_config_phase1(struct eap_sm *sm);
 +const char * eap_get_config_phase2(struct eap_sm *sm);
 +int eap_get_config_fragment_size(struct eap_sm *sm);
 +struct eap_peer_config * eap_get_config(struct eap_sm *sm);
 +void eap_set_config_blob(struct eap_sm *sm, struct wpa_config_blob *blob);
 +const struct wpa_config_blob *
 +eap_get_config_blob(struct eap_sm *sm, const char *name);
 +void eap_notify_pending(struct eap_sm *sm);
 +int eap_allowed_method(struct eap_sm *sm, int vendor, u32 method);
 +
 +#endif /* EAP_I_H */
index 9e486e7d18facf0069eedbd7bf644d0f243a7452,0000000000000000000000000000000000000000..6acf1e8ad390a59ad976b874a169941a7d5384a0
mode 100644,000000..100644
--- /dev/null
@@@ -1,895 -1,0 +1,901 @@@
 +/*
 + * EAP peer method: EAP-MSCHAPV2 (draft-kamath-pppext-eap-mschapv2-00.txt)
 + * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
 + *
 + * This software may be distributed under the terms of the BSD license.
 + * See README for more details.
 + *
 + * This file implements EAP peer part of EAP-MSCHAPV2 method (EAP type 26).
 + * draft-kamath-pppext-eap-mschapv2-00.txt defines the Microsoft EAP CHAP
 + * Extensions Protocol, Version 2, for mutual authentication and key
 + * derivation. This encapsulates MS-CHAP-v2 protocol which is defined in
 + * RFC 2759. Use of EAP-MSCHAPV2 derived keys with MPPE cipher is described in
 + * RFC 3079.
 + */
 +
 +#include "includes.h"
 +
 +#include "common.h"
 +#include "crypto/ms_funcs.h"
 +#include "crypto/random.h"
 +#include "common/wpa_ctrl.h"
 +#include "mschapv2.h"
 +#include "eap_i.h"
 +#include "eap_config.h"
 +
 +
 +#ifdef _MSC_VER
 +#pragma pack(push, 1)
 +#endif /* _MSC_VER */
 +
 +struct eap_mschapv2_hdr {
 +      u8 op_code; /* MSCHAPV2_OP_* */
 +      u8 mschapv2_id; /* usually same as EAP identifier; must be changed
 +                       * for challenges, but not for success/failure */
 +      u8 ms_length[2]; /* Note: misaligned; length - 5 */
 +      /* followed by data */
 +} STRUCT_PACKED;
 +
 +/* Response Data field */
 +struct ms_response {
 +      u8 peer_challenge[MSCHAPV2_CHAL_LEN];
 +      u8 reserved[8];
 +      u8 nt_response[MSCHAPV2_NT_RESPONSE_LEN];
 +      u8 flags;
 +} STRUCT_PACKED;
 +
 +/* Change-Password Data field */
 +struct ms_change_password {
 +      u8 encr_password[516];
 +      u8 encr_hash[16];
 +      u8 peer_challenge[MSCHAPV2_CHAL_LEN];
 +      u8 reserved[8];
 +      u8 nt_response[MSCHAPV2_NT_RESPONSE_LEN];
 +      u8 flags[2];
 +} STRUCT_PACKED;
 +
 +#ifdef _MSC_VER
 +#pragma pack(pop)
 +#endif /* _MSC_VER */
 +
 +#define MSCHAPV2_OP_CHALLENGE 1
 +#define MSCHAPV2_OP_RESPONSE 2
 +#define MSCHAPV2_OP_SUCCESS 3
 +#define MSCHAPV2_OP_FAILURE 4
 +#define MSCHAPV2_OP_CHANGE_PASSWORD 7
 +
 +#define ERROR_RESTRICTED_LOGON_HOURS 646
 +#define ERROR_ACCT_DISABLED 647
 +#define ERROR_PASSWD_EXPIRED 648
 +#define ERROR_NO_DIALIN_PERMISSION 649
 +#define ERROR_AUTHENTICATION_FAILURE 691
 +#define ERROR_CHANGING_PASSWORD 709
 +
 +#define PASSWD_CHANGE_CHAL_LEN 16
 +#define MSCHAPV2_KEY_LEN 16
 +
 +
 +struct eap_mschapv2_data {
 +      u8 auth_response[MSCHAPV2_AUTH_RESPONSE_LEN];
 +      int auth_response_valid;
 +
 +      int prev_error;
 +      u8 passwd_change_challenge[PASSWD_CHANGE_CHAL_LEN];
 +      int passwd_change_challenge_valid;
 +      int passwd_change_version;
 +
 +      /* Optional challenge values generated in EAP-FAST Phase 1 negotiation
 +       */
 +      u8 *peer_challenge;
 +      u8 *auth_challenge;
 +
 +      int phase2;
 +      u8 master_key[MSCHAPV2_MASTER_KEY_LEN];
 +      int master_key_valid;
 +      int success;
 +
 +      struct wpabuf *prev_challenge;
 +};
 +
 +
 +static void eap_mschapv2_deinit(struct eap_sm *sm, void *priv);
 +
 +
 +static void * eap_mschapv2_init(struct eap_sm *sm)
 +{
 +      struct eap_mschapv2_data *data;
 +      data = os_zalloc(sizeof(*data));
 +      if (data == NULL)
 +              return NULL;
 +
 +      if (sm->peer_challenge) {
 +              data->peer_challenge = os_malloc(MSCHAPV2_CHAL_LEN);
 +              if (data->peer_challenge == NULL) {
 +                      eap_mschapv2_deinit(sm, data);
 +                      return NULL;
 +              }
 +              os_memcpy(data->peer_challenge, sm->peer_challenge,
 +                        MSCHAPV2_CHAL_LEN);
 +      }
 +
 +      if (sm->auth_challenge) {
 +              data->auth_challenge = os_malloc(MSCHAPV2_CHAL_LEN);
 +              if (data->auth_challenge == NULL) {
 +                      eap_mschapv2_deinit(sm, data);
 +                      return NULL;
 +              }
 +              os_memcpy(data->auth_challenge, sm->auth_challenge,
 +                        MSCHAPV2_CHAL_LEN);
 +      }
 +
 +      data->phase2 = sm->init_phase2;
 +
 +      return data;
 +}
 +
 +
 +static void eap_mschapv2_deinit(struct eap_sm *sm, void *priv)
 +{
 +      struct eap_mschapv2_data *data = priv;
 +      os_free(data->peer_challenge);
 +      os_free(data->auth_challenge);
 +      wpabuf_free(data->prev_challenge);
 +      bin_clear_free(data, sizeof(*data));
 +}
 +
 +
 +static struct wpabuf * eap_mschapv2_challenge_reply(
 +      struct eap_sm *sm, struct eap_mschapv2_data *data, u8 id,
 +      u8 mschapv2_id, const u8 *auth_challenge)
 +{
 +      struct wpabuf *resp;
 +      struct eap_mschapv2_hdr *ms;
 +      u8 *peer_challenge;
 +      int ms_len;
 +      struct ms_response *r;
 +      size_t identity_len, password_len;
 +      const u8 *identity, *password;
 +      int pwhash;
 +
 +      wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Generating Challenge Response");
 +
 +      identity = eap_get_config_identity(sm, &identity_len);
 +      password = eap_get_config_password2(sm, &password_len, &pwhash);
 +      if (identity == NULL || password == NULL)
 +              return NULL;
 +
 +      ms_len = sizeof(*ms) + 1 + sizeof(*r) + identity_len;
 +      resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len,
 +                           EAP_CODE_RESPONSE, id);
 +      if (resp == NULL)
 +              return NULL;
 +
 +      ms = wpabuf_put(resp, sizeof(*ms));
 +      ms->op_code = MSCHAPV2_OP_RESPONSE;
 +      ms->mschapv2_id = mschapv2_id;
 +      if (data->prev_error) {
 +              /*
 +               * TODO: this does not seem to be enough when processing two
 +               * or more failure messages. IAS did not increment mschapv2_id
 +               * in its own packets, but it seemed to expect the peer to
 +               * increment this for all packets(?).
 +               */
 +              ms->mschapv2_id++;
 +      }
 +      WPA_PUT_BE16(ms->ms_length, ms_len);
 +
 +      wpabuf_put_u8(resp, sizeof(*r)); /* Value-Size */
 +
 +      /* Response */
 +      r = wpabuf_put(resp, sizeof(*r));
 +      peer_challenge = r->peer_challenge;
 +      if (data->peer_challenge) {
 +              wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: peer_challenge generated "
 +                         "in Phase 1");
 +              peer_challenge = data->peer_challenge;
 +              os_memset(r->peer_challenge, 0, MSCHAPV2_CHAL_LEN);
 +      } else if (random_get_bytes(peer_challenge, MSCHAPV2_CHAL_LEN)) {
 +              wpabuf_free(resp);
 +              return NULL;
 +      }
 +      os_memset(r->reserved, 0, 8);
 +      if (data->auth_challenge) {
 +              wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: auth_challenge generated "
 +                         "in Phase 1");
 +              auth_challenge = data->auth_challenge;
 +      }
 +      if (mschapv2_derive_response(identity, identity_len, password,
 +                                   password_len, pwhash, auth_challenge,
 +                                   peer_challenge, r->nt_response,
 +                                   data->auth_response, data->master_key)) {
 +              wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to derive "
 +                         "response");
 +              wpabuf_free(resp);
 +              return NULL;
 +      }
 +      data->auth_response_valid = 1;
 +      data->master_key_valid = 1;
 +
 +      r->flags = 0; /* reserved, must be zero */
 +
 +      wpabuf_put_data(resp, identity, identity_len);
 +      wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: TX identifier %d mschapv2_id %d "
 +                 "(response)", id, ms->mschapv2_id);
 +      return resp;
 +}
 +
 +
 +/**
 + * eap_mschapv2_process - Process an EAP-MSCHAPv2 challenge message
 + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
 + * @data: Pointer to private EAP method data from eap_mschapv2_init()
 + * @ret: Return values from EAP request validation and processing
 + * @req: Pointer to EAP-MSCHAPv2 header from the request
 + * @req_len: Length of the EAP-MSCHAPv2 data
 + * @id: EAP identifier used in the request
 + * Returns: Pointer to allocated EAP response packet (eapRespData) or %NULL if
 + * no reply available
 + */
 +static struct wpabuf * eap_mschapv2_challenge(
 +      struct eap_sm *sm, struct eap_mschapv2_data *data,
 +      struct eap_method_ret *ret, const struct eap_mschapv2_hdr *req,
 +      size_t req_len, u8 id)
 +{
 +      size_t len, challenge_len;
 +      const u8 *pos, *challenge;
 +
 +      if (eap_get_config_identity(sm, &len) == NULL ||
 +          eap_get_config_password(sm, &len) == NULL)
 +              return NULL;
 +
 +      wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received challenge");
 +      if (req_len < sizeof(*req) + 1) {
 +              wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Too short challenge data "
 +                         "(len %lu)", (unsigned long) req_len);
 +              ret->ignore = TRUE;
 +              return NULL;
 +      }
 +      pos = (const u8 *) (req + 1);
 +      challenge_len = *pos++;
 +      len = req_len - sizeof(*req) - 1;
 +      if (challenge_len != MSCHAPV2_CHAL_LEN) {
 +              wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Invalid challenge length "
 +                         "%lu", (unsigned long) challenge_len);
 +              ret->ignore = TRUE;
 +              return NULL;
 +      }
 +
 +      if (len < challenge_len) {
 +              wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Too short challenge"
 +                         " packet: len=%lu challenge_len=%lu",
 +                         (unsigned long) len, (unsigned long) challenge_len);
 +              ret->ignore = TRUE;
 +              return NULL;
 +      }
 +
 +      if (data->passwd_change_challenge_valid) {
 +              wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Using challenge from the "
 +                         "failure message");
 +              challenge = data->passwd_change_challenge;
 +      } else
 +              challenge = pos;
 +      pos += challenge_len;
 +      len -= challenge_len;
 +      wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Authentication Servername",
 +                  pos, len);
 +
 +      ret->ignore = FALSE;
 +      ret->methodState = METHOD_MAY_CONT;
 +      ret->decision = DECISION_FAIL;
 +      ret->allowNotifications = TRUE;
 +
 +      return eap_mschapv2_challenge_reply(sm, data, id, req->mschapv2_id,
 +                                          challenge);
 +}
 +
 +
 +static void eap_mschapv2_password_changed(struct eap_sm *sm,
 +                                        struct eap_mschapv2_data *data)
 +{
 +      struct eap_peer_config *config = eap_get_config(sm);
 +      if (config && config->new_password) {
 +              wpa_msg(sm->msg_ctx, MSG_INFO,
 +                      WPA_EVENT_PASSWORD_CHANGED
 +                      "EAP-MSCHAPV2: Password changed successfully");
 +              data->prev_error = 0;
 +              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)) {
 +                              bin_clear_free(config->password,
 +                                             config->password_len);
 +                              config->password = NULL;
 +                              config->password_len = 0;
 +                      }
 +                      bin_clear_free(config->new_password,
 +                                     config->new_password_len);
 +              } else {
 +                      config->password = config->new_password;
 +                      config->password_len = config->new_password_len;
 +              }
 +              config->new_password = NULL;
 +              config->new_password_len = 0;
 +      }
 +}
 +
 +
 +/**
 + * eap_mschapv2_process - Process an EAP-MSCHAPv2 success message
 + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
 + * @data: Pointer to private EAP method data from eap_mschapv2_init()
 + * @ret: Return values from EAP request validation and processing
 + * @req: Pointer to EAP-MSCHAPv2 header from the request
 + * @req_len: Length of the EAP-MSCHAPv2 data
 + * @id: EAP identifier used in th erequest
 + * Returns: Pointer to allocated EAP response packet (eapRespData) or %NULL if
 + * no reply available
 + */
 +static struct wpabuf * eap_mschapv2_success(struct eap_sm *sm,
 +                                          struct eap_mschapv2_data *data,
 +                                          struct eap_method_ret *ret,
 +                                          const struct eap_mschapv2_hdr *req,
 +                                          size_t req_len, u8 id)
 +{
 +      struct wpabuf *resp;
 +      const u8 *pos;
 +      size_t len;
 +
 +      wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received success");
 +      len = req_len - sizeof(*req);
 +      pos = (const u8 *) (req + 1);
 +      if (!data->auth_response_valid ||
 +          mschapv2_verify_auth_response(data->auth_response, pos, len)) {
 +              wpa_printf(MSG_WARNING, "EAP-MSCHAPV2: Invalid authenticator "
 +                         "response in success request");
 +              ret->methodState = METHOD_DONE;
 +              ret->decision = DECISION_FAIL;
 +              return NULL;
 +      }
 +      pos += 2 + 2 * MSCHAPV2_AUTH_RESPONSE_LEN;
 +      len -= 2 + 2 * MSCHAPV2_AUTH_RESPONSE_LEN;
 +      while (len > 0 && *pos == ' ') {
 +              pos++;
 +              len--;
 +      }
 +      wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Success message",
 +                        pos, len);
 +      wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Authentication succeeded");
 +
 +      /* Note: Only op_code of the EAP-MSCHAPV2 header is included in success
 +       * message. */
 +      resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, 1,
 +                           EAP_CODE_RESPONSE, id);
 +      if (resp == NULL) {
 +              wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Failed to allocate "
 +                         "buffer for success response");
 +              ret->ignore = TRUE;
 +              return NULL;
 +      }
 +
 +      wpabuf_put_u8(resp, MSCHAPV2_OP_SUCCESS); /* op_code */
 +
 +      ret->methodState = METHOD_DONE;
 +      ret->decision = DECISION_UNCOND_SUCC;
 +      ret->allowNotifications = FALSE;
 +      data->success = 1;
 +
 +      if (data->prev_error == ERROR_PASSWD_EXPIRED)
 +              eap_mschapv2_password_changed(sm, data);
 +
 +      return resp;
 +}
 +
 +
 +static int eap_mschapv2_failure_txt(struct eap_sm *sm,
 +                                  struct eap_mschapv2_data *data, char *txt)
 +{
 +      char *pos, *msg = "";
 +      int retry = 1;
 +      struct eap_peer_config *config = eap_get_config(sm);
 +
 +      /* For example:
 +       * E=691 R=1 C=<32 octets hex challenge> V=3 M=Authentication Failure
 +       */
 +
 +      pos = txt;
 +
 +      if (pos && os_strncmp(pos, "E=", 2) == 0) {
 +              pos += 2;
 +              data->prev_error = atoi(pos);
 +              wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: error %d",
 +                         data->prev_error);
 +              pos = os_strchr(pos, ' ');
 +              if (pos)
 +                      pos++;
 +      }
 +
 +      if (pos && os_strncmp(pos, "R=", 2) == 0) {
 +              pos += 2;
 +              retry = atoi(pos);
 +              wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: retry is %sallowed",
 +                         retry == 1 ? "" : "not ");
 +              pos = os_strchr(pos, ' ');
 +              if (pos)
 +                      pos++;
 +      }
 +
 +      if (pos && os_strncmp(pos, "C=", 2) == 0) {
 +              int hex_len;
 +              pos += 2;
 +              hex_len = os_strchr(pos, ' ') - (char *) pos;
 +              if (hex_len == PASSWD_CHANGE_CHAL_LEN * 2) {
 +                      if (hexstr2bin(pos, data->passwd_change_challenge,
 +                                     PASSWD_CHANGE_CHAL_LEN)) {
 +                              wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: invalid "
 +                                         "failure challenge");
 +                      } else {
 +                              wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: failure "
 +                                          "challenge",
 +                                          data->passwd_change_challenge,
 +                                          PASSWD_CHANGE_CHAL_LEN);
 +                              data->passwd_change_challenge_valid = 1;
 +                      }
 +              } else {
 +                      wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: invalid failure "
 +                                 "challenge len %d", hex_len);
 +              }
 +              pos = os_strchr(pos, ' ');
 +              if (pos)
 +                      pos++;
 +      } else {
 +              wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: required challenge field "
 +                         "was not present in failure message");
 +      }
 +
 +      if (pos && os_strncmp(pos, "V=", 2) == 0) {
 +              pos += 2;
 +              data->passwd_change_version = atoi(pos);
 +              wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: password changing "
 +                         "protocol version %d", data->passwd_change_version);
 +              pos = os_strchr(pos, ' ');
 +              if (pos)
 +                      pos++;
 +      }
 +
 +      if (pos && os_strncmp(pos, "M=", 2) == 0) {
 +              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)",
 +              msg, retry == 1 ? "" : "not ", data->prev_error);
 +      if (data->prev_error == ERROR_PASSWD_EXPIRED &&
 +          data->passwd_change_version == 3 && config) {
 +              if (config->new_password == NULL) {
 +                      wpa_msg(sm->msg_ctx, MSG_INFO,
 +                              "EAP-MSCHAPV2: Password expired - password "
 +                              "change required");
 +                      eap_sm_request_new_password(sm);
 +              }
 +      } else if (retry == 1 && config) {
 +              /* TODO: could prevent the current password from being used
 +               * again at least for some period of time */
 +              if (!config->mschapv2_retry)
 +                      eap_sm_request_identity(sm);
 +              eap_sm_request_password(sm);
 +              config->mschapv2_retry = 1;
 +      } else if (config) {
 +              /* TODO: prevent retries using same username/password */
 +              config->mschapv2_retry = 0;
 +      }
 +
 +      return retry == 1;
 +}
 +
 +
 +static struct wpabuf * eap_mschapv2_change_password(
 +      struct eap_sm *sm, struct eap_mschapv2_data *data,
 +      struct eap_method_ret *ret, const struct eap_mschapv2_hdr *req, u8 id)
 +{
++#ifdef CONFIG_NO_RC4
++      wpa_printf(MSG_ERROR,
++              "EAP-MSCHAPV2: RC4 not support in the build - cannot change password");
++      return NULL;
++#else /* CONFIG_NO_RC4 */
 +      struct wpabuf *resp;
 +      int ms_len;
 +      const u8 *username, *password, *new_password;
 +      size_t username_len, password_len, new_password_len;
 +      struct eap_mschapv2_hdr *ms;
 +      struct ms_change_password *cp;
 +      u8 password_hash[16], password_hash_hash[16];
 +      int pwhash;
 +
 +      username = eap_get_config_identity(sm, &username_len);
 +      password = eap_get_config_password2(sm, &password_len, &pwhash);
 +      new_password = eap_get_config_new_password(sm, &new_password_len);
 +      if (username == NULL || password == NULL || new_password == NULL)
 +              return NULL;
 +
 +      username = mschapv2_remove_domain(username, &username_len);
 +
 +      ret->ignore = FALSE;
 +      ret->methodState = METHOD_MAY_CONT;
 +      ret->decision = DECISION_COND_SUCC;
 +      ret->allowNotifications = TRUE;
 +
 +      ms_len = sizeof(*ms) + sizeof(*cp);
 +      resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len,
 +                           EAP_CODE_RESPONSE, id);
 +      if (resp == NULL)
 +              return NULL;
 +
 +      ms = wpabuf_put(resp, sizeof(*ms));
 +      ms->op_code = MSCHAPV2_OP_CHANGE_PASSWORD;
 +      ms->mschapv2_id = req->mschapv2_id + 1;
 +      WPA_PUT_BE16(ms->ms_length, ms_len);
 +      cp = wpabuf_put(resp, sizeof(*cp));
 +
 +      /* Encrypted-Password */
 +      if (pwhash) {
 +              if (encrypt_pw_block_with_password_hash(
 +                          new_password, new_password_len,
 +                          password, cp->encr_password))
 +                      goto fail;
 +      } else {
 +              if (new_password_encrypted_with_old_nt_password_hash(
 +                          new_password, new_password_len,
 +                          password, password_len, cp->encr_password))
 +                      goto fail;
 +      }
 +
 +      /* Encrypted-Hash */
 +      if (pwhash) {
 +              u8 new_password_hash[16];
 +              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 {
 +              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 */
 +      if (random_get_bytes(cp->peer_challenge, MSCHAPV2_CHAL_LEN))
 +              goto fail;
 +
 +      /* Reserved, must be zero */
 +      os_memset(cp->reserved, 0, 8);
 +
 +      /* NT-Response */
 +      wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: auth_challenge",
 +                  data->passwd_change_challenge, PASSWD_CHANGE_CHAL_LEN);
 +      wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: peer_challenge",
 +                  cp->peer_challenge, MSCHAPV2_CHAL_LEN);
 +      wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: username",
 +                        username, username_len);
 +      wpa_hexdump_ascii_key(MSG_DEBUG, "EAP-MSCHAPV2: new password",
 +                            new_password, new_password_len);
 +      generate_nt_response(data->passwd_change_challenge, cp->peer_challenge,
 +                           username, username_len,
 +                           new_password, new_password_len,
 +                           cp->nt_response);
 +      wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: NT-Response",
 +                  cp->nt_response, MSCHAPV2_NT_RESPONSE_LEN);
 +
 +      /* Authenticator response is not really needed yet, but calculate it
 +       * here so that challenges need not be saved. */
 +      generate_authenticator_response(new_password, new_password_len,
 +                                      cp->peer_challenge,
 +                                      data->passwd_change_challenge,
 +                                      username, username_len,
 +                                      cp->nt_response, data->auth_response);
 +      data->auth_response_valid = 1;
 +
 +      /* Likewise, generate master_key here since we have the needed data
 +       * available. */
 +      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 */
 +      os_memset(cp->flags, 0, 2);
 +
 +      wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: TX identifier %d mschapv2_id %d "
 +                 "(change pw)", id, ms->mschapv2_id);
 +
 +      return resp;
 +
 +fail:
 +      wpabuf_free(resp);
 +      return NULL;
++#endif /* CONFIG_NO_RC4 */
 +}
 +
 +
 +/**
 + * eap_mschapv2_process - Process an EAP-MSCHAPv2 failure message
 + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
 + * @data: Pointer to private EAP method data from eap_mschapv2_init()
 + * @ret: Return values from EAP request validation and processing
 + * @req: Pointer to EAP-MSCHAPv2 header from the request
 + * @req_len: Length of the EAP-MSCHAPv2 data
 + * @id: EAP identifier used in th erequest
 + * Returns: Pointer to allocated EAP response packet (eapRespData) or %NULL if
 + * no reply available
 + */
 +static struct wpabuf * eap_mschapv2_failure(struct eap_sm *sm,
 +                                          struct eap_mschapv2_data *data,
 +                                          struct eap_method_ret *ret,
 +                                          const struct eap_mschapv2_hdr *req,
 +                                          size_t req_len, u8 id)
 +{
 +      struct wpabuf *resp;
 +      const u8 *msdata = (const u8 *) (req + 1);
 +      char *buf;
 +      size_t len = req_len - sizeof(*req);
 +      int retry = 0;
 +
 +      wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received failure");
 +      wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Failure data",
 +                        msdata, len);
 +      /*
 +       * eap_mschapv2_failure_txt() expects a nul terminated string, so we
 +       * must allocate a large enough temporary buffer to create that since
 +       * the received message does not include nul termination.
 +       */
 +      buf = dup_binstr(msdata, len);
 +      if (buf) {
 +              retry = eap_mschapv2_failure_txt(sm, data, buf);
 +              os_free(buf);
 +      }
 +
 +      ret->ignore = FALSE;
 +      ret->methodState = METHOD_DONE;
 +      ret->decision = DECISION_FAIL;
 +      ret->allowNotifications = FALSE;
 +
 +      if (data->prev_error == ERROR_PASSWD_EXPIRED &&
 +          data->passwd_change_version == 3) {
 +              struct eap_peer_config *config = eap_get_config(sm);
 +              if (config && config->new_password)
 +                      return eap_mschapv2_change_password(sm, data, ret, req,
 +                                                          id);
 +              if (config && config->pending_req_new_password)
 +                      return NULL;
 +      } else if (retry && data->prev_error == ERROR_AUTHENTICATION_FAILURE) {
 +              /* TODO: could try to retry authentication, e.g, after having
 +               * changed the username/password. In this case, EAP MS-CHAP-v2
 +               * Failure Response would not be sent here. */
 +              return NULL;
 +      }
 +
 +      /* Note: Only op_code of the EAP-MSCHAPV2 header is included in failure
 +       * message. */
 +      resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, 1,
 +                           EAP_CODE_RESPONSE, id);
 +      if (resp == NULL)
 +              return NULL;
 +
 +      wpabuf_put_u8(resp, MSCHAPV2_OP_FAILURE); /* op_code */
 +
 +      return resp;
 +}
 +
 +
 +static int eap_mschapv2_check_config(struct eap_sm *sm)
 +{
 +      size_t len;
 +
 +      if (eap_get_config_identity(sm, &len) == NULL) {
 +              wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Identity not configured");
 +              eap_sm_request_identity(sm);
 +              return -1;
 +      }
 +
 +      if (eap_get_config_password(sm, &len) == NULL) {
 +              wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Password not configured");
 +              eap_sm_request_password(sm);
 +              return -1;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +static int eap_mschapv2_check_mslen(struct eap_sm *sm, size_t len,
 +                                  const struct eap_mschapv2_hdr *ms)
 +{
 +      size_t ms_len = WPA_GET_BE16(ms->ms_length);
 +
 +      if (ms_len == len)
 +              return 0;
 +
 +      wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Invalid header: len=%lu "
 +                 "ms_len=%lu", (unsigned long) len, (unsigned long) ms_len);
 +      if (sm->workaround) {
 +              /* Some authentication servers use invalid ms_len,
 +               * ignore it for interoperability. */
 +              wpa_printf(MSG_INFO, "EAP-MSCHAPV2: workaround, ignore"
 +                         " invalid ms_len %lu (len %lu)",
 +                         (unsigned long) ms_len,
 +                         (unsigned long) len);
 +              return 0;
 +      }
 +
 +      return -1;
 +}
 +
 +
 +static void eap_mschapv2_copy_challenge(struct eap_mschapv2_data *data,
 +                                      const struct wpabuf *reqData)
 +{
 +      /*
 +       * Store a copy of the challenge message, so that it can be processed
 +       * again in case retry is allowed after a possible failure.
 +       */
 +      wpabuf_free(data->prev_challenge);
 +      data->prev_challenge = wpabuf_dup(reqData);
 +}
 +
 +
 +/**
 + * eap_mschapv2_process - Process an EAP-MSCHAPv2 request
 + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
 + * @priv: Pointer to private EAP method data from eap_mschapv2_init()
 + * @ret: Return values from EAP request validation and processing
 + * @reqData: EAP request to be processed (eapReqData)
 + * Returns: Pointer to allocated EAP response packet (eapRespData) or %NULL if
 + * no reply available
 + */
 +static struct wpabuf * eap_mschapv2_process(struct eap_sm *sm, void *priv,
 +                                          struct eap_method_ret *ret,
 +                                          const struct wpabuf *reqData)
 +{
 +      struct eap_mschapv2_data *data = priv;
 +      struct eap_peer_config *config = eap_get_config(sm);
 +      const struct eap_mschapv2_hdr *ms;
 +      int using_prev_challenge = 0;
 +      const u8 *pos;
 +      size_t len;
 +      u8 id;
 +
 +      if (eap_mschapv2_check_config(sm)) {
 +              ret->ignore = TRUE;
 +              return NULL;
 +      }
 +
 +      if (config->mschapv2_retry && data->prev_challenge &&
 +          data->prev_error == ERROR_AUTHENTICATION_FAILURE) {
 +              wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Replacing pending packet "
 +                         "with the previous challenge");
 +
 +              reqData = data->prev_challenge;
 +              using_prev_challenge = 1;
 +              config->mschapv2_retry = 0;
 +      }
 +
 +      pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, reqData,
 +                             &len);
 +      if (pos == NULL || len < sizeof(*ms) + 1) {
 +              ret->ignore = TRUE;
 +              return NULL;
 +      }
 +
 +      ms = (const struct eap_mschapv2_hdr *) pos;
 +      if (eap_mschapv2_check_mslen(sm, len, ms)) {
 +              ret->ignore = TRUE;
 +              return NULL;
 +      }
 +
 +      id = eap_get_id(reqData);
 +      wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: RX identifier %d mschapv2_id %d",
 +                 id, ms->mschapv2_id);
 +
 +      switch (ms->op_code) {
 +      case MSCHAPV2_OP_CHALLENGE:
 +              if (!using_prev_challenge)
 +                      eap_mschapv2_copy_challenge(data, reqData);
 +              return eap_mschapv2_challenge(sm, data, ret, ms, len, id);
 +      case MSCHAPV2_OP_SUCCESS:
 +              return eap_mschapv2_success(sm, data, ret, ms, len, id);
 +      case MSCHAPV2_OP_FAILURE:
 +              return eap_mschapv2_failure(sm, data, ret, ms, len, id);
 +      default:
 +              wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Unknown op %d - ignored",
 +                         ms->op_code);
 +              ret->ignore = TRUE;
 +              return NULL;
 +      }
 +}
 +
 +
 +static Boolean eap_mschapv2_isKeyAvailable(struct eap_sm *sm, void *priv)
 +{
 +      struct eap_mschapv2_data *data = priv;
 +      return data->success && data->master_key_valid;
 +}
 +
 +
 +static u8 * eap_mschapv2_getKey(struct eap_sm *sm, void *priv, size_t *len)
 +{
 +      struct eap_mschapv2_data *data = priv;
 +      u8 *key;
 +      int key_len;
 +
 +      if (!data->master_key_valid || !data->success)
 +              return NULL;
 +
 +      key_len = 2 * MSCHAPV2_KEY_LEN;
 +
 +      key = os_malloc(key_len);
 +      if (key == NULL)
 +              return NULL;
 +
 +      /* MSK = server MS-MPPE-Recv-Key | MS-MPPE-Send-Key, i.e.,
 +       *      peer MS-MPPE-Send-Key | MS-MPPE-Recv-Key */
 +      get_asymetric_start_key(data->master_key, key, MSCHAPV2_KEY_LEN, 1, 0);
 +      get_asymetric_start_key(data->master_key, key + MSCHAPV2_KEY_LEN,
 +                              MSCHAPV2_KEY_LEN, 0, 0);
 +
 +      wpa_hexdump_key(MSG_DEBUG, "EAP-MSCHAPV2: Derived key",
 +                      key, key_len);
 +
 +      *len = key_len;
 +      return key;
 +}
 +
 +
 +/**
 + * eap_peer_mschapv2_register - Register EAP-MSCHAPv2 peer method
 + * Returns: 0 on success, -1 on failure
 + *
 + * This function is used to register EAP-MSCHAPv2 peer method into the EAP
 + * method list.
 + */
 +int eap_peer_mschapv2_register(void)
 +{
 +      struct eap_method *eap;
 +      int ret;
 +
 +      eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
 +                                  EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2,
 +                                  "MSCHAPV2");
 +      if (eap == NULL)
 +              return -1;
 +
 +      eap->init = eap_mschapv2_init;
 +      eap->deinit = eap_mschapv2_deinit;
 +      eap->process = eap_mschapv2_process;
 +      eap->isKeyAvailable = eap_mschapv2_isKeyAvailable;
 +      eap->getKey = eap_mschapv2_getKey;
 +
 +      ret = eap_peer_method_register(eap);
 +      if (ret)
 +              eap_peer_method_free(eap);
 +      return ret;
 +}
index 6d1ff208ac7ef857c31f941fde02456566451f74,0000000000000000000000000000000000000000..c920bcd3182f8521a3d94d7f62b5551bbc4c3f2d
mode 100644,000000..100644
--- /dev/null
@@@ -1,547 -1,0 +1,547 @@@
-       if (pos == NULL || len < EAP_PAX_ICV_LEN) {
 +/*
 + * EAP peer method: EAP-PAX (RFC 4746)
 + * Copyright (c) 2005-2008, 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_common/eap_pax_common.h"
 +#include "eap_i.h"
 +
 +/*
 + * Note: only PAX_STD subprotocol is currently supported
 + *
 + * TODO: Add support with PAX_SEC with the mandatory to implement ciphersuite
 + * (HMAC_SHA1_128, IANA DH Group 14 (2048 bits), RSA-PKCS1-V1_5) and
 + * recommended ciphersuite (HMAC_SHA256_128, IANA DH Group 15 (3072 bits),
 + * RSAES-OAEP).
 + */
 +
 +struct eap_pax_data {
 +      enum { PAX_INIT, PAX_STD_2_SENT, PAX_DONE } state;
 +      u8 mac_id, dh_group_id, public_key_id;
 +      union {
 +              u8 e[2 * EAP_PAX_RAND_LEN];
 +              struct {
 +                      u8 x[EAP_PAX_RAND_LEN]; /* server rand */
 +                      u8 y[EAP_PAX_RAND_LEN]; /* client rand */
 +              } r;
 +      } rand;
 +      char *cid;
 +      size_t cid_len;
 +      u8 ak[EAP_PAX_AK_LEN];
 +      u8 mk[EAP_PAX_MK_LEN];
 +      u8 ck[EAP_PAX_CK_LEN];
 +      u8 ick[EAP_PAX_ICK_LEN];
 +      u8 mid[EAP_PAX_MID_LEN];
 +};
 +
 +
 +static void eap_pax_deinit(struct eap_sm *sm, void *priv);
 +
 +
 +static void * eap_pax_init(struct eap_sm *sm)
 +{
 +      struct eap_pax_data *data;
 +      const u8 *identity, *password;
 +      size_t identity_len, password_len;
 +
 +      identity = eap_get_config_identity(sm, &identity_len);
 +      password = eap_get_config_password(sm, &password_len);
 +      if (!identity || !password) {
 +              wpa_printf(MSG_INFO, "EAP-PAX: CID (nai) or key (password) "
 +                         "not configured");
 +              return NULL;
 +      }
 +
 +      if (password_len != EAP_PAX_AK_LEN) {
 +              wpa_printf(MSG_INFO, "EAP-PAX: Invalid PSK length");
 +              return NULL;
 +      }
 +
 +      data = os_zalloc(sizeof(*data));
 +      if (data == NULL)
 +              return NULL;
 +      data->state = PAX_INIT;
 +
 +      data->cid = os_malloc(identity_len);
 +      if (data->cid == NULL) {
 +              eap_pax_deinit(sm, data);
 +              return NULL;
 +      }
 +      os_memcpy(data->cid, identity, identity_len);
 +      data->cid_len = identity_len;
 +
 +      os_memcpy(data->ak, password, EAP_PAX_AK_LEN);
 +
 +      return data;
 +}
 +
 +
 +static void eap_pax_deinit(struct eap_sm *sm, void *priv)
 +{
 +      struct eap_pax_data *data = priv;
 +      os_free(data->cid);
 +      bin_clear_free(data, sizeof(*data));
 +}
 +
 +
 +static struct wpabuf * eap_pax_alloc_resp(const struct eap_pax_hdr *req,
 +                                        u8 id, u8 op_code, size_t plen)
 +{
 +      struct wpabuf *resp;
 +      struct eap_pax_hdr *pax;
 +
 +      resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PAX,
 +                           sizeof(*pax) + plen, EAP_CODE_RESPONSE, id);
 +      if (resp == NULL)
 +              return NULL;
 +
 +      pax = wpabuf_put(resp, sizeof(*pax));
 +      pax->op_code = op_code;
 +      pax->flags = 0;
 +      pax->mac_id = req->mac_id;
 +      pax->dh_group_id = req->dh_group_id;
 +      pax->public_key_id = req->public_key_id;
 +
 +      return resp;
 +}
 +
 +
 +static struct wpabuf * eap_pax_process_std_1(struct eap_pax_data *data,
 +                                           struct eap_method_ret *ret, u8 id,
 +                                           const struct eap_pax_hdr *req,
 +                                           size_t req_plen)
 +{
 +      struct wpabuf *resp;
 +      const u8 *pos;
 +      u8 *rpos;
 +      size_t left, plen;
 +
 +      wpa_printf(MSG_DEBUG, "EAP-PAX: PAX_STD-1 (received)");
 +
 +      if (data->state != PAX_INIT) {
 +              wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-1 received in "
 +                         "unexpected state (%d) - ignored", data->state);
 +              ret->ignore = TRUE;
 +              return NULL;
 +      }
 +
 +      if (req->flags & EAP_PAX_FLAGS_CE) {
 +              wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-1 with CE flag set - "
 +                         "ignored");
 +              ret->ignore = TRUE;
 +              return NULL;
 +      }
 +
 +      left = req_plen - sizeof(*req);
 +
 +      if (left < 2 + EAP_PAX_RAND_LEN) {
 +              wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-1 with too short "
 +                         "payload");
 +              ret->ignore = TRUE;
 +              return NULL;
 +      }
 +
 +      pos = (const u8 *) (req + 1);
 +      if (WPA_GET_BE16(pos) != EAP_PAX_RAND_LEN) {
 +              wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-1 with incorrect A "
 +                         "length %d (expected %d)",
 +                         WPA_GET_BE16(pos), EAP_PAX_RAND_LEN);
 +              ret->ignore = TRUE;
 +              return NULL;
 +      }
 +
 +      pos += 2;
 +      left -= 2;
 +      os_memcpy(data->rand.r.x, pos, EAP_PAX_RAND_LEN);
 +      wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: X (server rand)",
 +                  data->rand.r.x, EAP_PAX_RAND_LEN);
 +      pos += EAP_PAX_RAND_LEN;
 +      left -= EAP_PAX_RAND_LEN;
 +
 +      if (left > 0) {
 +              wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ignored extra payload",
 +                          pos, left);
 +      }
 +
 +      if (random_get_bytes(data->rand.r.y, EAP_PAX_RAND_LEN)) {
 +              wpa_printf(MSG_ERROR, "EAP-PAX: Failed to get random data");
 +              ret->ignore = TRUE;
 +              return NULL;
 +      }
 +      wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Y (client rand)",
 +                  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,
 +                                         data->mid) < 0) {
 +              ret->ignore = TRUE;
 +              return NULL;
 +      }
 +
 +      wpa_printf(MSG_DEBUG, "EAP-PAX: PAX_STD-2 (sending)");
 +
 +      plen = 2 + EAP_PAX_RAND_LEN + 2 + data->cid_len + 2 + EAP_PAX_MAC_LEN +
 +              EAP_PAX_ICV_LEN;
 +      resp = eap_pax_alloc_resp(req, id, EAP_PAX_OP_STD_2, plen);
 +      if (resp == NULL)
 +              return NULL;
 +
 +      wpabuf_put_be16(resp, EAP_PAX_RAND_LEN);
 +      wpabuf_put_data(resp, data->rand.r.y, EAP_PAX_RAND_LEN);
 +      wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: B = Y (client rand)",
 +                  data->rand.r.y, EAP_PAX_RAND_LEN);
 +
 +      wpabuf_put_be16(resp, data->cid_len);
 +      wpabuf_put_data(resp, data->cid, data->cid_len);
 +      wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-PAX: CID",
 +                        (u8 *) data->cid, data->cid_len);
 +
 +      wpabuf_put_be16(resp, EAP_PAX_MAC_LEN);
 +      rpos = wpabuf_put(resp, EAP_PAX_MAC_LEN);
 +      eap_pax_mac(req->mac_id, data->ck, EAP_PAX_CK_LEN,
 +                  data->rand.r.x, EAP_PAX_RAND_LEN,
 +                  data->rand.r.y, EAP_PAX_RAND_LEN,
 +                  (u8 *) data->cid, data->cid_len, rpos);
 +      wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: MAC_CK(A, B, CID)",
 +                  rpos, EAP_PAX_MAC_LEN);
 +
 +      /* Optional ADE could be added here, if needed */
 +
 +      rpos = wpabuf_put(resp, EAP_PAX_ICV_LEN);
 +      eap_pax_mac(req->mac_id, data->ick, EAP_PAX_ICK_LEN,
 +                  wpabuf_head(resp), wpabuf_len(resp) - EAP_PAX_ICV_LEN,
 +                  NULL, 0, NULL, 0, rpos);
 +      wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", rpos, EAP_PAX_ICV_LEN);
 +
 +      data->state = PAX_STD_2_SENT;
 +      data->mac_id = req->mac_id;
 +      data->dh_group_id = req->dh_group_id;
 +      data->public_key_id = req->public_key_id;
 +
 +      return resp;
 +}
 +
 +
 +static struct wpabuf * eap_pax_process_std_3(struct eap_pax_data *data,
 +                                           struct eap_method_ret *ret, u8 id,
 +                                           const struct eap_pax_hdr *req,
 +                                           size_t req_plen)
 +{
 +      struct wpabuf *resp;
 +      u8 *rpos, mac[EAP_PAX_MAC_LEN];
 +      const u8 *pos;
 +      size_t left;
 +
 +      wpa_printf(MSG_DEBUG, "EAP-PAX: PAX_STD-3 (received)");
 +
 +      if (data->state != PAX_STD_2_SENT) {
 +              wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-3 received in "
 +                         "unexpected state (%d) - ignored", data->state);
 +              ret->ignore = TRUE;
 +              return NULL;
 +      }
 +
 +      if (req->flags & EAP_PAX_FLAGS_CE) {
 +              wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-3 with CE flag set - "
 +                         "ignored");
 +              ret->ignore = TRUE;
 +              return NULL;
 +      }
 +
 +      left = req_plen - sizeof(*req);
 +
 +      if (left < 2 + EAP_PAX_MAC_LEN) {
 +              wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-3 with too short "
 +                         "payload");
 +              ret->ignore = TRUE;
 +              return NULL;
 +      }
 +
 +      pos = (const u8 *) (req + 1);
 +      if (WPA_GET_BE16(pos) != EAP_PAX_MAC_LEN) {
 +              wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-3 with incorrect "
 +                         "MAC_CK length %d (expected %d)",
 +                         WPA_GET_BE16(pos), EAP_PAX_MAC_LEN);
 +              ret->ignore = TRUE;
 +              return NULL;
 +      }
 +      pos += 2;
 +      left -= 2;
 +      wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: MAC_CK(B, CID)",
 +                  pos, EAP_PAX_MAC_LEN);
 +      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_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)",
 +                          mac, EAP_PAX_MAC_LEN);
 +              ret->methodState = METHOD_DONE;
 +              ret->decision = DECISION_FAIL;
 +              return NULL;
 +      }
 +
 +      pos += EAP_PAX_MAC_LEN;
 +      left -= EAP_PAX_MAC_LEN;
 +
 +      if (left > 0) {
 +              wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ignored extra payload",
 +                          pos, left);
 +      }
 +
 +      wpa_printf(MSG_DEBUG, "EAP-PAX: PAX-ACK (sending)");
 +
 +      resp = eap_pax_alloc_resp(req, id, EAP_PAX_OP_ACK, EAP_PAX_ICV_LEN);
 +      if (resp == NULL)
 +              return NULL;
 +
 +      /* Optional ADE could be added here, if needed */
 +
 +      rpos = wpabuf_put(resp, EAP_PAX_ICV_LEN);
 +      eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN,
 +                  wpabuf_head(resp), wpabuf_len(resp) - EAP_PAX_ICV_LEN,
 +                  NULL, 0, NULL, 0, rpos);
 +      wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", rpos, EAP_PAX_ICV_LEN);
 +
 +      data->state = PAX_DONE;
 +      ret->methodState = METHOD_DONE;
 +      ret->decision = DECISION_UNCOND_SUCC;
 +      ret->allowNotifications = FALSE;
 +
 +      return resp;
 +}
 +
 +
 +static struct wpabuf * eap_pax_process(struct eap_sm *sm, void *priv,
 +                                     struct eap_method_ret *ret,
 +                                     const struct wpabuf *reqData)
 +{
 +      struct eap_pax_data *data = priv;
 +      const struct eap_pax_hdr *req;
 +      struct wpabuf *resp;
 +      u8 icvbuf[EAP_PAX_ICV_LEN], id;
 +      const u8 *icv, *pos;
 +      size_t len;
 +      u16 flen, mlen;
 +
 +      pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PAX, reqData, &len);
++      if (pos == NULL || len < sizeof(*req) + EAP_PAX_ICV_LEN) {
 +              ret->ignore = TRUE;
 +              return NULL;
 +      }
 +      id = eap_get_id(reqData);
 +      req = (const struct eap_pax_hdr *) pos;
 +      flen = len - EAP_PAX_ICV_LEN;
 +      mlen = wpabuf_len(reqData) - EAP_PAX_ICV_LEN;
 +
 +      wpa_printf(MSG_DEBUG, "EAP-PAX: received frame: op_code 0x%x "
 +                 "flags 0x%x mac_id 0x%x dh_group_id 0x%x "
 +                 "public_key_id 0x%x",
 +                 req->op_code, req->flags, req->mac_id, req->dh_group_id,
 +                 req->public_key_id);
 +      wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: received payload",
 +                  pos, len - EAP_PAX_ICV_LEN);
 +
 +      if (data->state != PAX_INIT && data->mac_id != req->mac_id) {
 +              wpa_printf(MSG_INFO, "EAP-PAX: MAC ID changed during "
 +                         "authentication (was 0x%d, is 0x%d)",
 +                         data->mac_id, req->mac_id);
 +              ret->ignore = TRUE;
 +              return NULL;
 +      }
 +
 +      if (data->state != PAX_INIT && data->dh_group_id != req->dh_group_id) {
 +              wpa_printf(MSG_INFO, "EAP-PAX: DH Group ID changed during "
 +                         "authentication (was 0x%d, is 0x%d)",
 +                         data->dh_group_id, req->dh_group_id);
 +              ret->ignore = TRUE;
 +              return NULL;
 +      }
 +
 +      if (data->state != PAX_INIT &&
 +          data->public_key_id != req->public_key_id) {
 +              wpa_printf(MSG_INFO, "EAP-PAX: Public Key ID changed during "
 +                         "authentication (was 0x%d, is 0x%d)",
 +                         data->public_key_id, req->public_key_id);
 +              ret->ignore = TRUE;
 +              return NULL;
 +      }
 +
 +      /* TODO: add support EAP_PAX_HMAC_SHA256_128 */
 +      if (req->mac_id != EAP_PAX_MAC_HMAC_SHA1_128) {
 +              wpa_printf(MSG_INFO, "EAP-PAX: Unsupported MAC ID 0x%x",
 +                         req->mac_id);
 +              ret->ignore = TRUE;
 +              return NULL;
 +      }
 +
 +      if (req->dh_group_id != EAP_PAX_DH_GROUP_NONE) {
 +              wpa_printf(MSG_INFO, "EAP-PAX: Unsupported DH Group ID 0x%x",
 +                         req->dh_group_id);
 +              ret->ignore = TRUE;
 +              return NULL;
 +      }
 +
 +      if (req->public_key_id != EAP_PAX_PUBLIC_KEY_NONE) {
 +              wpa_printf(MSG_INFO, "EAP-PAX: Unsupported Public Key ID 0x%x",
 +                         req->public_key_id);
 +              ret->ignore = TRUE;
 +              return NULL;
 +      }
 +
 +      if (req->flags & EAP_PAX_FLAGS_MF) {
 +              /* TODO: add support for reassembling fragments */
 +              wpa_printf(MSG_INFO, "EAP-PAX: fragmentation not supported - "
 +                         "ignored packet");
 +              ret->ignore = TRUE;
 +              return NULL;
 +      }
 +
 +      icv = pos + len - EAP_PAX_ICV_LEN;
 +      wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", icv, EAP_PAX_ICV_LEN);
 +      if (req->op_code == EAP_PAX_OP_STD_1) {
 +              eap_pax_mac(req->mac_id, (u8 *) "", 0,
 +                          wpabuf_head(reqData), mlen, NULL, 0, NULL, 0,
 +                          icvbuf);
 +      } else {
 +              eap_pax_mac(req->mac_id, data->ick, EAP_PAX_ICK_LEN,
 +                          wpabuf_head(reqData), mlen, NULL, 0, NULL, 0,
 +                          icvbuf);
 +      }
 +      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",
 +                          icvbuf, EAP_PAX_ICV_LEN);
 +              ret->ignore = TRUE;
 +              return NULL;
 +      }
 +
 +      ret->ignore = FALSE;
 +      ret->methodState = METHOD_MAY_CONT;
 +      ret->decision = DECISION_FAIL;
 +      ret->allowNotifications = TRUE;
 +
 +      switch (req->op_code) {
 +      case EAP_PAX_OP_STD_1:
 +              resp = eap_pax_process_std_1(data, ret, id, req, flen);
 +              break;
 +      case EAP_PAX_OP_STD_3:
 +              resp = eap_pax_process_std_3(data, ret, id, req, flen);
 +              break;
 +      default:
 +              wpa_printf(MSG_DEBUG, "EAP-PAX: ignoring message with unknown "
 +                         "op_code %d", req->op_code);
 +              ret->ignore = TRUE;
 +              return NULL;
 +      }
 +
 +      if (ret->methodState == METHOD_DONE) {
 +              ret->allowNotifications = FALSE;
 +      }
 +
 +      return resp;
 +}
 +
 +
 +static Boolean eap_pax_isKeyAvailable(struct eap_sm *sm, void *priv)
 +{
 +      struct eap_pax_data *data = priv;
 +      return data->state == PAX_DONE;
 +}
 +
 +
 +static u8 * eap_pax_getKey(struct eap_sm *sm, void *priv, size_t *len)
 +{
 +      struct eap_pax_data *data = priv;
 +      u8 *key;
 +
 +      if (data->state != PAX_DONE)
 +              return NULL;
 +
 +      key = os_malloc(EAP_MSK_LEN);
 +      if (key == NULL)
 +              return NULL;
 +
 +      *len = EAP_MSK_LEN;
 +      eap_pax_kdf(data->mac_id, data->mk, EAP_PAX_MK_LEN,
 +                  "Master Session Key", data->rand.e, 2 * EAP_PAX_RAND_LEN,
 +                  EAP_MSK_LEN, key);
 +
 +      return key;
 +}
 +
 +
 +static u8 * eap_pax_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
 +{
 +      struct eap_pax_data *data = priv;
 +      u8 *key;
 +
 +      if (data->state != PAX_DONE)
 +              return NULL;
 +
 +      key = os_malloc(EAP_EMSK_LEN);
 +      if (key == NULL)
 +              return NULL;
 +
 +      *len = EAP_EMSK_LEN;
 +      eap_pax_kdf(data->mac_id, data->mk, EAP_PAX_MK_LEN,
 +                  "Extended Master Session Key",
 +                  data->rand.e, 2 * EAP_PAX_RAND_LEN,
 +                  EAP_EMSK_LEN, key);
 +
 +      return key;
 +}
 +
 +
 +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;
 +      int ret;
 +
 +      eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
 +                                  EAP_VENDOR_IETF, EAP_TYPE_PAX, "PAX");
 +      if (eap == NULL)
 +              return -1;
 +
 +      eap->init = eap_pax_init;
 +      eap->deinit = eap_pax_deinit;
 +      eap->process = eap_pax_process;
 +      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)
 +              eap_peer_method_free(eap);
 +      return ret;
 +}
index 86a18bb866de458bec9d99b09c2827afb850f9a1,0000000000000000000000000000000000000000..98a48a6cf5d315bef8c8e65e09a6264fab057810
mode 100644,000000..100644
--- /dev/null
@@@ -1,1256 -1,0 +1,1262 @@@
-               struct wpabuf msg;
-               wpabuf_set(&msg, pos, left);
 +/*
 + * EAP peer method: EAP-PEAP (draft-josefsson-pppext-eap-tls-eap-10.txt)
 + * Copyright (c) 2004-2008, 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/sha1.h"
 +#include "crypto/tls.h"
 +#include "eap_common/eap_tlv_common.h"
 +#include "eap_common/eap_peap_common.h"
 +#include "eap_i.h"
 +#include "eap_tls_common.h"
 +#include "eap_config.h"
 +#include "tncc.h"
 +
 +
 +/* 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
 + */
 +#define EAP_PEAP_VERSION 1
 +
 +
 +static void eap_peap_deinit(struct eap_sm *sm, void *priv);
 +
 +
 +struct eap_peap_data {
 +      struct eap_ssl_data ssl;
 +
 +      int peap_version, force_peap_version, force_new_label;
 +
 +      const struct eap_method *phase2_method;
 +      void *phase2_priv;
 +      int phase2_success;
 +      int phase2_eap_success;
 +      int phase2_eap_started;
 +
 +      struct eap_method_type phase2_type;
 +      struct eap_method_type *phase2_types;
 +      size_t num_phase2_types;
 +
 +      int peap_outer_success; /* 0 = PEAP terminated on Phase 2 inner
 +                               * EAP-Success
 +                               * 1 = reply with tunneled EAP-Success to inner
 +                               * EAP-Success and expect AS to send outer
 +                               * (unencrypted) EAP-Success after this
 +                               * 2 = reply with PEAP/TLS ACK to inner
 +                               * EAP-Success and expect AS to send outer
 +                               * (unencrypted) EAP-Success after this */
 +      int resuming; /* starting a resumed session */
 +      int reauth; /* reauthentication */
 +      u8 *key_data;
 +      u8 *session_id;
 +      size_t id_len;
 +
 +      struct wpabuf *pending_phase2_req;
 +      enum { NO_BINDING, OPTIONAL_BINDING, REQUIRE_BINDING } crypto_binding;
 +      int crypto_binding_used;
 +      u8 binding_nonce[32];
 +      u8 ipmk[40];
 +      u8 cmk[20];
 +      int soh; /* Whether IF-TNCCS-SOH (Statement of Health; Microsoft NAP)
 +                * is enabled. */
 +};
 +
 +
 +static int eap_peap_parse_phase1(struct eap_peap_data *data,
 +                               const char *phase1)
 +{
 +      const char *pos;
 +
 +      pos = os_strstr(phase1, "peapver=");
 +      if (pos) {
 +              data->force_peap_version = atoi(pos + 8);
 +              data->peap_version = data->force_peap_version;
 +              wpa_printf(MSG_DEBUG, "EAP-PEAP: Forced PEAP version %d",
 +                         data->force_peap_version);
 +      }
 +
 +      if (os_strstr(phase1, "peaplabel=1")) {
 +              data->force_new_label = 1;
 +              wpa_printf(MSG_DEBUG, "EAP-PEAP: Force new label for key "
 +                         "derivation");
 +      }
 +
 +      if (os_strstr(phase1, "peap_outer_success=0")) {
 +              data->peap_outer_success = 0;
 +              wpa_printf(MSG_DEBUG, "EAP-PEAP: terminate authentication on "
 +                         "tunneled EAP-Success");
 +      } else if (os_strstr(phase1, "peap_outer_success=1")) {
 +              data->peap_outer_success = 1;
 +              wpa_printf(MSG_DEBUG, "EAP-PEAP: send tunneled EAP-Success "
 +                         "after receiving tunneled EAP-Success");
 +      } else if (os_strstr(phase1, "peap_outer_success=2")) {
 +              data->peap_outer_success = 2;
 +              wpa_printf(MSG_DEBUG, "EAP-PEAP: send PEAP/TLS ACK after "
 +                         "receiving tunneled EAP-Success");
 +      }
 +
 +      if (os_strstr(phase1, "crypto_binding=0")) {
 +              data->crypto_binding = NO_BINDING;
 +              wpa_printf(MSG_DEBUG, "EAP-PEAP: Do not use cryptobinding");
 +      } else if (os_strstr(phase1, "crypto_binding=1")) {
 +              data->crypto_binding = OPTIONAL_BINDING;
 +              wpa_printf(MSG_DEBUG, "EAP-PEAP: Optional cryptobinding");
 +      } else if (os_strstr(phase1, "crypto_binding=2")) {
 +              data->crypto_binding = REQUIRE_BINDING;
 +              wpa_printf(MSG_DEBUG, "EAP-PEAP: Require cryptobinding");
 +      }
 +
 +#ifdef EAP_TNC
 +      if (os_strstr(phase1, "tnc=soh2")) {
 +              data->soh = 2;
 +              wpa_printf(MSG_DEBUG, "EAP-PEAP: SoH version 2 enabled");
 +      } else if (os_strstr(phase1, "tnc=soh1")) {
 +              data->soh = 1;
 +              wpa_printf(MSG_DEBUG, "EAP-PEAP: SoH version 1 enabled");
 +      } else if (os_strstr(phase1, "tnc=soh")) {
 +              data->soh = 2;
 +              wpa_printf(MSG_DEBUG, "EAP-PEAP: SoH version 2 enabled");
 +      }
 +#endif /* EAP_TNC */
 +
 +      return 0;
 +}
 +
 +
 +static void * eap_peap_init(struct eap_sm *sm)
 +{
 +      struct eap_peap_data *data;
 +      struct eap_peer_config *config = eap_get_config(sm);
 +
 +      data = os_zalloc(sizeof(*data));
 +      if (data == NULL)
 +              return NULL;
 +      sm->peap_done = FALSE;
 +      data->peap_version = EAP_PEAP_VERSION;
 +      data->force_peap_version = -1;
 +      data->peap_outer_success = 2;
 +      data->crypto_binding = OPTIONAL_BINDING;
 +
 +      if (config && config->phase1 &&
 +          eap_peap_parse_phase1(data, config->phase1) < 0) {
 +              eap_peap_deinit(sm, data);
 +              return NULL;
 +      }
 +
 +      if (eap_peer_select_phase2_methods(config, "auth=",
 +                                         &data->phase2_types,
 +                                         &data->num_phase2_types) < 0) {
 +              eap_peap_deinit(sm, data);
 +              return NULL;
 +      }
 +
 +      data->phase2_type.vendor = EAP_VENDOR_IETF;
 +      data->phase2_type.method = EAP_TYPE_NONE;
 +
 +      if (eap_peer_tls_ssl_init(sm, &data->ssl, config, EAP_TYPE_PEAP)) {
 +              wpa_printf(MSG_INFO, "EAP-PEAP: Failed to initialize SSL.");
 +              eap_peap_deinit(sm, data);
 +              return NULL;
 +      }
 +
 +      return data;
 +}
 +
 +
 +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;
 +      if (data == NULL)
 +              return;
 +      if (data->phase2_priv && data->phase2_method)
 +              data->phase2_method->deinit(sm, data->phase2_priv);
 +      os_free(data->phase2_types);
 +      eap_peer_tls_ssl_deinit(sm, &data->ssl);
 +      eap_peap_free_key(data);
 +      os_free(data->session_id);
 +      wpabuf_free(data->pending_phase2_req);
 +      os_free(data);
 +}
 +
 +
 +/**
 + * eap_tlv_build_nak - Build EAP-TLV NAK message
 + * @id: EAP identifier for the header
 + * @nak_type: TLV type (EAP_TLV_*)
 + * Returns: Buffer to the allocated EAP-TLV NAK message or %NULL on failure
 + *
 + * This function builds an EAP-TLV NAK message. The caller is responsible for
 + * freeing the returned buffer.
 + */
 +static struct wpabuf * eap_tlv_build_nak(int id, u16 nak_type)
 +{
 +      struct wpabuf *msg;
 +
 +      msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TLV, 10,
 +                          EAP_CODE_RESPONSE, id);
 +      if (msg == NULL)
 +              return NULL;
 +
 +      wpabuf_put_u8(msg, 0x80); /* Mandatory */
 +      wpabuf_put_u8(msg, EAP_TLV_NAK_TLV);
 +      wpabuf_put_be16(msg, 6); /* Length */
 +      wpabuf_put_be32(msg, 0); /* Vendor-Id */
 +      wpabuf_put_be16(msg, nak_type); /* NAK-Type */
 +
 +      return msg;
 +}
 +
 +
 +static int eap_peap_get_isk(struct eap_sm *sm, struct eap_peap_data *data,
 +                          u8 *isk, size_t isk_len)
 +{
 +      u8 *key;
 +      size_t key_len;
 +
 +      os_memset(isk, 0, isk_len);
 +      if (data->phase2_method == NULL || data->phase2_priv == NULL ||
 +          data->phase2_method->isKeyAvailable == NULL ||
 +          data->phase2_method->getKey == NULL)
 +              return 0;
 +
 +      if (!data->phase2_method->isKeyAvailable(sm, data->phase2_priv) ||
 +          (key = data->phase2_method->getKey(sm, data->phase2_priv,
 +                                             &key_len)) == NULL) {
 +              wpa_printf(MSG_DEBUG, "EAP-PEAP: Could not get key material "
 +                         "from Phase 2");
 +              return -1;
 +      }
 +
 +      if (key_len > isk_len)
 +              key_len = isk_len;
 +      os_memcpy(isk, key, key_len);
 +      os_free(key);
 +
 +      return 0;
 +}
 +
 +
 +static int eap_peap_derive_cmk(struct eap_sm *sm, struct eap_peap_data *data)
 +{
 +      u8 *tk;
 +      u8 isk[32], imck[60];
 +
 +      /*
 +       * Tunnel key (TK) is the first 60 octets of the key generated by
 +       * phase 1 of PEAP (based on TLS).
 +       */
 +      tk = data->key_data;
 +      if (tk == NULL)
 +              return -1;
 +      wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: TK", tk, 60);
 +
 +      if (data->reauth &&
 +          tls_connection_resumed(sm->ssl_ctx, data->ssl.conn)) {
 +              /* Fast-connect: IPMK|CMK = TK */
 +              os_memcpy(data->ipmk, tk, 40);
 +              wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IPMK from TK",
 +                              data->ipmk, 40);
 +              os_memcpy(data->cmk, tk + 40, 20);
 +              wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: CMK from TK",
 +                              data->cmk, 20);
 +              return 0;
 +      }
 +
 +      if (eap_peap_get_isk(sm, data, isk, sizeof(isk)) < 0)
 +              return -1;
 +      wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: ISK", isk, sizeof(isk));
 +
 +      /*
 +       * IPMK Seed = "Inner Methods Compound Keys" | ISK
 +       * TempKey = First 40 octets of TK
 +       * IPMK|CMK = PRF+(TempKey, IPMK Seed, 60)
 +       * (note: draft-josefsson-pppext-eap-tls-eap-10.txt includes a space
 +       * in the end of the label just before ISK; is that just a typo?)
 +       */
 +      wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: TempKey", tk, 40);
 +      if (peap_prfplus(data->peap_version, tk, 40,
 +                       "Inner Methods Compound Keys",
 +                       isk, sizeof(isk), imck, sizeof(imck)) < 0)
 +              return -1;
 +      wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IMCK (IPMKj)",
 +                      imck, sizeof(imck));
 +
 +      os_memcpy(data->ipmk, imck, 40);
 +      wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IPMK (S-IPMKj)", data->ipmk, 40);
 +      os_memcpy(data->cmk, imck + 40, 20);
 +      wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: CMK (CMKj)", data->cmk, 20);
 +
 +      return 0;
 +}
 +
 +
 +static int eap_tlv_add_cryptobinding(struct eap_sm *sm,
 +                                   struct eap_peap_data *data,
 +                                   struct wpabuf *buf)
 +{
 +      u8 *mac;
 +      u8 eap_type = EAP_TYPE_PEAP;
 +      const u8 *addr[2];
 +      size_t len[2];
 +      u16 tlv_type;
 +
 +      /* Compound_MAC: HMAC-SHA1-160(cryptobinding TLV | EAP type) */
 +      addr[0] = wpabuf_put(buf, 0);
 +      len[0] = 60;
 +      addr[1] = &eap_type;
 +      len[1] = 1;
 +
 +      tlv_type = EAP_TLV_CRYPTO_BINDING_TLV;
 +      wpabuf_put_be16(buf, tlv_type);
 +      wpabuf_put_be16(buf, 56);
 +
 +      wpabuf_put_u8(buf, 0); /* Reserved */
 +      wpabuf_put_u8(buf, data->peap_version); /* Version */
 +      wpabuf_put_u8(buf, data->peap_version); /* RecvVersion */
 +      wpabuf_put_u8(buf, 1); /* SubType: 0 = Request, 1 = Response */
 +      wpabuf_put_data(buf, data->binding_nonce, 32); /* Nonce */
 +      mac = wpabuf_put(buf, 20); /* Compound_MAC */
 +      wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC CMK", data->cmk, 20);
 +      wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC data 1",
 +                  addr[0], len[0]);
 +      wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC data 2",
 +                  addr[1], len[1]);
 +      hmac_sha1_vector(data->cmk, 20, 2, addr, len, mac);
 +      wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC", mac, SHA1_MAC_LEN);
 +      data->crypto_binding_used = 1;
 +
 +      return 0;
 +}
 +
 +
 +/**
 + * eap_tlv_build_result - Build EAP-TLV Result message
 + * @id: EAP identifier for the header
 + * @status: Status (EAP_TLV_RESULT_SUCCESS or EAP_TLV_RESULT_FAILURE)
 + * Returns: Buffer to the allocated EAP-TLV Result message or %NULL on failure
 + *
 + * This function builds an EAP-TLV Result message. The caller is responsible
 + * for freeing the returned buffer.
 + */
 +static struct wpabuf * eap_tlv_build_result(struct eap_sm *sm,
 +                                          struct eap_peap_data *data,
 +                                          int crypto_tlv_used,
 +                                          int id, u16 status)
 +{
 +      struct wpabuf *msg;
 +      size_t len;
 +
 +      if (data->crypto_binding == NO_BINDING)
 +              crypto_tlv_used = 0;
 +
 +      len = 6;
 +      if (crypto_tlv_used)
 +              len += 60; /* Cryptobinding TLV */
 +      msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TLV, len,
 +                          EAP_CODE_RESPONSE, id);
 +      if (msg == NULL)
 +              return NULL;
 +
 +      wpabuf_put_u8(msg, 0x80); /* Mandatory */
 +      wpabuf_put_u8(msg, EAP_TLV_RESULT_TLV);
 +      wpabuf_put_be16(msg, 2); /* Length */
 +      wpabuf_put_be16(msg, status); /* Status */
 +
 +      if (crypto_tlv_used && eap_tlv_add_cryptobinding(sm, data, msg)) {
 +              wpabuf_free(msg);
 +              return NULL;
 +      }
 +
 +      return msg;
 +}
 +
 +
 +static int eap_tlv_validate_cryptobinding(struct eap_sm *sm,
 +                                        struct eap_peap_data *data,
 +                                        const u8 *crypto_tlv,
 +                                        size_t crypto_tlv_len)
 +{
 +      u8 buf[61], mac[SHA1_MAC_LEN];
 +      const u8 *pos;
 +
 +      if (eap_peap_derive_cmk(sm, data) < 0) {
 +              wpa_printf(MSG_DEBUG, "EAP-PEAP: Could not derive CMK");
 +              return -1;
 +      }
 +
 +      if (crypto_tlv_len != 4 + 56) {
 +              wpa_printf(MSG_DEBUG, "EAP-PEAP: Invalid cryptobinding TLV "
 +                         "length %d", (int) crypto_tlv_len);
 +              return -1;
 +      }
 +
 +      pos = crypto_tlv;
 +      pos += 4; /* TLV header */
 +      if (pos[1] != data->peap_version) {
 +              wpa_printf(MSG_DEBUG, "EAP-PEAP: Cryptobinding TLV Version "
 +                         "mismatch (was %d; expected %d)",
 +                         pos[1], data->peap_version);
 +              return -1;
 +      }
 +
 +      if (pos[3] != 0) {
 +              wpa_printf(MSG_DEBUG, "EAP-PEAP: Unexpected Cryptobinding TLV "
 +                         "SubType %d", pos[3]);
 +              return -1;
 +      }
 +      pos += 4;
 +      os_memcpy(data->binding_nonce, pos, 32);
 +      pos += 32; /* Nonce */
 +
 +      /* Compound_MAC: HMAC-SHA1-160(cryptobinding TLV | EAP type) */
 +      os_memcpy(buf, crypto_tlv, 60);
 +      os_memset(buf + 4 + 4 + 32, 0, 20); /* Compound_MAC */
 +      buf[60] = EAP_TYPE_PEAP;
 +      wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Compound_MAC data",
 +                  buf, sizeof(buf));
 +      hmac_sha1(data->cmk, 20, buf, sizeof(buf), mac);
 +
 +      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",
 +                          pos, SHA1_MAC_LEN);
 +              wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Expected MAC",
 +                          mac, SHA1_MAC_LEN);
 +              return -1;
 +      }
 +
 +      wpa_printf(MSG_DEBUG, "EAP-PEAP: Valid cryptobinding TLV received");
 +
 +      return 0;
 +}
 +
 +
 +/**
 + * eap_tlv_process - Process a received EAP-TLV message and generate a response
 + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
 + * @ret: Return values from EAP request validation and processing
 + * @req: EAP-TLV request to be processed. The caller must have validated that
 + * the buffer is large enough to contain full request (hdr->length bytes) and
 + * that the EAP type is EAP_TYPE_TLV.
 + * @resp: Buffer to return a pointer to the allocated response message. This
 + * field should be initialized to %NULL before the call. The value will be
 + * updated if a response message is generated. The caller is responsible for
 + * freeing the allocated message.
 + * @force_failure: Force negotiation to fail
 + * Returns: 0 on success, -1 on failure
 + */
 +static int eap_tlv_process(struct eap_sm *sm, struct eap_peap_data *data,
 +                         struct eap_method_ret *ret,
 +                         const struct wpabuf *req, struct wpabuf **resp,
 +                         int force_failure)
 +{
 +      size_t left, tlv_len;
 +      const u8 *pos;
 +      const u8 *result_tlv = NULL, *crypto_tlv = NULL;
 +      size_t result_tlv_len = 0, crypto_tlv_len = 0;
 +      int tlv_type, mandatory;
 +
 +      /* Parse TLVs */
 +      pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_TLV, req, &left);
 +      if (pos == NULL)
 +              return -1;
 +      wpa_hexdump(MSG_DEBUG, "EAP-TLV: Received TLVs", pos, left);
 +      while (left >= 4) {
 +              mandatory = !!(pos[0] & 0x80);
 +              tlv_type = WPA_GET_BE16(pos) & 0x3fff;
 +              pos += 2;
 +              tlv_len = WPA_GET_BE16(pos);
 +              pos += 2;
 +              left -= 4;
 +              if (tlv_len > left) {
 +                      wpa_printf(MSG_DEBUG, "EAP-TLV: TLV underrun "
 +                                 "(tlv_len=%lu left=%lu)",
 +                                 (unsigned long) tlv_len,
 +                                 (unsigned long) left);
 +                      return -1;
 +              }
 +              switch (tlv_type) {
 +              case EAP_TLV_RESULT_TLV:
 +                      result_tlv = pos;
 +                      result_tlv_len = tlv_len;
 +                      break;
 +              case EAP_TLV_CRYPTO_BINDING_TLV:
 +                      crypto_tlv = pos;
 +                      crypto_tlv_len = tlv_len;
 +                      break;
 +              default:
 +                      wpa_printf(MSG_DEBUG, "EAP-TLV: Unsupported TLV Type "
 +                                 "%d%s", tlv_type,
 +                                 mandatory ? " (mandatory)" : "");
 +                      if (mandatory) {
 +                              /* NAK TLV and ignore all TLVs in this packet.
 +                               */
 +                              *resp = eap_tlv_build_nak(eap_get_id(req),
 +                                                        tlv_type);
 +                              return *resp == NULL ? -1 : 0;
 +                      }
 +                      /* Ignore this TLV, but process other TLVs */
 +                      break;
 +              }
 +
 +              pos += tlv_len;
 +              left -= tlv_len;
 +      }
 +      if (left) {
 +              wpa_printf(MSG_DEBUG, "EAP-TLV: Last TLV too short in "
 +                         "Request (left=%lu)", (unsigned long) left);
 +              return -1;
 +      }
 +
 +      /* Process supported TLVs */
 +      if (crypto_tlv && data->crypto_binding != NO_BINDING) {
 +              wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Cryptobinding TLV",
 +                          crypto_tlv, crypto_tlv_len);
 +              if (eap_tlv_validate_cryptobinding(sm, data, crypto_tlv - 4,
 +                                                 crypto_tlv_len + 4) < 0) {
 +                      if (result_tlv == NULL)
 +                              return -1;
 +                      force_failure = 1;
 +                      crypto_tlv = NULL; /* do not include Cryptobinding TLV
 +                                          * in response, if the received
 +                                          * cryptobinding was invalid. */
 +              }
 +      } else if (!crypto_tlv && data->crypto_binding == REQUIRE_BINDING) {
 +              wpa_printf(MSG_DEBUG, "EAP-PEAP: No cryptobinding TLV");
 +              return -1;
 +      }
 +
 +      if (result_tlv) {
 +              int status, resp_status;
 +              wpa_hexdump(MSG_DEBUG, "EAP-TLV: Result TLV",
 +                          result_tlv, result_tlv_len);
 +              if (result_tlv_len < 2) {
 +                      wpa_printf(MSG_INFO, "EAP-TLV: Too short Result TLV "
 +                                 "(len=%lu)",
 +                                 (unsigned long) result_tlv_len);
 +                      return -1;
 +              }
 +              status = WPA_GET_BE16(result_tlv);
 +              if (status == EAP_TLV_RESULT_SUCCESS) {
 +                      wpa_printf(MSG_INFO, "EAP-TLV: TLV Result - Success "
 +                                 "- EAP-TLV/Phase2 Completed");
 +                      if (force_failure) {
 +                              wpa_printf(MSG_INFO, "EAP-TLV: Earlier failure"
 +                                         " - force failed Phase 2");
 +                              resp_status = EAP_TLV_RESULT_FAILURE;
 +                              ret->decision = DECISION_FAIL;
 +                      } else {
 +                              resp_status = EAP_TLV_RESULT_SUCCESS;
 +                              ret->decision = DECISION_UNCOND_SUCC;
 +                      }
 +              } else if (status == EAP_TLV_RESULT_FAILURE) {
 +                      wpa_printf(MSG_INFO, "EAP-TLV: TLV Result - Failure");
 +                      resp_status = EAP_TLV_RESULT_FAILURE;
 +                      ret->decision = DECISION_FAIL;
 +              } else {
 +                      wpa_printf(MSG_INFO, "EAP-TLV: Unknown TLV Result "
 +                                 "Status %d", status);
 +                      resp_status = EAP_TLV_RESULT_FAILURE;
 +                      ret->decision = DECISION_FAIL;
 +              }
 +              ret->methodState = METHOD_DONE;
 +
 +              *resp = eap_tlv_build_result(sm, data, crypto_tlv != NULL,
 +                                           eap_get_id(req), resp_status);
 +      }
 +
 +      return 0;
 +}
 +
 +
 +static int eap_peap_phase2_request(struct eap_sm *sm,
 +                                 struct eap_peap_data *data,
 +                                 struct eap_method_ret *ret,
 +                                 struct wpabuf *req,
 +                                 struct wpabuf **resp)
 +{
 +      struct eap_hdr *hdr = wpabuf_mhead(req);
 +      size_t len = be_to_host16(hdr->length);
 +      u8 *pos;
 +      struct eap_method_ret iret;
 +      struct eap_peer_config *config = eap_get_config(sm);
 +
 +      if (len <= sizeof(struct eap_hdr)) {
 +              wpa_printf(MSG_INFO, "EAP-PEAP: too short "
 +                         "Phase 2 request (len=%lu)", (unsigned long) len);
 +              return -1;
 +      }
 +      pos = (u8 *) (hdr + 1);
 +      wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 Request: type=%d", *pos);
 +      switch (*pos) {
 +      case EAP_TYPE_IDENTITY:
 +              *resp = eap_sm_buildIdentity(sm, hdr->identifier, 1);
 +              break;
 +      case EAP_TYPE_TLV:
 +              os_memset(&iret, 0, sizeof(iret));
 +              if (eap_tlv_process(sm, data, &iret, req, resp,
 +                                  data->phase2_eap_started &&
 +                                  !data->phase2_eap_success)) {
 +                      ret->methodState = METHOD_DONE;
 +                      ret->decision = DECISION_FAIL;
 +                      return -1;
 +              }
 +              if (iret.methodState == METHOD_DONE ||
 +                  iret.methodState == METHOD_MAY_CONT) {
 +                      ret->methodState = iret.methodState;
 +                      ret->decision = iret.decision;
 +                      data->phase2_success = 1;
 +              }
 +              break;
 +      case EAP_TYPE_EXPANDED:
 +#ifdef EAP_TNC
 +              if (data->soh) {
 +                      const u8 *epos;
 +                      size_t eleft;
 +
 +                      epos = eap_hdr_validate(EAP_VENDOR_MICROSOFT, 0x21,
 +                                              req, &eleft);
 +                      if (epos) {
 +                              struct wpabuf *buf;
 +                              wpa_printf(MSG_DEBUG,
 +                                         "EAP-PEAP: SoH EAP Extensions");
 +                              buf = tncc_process_soh_request(data->soh,
 +                                                             epos, eleft);
 +                              if (buf) {
 +                                      *resp = eap_msg_alloc(
 +                                              EAP_VENDOR_MICROSOFT, 0x21,
 +                                              wpabuf_len(buf),
 +                                              EAP_CODE_RESPONSE,
 +                                              hdr->identifier);
 +                                      if (*resp == NULL) {
 +                                              ret->methodState = METHOD_DONE;
 +                                              ret->decision = DECISION_FAIL;
 +                                              return -1;
 +                                      }
 +                                      wpabuf_put_buf(*resp, buf);
 +                                      wpabuf_free(buf);
 +                                      break;
 +                              }
 +                      }
 +              }
 +#endif /* EAP_TNC */
 +              /* fall through */
 +      default:
 +              if (data->phase2_type.vendor == EAP_VENDOR_IETF &&
 +                  data->phase2_type.method == EAP_TYPE_NONE) {
 +                      size_t i;
 +                      for (i = 0; i < data->num_phase2_types; i++) {
 +                              if (data->phase2_types[i].vendor !=
 +                                  EAP_VENDOR_IETF ||
 +                                  data->phase2_types[i].method != *pos)
 +                                      continue;
 +
 +                              data->phase2_type.vendor =
 +                                      data->phase2_types[i].vendor;
 +                              data->phase2_type.method =
 +                                      data->phase2_types[i].method;
 +                              wpa_printf(MSG_DEBUG, "EAP-PEAP: Selected "
 +                                         "Phase 2 EAP vendor %d method %d",
 +                                         data->phase2_type.vendor,
 +                                         data->phase2_type.method);
 +                              break;
 +                      }
 +              }
 +              if (*pos != data->phase2_type.method ||
 +                  *pos == EAP_TYPE_NONE) {
 +                      if (eap_peer_tls_phase2_nak(data->phase2_types,
 +                                                  data->num_phase2_types,
 +                                                  hdr, resp))
 +                              return -1;
 +                      return 0;
 +              }
 +
 +              if (data->phase2_priv == NULL) {
 +                      data->phase2_method = eap_peer_get_eap_method(
 +                              data->phase2_type.vendor,
 +                              data->phase2_type.method);
 +                      if (data->phase2_method) {
 +                              sm->init_phase2 = 1;
 +                              data->phase2_priv =
 +                                      data->phase2_method->init(sm);
 +                              sm->init_phase2 = 0;
 +                      }
 +              }
 +              if (data->phase2_priv == NULL || data->phase2_method == NULL) {
 +                      wpa_printf(MSG_INFO, "EAP-PEAP: failed to initialize "
 +                                 "Phase 2 EAP method %d", *pos);
 +                      ret->methodState = METHOD_DONE;
 +                      ret->decision = DECISION_FAIL;
 +                      return -1;
 +              }
 +              data->phase2_eap_started = 1;
 +              os_memset(&iret, 0, sizeof(iret));
 +              *resp = data->phase2_method->process(sm, data->phase2_priv,
 +                                                   &iret, req);
 +              if ((iret.methodState == METHOD_DONE ||
 +                   iret.methodState == METHOD_MAY_CONT) &&
 +                  (iret.decision == DECISION_UNCOND_SUCC ||
 +                   iret.decision == DECISION_COND_SUCC)) {
 +                      data->phase2_eap_success = 1;
 +                      data->phase2_success = 1;
 +              }
 +              break;
 +      }
 +
 +      if (*resp == NULL &&
 +          (config->pending_req_identity || config->pending_req_password ||
 +           config->pending_req_otp || config->pending_req_new_password)) {
 +              wpabuf_free(data->pending_phase2_req);
 +              data->pending_phase2_req = wpabuf_alloc_copy(hdr, len);
 +      }
 +
 +      return 0;
 +}
 +
 +
 +static int eap_peap_decrypt(struct eap_sm *sm, struct eap_peap_data *data,
 +                          struct eap_method_ret *ret,
 +                          const struct eap_hdr *req,
 +                          const struct wpabuf *in_data,
 +                          struct wpabuf **out_data)
 +{
 +      struct wpabuf *in_decrypted = NULL;
 +      int res, skip_change = 0;
 +      struct eap_hdr *hdr, *rhdr;
 +      struct wpabuf *resp = NULL;
 +      size_t len;
 +
 +      wpa_printf(MSG_DEBUG, "EAP-PEAP: received %lu bytes encrypted data for"
 +                 " Phase 2", (unsigned long) wpabuf_len(in_data));
 +
 +      if (data->pending_phase2_req) {
 +              wpa_printf(MSG_DEBUG, "EAP-PEAP: Pending Phase 2 request - "
 +                         "skip decryption and use old data");
 +              /* Clear TLS reassembly state. */
 +              eap_peer_tls_reset_input(&data->ssl);
 +              in_decrypted = data->pending_phase2_req;
 +              data->pending_phase2_req = NULL;
 +              skip_change = 1;
 +              goto continue_req;
 +      }
 +
 +      if (wpabuf_len(in_data) == 0 && sm->workaround &&
 +          data->phase2_success) {
 +              /*
 +               * Cisco ACS seems to be using TLS ACK to terminate
 +               * EAP-PEAPv0/GTC. Try to reply with TLS ACK.
 +               */
 +              wpa_printf(MSG_DEBUG, "EAP-PEAP: Received TLS ACK, but "
 +                         "expected data - acknowledge with TLS ACK since "
 +                         "Phase 2 has been completed");
 +              ret->decision = DECISION_COND_SUCC;
 +              ret->methodState = METHOD_DONE;
 +              return 1;
 +      } else if (wpabuf_len(in_data) == 0) {
 +              /* Received TLS ACK - requesting more fragments */
 +              return eap_peer_tls_encrypt(sm, &data->ssl, EAP_TYPE_PEAP,
 +                                          data->peap_version,
 +                                          req->identifier, NULL, out_data);
 +      }
 +
 +      res = eap_peer_tls_decrypt(sm, &data->ssl, in_data, &in_decrypted);
 +      if (res)
 +              return res;
 +
 +continue_req:
 +      wpa_hexdump_buf(MSG_DEBUG, "EAP-PEAP: Decrypted Phase 2 EAP",
 +                      in_decrypted);
 +
 +      hdr = wpabuf_mhead(in_decrypted);
 +      if (wpabuf_len(in_decrypted) == 5 && hdr->code == EAP_CODE_REQUEST &&
 +          be_to_host16(hdr->length) == 5 &&
 +          eap_get_type(in_decrypted) == EAP_TYPE_IDENTITY) {
 +              /* At least FreeRADIUS seems to send full EAP header with
 +               * EAP Request Identity */
 +              skip_change = 1;
 +      }
 +      if (wpabuf_len(in_decrypted) >= 5 && hdr->code == EAP_CODE_REQUEST &&
 +          eap_get_type(in_decrypted) == EAP_TYPE_TLV) {
 +              skip_change = 1;
 +      }
 +
 +      if (data->peap_version == 0 && !skip_change) {
 +              struct eap_hdr *nhdr;
 +              struct wpabuf *nmsg = wpabuf_alloc(sizeof(struct eap_hdr) +
 +                                                 wpabuf_len(in_decrypted));
 +              if (nmsg == NULL) {
 +                      wpabuf_free(in_decrypted);
 +                      return 0;
 +              }
 +              nhdr = wpabuf_put(nmsg, sizeof(*nhdr));
 +              wpabuf_put_buf(nmsg, in_decrypted);
 +              nhdr->code = req->code;
 +              nhdr->identifier = req->identifier;
 +              nhdr->length = host_to_be16(sizeof(struct eap_hdr) +
 +                                          wpabuf_len(in_decrypted));
 +
 +              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 "
 +                         "EAP frame (len=%lu)",
 +                         (unsigned long) wpabuf_len(in_decrypted));
 +              wpabuf_free(in_decrypted);
 +              return 0;
 +      }
 +      len = be_to_host16(hdr->length);
 +      if (len > wpabuf_len(in_decrypted)) {
 +              wpa_printf(MSG_INFO, "EAP-PEAP: Length mismatch in "
 +                         "Phase 2 EAP frame (len=%lu hdr->length=%lu)",
 +                         (unsigned long) wpabuf_len(in_decrypted),
 +                         (unsigned long) len);
 +              wpabuf_free(in_decrypted);
 +              return 0;
 +      }
 +      if (len < wpabuf_len(in_decrypted)) {
 +              wpa_printf(MSG_INFO, "EAP-PEAP: Odd.. Phase 2 EAP header has "
 +                         "shorter length than full decrypted data "
 +                         "(%lu < %lu)",
 +                         (unsigned long) len,
 +                         (unsigned long) wpabuf_len(in_decrypted));
 +      }
 +      wpa_printf(MSG_DEBUG, "EAP-PEAP: received Phase 2: code=%d "
 +                 "identifier=%d length=%lu", hdr->code, hdr->identifier,
 +                 (unsigned long) len);
 +      switch (hdr->code) {
 +      case EAP_CODE_REQUEST:
 +              if (eap_peap_phase2_request(sm, data, ret, in_decrypted,
 +                                          &resp)) {
 +                      wpabuf_free(in_decrypted);
 +                      wpa_printf(MSG_INFO, "EAP-PEAP: Phase2 Request "
 +                                 "processing failed");
 +                      return 0;
 +              }
 +              break;
 +      case EAP_CODE_SUCCESS:
 +              wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 Success");
 +              if (data->peap_version == 1) {
 +                      /* EAP-Success within TLS tunnel is used to indicate
 +                       * shutdown of the TLS channel. The authentication has
 +                       * been completed. */
 +                      if (data->phase2_eap_started &&
 +                          !data->phase2_eap_success) {
 +                              wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 "
 +                                         "Success used to indicate success, "
 +                                         "but Phase 2 EAP was not yet "
 +                                         "completed successfully");
 +                              ret->methodState = METHOD_DONE;
 +                              ret->decision = DECISION_FAIL;
 +                              wpabuf_free(in_decrypted);
 +                              return 0;
 +                      }
 +                      wpa_printf(MSG_DEBUG, "EAP-PEAP: Version 1 - "
 +                                 "EAP-Success within TLS tunnel - "
 +                                 "authentication completed");
 +                      ret->decision = DECISION_UNCOND_SUCC;
 +                      ret->methodState = METHOD_DONE;
 +                      data->phase2_success = 1;
 +                      if (data->peap_outer_success == 2) {
 +                              wpabuf_free(in_decrypted);
 +                              wpa_printf(MSG_DEBUG, "EAP-PEAP: Use TLS ACK "
 +                                         "to finish authentication");
 +                              return 1;
 +                      } else if (data->peap_outer_success == 1) {
 +                              /* Reply with EAP-Success within the TLS
 +                               * channel to complete the authentication. */
 +                              resp = wpabuf_alloc(sizeof(struct eap_hdr));
 +                              if (resp) {
 +                                      rhdr = wpabuf_put(resp, sizeof(*rhdr));
 +                                      rhdr->code = EAP_CODE_SUCCESS;
 +                                      rhdr->identifier = hdr->identifier;
 +                                      rhdr->length =
 +                                              host_to_be16(sizeof(*rhdr));
 +                              }
 +                      } else {
 +                              /* No EAP-Success expected for Phase 1 (outer,
 +                               * unencrypted auth), so force EAP state
 +                               * machine to SUCCESS state. */
 +                              sm->peap_done = TRUE;
 +                      }
 +              } else {
 +                      /* FIX: ? */
 +              }
 +              break;
 +      case EAP_CODE_FAILURE:
 +              wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 Failure");
 +              ret->decision = DECISION_FAIL;
 +              ret->methodState = METHOD_MAY_CONT;
 +              ret->allowNotifications = FALSE;
 +              /* Reply with EAP-Failure within the TLS channel to complete
 +               * failure reporting. */
 +              resp = wpabuf_alloc(sizeof(struct eap_hdr));
 +              if (resp) {
 +                      rhdr = wpabuf_put(resp, sizeof(*rhdr));
 +                      rhdr->code = EAP_CODE_FAILURE;
 +                      rhdr->identifier = hdr->identifier;
 +                      rhdr->length = host_to_be16(sizeof(*rhdr));
 +              }
 +              break;
 +      default:
 +              wpa_printf(MSG_INFO, "EAP-PEAP: Unexpected code=%d in "
 +                         "Phase 2 EAP header", hdr->code);
 +              break;
 +      }
 +
 +      wpabuf_free(in_decrypted);
 +
 +      if (resp) {
 +              int skip_change2 = 0;
 +              struct wpabuf *rmsg, buf;
 +
 +              wpa_hexdump_buf_key(MSG_DEBUG,
 +                                  "EAP-PEAP: Encrypting Phase 2 data", resp);
 +              /* PEAP version changes */
 +              if (wpabuf_len(resp) >= 5 &&
 +                  wpabuf_head_u8(resp)[0] == EAP_CODE_RESPONSE &&
 +                  eap_get_type(resp) == EAP_TYPE_TLV)
 +                      skip_change2 = 1;
 +              rmsg = resp;
 +              if (data->peap_version == 0 && !skip_change2) {
 +                      wpabuf_set(&buf, wpabuf_head_u8(resp) +
 +                                 sizeof(struct eap_hdr),
 +                                 wpabuf_len(resp) - sizeof(struct eap_hdr));
 +                      rmsg = &buf;
 +              }
 +
 +              if (eap_peer_tls_encrypt(sm, &data->ssl, EAP_TYPE_PEAP,
 +                                       data->peap_version, req->identifier,
 +                                       rmsg, out_data)) {
 +                      wpa_printf(MSG_INFO, "EAP-PEAP: Failed to encrypt "
 +                                 "a Phase 2 frame");
 +              }
 +              wpabuf_free(resp);
 +      }
 +
 +      return 0;
 +}
 +
 +
 +static struct wpabuf * eap_peap_process(struct eap_sm *sm, void *priv,
 +                                      struct eap_method_ret *ret,
 +                                      const struct wpabuf *reqData)
 +{
 +      const struct eap_hdr *req;
 +      size_t left;
 +      int res;
 +      u8 flags, id;
 +      struct wpabuf *resp;
 +      const u8 *pos;
 +      struct eap_peap_data *data = priv;
++      struct wpabuf msg;
 +
 +      pos = eap_peer_tls_process_init(sm, &data->ssl, EAP_TYPE_PEAP, ret,
 +                                      reqData, &left, &flags);
 +      if (pos == NULL)
 +              return NULL;
 +      req = wpabuf_head(reqData);
 +      id = req->identifier;
 +
 +      if (flags & EAP_TLS_FLAGS_START) {
 +              wpa_printf(MSG_DEBUG, "EAP-PEAP: Start (server ver=%d, own "
 +                         "ver=%d)", flags & EAP_TLS_VERSION_MASK,
 +                      data->peap_version);
 +              if ((flags & EAP_TLS_VERSION_MASK) < data->peap_version)
 +                      data->peap_version = flags & EAP_TLS_VERSION_MASK;
 +              if (data->force_peap_version >= 0 &&
 +                  data->force_peap_version != data->peap_version) {
 +                      wpa_printf(MSG_WARNING, "EAP-PEAP: Failed to select "
 +                                 "forced PEAP version %d",
 +                                 data->force_peap_version);
 +                      ret->methodState = METHOD_DONE;
 +                      ret->decision = DECISION_FAIL;
 +                      ret->allowNotifications = FALSE;
 +                      return NULL;
 +              }
 +              wpa_printf(MSG_DEBUG, "EAP-PEAP: Using PEAP version %d",
 +                         data->peap_version);
 +              left = 0; /* make sure that this frame is empty, even though it
 +                         * should always be, anyway */
 +      }
 +
++      wpabuf_set(&msg, pos, left);
++
 +      resp = NULL;
 +      if (tls_connection_established(sm->ssl_ctx, data->ssl.conn) &&
 +          !data->resuming) {
-                                                 data->peap_version, id, pos,
-                                                 left, &resp);
 +              res = eap_peap_decrypt(sm, data, ret, req, &msg, &resp);
 +      } else {
 +              res = eap_peer_tls_process_helper(sm, &data->ssl,
 +                                                EAP_TYPE_PEAP,
-                       struct wpabuf msg;
++                                                data->peap_version, id, &msg,
++                                                &resp);
 +
++              if (res < 0) {
++                      wpa_printf(MSG_DEBUG,
++                                 "EAP-PEAP: TLS processing failed");
++                      ret->methodState = METHOD_DONE;
++                      ret->decision = DECISION_FAIL;
++                      return resp;
++              }
 +              if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) {
 +                      char *label;
 +                      wpa_printf(MSG_DEBUG,
 +                                 "EAP-PEAP: TLS done, proceed to Phase 2");
 +                      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
 +                       * PEAPv1 implementations seem to be using the old
 +                       * label, "client EAP encryption", instead. Use the old
 +                       * label by default, but allow it to be configured with
 +                       * phase1 parameter peaplabel=1. */
 +                      if (data->force_new_label)
 +                              label = "client PEAP encryption";
 +                      else
 +                              label = "client EAP encryption";
 +                      wpa_printf(MSG_DEBUG, "EAP-PEAP: using label '%s' in "
 +                                 "key derivation", label);
 +                      data->key_data =
 +                              eap_peer_tls_derive_key(sm, &data->ssl, label,
 +                                                      EAP_TLS_KEY_LEN);
 +                      if (data->key_data) {
 +                              wpa_hexdump_key(MSG_DEBUG, 
 +                                              "EAP-PEAP: Derived key",
 +                                              data->key_data,
 +                                              EAP_TLS_KEY_LEN);
 +                      } else {
 +                              wpa_printf(MSG_DEBUG, "EAP-PEAP: Failed to "
 +                                         "derive key");
 +                      }
 +
 +                      os_free(data->session_id);
 +                      data->session_id =
 +                              eap_peer_tls_derive_session_id(sm, &data->ssl,
 +                                                             EAP_TYPE_PEAP,
 +                                                             &data->id_len);
 +                      if (data->session_id) {
 +                              wpa_hexdump(MSG_DEBUG,
 +                                          "EAP-PEAP: Derived Session-Id",
 +                                          data->session_id, data->id_len);
 +                      } else {
 +                              wpa_printf(MSG_ERROR, "EAP-PEAP: Failed to "
 +                                         "derive Session-Id");
 +                      }
 +
 +                      if (sm->workaround && data->resuming) {
 +                              /*
 +                               * At least few RADIUS servers (Aegis v1.1.6;
 +                               * but not v1.1.4; and Cisco ACS) seem to be
 +                               * terminating PEAPv1 (Aegis) or PEAPv0 (Cisco
 +                               * ACS) session resumption with outer
 +                               * EAP-Success. This does not seem to follow
 +                               * draft-josefsson-pppext-eap-tls-eap-05.txt
 +                               * section 4.2, so only allow this if EAP
 +                               * workarounds are enabled.
 +                               */
 +                              wpa_printf(MSG_DEBUG, "EAP-PEAP: Workaround - "
 +                                         "allow outer EAP-Success to "
 +                                         "terminate PEAP resumption");
 +                              ret->decision = DECISION_COND_SUCC;
 +                              data->phase2_success = 1;
 +                      }
 +
 +                      data->resuming = 0;
 +              }
 +
 +              if (res == 2) {
-                       wpabuf_set(&msg, pos, left);
 +                      /*
 +                       * Application data included in the handshake message.
 +                       */
 +                      wpabuf_free(data->pending_phase2_req);
 +                      data->pending_phase2_req = resp;
 +                      resp = NULL;
 +                      res = eap_peap_decrypt(sm, data, ret, req, &msg,
 +                                             &resp);
 +              }
 +      }
 +
 +      if (ret->methodState == METHOD_DONE) {
 +              ret->allowNotifications = FALSE;
 +      }
 +
 +      if (res == 1) {
 +              wpabuf_free(resp);
 +              return eap_peer_tls_build_ack(id, EAP_TYPE_PEAP,
 +                                            data->peap_version);
 +      }
 +
 +      return resp;
 +}
 +
 +
 +static Boolean eap_peap_has_reauth_data(struct eap_sm *sm, void *priv)
 +{
 +      struct eap_peap_data *data = priv;
 +      return tls_connection_established(sm->ssl_ctx, data->ssl.conn) &&
 +              data->phase2_success;
 +}
 +
 +
 +static void eap_peap_deinit_for_reauth(struct eap_sm *sm, void *priv)
 +{
 +      struct eap_peap_data *data = priv;
 +      wpabuf_free(data->pending_phase2_req);
 +      data->pending_phase2_req = NULL;
 +      data->crypto_binding_used = 0;
 +}
 +
 +
 +static void * eap_peap_init_for_reauth(struct eap_sm *sm, void *priv)
 +{
 +      struct eap_peap_data *data = priv;
 +      eap_peap_free_key(data);
 +      os_free(data->session_id);
 +      data->session_id = NULL;
 +      if (eap_peer_tls_reauth_init(sm, &data->ssl)) {
 +              os_free(data);
 +              return NULL;
 +      }
 +      if (data->phase2_priv && data->phase2_method &&
 +          data->phase2_method->init_for_reauth)
 +              data->phase2_method->init_for_reauth(sm, data->phase2_priv);
 +      data->phase2_success = 0;
 +      data->phase2_eap_success = 0;
 +      data->phase2_eap_started = 0;
 +      data->resuming = 1;
 +      data->reauth = 1;
 +      sm->peap_done = FALSE;
 +      return priv;
 +}
 +
 +
 +static int eap_peap_get_status(struct eap_sm *sm, void *priv, char *buf,
 +                             size_t buflen, int verbose)
 +{
 +      struct eap_peap_data *data = priv;
 +      int len, ret;
 +
 +      len = eap_peer_tls_status(sm, &data->ssl, buf, buflen, verbose);
 +      if (data->phase2_method) {
 +              ret = os_snprintf(buf + len, buflen - len,
 +                                "EAP-PEAPv%d Phase2 method=%s\n",
 +                                data->peap_version,
 +                                data->phase2_method->name);
 +              if (os_snprintf_error(buflen - len, ret))
 +                      return len;
 +              len += ret;
 +      }
 +      return len;
 +}
 +
 +
 +static Boolean eap_peap_isKeyAvailable(struct eap_sm *sm, void *priv)
 +{
 +      struct eap_peap_data *data = priv;
 +      return data->key_data != NULL && data->phase2_success;
 +}
 +
 +
 +static u8 * eap_peap_getKey(struct eap_sm *sm, void *priv, size_t *len)
 +{
 +      struct eap_peap_data *data = priv;
 +      u8 *key;
 +
 +      if (data->key_data == NULL || !data->phase2_success)
 +              return NULL;
 +
 +      key = os_malloc(EAP_TLS_KEY_LEN);
 +      if (key == NULL)
 +              return NULL;
 +
 +      *len = EAP_TLS_KEY_LEN;
 +
 +      if (data->crypto_binding_used) {
 +              u8 csk[128];
 +              /*
 +               * Note: It looks like Microsoft implementation requires null
 +               * termination for this label while the one used for deriving
 +               * IPMK|CMK did not use null termination.
 +               */
 +              if (peap_prfplus(data->peap_version, data->ipmk, 40,
 +                               "Session Key Generating Function",
 +                               (u8 *) "\00", 1, csk, sizeof(csk)) < 0) {
 +                      os_free(key);
 +                      return NULL;
 +              }
 +              wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: CSK", csk, sizeof(csk));
 +              os_memcpy(key, csk, EAP_TLS_KEY_LEN);
 +              wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Derived key",
 +                          key, EAP_TLS_KEY_LEN);
 +      } else
 +              os_memcpy(key, data->key_data, EAP_TLS_KEY_LEN);
 +
 +      return key;
 +}
 +
 +
 +static u8 * eap_peap_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
 +{
 +      struct eap_peap_data *data = priv;
 +      u8 *id;
 +
 +      if (data->session_id == NULL || !data->phase2_success)
 +              return NULL;
 +
 +      id = os_malloc(data->id_len);
 +      if (id == NULL)
 +              return NULL;
 +
 +      *len = data->id_len;
 +      os_memcpy(id, data->session_id, data->id_len);
 +
 +      return id;
 +}
 +
 +
 +int eap_peer_peap_register(void)
 +{
 +      struct eap_method *eap;
 +      int ret;
 +
 +      eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
 +                                  EAP_VENDOR_IETF, EAP_TYPE_PEAP, "PEAP");
 +      if (eap == NULL)
 +              return -1;
 +
 +      eap->init = eap_peap_init;
 +      eap->deinit = eap_peap_deinit;
 +      eap->process = eap_peap_process;
 +      eap->isKeyAvailable = eap_peap_isKeyAvailable;
 +      eap->getKey = eap_peap_getKey;
 +      eap->get_status = eap_peap_get_status;
 +      eap->has_reauth_data = eap_peap_has_reauth_data;
 +      eap->deinit_for_reauth = eap_peap_deinit_for_reauth;
 +      eap->init_for_reauth = eap_peap_init_for_reauth;
 +      eap->getSessionId = eap_peap_get_session_id;
 +
 +      ret = eap_peer_method_register(eap);
 +      if (ret)
 +              eap_peer_method_free(eap);
 +      return ret;
 +}
index 059bbeecb72da65d02e7d7f8fdf2dda05c3b6655,0000000000000000000000000000000000000000..1f785443ee5acea64896aa3e770721d7cb6f5325
mode 100644,000000..100644
--- /dev/null
@@@ -1,972 -1,0 +1,1076 @@@
-       password = eap_get_config_password(sm, &password_len);
 +/*
 + * EAP peer method: EAP-pwd (RFC 5931)
 + * Copyright (c) 2010, Dan Harkins <dharkins@lounge.org>
 + *
 + * 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/sha256.h"
++#include "crypto/ms_funcs.h"
 +#include "eap_peer/eap_i.h"
 +#include "eap_common/eap_pwd_common.h"
 +
 +
 +struct eap_pwd_data {
 +      enum {
 +              PWD_ID_Req, PWD_Commit_Req, PWD_Confirm_Req,
 +              SUCCESS_ON_FRAG_COMPLETION, SUCCESS, FAILURE
 +      } state;
 +      u8 *id_peer;
 +      size_t id_peer_len;
 +      u8 *id_server;
 +      size_t id_server_len;
 +      u8 *password;
 +      size_t password_len;
++      int password_hash;
 +      u16 group_num;
 +      EAP_PWD_group *grp;
 +
 +      struct wpabuf *inbuf;
 +      size_t in_frag_pos;
 +      struct wpabuf *outbuf;
 +      size_t out_frag_pos;
 +      size_t mtu;
 +
 +      BIGNUM *k;
 +      BIGNUM *private_value;
 +      BIGNUM *server_scalar;
 +      BIGNUM *my_scalar;
 +      EC_POINT *my_element;
 +      EC_POINT *server_element;
 +
 +      u8 msk[EAP_MSK_LEN];
 +      u8 emsk[EAP_EMSK_LEN];
 +      u8 session_id[1 + SHA256_MAC_LEN];
 +
 +      BN_CTX *bnctx;
 +};
 +
 +
 +#ifndef CONFIG_NO_STDOUT_DEBUG
 +static const char * eap_pwd_state_txt(int state)
 +{
 +      switch (state) {
 +        case PWD_ID_Req:
 +              return "PWD-ID-Req";
 +        case PWD_Commit_Req:
 +              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:
 +              return "FAILURE";
 +        default:
 +              return "PWD-UNK";
 +      }
 +}
 +#endif  /* CONFIG_NO_STDOUT_DEBUG */
 +
 +
 +static void eap_pwd_state(struct eap_pwd_data *data, int state)
 +{
 +      wpa_printf(MSG_DEBUG, "EAP-PWD: %s -> %s",
 +                 eap_pwd_state_txt(data->state), eap_pwd_state_txt(state));
 +      data->state = state;
 +}
 +
 +
 +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;
++      int pwhash;
 +
-       if (compute_password_element(data->grp, data->group_num,
-                                    data->password, data->password_len,
-                                    data->id_server, data->id_server_len,
-                                    data->id_peer, data->id_peer_len,
-                                    id->token)) {
++      password = eap_get_config_password2(sm, &password_len, &pwhash);
 +      if (password == NULL) {
 +              wpa_printf(MSG_INFO, "EAP-PWD: No password configured!");
 +              return NULL;
 +      }
 +
 +      identity = eap_get_config_identity(sm, &identity_len);
 +      if (identity == NULL) {
 +              wpa_printf(MSG_INFO, "EAP-PWD: No identity configured!");
 +              return NULL;
 +      }
 +
 +      if ((data = os_zalloc(sizeof(*data))) == NULL) {
 +              wpa_printf(MSG_INFO, "EAP-PWD: memory allocation data fail");
 +              return NULL;
 +      }
 +
 +      if ((data->bnctx = BN_CTX_new()) == NULL) {
 +              wpa_printf(MSG_INFO, "EAP-PWD: bn context allocation fail");
 +              os_free(data);
 +              return NULL;
 +      }
 +
 +      if ((data->id_peer = os_malloc(identity_len)) == NULL) {
 +              wpa_printf(MSG_INFO, "EAP-PWD: memory allocation id fail");
 +              BN_CTX_free(data->bnctx);
 +              os_free(data);
 +              return NULL;
 +      }
 +
 +      os_memcpy(data->id_peer, identity, identity_len);
 +      data->id_peer_len = identity_len;
 +
 +      if ((data->password = os_malloc(password_len)) == NULL) {
 +              wpa_printf(MSG_INFO, "EAP-PWD: memory allocation psk fail");
 +              BN_CTX_free(data->bnctx);
 +              bin_clear_free(data->id_peer, data->id_peer_len);
 +              os_free(data);
 +              return NULL;
 +      }
 +      os_memcpy(data->password, password, password_len);
 +      data->password_len = password_len;
++      data->password_hash = pwhash;
 +
 +      data->out_frag_pos = data->in_frag_pos = 0;
 +      data->inbuf = data->outbuf = NULL;
 +      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;
 +
 +      return data;
 +}
 +
 +
 +static void eap_pwd_deinit(struct eap_sm *sm, void *priv)
 +{
 +      struct eap_pwd_data *data = priv;
 +
 +      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_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_clear_free(data->grp->pwe);
 +              BN_clear_free(data->grp->order);
 +              BN_clear_free(data->grp->prime);
 +              os_free(data->grp);
 +      }
 +      wpabuf_free(data->inbuf);
 +      wpabuf_free(data->outbuf);
 +      bin_clear_free(data, sizeof(*data));
 +}
 +
 +
 +static u8 * eap_pwd_getkey(struct eap_sm *sm, void *priv, size_t *len)
 +{
 +      struct eap_pwd_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_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,
 +                          const struct wpabuf *reqData,
 +                          const u8 *payload, size_t payload_len)
 +{
 +      struct eap_pwd_id *id;
++      const u8 *password;
++      size_t password_len;
++      u8 pwhashhash[16];
++      int res;
 +
 +      if (data->state != PWD_ID_Req) {
 +              ret->ignore = TRUE;
 +              eap_pwd_state(data, FAILURE);
 +              return;
 +      }
 +
 +      if (payload_len < sizeof(struct eap_pwd_id)) {
 +              ret->ignore = TRUE;
 +              eap_pwd_state(data, FAILURE);
 +              return;
 +      }
 +
 +      id = (struct eap_pwd_id *) payload;
 +      data->group_num = be_to_host16(id->group_num);
++      wpa_printf(MSG_DEBUG,
++                 "EAP-PWD: Server EAP-pwd-ID proposal: group=%u random=%u prf=%u prep=%u",
++                 data->group_num, id->random_function, id->prf, id->prep);
 +      if ((id->random_function != EAP_PWD_DEFAULT_RAND_FUNC) ||
 +          (id->prf != EAP_PWD_DEFAULT_PRF)) {
 +              ret->ignore = TRUE;
 +              eap_pwd_state(data, FAILURE);
 +              return;
 +      }
 +
++      if (id->prep != EAP_PWD_PREP_NONE &&
++          id->prep != EAP_PWD_PREP_MS) {
++              wpa_printf(MSG_DEBUG,
++                         "EAP-PWD: Unsupported password pre-processing technique (Prep=%u)",
++                         id->prep);
++              eap_pwd_state(data, FAILURE);
++              return;
++      }
++
++      if (id->prep == EAP_PWD_PREP_NONE && data->password_hash) {
++              wpa_printf(MSG_DEBUG,
++                         "EAP-PWD: Unhashed password not available");
++              eap_pwd_state(data, FAILURE);
++              return;
++      }
++
 +      wpa_printf(MSG_DEBUG, "EAP-PWD (peer): using group %d",
 +                 data->group_num);
 +
 +      data->id_server = os_malloc(payload_len - sizeof(struct eap_pwd_id));
 +      if (data->id_server == NULL) {
 +              wpa_printf(MSG_INFO, "EAP-PWD: memory allocation id fail");
 +              eap_pwd_state(data, FAILURE);
 +              return;
 +      }
 +      data->id_server_len = payload_len - sizeof(struct eap_pwd_id);
 +      os_memcpy(data->id_server, id->identity, data->id_server_len);
 +      wpa_hexdump_ascii(MSG_INFO, "EAP-PWD (peer): server sent id of",
 +                        data->id_server, data->id_server_len);
 +
 +      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);
 +              return;
 +      }
 +
++      if (id->prep == EAP_PWD_PREP_MS) {
++#ifdef CONFIG_FIPS
++              wpa_printf(MSG_ERROR,
++                         "EAP-PWD (peer): MS password hash not supported in FIPS mode");
++              eap_pwd_state(data, FAILURE);
++              return;
++#else /* CONFIG_FIPS */
++              if (data->password_hash) {
++                      res = hash_nt_password_hash(data->password, pwhashhash);
++              } else {
++                      u8 pwhash[16];
++
++                      res = nt_password_hash(data->password,
++                                             data->password_len, pwhash);
++                      if (res == 0)
++                              res = hash_nt_password_hash(pwhash, pwhashhash);
++                      os_memset(pwhash, 0, sizeof(pwhash));
++              }
++
++              if (res) {
++                      eap_pwd_state(data, FAILURE);
++                      return;
++              }
++
++              password = pwhashhash;
++              password_len = sizeof(pwhashhash);
++#endif /* CONFIG_FIPS */
++      } else {
++              password = data->password;
++              password_len = data->password_len;
++      }
++
 +      /* compute PWE */
++      res = compute_password_element(data->grp, data->group_num,
++                                     password, password_len,
++                                     data->id_server, data->id_server_len,
++                                     data->id_peer, data->id_peer_len,
++                                     id->token);
++      os_memset(pwhashhash, 0, sizeof(pwhashhash));
++      if (res) {
 +              wpa_printf(MSG_INFO, "EAP-PWD (peer): unable to compute PWE");
 +              eap_pwd_state(data, FAILURE);
 +              return;
 +      }
 +
 +      wpa_printf(MSG_DEBUG, "EAP-PWD (peer): computed %d bit PWE...",
 +                 BN_num_bits(data->grp->prime));
 +
 +      data->outbuf = wpabuf_alloc(sizeof(struct eap_pwd_id) +
 +                                  data->id_peer_len);
 +      if (data->outbuf == NULL) {
 +              eap_pwd_state(data, FAILURE);
 +              return;
 +      }
 +      wpabuf_put_be16(data->outbuf, data->group_num);
 +      wpabuf_put_u8(data->outbuf, EAP_PWD_DEFAULT_RAND_FUNC);
 +      wpabuf_put_u8(data->outbuf, EAP_PWD_DEFAULT_PRF);
 +      wpabuf_put_data(data->outbuf, id->token, sizeof(id->token));
 +      wpabuf_put_u8(data->outbuf, EAP_PWD_PREP_NONE);
 +      wpabuf_put_data(data->outbuf, data->id_peer, data->id_peer_len);
 +
 +      eap_pwd_state(data, PWD_Commit_Req);
 +}
 +
 +
 +static void
 +eap_pwd_perform_commit_exchange(struct eap_sm *sm, struct eap_pwd_data *data,
 +                              struct eap_method_ret *ret,
 +                              const struct wpabuf *reqData,
 +                              const u8 *payload, size_t payload_len)
 +{
 +      EC_POINT *K = NULL, *point = NULL;
 +      BIGNUM *mask = NULL, *x = NULL, *y = NULL, *cofactor = NULL;
 +      u16 offset;
 +      u8 *ptr, *scalar = NULL, *element = NULL;
++      size_t prime_len, order_len;
++
++      if (data->state != PWD_Commit_Req) {
++              ret->ignore = TRUE;
++              goto fin;
++      }
++
++      prime_len = BN_num_bytes(data->grp->prime);
++      order_len = BN_num_bytes(data->grp->order);
++
++      if (payload_len != 2 * prime_len + order_len) {
++              wpa_printf(MSG_INFO,
++                         "EAP-pwd: Unexpected Commit payload length %u (expected %u)",
++                         (unsigned int) payload_len,
++                         (unsigned int) (2 * prime_len + order_len));
++              goto fin;
++      }
 +
 +      if (((data->private_value = BN_new()) == NULL) ||
 +          ((data->my_element = EC_POINT_new(data->grp->group)) == NULL) ||
 +          ((cofactor = BN_new()) == NULL) ||
 +          ((data->my_scalar = BN_new()) == NULL) ||
 +          ((mask = BN_new()) == NULL)) {
 +              wpa_printf(MSG_INFO, "EAP-PWD (peer): scalar allocation fail");
 +              goto fin;
 +      }
 +
 +      if (!EC_GROUP_get_cofactor(data->grp->group, cofactor, NULL)) {
 +              wpa_printf(MSG_INFO, "EAP-pwd (peer): unable to get cofactor "
 +                         "for curve");
 +              goto fin;
 +      }
 +
 +      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)) {
 +              wpa_printf(MSG_INFO, "EAP-PWD (peer): element allocation "
 +                         "fail");
 +              eap_pwd_state(data, FAILURE);
 +              goto fin;
 +      }
 +
 +      if (!EC_POINT_invert(data->grp->group, data->my_element, data->bnctx))
 +      {
 +              wpa_printf(MSG_INFO, "EAP-PWD (peer): element inversion fail");
 +              goto fin;
 +      }
 +      BN_clear_free(mask);
 +
 +      if (((x = BN_new()) == NULL) ||
 +          ((y = BN_new()) == NULL)) {
 +              wpa_printf(MSG_INFO, "EAP-PWD (peer): point allocation fail");
 +              goto fin;
 +      }
 +
 +      /* process the request */
 +      if (((data->server_scalar = BN_new()) == NULL) ||
 +          ((data->k = BN_new()) == NULL) ||
 +          ((K = EC_POINT_new(data->grp->group)) == NULL) ||
 +          ((point = EC_POINT_new(data->grp->group)) == NULL) ||
 +          ((data->server_element = EC_POINT_new(data->grp->group)) == NULL))
 +      {
 +              wpa_printf(MSG_INFO, "EAP-PWD (peer): peer data allocation "
 +                         "fail");
 +              goto fin;
 +      }
 +
 +      /* element, x then y, followed by scalar */
 +      ptr = (u8 *) payload;
 +      BN_bin2bn(ptr, BN_num_bytes(data->grp->prime), x);
 +      ptr += BN_num_bytes(data->grp->prime);
 +      BN_bin2bn(ptr, BN_num_bytes(data->grp->prime), y);
 +      ptr += BN_num_bytes(data->grp->prime);
 +      BN_bin2bn(ptr, BN_num_bytes(data->grp->order), data->server_scalar);
 +      if (!EC_POINT_set_affine_coordinates_GFp(data->grp->group,
 +                                               data->server_element, x, y,
 +                                               data->bnctx)) {
 +              wpa_printf(MSG_INFO, "EAP-PWD (peer): setting peer element "
 +                         "fail");
 +              goto fin;
 +      }
 +
 +      /* check to ensure server's element is not in a small sub-group */
 +      if (BN_cmp(cofactor, BN_value_one())) {
 +              if (!EC_POINT_mul(data->grp->group, point, NULL,
 +                                data->server_element, cofactor, NULL)) {
 +                      wpa_printf(MSG_INFO, "EAP-PWD (peer): cannot multiply "
 +                                 "server element by order!\n");
 +                      goto fin;
 +              }
 +              if (EC_POINT_is_at_infinity(data->grp->group, point)) {
 +                      wpa_printf(MSG_INFO, "EAP-PWD (peer): server element "
 +                                 "is at infinity!\n");
 +                      goto fin;
 +              }
 +      }
 +
 +      /* compute the shared key, k */
 +      if ((!EC_POINT_mul(data->grp->group, K, NULL, data->grp->pwe,
 +                         data->server_scalar, data->bnctx)) ||
 +          (!EC_POINT_add(data->grp->group, K, K, data->server_element,
 +                         data->bnctx)) ||
 +          (!EC_POINT_mul(data->grp->group, K, NULL, K, data->private_value,
 +                         data->bnctx))) {
 +              wpa_printf(MSG_INFO, "EAP-PWD (peer): computing shared key "
 +                         "fail");
 +              goto fin;
 +      }
 +
 +      /* ensure that the shared key isn't in a small sub-group */
 +      if (BN_cmp(cofactor, BN_value_one())) {
 +              if (!EC_POINT_mul(data->grp->group, K, NULL, K, cofactor,
 +                                NULL)) {
 +                      wpa_printf(MSG_INFO, "EAP-PWD (peer): cannot multiply "
 +                                 "shared key point by order");
 +                      goto fin;
 +              }
 +      }
 +
 +      /*
 +       * This check is strictly speaking just for the case above where
 +       * co-factor > 1 but it was suggested that even though this is probably
 +       * never going to happen it is a simple and safe check "just to be
 +       * sure" so let's be safe.
 +       */
 +      if (EC_POINT_is_at_infinity(data->grp->group, K)) {
 +              wpa_printf(MSG_INFO, "EAP-PWD (peer): shared key point is at "
 +                         "infinity!\n");
 +              goto fin;
 +      }
 +
 +      if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, K, data->k,
 +                                               NULL, data->bnctx)) {
 +              wpa_printf(MSG_INFO, "EAP-PWD (peer): unable to extract "
 +                         "shared secret from point");
 +              goto fin;
 +      }
 +
 +      /* now do the response */
 +      if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
 +                                               data->my_element, x, y,
 +                                               data->bnctx)) {
 +              wpa_printf(MSG_INFO, "EAP-PWD (peer): point assignment fail");
 +              goto fin;
 +      }
 +
 +      if (((scalar = os_malloc(BN_num_bytes(data->grp->order))) == NULL) ||
 +          ((element = os_malloc(BN_num_bytes(data->grp->prime) * 2)) ==
 +           NULL)) {
 +              wpa_printf(MSG_INFO, "EAP-PWD (peer): data allocation fail");
 +              goto fin;
 +      }
 +
 +      /*
 +       * bignums occupy as little memory as possible so one that is
 +       * sufficiently smaller than the prime or order might need pre-pending
 +       * with zeros.
 +       */
 +      os_memset(scalar, 0, BN_num_bytes(data->grp->order));
 +      os_memset(element, 0, BN_num_bytes(data->grp->prime) * 2);
 +      offset = BN_num_bytes(data->grp->order) -
 +              BN_num_bytes(data->my_scalar);
 +      BN_bn2bin(data->my_scalar, scalar + offset);
 +
 +      offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x);
 +      BN_bn2bin(x, element + offset);
 +      offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y);
 +      BN_bn2bin(y, element + BN_num_bytes(data->grp->prime) + offset);
 +
 +      data->outbuf = wpabuf_alloc(BN_num_bytes(data->grp->order) +
 +                                  2 * BN_num_bytes(data->grp->prime));
 +      if (data->outbuf == NULL)
 +              goto fin;
 +
 +      /* we send the element as (x,y) follwed by the scalar */
 +      wpabuf_put_data(data->outbuf, element,
 +                      2 * BN_num_bytes(data->grp->prime));
 +      wpabuf_put_data(data->outbuf, scalar, BN_num_bytes(data->grp->order));
 +
 +fin:
 +      os_free(scalar);
 +      os_free(element);
 +      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
 +              eap_pwd_state(data, PWD_Confirm_Req);
 +}
 +
 +
 +static void
 +eap_pwd_perform_confirm_exchange(struct eap_sm *sm, struct eap_pwd_data *data,
 +                               struct eap_method_ret *ret,
 +                               const struct wpabuf *reqData,
 +                               const u8 *payload, size_t payload_len)
 +{
 +      BIGNUM *x = NULL, *y = NULL;
 +      struct crypto_hash *hash;
 +      u32 cs;
 +      u16 grp;
 +      u8 conf[SHA256_MAC_LEN], *cruft = NULL, *ptr;
 +      int offset;
 +
++      if (data->state != PWD_Confirm_Req) {
++              ret->ignore = TRUE;
++              goto fin;
++      }
++
++      if (payload_len != SHA256_MAC_LEN) {
++              wpa_printf(MSG_INFO,
++                         "EAP-pwd: Unexpected Confirm payload length %u (expected %u)",
++                         (unsigned int) payload_len, SHA256_MAC_LEN);
++              goto fin;
++      }
++
 +      /*
 +       * first build up the ciphersuite which is group | random_function |
 +       *      prf
 +       */
 +      grp = htons(data->group_num);
 +      ptr = (u8 *) &cs;
 +      os_memcpy(ptr, &grp, sizeof(u16));
 +      ptr += sizeof(u16);
 +      *ptr = EAP_PWD_DEFAULT_RAND_FUNC;
 +      ptr += sizeof(u8);
 +      *ptr = EAP_PWD_DEFAULT_PRF;
 +
 +      /* each component of the cruft will be at most as big as the prime */
 +      if (((cruft = os_malloc(BN_num_bytes(data->grp->prime))) == NULL) ||
 +          ((x = BN_new()) == NULL) || ((y = BN_new()) == NULL)) {
 +              wpa_printf(MSG_INFO, "EAP-PWD (server): confirm allocation "
 +                         "fail");
 +              goto fin;
 +      }
 +
 +      /*
 +       * server's commit is H(k | server_element | server_scalar |
 +       *                      peer_element | peer_scalar | ciphersuite)
 +       */
 +      hash = eap_pwd_h_init();
 +      if (hash == NULL)
 +              goto fin;
 +
 +      /*
 +       * zero the memory each time because this is mod prime math and some
 +       * value may start with a few zeros and the previous one did not.
 +       */
 +      os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
 +      offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(data->k);
 +      BN_bn2bin(data->k, cruft + offset);
 +      eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
 +
 +      /* server element: x, y */
 +      if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
 +                                               data->server_element, x, y,
 +                                               data->bnctx)) {
 +              wpa_printf(MSG_INFO, "EAP-PWD (server): confirm point "
 +                         "assignment fail");
 +              goto fin;
 +      }
 +      os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
 +      offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x);
 +      BN_bn2bin(x, cruft + offset);
 +      eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
 +      os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
 +      offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y);
 +      BN_bn2bin(y, cruft + offset);
 +      eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
 +
 +      /* server scalar */
 +      os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
 +      offset = BN_num_bytes(data->grp->order) -
 +              BN_num_bytes(data->server_scalar);
 +      BN_bn2bin(data->server_scalar, cruft + offset);
 +      eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order));
 +
 +      /* my element: x, y */
 +      if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
 +                                               data->my_element, x, y,
 +                                               data->bnctx)) {
 +              wpa_printf(MSG_INFO, "EAP-PWD (server): confirm point "
 +                         "assignment fail");
 +              goto fin;
 +      }
 +
 +      os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
 +      offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x);
 +      BN_bn2bin(x, cruft + offset);
 +      eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
 +      os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
 +      offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y);
 +      BN_bn2bin(y, cruft + offset);
 +      eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
 +
 +      /* my scalar */
 +      os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
 +      offset = BN_num_bytes(data->grp->order) -
 +              BN_num_bytes(data->my_scalar);
 +      BN_bn2bin(data->my_scalar, cruft + offset);
 +      eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order));
 +
 +      /* the ciphersuite */
 +      eap_pwd_h_update(hash, (u8 *) &cs, sizeof(u32));
 +
 +      /* random function fin */
 +      eap_pwd_h_final(hash, conf);
 +
 +      ptr = (u8 *) payload;
 +      if (os_memcmp_const(conf, ptr, SHA256_MAC_LEN)) {
 +              wpa_printf(MSG_INFO, "EAP-PWD (peer): confirm did not verify");
 +              goto fin;
 +      }
 +
 +      wpa_printf(MSG_DEBUG, "EAP-pwd (peer): confirm verified");
 +
 +      /*
 +       * compute confirm:
 +       *  H(k | peer_element | peer_scalar | server_element | server_scalar |
 +       *    ciphersuite)
 +       */
 +      hash = eap_pwd_h_init();
 +      if (hash == NULL)
 +              goto fin;
 +
 +      /* k */
 +      os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
 +      offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(data->k);
 +      BN_bn2bin(data->k, cruft + offset);
 +      eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
 +
 +      /* my element */
 +      if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
 +                                               data->my_element, x, y,
 +                                               data->bnctx)) {
 +              wpa_printf(MSG_INFO, "EAP-PWD (peer): confirm point "
 +                         "assignment fail");
 +              goto fin;
 +      }
 +      os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
 +      offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x);
 +      BN_bn2bin(x, cruft + offset);
 +      eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
 +      os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
 +      offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y);
 +      BN_bn2bin(y, cruft + offset);
 +      eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
 +
 +      /* my scalar */
 +      os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
 +      offset = BN_num_bytes(data->grp->order) -
 +              BN_num_bytes(data->my_scalar);
 +      BN_bn2bin(data->my_scalar, cruft + offset);
 +      eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order));
 +
 +      /* server element: x, y */
 +      if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
 +                                               data->server_element, x, y,
 +                                               data->bnctx)) {
 +              wpa_printf(MSG_INFO, "EAP-PWD (peer): confirm point "
 +                         "assignment fail");
 +              goto fin;
 +      }
 +      os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
 +      offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x);
 +      BN_bn2bin(x, cruft + offset);
 +      eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
 +      os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
 +      offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y);
 +      BN_bn2bin(y, cruft + offset);
 +      eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
 +
 +      /* server scalar */
 +      os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
 +      offset = BN_num_bytes(data->grp->order) -
 +              BN_num_bytes(data->server_scalar);
 +      BN_bn2bin(data->server_scalar, cruft + offset);
 +      eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order));
 +
 +      /* the ciphersuite */
 +      eap_pwd_h_update(hash, (u8 *) &cs, sizeof(u32));
 +
 +      /* all done */
 +      eap_pwd_h_final(hash, conf);
 +
 +      if (compute_keys(data->grp, data->bnctx, data->k,
 +                       data->my_scalar, data->server_scalar, conf, ptr,
 +                       &cs, data->msk, data->emsk, data->session_id) < 0) {
 +              wpa_printf(MSG_INFO, "EAP-PWD (peer): unable to compute MSK | "
 +                         "EMSK");
 +              goto fin;
 +      }
 +
 +      data->outbuf = wpabuf_alloc(SHA256_MAC_LEN);
 +      if (data->outbuf == NULL)
 +              goto fin;
 +
 +      wpabuf_put_data(data->outbuf, conf, SHA256_MAC_LEN);
 +
 +fin:
 +      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 {
 +              eap_pwd_state(data, SUCCESS_ON_FRAG_COMPLETION);
 +      }
 +}
 +
 +
 +static struct wpabuf *
 +eap_pwd_process(struct eap_sm *sm, void *priv, struct eap_method_ret *ret,
 +              const struct wpabuf *reqData)
 +{
 +      struct eap_pwd_data *data = priv;
 +      struct wpabuf *resp = NULL;
 +      const u8 *pos, *buf;
 +      size_t len;
 +      u16 tot_len = 0;
 +      u8 lm_exch;
 +
 +      pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PWD, reqData, &len);
 +      if ((pos == NULL) || (len < 1)) {
 +              wpa_printf(MSG_DEBUG, "EAP-pwd: Got a frame but pos is %s and "
 +                         "len is %d",
 +                         pos == NULL ? "NULL" : "not NULL", (int) len);
 +              ret->ignore = TRUE;
 +              return NULL;
 +      }
 +
 +      ret->ignore = FALSE;
 +      ret->methodState = METHOD_MAY_CONT;
 +      ret->decision = DECISION_FAIL;
 +      ret->allowNotifications = FALSE;
 +
 +      lm_exch = *pos;
 +      pos++;                  /* skip over the bits and the exch */
 +      len--;
 +
 +      /*
 +       * we're fragmenting so send out the next fragment
 +       */
 +      if (data->out_frag_pos) {
 +              /*
 +               * this should be an ACK
 +               */
 +              if (len)
 +                      wpa_printf(MSG_INFO, "Bad Response! Fragmenting but "
 +                                 "not an ACK");
 +
 +              wpa_printf(MSG_DEBUG, "EAP-pwd: Got an ACK for a fragment");
 +              /*
 +               * check if there are going to be more fragments
 +               */
 +              len = wpabuf_len(data->outbuf) - data->out_frag_pos;
 +              if ((len + EAP_PWD_HDR_SIZE) > data->mtu) {
 +                      len = data->mtu - EAP_PWD_HDR_SIZE;
 +                      EAP_PWD_SET_MORE_BIT(lm_exch);
 +              }
 +              resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD,
 +                                   EAP_PWD_HDR_SIZE + len,
 +                                   EAP_CODE_RESPONSE, eap_get_id(reqData));
 +              if (resp == NULL) {
 +                      wpa_printf(MSG_INFO, "Unable to allocate memory for "
 +                                 "next fragment!");
 +                      return NULL;
 +              }
 +              wpabuf_put_u8(resp, lm_exch);
 +              buf = wpabuf_head_u8(data->outbuf);
 +              wpabuf_put_data(resp, buf + data->out_frag_pos, len);
 +              data->out_frag_pos += len;
 +              /*
 +               * this is the last fragment so get rid of the out buffer
 +               */
 +              if (data->out_frag_pos >= wpabuf_len(data->outbuf)) {
 +                      wpabuf_free(data->outbuf);
 +                      data->outbuf = NULL;
 +                      data->out_frag_pos = 0;
 +              }
 +              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;
 +      }
 +
 +      /*
 +       * see if this is a fragment that needs buffering
 +       *
 +       * if it's the first fragment there'll be a length field
 +       */
 +      if (EAP_PWD_GET_LENGTH_BIT(lm_exch)) {
++              if (len < 2) {
++                      wpa_printf(MSG_DEBUG,
++                                 "EAP-pwd: Frame too short to contain Total-Length field");
++                      ret->ignore = TRUE;
++                      return NULL;
++              }
 +              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;
++              if (data->inbuf) {
++                      wpa_printf(MSG_DEBUG,
++                                 "EAP-pwd: Unexpected new fragment start when previous fragment is still in use");
++                      ret->ignore = TRUE;
++                      return NULL;
++              }
 +              data->inbuf = wpabuf_alloc(tot_len);
 +              if (data->inbuf == NULL) {
 +                      wpa_printf(MSG_INFO, "Out of memory to buffer "
 +                                 "fragments!");
 +                      return NULL;
 +              }
++              data->in_frag_pos = 0;
 +              pos += sizeof(u16);
 +              len -= sizeof(u16);
 +      }
 +      /*
 +       * buffer and ACK the fragment
 +       */
 +      if (EAP_PWD_GET_MORE_BIT(lm_exch)) {
 +              data->in_frag_pos += len;
 +              if (data->in_frag_pos > wpabuf_size(data->inbuf)) {
 +                      wpa_printf(MSG_INFO, "EAP-pwd: Buffer overflow attack "
 +                                 "detected (%d vs. %d)!",
 +                                 (int) data->in_frag_pos,
 +                                 (int) wpabuf_len(data->inbuf));
 +                      wpabuf_free(data->inbuf);
 +                      data->inbuf = NULL;
 +                      data->in_frag_pos = 0;
 +                      return NULL;
 +              }
 +              wpabuf_put_data(data->inbuf, pos, len);
 +
 +              resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD,
 +                                   EAP_PWD_HDR_SIZE,
 +                                   EAP_CODE_RESPONSE, eap_get_id(reqData));
 +              if (resp != NULL)
 +                      wpabuf_put_u8(resp, (EAP_PWD_GET_EXCHANGE(lm_exch)));
 +              wpa_printf(MSG_DEBUG, "EAP-pwd: ACKing a %d byte fragment",
 +                         (int) len);
 +              return resp;
 +      }
 +      /*
 +       * we're buffering and this is the last fragment
 +       */
 +      if (data->in_frag_pos) {
 +              wpabuf_put_data(data->inbuf, pos, len);
 +              wpa_printf(MSG_DEBUG, "EAP-pwd: Last fragment, %d bytes",
 +                         (int) len);
 +              data->in_frag_pos += len;
 +              pos = wpabuf_head_u8(data->inbuf);
 +              len = data->in_frag_pos;
 +      }
 +      wpa_printf(MSG_DEBUG, "EAP-pwd: processing frame: exch %d, len %d",
 +                 EAP_PWD_GET_EXCHANGE(lm_exch), (int) len);
 +
 +      switch (EAP_PWD_GET_EXCHANGE(lm_exch)) {
 +      case EAP_PWD_OPCODE_ID_EXCH:
 +              eap_pwd_perform_id_exchange(sm, data, ret, reqData,
 +                                          pos, len);
 +              break;
 +      case EAP_PWD_OPCODE_COMMIT_EXCH:
 +              eap_pwd_perform_commit_exchange(sm, data, ret, reqData,
 +                                              pos, len);
 +              break;
 +      case EAP_PWD_OPCODE_CONFIRM_EXCH:
 +              eap_pwd_perform_confirm_exchange(sm, data, ret, reqData,
 +                                               pos, len);
 +              break;
 +      default:
 +              wpa_printf(MSG_INFO, "EAP-pwd: Ignoring message with unknown "
 +                         "opcode %d", lm_exch);
 +              break;
 +      }
 +      /*
 +       * if we buffered the just processed input now's the time to free it
 +       */
 +      if (data->in_frag_pos) {
 +              wpabuf_free(data->inbuf);
 +              data->inbuf = NULL;
 +              data->in_frag_pos = 0;
 +      }
 +
 +      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?
 +       */
++      lm_exch = EAP_PWD_GET_EXCHANGE(lm_exch);
 +      len = wpabuf_len(data->outbuf);
 +      if ((len + EAP_PWD_HDR_SIZE) > data->mtu) {
 +              resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD, data->mtu,
 +                                   EAP_CODE_RESPONSE, eap_get_id(reqData));
 +              /*
 +               * if so it's the first so include a length field
 +               */
 +              EAP_PWD_SET_LENGTH_BIT(lm_exch);
 +              EAP_PWD_SET_MORE_BIT(lm_exch);
 +              tot_len = len;
 +              /*
 +               * keep the packet at the MTU
 +               */
 +              len = data->mtu - EAP_PWD_HDR_SIZE - sizeof(u16);
 +              wpa_printf(MSG_DEBUG, "EAP-pwd: Fragmenting output, total "
 +                         "length = %d", tot_len);
 +      } else {
 +              resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD,
 +                                   EAP_PWD_HDR_SIZE + len,
 +                                   EAP_CODE_RESPONSE, eap_get_id(reqData));
 +      }
 +      if (resp == NULL)
 +              return NULL;
 +
 +      wpabuf_put_u8(resp, lm_exch);
 +      if (EAP_PWD_GET_LENGTH_BIT(lm_exch)) {
 +              wpabuf_put_be16(resp, tot_len);
 +              data->out_frag_pos += len;
 +      }
 +      buf = wpabuf_head_u8(data->outbuf);
 +      wpabuf_put_data(resp, buf, len);
 +      /*
 +       * if we're not fragmenting then there's no need to carry this around
 +       */
 +      if (data->out_frag_pos == 0) {
 +              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;
 +}
 +
 +
 +static Boolean eap_pwd_key_available(struct eap_sm *sm, void *priv)
 +{
 +      struct eap_pwd_data *data = priv;
 +      return data->state == SUCCESS;
 +}
 +
 +
 +static u8 * eap_pwd_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
 +{
 +      struct eap_pwd_data *data = priv;
 +      u8 *key;
 +
 +      if (data->state != SUCCESS)
 +              return NULL;
 +
 +      if ((key = os_malloc(EAP_EMSK_LEN)) == NULL)
 +              return NULL;
 +
 +      os_memcpy(key, data->emsk, EAP_EMSK_LEN);
 +      *len = EAP_EMSK_LEN;
 +
 +      return key;
 +}
 +
 +
 +int eap_peer_pwd_register(void)
 +{
 +      struct eap_method *eap;
 +      int ret;
 +
 +      eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
 +                                  EAP_VENDOR_IETF, EAP_TYPE_PWD, "PWD");
 +      if (eap == NULL)
 +              return -1;
 +
 +      eap->init = eap_pwd_init;
 +      eap->deinit = eap_pwd_deinit;
 +      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);
 +      if (ret)
 +              eap_peer_method_free(eap);
 +      return ret;
 +}
index 7d14907433e5032d2e1067ed02b94820f6f21112,0000000000000000000000000000000000000000..c4f9843febb35f8e6840023e7d452e1d2161a2ba
mode 100644,000000..100644
--- /dev/null
@@@ -1,517 -1,0 +1,516 @@@
-                                                const struct wpabuf *reqData,
 +/*
 + * EAP peer method: EAP-SAKE (RFC 4763)
 + * Copyright (c) 2006-2008, 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_sake_common.h"
 +
 +struct eap_sake_data {
 +      enum { IDENTITY, CHALLENGE, CONFIRM, SUCCESS, FAILURE } state;
 +      u8 root_secret_a[EAP_SAKE_ROOT_SECRET_LEN];
 +      u8 root_secret_b[EAP_SAKE_ROOT_SECRET_LEN];
 +      u8 rand_s[EAP_SAKE_RAND_LEN];
 +      u8 rand_p[EAP_SAKE_RAND_LEN];
 +      struct {
 +              u8 auth[EAP_SAKE_TEK_AUTH_LEN];
 +              u8 cipher[EAP_SAKE_TEK_CIPHER_LEN];
 +      } tek;
 +      u8 msk[EAP_MSK_LEN];
 +      u8 emsk[EAP_EMSK_LEN];
 +      u8 session_id;
 +      int session_id_set;
 +      u8 *peerid;
 +      size_t peerid_len;
 +      u8 *serverid;
 +      size_t serverid_len;
 +};
 +
 +
 +static const char * eap_sake_state_txt(int state)
 +{
 +      switch (state) {
 +      case IDENTITY:
 +              return "IDENTITY";
 +      case CHALLENGE:
 +              return "CHALLENGE";
 +      case CONFIRM:
 +              return "CONFIRM";
 +      case SUCCESS:
 +              return "SUCCESS";
 +      case FAILURE:
 +              return "FAILURE";
 +      default:
 +              return "?";
 +      }
 +}
 +
 +
 +static void eap_sake_state(struct eap_sake_data *data, int state)
 +{
 +      wpa_printf(MSG_DEBUG, "EAP-SAKE: %s -> %s",
 +                 eap_sake_state_txt(data->state),
 +                 eap_sake_state_txt(state));
 +      data->state = state;
 +}
 +
 +
 +static void eap_sake_deinit(struct eap_sm *sm, void *priv);
 +
 +
 +static void * eap_sake_init(struct eap_sm *sm)
 +{
 +      struct eap_sake_data *data;
 +      const u8 *identity, *password;
 +      size_t identity_len, password_len;
 +
 +      password = eap_get_config_password(sm, &password_len);
 +      if (!password || password_len != 2 * EAP_SAKE_ROOT_SECRET_LEN) {
 +              wpa_printf(MSG_INFO, "EAP-SAKE: No key of correct length "
 +                         "configured");
 +              return NULL;
 +      }
 +
 +      data = os_zalloc(sizeof(*data));
 +      if (data == NULL)
 +              return NULL;
 +      data->state = IDENTITY;
 +
 +      identity = eap_get_config_identity(sm, &identity_len);
 +      if (identity) {
 +              data->peerid = os_malloc(identity_len);
 +              if (data->peerid == NULL) {
 +                      eap_sake_deinit(sm, data);
 +                      return NULL;
 +              }
 +              os_memcpy(data->peerid, identity, identity_len);
 +              data->peerid_len = identity_len;
 +      }
 +
 +      os_memcpy(data->root_secret_a, password, EAP_SAKE_ROOT_SECRET_LEN);
 +      os_memcpy(data->root_secret_b,
 +                password + EAP_SAKE_ROOT_SECRET_LEN,
 +                EAP_SAKE_ROOT_SECRET_LEN);
 +
 +      return data;
 +}
 +
 +
 +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);
 +      bin_clear_free(data, sizeof(*data));
 +}
 +
 +
 +static struct wpabuf * eap_sake_build_msg(struct eap_sake_data *data,
 +                                        int id, size_t length, u8 subtype)
 +{
 +      struct eap_sake_hdr *sake;
 +      struct wpabuf *msg;
 +      size_t plen;
 +
 +      plen = length + sizeof(struct eap_sake_hdr);
 +
 +      msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_SAKE, plen,
 +                          EAP_CODE_RESPONSE, id);
 +      if (msg == NULL) {
 +              wpa_printf(MSG_ERROR, "EAP-SAKE: Failed to allocate memory "
 +                         "request");
 +              return NULL;
 +      }
 +
 +      sake = wpabuf_put(msg, sizeof(*sake));
 +      sake->version = EAP_SAKE_VERSION;
 +      sake->session_id = data->session_id;
 +      sake->subtype = subtype;
 +
 +      return msg;
 +}
 +
 +
 +static struct wpabuf * eap_sake_process_identity(struct eap_sm *sm,
 +                                               struct eap_sake_data *data,
 +                                               struct eap_method_ret *ret,
-       resp = eap_sake_build_msg(data, eap_get_id(reqData),
-                                 2 + data->peerid_len,
++                                               u8 id,
 +                                               const u8 *payload,
 +                                               size_t payload_len)
 +{
 +      struct eap_sake_parse_attr attr;
 +      struct wpabuf *resp;
 +
 +      if (data->state != IDENTITY) {
 +              ret->ignore = TRUE;
 +              return NULL;
 +      }
 +
 +      wpa_printf(MSG_DEBUG, "EAP-SAKE: Received Request/Identity");
 +
 +      if (eap_sake_parse_attributes(payload, payload_len, &attr))
 +              return NULL;
 +
 +      if (!attr.perm_id_req && !attr.any_id_req) {
 +              wpa_printf(MSG_INFO, "EAP-SAKE: No AT_PERM_ID_REQ or "
 +                         "AT_ANY_ID_REQ in Request/Identity");
 +              return NULL;
 +      }
 +
 +      wpa_printf(MSG_DEBUG, "EAP-SAKE: Sending Response/Identity");
 +
-                                                 const struct wpabuf *reqData,
++      resp = eap_sake_build_msg(data, id, 2 + data->peerid_len,
 +                                EAP_SAKE_SUBTYPE_IDENTITY);
 +      if (resp == NULL)
 +              return NULL;
 +
 +      wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_PEERID");
 +      eap_sake_add_attr(resp, EAP_SAKE_AT_PEERID,
 +                        data->peerid, data->peerid_len);
 +
 +      eap_sake_state(data, CHALLENGE);
 +
 +      return resp;
 +}
 +
 +
 +static struct wpabuf * eap_sake_process_challenge(struct eap_sm *sm,
 +                                                struct eap_sake_data *data,
 +                                                struct eap_method_ret *ret,
-       resp = eap_sake_build_msg(data, eap_get_id(reqData), rlen,
-                                 EAP_SAKE_SUBTYPE_CHALLENGE);
++                                                u8 id,
 +                                                const u8 *payload,
 +                                                size_t payload_len)
 +{
 +      struct eap_sake_parse_attr attr;
 +      struct wpabuf *resp;
 +      u8 *rpos;
 +      size_t rlen;
 +
 +      if (data->state != IDENTITY && data->state != CHALLENGE) {
 +              wpa_printf(MSG_DEBUG, "EAP-SAKE: Request/Challenge received "
 +                         "in unexpected state (%d)", data->state);
 +              ret->ignore = TRUE;
 +              return NULL;
 +      }
 +      if (data->state == IDENTITY)
 +              eap_sake_state(data, CHALLENGE);
 +
 +      wpa_printf(MSG_DEBUG, "EAP-SAKE: Received Request/Challenge");
 +
 +      if (eap_sake_parse_attributes(payload, payload_len, &attr))
 +              return NULL;
 +
 +      if (!attr.rand_s) {
 +              wpa_printf(MSG_INFO, "EAP-SAKE: Request/Challenge did not "
 +                         "include AT_RAND_S");
 +              return NULL;
 +      }
 +
 +      os_memcpy(data->rand_s, attr.rand_s, EAP_SAKE_RAND_LEN);
 +      wpa_hexdump(MSG_MSGDUMP, "EAP-SAKE: RAND_S (server rand)",
 +                  data->rand_s, EAP_SAKE_RAND_LEN);
 +
 +      if (random_get_bytes(data->rand_p, EAP_SAKE_RAND_LEN)) {
 +              wpa_printf(MSG_ERROR, "EAP-SAKE: Failed to get random data");
 +              return NULL;
 +      }
 +      wpa_hexdump(MSG_MSGDUMP, "EAP-SAKE: RAND_P (peer rand)",
 +                  data->rand_p, EAP_SAKE_RAND_LEN);
 +
 +      os_free(data->serverid);
 +      data->serverid = NULL;
 +      data->serverid_len = 0;
 +      if (attr.serverid) {
 +              wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-SAKE: SERVERID",
 +                                attr.serverid, attr.serverid_len);
 +              data->serverid = os_malloc(attr.serverid_len);
 +              if (data->serverid == NULL)
 +                      return NULL;
 +              os_memcpy(data->serverid, attr.serverid, attr.serverid_len);
 +              data->serverid_len = attr.serverid_len;
 +      }
 +
 +      eap_sake_derive_keys(data->root_secret_a, data->root_secret_b,
 +                           data->rand_s, data->rand_p,
 +                           (u8 *) &data->tek, data->msk, data->emsk);
 +
 +      wpa_printf(MSG_DEBUG, "EAP-SAKE: Sending Response/Challenge");
 +
 +      rlen = 2 + EAP_SAKE_RAND_LEN + 2 + EAP_SAKE_MIC_LEN;
 +      if (data->peerid)
 +              rlen += 2 + data->peerid_len;
-               return eap_sake_build_msg(data, eap_get_id(reqData), 0,
++      resp = eap_sake_build_msg(data, id, rlen, EAP_SAKE_SUBTYPE_CHALLENGE);
 +      if (resp == NULL)
 +              return NULL;
 +
 +      wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_RAND_P");
 +      eap_sake_add_attr(resp, EAP_SAKE_AT_RAND_P,
 +                        data->rand_p, EAP_SAKE_RAND_LEN);
 +
 +      if (data->peerid) {
 +              wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_PEERID");
 +              eap_sake_add_attr(resp, EAP_SAKE_AT_PEERID,
 +                                data->peerid, data->peerid_len);
 +      }
 +
 +      wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_MIC_P");
 +      wpabuf_put_u8(resp, EAP_SAKE_AT_MIC_P);
 +      wpabuf_put_u8(resp, 2 + EAP_SAKE_MIC_LEN);
 +      rpos = wpabuf_put(resp, EAP_SAKE_MIC_LEN);
 +      if (eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p,
 +                               data->serverid, data->serverid_len,
 +                               data->peerid, data->peerid_len, 1,
 +                               wpabuf_head(resp), wpabuf_len(resp), rpos,
 +                               rpos)) {
 +              wpa_printf(MSG_INFO, "EAP-SAKE: Failed to compute MIC");
 +              wpabuf_free(resp);
 +              return NULL;
 +      }
 +
 +      eap_sake_state(data, CONFIRM);
 +
 +      return resp;
 +}
 +
 +
 +static struct wpabuf * eap_sake_process_confirm(struct eap_sm *sm,
 +                                              struct eap_sake_data *data,
 +                                              struct eap_method_ret *ret,
++                                              u8 id,
 +                                              const struct wpabuf *reqData,
 +                                              const u8 *payload,
 +                                              size_t payload_len)
 +{
 +      struct eap_sake_parse_attr attr;
 +      u8 mic_s[EAP_SAKE_MIC_LEN];
 +      struct wpabuf *resp;
 +      u8 *rpos;
 +
 +      if (data->state != CONFIRM) {
 +              ret->ignore = TRUE;
 +              return NULL;
 +      }
 +
 +      wpa_printf(MSG_DEBUG, "EAP-SAKE: Received Request/Confirm");
 +
 +      if (eap_sake_parse_attributes(payload, payload_len, &attr))
 +              return NULL;
 +
 +      if (!attr.mic_s) {
 +              wpa_printf(MSG_INFO, "EAP-SAKE: Request/Confirm did not "
 +                         "include AT_MIC_S");
 +              return NULL;
 +      }
 +
 +      eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p,
 +                           data->serverid, data->serverid_len,
 +                           data->peerid, data->peerid_len, 0,
 +                           wpabuf_head(reqData), wpabuf_len(reqData),
 +                           attr.mic_s, mic_s);
 +      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;
 +              ret->decision = DECISION_FAIL;
 +              ret->allowNotifications = FALSE;
 +              wpa_printf(MSG_DEBUG, "EAP-SAKE: Sending "
 +                         "Response/Auth-Reject");
-       resp = eap_sake_build_msg(data, eap_get_id(reqData),
-                                 2 + EAP_SAKE_MIC_LEN,
++              return eap_sake_build_msg(data, id, 0,
 +                                        EAP_SAKE_SUBTYPE_AUTH_REJECT);
 +      }
 +
 +      wpa_printf(MSG_DEBUG, "EAP-SAKE: Sending Response/Confirm");
 +
-       u8 subtype, session_id;
++      resp = eap_sake_build_msg(data, id, 2 + EAP_SAKE_MIC_LEN,
 +                                EAP_SAKE_SUBTYPE_CONFIRM);
 +      if (resp == NULL)
 +              return NULL;
 +
 +      wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_MIC_P");
 +      wpabuf_put_u8(resp, EAP_SAKE_AT_MIC_P);
 +      wpabuf_put_u8(resp, 2 + EAP_SAKE_MIC_LEN);
 +      rpos = wpabuf_put(resp, EAP_SAKE_MIC_LEN);
 +      if (eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p,
 +                               data->serverid, data->serverid_len,
 +                               data->peerid, data->peerid_len, 1,
 +                               wpabuf_head(resp), wpabuf_len(resp), rpos,
 +                               rpos)) {
 +              wpa_printf(MSG_INFO, "EAP-SAKE: Failed to compute MIC");
 +              wpabuf_free(resp);
 +              return NULL;
 +      }
 +
 +      eap_sake_state(data, SUCCESS);
 +      ret->methodState = METHOD_DONE;
 +      ret->decision = DECISION_UNCOND_SUCC;
 +      ret->allowNotifications = FALSE;
 +
 +      return resp;
 +}
 +
 +
 +static struct wpabuf * eap_sake_process(struct eap_sm *sm, void *priv,
 +                                      struct eap_method_ret *ret,
 +                                      const struct wpabuf *reqData)
 +{
 +      struct eap_sake_data *data = priv;
 +      const struct eap_sake_hdr *req;
 +      struct wpabuf *resp;
 +      const u8 *pos, *end;
 +      size_t len;
-               resp = eap_sake_process_identity(sm, data, ret, reqData,
++      u8 subtype, session_id, id;
 +
 +      pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_SAKE, reqData, &len);
 +      if (pos == NULL || len < sizeof(struct eap_sake_hdr)) {
 +              ret->ignore = TRUE;
 +              return NULL;
 +      }
 +
 +      req = (const struct eap_sake_hdr *) pos;
 +      end = pos + len;
++      id = eap_get_id(reqData);
 +      subtype = req->subtype;
 +      session_id = req->session_id;
 +      pos = (const u8 *) (req + 1);
 +
 +      wpa_printf(MSG_DEBUG, "EAP-SAKE: Received frame: subtype %d "
 +                 "session_id %d", subtype, session_id);
 +      wpa_hexdump(MSG_DEBUG, "EAP-SAKE: Received attributes",
 +                  pos, end - pos);
 +
 +      if (data->session_id_set && data->session_id != session_id) {
 +              wpa_printf(MSG_INFO, "EAP-SAKE: Session ID mismatch (%d,%d)",
 +                         session_id, data->session_id);
 +              ret->ignore = TRUE;
 +              return NULL;
 +      }
 +      data->session_id = session_id;
 +      data->session_id_set = 1;
 +
 +      ret->ignore = FALSE;
 +      ret->methodState = METHOD_MAY_CONT;
 +      ret->decision = DECISION_FAIL;
 +      ret->allowNotifications = TRUE;
 +
 +      switch (subtype) {
 +      case EAP_SAKE_SUBTYPE_IDENTITY:
-               resp = eap_sake_process_challenge(sm, data, ret, reqData,
++              resp = eap_sake_process_identity(sm, data, ret, id,
 +                                               pos, end - pos);
 +              break;
 +      case EAP_SAKE_SUBTYPE_CHALLENGE:
-               resp = eap_sake_process_confirm(sm, data, ret, reqData,
++              resp = eap_sake_process_challenge(sm, data, ret, id,
 +                                                pos, end - pos);
 +              break;
 +      case EAP_SAKE_SUBTYPE_CONFIRM:
++              resp = eap_sake_process_confirm(sm, data, ret, id, reqData,
 +                                              pos, end - pos);
 +              break;
 +      default:
 +              wpa_printf(MSG_DEBUG, "EAP-SAKE: Ignoring message with "
 +                         "unknown subtype %d", subtype);
 +              ret->ignore = TRUE;
 +              return NULL;
 +      }
 +
 +      if (ret->methodState == METHOD_DONE)
 +              ret->allowNotifications = FALSE;
 +
 +      return resp;
 +}
 +
 +
 +static Boolean eap_sake_isKeyAvailable(struct eap_sm *sm, void *priv)
 +{
 +      struct eap_sake_data *data = priv;
 +      return data->state == SUCCESS;
 +}
 +
 +
 +static u8 * eap_sake_getKey(struct eap_sm *sm, void *priv, size_t *len)
 +{
 +      struct eap_sake_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_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;
 +}
 +
 +
 +static u8 * eap_sake_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
 +{
 +      struct eap_sake_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_sake_register(void)
 +{
 +      struct eap_method *eap;
 +      int ret;
 +
 +      eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
 +                                  EAP_VENDOR_IETF, EAP_TYPE_SAKE, "SAKE");
 +      if (eap == NULL)
 +              return -1;
 +
 +      eap->init = eap_sake_init;
 +      eap->deinit = eap_sake_deinit;
 +      eap->process = eap_sake_process;
 +      eap->isKeyAvailable = eap_sake_isKeyAvailable;
 +      eap->getKey = eap_sake_getKey;
 +      eap->getSessionId = eap_sake_get_session_id;
 +      eap->get_emsk = eap_sake_get_emsk;
 +
 +      ret = eap_peer_method_register(eap);
 +      if (ret)
 +              eap_peer_method_free(eap);
 +      return ret;
 +}
index bd06df78db4c443b0a70b91cb35cce1530d75c64,0000000000000000000000000000000000000000..99a2816ce61edd30c0806feda1a7c05b44554a20
mode 100644,000000..100644
--- /dev/null
@@@ -1,1261 -1,0 +1,1261 @@@
-       if (pos == NULL || len < 1) {
 +/*
 + * EAP peer method: EAP-SIM (RFC 4186)
 + * Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi>
 + *
 + * This software may be distributed under the terms of the BSD license.
 + * See README for more details.
 + */
 +
 +#include "includes.h"
 +
 +#include "common.h"
 +#include "pcsc_funcs.h"
 +#include "crypto/milenage.h"
 +#include "crypto/random.h"
 +#include "eap_peer/eap_i.h"
 +#include "eap_config.h"
 +#include "eap_common/eap_sim_common.h"
 +
 +
 +struct eap_sim_data {
 +      u8 *ver_list;
 +      size_t ver_list_len;
 +      int selected_version;
 +      size_t min_num_chal, num_chal;
 +
 +      u8 kc[3][EAP_SIM_KC_LEN];
 +      u8 sres[3][EAP_SIM_SRES_LEN];
 +      u8 nonce_mt[EAP_SIM_NONCE_MT_LEN], nonce_s[EAP_SIM_NONCE_S_LEN];
 +      u8 mk[EAP_SIM_MK_LEN];
 +      u8 k_aut[EAP_SIM_K_AUT_LEN];
 +      u8 k_encr[EAP_SIM_K_ENCR_LEN];
 +      u8 msk[EAP_SIM_KEYING_DATA_LEN];
 +      u8 emsk[EAP_EMSK_LEN];
 +      u8 rand[3][GSM_RAND_LEN];
 +
 +      int num_id_req, num_notification;
 +      u8 *pseudonym;
 +      size_t pseudonym_len;
 +      u8 *reauth_id;
 +      size_t reauth_id_len;
 +      int reauth;
 +      unsigned int counter, counter_too_small;
 +      u8 *last_eap_identity;
 +      size_t last_eap_identity_len;
 +      enum {
 +              CONTINUE, RESULT_SUCCESS, SUCCESS, FAILURE
 +      } state;
 +      int result_ind, use_result_ind;
 +};
 +
 +
 +#ifndef CONFIG_NO_STDOUT_DEBUG
 +static const char * eap_sim_state_txt(int state)
 +{
 +      switch (state) {
 +      case CONTINUE:
 +              return "CONTINUE";
 +      case RESULT_SUCCESS:
 +              return "RESULT_SUCCESS";
 +      case SUCCESS:
 +              return "SUCCESS";
 +      case FAILURE:
 +              return "FAILURE";
 +      default:
 +              return "?";
 +      }
 +}
 +#endif /* CONFIG_NO_STDOUT_DEBUG */
 +
 +
 +static void eap_sim_state(struct eap_sim_data *data, int state)
 +{
 +      wpa_printf(MSG_DEBUG, "EAP-SIM: %s -> %s",
 +                 eap_sim_state_txt(data->state),
 +                 eap_sim_state_txt(state));
 +      data->state = state;
 +}
 +
 +
 +static void * eap_sim_init(struct eap_sm *sm)
 +{
 +      struct eap_sim_data *data;
 +      struct eap_peer_config *config = eap_get_config(sm);
 +
 +      data = os_zalloc(sizeof(*data));
 +      if (data == NULL)
 +              return NULL;
 +
 +      if (random_get_bytes(data->nonce_mt, EAP_SIM_NONCE_MT_LEN)) {
 +              wpa_printf(MSG_WARNING, "EAP-SIM: Failed to get random data "
 +                         "for NONCE_MT");
 +              os_free(data);
 +              return NULL;
 +      }
 +
 +      data->min_num_chal = 2;
 +      if (config && config->phase1) {
 +              char *pos = os_strstr(config->phase1, "sim_min_num_chal=");
 +              if (pos) {
 +                      data->min_num_chal = atoi(pos + 17);
 +                      if (data->min_num_chal < 2 || data->min_num_chal > 3) {
 +                              wpa_printf(MSG_WARNING, "EAP-SIM: Invalid "
 +                                         "sim_min_num_chal configuration "
 +                                         "(%lu, expected 2 or 3)",
 +                                         (unsigned long) data->min_num_chal);
 +                              os_free(data);
 +                              return NULL;
 +                      }
 +                      wpa_printf(MSG_DEBUG, "EAP-SIM: Set minimum number of "
 +                                 "challenges to %lu",
 +                                 (unsigned long) data->min_num_chal);
 +              }
 +
 +              data->result_ind = os_strstr(config->phase1, "result_ind=1") !=
 +                      NULL;
 +      }
 +
 +      if (config && config->anonymous_identity) {
 +              data->pseudonym = os_malloc(config->anonymous_identity_len);
 +              if (data->pseudonym) {
 +                      os_memcpy(data->pseudonym, config->anonymous_identity,
 +                                config->anonymous_identity_len);
 +                      data->pseudonym_len = config->anonymous_identity_len;
 +              }
 +      }
 +
 +      eap_sim_state(data, CONTINUE);
 +
 +      return data;
 +}
 +
 +
 +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;
 +      if (data) {
 +              os_free(data->ver_list);
 +              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;
 +
 +      wpa_printf(MSG_DEBUG, "EAP-SIM: GSM authentication algorithm");
 +
 +      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]) ||
 +                  scard_gsm_auth(sm->scard_ctx, data->rand[1],
 +                                 data->sres[1], data->kc[1]) ||
 +                  (data->num_chal > 2 &&
 +                   scard_gsm_auth(sm->scard_ctx, data->rand[2],
 +                                  data->sres[2], data->kc[2]))) {
 +                      wpa_printf(MSG_DEBUG, "EAP-SIM: GSM SIM "
 +                                 "authentication could not be completed");
 +                      return -1;
 +              }
 +              return 0;
 +      }
 +
 +#ifdef CONFIG_SIM_SIMULATOR
 +      if (conf->password) {
 +              u8 opc[16], k[16];
 +              const char *pos;
 +              size_t i;
 +              wpa_printf(MSG_DEBUG, "EAP-SIM: Use internal GSM-Milenage "
 +                         "implementation for authentication");
 +              if (conf->password_len < 65) {
 +                      wpa_printf(MSG_DEBUG, "EAP-SIM: invalid GSM-Milenage "
 +                                 "password");
 +                      return -1;
 +              }
 +              pos = (const char *) conf->password;
 +              if (hexstr2bin(pos, k, 16))
 +                      return -1;
 +              pos += 32;
 +              if (*pos != ':')
 +                      return -1;
 +              pos++;
 +
 +              if (hexstr2bin(pos, opc, 16))
 +                      return -1;
 +
 +              for (i = 0; i < data->num_chal; i++) {
 +                      if (gsm_milenage(opc, k, data->rand[i],
 +                                       data->sres[i], data->kc[i])) {
 +                              wpa_printf(MSG_DEBUG, "EAP-SIM: "
 +                                         "GSM-Milenage authentication "
 +                                         "could not be completed");
 +                              return -1;
 +                      }
 +                      wpa_hexdump(MSG_DEBUG, "EAP-SIM: RAND",
 +                                  data->rand[i], GSM_RAND_LEN);
 +                      wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: SRES",
 +                                      data->sres[i], EAP_SIM_SRES_LEN);
 +                      wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: Kc",
 +                                      data->kc[i], EAP_SIM_KC_LEN);
 +              }
 +              return 0;
 +      }
 +#endif /* CONFIG_SIM_SIMULATOR */
 +
 +#ifdef CONFIG_SIM_HARDCODED
 +      /* These hardcoded Kc and SRES values are used for testing. RAND to
 +       * KC/SREC mapping is very bogus as far as real authentication is
 +       * concerned, but it is quite useful for cases where the AS is rotating
 +       * the order of pre-configured values. */
 +      {
 +              size_t i;
 +
 +              wpa_printf(MSG_DEBUG, "EAP-SIM: Use hardcoded Kc and SRES "
 +                         "values for testing");
 +
 +              for (i = 0; i < data->num_chal; i++) {
 +                      if (data->rand[i][0] == 0xaa) {
 +                              os_memcpy(data->kc[i],
 +                                        "\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7",
 +                                        EAP_SIM_KC_LEN);
 +                              os_memcpy(data->sres[i], "\xd1\xd2\xd3\xd4",
 +                                        EAP_SIM_SRES_LEN);
 +                      } else if (data->rand[i][0] == 0xbb) {
 +                              os_memcpy(data->kc[i],
 +                                        "\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7",
 +                                        EAP_SIM_KC_LEN);
 +                              os_memcpy(data->sres[i], "\xe1\xe2\xe3\xe4",
 +                                        EAP_SIM_SRES_LEN);
 +                      } else {
 +                              os_memcpy(data->kc[i],
 +                                        "\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7",
 +                                        EAP_SIM_KC_LEN);
 +                              os_memcpy(data->sres[i], "\xf1\xf2\xf3\xf4",
 +                                        EAP_SIM_SRES_LEN);
 +                      }
 +              }
 +      }
 +
 +      return 0;
 +
 +#else /* CONFIG_SIM_HARDCODED */
 +
 +      wpa_printf(MSG_DEBUG, "EAP-SIM: No GSM authentication algorithm "
 +                 "enabled");
 +      return -1;
 +
 +#endif /* CONFIG_SIM_HARDCODED */
 +}
 +
 +
 +static int eap_sim_supported_ver(int version)
 +{
 +      return version == EAP_SIM_VERSION;
 +}
 +
 +
 +#define CLEAR_PSEUDONYM       0x01
 +#define CLEAR_REAUTH_ID       0x02
 +#define CLEAR_EAP_ID  0x04
 +
 +static void eap_sim_clear_identities(struct eap_sm *sm,
 +                                   struct eap_sim_data *data, int id)
 +{
 +      if ((id & CLEAR_PSEUDONYM) && data->pseudonym) {
 +              wpa_printf(MSG_DEBUG, "EAP-SIM: forgetting old pseudonym");
 +              os_free(data->pseudonym);
 +              data->pseudonym = NULL;
 +              data->pseudonym_len = 0;
 +              eap_set_anon_id(sm, NULL, 0);
 +      }
 +      if ((id & CLEAR_REAUTH_ID) && data->reauth_id) {
 +              wpa_printf(MSG_DEBUG, "EAP-SIM: forgetting old reauth_id");
 +              os_free(data->reauth_id);
 +              data->reauth_id = NULL;
 +              data->reauth_id_len = 0;
 +      }
 +      if ((id & CLEAR_EAP_ID) && data->last_eap_identity) {
 +              wpa_printf(MSG_DEBUG, "EAP-SIM: forgetting old eap_id");
 +              os_free(data->last_eap_identity);
 +              data->last_eap_identity = NULL;
 +              data->last_eap_identity_len = 0;
 +      }
 +}
 +
 +
 +static int eap_sim_learn_ids(struct eap_sm *sm, struct eap_sim_data *data,
 +                           struct eap_sim_attrs *attr)
 +{
 +      if (attr->next_pseudonym) {
 +              const u8 *identity = NULL;
 +              size_t identity_len = 0;
 +              const u8 *realm = NULL;
 +              size_t realm_len = 0;
 +
 +              wpa_hexdump_ascii(MSG_DEBUG,
 +                                "EAP-SIM: (encr) AT_NEXT_PSEUDONYM",
 +                                attr->next_pseudonym,
 +                                attr->next_pseudonym_len);
 +              os_free(data->pseudonym);
 +              /* Look for the realm of the permanent identity */
 +              identity = eap_get_config_identity(sm, &identity_len);
 +              if (identity) {
 +                      for (realm = identity, realm_len = identity_len;
 +                           realm_len > 0; realm_len--, realm++) {
 +                              if (*realm == '@')
 +                                      break;
 +                      }
 +              }
 +              data->pseudonym = os_malloc(attr->next_pseudonym_len +
 +                                          realm_len);
 +              if (data->pseudonym == NULL) {
 +                      wpa_printf(MSG_INFO, "EAP-SIM: (encr) No memory for "
 +                                 "next pseudonym");
 +                      data->pseudonym_len = 0;
 +                      return -1;
 +              }
 +              os_memcpy(data->pseudonym, attr->next_pseudonym,
 +                        attr->next_pseudonym_len);
 +              if (realm_len) {
 +                      os_memcpy(data->pseudonym + attr->next_pseudonym_len,
 +                                realm, realm_len);
 +              }
 +              data->pseudonym_len = attr->next_pseudonym_len + realm_len;
 +              eap_set_anon_id(sm, data->pseudonym, data->pseudonym_len);
 +      }
 +
 +      if (attr->next_reauth_id) {
 +              os_free(data->reauth_id);
 +              data->reauth_id = os_malloc(attr->next_reauth_id_len);
 +              if (data->reauth_id == NULL) {
 +                      wpa_printf(MSG_INFO, "EAP-SIM: (encr) No memory for "
 +                                 "next reauth_id");
 +                      data->reauth_id_len = 0;
 +                      return -1;
 +              }
 +              os_memcpy(data->reauth_id, attr->next_reauth_id,
 +                        attr->next_reauth_id_len);
 +              data->reauth_id_len = attr->next_reauth_id_len;
 +              wpa_hexdump_ascii(MSG_DEBUG,
 +                                "EAP-SIM: (encr) AT_NEXT_REAUTH_ID",
 +                                data->reauth_id,
 +                                data->reauth_id_len);
 +      }
 +
 +      return 0;
 +}
 +
 +
 +static struct wpabuf * eap_sim_client_error(struct eap_sim_data *data, u8 id,
 +                                          int err)
 +{
 +      struct eap_sim_msg *msg;
 +
 +      eap_sim_state(data, FAILURE);
 +      data->num_id_req = 0;
 +      data->num_notification = 0;
 +
 +      wpa_printf(MSG_DEBUG, "EAP-SIM: Send Client-Error (error code %d)",
 +                 err);
 +      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, EAP_TYPE_SIM, NULL, NULL, 0);
 +}
 +
 +
 +static struct wpabuf * eap_sim_response_start(struct eap_sm *sm,
 +                                            struct eap_sim_data *data, u8 id,
 +                                            enum eap_sim_id_req id_req)
 +{
 +      const u8 *identity = NULL;
 +      size_t identity_len = 0;
 +      struct eap_sim_msg *msg;
 +
 +      data->reauth = 0;
 +      if (id_req == ANY_ID && data->reauth_id) {
 +              identity = data->reauth_id;
 +              identity_len = data->reauth_id_len;
 +              data->reauth = 1;
 +      } else if ((id_req == ANY_ID || id_req == FULLAUTH_ID) &&
 +                 data->pseudonym) {
 +              identity = data->pseudonym;
 +              identity_len = data->pseudonym_len;
 +              eap_sim_clear_identities(sm, data, CLEAR_REAUTH_ID);
 +      } else if (id_req != NO_ID_REQ) {
 +              identity = eap_get_config_identity(sm, &identity_len);
 +              if (identity) {
 +                      eap_sim_clear_identities(sm, data, CLEAR_PSEUDONYM |
 +                                               CLEAR_REAUTH_ID);
 +              }
 +      }
 +      if (id_req != NO_ID_REQ)
 +              eap_sim_clear_identities(sm, data, CLEAR_EAP_ID);
 +
 +      wpa_printf(MSG_DEBUG, "Generating EAP-SIM Start (id=%d)", id);
 +      msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id,
 +                             EAP_TYPE_SIM, EAP_SIM_SUBTYPE_START);
 +      if (!data->reauth) {
 +              wpa_hexdump(MSG_DEBUG, "   AT_NONCE_MT",
 +                          data->nonce_mt, EAP_SIM_NONCE_MT_LEN);
 +              eap_sim_msg_add(msg, EAP_SIM_AT_NONCE_MT, 0,
 +                              data->nonce_mt, EAP_SIM_NONCE_MT_LEN);
 +              wpa_printf(MSG_DEBUG, "   AT_SELECTED_VERSION %d",
 +                         data->selected_version);
 +              eap_sim_msg_add(msg, EAP_SIM_AT_SELECTED_VERSION,
 +                              data->selected_version, NULL, 0);
 +      }
 +
 +      if (identity) {
 +              wpa_hexdump_ascii(MSG_DEBUG, "   AT_IDENTITY",
 +                                identity, identity_len);
 +              eap_sim_msg_add(msg, EAP_SIM_AT_IDENTITY, identity_len,
 +                              identity, identity_len);
 +      }
 +
 +      return eap_sim_msg_finish(msg, EAP_TYPE_SIM, NULL, NULL, 0);
 +}
 +
 +
 +static struct wpabuf * eap_sim_response_challenge(struct eap_sim_data *data,
 +                                                u8 id)
 +{
 +      struct eap_sim_msg *msg;
 +
 +      wpa_printf(MSG_DEBUG, "Generating EAP-SIM Challenge (id=%d)", id);
 +      msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, EAP_TYPE_SIM,
 +                             EAP_SIM_SUBTYPE_CHALLENGE);
 +      if (data->use_result_ind) {
 +              wpa_printf(MSG_DEBUG, "   AT_RESULT_IND");
 +              eap_sim_msg_add(msg, EAP_SIM_AT_RESULT_IND, 0, NULL, 0);
 +      }
 +      wpa_printf(MSG_DEBUG, "   AT_MAC");
 +      eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
 +      return eap_sim_msg_finish(msg, EAP_TYPE_SIM, data->k_aut,
 +                                (u8 *) data->sres,
 +                                data->num_chal * EAP_SIM_SRES_LEN);
 +}
 +
 +
 +static struct wpabuf * eap_sim_response_reauth(struct eap_sim_data *data,
 +                                             u8 id, int counter_too_small,
 +                                             const u8 *nonce_s)
 +{
 +      struct eap_sim_msg *msg;
 +      unsigned int counter;
 +
 +      wpa_printf(MSG_DEBUG, "Generating EAP-SIM Reauthentication (id=%d)",
 +                 id);
 +      msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, EAP_TYPE_SIM,
 +                             EAP_SIM_SUBTYPE_REAUTHENTICATION);
 +      wpa_printf(MSG_DEBUG, "   AT_IV");
 +      wpa_printf(MSG_DEBUG, "   AT_ENCR_DATA");
 +      eap_sim_msg_add_encr_start(msg, EAP_SIM_AT_IV, EAP_SIM_AT_ENCR_DATA);
 +
 +      if (counter_too_small) {
 +              wpa_printf(MSG_DEBUG, "   *AT_COUNTER_TOO_SMALL");
 +              eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER_TOO_SMALL, 0, NULL, 0);
 +              counter = data->counter_too_small;
 +      } else
 +              counter = data->counter;
 +
 +      wpa_printf(MSG_DEBUG, "   *AT_COUNTER %d", counter);
 +      eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER, counter, NULL, 0);
 +
 +      if (eap_sim_msg_add_encr_end(msg, data->k_encr, EAP_SIM_AT_PADDING)) {
 +              wpa_printf(MSG_WARNING, "EAP-SIM: Failed to encrypt "
 +                         "AT_ENCR_DATA");
 +              eap_sim_msg_free(msg);
 +              return NULL;
 +      }
 +      if (data->use_result_ind) {
 +              wpa_printf(MSG_DEBUG, "   AT_RESULT_IND");
 +              eap_sim_msg_add(msg, EAP_SIM_AT_RESULT_IND, 0, NULL, 0);
 +      }
 +      wpa_printf(MSG_DEBUG, "   AT_MAC");
 +      eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
 +      return eap_sim_msg_finish(msg, EAP_TYPE_SIM, data->k_aut, nonce_s,
 +                                EAP_SIM_NONCE_S_LEN);
 +}
 +
 +
 +static struct wpabuf * eap_sim_response_notification(struct eap_sim_data *data,
 +                                                   u8 id, u16 notification)
 +{
 +      struct eap_sim_msg *msg;
 +      u8 *k_aut = (notification & 0x4000) == 0 ? data->k_aut : NULL;
 +
 +      wpa_printf(MSG_DEBUG, "Generating EAP-SIM Notification (id=%d)", id);
 +      msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id,
 +                             EAP_TYPE_SIM, EAP_SIM_SUBTYPE_NOTIFICATION);
 +      if (k_aut && data->reauth) {
 +              wpa_printf(MSG_DEBUG, "   AT_IV");
 +              wpa_printf(MSG_DEBUG, "   AT_ENCR_DATA");
 +              eap_sim_msg_add_encr_start(msg, EAP_SIM_AT_IV,
 +                                         EAP_SIM_AT_ENCR_DATA);
 +              wpa_printf(MSG_DEBUG, "   *AT_COUNTER %d", data->counter);
 +              eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER, data->counter,
 +                              NULL, 0);
 +              if (eap_sim_msg_add_encr_end(msg, data->k_encr,
 +                                           EAP_SIM_AT_PADDING)) {
 +                      wpa_printf(MSG_WARNING, "EAP-SIM: Failed to encrypt "
 +                                 "AT_ENCR_DATA");
 +                      eap_sim_msg_free(msg);
 +                      return NULL;
 +              }
 +      }
 +      if (k_aut) {
 +              wpa_printf(MSG_DEBUG, "   AT_MAC");
 +              eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
 +      }
 +      return eap_sim_msg_finish(msg, EAP_TYPE_SIM, k_aut, (u8 *) "", 0);
 +}
 +
 +
 +static struct wpabuf * eap_sim_process_start(struct eap_sm *sm,
 +                                           struct eap_sim_data *data, u8 id,
 +                                           struct eap_sim_attrs *attr)
 +{
 +      int selected_version = -1, id_error;
 +      size_t i;
 +      u8 *pos;
 +
 +      wpa_printf(MSG_DEBUG, "EAP-SIM: subtype Start");
 +      if (attr->version_list == NULL) {
 +              wpa_printf(MSG_INFO, "EAP-SIM: No AT_VERSION_LIST in "
 +                         "SIM/Start");
 +              return eap_sim_client_error(data, id,
 +                                          EAP_SIM_UNSUPPORTED_VERSION);
 +      }
 +
 +      os_free(data->ver_list);
 +      data->ver_list = os_malloc(attr->version_list_len);
 +      if (data->ver_list == NULL) {
 +              wpa_printf(MSG_DEBUG, "EAP-SIM: Failed to allocate "
 +                         "memory for version list");
 +              return eap_sim_client_error(data, id,
 +                                          EAP_SIM_UNABLE_TO_PROCESS_PACKET);
 +      }
 +      os_memcpy(data->ver_list, attr->version_list, attr->version_list_len);
 +      data->ver_list_len = attr->version_list_len;
 +      pos = data->ver_list;
 +      for (i = 0; i < data->ver_list_len / 2; i++) {
 +              int ver = pos[0] * 256 + pos[1];
 +              pos += 2;
 +              if (eap_sim_supported_ver(ver)) {
 +                      selected_version = ver;
 +                      break;
 +              }
 +      }
 +      if (selected_version < 0) {
 +              wpa_printf(MSG_INFO, "EAP-SIM: Could not find a supported "
 +                         "version");
 +              return eap_sim_client_error(data, id,
 +                                          EAP_SIM_UNSUPPORTED_VERSION);
 +      }
 +      wpa_printf(MSG_DEBUG, "EAP-SIM: Selected Version %d",
 +                 selected_version);
 +      data->selected_version = selected_version;
 +
 +      id_error = 0;
 +      switch (attr->id_req) {
 +      case NO_ID_REQ:
 +              break;
 +      case ANY_ID:
 +              if (data->num_id_req > 0)
 +                      id_error++;
 +              data->num_id_req++;
 +              break;
 +      case FULLAUTH_ID:
 +              if (data->num_id_req > 1)
 +                      id_error++;
 +              data->num_id_req++;
 +              break;
 +      case PERMANENT_ID:
 +              if (data->num_id_req > 2)
 +                      id_error++;
 +              data->num_id_req++;
 +              break;
 +      }
 +      if (id_error) {
 +              wpa_printf(MSG_INFO, "EAP-SIM: Too many ID requests "
 +                         "used within one authentication");
 +              return eap_sim_client_error(data, id,
 +                                          EAP_SIM_UNABLE_TO_PROCESS_PACKET);
 +      }
 +
 +      return eap_sim_response_start(sm, data, id, attr->id_req);
 +}
 +
 +
 +static struct wpabuf * eap_sim_process_challenge(struct eap_sm *sm,
 +                                               struct eap_sim_data *data,
 +                                               u8 id,
 +                                               const struct wpabuf *reqData,
 +                                               struct eap_sim_attrs *attr)
 +{
 +      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;
 +      if (!attr->mac || !attr->rand) {
 +              wpa_printf(MSG_WARNING, "EAP-SIM: Challenge message "
 +                         "did not include%s%s",
 +                         !attr->mac ? " AT_MAC" : "",
 +                         !attr->rand ? " AT_RAND" : "");
 +              return eap_sim_client_error(data, id,
 +                                          EAP_SIM_UNABLE_TO_PROCESS_PACKET);
 +      }
 +
 +      wpa_printf(MSG_DEBUG, "EAP-SIM: %lu challenges",
 +                 (unsigned long) attr->num_chal);
 +      if (attr->num_chal < data->min_num_chal) {
 +              wpa_printf(MSG_INFO, "EAP-SIM: Insufficient number of "
 +                         "challenges (%lu)", (unsigned long) attr->num_chal);
 +              return eap_sim_client_error(data, id,
 +                                          EAP_SIM_INSUFFICIENT_NUM_OF_CHAL);
 +      }
 +      if (attr->num_chal > 3) {
 +              wpa_printf(MSG_INFO, "EAP-SIM: Too many challenges "
 +                         "(%lu)", (unsigned long) attr->num_chal);
 +              return eap_sim_client_error(data, id,
 +                                          EAP_SIM_UNABLE_TO_PROCESS_PACKET);
 +      }
 +
 +      /* Verify that RANDs are different */
 +      if (os_memcmp(attr->rand, attr->rand + GSM_RAND_LEN,
 +                 GSM_RAND_LEN) == 0 ||
 +          (attr->num_chal > 2 &&
 +           (os_memcmp(attr->rand, attr->rand + 2 * GSM_RAND_LEN,
 +                      GSM_RAND_LEN) == 0 ||
 +            os_memcmp(attr->rand + GSM_RAND_LEN,
 +                      attr->rand + 2 * GSM_RAND_LEN,
 +                      GSM_RAND_LEN) == 0))) {
 +              wpa_printf(MSG_INFO, "EAP-SIM: Same RAND used multiple times");
 +              return eap_sim_client_error(data, id,
 +                                          EAP_SIM_RAND_NOT_FRESH);
 +      }
 +
 +      os_memcpy(data->rand, attr->rand, attr->num_chal * GSM_RAND_LEN);
 +      data->num_chal = attr->num_chal;
 +
 +      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);
 +      }
 +      if (data->last_eap_identity) {
 +              identity = data->last_eap_identity;
 +              identity_len = data->last_eap_identity_len;
 +      } else if (data->pseudonym) {
 +              identity = data->pseudonym;
 +              identity_len = data->pseudonym_len;
 +      } else
 +              identity = eap_get_config_identity(sm, &identity_len);
 +      wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM: Selected identity for MK "
 +                        "derivation", identity, identity_len);
 +      eap_sim_derive_mk(identity, identity_len, data->nonce_mt,
 +                        data->selected_version, data->ver_list,
 +                        data->ver_list_len, data->num_chal,
 +                        (const u8 *) data->kc, data->mk);
 +      eap_sim_derive_keys(data->mk, data->k_encr, data->k_aut, data->msk,
 +                          data->emsk);
 +      if (eap_sim_verify_mac(data->k_aut, reqData, attr->mac, data->nonce_mt,
 +                             EAP_SIM_NONCE_MT_LEN)) {
 +              wpa_printf(MSG_WARNING, "EAP-SIM: Challenge message "
 +                         "used invalid AT_MAC");
 +              return eap_sim_client_error(data, id,
 +                                          EAP_SIM_UNABLE_TO_PROCESS_PACKET);
 +      }
 +
 +      /* Old reauthentication identity must not be used anymore. In
 +       * other words, if no new reauth identity is received, full
 +       * authentication will be used on next reauthentication (using
 +       * pseudonym identity or permanent identity). */
 +      eap_sim_clear_identities(sm, data, CLEAR_REAUTH_ID | CLEAR_EAP_ID);
 +
 +      if (attr->encr_data) {
 +              u8 *decrypted;
 +              decrypted = eap_sim_parse_encr(data->k_encr, attr->encr_data,
 +                                             attr->encr_data_len, attr->iv,
 +                                             &eattr, 0);
 +              if (decrypted == NULL) {
 +                      return eap_sim_client_error(
 +                              data, id, EAP_SIM_UNABLE_TO_PROCESS_PACKET);
 +              }
 +              eap_sim_learn_ids(sm, data, &eattr);
 +              os_free(decrypted);
 +      }
 +
 +      if (data->result_ind && attr->result_ind)
 +              data->use_result_ind = 1;
 +
 +      if (data->state != FAILURE) {
 +              eap_sim_state(data, data->use_result_ind ?
 +                            RESULT_SUCCESS : SUCCESS);
 +      }
 +
 +      data->num_id_req = 0;
 +      data->num_notification = 0;
 +      /* RFC 4186 specifies that counter is initialized to one after
 +       * fullauth, but initializing it to zero makes it easier to implement
 +       * reauth verification. */
 +      data->counter = 0;
 +      return eap_sim_response_challenge(data, id);
 +}
 +
 +
 +static int eap_sim_process_notification_reauth(struct eap_sim_data *data,
 +                                             struct eap_sim_attrs *attr)
 +{
 +      struct eap_sim_attrs eattr;
 +      u8 *decrypted;
 +
 +      if (attr->encr_data == NULL || attr->iv == NULL) {
 +              wpa_printf(MSG_WARNING, "EAP-SIM: Notification message after "
 +                         "reauth did not include encrypted data");
 +              return -1;
 +      }
 +
 +      decrypted = eap_sim_parse_encr(data->k_encr, attr->encr_data,
 +                                     attr->encr_data_len, attr->iv, &eattr,
 +                                     0);
 +      if (decrypted == NULL) {
 +              wpa_printf(MSG_WARNING, "EAP-SIM: Failed to parse encrypted "
 +                         "data from notification message");
 +              return -1;
 +      }
 +
 +      if (eattr.counter < 0 || (size_t) eattr.counter != data->counter) {
 +              wpa_printf(MSG_WARNING, "EAP-SIM: Counter in notification "
 +                         "message does not match with counter in reauth "
 +                         "message");
 +              os_free(decrypted);
 +              return -1;
 +      }
 +
 +      os_free(decrypted);
 +      return 0;
 +}
 +
 +
 +static int eap_sim_process_notification_auth(struct eap_sim_data *data,
 +                                           const struct wpabuf *reqData,
 +                                           struct eap_sim_attrs *attr)
 +{
 +      if (attr->mac == NULL) {
 +              wpa_printf(MSG_INFO, "EAP-SIM: no AT_MAC in after_auth "
 +                         "Notification message");
 +              return -1;
 +      }
 +
 +      if (eap_sim_verify_mac(data->k_aut, reqData, attr->mac, (u8 *) "", 0))
 +      {
 +              wpa_printf(MSG_WARNING, "EAP-SIM: Notification message "
 +                         "used invalid AT_MAC");
 +              return -1;
 +      }
 +
 +      if (data->reauth &&
 +          eap_sim_process_notification_reauth(data, attr)) {
 +              wpa_printf(MSG_WARNING, "EAP-SIM: Invalid notification "
 +                         "message after reauth");
 +              return -1;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +static struct wpabuf * eap_sim_process_notification(
 +      struct eap_sm *sm, struct eap_sim_data *data, u8 id,
 +      const struct wpabuf *reqData, struct eap_sim_attrs *attr)
 +{
 +      wpa_printf(MSG_DEBUG, "EAP-SIM: subtype Notification");
 +      if (data->num_notification > 0) {
 +              wpa_printf(MSG_INFO, "EAP-SIM: too many notification "
 +                         "rounds (only one allowed)");
 +              return eap_sim_client_error(data, id,
 +                                          EAP_SIM_UNABLE_TO_PROCESS_PACKET);
 +      }
 +      data->num_notification++;
 +      if (attr->notification == -1) {
 +              wpa_printf(MSG_INFO, "EAP-SIM: no AT_NOTIFICATION in "
 +                         "Notification message");
 +              return eap_sim_client_error(data, id,
 +                                          EAP_SIM_UNABLE_TO_PROCESS_PACKET);
 +      }
 +
 +      if ((attr->notification & 0x4000) == 0 &&
 +          eap_sim_process_notification_auth(data, reqData, attr)) {
 +              return eap_sim_client_error(data, id,
 +                                          EAP_SIM_UNABLE_TO_PROCESS_PACKET);
 +      }
 +
 +      eap_sim_report_notification(sm->msg_ctx, attr->notification, 0);
 +      if (attr->notification >= 0 && attr->notification < 32768) {
 +              eap_sim_state(data, FAILURE);
 +      } else if (attr->notification == EAP_SIM_SUCCESS &&
 +                 data->state == RESULT_SUCCESS)
 +              eap_sim_state(data, SUCCESS);
 +      return eap_sim_response_notification(data, id, attr->notification);
 +}
 +
 +
 +static struct wpabuf * eap_sim_process_reauthentication(
 +      struct eap_sm *sm, struct eap_sim_data *data, u8 id,
 +      const struct wpabuf *reqData, struct eap_sim_attrs *attr)
 +{
 +      struct eap_sim_attrs eattr;
 +      u8 *decrypted;
 +
 +      wpa_printf(MSG_DEBUG, "EAP-SIM: subtype Reauthentication");
 +
 +      if (data->reauth_id == NULL) {
 +              wpa_printf(MSG_WARNING, "EAP-SIM: Server is trying "
 +                         "reauthentication, but no reauth_id available");
 +              return eap_sim_client_error(data, id,
 +                                          EAP_SIM_UNABLE_TO_PROCESS_PACKET);
 +      }
 +
 +      data->reauth = 1;
 +      if (eap_sim_verify_mac(data->k_aut, reqData, attr->mac, (u8 *) "", 0))
 +      {
 +              wpa_printf(MSG_WARNING, "EAP-SIM: Reauthentication "
 +                         "did not have valid AT_MAC");
 +              return eap_sim_client_error(data, id,
 +                                          EAP_SIM_UNABLE_TO_PROCESS_PACKET);
 +      }
 +
 +      if (attr->encr_data == NULL || attr->iv == NULL) {
 +              wpa_printf(MSG_WARNING, "EAP-SIM: Reauthentication "
 +                         "message did not include encrypted data");
 +              return eap_sim_client_error(data, id,
 +                                          EAP_SIM_UNABLE_TO_PROCESS_PACKET);
 +      }
 +
 +      decrypted = eap_sim_parse_encr(data->k_encr, attr->encr_data,
 +                                     attr->encr_data_len, attr->iv, &eattr,
 +                                     0);
 +      if (decrypted == NULL) {
 +              wpa_printf(MSG_WARNING, "EAP-SIM: Failed to parse encrypted "
 +                         "data from reauthentication message");
 +              return eap_sim_client_error(data, id,
 +                                          EAP_SIM_UNABLE_TO_PROCESS_PACKET);
 +      }
 +
 +      if (eattr.nonce_s == NULL || eattr.counter < 0) {
 +              wpa_printf(MSG_INFO, "EAP-SIM: (encr) No%s%s in reauth packet",
 +                         !eattr.nonce_s ? " AT_NONCE_S" : "",
 +                         eattr.counter < 0 ? " AT_COUNTER" : "");
 +              os_free(decrypted);
 +              return eap_sim_client_error(data, id,
 +                                          EAP_SIM_UNABLE_TO_PROCESS_PACKET);
 +      }
 +
 +      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
 +               * packet, it has to saved for the following fullauth to be
 +               * used in MK derivation. */
 +              os_free(data->last_eap_identity);
 +              data->last_eap_identity = data->reauth_id;
 +              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 res;
 +      }
 +      data->counter = eattr.counter;
 +
 +      os_memcpy(data->nonce_s, eattr.nonce_s, EAP_SIM_NONCE_S_LEN);
 +      wpa_hexdump(MSG_DEBUG, "EAP-SIM: (encr) AT_NONCE_S",
 +                  data->nonce_s, EAP_SIM_NONCE_S_LEN);
 +
 +      eap_sim_derive_keys_reauth(data->counter,
 +                                 data->reauth_id, data->reauth_id_len,
 +                                 data->nonce_s, data->mk, data->msk,
 +                                 data->emsk);
 +      eap_sim_clear_identities(sm, data, CLEAR_REAUTH_ID | CLEAR_EAP_ID);
 +      eap_sim_learn_ids(sm, data, &eattr);
 +
 +      if (data->result_ind && attr->result_ind)
 +              data->use_result_ind = 1;
 +
 +      if (data->state != FAILURE) {
 +              eap_sim_state(data, data->use_result_ind ?
 +                            RESULT_SUCCESS : SUCCESS);
 +      }
 +
 +      data->num_id_req = 0;
 +      data->num_notification = 0;
 +      if (data->counter > EAP_SIM_MAX_FAST_REAUTHS) {
 +              wpa_printf(MSG_DEBUG, "EAP-SIM: Maximum number of "
 +                         "fast reauths performed - force fullauth");
 +              eap_sim_clear_identities(sm, data,
 +                                       CLEAR_REAUTH_ID | CLEAR_EAP_ID);
 +      }
 +      os_free(decrypted);
 +      return eap_sim_response_reauth(data, id, 0, data->nonce_s);
 +}
 +
 +
 +static struct wpabuf * eap_sim_process(struct eap_sm *sm, void *priv,
 +                                     struct eap_method_ret *ret,
 +                                     const struct wpabuf *reqData)
 +{
 +      struct eap_sim_data *data = priv;
 +      const struct eap_hdr *req;
 +      u8 subtype, id;
 +      struct wpabuf *res;
 +      const u8 *pos;
 +      struct eap_sim_attrs attr;
 +      size_t len;
 +
 +      wpa_hexdump_buf(MSG_DEBUG, "EAP-SIM: EAP data", reqData);
 +      if (eap_get_config_identity(sm, &len) == NULL) {
 +              wpa_printf(MSG_INFO, "EAP-SIM: Identity not configured");
 +              eap_sm_request_identity(sm);
 +              ret->ignore = TRUE;
 +              return NULL;
 +      }
 +
 +      pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_SIM, reqData, &len);
++      if (pos == NULL || len < 3) {
 +              ret->ignore = TRUE;
 +              return NULL;
 +      }
 +      req = wpabuf_head(reqData);
 +      id = req->identifier;
 +      len = be_to_host16(req->length);
 +
 +      ret->ignore = FALSE;
 +      ret->methodState = METHOD_MAY_CONT;
 +      ret->decision = DECISION_FAIL;
 +      ret->allowNotifications = TRUE;
 +
 +      subtype = *pos++;
 +      wpa_printf(MSG_DEBUG, "EAP-SIM: Subtype=%d", subtype);
 +      pos += 2; /* Reserved */
 +
 +      if (eap_sim_parse_attr(pos, wpabuf_head_u8(reqData) + len, &attr, 0,
 +                             0)) {
 +              res = eap_sim_client_error(data, id,
 +                                         EAP_SIM_UNABLE_TO_PROCESS_PACKET);
 +              goto done;
 +      }
 +
 +      switch (subtype) {
 +      case EAP_SIM_SUBTYPE_START:
 +              res = eap_sim_process_start(sm, data, id, &attr);
 +              break;
 +      case EAP_SIM_SUBTYPE_CHALLENGE:
 +              res = eap_sim_process_challenge(sm, data, id, reqData, &attr);
 +              break;
 +      case EAP_SIM_SUBTYPE_NOTIFICATION:
 +              res = eap_sim_process_notification(sm, data, id, reqData,
 +                                                 &attr);
 +              break;
 +      case EAP_SIM_SUBTYPE_REAUTHENTICATION:
 +              res = eap_sim_process_reauthentication(sm, data, id, reqData,
 +                                                     &attr);
 +              break;
 +      case EAP_SIM_SUBTYPE_CLIENT_ERROR:
 +              wpa_printf(MSG_DEBUG, "EAP-SIM: subtype Client-Error");
 +              res = eap_sim_client_error(data, id,
 +                                         EAP_SIM_UNABLE_TO_PROCESS_PACKET);
 +              break;
 +      default:
 +              wpa_printf(MSG_DEBUG, "EAP-SIM: Unknown subtype=%d", subtype);
 +              res = eap_sim_client_error(data, id,
 +                                         EAP_SIM_UNABLE_TO_PROCESS_PACKET);
 +              break;
 +      }
 +
 +done:
 +      if (data->state == FAILURE) {
 +              ret->decision = DECISION_FAIL;
 +              ret->methodState = METHOD_DONE;
 +      } else if (data->state == SUCCESS) {
 +              ret->decision = data->use_result_ind ?
 +                      DECISION_UNCOND_SUCC : DECISION_COND_SUCC;
 +              ret->methodState = data->use_result_ind ?
 +                      METHOD_DONE : METHOD_MAY_CONT;
 +      } else if (data->state == RESULT_SUCCESS)
 +              ret->methodState = METHOD_CONT;
 +
 +      if (ret->methodState == METHOD_DONE) {
 +              ret->allowNotifications = FALSE;
 +      }
 +
 +      return res;
 +}
 +
 +
 +static Boolean eap_sim_has_reauth_data(struct eap_sm *sm, void *priv)
 +{
 +      struct eap_sim_data *data = priv;
 +      return data->pseudonym || data->reauth_id;
 +}
 +
 +
 +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);
 +}
 +
 +
 +static void * eap_sim_init_for_reauth(struct eap_sm *sm, void *priv)
 +{
 +      struct eap_sim_data *data = priv;
 +      if (random_get_bytes(data->nonce_mt, EAP_SIM_NONCE_MT_LEN)) {
 +              wpa_printf(MSG_WARNING, "EAP-SIM: Failed to get random data "
 +                         "for NONCE_MT");
 +              os_free(data);
 +              return NULL;
 +      }
 +      data->num_id_req = 0;
 +      data->num_notification = 0;
 +      eap_sim_state(data, CONTINUE);
 +      return priv;
 +}
 +
 +
 +static const u8 * eap_sim_get_identity(struct eap_sm *sm, void *priv,
 +                                     size_t *len)
 +{
 +      struct eap_sim_data *data = priv;
 +
 +      if (data->reauth_id) {
 +              *len = data->reauth_id_len;
 +              return data->reauth_id;
 +      }
 +
 +      if (data->pseudonym) {
 +              *len = data->pseudonym_len;
 +              return data->pseudonym;
 +      }
 +
 +      return NULL;
 +}
 +
 +
 +static Boolean eap_sim_isKeyAvailable(struct eap_sm *sm, void *priv)
 +{
 +      struct eap_sim_data *data = priv;
 +      return data->state == SUCCESS;
 +}
 +
 +
 +static u8 * eap_sim_getKey(struct eap_sm *sm, void *priv, size_t *len)
 +{
 +      struct eap_sim_data *data = priv;
 +      u8 *key;
 +
 +      if (data->state != SUCCESS)
 +              return NULL;
 +
 +      key = os_malloc(EAP_SIM_KEYING_DATA_LEN);
 +      if (key == NULL)
 +              return NULL;
 +
 +      *len = EAP_SIM_KEYING_DATA_LEN;
 +      os_memcpy(key, data->msk, EAP_SIM_KEYING_DATA_LEN);
 +
 +      return key;
 +}
 +
 +
 +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;
 +}
 +
 +
 +static u8 * eap_sim_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
 +{
 +      struct eap_sim_data *data = priv;
 +      u8 *key;
 +
 +      if (data->state != SUCCESS)
 +              return NULL;
 +
 +      key = os_malloc(EAP_EMSK_LEN);
 +      if (key == NULL)
 +              return NULL;
 +
 +      *len = EAP_EMSK_LEN;
 +      os_memcpy(key, data->emsk, EAP_EMSK_LEN);
 +
 +      return key;
 +}
 +
 +
 +int eap_peer_sim_register(void)
 +{
 +      struct eap_method *eap;
 +      int ret;
 +
 +      eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
 +                                  EAP_VENDOR_IETF, EAP_TYPE_SIM, "SIM");
 +      if (eap == NULL)
 +              return -1;
 +
 +      eap->init = eap_sim_init;
 +      eap->deinit = eap_sim_deinit;
 +      eap->process = eap_sim_process;
 +      eap->isKeyAvailable = eap_sim_isKeyAvailable;
 +      eap->getKey = eap_sim_getKey;
 +      eap->getSessionId = eap_sim_get_session_id;
 +      eap->has_reauth_data = eap_sim_has_reauth_data;
 +      eap->deinit_for_reauth = eap_sim_deinit_for_reauth;
 +      eap->init_for_reauth = eap_sim_init_for_reauth;
 +      eap->get_identity = eap_sim_get_identity;
 +      eap->get_emsk = eap_sim_get_emsk;
 +
 +      ret = eap_peer_method_register(eap);
 +      if (ret)
 +              eap_peer_method_free(eap);
 +      return ret;
 +}
index 5aa3fd5912563570b63315713e7d12e86fd02b02,0000000000000000000000000000000000000000..66a027a626e0dba5cac6f1673689ccca25d58c69
mode 100644,000000..100644
--- /dev/null
@@@ -1,451 -1,0 +1,439 @@@
-       if (res == -1) {
-               struct eap_peer_config *config = eap_get_config(sm);
-               if (config) {
-                       /*
-                        * The TLS handshake failed. So better forget the old
-                        * PIN. It may be wrong, we cannot be sure but trying
-                        * the wrong one again might block it on the card--so
-                        * better ask the user again.
-                        */
-                       os_free(config->pin);
-                       config->pin = NULL;
-               }
-       }
 +/*
 + * EAP peer method: EAP-TLS (RFC 2716)
 + * Copyright (c) 2004-2008, 2012, Jouni Malinen <j@w1.fi>
 + *
 + * This software may be distributed under the terms of the BSD license.
 + * See README for more details.
 + */
 +
 +#include "includes.h"
 +
 +#include "common.h"
 +#include "crypto/tls.h"
 +#include "eap_i.h"
 +#include "eap_tls_common.h"
 +#include "eap_config.h"
 +
 +
 +static void eap_tls_deinit(struct eap_sm *sm, void *priv);
 +
 +
 +struct eap_tls_data {
 +      struct eap_ssl_data ssl;
 +      u8 *key_data;
 +      u8 *session_id;
 +      size_t id_len;
 +      void *ssl_ctx;
 +      u8 eap_type;
 +};
 +
 +
 +static void * eap_tls_init(struct eap_sm *sm)
 +{
 +      struct eap_tls_data *data;
 +      struct eap_peer_config *config = eap_get_config(sm);
 +      if (config == NULL ||
 +          ((sm->init_phase2 ? config->private_key2 : config->private_key)
 +           == NULL &&
 +           (sm->init_phase2 ? config->engine2 : config->engine) == 0)) {
 +              wpa_printf(MSG_INFO, "EAP-TLS: Private key not configured");
 +              return NULL;
 +      }
 +
 +      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_TYPE_TLS)) {
 +              wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL.");
 +              eap_tls_deinit(sm, data);
 +              if (config->engine) {
 +                      wpa_printf(MSG_DEBUG, "EAP-TLS: Requesting Smartcard "
 +                                 "PIN");
 +                      eap_sm_request_pin(sm);
 +                      sm->ignore = TRUE;
 +              } else if (config->private_key && !config->private_key_passwd)
 +              {
 +                      wpa_printf(MSG_DEBUG, "EAP-TLS: Requesting private "
 +                                 "key passphrase");
 +                      eap_sm_request_passphrase(sm);
 +                      sm->ignore = TRUE;
 +              }
 +              return NULL;
 +      }
 +
 +      data->eap_type = EAP_TYPE_TLS;
 +
 +      return data;
 +}
 +
 +
 +#ifdef EAP_UNAUTH_TLS
 +static void * eap_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_UNAUTH_TLS_TYPE)) {
 +              wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL.");
 +              eap_tls_deinit(sm, data);
 +              return NULL;
 +      }
 +
 +      data->eap_type = EAP_UNAUTH_TLS_TYPE;
 +
 +      return data;
 +}
 +#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);
 +      eap_tls_free_key(data);
 +      os_free(data->session_id);
 +      os_free(data);
 +}
 +
 +
 +static struct wpabuf * eap_tls_failure(struct eap_sm *sm,
 +                                     struct eap_tls_data *data,
 +                                     struct eap_method_ret *ret, int res,
 +                                     struct wpabuf *resp, u8 id)
 +{
 +      wpa_printf(MSG_DEBUG, "EAP-TLS: TLS processing failed");
 +
 +      ret->methodState = METHOD_DONE;
 +      ret->decision = DECISION_FAIL;
 +
-                                         id, pos, left, &resp);
 +      if (resp) {
 +              /*
 +               * This is likely an alert message, so send it instead of just
 +               * ACKing the error.
 +               */
 +              return resp;
 +      }
 +
 +      return eap_peer_tls_build_ack(id, data->eap_type, 0);
 +}
 +
 +
 +static void eap_tls_success(struct eap_sm *sm, struct eap_tls_data *data,
 +                          struct eap_method_ret *ret)
 +{
 +      wpa_printf(MSG_DEBUG, "EAP-TLS: Done");
 +
 +      ret->methodState = METHOD_DONE;
 +      ret->decision = DECISION_UNCOND_SUCC;
 +
 +      eap_tls_free_key(data);
 +      data->key_data = eap_peer_tls_derive_key(sm, &data->ssl,
 +                                               "client EAP encryption",
 +                                               EAP_TLS_KEY_LEN +
 +                                               EAP_EMSK_LEN);
 +      if (data->key_data) {
 +              wpa_hexdump_key(MSG_DEBUG, "EAP-TLS: Derived key",
 +                              data->key_data, EAP_TLS_KEY_LEN);
 +              wpa_hexdump_key(MSG_DEBUG, "EAP-TLS: Derived EMSK",
 +                              data->key_data + EAP_TLS_KEY_LEN,
 +                              EAP_EMSK_LEN);
 +      } else {
 +              wpa_printf(MSG_INFO, "EAP-TLS: Failed to derive key");
 +      }
 +
 +      os_free(data->session_id);
 +      data->session_id = eap_peer_tls_derive_session_id(sm, &data->ssl,
 +                                                        EAP_TYPE_TLS,
 +                                                        &data->id_len);
 +      if (data->session_id) {
 +              wpa_hexdump(MSG_DEBUG, "EAP-TLS: Derived Session-Id",
 +                          data->session_id, data->id_len);
 +      } else {
 +              wpa_printf(MSG_ERROR, "EAP-TLS: Failed to derive Session-Id");
 +      }
 +}
 +
 +
 +static struct wpabuf * eap_tls_process(struct eap_sm *sm, void *priv,
 +                                     struct eap_method_ret *ret,
 +                                     const struct wpabuf *reqData)
 +{
 +      size_t left;
 +      int res;
 +      struct wpabuf *resp;
 +      u8 flags, id;
 +      const u8 *pos;
 +      struct eap_tls_data *data = priv;
++      struct wpabuf msg;
 +
 +      pos = eap_peer_tls_process_init(sm, &data->ssl, data->eap_type, ret,
 +                                      reqData, &left, &flags);
 +      if (pos == NULL)
 +              return NULL;
 +      id = eap_get_id(reqData);
 +
 +      if (flags & EAP_TLS_FLAGS_START) {
 +              wpa_printf(MSG_DEBUG, "EAP-TLS: Start");
 +              left = 0; /* make sure that this frame is empty, even though it
 +                         * should always be, anyway */
 +      }
 +
 +      resp = NULL;
++      wpabuf_set(&msg, pos, left);
 +      res = eap_peer_tls_process_helper(sm, &data->ssl, data->eap_type, 0,
++                                        id, &msg, &resp);
 +
 +      if (res < 0) {
 +              return eap_tls_failure(sm, data, ret, res, resp, id);
 +      }
 +
 +      if (tls_connection_established(data->ssl_ctx, data->ssl.conn))
 +              eap_tls_success(sm, data, ret);
 +
 +      if (res == 1) {
 +              wpabuf_free(resp);
 +              return eap_peer_tls_build_ack(id, data->eap_type, 0);
 +      }
 +
 +      return resp;
 +}
 +
 +
 +static Boolean eap_tls_has_reauth_data(struct eap_sm *sm, void *priv)
 +{
 +      struct eap_tls_data *data = priv;
 +      return tls_connection_established(data->ssl_ctx, data->ssl.conn);
 +}
 +
 +
 +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;
 +      eap_tls_free_key(data);
 +      os_free(data->session_id);
 +      data->session_id = NULL;
 +      if (eap_peer_tls_reauth_init(sm, &data->ssl)) {
 +              os_free(data);
 +              return NULL;
 +      }
 +      return priv;
 +}
 +
 +
 +static int eap_tls_get_status(struct eap_sm *sm, void *priv, char *buf,
 +                            size_t buflen, int verbose)
 +{
 +      struct eap_tls_data *data = priv;
 +      return eap_peer_tls_status(sm, &data->ssl, buf, buflen, verbose);
 +}
 +
 +
 +static Boolean eap_tls_isKeyAvailable(struct eap_sm *sm, void *priv)
 +{
 +      struct eap_tls_data *data = priv;
 +      return data->key_data != NULL;
 +}
 +
 +
 +static u8 * eap_tls_getKey(struct eap_sm *sm, void *priv, size_t *len)
 +{
 +      struct eap_tls_data *data = priv;
 +      u8 *key;
 +
 +      if (data->key_data == NULL)
 +              return NULL;
 +
 +      key = os_malloc(EAP_TLS_KEY_LEN);
 +      if (key == NULL)
 +              return NULL;
 +
 +      *len = EAP_TLS_KEY_LEN;
 +      os_memcpy(key, data->key_data, EAP_TLS_KEY_LEN);
 +
 +      return key;
 +}
 +
 +
 +static u8 * eap_tls_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
 +{
 +      struct eap_tls_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;
 +}
 +
 +
 +static u8 * eap_tls_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
 +{
 +      struct eap_tls_data *data = priv;
 +      u8 *id;
 +
 +      if (data->session_id == NULL)
 +              return NULL;
 +
 +      id = os_malloc(data->id_len);
 +      if (id == NULL)
 +              return NULL;
 +
 +      *len = data->id_len;
 +      os_memcpy(id, data->session_id, data->id_len);
 +
 +      return id;
 +}
 +
 +
 +int eap_peer_tls_register(void)
 +{
 +      struct eap_method *eap;
 +      int ret;
 +
 +      eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
 +                                  EAP_VENDOR_IETF, EAP_TYPE_TLS, "TLS");
 +      if (eap == NULL)
 +              return -1;
 +
 +      eap->init = eap_tls_init;
 +      eap->deinit = eap_tls_deinit;
 +      eap->process = eap_tls_process;
 +      eap->isKeyAvailable = eap_tls_isKeyAvailable;
 +      eap->getKey = eap_tls_getKey;
 +      eap->getSessionId = eap_tls_get_session_id;
 +      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;
 +}
 +
 +
 +#ifdef EAP_UNAUTH_TLS
 +int eap_peer_unauth_tls_register(void)
 +{
 +      struct eap_method *eap;
 +      int ret;
 +
 +      eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
 +                                  EAP_VENDOR_UNAUTH_TLS,
 +                                  EAP_VENDOR_TYPE_UNAUTH_TLS, "UNAUTH-TLS");
 +      if (eap == NULL)
 +              return -1;
 +
 +      eap->init = eap_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 /* 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 8710781618effb7cf655047a3ccb2fddf9dc8c69,0000000000000000000000000000000000000000..af2b7541d70168d909cc43b654fde4d3fd6d2a77
mode 100644,000000..100644
--- /dev/null
@@@ -1,1135 -1,0 +1,1108 @@@
-       if (res == TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED) {
 +/*
 + * EAP peer: EAP-TLS/PEAP/TTLS/FAST common functions
 + * 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.
 + */
 +
 +#include "includes.h"
 +
 +#include "common.h"
 +#include "crypto/sha1.h"
 +#include "crypto/tls.h"
 +#include "eap_i.h"
 +#include "eap_tls_common.h"
 +#include "eap_config.h"
 +
 +
 +static struct wpabuf * eap_tls_msg_alloc(EapType type, size_t payload_len,
 +                                       u8 code, u8 identifier)
 +{
 +      if (type == EAP_UNAUTH_TLS_TYPE)
 +              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);
 +}
 +
 +
 +static int eap_tls_check_blob(struct eap_sm *sm, const char **name,
 +                            const u8 **data, size_t *data_len)
 +{
 +      const struct wpa_config_blob *blob;
 +
 +      if (*name == NULL || os_strncmp(*name, "blob://", 7) != 0)
 +              return 0;
 +
 +      blob = eap_get_config_blob(sm, *name + 7);
 +      if (blob == NULL) {
 +              wpa_printf(MSG_ERROR, "%s: Named configuration blob '%s' not "
 +                         "found", __func__, *name + 7);
 +              return -1;
 +      }
 +
 +      *name = NULL;
 +      *data = blob->data;
 +      *data_len = blob->len;
 +
 +      return 0;
 +}
 +
 +
 +static void eap_tls_params_flags(struct tls_connection_params *params,
 +                               const char *txt)
 +{
 +      if (txt == NULL)
 +              return;
 +      if (os_strstr(txt, "tls_allow_md5=1"))
 +              params->flags |= TLS_CONN_ALLOW_SIGN_RSA_MD5;
 +      if (os_strstr(txt, "tls_disable_time_checks=1"))
 +              params->flags |= TLS_CONN_DISABLE_TIME_CHECKS;
 +      if (os_strstr(txt, "tls_disable_session_ticket=1"))
 +              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_0=1"))
++              params->flags |= TLS_CONN_DISABLE_TLSv1_0;
++      if (os_strstr(txt, "tls_disable_tlsv1_0=0"))
++              params->flags &= ~TLS_CONN_DISABLE_TLSv1_0;
 +      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;
 +}
 +
 +
 +static void eap_tls_params_from_conf1(struct tls_connection_params *params,
 +                                    struct eap_peer_config *config)
 +{
 +      params->ca_cert = (char *) config->ca_cert;
 +      params->ca_path = (char *) config->ca_path;
 +      params->client_cert = (char *) config->client_cert;
 +      params->private_key = (char *) config->private_key;
 +      params->private_key_passwd = (char *) config->private_key_passwd;
 +      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;
 +      params->key_id = config->key_id;
 +      params->cert_id = config->cert_id;
 +      params->ca_cert_id = config->ca_cert_id;
 +      eap_tls_params_flags(params, config->phase1);
 +}
 +
 +
 +static void eap_tls_params_from_conf2(struct tls_connection_params *params,
 +                                    struct eap_peer_config *config)
 +{
 +      params->ca_cert = (char *) config->ca_cert2;
 +      params->ca_path = (char *) config->ca_path2;
 +      params->client_cert = (char *) config->client_cert2;
 +      params->private_key = (char *) config->private_key2;
 +      params->private_key_passwd = (char *) config->private_key2_passwd;
 +      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;
 +      params->key_id = config->key2_id;
 +      params->cert_id = config->cert2_id;
 +      params->ca_cert_id = config->ca_cert2_id;
 +      eap_tls_params_flags(params, config->phase2);
 +}
 +
 +
 +static int eap_tls_params_from_conf(struct eap_sm *sm,
 +                                  struct eap_ssl_data *data,
 +                                  struct tls_connection_params *params,
 +                                  struct eap_peer_config *config, int phase2)
 +{
 +      os_memset(params, 0, sizeof(*params));
 +      if (sm->workaround && data->eap_type != EAP_TYPE_FAST) {
 +              /*
 +               * Some deployed authentication servers seem to be unable to
 +               * handle the TLS Session Ticket extension (they are supposed
 +               * to ignore unrecognized TLS extensions, but end up rejecting
 +               * the ClientHello instead). As a workaround, disable use of
 +               * TLS Sesson Ticket extension for EAP-TLS, EAP-PEAP, and
 +               * EAP-TTLS (EAP-FAST uses session ticket, so any server that
 +               * supports EAP-FAST does not need this workaround).
 +               */
 +              params->flags |= TLS_CONN_DISABLE_SESSION_TICKET;
 +      }
 +      if (phase2) {
 +              wpa_printf(MSG_DEBUG, "TLS: using phase2 config options");
 +              eap_tls_params_from_conf2(params, config);
 +      } 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;
 +      }
 +
 +      /*
 +       * Use blob data, if available. Otherwise, leave reference to external
 +       * file as-is.
 +       */
 +      if (eap_tls_check_blob(sm, &params->ca_cert, &params->ca_cert_blob,
 +                             &params->ca_cert_blob_len) ||
 +          eap_tls_check_blob(sm, &params->client_cert,
 +                             &params->client_cert_blob,
 +                             &params->client_cert_blob_len) ||
 +          eap_tls_check_blob(sm, &params->private_key,
 +                             &params->private_key_blob,
 +                             &params->private_key_blob_len) ||
 +          eap_tls_check_blob(sm, &params->dh_file, &params->dh_blob,
 +                             &params->dh_blob_len)) {
 +              wpa_printf(MSG_INFO, "SSL: Failed to get configuration blobs");
 +              return -1;
 +      }
 +
 +      params->openssl_ciphers = config->openssl_ciphers;
 +
 +      return 0;
 +}
 +
 +
 +static int eap_tls_init_connection(struct eap_sm *sm,
 +                                 struct eap_ssl_data *data,
 +                                 struct eap_peer_config *config,
 +                                 struct tls_connection_params *params)
 +{
 +      int res;
 +
 +      if (config->ocsp)
 +              params->flags |= TLS_CONN_REQUEST_OCSP;
 +      if (config->ocsp == 2)
 +              params->flags |= TLS_CONN_REQUIRE_OCSP;
 +      data->conn = tls_connection_init(data->ssl_ctx);
 +      if (data->conn == NULL) {
 +              wpa_printf(MSG_INFO, "SSL: Failed to initialize new TLS "
 +                         "connection");
 +              return -1;
 +      }
 +
 +      res = tls_connection_set_params(data->ssl_ctx, data->conn, params);
-                * At this point with the pkcs11 engine the PIN might be wrong.
-                * We reset the PIN in the configuration to be sure to not use
-                * it again and the calling function must request a new one.
++      if (res == TLS_SET_PARAMS_ENGINE_PRV_BAD_PIN) {
 +              /*
-               /*
-                * We do not know exactly but maybe the PIN was wrong,
-                * so ask for a new one.
-                */
-               os_free(config->pin);
-               config->pin = NULL;
-               eap_sm_request_pin(sm);
++               * At this point with the pkcs11 engine the PIN is wrong. We
++               * reset the PIN in the configuration to be sure to not use it
++               * again and the calling function must request a new one.
 +               */
++              wpa_printf(MSG_INFO,
++                         "TLS: Bad PIN provided, requesting a new one");
 +              os_free(config->pin);
 +              config->pin = NULL;
++              eap_sm_request_pin(sm);
++              sm->ignore = TRUE;
++      } else if (res == TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED) {
++              wpa_printf(MSG_INFO, "TLS: Failed to initialize engine");
 +      } else if (res == TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED) {
 +              wpa_printf(MSG_INFO, "TLS: Failed to load private key");
-               tls_connection_deinit(data->ssl_ctx, data->conn);
-               data->conn = NULL;
-               return -1;
-       } else if (res) {
 +              sm->ignore = TRUE;
- #ifndef CONFIG_FIPS
-       struct tls_keys keys;
- #endif /* CONFIG_FIPS */
-       u8 *rnd = NULL, *out;
++      }
++      if (res) {
 +              wpa_printf(MSG_INFO, "TLS: Failed to set TLS connection "
 +                         "parameters");
 +              tls_connection_deinit(data->ssl_ctx, data->conn);
 +              data->conn = NULL;
 +              return -1;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +/**
 + * eap_peer_tls_ssl_init - Initialize shared TLS functionality
 + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
 + * @data: Data for TLS processing
 + * @config: Pointer to the network configuration
 + * @eap_type: EAP method used in Phase 1 (EAP_TYPE_TLS/PEAP/TTLS/FAST)
 + * Returns: 0 on success, -1 on failure
 + *
 + * This function is used to initialize shared TLS functionality for EAP-TLS,
 + * EAP-PEAP, EAP-TTLS, and EAP-FAST.
 + */
 +int eap_peer_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data,
 +                        struct eap_peer_config *config, u8 eap_type)
 +{
 +      struct tls_connection_params params;
 +
 +      if (config == NULL)
 +              return -1;
 +
 +      data->eap = sm;
 +      data->eap_type = eap_type;
 +      data->phase2 = sm->init_phase2;
 +      data->ssl_ctx = sm->init_phase2 && sm->ssl_ctx2 ? sm->ssl_ctx2 :
 +              sm->ssl_ctx;
 +      if (eap_tls_params_from_conf(sm, data, &params, config, data->phase2) <
 +          0)
 +              return -1;
 +
 +      if (eap_tls_init_connection(sm, data, config, &params) < 0)
 +              return -1;
 +
 +      data->tls_out_limit = config->fragment_size;
 +      if (data->phase2) {
 +              /* Limit the fragment size in the inner TLS authentication
 +               * since the outer authentication with EAP-PEAP does not yet
 +               * support fragmentation */
 +              if (data->tls_out_limit > 100)
 +                      data->tls_out_limit -= 100;
 +      }
 +
 +      if (config->phase1 &&
 +          os_strstr(config->phase1, "include_tls_length=1")) {
 +              wpa_printf(MSG_DEBUG, "TLS: Include TLS Message Length in "
 +                         "unfragmented packets");
 +              data->include_tls_length = 1;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +/**
 + * eap_peer_tls_ssl_deinit - Deinitialize shared TLS functionality
 + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
 + * @data: Data for TLS processing
 + *
 + * This function deinitializes shared TLS functionality that was initialized
 + * with eap_peer_tls_ssl_init().
 + */
 +void eap_peer_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data)
 +{
 +      tls_connection_deinit(data->ssl_ctx, data->conn);
 +      eap_peer_tls_reset_input(data);
 +      eap_peer_tls_reset_output(data);
 +}
 +
 +
 +/**
 + * eap_peer_tls_derive_key - Derive a key based on TLS session data
 + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
 + * @data: Data for TLS processing
 + * @label: Label string for deriving the keys, e.g., "client EAP encryption"
 + * @len: Length of the key material to generate (usually 64 for MSK)
 + * Returns: Pointer to allocated key on success or %NULL on failure
 + *
 + * This function uses TLS-PRF to generate pseudo-random data based on the TLS
 + * session data (client/server random and master key). Each key type may use a
 + * different label to bind the key usage into the generated material.
 + *
 + * The caller is responsible for freeing the returned buffer.
 + */
 +u8 * eap_peer_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data,
 +                           const char *label, size_t len)
 +{
-       /* First, try to use TLS library function for PRF, if available. */
-       if (tls_connection_prf(data->ssl_ctx, data->conn, label, 0, out, len)
-           == 0)
-               return out;
- #ifndef CONFIG_FIPS
-       /*
-        * TLS library did not support key generation, so get the needed TLS
-        * session parameters and use an internal implementation of TLS PRF to
-        * derive the key.
-        */
-       if (tls_connection_get_keys(data->ssl_ctx, data->conn, &keys))
-               goto fail;
-       if (keys.client_random == NULL || keys.server_random == NULL ||
-           keys.master_key == NULL)
-               goto fail;
-       rnd = os_malloc(keys.client_random_len + keys.server_random_len);
-       if (rnd == NULL)
-               goto fail;
-       os_memcpy(rnd, keys.client_random, keys.client_random_len);
-       os_memcpy(rnd + keys.client_random_len, keys.server_random,
-                 keys.server_random_len);
-       if (tls_prf_sha1_md5(keys.master_key, keys.master_key_len,
-                            label, rnd, keys.client_random_len +
-                            keys.server_random_len, out, len))
-               goto fail;
++      u8 *out;
 +
 +      out = os_malloc(len);
 +      if (out == NULL)
 +              return NULL;
 +
-       os_free(rnd);
++      if (tls_connection_prf(data->ssl_ctx, data->conn, label, 0, 0,
++                             out, len)) {
++              os_free(out);
++              return NULL;
++      }
 +
- fail:
- #endif /* CONFIG_FIPS */
-       os_free(out);
-       os_free(rnd);
-       return NULL;
 +      return out;
-       struct tls_keys keys;
 +}
 +
 +
 +/**
 + * eap_peer_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_peer_tls_derive_session_id(struct eap_sm *sm,
 +                                  struct eap_ssl_data *data, u8 eap_type,
 +                                  size_t *len)
 +{
-       if (tls_connection_get_keys(sm->ssl_ctx, data->conn, &keys))
++      struct tls_random keys;
 +      u8 *out;
 +
-  * @in_len: Length of in_data
++      if (tls_connection_get_random(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;
 +}
 +
 +
 +/**
 + * eap_peer_tls_reassemble_fragment - Reassemble a received fragment
 + * @data: Data for TLS processing
 + * @in_data: Next incoming TLS segment
 + * Returns: 0 on success, 1 if more data is needed for the full message, or
 + * -1 on error
 + */
 +static int eap_peer_tls_reassemble_fragment(struct eap_ssl_data *data,
 +                                          const struct wpabuf *in_data)
 +{
 +      size_t tls_in_len, in_len;
 +
 +      tls_in_len = data->tls_in ? wpabuf_len(data->tls_in) : 0;
 +      in_len = in_data ? wpabuf_len(in_data) : 0;
 +
 +      if (tls_in_len + in_len == 0) {
 +              /* No message data received?! */
 +              wpa_printf(MSG_WARNING, "SSL: Invalid reassembly state: "
 +                         "tls_in_left=%lu tls_in_len=%lu in_len=%lu",
 +                         (unsigned long) data->tls_in_left,
 +                         (unsigned long) tls_in_len,
 +                         (unsigned long) in_len);
 +              eap_peer_tls_reset_input(data);
 +              return -1;
 +      }
 +
 +      if (tls_in_len + in_len > 65536) {
 +              /*
 +               * Limit length to avoid rogue servers from causing large
 +               * memory allocations.
 +               */
 +              wpa_printf(MSG_INFO, "SSL: Too long TLS fragment (size over "
 +                         "64 kB)");
 +              eap_peer_tls_reset_input(data);
 +              return -1;
 +      }
 +
 +      if (in_len > data->tls_in_left) {
 +              /* Sender is doing something odd - reject message */
 +              wpa_printf(MSG_INFO, "SSL: more data than TLS message length "
 +                         "indicated");
 +              eap_peer_tls_reset_input(data);
 +              return -1;
 +      }
 +
 +      if (wpabuf_resize(&data->tls_in, in_len) < 0) {
 +              wpa_printf(MSG_INFO, "SSL: Could not allocate memory for TLS "
 +                         "data");
 +              eap_peer_tls_reset_input(data);
 +              return -1;
 +      }
 +      if (in_data)
 +              wpabuf_put_buf(data->tls_in, in_data);
 +      data->tls_in_left -= in_len;
 +
 +      if (data->tls_in_left > 0) {
 +              wpa_printf(MSG_DEBUG, "SSL: Need %lu bytes more input "
 +                         "data", (unsigned long) data->tls_in_left);
 +              return 1;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +/**
 + * eap_peer_tls_data_reassemble - Reassemble TLS data
 + * @data: Data for TLS processing
 + * @in_data: Next incoming TLS segment
 + * @need_more_input: Variable for returning whether more input data is needed
 + * to reassemble this TLS packet
 + * Returns: Pointer to output data, %NULL on error or when more data is needed
 + * for the full message (in which case, *need_more_input is also set to 1).
 + *
 + * This function reassembles TLS fragments. Caller must not free the returned
 + * data buffer since an internal pointer to it is maintained.
 + */
 +static const struct wpabuf * eap_peer_tls_data_reassemble(
 +      struct eap_ssl_data *data, const struct wpabuf *in_data,
 +      int *need_more_input)
 +{
 +      *need_more_input = 0;
 +
 +      if (data->tls_in_left > wpabuf_len(in_data) || data->tls_in) {
 +              /* Message has fragments */
 +              int res = eap_peer_tls_reassemble_fragment(data, in_data);
 +              if (res) {
 +                      if (res == 1)
 +                              *need_more_input = 1;
 +                      return NULL;
 +              }
 +
 +              /* Message is now fully reassembled. */
 +      } else {
 +              /* No fragments in this message, so just make a copy of it. */
 +              data->tls_in_left = 0;
 +              data->tls_in = wpabuf_dup(in_data);
 +              if (data->tls_in == NULL)
 +                      return NULL;
 +      }
 +
 +      return data->tls_in;
 +}
 +
 +
 +/**
 + * eap_tls_process_input - Process incoming TLS message
 + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
 + * @data: Data for TLS processing
 + * @in_data: Message received from the server
-                                const u8 *in_data, size_t in_len,
 + * @out_data: Buffer for returning a pointer to application data (if available)
 + * Returns: 0 on success, 1 if more input data is needed, 2 if application data
 + * is available, -1 on failure
 + */
 +static int eap_tls_process_input(struct eap_sm *sm, struct eap_ssl_data *data,
-       struct wpabuf buf;
++                               const struct wpabuf *in_data,
 +                               struct wpabuf **out_data)
 +{
 +      const struct wpabuf *msg;
 +      int need_more_input;
 +      struct wpabuf *appl_data;
-       wpabuf_set(&buf, in_data, in_len);
-       msg = eap_peer_tls_data_reassemble(data, &buf, &need_more_input);
 +
-  * @in_len: Length of in_data
++      msg = eap_peer_tls_data_reassemble(data, in_data, &need_more_input);
 +      if (msg == NULL)
 +              return need_more_input ? 1 : -1;
 +
 +      /* Full TLS message reassembled - continue handshake processing */
 +      if (data->tls_out) {
 +              /* This should not happen.. */
 +              wpa_printf(MSG_INFO, "SSL: eap_tls_process_input - pending "
 +                         "tls_out data even though tls_out_len = 0");
 +              wpabuf_free(data->tls_out);
 +              WPA_ASSERT(data->tls_out == NULL);
 +      }
 +      appl_data = NULL;
 +      data->tls_out = tls_connection_handshake(data->ssl_ctx, data->conn,
 +                                               msg, &appl_data);
 +
 +      eap_peer_tls_reset_input(data);
 +
 +      if (appl_data &&
 +          tls_connection_established(data->ssl_ctx, data->conn) &&
 +          !tls_connection_get_failed(data->ssl_ctx, data->conn)) {
 +              wpa_hexdump_buf_key(MSG_MSGDUMP, "SSL: Application data",
 +                                  appl_data);
 +              *out_data = appl_data;
 +              return 2;
 +      }
 +
 +      wpabuf_free(appl_data);
 +
 +      return 0;
 +}
 +
 +
 +/**
 + * eap_tls_process_output - Process outgoing TLS message
 + * @data: Data for TLS processing
 + * @eap_type: EAP type (EAP_TYPE_TLS, EAP_TYPE_PEAP, ...)
 + * @peap_version: Version number for EAP-PEAP/TTLS
 + * @id: EAP identifier for the response
 + * @ret: Return value to use on success
 + * @out_data: Buffer for returning the allocated output buffer
 + * Returns: ret (0 or 1) on success, -1 on failure
 + */
 +static int eap_tls_process_output(struct eap_ssl_data *data, EapType eap_type,
 +                                int peap_version, u8 id, int ret,
 +                                struct wpabuf **out_data)
 +{
 +      size_t len;
 +      u8 *flags;
 +      int more_fragments, length_included;
 +
 +      if (data->tls_out == NULL)
 +              return -1;
 +      len = wpabuf_len(data->tls_out) - data->tls_out_pos;
 +      wpa_printf(MSG_DEBUG, "SSL: %lu bytes left to be sent out (of total "
 +                 "%lu bytes)",
 +                 (unsigned long) len,
 +                 (unsigned long) wpabuf_len(data->tls_out));
 +
 +      /*
 +       * Limit outgoing message to the configured maximum size. Fragment
 +       * message if needed.
 +       */
 +      if (len > data->tls_out_limit) {
 +              more_fragments = 1;
 +              len = data->tls_out_limit;
 +              wpa_printf(MSG_DEBUG, "SSL: sending %lu bytes, more fragments "
 +                         "will follow", (unsigned long) len);
 +      } else
 +              more_fragments = 0;
 +
 +      length_included = data->tls_out_pos == 0 &&
 +              (wpabuf_len(data->tls_out) > data->tls_out_limit ||
 +               data->include_tls_length);
 +      if (!length_included &&
 +          eap_type == EAP_TYPE_PEAP && peap_version == 0 &&
 +          !tls_connection_established(data->eap->ssl_ctx, data->conn)) {
 +              /*
 +               * Windows Server 2008 NPS really wants to have the TLS Message
 +               * length included in phase 0 even for unfragmented frames or
 +               * it will get very confused with Compound MAC calculation and
 +               * Outer TLVs.
 +               */
 +              length_included = 1;
 +      }
 +
 +      *out_data = eap_tls_msg_alloc(eap_type, 1 + length_included * 4 + len,
 +                                    EAP_CODE_RESPONSE, id);
 +      if (*out_data == NULL)
 +              return -1;
 +
 +      flags = wpabuf_put(*out_data, 1);
 +      *flags = peap_version;
 +      if (more_fragments)
 +              *flags |= EAP_TLS_FLAGS_MORE_FRAGMENTS;
 +      if (length_included) {
 +              *flags |= EAP_TLS_FLAGS_LENGTH_INCLUDED;
 +              wpabuf_put_be32(*out_data, wpabuf_len(data->tls_out));
 +      }
 +
 +      wpabuf_put_data(*out_data,
 +                      wpabuf_head_u8(data->tls_out) + data->tls_out_pos,
 +                      len);
 +      data->tls_out_pos += len;
 +
 +      if (!more_fragments)
 +              eap_peer_tls_reset_output(data);
 +
 +      return ret;
 +}
 +
 +
 +/**
 + * eap_peer_tls_process_helper - Process TLS handshake message
 + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
 + * @data: Data for TLS processing
 + * @eap_type: EAP type (EAP_TYPE_TLS, EAP_TYPE_PEAP, ...)
 + * @peap_version: Version number for EAP-PEAP/TTLS
 + * @id: EAP identifier for the response
 + * @in_data: Message received from the server
-                               u8 id, const u8 *in_data, size_t in_len,
 + * @out_data: Buffer for returning a pointer to the response message
 + * Returns: 0 on success, 1 if more input data is needed, 2 if application data
 + * is available, or -1 on failure
 + *
 + * This function can be used to process TLS handshake messages. It reassembles
 + * the received fragments and uses a TLS library to process the messages. The
 + * response data from the TLS library is fragmented to suitable output messages
 + * that the caller can send out.
 + *
 + * out_data is used to return the response message if the return value of this
 + * function is 0, 2, or -1. In case of failure, the message is likely a TLS
 + * alarm message. The caller is responsible for freeing the allocated buffer if
 + * *out_data is not %NULL.
 + *
 + * This function is called for each received TLS message during the TLS
 + * handshake after eap_peer_tls_process_init() call and possible processing of
 + * TLS Flags field. Once the handshake has been completed, i.e., when
 + * tls_connection_established() returns 1, EAP method specific decrypting of
 + * the tunneled data is used.
 + */
 +int eap_peer_tls_process_helper(struct eap_sm *sm, struct eap_ssl_data *data,
 +                              EapType eap_type, int peap_version,
-       if (data->tls_out && wpabuf_len(data->tls_out) > 0 && in_len > 0) {
++                              u8 id, const struct wpabuf *in_data,
 +                              struct wpabuf **out_data)
 +{
 +      int ret = 0;
 +
 +      *out_data = NULL;
 +
-               int res = eap_tls_process_input(sm, data, in_data, in_len,
-                                               out_data);
++      if (data->tls_out && wpabuf_len(data->tls_out) > 0 &&
++          wpabuf_len(in_data) > 0) {
 +              wpa_printf(MSG_DEBUG, "SSL: Received non-ACK when output "
 +                         "fragments are waiting to be sent out");
 +              return -1;
 +      }
 +
 +      if (data->tls_out == NULL || wpabuf_len(data->tls_out) == 0) {
 +              /*
 +               * No more data to send out - expect to receive more data from
 +               * the AS.
 +               */
-                          "report error");
++              int res = eap_tls_process_input(sm, data, in_data, out_data);
 +              if (res) {
 +                      /*
 +                       * Input processing failed (res = -1) or more data is
 +                       * needed (res = 1).
 +                       */
 +                      return res;
 +              }
 +
 +              /*
 +               * The incoming message has been reassembled and processed. The
 +               * response was allocated into data->tls_out buffer.
 +               */
 +      }
 +
 +      if (data->tls_out == NULL) {
 +              /*
 +               * No outgoing fragments remaining from the previous message
 +               * and no new message generated. This indicates an error in TLS
 +               * processing.
 +               */
 +              eap_peer_tls_reset_output(data);
 +              return -1;
 +      }
 +
 +      if (tls_connection_get_failed(data->ssl_ctx, data->conn)) {
 +              /* TLS processing has failed - return error */
 +              wpa_printf(MSG_DEBUG, "SSL: Failed - tls_out available to "
-       if (data->tls_out == NULL || wpabuf_len(data->tls_out) == 0) {
++                         "report error (len=%u)",
++                         (unsigned int) wpabuf_len(data->tls_out));
 +              ret = -1;
 +              /* TODO: clean pin if engine used? */
++              if (wpabuf_len(data->tls_out) == 0) {
++                      wpabuf_free(data->tls_out);
++                      data->tls_out = NULL;
++                      return -1;
++              }
 +      }
 +
-       char name[128];
++      if (wpabuf_len(data->tls_out) == 0) {
 +              /*
 +               * TLS negotiation should now be complete since all other cases
 +               * needing more data should have been caught above based on
 +               * the TLS Message Length field.
 +               */
 +              wpa_printf(MSG_DEBUG, "SSL: No data to be sent out");
 +              wpabuf_free(data->tls_out);
 +              data->tls_out = NULL;
 +              return 1;
 +      }
 +
 +      /* Send the pending message (in fragments, if needed). */
 +      return eap_tls_process_output(data, eap_type, peap_version, id, ret,
 +                                    out_data);
 +}
 +
 +
 +/**
 + * eap_peer_tls_build_ack - Build a TLS ACK frame
 + * @id: EAP identifier for the response
 + * @eap_type: EAP type (EAP_TYPE_TLS, EAP_TYPE_PEAP, ...)
 + * @peap_version: Version number for EAP-PEAP/TTLS
 + * Returns: Pointer to the allocated ACK frame or %NULL on failure
 + */
 +struct wpabuf * eap_peer_tls_build_ack(u8 id, EapType eap_type,
 +                                     int peap_version)
 +{
 +      struct wpabuf *resp;
 +
 +      resp = eap_tls_msg_alloc(eap_type, 1, EAP_CODE_RESPONSE, id);
 +      if (resp == NULL)
 +              return NULL;
 +      wpa_printf(MSG_DEBUG, "SSL: Building ACK (type=%d id=%d ver=%d)",
 +                 (int) eap_type, id, peap_version);
 +      wpabuf_put_u8(resp, peap_version); /* Flags */
 +      return resp;
 +}
 +
 +
 +/**
 + * eap_peer_tls_reauth_init - Re-initialize shared TLS for session resumption
 + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
 + * @data: Data for TLS processing
 + * Returns: 0 on success, -1 on failure
 + */
 +int eap_peer_tls_reauth_init(struct eap_sm *sm, struct eap_ssl_data *data)
 +{
 +      eap_peer_tls_reset_input(data);
 +      eap_peer_tls_reset_output(data);
 +      return tls_connection_shutdown(data->ssl_ctx, data->conn);
 +}
 +
 +
 +/**
 + * eap_peer_tls_status - Get TLS status
 + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
 + * @data: Data for TLS processing
 + * @buf: Buffer for status information
 + * @buflen: Maximum buffer length
 + * @verbose: Whether to include verbose status information
 + * Returns: Number of bytes written to buf.
 + */
 +int eap_peer_tls_status(struct eap_sm *sm, struct eap_ssl_data *data,
 +                      char *buf, size_t buflen, int verbose)
 +{
-       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"
-                                 "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;
-       }
++      char version[20], name[128];
 +      int len = 0, ret;
 +
-       u8 method;
++      if (tls_get_version(data->ssl_ctx, data->conn, version,
++                          sizeof(version)) < 0)
++              version[0] = '\0';
++      if (tls_get_cipher(data->ssl_ctx, data->conn, name, sizeof(name)) < 0)
++              name[0] = '\0';
++
++      ret = os_snprintf(buf + len, buflen - len,
++                        "eap_tls_version=%s\n"
++                        "EAP TLS cipher=%s\n"
++                        "tls_session_reused=%d\n",
++                        version, name,
++                        tls_connection_resumed(data->ssl_ctx, data->conn));
++      if (os_snprintf_error(buflen - len, ret))
++              return len;
++      len += ret;
 +
 +      return len;
 +}
 +
 +
 +/**
 + * eap_peer_tls_process_init - Initial validation/processing of EAP requests
 + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
 + * @data: Data for TLS processing
 + * @eap_type: EAP type (EAP_TYPE_TLS, EAP_TYPE_PEAP, ...)
 + * @ret: Return values from EAP request validation and processing
 + * @reqData: EAP request to be processed (eapReqData)
 + * @len: Buffer for returning length of the remaining payload
 + * @flags: Buffer for returning TLS flags
 + * Returns: Pointer to payload after TLS flags and length or %NULL on failure
 + *
 + * This function validates the EAP header and processes the optional TLS
 + * Message Length field. If this is the first fragment of a TLS message, the
 + * TLS reassembly code is initialized to receive the indicated number of bytes.
 + *
 + * EAP-TLS, EAP-PEAP, EAP-TTLS, and EAP-FAST methods are expected to use this
 + * function as the first step in processing received messages. They will need
 + * to process the flags (apart from Message Length Included) that are returned
 + * through the flags pointer and the message payload that will be returned (and
 + * the length is returned through the len pointer). Return values (ret) are set
 + * for continuation of EAP method processing. The caller is responsible for
 + * setting these to indicate completion (either success or failure) based on
 + * the authentication result.
 + */
 +const u8 * eap_peer_tls_process_init(struct eap_sm *sm,
 +                                   struct eap_ssl_data *data,
 +                                   EapType eap_type,
 +                                   struct eap_method_ret *ret,
 +                                   const struct wpabuf *reqData,
 +                                   size_t *len, u8 *flags)
 +{
 +      const u8 *pos;
 +      size_t left;
 +      unsigned int tls_msg_len;
 +
 +      if (tls_get_errors(data->ssl_ctx)) {
 +              wpa_printf(MSG_INFO, "SSL: TLS errors detected");
 +              ret->ignore = TRUE;
 +              return NULL;
 +      }
 +
 +      if (eap_type == EAP_UNAUTH_TLS_TYPE)
 +              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);
 +      if (pos == NULL) {
 +              ret->ignore = TRUE;
 +              return NULL;
 +      }
 +      if (left == 0) {
 +              wpa_printf(MSG_DEBUG, "SSL: Invalid TLS message: no Flags "
 +                         "octet included");
 +              if (!sm->workaround) {
 +                      ret->ignore = TRUE;
 +                      return NULL;
 +              }
 +
 +              wpa_printf(MSG_DEBUG, "SSL: Workaround - assume no Flags "
 +                         "indicates ACK frame");
 +              *flags = 0;
 +      } else {
 +              *flags = *pos++;
 +              left--;
 +      }
 +      wpa_printf(MSG_DEBUG, "SSL: Received packet(len=%lu) - "
 +                 "Flags 0x%02x", (unsigned long) wpabuf_len(reqData),
 +                 *flags);
 +      if (*flags & EAP_TLS_FLAGS_LENGTH_INCLUDED) {
 +              if (left < 4) {
 +                      wpa_printf(MSG_INFO, "SSL: Short frame with TLS "
 +                                 "length");
 +                      ret->ignore = TRUE;
 +                      return NULL;
 +              }
 +              tls_msg_len = WPA_GET_BE32(pos);
 +              wpa_printf(MSG_DEBUG, "SSL: TLS Message Length: %d",
 +                         tls_msg_len);
 +              if (data->tls_in_left == 0) {
 +                      data->tls_in_total = tls_msg_len;
 +                      data->tls_in_left = tls_msg_len;
 +                      wpabuf_free(data->tls_in);
 +                      data->tls_in = NULL;
 +              }
 +              pos += 4;
 +              left -= 4;
 +
 +              if (left > tls_msg_len) {
 +                      wpa_printf(MSG_INFO, "SSL: TLS Message Length (%d "
 +                                 "bytes) smaller than this fragment (%d "
 +                                 "bytes)", (int) tls_msg_len, (int) left);
 +                      ret->ignore = TRUE;
 +                      return NULL;
 +              }
 +      }
 +
 +      ret->ignore = FALSE;
 +      ret->methodState = METHOD_MAY_CONT;
 +      ret->decision = DECISION_FAIL;
 +      ret->allowNotifications = TRUE;
 +
 +      *len = left;
 +      return pos;
 +}
 +
 +
 +/**
 + * eap_peer_tls_reset_input - Reset input buffers
 + * @data: Data for TLS processing
 + *
 + * This function frees any allocated memory for input buffers and resets input
 + * state.
 + */
 +void eap_peer_tls_reset_input(struct eap_ssl_data *data)
 +{
 +      data->tls_in_left = data->tls_in_total = 0;
 +      wpabuf_free(data->tls_in);
 +      data->tls_in = NULL;
 +}
 +
 +
 +/**
 + * eap_peer_tls_reset_output - Reset output buffers
 + * @data: Data for TLS processing
 + *
 + * This function frees any allocated memory for output buffers and resets
 + * output state.
 + */
 +void eap_peer_tls_reset_output(struct eap_ssl_data *data)
 +{
 +      data->tls_out_pos = 0;
 +      wpabuf_free(data->tls_out);
 +      data->tls_out = NULL;
 +}
 +
 +
 +/**
 + * eap_peer_tls_decrypt - Decrypt received phase 2 TLS message
 + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
 + * @data: Data for TLS processing
 + * @in_data: Message received from the server
 + * @in_decrypted: Buffer for returning a pointer to the decrypted message
 + * Returns: 0 on success, 1 if more input data is needed, or -1 on failure
 + */
 +int eap_peer_tls_decrypt(struct eap_sm *sm, struct eap_ssl_data *data,
 +                       const struct wpabuf *in_data,
 +                       struct wpabuf **in_decrypted)
 +{
 +      const struct wpabuf *msg;
 +      int need_more_input;
 +
 +      msg = eap_peer_tls_data_reassemble(data, in_data, &need_more_input);
 +      if (msg == NULL)
 +              return need_more_input ? 1 : -1;
 +
 +      *in_decrypted = tls_connection_decrypt(data->ssl_ctx, data->conn, msg);
 +      eap_peer_tls_reset_input(data);
 +      if (*in_decrypted == NULL) {
 +              wpa_printf(MSG_INFO, "SSL: Failed to decrypt Phase 2 data");
 +              return -1;
 +      }
 +      return 0;
 +}
 +
 +
 +/**
 + * eap_peer_tls_encrypt - Encrypt phase 2 TLS message
 + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
 + * @data: Data for TLS processing
 + * @eap_type: EAP type (EAP_TYPE_TLS, EAP_TYPE_PEAP, ...)
 + * @peap_version: Version number for EAP-PEAP/TTLS
 + * @id: EAP identifier for the response
 + * @in_data: Plaintext phase 2 data to encrypt or %NULL to continue fragments
 + * @out_data: Buffer for returning a pointer to the encrypted response message
 + * Returns: 0 on success, -1 on failure
 + */
 +int eap_peer_tls_encrypt(struct eap_sm *sm, struct eap_ssl_data *data,
 +                       EapType eap_type, int peap_version, u8 id,
 +                       const struct wpabuf *in_data,
 +                       struct wpabuf **out_data)
 +{
 +      if (in_data) {
 +              eap_peer_tls_reset_output(data);
 +              data->tls_out = tls_connection_encrypt(data->ssl_ctx,
 +                                                     data->conn, in_data);
 +              if (data->tls_out == NULL) {
 +                      wpa_printf(MSG_INFO, "SSL: Failed to encrypt Phase 2 "
 +                                 "data (in_len=%lu)",
 +                                 (unsigned long) wpabuf_len(in_data));
 +                      eap_peer_tls_reset_output(data);
 +                      return -1;
 +              }
 +      }
 +
 +      return eap_tls_process_output(data, eap_type, peap_version, id, 0,
 +                                    out_data);
 +}
 +
 +
 +/**
 + * eap_peer_select_phase2_methods - Select phase 2 EAP method
 + * @config: Pointer to the network configuration
 + * @prefix: 'phase2' configuration prefix, e.g., "auth="
 + * @types: Buffer for returning allocated list of allowed EAP methods
 + * @num_types: Buffer for returning number of allocated EAP methods
 + * Returns: 0 on success, -1 on failure
 + *
 + * This function is used to parse EAP method list and select allowed methods
 + * for Phase2 authentication.
 + */
 +int eap_peer_select_phase2_methods(struct eap_peer_config *config,
 +                                 const char *prefix,
 +                                 struct eap_method_type **types,
 +                                 size_t *num_types)
 +{
 +      char *start, *pos, *buf;
 +      struct eap_method_type *methods = NULL, *_methods;
++      u32 method;
 +      size_t num_methods = 0, prefix_len;
 +
 +      if (config == NULL || config->phase2 == NULL)
 +              goto get_defaults;
 +
 +      start = buf = os_strdup(config->phase2);
 +      if (buf == NULL)
 +              return -1;
 +
 +      prefix_len = os_strlen(prefix);
 +
 +      while (start && *start != '\0') {
 +              int vendor;
 +              pos = os_strstr(start, prefix);
 +              if (pos == NULL)
 +                      break;
 +              if (start != pos && *(pos - 1) != ' ') {
 +                      start = pos + prefix_len;
 +                      continue;
 +              }
 +
 +              start = pos + prefix_len;
 +              pos = os_strchr(start, ' ');
 +              if (pos)
 +                      *pos++ = '\0';
 +              method = eap_get_phase2_type(start, &vendor);
 +              if (vendor == EAP_VENDOR_IETF && method == EAP_TYPE_NONE) {
 +                      wpa_printf(MSG_ERROR, "TLS: Unsupported Phase2 EAP "
 +                                 "method '%s'", start);
 +              } else {
 +                      num_methods++;
 +                      _methods = os_realloc_array(methods, num_methods,
 +                                                  sizeof(*methods));
 +                      if (_methods == NULL) {
 +                              os_free(methods);
 +                              os_free(buf);
 +                              return -1;
 +                      }
 +                      methods = _methods;
 +                      methods[num_methods - 1].vendor = vendor;
 +                      methods[num_methods - 1].method = method;
 +              }
 +
 +              start = pos;
 +      }
 +
 +      os_free(buf);
 +
 +get_defaults:
 +      if (methods == NULL)
 +              methods = eap_get_phase2_types(config, &num_methods);
 +
 +      if (methods == NULL) {
 +              wpa_printf(MSG_ERROR, "TLS: No Phase2 EAP methods available");
 +              return -1;
 +      }
 +      wpa_hexdump(MSG_DEBUG, "TLS: Phase2 EAP types",
 +                  (u8 *) methods,
 +                  num_methods * sizeof(struct eap_method_type));
 +
 +      *types = methods;
 +      *num_types = num_methods;
 +
 +      return 0;
 +}
 +
 +
 +/**
 + * eap_peer_tls_phase2_nak - Generate EAP-Nak for Phase 2
 + * @types: Buffer for returning allocated list of allowed EAP methods
 + * @num_types: Buffer for returning number of allocated EAP methods
 + * @hdr: EAP-Request header (and the following EAP type octet)
 + * @resp: Buffer for returning the EAP-Nak message
 + * Returns: 0 on success, -1 on failure
 + */
 +int eap_peer_tls_phase2_nak(struct eap_method_type *types, size_t num_types,
 +                          struct eap_hdr *hdr, struct wpabuf **resp)
 +{
 +      u8 *pos = (u8 *) (hdr + 1);
 +      size_t i;
 +
 +      /* TODO: add support for expanded Nak */
 +      wpa_printf(MSG_DEBUG, "TLS: Phase 2 Request: Nak type=%d", *pos);
 +      wpa_hexdump(MSG_DEBUG, "TLS: Allowed Phase2 EAP types",
 +                  (u8 *) types, num_types * sizeof(struct eap_method_type));
 +      *resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_NAK, num_types,
 +                            EAP_CODE_RESPONSE, hdr->identifier);
 +      if (*resp == NULL)
 +              return -1;
 +
 +      for (i = 0; i < num_types; i++) {
 +              if (types[i].vendor == EAP_VENDOR_IETF &&
 +                  types[i].method < 256)
 +                      wpabuf_put_u8(*resp, types[i].method);
 +      }
 +
 +      eap_update_len(*resp);
 +
 +      return 0;
 +}
index 390c2165927cb5cd9638099f5f6516ae6d31c95e,0000000000000000000000000000000000000000..acd2b783617feb2dabb70b068836293ebf573921
mode 100644,000000..100644
--- /dev/null
@@@ -1,132 -1,0 +1,132 @@@
-                               u8 id, const u8 *in_data, size_t in_len,
 +/*
 + * EAP peer: EAP-TLS/PEAP/TTLS/FAST common functions
 + * Copyright (c) 2004-2009, 2012, Jouni Malinen <j@w1.fi>
 + *
 + * This software may be distributed under the terms of the BSD license.
 + * See README for more details.
 + */
 +
 +#ifndef EAP_TLS_COMMON_H
 +#define EAP_TLS_COMMON_H
 +
 +/**
 + * struct eap_ssl_data - TLS data for EAP methods
 + */
 +struct eap_ssl_data {
 +      /**
 +       * conn - TLS connection context data from tls_connection_init()
 +       */
 +      struct tls_connection *conn;
 +
 +      /**
 +       * tls_out - TLS message to be sent out in fragments
 +       */
 +      struct wpabuf *tls_out;
 +
 +      /**
 +       * tls_out_pos - The current position in the outgoing TLS message
 +       */
 +      size_t tls_out_pos;
 +
 +      /**
 +       * tls_out_limit - Maximum fragment size for outgoing TLS messages
 +       */
 +      size_t tls_out_limit;
 +
 +      /**
 +       * tls_in - Received TLS message buffer for re-assembly
 +       */
 +      struct wpabuf *tls_in;
 +
 +      /**
 +       * tls_in_left - Number of remaining bytes in the incoming TLS message
 +       */
 +      size_t tls_in_left;
 +
 +      /**
 +       * tls_in_total - Total number of bytes in the incoming TLS message
 +       */
 +      size_t tls_in_total;
 +
 +      /**
 +       * phase2 - Whether this TLS connection is used in EAP phase 2 (tunnel)
 +       */
 +      int phase2;
 +
 +      /**
 +       * include_tls_length - Whether the TLS length field is included even
 +       * if the TLS data is not fragmented
 +       */
 +      int include_tls_length;
 +
 +      /**
 +       * eap - EAP state machine allocated with eap_peer_sm_init()
 +       */
 +      struct eap_sm *eap;
 +
 +      /**
 +       * ssl_ctx - TLS library context to use for the connection
 +       */
 +      void *ssl_ctx;
 +
 +      /**
 +       * eap_type - EAP method used in Phase 1 (EAP_TYPE_TLS/PEAP/TTLS/FAST)
 +       */
 +      u8 eap_type;
 +};
 +
 +
 +/* EAP TLS Flags */
 +#define EAP_TLS_FLAGS_LENGTH_INCLUDED 0x80
 +#define EAP_TLS_FLAGS_MORE_FRAGMENTS 0x40
 +#define EAP_TLS_FLAGS_START 0x20
 +#define EAP_TLS_VERSION_MASK 0x07
 +
 + /* could be up to 128 bytes, but only the first 64 bytes are used */
 +#define EAP_TLS_KEY_LEN 64
 +
 +/* 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,
 +                        struct eap_peer_config *config, u8 eap_type);
 +void eap_peer_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data);
 +u8 * eap_peer_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data,
 +                           const char *label, size_t len);
 +u8 * eap_peer_tls_derive_session_id(struct eap_sm *sm,
 +                                  struct eap_ssl_data *data, u8 eap_type,
 +                                  size_t *len);
 +int eap_peer_tls_process_helper(struct eap_sm *sm, struct eap_ssl_data *data,
 +                              EapType eap_type, int peap_version,
++                              u8 id, const struct wpabuf *in_data,
 +                              struct wpabuf **out_data);
 +struct wpabuf * eap_peer_tls_build_ack(u8 id, EapType eap_type,
 +                                     int peap_version);
 +int eap_peer_tls_reauth_init(struct eap_sm *sm, struct eap_ssl_data *data);
 +int eap_peer_tls_status(struct eap_sm *sm, struct eap_ssl_data *data,
 +                      char *buf, size_t buflen, int verbose);
 +const u8 * eap_peer_tls_process_init(struct eap_sm *sm,
 +                                   struct eap_ssl_data *data,
 +                                   EapType eap_type,
 +                                   struct eap_method_ret *ret,
 +                                   const struct wpabuf *reqData,
 +                                   size_t *len, u8 *flags);
 +void eap_peer_tls_reset_input(struct eap_ssl_data *data);
 +void eap_peer_tls_reset_output(struct eap_ssl_data *data);
 +int eap_peer_tls_decrypt(struct eap_sm *sm, struct eap_ssl_data *data,
 +                       const struct wpabuf *in_data,
 +                       struct wpabuf **in_decrypted);
 +int eap_peer_tls_encrypt(struct eap_sm *sm, struct eap_ssl_data *data,
 +                       EapType eap_type, int peap_version, u8 id,
 +                       const struct wpabuf *in_data,
 +                       struct wpabuf **out_data);
 +int eap_peer_select_phase2_methods(struct eap_peer_config *config,
 +                                 const char *prefix,
 +                                 struct eap_method_type **types,
 +                                 size_t *num_types);
 +int eap_peer_tls_phase2_nak(struct eap_method_type *types, size_t num_types,
 +                          struct eap_hdr *hdr, struct wpabuf **resp);
 +
 +#endif /* EAP_TLS_COMMON_H */
index b5c028b5276d587dcde9c1542c77f7a182f628a7,0000000000000000000000000000000000000000..b186c9156a741a03f3641c15c42903259bc6fc92
mode 100644,000000..100644
--- /dev/null
@@@ -1,1698 -1,0 +1,1721 @@@
-       avp->avp_length = host_to_be32((flags << 24) | (u32) (hdrlen + len));
 +/*
 + * EAP peer method: EAP-TTLS (RFC 5281)
 + * Copyright (c) 2004-2011, Jouni Malinen <j@w1.fi>
 + *
 + * This software may be distributed under the terms of the BSD license.
 + * See README for more details.
 + */
 +
 +#include "includes.h"
 +
 +#include "common.h"
 +#include "crypto/ms_funcs.h"
 +#include "crypto/sha1.h"
 +#include "crypto/tls.h"
 +#include "eap_common/chap.h"
 +#include "eap_common/eap_ttls.h"
 +#include "mschapv2.h"
 +#include "eap_i.h"
 +#include "eap_tls_common.h"
 +#include "eap_config.h"
 +
 +
 +#define EAP_TTLS_VERSION 0
 +
 +
 +static void eap_ttls_deinit(struct eap_sm *sm, void *priv);
 +
 +
 +struct eap_ttls_data {
 +      struct eap_ssl_data ssl;
 +
 +      int ttls_version;
 +
 +      const struct eap_method *phase2_method;
 +      void *phase2_priv;
 +      int phase2_success;
 +      int phase2_start;
 +
 +      enum phase2_types {
 +              EAP_TTLS_PHASE2_EAP,
 +              EAP_TTLS_PHASE2_MSCHAPV2,
 +              EAP_TTLS_PHASE2_MSCHAP,
 +              EAP_TTLS_PHASE2_PAP,
 +              EAP_TTLS_PHASE2_CHAP
 +      } phase2_type;
 +      struct eap_method_type phase2_eap_type;
 +      struct eap_method_type *phase2_eap_types;
 +      size_t num_phase2_eap_types;
 +
 +      u8 auth_response[MSCHAPV2_AUTH_RESPONSE_LEN];
 +      int auth_response_valid;
 +      u8 master_key[MSCHAPV2_MASTER_KEY_LEN]; /* MSCHAPv2 master key */
 +      u8 ident;
 +      int resuming; /* starting a resumed session */
 +      int reauth; /* reauthentication */
 +      u8 *key_data;
 +      u8 *session_id;
 +      size_t id_len;
 +
 +      struct wpabuf *pending_phase2_req;
 +
 +#ifdef EAP_TNC
 +      int ready_for_tnc;
 +      int tnc_started;
 +#endif /* EAP_TNC */
 +};
 +
 +
 +static void * eap_ttls_init(struct eap_sm *sm)
 +{
 +      struct eap_ttls_data *data;
 +      struct eap_peer_config *config = eap_get_config(sm);
 +      char *selected;
 +
 +      data = os_zalloc(sizeof(*data));
 +      if (data == NULL)
 +              return NULL;
 +      data->ttls_version = EAP_TTLS_VERSION;
 +      selected = "EAP";
 +      data->phase2_type = EAP_TTLS_PHASE2_EAP;
 +
 +      if (config && config->phase2) {
 +              if (os_strstr(config->phase2, "autheap=")) {
 +                      selected = "EAP";
 +                      data->phase2_type = EAP_TTLS_PHASE2_EAP;
 +              } else if (os_strstr(config->phase2, "auth=MSCHAPV2")) {
 +                      selected = "MSCHAPV2";
 +                      data->phase2_type = EAP_TTLS_PHASE2_MSCHAPV2;
 +              } else if (os_strstr(config->phase2, "auth=MSCHAP")) {
 +                      selected = "MSCHAP";
 +                      data->phase2_type = EAP_TTLS_PHASE2_MSCHAP;
 +              } else if (os_strstr(config->phase2, "auth=PAP")) {
 +                      selected = "PAP";
 +                      data->phase2_type = EAP_TTLS_PHASE2_PAP;
 +              } else if (os_strstr(config->phase2, "auth=CHAP")) {
 +                      selected = "CHAP";
 +                      data->phase2_type = EAP_TTLS_PHASE2_CHAP;
 +              }
 +      }
 +      wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase2 type: %s", selected);
 +
 +      if (data->phase2_type == EAP_TTLS_PHASE2_EAP) {
 +              if (eap_peer_select_phase2_methods(config, "autheap=",
 +                                                 &data->phase2_eap_types,
 +                                                 &data->num_phase2_eap_types)
 +                  < 0) {
 +                      eap_ttls_deinit(sm, data);
 +                      return NULL;
 +              }
 +
 +              data->phase2_eap_type.vendor = EAP_VENDOR_IETF;
 +              data->phase2_eap_type.method = EAP_TYPE_NONE;
 +      }
 +
 +      if (eap_peer_tls_ssl_init(sm, &data->ssl, config, EAP_TYPE_TTLS)) {
 +              wpa_printf(MSG_INFO, "EAP-TTLS: Failed to initialize SSL.");
 +              eap_ttls_deinit(sm, data);
 +              return NULL;
 +      }
 +
 +      return data;
 +}
 +
 +
 +static void eap_ttls_phase2_eap_deinit(struct eap_sm *sm,
 +                                     struct eap_ttls_data *data)
 +{
 +      if (data->phase2_priv && data->phase2_method) {
 +              data->phase2_method->deinit(sm, data->phase2_priv);
 +              data->phase2_method = NULL;
 +              data->phase2_priv = NULL;
 +      }
 +}
 +
 +
 +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;
 +      if (data == NULL)
 +              return;
 +      eap_ttls_phase2_eap_deinit(sm, data);
 +      os_free(data->phase2_eap_types);
 +      eap_peer_tls_ssl_deinit(sm, &data->ssl);
 +      eap_ttls_free_key(data);
 +      os_free(data->session_id);
 +      wpabuf_free(data->pending_phase2_req);
 +      os_free(data);
 +}
 +
 +
 +static u8 * eap_ttls_avp_hdr(u8 *avphdr, u32 avp_code, u32 vendor_id,
 +                           int mandatory, size_t len)
 +{
 +      struct ttls_avp_vendor *avp;
 +      u8 flags;
 +      size_t hdrlen;
 +
 +      avp = (struct ttls_avp_vendor *) avphdr;
 +      flags = mandatory ? AVP_FLAGS_MANDATORY : 0;
 +      if (vendor_id) {
 +              flags |= AVP_FLAGS_VENDOR;
 +              hdrlen = sizeof(*avp);
 +              avp->vendor_id = host_to_be32(vendor_id);
 +      } else {
 +              hdrlen = sizeof(struct ttls_avp);
 +      }
 +
 +      avp->avp_code = host_to_be32(avp_code);
-                                     const u8 *in_data, size_t in_len,
++      avp->avp_length = host_to_be32(((u32) flags << 24) |
++                                     (u32) (hdrlen + len));
 +
 +      return avphdr + hdrlen;
 +}
 +
 +
 +static u8 * eap_ttls_avp_add(u8 *start, u8 *avphdr, u32 avp_code,
 +                           u32 vendor_id, int mandatory,
 +                           const u8 *data, size_t len)
 +{
 +      u8 *pos;
 +      pos = eap_ttls_avp_hdr(avphdr, avp_code, vendor_id, mandatory, len);
 +      os_memcpy(pos, data, len);
 +      pos += len;
 +      AVP_PAD(start, pos);
 +      return pos;
 +}
 +
 +
 +static int eap_ttls_avp_encapsulate(struct wpabuf **resp, u32 avp_code,
 +                                  int mandatory)
 +{
 +      struct wpabuf *msg;
 +      u8 *avp, *pos;
 +
 +      msg = wpabuf_alloc(sizeof(struct ttls_avp) + wpabuf_len(*resp) + 4);
 +      if (msg == NULL) {
 +              wpabuf_free(*resp);
 +              *resp = NULL;
 +              return -1;
 +      }
 +
 +      avp = wpabuf_mhead(msg);
 +      pos = eap_ttls_avp_hdr(avp, avp_code, 0, mandatory, wpabuf_len(*resp));
 +      os_memcpy(pos, wpabuf_head(*resp), wpabuf_len(*resp));
 +      pos += wpabuf_len(*resp);
 +      AVP_PAD(avp, pos);
 +      wpabuf_free(*resp);
 +      wpabuf_put(msg, pos - avp);
 +      *resp = msg;
 +      return 0;
 +}
 +
 +
 +static int eap_ttls_v0_derive_key(struct eap_sm *sm,
 +                                struct eap_ttls_data *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_EMSK_LEN);
 +      if (!data->key_data) {
 +              wpa_printf(MSG_INFO, "EAP-TTLS: Failed to derive key");
 +              return -1;
 +      }
 +
 +      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,
 +                                                        EAP_TYPE_TTLS,
 +                                                        &data->id_len);
 +      if (data->session_id) {
 +              wpa_hexdump(MSG_DEBUG, "EAP-TTLS: Derived Session-Id",
 +                          data->session_id, data->id_len);
 +      } else {
 +              wpa_printf(MSG_ERROR, "EAP-TTLS: Failed to derive Session-Id");
 +      }
 +
 +      return 0;
 +}
 +
 +
++#ifndef CONFIG_FIPS
 +static u8 * eap_ttls_implicit_challenge(struct eap_sm *sm,
 +                                      struct eap_ttls_data *data, size_t len)
 +{
 +      return eap_peer_tls_derive_key(sm, &data->ssl, "ttls challenge", len);
 +}
++#endif /* CONFIG_FIPS */
 +
 +
 +static void eap_ttls_phase2_select_eap_method(struct eap_ttls_data *data,
 +                                            u8 method)
 +{
 +      size_t i;
 +      for (i = 0; i < data->num_phase2_eap_types; i++) {
 +              if (data->phase2_eap_types[i].vendor != EAP_VENDOR_IETF ||
 +                  data->phase2_eap_types[i].method != method)
 +                      continue;
 +
 +              data->phase2_eap_type.vendor =
 +                      data->phase2_eap_types[i].vendor;
 +              data->phase2_eap_type.method =
 +                      data->phase2_eap_types[i].method;
 +              wpa_printf(MSG_DEBUG, "EAP-TTLS: Selected "
 +                         "Phase 2 EAP vendor %d method %d",
 +                         data->phase2_eap_type.vendor,
 +                         data->phase2_eap_type.method);
 +              break;
 +      }
 +}
 +
 +
 +static int eap_ttls_phase2_eap_process(struct eap_sm *sm,
 +                                     struct eap_ttls_data *data,
 +                                     struct eap_method_ret *ret,
 +                                     struct eap_hdr *hdr, size_t len,
 +                                     struct wpabuf **resp)
 +{
 +      struct wpabuf msg;
 +      struct eap_method_ret iret;
 +
 +      os_memset(&iret, 0, sizeof(iret));
 +      wpabuf_set(&msg, hdr, len);
 +      *resp = data->phase2_method->process(sm, data->phase2_priv, &iret,
 +                                           &msg);
 +      if ((iret.methodState == METHOD_DONE ||
 +           iret.methodState == METHOD_MAY_CONT) &&
 +          (iret.decision == DECISION_UNCOND_SUCC ||
 +           iret.decision == DECISION_COND_SUCC ||
 +           iret.decision == DECISION_FAIL)) {
 +              ret->methodState = iret.methodState;
 +              ret->decision = iret.decision;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +static int eap_ttls_phase2_request_eap_method(struct eap_sm *sm,
 +                                            struct eap_ttls_data *data,
 +                                            struct eap_method_ret *ret,
 +                                            struct eap_hdr *hdr, size_t len,
 +                                            u8 method, struct wpabuf **resp)
 +{
 +#ifdef EAP_TNC
 +      if (data->tnc_started && data->phase2_method &&
 +          data->phase2_priv && method == EAP_TYPE_TNC &&
 +          data->phase2_eap_type.method == EAP_TYPE_TNC)
 +              return eap_ttls_phase2_eap_process(sm, data, ret, hdr, len,
 +                                                 resp);
 +
 +      if (data->ready_for_tnc && !data->tnc_started &&
 +          method == EAP_TYPE_TNC) {
 +              wpa_printf(MSG_DEBUG, "EAP-TTLS: Start TNC after completed "
 +                         "EAP method");
 +              data->tnc_started = 1;
 +      }
 +
 +      if (data->tnc_started) {
 +              if (data->phase2_eap_type.vendor != EAP_VENDOR_IETF ||
 +                  data->phase2_eap_type.method == EAP_TYPE_TNC) {
 +                      wpa_printf(MSG_DEBUG, "EAP-TTLS: Unexpected EAP "
 +                                 "type %d for TNC", method);
 +                      return -1;
 +              }
 +
 +              data->phase2_eap_type.vendor = EAP_VENDOR_IETF;
 +              data->phase2_eap_type.method = method;
 +              wpa_printf(MSG_DEBUG, "EAP-TTLS: Selected "
 +                         "Phase 2 EAP vendor %d method %d (TNC)",
 +                         data->phase2_eap_type.vendor,
 +                         data->phase2_eap_type.method);
 +
 +              if (data->phase2_type == EAP_TTLS_PHASE2_EAP)
 +                      eap_ttls_phase2_eap_deinit(sm, data);
 +      }
 +#endif /* EAP_TNC */
 +
 +      if (data->phase2_eap_type.vendor == EAP_VENDOR_IETF &&
 +          data->phase2_eap_type.method == EAP_TYPE_NONE)
 +              eap_ttls_phase2_select_eap_method(data, method);
 +
 +      if (method != data->phase2_eap_type.method || method == EAP_TYPE_NONE)
 +      {
 +              if (eap_peer_tls_phase2_nak(data->phase2_eap_types,
 +                                          data->num_phase2_eap_types,
 +                                          hdr, resp))
 +                      return -1;
 +              return 0;
 +      }
 +
 +      if (data->phase2_priv == NULL) {
 +              data->phase2_method = eap_peer_get_eap_method(
 +                      EAP_VENDOR_IETF, method);
 +              if (data->phase2_method) {
 +                      sm->init_phase2 = 1;
 +                      data->phase2_priv = data->phase2_method->init(sm);
 +                      sm->init_phase2 = 0;
 +              }
 +      }
 +      if (data->phase2_priv == NULL || data->phase2_method == NULL) {
 +              wpa_printf(MSG_INFO, "EAP-TTLS: failed to initialize "
 +                         "Phase 2 EAP method %d", method);
 +              return -1;
 +      }
 +
 +      return eap_ttls_phase2_eap_process(sm, data, ret, hdr, len, resp);
 +}
 +
 +
 +static int eap_ttls_phase2_request_eap(struct eap_sm *sm,
 +                                     struct eap_ttls_data *data,
 +                                     struct eap_method_ret *ret,
 +                                     struct eap_hdr *hdr,
 +                                     struct wpabuf **resp)
 +{
 +      size_t len = be_to_host16(hdr->length);
 +      u8 *pos;
 +      struct eap_peer_config *config = eap_get_config(sm);
 +
 +      if (len <= sizeof(struct eap_hdr)) {
 +              wpa_printf(MSG_INFO, "EAP-TTLS: too short "
 +                         "Phase 2 request (len=%lu)", (unsigned long) len);
 +              return -1;
 +      }
 +      pos = (u8 *) (hdr + 1);
 +      wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 EAP Request: type=%d", *pos);
 +      switch (*pos) {
 +      case EAP_TYPE_IDENTITY:
 +              *resp = eap_sm_buildIdentity(sm, hdr->identifier, 1);
 +              break;
 +      default:
 +              if (eap_ttls_phase2_request_eap_method(sm, data, ret, hdr, len,
 +                                                     *pos, resp) < 0)
 +                      return -1;
 +              break;
 +      }
 +
 +      if (*resp == NULL &&
 +          (config->pending_req_identity || config->pending_req_password ||
 +           config->pending_req_otp)) {
 +              return 0;
 +      }
 +
 +      if (*resp == NULL)
 +              return -1;
 +
 +      wpa_hexdump_buf(MSG_DEBUG, "EAP-TTLS: AVP encapsulate EAP Response",
 +                      *resp);
 +      return eap_ttls_avp_encapsulate(resp, RADIUS_ATTR_EAP_MESSAGE, 1);
 +}
 +
 +
 +static int eap_ttls_phase2_request_mschapv2(struct eap_sm *sm,
 +                                          struct eap_ttls_data *data,
 +                                          struct eap_method_ret *ret,
 +                                          struct wpabuf **resp)
 +{
++#ifdef CONFIG_FIPS
++      wpa_printf(MSG_ERROR, "EAP-TTLS: MSCHAPV2 not supported in FIPS build");
++      return -1;
++#else /* CONFIG_FIPS */
 +#ifdef EAP_MSCHAPv2
 +      struct wpabuf *msg;
 +      u8 *buf, *pos, *challenge, *peer_challenge;
 +      const u8 *identity, *password;
 +      size_t identity_len, password_len;
 +      int pwhash;
 +
 +      wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 MSCHAPV2 Request");
 +
 +      identity = eap_get_config_identity(sm, &identity_len);
 +      password = eap_get_config_password2(sm, &password_len, &pwhash);
 +      if (identity == NULL || password == NULL)
 +              return -1;
 +
 +      msg = wpabuf_alloc(identity_len + 1000);
 +      if (msg == NULL) {
 +              wpa_printf(MSG_ERROR,
 +                         "EAP-TTLS/MSCHAPV2: Failed to allocate memory");
 +              return -1;
 +      }
 +      pos = buf = wpabuf_mhead(msg);
 +
 +      /* User-Name */
 +      pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_USER_NAME, 0, 1,
 +                             identity, identity_len);
 +
 +      /* MS-CHAP-Challenge */
 +      challenge = eap_ttls_implicit_challenge(
 +              sm, data, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN + 1);
 +      if (challenge == NULL) {
 +              wpabuf_free(msg);
 +              wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAPV2: Failed to derive "
 +                         "implicit challenge");
 +              return -1;
 +      }
 +
 +      pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_MS_CHAP_CHALLENGE,
 +                             RADIUS_VENDOR_ID_MICROSOFT, 1,
 +                             challenge, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN);
 +
 +      /* MS-CHAP2-Response */
 +      pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_MS_CHAP2_RESPONSE,
 +                             RADIUS_VENDOR_ID_MICROSOFT, 1,
 +                             EAP_TTLS_MSCHAPV2_RESPONSE_LEN);
 +      data->ident = challenge[EAP_TTLS_MSCHAPV2_CHALLENGE_LEN];
 +      *pos++ = data->ident;
 +      *pos++ = 0; /* Flags */
 +      if (os_get_random(pos, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN) < 0) {
 +              os_free(challenge);
 +              wpabuf_free(msg);
 +              wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAPV2: Failed to get "
 +                         "random data for peer challenge");
 +              return -1;
 +      }
 +      peer_challenge = pos;
 +      pos += EAP_TTLS_MSCHAPV2_CHALLENGE_LEN;
 +      os_memset(pos, 0, 8); /* Reserved, must be zero */
 +      pos += 8;
 +      if (mschapv2_derive_response(identity, identity_len, password,
 +                                   password_len, pwhash, challenge,
 +                                   peer_challenge, pos, data->auth_response,
 +                                   data->master_key)) {
 +              os_free(challenge);
 +              wpabuf_free(msg);
 +              wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAPV2: Failed to derive "
 +                         "response");
 +              return -1;
 +      }
 +      data->auth_response_valid = 1;
 +
 +      pos += 24;
 +      os_free(challenge);
 +      AVP_PAD(buf, pos);
 +
 +      wpabuf_put(msg, pos - buf);
 +      *resp = msg;
 +
 +      return 0;
 +#else /* EAP_MSCHAPv2 */
 +      wpa_printf(MSG_ERROR, "EAP-TTLS: MSCHAPv2 not included in the build");
 +      return -1;
 +#endif /* EAP_MSCHAPv2 */
++#endif /* CONFIG_FIPS */
 +}
 +
 +
 +static int eap_ttls_phase2_request_mschap(struct eap_sm *sm,
 +                                        struct eap_ttls_data *data,
 +                                        struct eap_method_ret *ret,
 +                                        struct wpabuf **resp)
 +{
++#ifdef CONFIG_FIPS
++      wpa_printf(MSG_ERROR, "EAP-TTLS: MSCHAP not supported in FIPS build");
++      return -1;
++#else /* CONFIG_FIPS */
 +      struct wpabuf *msg;
 +      u8 *buf, *pos, *challenge;
 +      const u8 *identity, *password;
 +      size_t identity_len, password_len;
 +      int pwhash;
 +
 +      wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 MSCHAP Request");
 +
 +      identity = eap_get_config_identity(sm, &identity_len);
 +      password = eap_get_config_password2(sm, &password_len, &pwhash);
 +      if (identity == NULL || password == NULL)
 +              return -1;
 +
 +      msg = wpabuf_alloc(identity_len + 1000);
 +      if (msg == NULL) {
 +              wpa_printf(MSG_ERROR,
 +                         "EAP-TTLS/MSCHAP: Failed to allocate memory");
 +              return -1;
 +      }
 +      pos = buf = wpabuf_mhead(msg);
 +
 +      /* User-Name */
 +      pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_USER_NAME, 0, 1,
 +                             identity, identity_len);
 +
 +      /* MS-CHAP-Challenge */
 +      challenge = eap_ttls_implicit_challenge(
 +              sm, data, EAP_TTLS_MSCHAP_CHALLENGE_LEN + 1);
 +      if (challenge == NULL) {
 +              wpabuf_free(msg);
 +              wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAP: Failed to derive "
 +                         "implicit challenge");
 +              return -1;
 +      }
 +
 +      pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_MS_CHAP_CHALLENGE,
 +                             RADIUS_VENDOR_ID_MICROSOFT, 1,
 +                             challenge, EAP_TTLS_MSCHAP_CHALLENGE_LEN);
 +
 +      /* MS-CHAP-Response */
 +      pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_MS_CHAP_RESPONSE,
 +                             RADIUS_VENDOR_ID_MICROSOFT, 1,
 +                             EAP_TTLS_MSCHAP_RESPONSE_LEN);
 +      data->ident = challenge[EAP_TTLS_MSCHAP_CHALLENGE_LEN];
 +      *pos++ = data->ident;
 +      *pos++ = 1; /* Flags: Use NT style passwords */
 +      os_memset(pos, 0, 24); /* LM-Response */
 +      pos += 24;
 +      if (pwhash) {
 +              challenge_response(challenge, password, pos); /* NT-Response */
 +              wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: MSCHAP password hash",
 +                              password, 16);
 +      } else {
 +              nt_challenge_response(challenge, password, password_len,
 +                                    pos); /* NT-Response */
 +              wpa_hexdump_ascii_key(MSG_DEBUG, "EAP-TTLS: MSCHAP password",
 +                                    password, password_len);
 +      }
 +      wpa_hexdump(MSG_DEBUG, "EAP-TTLS: MSCHAP implicit challenge",
 +                  challenge, EAP_TTLS_MSCHAP_CHALLENGE_LEN);
 +      wpa_hexdump(MSG_DEBUG, "EAP-TTLS: MSCHAP response", pos, 24);
 +      pos += 24;
 +      os_free(challenge);
 +      AVP_PAD(buf, pos);
 +
 +      wpabuf_put(msg, pos - buf);
 +      *resp = msg;
 +
 +      /* EAP-TTLS/MSCHAP does not provide tunneled success
 +       * notification, so assume that Phase2 succeeds. */
 +      ret->methodState = METHOD_DONE;
 +      ret->decision = DECISION_COND_SUCC;
 +
 +      return 0;
++#endif /* CONFIG_FIPS */
 +}
 +
 +
 +static int eap_ttls_phase2_request_pap(struct eap_sm *sm,
 +                                     struct eap_ttls_data *data,
 +                                     struct eap_method_ret *ret,
 +                                     struct wpabuf **resp)
 +{
 +      struct wpabuf *msg;
 +      u8 *buf, *pos;
 +      size_t pad;
 +      const u8 *identity, *password;
 +      size_t identity_len, password_len;
 +
 +      wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 PAP Request");
 +
 +      identity = eap_get_config_identity(sm, &identity_len);
 +      password = eap_get_config_password(sm, &password_len);
 +      if (identity == NULL || password == NULL)
 +              return -1;
 +
 +      msg = wpabuf_alloc(identity_len + password_len + 100);
 +      if (msg == NULL) {
 +              wpa_printf(MSG_ERROR,
 +                         "EAP-TTLS/PAP: Failed to allocate memory");
 +              return -1;
 +      }
 +      pos = buf = wpabuf_mhead(msg);
 +
 +      /* User-Name */
 +      pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_USER_NAME, 0, 1,
 +                             identity, identity_len);
 +
 +      /* User-Password; in RADIUS, this is encrypted, but EAP-TTLS encrypts
 +       * the data, so no separate encryption is used in the AVP itself.
 +       * However, the password is padded to obfuscate its length. */
 +      pad = password_len == 0 ? 16 : (16 - (password_len & 15)) & 15;
 +      pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_USER_PASSWORD, 0, 1,
 +                             password_len + pad);
 +      os_memcpy(pos, password, password_len);
 +      pos += password_len;
 +      os_memset(pos, 0, pad);
 +      pos += pad;
 +      AVP_PAD(buf, pos);
 +
 +      wpabuf_put(msg, pos - buf);
 +      *resp = msg;
 +
 +      /* EAP-TTLS/PAP does not provide tunneled success notification,
 +       * so assume that Phase2 succeeds. */
 +      ret->methodState = METHOD_DONE;
 +      ret->decision = DECISION_COND_SUCC;
 +
 +      return 0;
 +}
 +
 +
 +static int eap_ttls_phase2_request_chap(struct eap_sm *sm,
 +                                      struct eap_ttls_data *data,
 +                                      struct eap_method_ret *ret,
 +                                      struct wpabuf **resp)
 +{
++#ifdef CONFIG_FIPS
++      wpa_printf(MSG_ERROR, "EAP-TTLS: CHAP not supported in FIPS build");
++      return -1;
++#else /* CONFIG_FIPS */
 +      struct wpabuf *msg;
 +      u8 *buf, *pos, *challenge;
 +      const u8 *identity, *password;
 +      size_t identity_len, password_len;
 +
 +      wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 CHAP Request");
 +
 +      identity = eap_get_config_identity(sm, &identity_len);
 +      password = eap_get_config_password(sm, &password_len);
 +      if (identity == NULL || password == NULL)
 +              return -1;
 +
 +      msg = wpabuf_alloc(identity_len + 1000);
 +      if (msg == NULL) {
 +              wpa_printf(MSG_ERROR,
 +                         "EAP-TTLS/CHAP: Failed to allocate memory");
 +              return -1;
 +      }
 +      pos = buf = wpabuf_mhead(msg);
 +
 +      /* User-Name */
 +      pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_USER_NAME, 0, 1,
 +                             identity, identity_len);
 +
 +      /* CHAP-Challenge */
 +      challenge = eap_ttls_implicit_challenge(
 +              sm, data, EAP_TTLS_CHAP_CHALLENGE_LEN + 1);
 +      if (challenge == NULL) {
 +              wpabuf_free(msg);
 +              wpa_printf(MSG_ERROR, "EAP-TTLS/CHAP: Failed to derive "
 +                         "implicit challenge");
 +              return -1;
 +      }
 +
 +      pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_CHAP_CHALLENGE, 0, 1,
 +                             challenge, EAP_TTLS_CHAP_CHALLENGE_LEN);
 +
 +      /* CHAP-Password */
 +      pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_CHAP_PASSWORD, 0, 1,
 +                             1 + EAP_TTLS_CHAP_PASSWORD_LEN);
 +      data->ident = challenge[EAP_TTLS_CHAP_CHALLENGE_LEN];
 +      *pos++ = data->ident;
 +
 +      /* MD5(Ident + Password + Challenge) */
 +      chap_md5(data->ident, password, password_len, challenge,
 +               EAP_TTLS_CHAP_CHALLENGE_LEN, pos);
 +
 +      wpa_hexdump_ascii(MSG_DEBUG, "EAP-TTLS: CHAP username",
 +                        identity, identity_len);
 +      wpa_hexdump_ascii_key(MSG_DEBUG, "EAP-TTLS: CHAP password",
 +                            password, password_len);
 +      wpa_hexdump(MSG_DEBUG, "EAP-TTLS: CHAP implicit challenge",
 +                  challenge, EAP_TTLS_CHAP_CHALLENGE_LEN);
 +      wpa_hexdump(MSG_DEBUG, "EAP-TTLS: CHAP password",
 +                  pos, EAP_TTLS_CHAP_PASSWORD_LEN);
 +      pos += EAP_TTLS_CHAP_PASSWORD_LEN;
 +      os_free(challenge);
 +      AVP_PAD(buf, pos);
 +
 +      wpabuf_put(msg, pos - buf);
 +      *resp = msg;
 +
 +      /* EAP-TTLS/CHAP does not provide tunneled success
 +       * notification, so assume that Phase2 succeeds. */
 +      ret->methodState = METHOD_DONE;
 +      ret->decision = DECISION_COND_SUCC;
 +
 +      return 0;
++#endif /* CONFIG_FIPS */
 +}
 +
 +
 +static int eap_ttls_phase2_request(struct eap_sm *sm,
 +                                 struct eap_ttls_data *data,
 +                                 struct eap_method_ret *ret,
 +                                 struct eap_hdr *hdr,
 +                                 struct wpabuf **resp)
 +{
 +      int res = 0;
 +      size_t len;
 +      enum phase2_types phase2_type = data->phase2_type;
 +
 +#ifdef EAP_TNC
 +      if (data->tnc_started) {
 +              wpa_printf(MSG_DEBUG, "EAP-TTLS: Processing TNC");
 +              phase2_type = EAP_TTLS_PHASE2_EAP;
 +      }
 +#endif /* EAP_TNC */
 +
 +      if (phase2_type == EAP_TTLS_PHASE2_MSCHAPV2 ||
 +          phase2_type == EAP_TTLS_PHASE2_MSCHAP ||
 +          phase2_type == EAP_TTLS_PHASE2_PAP ||
 +          phase2_type == EAP_TTLS_PHASE2_CHAP) {
 +              if (eap_get_config_identity(sm, &len) == NULL) {
 +                      wpa_printf(MSG_INFO,
 +                                 "EAP-TTLS: Identity not configured");
 +                      eap_sm_request_identity(sm);
 +                      if (eap_get_config_password(sm, &len) == NULL)
 +                              eap_sm_request_password(sm);
 +                      return 0;
 +              }
 +
 +              if (eap_get_config_password(sm, &len) == NULL) {
 +                      wpa_printf(MSG_INFO,
 +                                 "EAP-TTLS: Password not configured");
 +                      eap_sm_request_password(sm);
 +                      return 0;
 +              }
 +      }
 +
 +      switch (phase2_type) {
 +      case EAP_TTLS_PHASE2_EAP:
 +              res = eap_ttls_phase2_request_eap(sm, data, ret, hdr, resp);
 +              break;
 +      case EAP_TTLS_PHASE2_MSCHAPV2:
 +              res = eap_ttls_phase2_request_mschapv2(sm, data, ret, resp);
 +              break;
 +      case EAP_TTLS_PHASE2_MSCHAP:
 +              res = eap_ttls_phase2_request_mschap(sm, data, ret, resp);
 +              break;
 +      case EAP_TTLS_PHASE2_PAP:
 +              res = eap_ttls_phase2_request_pap(sm, data, ret, resp);
 +              break;
 +      case EAP_TTLS_PHASE2_CHAP:
 +              res = eap_ttls_phase2_request_chap(sm, data, ret, resp);
 +              break;
 +      default:
 +              wpa_printf(MSG_ERROR, "EAP-TTLS: Phase 2 - Unknown");
 +              res = -1;
 +              break;
 +      }
 +
 +      if (res < 0) {
 +              ret->methodState = METHOD_DONE;
 +              ret->decision = DECISION_FAIL;
 +      }
 +
 +      return res;
 +}
 +
 +
 +struct ttls_parse_avp {
 +      u8 *mschapv2;
 +      u8 *eapdata;
 +      size_t eap_len;
 +      int mschapv2_error;
 +};
 +
 +
 +static int eap_ttls_parse_attr_eap(const u8 *dpos, size_t dlen,
 +                                 struct ttls_parse_avp *parse)
 +{
 +      wpa_printf(MSG_DEBUG, "EAP-TTLS: AVP - EAP Message");
 +      if (parse->eapdata == NULL) {
 +              parse->eapdata = os_malloc(dlen);
 +              if (parse->eapdata == NULL) {
 +                      wpa_printf(MSG_WARNING, "EAP-TTLS: Failed to allocate "
 +                                 "memory for Phase 2 EAP data");
 +                      return -1;
 +              }
 +              os_memcpy(parse->eapdata, dpos, dlen);
 +              parse->eap_len = dlen;
 +      } else {
 +              u8 *neweap = os_realloc(parse->eapdata, parse->eap_len + dlen);
 +              if (neweap == NULL) {
 +                      wpa_printf(MSG_WARNING, "EAP-TTLS: Failed to allocate "
 +                                 "memory for Phase 2 EAP data");
 +                      return -1;
 +              }
 +              os_memcpy(neweap + parse->eap_len, dpos, dlen);
 +              parse->eapdata = neweap;
 +              parse->eap_len += dlen;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +static int eap_ttls_parse_avp(u8 *pos, size_t left,
 +                            struct ttls_parse_avp *parse)
 +{
 +      struct ttls_avp *avp;
 +      u32 avp_code, avp_length, vendor_id = 0;
 +      u8 avp_flags, *dpos;
 +      size_t dlen;
 +
 +      avp = (struct ttls_avp *) pos;
 +      avp_code = be_to_host32(avp->avp_code);
 +      avp_length = be_to_host32(avp->avp_length);
 +      avp_flags = (avp_length >> 24) & 0xff;
 +      avp_length &= 0xffffff;
 +      wpa_printf(MSG_DEBUG, "EAP-TTLS: AVP: code=%d flags=0x%02x "
 +                 "length=%d", (int) avp_code, avp_flags,
 +                 (int) avp_length);
 +
 +      if (avp_length > left) {
 +              wpa_printf(MSG_WARNING, "EAP-TTLS: AVP overflow "
 +                         "(len=%d, left=%lu) - dropped",
 +                         (int) avp_length, (unsigned long) left);
 +              return -1;
 +      }
 +
 +      if (avp_length < sizeof(*avp)) {
 +              wpa_printf(MSG_WARNING, "EAP-TTLS: Invalid AVP length %d",
 +                         avp_length);
 +              return -1;
 +      }
 +
 +      dpos = (u8 *) (avp + 1);
 +      dlen = avp_length - sizeof(*avp);
 +      if (avp_flags & AVP_FLAGS_VENDOR) {
 +              if (dlen < 4) {
 +                      wpa_printf(MSG_WARNING, "EAP-TTLS: Vendor AVP "
 +                                 "underflow");
 +                      return -1;
 +              }
 +              vendor_id = WPA_GET_BE32(dpos);
 +              wpa_printf(MSG_DEBUG, "EAP-TTLS: AVP vendor_id %d",
 +                         (int) vendor_id);
 +              dpos += 4;
 +              dlen -= 4;
 +      }
 +
 +      wpa_hexdump(MSG_DEBUG, "EAP-TTLS: AVP data", dpos, dlen);
 +
 +      if (vendor_id == 0 && avp_code == RADIUS_ATTR_EAP_MESSAGE) {
 +              if (eap_ttls_parse_attr_eap(dpos, dlen, parse) < 0)
 +                      return -1;
 +      } else if (vendor_id == 0 && avp_code == RADIUS_ATTR_REPLY_MESSAGE) {
 +              /* This is an optional message that can be displayed to
 +               * the user. */
 +              wpa_hexdump_ascii(MSG_DEBUG, "EAP-TTLS: AVP - Reply-Message",
 +                                dpos, dlen);
 +      } else if (vendor_id == RADIUS_VENDOR_ID_MICROSOFT &&
 +                 avp_code == RADIUS_ATTR_MS_CHAP2_SUCCESS) {
 +              wpa_hexdump_ascii(MSG_DEBUG, "EAP-TTLS: MS-CHAP2-Success",
 +                                dpos, dlen);
 +              if (dlen != 43) {
 +                      wpa_printf(MSG_WARNING, "EAP-TTLS: Unexpected "
 +                                 "MS-CHAP2-Success length "
 +                                 "(len=%lu, expected 43)",
 +                                 (unsigned long) dlen);
 +                      return -1;
 +              }
 +              parse->mschapv2 = dpos;
 +      } else if (vendor_id == RADIUS_VENDOR_ID_MICROSOFT &&
 +                 avp_code == RADIUS_ATTR_MS_CHAP_ERROR) {
 +              wpa_hexdump_ascii(MSG_DEBUG, "EAP-TTLS: MS-CHAP-Error",
 +                                dpos, dlen);
 +              parse->mschapv2_error = 1;
 +      } else if (avp_flags & AVP_FLAGS_MANDATORY) {
 +              wpa_printf(MSG_WARNING, "EAP-TTLS: Unsupported mandatory AVP "
 +                         "code %d vendor_id %d - dropped",
 +                         (int) avp_code, (int) vendor_id);
 +              return -1;
 +      } else {
 +              wpa_printf(MSG_DEBUG, "EAP-TTLS: Ignoring unsupported AVP "
 +                         "code %d vendor_id %d",
 +                         (int) avp_code, (int) vendor_id);
 +      }
 +
 +      return avp_length;
 +}
 +
 +
 +static int eap_ttls_parse_avps(struct wpabuf *in_decrypted,
 +                             struct ttls_parse_avp *parse)
 +{
 +      u8 *pos;
 +      size_t left, pad;
 +      int avp_length;
 +
 +      pos = wpabuf_mhead(in_decrypted);
 +      left = wpabuf_len(in_decrypted);
 +      wpa_hexdump(MSG_DEBUG, "EAP-TTLS: Decrypted Phase 2 AVPs", pos, left);
 +      if (left < sizeof(struct ttls_avp)) {
 +              wpa_printf(MSG_WARNING, "EAP-TTLS: Too short Phase 2 AVP frame"
 +                         " len=%lu expected %lu or more - dropped",
 +                         (unsigned long) left,
 +                         (unsigned long) sizeof(struct ttls_avp));
 +              return -1;
 +      }
 +
 +      /* Parse AVPs */
 +      os_memset(parse, 0, sizeof(*parse));
 +
 +      while (left > 0) {
 +              avp_length = eap_ttls_parse_avp(pos, left, parse);
 +              if (avp_length < 0)
 +                      return -1;
 +
 +              pad = (4 - (avp_length & 3)) & 3;
 +              pos += avp_length + pad;
 +              if (left < avp_length + pad)
 +                      left = 0;
 +              else
 +                      left -= avp_length + pad;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +static u8 * eap_ttls_fake_identity_request(void)
 +{
 +      struct eap_hdr *hdr;
 +      u8 *buf;
 +
 +      wpa_printf(MSG_DEBUG, "EAP-TTLS: empty data in beginning of "
 +                 "Phase 2 - use fake EAP-Request Identity");
 +      buf = os_malloc(sizeof(*hdr) + 1);
 +      if (buf == NULL) {
 +              wpa_printf(MSG_WARNING, "EAP-TTLS: failed to allocate "
 +                         "memory for fake EAP-Identity Request");
 +              return NULL;
 +      }
 +
 +      hdr = (struct eap_hdr *) buf;
 +      hdr->code = EAP_CODE_REQUEST;
 +      hdr->identifier = 0;
 +      hdr->length = host_to_be16(sizeof(*hdr) + 1);
 +      buf[sizeof(*hdr)] = EAP_TYPE_IDENTITY;
 +
 +      return buf;
 +}
 +
 +
 +static int eap_ttls_encrypt_response(struct eap_sm *sm,
 +                                   struct eap_ttls_data *data,
 +                                   struct wpabuf *resp, u8 identifier,
 +                                   struct wpabuf **out_data)
 +{
 +      if (resp == NULL)
 +              return 0;
 +
 +      wpa_hexdump_buf_key(MSG_DEBUG, "EAP-TTLS: Encrypting Phase 2 data",
 +                          resp);
 +      if (eap_peer_tls_encrypt(sm, &data->ssl, EAP_TYPE_TTLS,
 +                               data->ttls_version, identifier,
 +                               resp, out_data)) {
 +              wpa_printf(MSG_INFO, "EAP-TTLS: Failed to encrypt a Phase 2 "
 +                         "frame");
 +              wpabuf_free(resp);
 +              return -1;
 +      }
 +      wpabuf_free(resp);
 +
 +      return 0;
 +}
 +
 +
 +static int eap_ttls_process_phase2_eap(struct eap_sm *sm,
 +                                     struct eap_ttls_data *data,
 +                                     struct eap_method_ret *ret,
 +                                     struct ttls_parse_avp *parse,
 +                                     struct wpabuf **resp)
 +{
 +      struct eap_hdr *hdr;
 +      size_t len;
 +
 +      if (parse->eapdata == NULL) {
 +              wpa_printf(MSG_WARNING, "EAP-TTLS: No EAP Message in the "
 +                         "packet - dropped");
 +              return -1;
 +      }
 +
 +      wpa_hexdump(MSG_DEBUG, "EAP-TTLS: Phase 2 EAP",
 +                  parse->eapdata, parse->eap_len);
 +      hdr = (struct eap_hdr *) parse->eapdata;
 +
 +      if (parse->eap_len < sizeof(*hdr)) {
 +              wpa_printf(MSG_WARNING, "EAP-TTLS: Too short Phase 2 EAP "
 +                         "frame (len=%lu, expected %lu or more) - dropped",
 +                         (unsigned long) parse->eap_len,
 +                         (unsigned long) sizeof(*hdr));
 +              return -1;
 +      }
 +      len = be_to_host16(hdr->length);
 +      if (len > parse->eap_len) {
 +              wpa_printf(MSG_INFO, "EAP-TTLS: Length mismatch in Phase 2 "
 +                         "EAP frame (EAP hdr len=%lu, EAP data len in "
 +                         "AVP=%lu)",
 +                         (unsigned long) len,
 +                         (unsigned long) parse->eap_len);
 +              return -1;
 +      }
 +      wpa_printf(MSG_DEBUG, "EAP-TTLS: received Phase 2: code=%d "
 +                 "identifier=%d length=%lu",
 +                 hdr->code, hdr->identifier, (unsigned long) len);
 +      switch (hdr->code) {
 +      case EAP_CODE_REQUEST:
 +              if (eap_ttls_phase2_request(sm, data, ret, hdr, resp)) {
 +                      wpa_printf(MSG_INFO, "EAP-TTLS: Phase2 Request "
 +                                 "processing failed");
 +                      return -1;
 +              }
 +              break;
 +      default:
 +              wpa_printf(MSG_INFO, "EAP-TTLS: Unexpected code=%d in "
 +                         "Phase 2 EAP header", hdr->code);
 +              return -1;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +static int eap_ttls_process_phase2_mschapv2(struct eap_sm *sm,
 +                                          struct eap_ttls_data *data,
 +                                          struct eap_method_ret *ret,
 +                                          struct ttls_parse_avp *parse)
 +{
 +#ifdef EAP_MSCHAPv2
 +      if (parse->mschapv2_error) {
 +              wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Received "
 +                         "MS-CHAP-Error - failed");
 +              ret->methodState = METHOD_DONE;
 +              ret->decision = DECISION_FAIL;
 +              /* Reply with empty data to ACK error */
 +              return 1;
 +      }
 +
 +      if (parse->mschapv2 == NULL) {
 +#ifdef EAP_TNC
 +              if (data->phase2_success && parse->eapdata) {
 +                      /*
 +                       * Allow EAP-TNC to be started after successfully
 +                       * completed MSCHAPV2.
 +                       */
 +                      return 1;
 +              }
 +#endif /* EAP_TNC */
 +              wpa_printf(MSG_WARNING, "EAP-TTLS: no MS-CHAP2-Success AVP "
 +                         "received for Phase2 MSCHAPV2");
 +              return -1;
 +      }
 +      if (parse->mschapv2[0] != data->ident) {
 +              wpa_printf(MSG_WARNING, "EAP-TTLS: Ident mismatch for Phase 2 "
 +                         "MSCHAPV2 (received Ident 0x%02x, expected 0x%02x)",
 +                         parse->mschapv2[0], data->ident);
 +              return -1;
 +      }
 +      if (!data->auth_response_valid ||
 +          mschapv2_verify_auth_response(data->auth_response,
 +                                        parse->mschapv2 + 1, 42)) {
 +              wpa_printf(MSG_WARNING, "EAP-TTLS: Invalid authenticator "
 +                         "response in Phase 2 MSCHAPV2 success request");
 +              return -1;
 +      }
 +
 +      wpa_printf(MSG_INFO, "EAP-TTLS: Phase 2 MSCHAPV2 "
 +                 "authentication succeeded");
 +      ret->methodState = METHOD_DONE;
 +      ret->decision = DECISION_UNCOND_SUCC;
 +      data->phase2_success = 1;
 +
 +      /*
 +       * Reply with empty data; authentication server will reply
 +       * with EAP-Success after this.
 +       */
 +      return 1;
 +#else /* EAP_MSCHAPv2 */
 +      wpa_printf(MSG_ERROR, "EAP-TTLS: MSCHAPv2 not included in the build");
 +      return -1;
 +#endif /* EAP_MSCHAPv2 */
 +}
 +
 +
 +#ifdef EAP_TNC
 +static int eap_ttls_process_tnc_start(struct eap_sm *sm,
 +                                    struct eap_ttls_data *data,
 +                                    struct eap_method_ret *ret,
 +                                    struct ttls_parse_avp *parse,
 +                                    struct wpabuf **resp)
 +{
 +      /* TNC uses inner EAP method after non-EAP TTLS phase 2. */
 +      if (parse->eapdata == NULL) {
 +              wpa_printf(MSG_INFO, "EAP-TTLS: Phase 2 received "
 +                         "unexpected tunneled data (no EAP)");
 +              return -1;
 +      }
 +
 +      if (!data->ready_for_tnc) {
 +              wpa_printf(MSG_INFO, "EAP-TTLS: Phase 2 received "
 +                         "EAP after non-EAP, but not ready for TNC");
 +              return -1;
 +      }
 +
 +      wpa_printf(MSG_DEBUG, "EAP-TTLS: Start TNC after completed "
 +                 "non-EAP method");
 +      data->tnc_started = 1;
 +
 +      if (eap_ttls_process_phase2_eap(sm, data, ret, parse, resp) < 0)
 +              return -1;
 +
 +      return 0;
 +}
 +#endif /* EAP_TNC */
 +
 +
 +static int eap_ttls_process_decrypted(struct eap_sm *sm,
 +                                    struct eap_ttls_data *data,
 +                                    struct eap_method_ret *ret,
 +                                    u8 identifier,
 +                                    struct ttls_parse_avp *parse,
 +                                    struct wpabuf *in_decrypted,
 +                                    struct wpabuf **out_data)
 +{
 +      struct wpabuf *resp = NULL;
 +      struct eap_peer_config *config = eap_get_config(sm);
 +      int res;
 +      enum phase2_types phase2_type = data->phase2_type;
 +
 +#ifdef EAP_TNC
 +      if (data->tnc_started)
 +              phase2_type = EAP_TTLS_PHASE2_EAP;
 +#endif /* EAP_TNC */
 +
 +      switch (phase2_type) {
 +      case EAP_TTLS_PHASE2_EAP:
 +              if (eap_ttls_process_phase2_eap(sm, data, ret, parse, &resp) <
 +                  0)
 +                      return -1;
 +              break;
 +      case EAP_TTLS_PHASE2_MSCHAPV2:
 +              res = eap_ttls_process_phase2_mschapv2(sm, data, ret, parse);
 +#ifdef EAP_TNC
 +              if (res == 1 && parse->eapdata && data->phase2_success) {
 +                      /*
 +                       * TNC may be required as the next
 +                       * authentication method within the tunnel.
 +                       */
 +                      ret->methodState = METHOD_MAY_CONT;
 +                      data->ready_for_tnc = 1;
 +                      if (eap_ttls_process_tnc_start(sm, data, ret, parse,
 +                                                     &resp) == 0)
 +                              break;
 +              }
 +#endif /* EAP_TNC */
 +              return res;
 +      case EAP_TTLS_PHASE2_MSCHAP:
 +      case EAP_TTLS_PHASE2_PAP:
 +      case EAP_TTLS_PHASE2_CHAP:
 +#ifdef EAP_TNC
 +              if (eap_ttls_process_tnc_start(sm, data, ret, parse, &resp) <
 +                  0)
 +                      return -1;
 +              break;
 +#else /* EAP_TNC */
 +              /* EAP-TTLS/{MSCHAP,PAP,CHAP} should not send any TLS tunneled
 +               * requests to the supplicant */
 +              wpa_printf(MSG_INFO, "EAP-TTLS: Phase 2 received unexpected "
 +                         "tunneled data");
 +              return -1;
 +#endif /* EAP_TNC */
 +      }
 +
 +      if (resp) {
 +              if (eap_ttls_encrypt_response(sm, data, resp, identifier,
 +                                            out_data) < 0)
 +                      return -1;
 +      } else if (config->pending_req_identity ||
 +                 config->pending_req_password ||
 +                 config->pending_req_otp ||
 +                 config->pending_req_new_password) {
 +              wpabuf_free(data->pending_phase2_req);
 +              data->pending_phase2_req = wpabuf_dup(in_decrypted);
 +      }
 +
 +      return 0;
 +}
 +
 +
 +static int eap_ttls_implicit_identity_request(struct eap_sm *sm,
 +                                            struct eap_ttls_data *data,
 +                                            struct eap_method_ret *ret,
 +                                            u8 identifier,
 +                                            struct wpabuf **out_data)
 +{
 +      int retval = 0;
 +      struct eap_hdr *hdr;
 +      struct wpabuf *resp;
 +
 +      hdr = (struct eap_hdr *) eap_ttls_fake_identity_request();
 +      if (hdr == NULL) {
 +              ret->methodState = METHOD_DONE;
 +              ret->decision = DECISION_FAIL;
 +              return -1;
 +      }
 +
 +      resp = NULL;
 +      if (eap_ttls_phase2_request(sm, data, ret, hdr, &resp)) {
 +              wpa_printf(MSG_INFO, "EAP-TTLS: Phase2 Request "
 +                         "processing failed");
 +              retval = -1;
 +      } else {
 +              struct eap_peer_config *config = eap_get_config(sm);
 +              if (resp == NULL &&
 +                  (config->pending_req_identity ||
 +                   config->pending_req_password ||
 +                   config->pending_req_otp ||
 +                   config->pending_req_new_password)) {
 +                      /*
 +                       * Use empty buffer to force implicit request
 +                       * processing when EAP request is re-processed after
 +                       * user input.
 +                       */
 +                      wpabuf_free(data->pending_phase2_req);
 +                      data->pending_phase2_req = wpabuf_alloc(0);
 +              }
 +
 +              retval = eap_ttls_encrypt_response(sm, data, resp, identifier,
 +                                                 out_data);
 +      }
 +
 +      os_free(hdr);
 +
 +      if (retval < 0) {
 +              ret->methodState = METHOD_DONE;
 +              ret->decision = DECISION_FAIL;
 +      }
 +
 +      return retval;
 +}
 +
 +
 +static int eap_ttls_phase2_start(struct eap_sm *sm, struct eap_ttls_data *data,
 +                               struct eap_method_ret *ret, u8 identifier,
 +                               struct wpabuf **out_data)
 +{
 +      data->phase2_start = 0;
 +
 +      /*
 +       * EAP-TTLS does not use Phase2 on fast re-auth; this must be done only
 +       * if TLS part was indeed resuming a previous session. Most
 +       * Authentication Servers terminate EAP-TTLS before reaching this
 +       * point, but some do not. Make wpa_supplicant stop phase 2 here, if
 +       * needed.
 +       */
 +      if (data->reauth &&
 +          tls_connection_resumed(sm->ssl_ctx, data->ssl.conn)) {
 +              wpa_printf(MSG_DEBUG, "EAP-TTLS: Session resumption - "
 +                         "skip phase 2");
 +              *out_data = eap_peer_tls_build_ack(identifier, EAP_TYPE_TTLS,
 +                                                 data->ttls_version);
 +              ret->methodState = METHOD_DONE;
 +              ret->decision = DECISION_UNCOND_SUCC;
 +              data->phase2_success = 1;
 +              return 0;
 +      }
 +
 +      return eap_ttls_implicit_identity_request(sm, data, ret, identifier,
 +                                                out_data);
 +}
 +
 +
 +static int eap_ttls_decrypt(struct eap_sm *sm, struct eap_ttls_data *data,
 +                          struct eap_method_ret *ret, u8 identifier,
 +                          const struct wpabuf *in_data,
 +                          struct wpabuf **out_data)
 +{
 +      struct wpabuf *in_decrypted = NULL;
 +      int retval = 0;
 +      struct ttls_parse_avp parse;
 +
 +      os_memset(&parse, 0, sizeof(parse));
 +
 +      wpa_printf(MSG_DEBUG, "EAP-TTLS: received %lu bytes encrypted data for"
 +                 " Phase 2",
 +                 in_data ? (unsigned long) wpabuf_len(in_data) : 0);
 +
 +      if (data->pending_phase2_req) {
 +              wpa_printf(MSG_DEBUG, "EAP-TTLS: Pending Phase 2 request - "
 +                         "skip decryption and use old data");
 +              /* Clear TLS reassembly state. */
 +              eap_peer_tls_reset_input(&data->ssl);
 +
 +              in_decrypted = data->pending_phase2_req;
 +              data->pending_phase2_req = NULL;
 +              if (wpabuf_len(in_decrypted) == 0) {
 +                      wpabuf_free(in_decrypted);
 +                      return eap_ttls_implicit_identity_request(
 +                              sm, data, ret, identifier, out_data);
 +              }
 +              goto continue_req;
 +      }
 +
 +      if ((in_data == NULL || wpabuf_len(in_data) == 0) &&
 +          data->phase2_start) {
 +              return eap_ttls_phase2_start(sm, data, ret, identifier,
 +                                           out_data);
 +      }
 +
 +      if (in_data == NULL || wpabuf_len(in_data) == 0) {
 +              /* Received TLS ACK - requesting more fragments */
 +              return eap_peer_tls_encrypt(sm, &data->ssl, EAP_TYPE_TTLS,
 +                                          data->ttls_version,
 +                                          identifier, NULL, out_data);
 +      }
 +
 +      retval = eap_peer_tls_decrypt(sm, &data->ssl, in_data, &in_decrypted);
 +      if (retval)
 +              goto done;
 +
 +continue_req:
 +      data->phase2_start = 0;
 +
 +      if (eap_ttls_parse_avps(in_decrypted, &parse) < 0) {
 +              retval = -1;
 +              goto done;
 +      }
 +
 +      retval = eap_ttls_process_decrypted(sm, data, ret, identifier,
 +                                          &parse, in_decrypted, out_data);
 +
 +done:
 +      wpabuf_free(in_decrypted);
 +      os_free(parse.eapdata);
 +
 +      if (retval < 0) {
 +              ret->methodState = METHOD_DONE;
 +              ret->decision = DECISION_FAIL;
 +      }
 +
 +      return retval;
 +}
 +
 +
 +static int eap_ttls_process_handshake(struct eap_sm *sm,
 +                                    struct eap_ttls_data *data,
 +                                    struct eap_method_ret *ret,
 +                                    u8 identifier,
-                                         in_data, in_len, out_data);
++                                    const struct wpabuf *in_data,
 +                                    struct wpabuf **out_data)
 +{
 +      int res;
 +
 +      res = eap_peer_tls_process_helper(sm, &data->ssl, EAP_TYPE_TTLS,
 +                                        data->ttls_version, identifier,
-               struct wpabuf msg;
++                                        in_data, out_data);
++      if (res < 0) {
++              wpa_printf(MSG_DEBUG, "EAP-TTLS: TLS processing failed");
++              ret->methodState = METHOD_DONE;
++              ret->decision = DECISION_FAIL;
++              return -1;
++      }
 +
 +      if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) {
 +              wpa_printf(MSG_DEBUG, "EAP-TTLS: TLS done, proceed to "
 +                         "Phase 2");
 +              if (data->resuming) {
 +                      wpa_printf(MSG_DEBUG, "EAP-TTLS: fast reauth - may "
 +                                 "skip Phase 2");
 +                      ret->decision = DECISION_COND_SUCC;
 +                      ret->methodState = METHOD_MAY_CONT;
 +              }
 +              data->phase2_start = 1;
 +              eap_ttls_v0_derive_key(sm, data);
 +
 +              if (*out_data == NULL || wpabuf_len(*out_data) == 0) {
 +                      if (eap_ttls_decrypt(sm, data, ret, identifier,
 +                                           NULL, out_data)) {
 +                              wpa_printf(MSG_WARNING, "EAP-TTLS: "
 +                                         "failed to process early "
 +                                         "start for Phase 2");
 +                      }
 +                      res = 0;
 +              }
 +              data->resuming = 0;
 +      }
 +
 +      if (res == 2) {
-               wpabuf_set(&msg, in_data, in_len);
-               res = eap_ttls_decrypt(sm, data, ret, identifier, &msg,
 +              /*
 +               * Application data included in the handshake message.
 +               */
 +              wpabuf_free(data->pending_phase2_req);
 +              data->pending_phase2_req = *out_data;
 +              *out_data = NULL;
-               struct wpabuf msg;
-               wpabuf_set(&msg, pos, left);
++              res = eap_ttls_decrypt(sm, data, ret, identifier, in_data,
 +                                     out_data);
 +      }
 +
 +      return res;
 +}
 +
 +
 +static void eap_ttls_check_auth_status(struct eap_sm *sm, 
 +                                     struct eap_ttls_data *data,
 +                                     struct eap_method_ret *ret)
 +{
 +      if (ret->methodState == METHOD_DONE) {
 +              ret->allowNotifications = FALSE;
 +              if (ret->decision == DECISION_UNCOND_SUCC ||
 +                  ret->decision == DECISION_COND_SUCC) {
 +                      wpa_printf(MSG_DEBUG, "EAP-TTLS: Authentication "
 +                                 "completed successfully");
 +                      data->phase2_success = 1;
 +#ifdef EAP_TNC
 +                      if (!data->ready_for_tnc && !data->tnc_started) {
 +                              /*
 +                               * TNC may be required as the next
 +                               * authentication method within the tunnel.
 +                               */
 +                              ret->methodState = METHOD_MAY_CONT;
 +                              data->ready_for_tnc = 1;
 +                      }
 +#endif /* EAP_TNC */
 +              }
 +      } else if (ret->methodState == METHOD_MAY_CONT &&
 +                 (ret->decision == DECISION_UNCOND_SUCC ||
 +                  ret->decision == DECISION_COND_SUCC)) {
 +                      wpa_printf(MSG_DEBUG, "EAP-TTLS: Authentication "
 +                                 "completed successfully (MAY_CONT)");
 +                      data->phase2_success = 1;
 +      }
 +}
 +
 +
 +static struct wpabuf * eap_ttls_process(struct eap_sm *sm, void *priv,
 +                                      struct eap_method_ret *ret,
 +                                      const struct wpabuf *reqData)
 +{
 +      size_t left;
 +      int res;
 +      u8 flags, id;
 +      struct wpabuf *resp;
 +      const u8 *pos;
 +      struct eap_ttls_data *data = priv;
++      struct wpabuf msg;
 +
 +      pos = eap_peer_tls_process_init(sm, &data->ssl, EAP_TYPE_TTLS, ret,
 +                                      reqData, &left, &flags);
 +      if (pos == NULL)
 +              return NULL;
 +      id = eap_get_id(reqData);
 +
 +      if (flags & EAP_TLS_FLAGS_START) {
 +              wpa_printf(MSG_DEBUG, "EAP-TTLS: Start (server ver=%d, own "
 +                         "ver=%d)", flags & EAP_TLS_VERSION_MASK,
 +                         data->ttls_version);
 +
 +              /* RFC 5281, Ch. 9.2:
 +               * "This packet MAY contain additional information in the form
 +               * of AVPs, which may provide useful hints to the client"
 +               * For now, ignore any potential extra data.
 +               */
 +              left = 0;
 +      }
 +
++      wpabuf_set(&msg, pos, left);
++
 +      resp = NULL;
 +      if (tls_connection_established(sm->ssl_ctx, data->ssl.conn) &&
 +          !data->resuming) {
-                                                pos, left, &resp);
 +              res = eap_ttls_decrypt(sm, data, ret, id, &msg, &resp);
 +      } else {
 +              res = eap_ttls_process_handshake(sm, data, ret, id,
++                                               &msg, &resp);
 +      }
 +
 +      eap_ttls_check_auth_status(sm, data, ret);
 +
 +      /* FIX: what about res == -1? Could just move all error processing into
 +       * the other functions and get rid of this res==1 case here. */
 +      if (res == 1) {
 +              wpabuf_free(resp);
 +              return eap_peer_tls_build_ack(id, EAP_TYPE_TTLS,
 +                                            data->ttls_version);
 +      }
 +      return resp;
 +}
 +
 +
 +static Boolean eap_ttls_has_reauth_data(struct eap_sm *sm, void *priv)
 +{
 +      struct eap_ttls_data *data = priv;
 +      return tls_connection_established(sm->ssl_ctx, data->ssl.conn) &&
 +              data->phase2_success;
 +}
 +
 +
 +static void eap_ttls_deinit_for_reauth(struct eap_sm *sm, void *priv)
 +{
 +      struct eap_ttls_data *data = priv;
 +      wpabuf_free(data->pending_phase2_req);
 +      data->pending_phase2_req = NULL;
 +#ifdef EAP_TNC
 +      data->ready_for_tnc = 0;
 +      data->tnc_started = 0;
 +#endif /* EAP_TNC */
 +}
 +
 +
 +static void * eap_ttls_init_for_reauth(struct eap_sm *sm, void *priv)
 +{
 +      struct eap_ttls_data *data = priv;
 +      eap_ttls_free_key(data);
 +      os_free(data->session_id);
 +      data->session_id = NULL;
 +      if (eap_peer_tls_reauth_init(sm, &data->ssl)) {
 +              os_free(data);
 +              return NULL;
 +      }
 +      if (data->phase2_priv && data->phase2_method &&
 +          data->phase2_method->init_for_reauth)
 +              data->phase2_method->init_for_reauth(sm, data->phase2_priv);
 +      data->phase2_start = 0;
 +      data->phase2_success = 0;
 +      data->resuming = 1;
 +      data->reauth = 1;
 +      return priv;
 +}
 +
 +
 +static int eap_ttls_get_status(struct eap_sm *sm, void *priv, char *buf,
 +                             size_t buflen, int verbose)
 +{
 +      struct eap_ttls_data *data = priv;
 +      int len, ret;
 +
 +      len = eap_peer_tls_status(sm, &data->ssl, buf, buflen, verbose);
 +      ret = os_snprintf(buf + len, buflen - len,
 +                        "EAP-TTLSv%d Phase2 method=",
 +                        data->ttls_version);
 +      if (os_snprintf_error(buflen - len, ret))
 +              return len;
 +      len += ret;
 +      switch (data->phase2_type) {
 +      case EAP_TTLS_PHASE2_EAP:
 +              ret = os_snprintf(buf + len, buflen - len, "EAP-%s\n",
 +                                data->phase2_method ?
 +                                data->phase2_method->name : "?");
 +              break;
 +      case EAP_TTLS_PHASE2_MSCHAPV2:
 +              ret = os_snprintf(buf + len, buflen - len, "MSCHAPV2\n");
 +              break;
 +      case EAP_TTLS_PHASE2_MSCHAP:
 +              ret = os_snprintf(buf + len, buflen - len, "MSCHAP\n");
 +              break;
 +      case EAP_TTLS_PHASE2_PAP:
 +              ret = os_snprintf(buf + len, buflen - len, "PAP\n");
 +              break;
 +      case EAP_TTLS_PHASE2_CHAP:
 +              ret = os_snprintf(buf + len, buflen - len, "CHAP\n");
 +              break;
 +      default:
 +              ret = 0;
 +              break;
 +      }
 +      if (os_snprintf_error(buflen - len, ret))
 +              return len;
 +      len += ret;
 +
 +      return len;
 +}
 +
 +
 +static Boolean eap_ttls_isKeyAvailable(struct eap_sm *sm, void *priv)
 +{
 +      struct eap_ttls_data *data = priv;
 +      return data->key_data != NULL && data->phase2_success;
 +}
 +
 +
 +static u8 * eap_ttls_getKey(struct eap_sm *sm, void *priv, size_t *len)
 +{
 +      struct eap_ttls_data *data = priv;
 +      u8 *key;
 +
 +      if (data->key_data == NULL || !data->phase2_success)
 +              return NULL;
 +
 +      key = os_malloc(EAP_TLS_KEY_LEN);
 +      if (key == NULL)
 +              return NULL;
 +
 +      *len = EAP_TLS_KEY_LEN;
 +      os_memcpy(key, data->key_data, EAP_TLS_KEY_LEN);
 +
 +      return key;
 +}
 +
 +
 +static u8 * eap_ttls_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
 +{
 +      struct eap_ttls_data *data = priv;
 +      u8 *id;
 +
 +      if (data->session_id == NULL || !data->phase2_success)
 +              return NULL;
 +
 +      id = os_malloc(data->id_len);
 +      if (id == NULL)
 +              return NULL;
 +
 +      *len = data->id_len;
 +      os_memcpy(id, data->session_id, data->id_len);
 +
 +      return id;
 +}
 +
 +
 +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;
 +      int ret;
 +
 +      eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
 +                                  EAP_VENDOR_IETF, EAP_TYPE_TTLS, "TTLS");
 +      if (eap == NULL)
 +              return -1;
 +
 +      eap->init = eap_ttls_init;
 +      eap->deinit = eap_ttls_deinit;
 +      eap->process = eap_ttls_process;
 +      eap->isKeyAvailable = eap_ttls_isKeyAvailable;
 +      eap->getKey = eap_ttls_getKey;
 +      eap->getSessionId = eap_ttls_get_session_id;
 +      eap->get_status = eap_ttls_get_status;
 +      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)
 +              eap_peer_method_free(eap);
 +      return ret;
 +}
index 7ce0a53d0b299a54f4a99d726c4970b0348914c1,0000000000000000000000000000000000000000..7ac99c7ce727b2c74dc58e336354d907bd39839f
mode 100644,000000..100644
--- /dev/null
@@@ -1,595 -1,0 +1,598 @@@
 +/*
 + * EAP-WSC peer for Wi-Fi Protected Setup
 + * Copyright (c) 2007-2009, 2012, Jouni Malinen <j@w1.fi>
 + *
 + * This software may be distributed under the terms of the BSD license.
 + * See README for more details.
 + */
 +
 +#include "includes.h"
 +
 +#include "common.h"
 +#include "uuid.h"
 +#include "eap_i.h"
 +#include "eap_common/eap_wsc_common.h"
 +#include "wps/wps.h"
 +#include "wps/wps_defs.h"
 +
 +
 +struct eap_wsc_data {
 +      enum { WAIT_START, MESG, FRAG_ACK, WAIT_FRAG_ACK, DONE, FAIL } state;
 +      int registrar;
 +      struct wpabuf *in_buf;
 +      struct wpabuf *out_buf;
 +      enum wsc_op_code in_op_code, out_op_code;
 +      size_t out_used;
 +      size_t fragment_size;
 +      struct wps_data *wps;
 +      struct wps_context *wps_ctx;
 +};
 +
 +
 +static const char * eap_wsc_state_txt(int state)
 +{
 +      switch (state) {
 +      case WAIT_START:
 +              return "WAIT_START";
 +      case MESG:
 +              return "MESG";
 +      case FRAG_ACK:
 +              return "FRAG_ACK";
 +      case WAIT_FRAG_ACK:
 +              return "WAIT_FRAG_ACK";
 +      case DONE:
 +              return "DONE";
 +      case FAIL:
 +              return "FAIL";
 +      default:
 +              return "?";
 +      }
 +}
 +
 +
 +static void eap_wsc_state(struct eap_wsc_data *data, int state)
 +{
 +      wpa_printf(MSG_DEBUG, "EAP-WSC: %s -> %s",
 +                 eap_wsc_state_txt(data->state),
 +                 eap_wsc_state_txt(state));
 +      data->state = state;
 +}
 +
 +
 +static int eap_wsc_new_ap_settings(struct wps_credential *cred,
 +                                 const char *params)
 +{
 +      const char *pos, *end;
 +      size_t len;
 +
 +      os_memset(cred, 0, sizeof(*cred));
 +
 +      pos = os_strstr(params, "new_ssid=");
 +      if (pos == NULL)
 +              return 0;
 +      pos += 9;
 +      end = os_strchr(pos, ' ');
 +      if (end == NULL)
 +              len = os_strlen(pos);
 +      else
 +              len = end - pos;
 +      if ((len & 1) || len > 2 * sizeof(cred->ssid) ||
 +          hexstr2bin(pos, cred->ssid, len / 2)) {
 +              wpa_printf(MSG_DEBUG, "EAP-WSC: Invalid new_ssid");
 +              return -1;
 +      }
 +      cred->ssid_len = len / 2;
 +
 +      pos = os_strstr(params, "new_auth=");
 +      if (pos == NULL) {
 +              wpa_printf(MSG_DEBUG, "EAP-WSC: Missing new_auth");
 +              return -1;
 +      }
 +      if (os_strncmp(pos + 9, "OPEN", 4) == 0)
 +              cred->auth_type = WPS_AUTH_OPEN;
 +      else if (os_strncmp(pos + 9, "WPAPSK", 6) == 0)
 +              cred->auth_type = WPS_AUTH_WPAPSK;
 +      else if (os_strncmp(pos + 9, "WPA2PSK", 7) == 0)
 +              cred->auth_type = WPS_AUTH_WPA2PSK;
 +      else {
 +              wpa_printf(MSG_DEBUG, "EAP-WSC: Unknown new_auth");
 +              return -1;
 +      }
 +
 +      pos = os_strstr(params, "new_encr=");
 +      if (pos == NULL) {
 +              wpa_printf(MSG_DEBUG, "EAP-WSC: Missing new_encr");
 +              return -1;
 +      }
 +      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)
 +              cred->encr_type = WPS_ENCR_AES;
 +      else {
 +              wpa_printf(MSG_DEBUG, "EAP-WSC: Unknown new_encr");
 +              return -1;
 +      }
 +
 +      pos = os_strstr(params, "new_key=");
 +      if (pos == NULL)
 +              return 0;
 +      pos += 8;
 +      end = os_strchr(pos, ' ');
 +      if (end == NULL)
 +              len = os_strlen(pos);
 +      else
 +              len = end - pos;
 +      if ((len & 1) || len > 2 * sizeof(cred->key) ||
 +          hexstr2bin(pos, cred->key, len / 2)) {
 +              wpa_printf(MSG_DEBUG, "EAP-WSC: Invalid new_key");
 +              return -1;
 +      }
 +      cred->key_len = len / 2;
 +
 +      return 1;
 +}
 +
 +
 +static void * eap_wsc_init(struct eap_sm *sm)
 +{
 +      struct eap_wsc_data *data;
 +      const u8 *identity;
 +      size_t identity_len;
 +      int registrar;
 +      struct wps_config cfg;
 +      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) {
 +              wpa_printf(MSG_ERROR, "EAP-WSC: WPS context not available");
 +              return NULL;
 +      }
 +
 +      identity = eap_get_config_identity(sm, &identity_len);
 +
 +      if (identity && identity_len == WSC_ID_REGISTRAR_LEN &&
 +          os_memcmp(identity, WSC_ID_REGISTRAR, WSC_ID_REGISTRAR_LEN) == 0)
 +              registrar = 1; /* Supplicant is Registrar */
 +      else if (identity && identity_len == WSC_ID_ENROLLEE_LEN &&
 +          os_memcmp(identity, WSC_ID_ENROLLEE, WSC_ID_ENROLLEE_LEN) == 0)
 +              registrar = 0; /* Supplicant is Enrollee */
 +      else {
 +              wpa_hexdump_ascii(MSG_INFO, "EAP-WSC: Unexpected identity",
 +                                identity, identity_len);
 +              return NULL;
 +      }
 +
 +      data = os_zalloc(sizeof(*data));
 +      if (data == NULL)
 +              return NULL;
 +      data->state = registrar ? MESG : WAIT_START;
 +      data->registrar = registrar;
 +      data->wps_ctx = wps;
 +
 +      os_memset(&cfg, 0, sizeof(cfg));
 +      cfg.wps = wps;
 +      cfg.registrar = registrar;
 +
 +      phase1 = eap_get_config_phase1(sm);
 +      if (phase1 == NULL) {
 +              wpa_printf(MSG_INFO, "EAP-WSC: phase1 configuration data not "
 +                         "set");
 +              os_free(data);
 +              return NULL;
 +      }
 +
 +      pos = os_strstr(phase1, "pin=");
 +      if (pos) {
 +              pos += 4;
 +              cfg.pin = (const u8 *) pos;
 +              while (*pos != '\0' && *pos != ' ')
 +                      pos++;
 +              cfg.pin_len = pos - (const char *) cfg.pin;
 +              if (cfg.pin_len == 6 &&
 +                  os_strncmp((const char *) cfg.pin, "nfc-pw", 6) == 0) {
 +                      cfg.pin = NULL;
 +                      cfg.pin_len = 0;
 +                      nfc = 1;
 +              }
 +      } else {
 +              pos = os_strstr(phase1, "pbc=1");
 +              if (pos)
 +                      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");
 +              os_free(data);
 +              return NULL;
 +      }
 +
 +      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) {
 +              os_free(data);
 +              wpa_printf(MSG_DEBUG, "EAP-WSC: Failed to parse new AP "
 +                         "settings");
 +              return NULL;
 +      }
 +      if (res == 1) {
 +              wpa_printf(MSG_DEBUG, "EAP-WSC: Provide new AP settings for "
 +                         "WPS");
 +              cfg.new_ap_settings = &new_ap_settings;
 +      }
 +
 +      data->wps = wps_init(&cfg);
 +      if (data->wps == NULL) {
 +              os_free(data);
 +              wpa_printf(MSG_DEBUG, "EAP-WSC: wps_init failed");
 +              return NULL;
 +      }
 +      res = eap_get_config_fragment_size(sm);
 +      if (res > 0)
 +              data->fragment_size = res;
 +      else
 +              data->fragment_size = WSC_FRAGMENT_SIZE;
 +      wpa_printf(MSG_DEBUG, "EAP-WSC: Fragment size limit %u",
 +                 (unsigned int) data->fragment_size);
 +
 +      if (registrar && cfg.pin) {
 +              wps_registrar_add_pin(data->wps_ctx->registrar, NULL, NULL,
 +                                    cfg.pin, cfg.pin_len, 0);
 +      }
 +
 +      /* Use reduced client timeout for WPS to avoid long wait */
 +      if (sm->ClientTimeout > 30)
 +              sm->ClientTimeout = 30;
 +
 +      return data;
 +}
 +
 +
 +static void eap_wsc_deinit(struct eap_sm *sm, void *priv)
 +{
 +      struct eap_wsc_data *data = priv;
 +      wpabuf_free(data->in_buf);
 +      wpabuf_free(data->out_buf);
 +      wps_deinit(data->wps);
 +      os_free(data->wps_ctx->network_key);
 +      data->wps_ctx->network_key = NULL;
 +      os_free(data);
 +}
 +
 +
 +static struct wpabuf * eap_wsc_build_msg(struct eap_wsc_data *data,
 +                                       struct eap_method_ret *ret, u8 id)
 +{
 +      struct wpabuf *resp;
 +      u8 flags;
 +      size_t send_len, plen;
 +
 +      ret->ignore = FALSE;
 +      wpa_printf(MSG_DEBUG, "EAP-WSC: Generating Response");
 +      ret->allowNotifications = TRUE;
 +
 +      flags = 0;
 +      send_len = wpabuf_len(data->out_buf) - data->out_used;
 +      if (2 + send_len > data->fragment_size) {
 +              send_len = data->fragment_size - 2;
 +              flags |= WSC_FLAGS_MF;
 +              if (data->out_used == 0) {
 +                      flags |= WSC_FLAGS_LF;
 +                      send_len -= 2;
 +              }
 +      }
 +      plen = 2 + send_len;
 +      if (flags & WSC_FLAGS_LF)
 +              plen += 2;
 +      resp = eap_msg_alloc(EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC, plen,
 +                           EAP_CODE_RESPONSE, id);
 +      if (resp == NULL)
 +              return NULL;
 +
 +      wpabuf_put_u8(resp, data->out_op_code); /* Op-Code */
 +      wpabuf_put_u8(resp, flags); /* Flags */
 +      if (flags & WSC_FLAGS_LF)
 +              wpabuf_put_be16(resp, wpabuf_len(data->out_buf));
 +
 +      wpabuf_put_data(resp, wpabuf_head_u8(data->out_buf) + data->out_used,
 +                      send_len);
 +      data->out_used += send_len;
 +
 +      ret->methodState = METHOD_MAY_CONT;
 +      ret->decision = DECISION_FAIL;
 +
 +      if (data->out_used == wpabuf_len(data->out_buf)) {
 +              wpa_printf(MSG_DEBUG, "EAP-WSC: Sending out %lu bytes "
 +                         "(message sent completely)",
 +                         (unsigned long) send_len);
 +              wpabuf_free(data->out_buf);
 +              data->out_buf = NULL;
 +              data->out_used = 0;
 +              if ((data->state == FAIL && data->out_op_code == WSC_ACK) ||
 +                  data->out_op_code == WSC_NACK ||
 +                  data->out_op_code == WSC_Done) {
 +                      eap_wsc_state(data, FAIL);
 +                      ret->methodState = METHOD_DONE;
 +              } else
 +                      eap_wsc_state(data, MESG);
 +      } else {
 +              wpa_printf(MSG_DEBUG, "EAP-WSC: Sending out %lu bytes "
 +                         "(%lu more to send)", (unsigned long) send_len,
 +                         (unsigned long) wpabuf_len(data->out_buf) -
 +                         data->out_used);
 +              eap_wsc_state(data, WAIT_FRAG_ACK);
 +      }
 +
 +      return resp;
 +}
 +
 +
 +static int eap_wsc_process_cont(struct eap_wsc_data *data,
 +                              const u8 *buf, size_t len, u8 op_code)
 +{
 +      /* Process continuation of a pending message */
 +      if (op_code != data->in_op_code) {
 +              wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d in "
 +                         "fragment (expected %d)",
 +                         op_code, data->in_op_code);
 +              return -1;
 +      }
 +
 +      if (len > wpabuf_tailroom(data->in_buf)) {
 +              wpa_printf(MSG_DEBUG, "EAP-WSC: Fragment overflow");
 +              eap_wsc_state(data, FAIL);
 +              return -1;
 +      }
 +
 +      wpabuf_put_data(data->in_buf, buf, len);
 +      wpa_printf(MSG_DEBUG, "EAP-WSC: Received %lu bytes, waiting "
 +                 "for %lu bytes more", (unsigned long) len,
 +                 (unsigned long) wpabuf_tailroom(data->in_buf));
 +
 +      return 0;
 +}
 +
 +
 +static struct wpabuf * eap_wsc_process_fragment(struct eap_wsc_data *data,
 +                                              struct eap_method_ret *ret,
 +                                              u8 id, u8 flags, u8 op_code,
 +                                              u16 message_length,
 +                                              const u8 *buf, size_t len)
 +{
 +      /* Process a fragment that is not the last one of the message */
 +      if (data->in_buf == NULL && !(flags & WSC_FLAGS_LF)) {
 +              wpa_printf(MSG_DEBUG, "EAP-WSC: No Message Length field in a "
 +                         "fragmented packet");
 +              ret->ignore = TRUE;
 +              return NULL;
 +      }
 +
 +      if (data->in_buf == NULL) {
 +              /* First fragment of the message */
 +              data->in_buf = wpabuf_alloc(message_length);
 +              if (data->in_buf == NULL) {
 +                      wpa_printf(MSG_DEBUG, "EAP-WSC: No memory for "
 +                                 "message");
 +                      ret->ignore = TRUE;
 +                      return NULL;
 +              }
 +              data->in_op_code = op_code;
 +              wpabuf_put_data(data->in_buf, buf, len);
 +              wpa_printf(MSG_DEBUG, "EAP-WSC: Received %lu bytes in first "
 +                         "fragment, waiting for %lu bytes more",
 +                         (unsigned long) len,
 +                         (unsigned long) wpabuf_tailroom(data->in_buf));
 +      }
 +
 +      return eap_wsc_build_frag_ack(id, EAP_CODE_RESPONSE);
 +}
 +
 +
 +static struct wpabuf * eap_wsc_process(struct eap_sm *sm, void *priv,
 +                                     struct eap_method_ret *ret,
 +                                     const struct wpabuf *reqData)
 +{
 +      struct eap_wsc_data *data = priv;
 +      const u8 *start, *pos, *end;
 +      size_t len;
 +      u8 op_code, flags, id;
 +      u16 message_length = 0;
 +      enum wps_process_res res;
 +      struct wpabuf tmpbuf;
 +      struct wpabuf *r;
 +
 +      pos = eap_hdr_validate(EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC, reqData,
 +                             &len);
 +      if (pos == NULL || len < 2) {
 +              ret->ignore = TRUE;
 +              return NULL;
 +      }
 +
 +      id = eap_get_id(reqData);
 +
 +      start = pos;
 +      end = start + len;
 +
 +      op_code = *pos++;
 +      flags = *pos++;
 +      if (flags & WSC_FLAGS_LF) {
 +              if (end - pos < 2) {
 +                      wpa_printf(MSG_DEBUG, "EAP-WSC: Message underflow");
 +                      ret->ignore = TRUE;
 +                      return NULL;
 +              }
 +              message_length = WPA_GET_BE16(pos);
 +              pos += 2;
 +
 +              if (message_length < end - pos || message_length > 50000) {
 +                      wpa_printf(MSG_DEBUG, "EAP-WSC: Invalid Message "
 +                                 "Length");
 +                      ret->ignore = TRUE;
 +                      return NULL;
 +              }
 +      }
 +
 +      wpa_printf(MSG_DEBUG, "EAP-WSC: Received packet: Op-Code %d "
 +                 "Flags 0x%x Message Length %d",
 +                 op_code, flags, message_length);
 +
 +      if (data->state == WAIT_FRAG_ACK) {
 +              if (op_code != WSC_FRAG_ACK) {
 +                      wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d "
 +                                 "in WAIT_FRAG_ACK state", op_code);
 +                      ret->ignore = TRUE;
 +                      return NULL;
 +              }
 +              wpa_printf(MSG_DEBUG, "EAP-WSC: Fragment acknowledged");
 +              eap_wsc_state(data, MESG);
 +              return eap_wsc_build_msg(data, ret, id);
 +      }
 +
 +      if (op_code != WSC_ACK && op_code != WSC_NACK && op_code != WSC_MSG &&
 +          op_code != WSC_Done && op_code != WSC_Start) {
 +              wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d",
 +                         op_code);
 +              ret->ignore = TRUE;
 +              return NULL;
 +      }
 +
 +      if (data->state == WAIT_START) {
 +              if (op_code != WSC_Start) {
 +                      wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d "
 +                                 "in WAIT_START state", op_code);
 +                      ret->ignore = TRUE;
 +                      return NULL;
 +              }
 +              wpa_printf(MSG_DEBUG, "EAP-WSC: Received start");
 +              eap_wsc_state(data, MESG);
 +              /* Start message has empty payload, skip processing */
 +              goto send_msg;
 +      } else if (op_code == WSC_Start) {
 +              wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d",
 +                         op_code);
 +              ret->ignore = TRUE;
 +              return NULL;
 +      }
 +
 +      if (data->in_buf &&
 +          eap_wsc_process_cont(data, pos, end - pos, op_code) < 0) {
 +              ret->ignore = TRUE;
 +              return NULL;
 +      }
 +
 +      if (flags & WSC_FLAGS_MF) {
 +              return eap_wsc_process_fragment(data, ret, id, flags, op_code,
 +                                              message_length, pos,
 +                                              end - pos);
 +      }
 +
 +      if (data->in_buf == NULL) {
 +              /* Wrap unfragmented messages as wpabuf without extra copy */
 +              wpabuf_set(&tmpbuf, pos, end - pos);
 +              data->in_buf = &tmpbuf;
 +      }
 +
 +      res = wps_process_msg(data->wps, op_code, data->in_buf);
 +      switch (res) {
 +      case WPS_DONE:
 +              wpa_printf(MSG_DEBUG, "EAP-WSC: WPS processing completed "
 +                         "successfully - wait for EAP failure");
 +              eap_wsc_state(data, FAIL);
 +              break;
 +      case WPS_CONTINUE:
 +              eap_wsc_state(data, MESG);
 +              break;
 +      case WPS_FAILURE:
 +      case WPS_PENDING:
 +              wpa_printf(MSG_DEBUG, "EAP-WSC: WPS processing failed");
 +              eap_wsc_state(data, FAIL);
 +              break;
 +      }
 +
 +      if (data->in_buf != &tmpbuf)
 +              wpabuf_free(data->in_buf);
 +      data->in_buf = NULL;
 +
 +send_msg:
 +      if (data->out_buf == NULL) {
 +              data->out_buf = wps_get_msg(data->wps, &data->out_op_code);
 +              if (data->out_buf == NULL) {
 +                      wpa_printf(MSG_DEBUG, "EAP-WSC: Failed to receive "
 +                                 "message from WPS");
++                      eap_wsc_state(data, FAIL);
++                      ret->methodState = METHOD_DONE;
++                      ret->decision = DECISION_FAIL;
 +                      return NULL;
 +              }
 +              data->out_used = 0;
 +      }
 +
 +      eap_wsc_state(data, MESG);
 +      r = eap_wsc_build_msg(data, ret, id);
 +      if (data->state == FAIL && ret->methodState == METHOD_DONE) {
 +              /* Use reduced client timeout for WPS to avoid long wait */
 +              if (sm->ClientTimeout > 2)
 +                      sm->ClientTimeout = 2;
 +      }
 +      return r;
 +}
 +
 +
 +int eap_peer_wsc_register(void)
 +{
 +      struct eap_method *eap;
 +      int ret;
 +
 +      eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
 +                                  EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC,
 +                                  "WSC");
 +      if (eap == NULL)
 +              return -1;
 +
 +      eap->init = eap_wsc_init;
 +      eap->deinit = eap_wsc_deinit;
 +      eap->process = eap_wsc_process;
 +
 +      ret = eap_peer_method_register(eap);
 +      if (ret)
 +              eap_peer_method_free(eap);
 +      return ret;
 +}
index 9de6cb62f517bc3314cd8a8128c0e9d0d613b5d4,0000000000000000000000000000000000000000..69eaab8de946b4b0dfc960038974cf93978b4fc6
mode 100644,000000..100644
--- /dev/null
@@@ -1,153 -1,0 +1,157 @@@
-                                  struct eapol_callbacks *eapol_cb,
 +/*
 + * hostapd / EAP Full Authenticator state machine (RFC 4137)
 + * 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.
 + */
 +
 +#ifndef EAP_H
 +#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"
 +
 +struct eap_sm;
 +
 +#define EAP_TTLS_AUTH_PAP 1
 +#define EAP_TTLS_AUTH_CHAP 2
 +#define EAP_TTLS_AUTH_MSCHAP 4
 +#define EAP_TTLS_AUTH_MSCHAPV2 8
 +
 +struct eap_user {
 +      struct {
 +              int vendor;
 +              u32 method;
 +      } methods[EAP_MAX_METHODS];
 +      u8 *password;
 +      size_t password_len;
 +      int password_hash; /* whether password is hashed with
 +                          * 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 {
 +      /* Lower layer to full authenticator variables */
 +      Boolean eapResp; /* shared with EAPOL Backend Authentication */
 +      struct wpabuf *eapRespData;
 +      Boolean portEnabled;
 +      int retransWhile;
 +      Boolean eapRestart; /* shared with EAPOL Authenticator PAE */
 +      int eapSRTT;
 +      int eapRTTVAR;
 +
 +      /* Full authenticator to lower layer variables */
 +      Boolean eapReq; /* shared with EAPOL Backend Authentication */
 +      Boolean eapNoReq; /* shared with EAPOL Backend Authentication */
 +      Boolean eapSuccess;
 +      Boolean eapFail;
 +      Boolean eapTimeout;
 +      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 */
 +      Boolean aaaEapReq;
 +      Boolean aaaEapNoReq;
 +      Boolean aaaSuccess;
 +      Boolean aaaFail;
 +      struct wpabuf *aaaEapReqData;
 +      u8 *aaaEapKeyData;
 +      size_t aaaEapKeyDataLen;
 +      Boolean aaaEapKeyAvailable;
 +      int aaaMethodTimeout;
 +
 +      /* Full authenticator to AAA interface variables */
 +      Boolean aaaEapResp;
 +      struct wpabuf *aaaEapRespData;
 +      /* aaaIdentity -> eap_get_identity() */
 +      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 {
 +      void *ssl_ctx;
 +      void *msg_ctx;
 +      void *eap_sim_db_priv;
 +      Boolean backend_auth;
 +      int eap_server;
 +      u16 pwd_group;
 +      u8 *pac_opaque_encr_key;
 +      u8 *eap_fast_a_id;
 +      size_t eap_fast_a_id_len;
 +      char *eap_fast_a_id_info;
 +      int eap_fast_prov;
 +      int pac_key_lifetime;
 +      int pac_key_refresh_time;
 +      int eap_sim_aka_result_ind;
 +      int tnc;
 +      struct wps_context *wps;
 +      const struct wpabuf *assoc_wps_ie;
 +      const struct wpabuf *assoc_p2p_ie;
 +      const u8 *peer_addr;
 +      int fragment_size;
 +
 +      int pbc_in_m1;
 +
 +      const u8 *server_id;
 +      size_t server_id_len;
 +      int erp;
++      unsigned int tls_session_lifetime;
 +
 +#ifdef CONFIG_TESTING_OPTIONS
 +      u32 tls_test_flags;
 +#endif /* CONFIG_TESTING_OPTIONS */
 +};
 +
 +
 +struct eap_sm * eap_server_sm_init(void *eapol_ctx,
++                                 const struct eapol_callbacks *eapol_cb,
 +                                 struct eap_config *eap_conf);
 +void eap_server_sm_deinit(struct eap_sm *sm);
 +int eap_server_sm_step(struct eap_sm *sm);
 +void eap_sm_notify_cached(struct eap_sm *sm);
 +void eap_sm_pending_cb(struct eap_sm *sm);
 +int eap_sm_method_pending(struct eap_sm *sm);
 +const u8 * eap_get_identity(struct eap_sm *sm, size_t *len);
 +struct eap_eapol_interface * eap_get_interface(struct eap_sm *sm);
 +void eap_server_clear_identity(struct eap_sm *sm);
++void eap_server_mschap_rx_callback(struct eap_sm *sm, const char *source,
++                                 const u8 *username, size_t username_len,
++                                 const u8 *challenge, const u8 *response);
 +
 +#endif /* EAP_H */
index 7d723091ffb5c51518e7b8c9c820eceb8c6e593d,0000000000000000000000000000000000000000..c90443d19cb935bea80037c12bcae591f5206df5
mode 100644,000000..100644
--- /dev/null
@@@ -1,225 -1,0 +1,226 @@@
-       struct eapol_callbacks *eapol_cb;
 +/*
 + * hostapd / EAP Authenticator state machine internal structures (RFC 4137)
 + * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
 + *
 + * This software may be distributed under the terms of the BSD license.
 + * See README for more details.
 + */
 +
 +#ifndef EAP_I_H
 +#define EAP_I_H
 +
 +#include "wpabuf.h"
 +#include "eap_server/eap.h"
 +#include "eap_common/eap_common.h"
 +
 +/* RFC 4137 - EAP Standalone Authenticator */
 +
 +/**
 + * struct eap_method - EAP method interface
 + * This structure defines the EAP method interface. Each method will need to
 + * register its own EAP type, EAP name, and set of function pointers for method
 + * specific operations. This interface is based on section 5.4 of RFC 4137.
 + */
 +struct eap_method {
 +      int vendor;
 +      EapType method;
 +      const char *name;
 +
 +      void * (*init)(struct eap_sm *sm);
 +      void * (*initPickUp)(struct eap_sm *sm);
 +      void (*reset)(struct eap_sm *sm, void *priv);
 +
 +      struct wpabuf * (*buildReq)(struct eap_sm *sm, void *priv, u8 id);
 +      int (*getTimeout)(struct eap_sm *sm, void *priv);
 +      Boolean (*check)(struct eap_sm *sm, void *priv,
 +                       struct wpabuf *respData);
 +      void (*process)(struct eap_sm *sm, void *priv,
 +                      struct wpabuf *respData);
 +      Boolean (*isDone)(struct eap_sm *sm, void *priv);
 +      u8 * (*getKey)(struct eap_sm *sm, void *priv, size_t *len);
 +      /* isSuccess is not specified in draft-ietf-eap-statemachine-05.txt,
 +       * but it is useful in implementing Policy.getDecision() */
 +      Boolean (*isSuccess)(struct eap_sm *sm, void *priv);
 +
 +      /**
 +       * free - Free EAP method data
 +       * @method: Pointer to the method data registered with
 +       * eap_server_method_register().
 +       *
 +       * This function will be called when the EAP method is being
 +       * unregistered. If the EAP method allocated resources during
 +       * registration (e.g., allocated struct eap_method), they should be
 +       * freed in this function. No other method functions will be called
 +       * after this call. If this function is not defined (i.e., function
 +       * pointer is %NULL), a default handler is used to release the method
 +       * data with free(method). This is suitable for most cases.
 +       */
 +      void (*free)(struct eap_method *method);
 +
 +#define EAP_SERVER_METHOD_INTERFACE_VERSION 1
 +      /**
 +       * version - Version of the EAP server method interface
 +       *
 +       * The EAP server method implementation should set this variable to
 +       * EAP_SERVER_METHOD_INTERFACE_VERSION. This is used to verify that the
 +       * EAP method is using supported API version when using dynamically
 +       * loadable EAP methods.
 +       */
 +      int version;
 +
 +      /**
 +       * next - Pointer to the next EAP method
 +       *
 +       * This variable is used internally in the EAP method registration code
 +       * to create a linked list of registered EAP methods.
 +       */
 +      struct eap_method *next;
 +
 +      /**
 +       * get_emsk - Get EAP method specific keying extended material (EMSK)
 +       * @sm: Pointer to EAP state machine allocated with eap_sm_init()
 +       * @priv: Pointer to private EAP method data from eap_method::init()
 +       * @len: Pointer to a variable to store EMSK length
 +       * Returns: EMSK or %NULL if not available
 +       *
 +       * This function can be used to get the extended keying material from
 +       * the EAP method. The key may already be stored in the method-specific
 +       * 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);
 +};
 +
 +/**
 + * struct eap_sm - EAP server state machine data
 + */
 +struct eap_sm {
 +      enum {
 +              EAP_DISABLED, EAP_INITIALIZE, EAP_IDLE, EAP_RECEIVED,
 +              EAP_INTEGRITY_CHECK, EAP_METHOD_RESPONSE, EAP_METHOD_REQUEST,
 +              EAP_PROPOSE_METHOD, EAP_SELECT_ACTION, EAP_SEND_REQUEST,
 +              EAP_DISCARD, EAP_NAK, EAP_RETRANSMIT, EAP_SUCCESS, EAP_FAILURE,
 +              EAP_TIMEOUT_FAILURE, EAP_PICK_UP_METHOD,
 +              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_INITIATE_REAUTH_START, EAP_INITIATE_RECEIVED
 +      } EAP_state;
 +
 +      /* Constants */
 +      int MaxRetrans;
 +
 +      struct eap_eapol_interface eap_if;
 +
 +      /* Full authenticator state machine local variables */
 +
 +      /* Long-term (maintained between packets) */
 +      EapType currentMethod;
 +      int currentId;
 +      enum {
 +              METHOD_PROPOSED, METHOD_CONTINUE, METHOD_END
 +      } methodState;
 +      int retransCount;
 +      struct wpabuf *lastReqData;
 +      int methodTimeout;
 +
 +      /* Short-term (not maintained between packets) */
 +      Boolean rxResp;
 +      Boolean rxInitiate;
 +      int respId;
 +      EapType respMethod;
 +      int respVendor;
 +      u32 respVendorMethod;
 +      Boolean ignore;
 +      enum {
 +              DECISION_SUCCESS, DECISION_FAILURE, DECISION_CONTINUE,
 +              DECISION_PASSTHROUGH, DECISION_INITIATE_REAUTH_START
 +      } decision;
 +
 +      /* Miscellaneous variables */
 +      const struct eap_method *m; /* selected EAP method */
 +      /* not defined in RFC 4137 */
 +      Boolean changed;
 +      void *eapol_ctx, *msg_ctx;
++      const struct eapol_callbacks *eapol_cb;
 +      void *eap_method_priv;
 +      u8 *identity;
 +      size_t identity_len;
 +      /* Whether Phase 2 method should validate identity match */
 +      int require_identity_match;
 +      int lastId; /* Identifier used in the last EAP-Packet */
 +      struct eap_user *user;
 +      int user_eap_method_index;
 +      int init_phase2;
 +      void *ssl_ctx;
 +      struct eap_sim_db_data *eap_sim_db_priv;
 +      Boolean backend_auth;
 +      Boolean update_user;
 +      int eap_server;
 +
 +      int num_rounds;
 +      enum {
 +              METHOD_PENDING_NONE, METHOD_PENDING_WAIT, METHOD_PENDING_CONT
 +      } method_pending;
 +
 +      u8 *auth_challenge;
 +      u8 *peer_challenge;
 +
 +      u8 *pac_opaque_encr_key;
 +      u8 *eap_fast_a_id;
 +      size_t eap_fast_a_id_len;
 +      char *eap_fast_a_id_info;
 +      enum {
 +              NO_PROV, ANON_PROV, AUTH_PROV, BOTH_PROV
 +      } eap_fast_prov;
 +      int pac_key_lifetime;
 +      int pac_key_refresh_time;
 +      int eap_sim_aka_result_ind;
 +      int tnc;
 +      u16 pwd_group;
 +      struct wps_context *wps;
 +      struct wpabuf *assoc_wps_ie;
 +      struct wpabuf *assoc_p2p_ie;
 +
 +      Boolean start_reauth;
 +
 +      u8 peer_addr[ETH_ALEN];
 +
 +      /* Fragmentation size for EAP method init() handler */
 +      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;
++      unsigned int tls_session_lifetime;
 +
 +#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 bd919e570c825549d7732b240b5340bd4cb5e879,0000000000000000000000000000000000000000..84ecafc7ca3e5db2b4e333079da523e66ccfeb37
mode 100644,000000..100644
--- /dev/null
@@@ -1,1981 -1,0 +1,2016 @@@
-       msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_ERP_TYPE_REAUTH_START, plen,
 +/*
 + * hostapd / EAP Full Authenticator state machine (RFC 4137)
 + * 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.
 + *
 + * This state machine is based on the full authenticator state machine defined
 + * in RFC 4137. However, to support backend authentication in RADIUS
 + * authentication server functionality, parts of backend authenticator (also
 + * from RFC 4137) are mixed in. This functionality is enabled by setting
 + * backend_auth configuration variable to TRUE.
 + */
 +
 +#include "includes.h"
 +
 +#include "common.h"
 +#include "crypto/sha256.h"
 +#include "eap_i.h"
 +#include "state_machine.h"
 +#include "common/wpa_ctrl.h"
 +
 +#define STATE_MACHINE_DATA struct eap_sm
 +#define STATE_MACHINE_DEBUG_PREFIX "EAP"
 +
 +#define EAP_MAX_AUTH_ROUNDS 50
 +
 +static void eap_user_free(struct eap_user *user);
 +
 +
 +/* EAP state machines are described in RFC 4137 */
 +
 +static int eap_sm_calculateTimeout(struct eap_sm *sm, int retransCount,
 +                                 int eapSRTT, int eapRTTVAR,
 +                                 int methodTimeout);
 +static void eap_sm_parseEapResp(struct eap_sm *sm, const struct wpabuf *resp);
 +static int eap_sm_getId(const struct wpabuf *data);
 +static struct wpabuf * eap_sm_buildSuccess(struct eap_sm *sm, u8 id);
 +static struct wpabuf * eap_sm_buildFailure(struct eap_sm *sm, u8 id);
 +static int eap_sm_nextId(struct eap_sm *sm, int id);
 +static void eap_sm_Policy_update(struct eap_sm *sm, const u8 *nak_list,
 +                               size_t len);
 +static EapType eap_sm_Policy_getNextMethod(struct eap_sm *sm, int *vendor);
 +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, plen,
-                           EAP_CODE_FINISH, id);
++      msg = eap_msg_alloc(EAP_VENDOR_IETF,
++                          (EapType) 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)
 +              return -1;
 +
 +      wpabuf_free(*dst);
 +      *dst = wpabuf_dup(src);
 +      return *dst ? 0 : -1;
 +}
 +
 +
 +static int eap_copy_data(u8 **dst, size_t *dst_len,
 +                       const u8 *src, size_t src_len)
 +{
 +      if (src == NULL)
 +              return -1;
 +
 +      os_free(*dst);
 +      *dst = os_malloc(src_len);
 +      if (*dst) {
 +              os_memcpy(*dst, src, src_len);
 +              *dst_len = src_len;
 +              return 0;
 +      } else {
 +              *dst_len = 0;
 +              return -1;
 +      }
 +}
 +
 +#define EAP_COPY(dst, src) \
 +      eap_copy_data((dst), (dst ## Len), (src), (src ## Len))
 +
 +
 +/**
 + * eap_user_get - Fetch user information from the database
 + * @sm: Pointer to EAP state machine allocated with eap_server_sm_init()
 + * @identity: Identity (User-Name) of the user
 + * @identity_len: Length of identity in bytes
 + * @phase2: 0 = EAP phase1 user, 1 = EAP phase2 (tunneled) user
 + * Returns: 0 on success, or -1 on failure
 + *
 + * This function is used to fetch user information for EAP. The user will be
 + * selected based on the specified identity. sm->user and
 + * sm->user_eap_method_index are updated for the new user when a matching user
 + * is found. sm->user can be used to get user information (e.g., password).
 + */
 +int eap_user_get(struct eap_sm *sm, const u8 *identity, size_t identity_len,
 +               int phase2)
 +{
 +      struct eap_user *user;
 +
 +      if (sm == NULL || sm->eapol_cb == NULL ||
 +          sm->eapol_cb->get_eap_user == NULL)
 +              return -1;
 +
 +      eap_user_free(sm->user);
 +      sm->user = NULL;
 +
 +      user = os_zalloc(sizeof(*user));
 +      if (user == NULL)
 +          return -1;
 +
 +      if (sm->eapol_cb->get_eap_user(sm->eapol_ctx, identity,
 +                                     identity_len, phase2, user) != 0) {
 +              eap_user_free(user);
 +              return -1;
 +      }
 +
 +      sm->user = user;
 +      sm->user_eap_method_index = 0;
 +
 +      return 0;
 +}
 +
 +
 +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);
 +      sm->num_rounds = 0;
 +}
 +
 +
 +SM_STATE(EAP, INITIALIZE)
 +{
 +      SM_ENTRY(EAP, INITIALIZE);
 +
 +      if (sm->eap_if.eapRestart && !sm->eap_server && sm->identity) {
 +              /*
 +               * Need to allow internal Identity method to be used instead
 +               * of passthrough at the beginning of reauthentication.
 +               */
 +              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;
 +      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;
 +
 +      /*
 +       * This is not defined in RFC 4137, but method state needs to be
 +       * reseted here so that it does not remain in success state when
 +       * re-authentication starts.
 +       */
 +      if (sm->m && sm->eap_method_priv) {
 +              sm->m->reset(sm, sm->eap_method_priv);
 +              sm->eap_method_priv = NULL;
 +      }
 +      sm->m = NULL;
 +      sm->user_eap_method_index = 0;
 +
 +      if (sm->backend_auth) {
 +              sm->currentMethod = EAP_TYPE_NONE;
 +              /* parse rxResp, respId, respMethod */
 +              eap_sm_parseEapResp(sm, sm->eap_if.eapRespData);
 +              if (sm->rxResp) {
 +                      sm->currentId = sm->respId;
 +              }
 +      }
 +      sm->num_rounds = 0;
 +      sm->method_pending = METHOD_PENDING_NONE;
 +
 +      wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_STARTED
 +              MACSTR, MAC2STR(sm->peer_addr));
 +}
 +
 +
 +SM_STATE(EAP, PICK_UP_METHOD)
 +{
 +      SM_ENTRY(EAP, PICK_UP_METHOD);
 +
 +      if (eap_sm_Policy_doPickUp(sm, sm->respMethod)) {
 +              sm->currentMethod = sm->respMethod;
 +              if (sm->m && sm->eap_method_priv) {
 +                      sm->m->reset(sm, sm->eap_method_priv);
 +                      sm->eap_method_priv = NULL;
 +              }
 +              sm->m = eap_server_get_eap_method(EAP_VENDOR_IETF,
 +                                                sm->currentMethod);
 +              if (sm->m && sm->m->initPickUp) {
 +                      sm->eap_method_priv = sm->m->initPickUp(sm);
 +                      if (sm->eap_method_priv == NULL) {
 +                              wpa_printf(MSG_DEBUG, "EAP: Failed to "
 +                                         "initialize EAP method %d",
 +                                         sm->currentMethod);
 +                              sm->m = NULL;
 +                              sm->currentMethod = EAP_TYPE_NONE;
 +                      }
 +              } else {
 +                      sm->m = NULL;
 +                      sm->currentMethod = EAP_TYPE_NONE;
 +              }
 +      }
 +
 +      wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_PROPOSED_METHOD
 +              "method=%u", sm->currentMethod);
 +}
 +
 +
 +SM_STATE(EAP, IDLE)
 +{
 +      SM_ENTRY(EAP, IDLE);
 +
 +      sm->eap_if.retransWhile = eap_sm_calculateTimeout(
 +              sm, sm->retransCount, sm->eap_if.eapSRTT, sm->eap_if.eapRTTVAR,
 +              sm->methodTimeout);
 +}
 +
 +
 +SM_STATE(EAP, RETRANSMIT)
 +{
 +      SM_ENTRY(EAP, RETRANSMIT);
 +
 +      sm->retransCount++;
 +      if (sm->retransCount <= sm->MaxRetrans && sm->lastReqData) {
 +              if (eap_copy_buf(&sm->eap_if.eapReqData, sm->lastReqData) == 0)
 +                      sm->eap_if.eapReq = TRUE;
 +      }
 +}
 +
 +
 +SM_STATE(EAP, RECEIVED)
 +{
 +      SM_ENTRY(EAP, RECEIVED);
 +
 +      /* parse rxResp, respId, respMethod */
 +      eap_sm_parseEapResp(sm, sm->eap_if.eapRespData);
 +      sm->num_rounds++;
 +}
 +
 +
 +SM_STATE(EAP, DISCARD)
 +{
 +      SM_ENTRY(EAP, DISCARD);
 +      sm->eap_if.eapResp = FALSE;
 +      sm->eap_if.eapNoReq = TRUE;
 +}
 +
 +
 +SM_STATE(EAP, SEND_REQUEST)
 +{
 +      SM_ENTRY(EAP, SEND_REQUEST);
 +
 +      sm->retransCount = 0;
 +      if (sm->eap_if.eapReqData) {
 +              if (eap_copy_buf(&sm->lastReqData, sm->eap_if.eapReqData) == 0)
 +              {
 +                      sm->eap_if.eapResp = FALSE;
 +                      sm->eap_if.eapReq = TRUE;
 +              } else {
 +                      sm->eap_if.eapResp = FALSE;
 +                      sm->eap_if.eapReq = FALSE;
 +              }
 +      } else {
 +              wpa_printf(MSG_INFO, "EAP: SEND_REQUEST - no eapReqData");
 +              sm->eap_if.eapResp = FALSE;
 +              sm->eap_if.eapReq = FALSE;
 +              sm->eap_if.eapNoReq = TRUE;
 +      }
 +}
 +
 +
 +SM_STATE(EAP, INTEGRITY_CHECK)
 +{
 +      SM_ENTRY(EAP, INTEGRITY_CHECK);
 +
 +      if (!eap_hdr_len_valid(sm->eap_if.eapRespData, 1)) {
 +              sm->ignore = TRUE;
 +              return;
 +      }
 +
 +      if (sm->m->check) {
 +              sm->ignore = sm->m->check(sm, sm->eap_method_priv,
 +                                        sm->eap_if.eapRespData);
 +      }
 +}
 +
 +
 +SM_STATE(EAP, METHOD_REQUEST)
 +{
 +      SM_ENTRY(EAP, METHOD_REQUEST);
 +
 +      if (sm->m == NULL) {
 +              wpa_printf(MSG_DEBUG, "EAP: method not initialized");
 +              return;
 +      }
 +
 +      sm->currentId = eap_sm_nextId(sm, sm->currentId);
 +      wpa_printf(MSG_DEBUG, "EAP: building EAP-Request: Identifier %d",
 +                 sm->currentId);
 +      sm->lastId = sm->currentId;
 +      wpabuf_free(sm->eap_if.eapReqData);
 +      sm->eap_if.eapReqData = sm->m->buildReq(sm, sm->eap_method_priv,
 +                                              sm->currentId);
 +      if (sm->m->getTimeout)
 +              sm->methodTimeout = sm->m->getTimeout(sm, sm->eap_method_priv);
 +      else
 +              sm->methodTimeout = 0;
 +}
 +
 +
 +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);
 +
 +      if (!eap_hdr_len_valid(sm->eap_if.eapRespData, 1))
 +              return;
 +
 +      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);
 +              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,
 +                              &sm->eap_if.eapKeyDataLen);
 +              } else {
 +                      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;
 +      }
 +}
 +
 +
 +SM_STATE(EAP, PROPOSE_METHOD)
 +{
 +      int vendor;
 +      EapType type;
 +
 +      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;
 +      else
 +              sm->currentMethod = EAP_TYPE_EXPANDED;
 +      if (sm->m && sm->eap_method_priv) {
 +              sm->m->reset(sm, sm->eap_method_priv);
 +              sm->eap_method_priv = NULL;
 +      }
 +      sm->m = eap_server_get_eap_method(vendor, type);
 +      if (sm->m) {
 +              sm->eap_method_priv = sm->m->init(sm);
 +              if (sm->eap_method_priv == NULL) {
 +                      wpa_printf(MSG_DEBUG, "EAP: Failed to initialize EAP "
 +                                 "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;
 +      else
 +              sm->methodState = METHOD_PROPOSED;
 +
 +      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);
 +}
 +
 +
 +SM_STATE(EAP, NAK)
 +{
 +      const struct eap_hdr *nak;
 +      size_t len = 0;
 +      const u8 *pos;
 +      const u8 *nak_list = NULL;
 +
 +      SM_ENTRY(EAP, NAK);
 +
 +      if (sm->eap_method_priv) {
 +              sm->m->reset(sm, sm->eap_method_priv);
 +              sm->eap_method_priv = NULL;
 +      }
 +      sm->m = NULL;
 +
 +      if (!eap_hdr_len_valid(sm->eap_if.eapRespData, 1))
 +              return;
 +
 +      nak = wpabuf_head(sm->eap_if.eapRespData);
 +      if (nak && wpabuf_len(sm->eap_if.eapRespData) > sizeof(*nak)) {
 +              len = be_to_host16(nak->length);
 +              if (len > wpabuf_len(sm->eap_if.eapRespData))
 +                      len = wpabuf_len(sm->eap_if.eapRespData);
 +              pos = (const u8 *) (nak + 1);
 +              len -= sizeof(*nak);
 +              if (*pos == EAP_TYPE_NAK) {
 +                      pos++;
 +                      len--;
 +                      nak_list = pos;
 +              }
 +      }
 +      eap_sm_Policy_update(sm, nak_list, len);
 +}
 +
 +
 +SM_STATE(EAP, SELECT_ACTION)
 +{
 +      SM_ENTRY(EAP, SELECT_ACTION);
 +
 +      sm->decision = eap_sm_Policy_getDecision(sm);
 +}
 +
 +
 +SM_STATE(EAP, TIMEOUT_FAILURE)
 +{
 +      SM_ENTRY(EAP, TIMEOUT_FAILURE);
 +
 +      sm->eap_if.eapTimeout = TRUE;
 +}
 +
 +
 +SM_STATE(EAP, FAILURE)
 +{
 +      SM_ENTRY(EAP, FAILURE);
 +
 +      wpabuf_free(sm->eap_if.eapReqData);
 +      sm->eap_if.eapReqData = eap_sm_buildFailure(sm, sm->currentId);
 +      wpabuf_free(sm->lastReqData);
 +      sm->lastReqData = NULL;
 +      sm->eap_if.eapFail = TRUE;
 +
 +      wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_FAILURE
 +              MACSTR, MAC2STR(sm->peer_addr));
 +}
 +
 +
 +SM_STATE(EAP, SUCCESS)
 +{
 +      SM_ENTRY(EAP, SUCCESS);
 +
 +      wpabuf_free(sm->eap_if.eapReqData);
 +      sm->eap_if.eapReqData = eap_sm_buildSuccess(sm, sm->currentId);
 +      wpabuf_free(sm->lastReqData);
 +      sm->lastReqData = NULL;
 +      if (sm->eap_if.eapKeyData)
 +              sm->eap_if.eapKeyAvailable = TRUE;
 +      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_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;
-       if (flags & 0x80) {
++      msg = eap_msg_alloc(EAP_VENDOR_IETF, (EapType) 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;
 +
-       pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_ERP_TYPE_REAUTH,
++      if ((flags & 0x80) || !erp) {
 +              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;
 +
-                                  struct eapol_callbacks *eapol_cb,
++      pos = eap_hdr_validate(EAP_VENDOR_IETF, (EapType) 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;
 +}
 +
 +
 +SM_STATE(EAP, IDLE2)
 +{
 +      SM_ENTRY(EAP, IDLE2);
 +
 +      sm->eap_if.retransWhile = eap_sm_calculateTimeout(
 +              sm, sm->retransCount, sm->eap_if.eapSRTT, sm->eap_if.eapRTTVAR,
 +              sm->methodTimeout);
 +}
 +
 +
 +SM_STATE(EAP, RETRANSMIT2)
 +{
 +      SM_ENTRY(EAP, RETRANSMIT2);
 +
 +      sm->retransCount++;
 +      if (sm->retransCount <= sm->MaxRetrans && sm->lastReqData) {
 +              if (eap_copy_buf(&sm->eap_if.eapReqData, sm->lastReqData) == 0)
 +                      sm->eap_if.eapReq = TRUE;
 +      }
 +}
 +
 +
 +SM_STATE(EAP, RECEIVED2)
 +{
 +      SM_ENTRY(EAP, RECEIVED2);
 +
 +      /* parse rxResp, respId, respMethod */
 +      eap_sm_parseEapResp(sm, sm->eap_if.eapRespData);
 +}
 +
 +
 +SM_STATE(EAP, DISCARD2)
 +{
 +      SM_ENTRY(EAP, DISCARD2);
 +      sm->eap_if.eapResp = FALSE;
 +      sm->eap_if.eapNoReq = TRUE;
 +}
 +
 +
 +SM_STATE(EAP, SEND_REQUEST2)
 +{
 +      SM_ENTRY(EAP, SEND_REQUEST2);
 +
 +      sm->retransCount = 0;
 +      if (sm->eap_if.eapReqData) {
 +              if (eap_copy_buf(&sm->lastReqData, sm->eap_if.eapReqData) == 0)
 +              {
 +                      sm->eap_if.eapResp = FALSE;
 +                      sm->eap_if.eapReq = TRUE;
 +              } else {
 +                      sm->eap_if.eapResp = FALSE;
 +                      sm->eap_if.eapReq = FALSE;
 +              }
 +      } else {
 +              wpa_printf(MSG_INFO, "EAP: SEND_REQUEST2 - no eapReqData");
 +              sm->eap_if.eapResp = FALSE;
 +              sm->eap_if.eapReq = FALSE;
 +              sm->eap_if.eapNoReq = TRUE;
 +      }
 +}
 +
 +
 +SM_STATE(EAP, AAA_REQUEST)
 +{
 +      SM_ENTRY(EAP, AAA_REQUEST);
 +
 +      if (sm->eap_if.eapRespData == NULL) {
 +              wpa_printf(MSG_INFO, "EAP: AAA_REQUEST - no eapRespData");
 +              return;
 +      }
 +
 +      /*
 +       * if (respMethod == IDENTITY)
 +       *      aaaIdentity = eapRespData
 +       * This is already taken care of by the EAP-Identity method which
 +       * stores the identity into sm->identity.
 +       */
 +
 +      eap_copy_buf(&sm->eap_if.aaaEapRespData, sm->eap_if.eapRespData);
 +}
 +
 +
 +SM_STATE(EAP, AAA_RESPONSE)
 +{
 +      SM_ENTRY(EAP, AAA_RESPONSE);
 +
 +      eap_copy_buf(&sm->eap_if.eapReqData, sm->eap_if.aaaEapReqData);
 +      sm->currentId = eap_sm_getId(sm->eap_if.eapReqData);
 +      sm->methodTimeout = sm->eap_if.aaaMethodTimeout;
 +}
 +
 +
 +SM_STATE(EAP, AAA_IDLE)
 +{
 +      SM_ENTRY(EAP, AAA_IDLE);
 +
 +      sm->eap_if.aaaFail = FALSE;
 +      sm->eap_if.aaaSuccess = FALSE;
 +      sm->eap_if.aaaEapReq = FALSE;
 +      sm->eap_if.aaaEapNoReq = FALSE;
 +      sm->eap_if.aaaEapResp = TRUE;
 +}
 +
 +
 +SM_STATE(EAP, TIMEOUT_FAILURE2)
 +{
 +      SM_ENTRY(EAP, TIMEOUT_FAILURE2);
 +
 +      sm->eap_if.eapTimeout = TRUE;
 +}
 +
 +
 +SM_STATE(EAP, FAILURE2)
 +{
 +      SM_ENTRY(EAP, FAILURE2);
 +
 +      eap_copy_buf(&sm->eap_if.eapReqData, sm->eap_if.aaaEapReqData);
 +      sm->eap_if.eapFail = TRUE;
 +}
 +
 +
 +SM_STATE(EAP, SUCCESS2)
 +{
 +      SM_ENTRY(EAP, SUCCESS2);
 +
 +      eap_copy_buf(&sm->eap_if.eapReqData, sm->eap_if.aaaEapReqData);
 +
 +      sm->eap_if.eapKeyAvailable = sm->eap_if.aaaEapKeyAvailable;
 +      if (sm->eap_if.aaaEapKeyAvailable) {
 +              EAP_COPY(&sm->eap_if.eapKeyData, sm->eap_if.aaaEapKeyData);
 +      } else {
 +              bin_clear_free(sm->eap_if.eapKeyData, sm->eap_if.eapKeyDataLen);
 +              sm->eap_if.eapKeyData = NULL;
 +              sm->eap_if.eapKeyDataLen = 0;
 +      }
 +
 +      sm->eap_if.eapSuccess = TRUE;
 +
 +      /*
 +       * Start reauthentication with identity request even though we know the
 +       * previously used identity. This is needed to get reauthentication
 +       * started properly.
 +       */
 +      sm->start_reauth = TRUE;
 +}
 +
 +
 +SM_STEP(EAP)
 +{
 +      if (sm->eap_if.eapRestart && sm->eap_if.portEnabled)
 +              SM_ENTER_GLOBAL(EAP, INITIALIZE);
 +      else if (!sm->eap_if.portEnabled)
 +              SM_ENTER_GLOBAL(EAP, DISABLED);
 +      else if (sm->num_rounds > EAP_MAX_AUTH_ROUNDS) {
 +              if (sm->num_rounds == EAP_MAX_AUTH_ROUNDS + 1) {
 +                      wpa_printf(MSG_DEBUG, "EAP: more than %d "
 +                                 "authentication rounds - abort",
 +                                 EAP_MAX_AUTH_ROUNDS);
 +                      sm->num_rounds++;
 +                      SM_ENTER_GLOBAL(EAP, FAILURE);
 +              }
 +      } else switch (sm->EAP_state) {
 +      case EAP_INITIALIZE:
 +              if (sm->backend_auth) {
 +                      if (!sm->rxResp)
 +                              SM_ENTER(EAP, SELECT_ACTION);
 +                      else if (sm->rxResp &&
 +                               (sm->respMethod == EAP_TYPE_NAK ||
 +                                (sm->respMethod == EAP_TYPE_EXPANDED &&
 +                                 sm->respVendor == EAP_VENDOR_IETF &&
 +                                 sm->respVendorMethod == EAP_TYPE_NAK)))
 +                              SM_ENTER(EAP, NAK);
 +                      else
 +                              SM_ENTER(EAP, PICK_UP_METHOD);
 +              } else {
 +                      SM_ENTER(EAP, SELECT_ACTION);
 +              }
 +              break;
 +      case EAP_PICK_UP_METHOD:
 +              if (sm->currentMethod == EAP_TYPE_NONE) {
 +                      SM_ENTER(EAP, SELECT_ACTION);
 +              } else {
 +                      SM_ENTER(EAP, METHOD_RESPONSE);
 +              }
 +              break;
 +      case EAP_DISABLED:
 +              if (sm->eap_if.portEnabled)
 +                      SM_ENTER(EAP, INITIALIZE);
 +              break;
 +      case EAP_IDLE:
 +              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:
 +              if (sm->retransCount > sm->MaxRetrans)
 +                      SM_ENTER(EAP, TIMEOUT_FAILURE);
 +              else
 +                      SM_ENTER(EAP, IDLE);
 +              break;
 +      case EAP_RECEIVED:
 +              if (sm->rxResp && (sm->respId == sm->currentId) &&
 +                  (sm->respMethod == EAP_TYPE_NAK ||
 +                   (sm->respMethod == EAP_TYPE_EXPANDED &&
 +                    sm->respVendor == EAP_VENDOR_IETF &&
 +                    sm->respVendorMethod == EAP_TYPE_NAK))
 +                  && (sm->methodState == METHOD_PROPOSED))
 +                      SM_ENTER(EAP, NAK);
 +              else if (sm->rxResp && (sm->respId == sm->currentId) &&
 +                       ((sm->respMethod == sm->currentMethod) ||
 +                        (sm->respMethod == EAP_TYPE_EXPANDED &&
 +                         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;
 +      case EAP_DISCARD:
 +              SM_ENTER(EAP, IDLE);
 +              break;
 +      case EAP_SEND_REQUEST:
 +              SM_ENTER(EAP, IDLE);
 +              break;
 +      case EAP_INTEGRITY_CHECK:
 +              if (sm->ignore)
 +                      SM_ENTER(EAP, DISCARD);
 +              else
 +                      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);
++              if (sm->eap_if.eapNoReq && !sm->eap_if.eapReq) {
++                      /*
++                       * This transition is not mentioned in RFC 4137, but it
++                       * is needed to handle cleanly a case where EAP method
++                       * buildReq fails.
++                       */
++                      wpa_printf(MSG_DEBUG,
++                                 "EAP: Method did not return a request");
++                      SM_ENTER(EAP, FAILURE);
++                      break;
++              }
 +              break;
 +      case EAP_METHOD_RESPONSE:
 +              /*
 +               * Note: Mechanism to allow EAP methods to wait while going
 +               * through pending processing is an extension to RFC 4137
 +               * which only defines the transits to SELECT_ACTION and
 +               * METHOD_REQUEST from this METHOD_RESPONSE state.
 +               */
 +              if (sm->methodState == METHOD_END)
 +                      SM_ENTER(EAP, SELECT_ACTION);
 +              else if (sm->method_pending == METHOD_PENDING_WAIT) {
 +                      wpa_printf(MSG_DEBUG, "EAP: Method has pending "
 +                                 "processing - wait before proceeding to "
 +                                 "METHOD_REQUEST state");
 +              } else if (sm->method_pending == METHOD_PENDING_CONT) {
 +                      wpa_printf(MSG_DEBUG, "EAP: Method has completed "
 +                                 "pending processing - reprocess pending "
 +                                 "EAP message");
 +                      sm->method_pending = METHOD_PENDING_NONE;
 +                      SM_ENTER(EAP, METHOD_RESPONSE);
 +              } else
 +                      SM_ENTER(EAP, METHOD_REQUEST);
 +              break;
 +      case EAP_PROPOSE_METHOD:
 +              /*
 +               * Note: Mechanism to allow EAP methods to wait while going
 +               * through pending processing is an extension to RFC 4137
 +               * which only defines the transit to METHOD_REQUEST from this
 +               * PROPOSE_METHOD state.
 +               */
 +              if (sm->method_pending == METHOD_PENDING_WAIT) {
 +                      wpa_printf(MSG_DEBUG, "EAP: Method has pending "
 +                                 "processing - wait before proceeding to "
 +                                 "METHOD_REQUEST state");
 +                      if (sm->user_eap_method_index > 0)
 +                              sm->user_eap_method_index--;
 +              } else if (sm->method_pending == METHOD_PENDING_CONT) {
 +                      wpa_printf(MSG_DEBUG, "EAP: Method has completed "
 +                                 "pending processing - reprocess pending "
 +                                 "EAP message");
 +                      sm->method_pending = METHOD_PENDING_NONE;
 +                      SM_ENTER(EAP, PROPOSE_METHOD);
 +              } else
 +                      SM_ENTER(EAP, METHOD_REQUEST);
 +              break;
 +      case EAP_NAK:
 +              SM_ENTER(EAP, SELECT_ACTION);
 +              break;
 +      case EAP_SELECT_ACTION:
 +              if (sm->decision == DECISION_FAILURE)
 +                      SM_ENTER(EAP, FAILURE);
 +              else if (sm->decision == DECISION_SUCCESS)
 +                      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:
 +              break;
 +      case EAP_SUCCESS:
 +              break;
 +
 +      case EAP_INITIALIZE_PASSTHROUGH:
 +              if (sm->currentId == -1)
 +                      SM_ENTER(EAP, AAA_IDLE);
 +              else
 +                      SM_ENTER(EAP, AAA_REQUEST);
 +              break;
 +      case EAP_IDLE2:
 +              if (sm->eap_if.eapResp)
 +                      SM_ENTER(EAP, RECEIVED2);
 +              else if (sm->eap_if.retransWhile == 0)
 +                      SM_ENTER(EAP, RETRANSMIT2);
 +              break;
 +      case EAP_RETRANSMIT2:
 +              if (sm->retransCount > sm->MaxRetrans)
 +                      SM_ENTER(EAP, TIMEOUT_FAILURE2);
 +              else
 +                      SM_ENTER(EAP, IDLE2);
 +              break;
 +      case EAP_RECEIVED2:
 +              if (sm->rxResp && (sm->respId == sm->currentId))
 +                      SM_ENTER(EAP, AAA_REQUEST);
 +              else
 +                      SM_ENTER(EAP, DISCARD2);
 +              break;
 +      case EAP_DISCARD2:
 +              SM_ENTER(EAP, IDLE2);
 +              break;
 +      case EAP_SEND_REQUEST2:
 +              SM_ENTER(EAP, IDLE2);
 +              break;
 +      case EAP_AAA_REQUEST:
 +              SM_ENTER(EAP, AAA_IDLE);
 +              break;
 +      case EAP_AAA_RESPONSE:
 +              SM_ENTER(EAP, SEND_REQUEST2);
 +              break;
 +      case EAP_AAA_IDLE:
 +              if (sm->eap_if.aaaFail)
 +                      SM_ENTER(EAP, FAILURE2);
 +              else if (sm->eap_if.aaaSuccess)
 +                      SM_ENTER(EAP, SUCCESS2);
 +              else if (sm->eap_if.aaaEapReq)
 +                      SM_ENTER(EAP, AAA_RESPONSE);
 +              else if (sm->eap_if.aaaTimeout)
 +                      SM_ENTER(EAP, TIMEOUT_FAILURE2);
 +              break;
 +      case EAP_TIMEOUT_FAILURE2:
 +              break;
 +      case EAP_FAILURE2:
 +              break;
 +      case EAP_SUCCESS2:
 +              break;
 +      }
 +}
 +
 +
 +static int eap_sm_calculateTimeout(struct eap_sm *sm, int retransCount,
 +                                 int eapSRTT, int eapRTTVAR,
 +                                 int methodTimeout)
 +{
 +      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
 +               * timeout hint. Use that as-is as a timeout for retransmitting
 +               * the EAP request if no response is received.
 +               */
 +              wpa_printf(MSG_DEBUG, "EAP: retransmit timeout %d seconds "
 +                         "(from EAP method hint)", methodTimeout);
 +              return methodTimeout;
 +      }
 +
 +      /*
 +       * RFC 3748 recommends algorithms described in RFC 2988 for estimation
 +       * of the retransmission timeout. This should be implemented once
 +       * round-trip time measurements are available. For nowm a simple
 +       * backoff mechanism is used instead if there are no EAP method
 +       * specific hints.
 +       *
 +       * SRTT = smoothed round-trip time
 +       * RTTVAR = round-trip time variation
 +       * RTO = retransmission timeout
 +       */
 +
 +      /*
 +       * RFC 2988, 2.1: before RTT measurement, set RTO to 3 seconds for
 +       * initial retransmission and then double the RTO to provide back off
 +       * per 5.5. Limit the maximum RTO to 20 seconds per RFC 3748, 4.3
 +       * modified RTOmax.
 +       */
 +      rto = 3;
 +      for (i = 0; i < retransCount; i++) {
 +              rto *= 2;
 +              if (rto >= 20) {
 +                      rto = 20;
 +                      break;
 +              }
 +      }
 +
 +      wpa_printf(MSG_DEBUG, "EAP: retransmit timeout %d seconds "
 +                 "(from dynamic back off; retransCount=%d)",
 +                 rto, retransCount);
 +
 +      return rto;
 +}
 +
 +
 +static void eap_sm_parseEapResp(struct eap_sm *sm, const struct wpabuf *resp)
 +{
 +      const struct eap_hdr *hdr;
 +      size_t plen;
 +
 +      /* parse rxResp, respId, respMethod */
 +      sm->rxResp = FALSE;
 +      sm->rxInitiate = FALSE;
 +      sm->respId = -1;
 +      sm->respMethod = EAP_TYPE_NONE;
 +      sm->respVendor = EAP_VENDOR_IETF;
 +      sm->respVendorMethod = EAP_TYPE_NONE;
 +
 +      if (resp == NULL || wpabuf_len(resp) < sizeof(*hdr)) {
 +              wpa_printf(MSG_DEBUG, "EAP: parseEapResp: invalid resp=%p "
 +                         "len=%lu", resp,
 +                         resp ? (unsigned long) wpabuf_len(resp) : 0);
 +              return;
 +      }
 +
 +      hdr = wpabuf_head(resp);
 +      plen = be_to_host16(hdr->length);
 +      if (plen > wpabuf_len(resp)) {
 +              wpa_printf(MSG_DEBUG, "EAP: Ignored truncated EAP-Packet "
 +                         "(len=%lu plen=%lu)",
 +                         (unsigned long) wpabuf_len(resp),
 +                         (unsigned long) plen);
 +              return;
 +      }
 +
 +      sm->respId = hdr->identifier;
 +
 +      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);
 +              sm->respMethod = *pos++;
 +              if (sm->respMethod == EAP_TYPE_EXPANDED) {
 +                      if (plen < sizeof(*hdr) + 8) {
 +                              wpa_printf(MSG_DEBUG, "EAP: Ignored truncated "
 +                                         "expanded EAP-Packet (plen=%lu)",
 +                                         (unsigned long) plen);
 +                              return;
 +                      }
 +                      sm->respVendor = WPA_GET_BE24(pos);
 +                      pos += 3;
 +                      sm->respVendorMethod = WPA_GET_BE32(pos);
 +              }
 +      }
 +
 +      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);
 +}
 +
 +
 +static int eap_sm_getId(const struct wpabuf *data)
 +{
 +      const struct eap_hdr *hdr;
 +
 +      if (data == NULL || wpabuf_len(data) < sizeof(*hdr))
 +              return -1;
 +
 +      hdr = wpabuf_head(data);
 +      wpa_printf(MSG_DEBUG, "EAP: getId: id=%d", hdr->identifier);
 +      return hdr->identifier;
 +}
 +
 +
 +static struct wpabuf * eap_sm_buildSuccess(struct eap_sm *sm, u8 id)
 +{
 +      struct wpabuf *msg;
 +      struct eap_hdr *resp;
 +      wpa_printf(MSG_DEBUG, "EAP: Building EAP-Success (id=%d)", id);
 +
 +      msg = wpabuf_alloc(sizeof(*resp));
 +      if (msg == NULL)
 +              return NULL;
 +      resp = wpabuf_put(msg, sizeof(*resp));
 +      resp->code = EAP_CODE_SUCCESS;
 +      resp->identifier = id;
 +      resp->length = host_to_be16(sizeof(*resp));
 +
 +      return msg;
 +}
 +
 +
 +static struct wpabuf * eap_sm_buildFailure(struct eap_sm *sm, u8 id)
 +{
 +      struct wpabuf *msg;
 +      struct eap_hdr *resp;
 +      wpa_printf(MSG_DEBUG, "EAP: Building EAP-Failure (id=%d)", id);
 +
 +      msg = wpabuf_alloc(sizeof(*resp));
 +      if (msg == NULL)
 +              return NULL;
 +      resp = wpabuf_put(msg, sizeof(*resp));
 +      resp->code = EAP_CODE_FAILURE;
 +      resp->identifier = id;
 +      resp->length = host_to_be16(sizeof(*resp));
 +
 +      return msg;
 +}
 +
 +
 +static int eap_sm_nextId(struct eap_sm *sm, int id)
 +{
 +      if (id < 0) {
 +              /* RFC 3748 Ch 4.1: recommended to initialize Identifier with a
 +               * random number */
 +              id = rand() & 0xff;
 +              if (id != sm->lastId)
 +                      return id;
 +      }
 +      return (id + 1) & 0xff;
 +}
 +
 +
 +/**
 + * eap_sm_process_nak - Process EAP-Response/Nak
 + * @sm: Pointer to EAP state machine allocated with eap_server_sm_init()
 + * @nak_list: Nak list (allowed methods) from the supplicant
 + * @len: Length of nak_list in bytes
 + *
 + * This function is called when EAP-Response/Nak is received from the
 + * supplicant. This can happen for both phase 1 and phase 2 authentications.
 + */
 +void eap_sm_process_nak(struct eap_sm *sm, const u8 *nak_list, size_t len)
 +{
 +      int i;
 +      size_t j;
 +
 +      if (sm->user == NULL)
 +              return;
 +
 +      wpa_printf(MSG_MSGDUMP, "EAP: processing NAK (current EAP method "
 +                 "index %d)", sm->user_eap_method_index);
 +
 +      wpa_hexdump(MSG_MSGDUMP, "EAP: configured methods",
 +                  (u8 *) sm->user->methods,
 +                  EAP_MAX_METHODS * sizeof(sm->user->methods[0]));
 +      wpa_hexdump(MSG_MSGDUMP, "EAP: list of methods supported by the peer",
 +                  nak_list, len);
 +
 +      i = sm->user_eap_method_index;
 +      while (i < EAP_MAX_METHODS &&
 +             (sm->user->methods[i].vendor != EAP_VENDOR_IETF ||
 +              sm->user->methods[i].method != EAP_TYPE_NONE)) {
 +              if (sm->user->methods[i].vendor != EAP_VENDOR_IETF)
 +                      goto not_found;
 +              for (j = 0; j < len; j++) {
 +                      if (nak_list[j] == sm->user->methods[i].method) {
 +                              break;
 +                      }
 +              }
 +
 +              if (j < len) {
 +                      /* found */
 +                      i++;
 +                      continue;
 +              }
 +
 +      not_found:
 +              /* not found - remove from the list */
 +              if (i + 1 < EAP_MAX_METHODS) {
 +                      os_memmove(&sm->user->methods[i],
 +                                 &sm->user->methods[i + 1],
 +                                 (EAP_MAX_METHODS - i - 1) *
 +                                 sizeof(sm->user->methods[0]));
 +              }
 +              sm->user->methods[EAP_MAX_METHODS - 1].vendor =
 +                      EAP_VENDOR_IETF;
 +              sm->user->methods[EAP_MAX_METHODS - 1].method = EAP_TYPE_NONE;
 +      }
 +
 +      wpa_hexdump(MSG_MSGDUMP, "EAP: new list of configured methods",
 +                  (u8 *) sm->user->methods, EAP_MAX_METHODS *
 +                  sizeof(sm->user->methods[0]));
 +}
 +
 +
 +static void eap_sm_Policy_update(struct eap_sm *sm, const u8 *nak_list,
 +                               size_t len)
 +{
 +      if (nak_list == NULL || sm == NULL || sm->user == NULL)
 +              return;
 +
 +      if (sm->user->phase2) {
 +              wpa_printf(MSG_DEBUG, "EAP: EAP-Nak received after Phase2 user"
 +                         " info was selected - reject");
 +              sm->decision = DECISION_FAILURE;
 +              return;
 +      }
 +
 +      eap_sm_process_nak(sm, nak_list, len);
 +}
 +
 +
 +static EapType eap_sm_Policy_getNextMethod(struct eap_sm *sm, int *vendor)
 +{
 +      EapType next;
 +      int idx = sm->user_eap_method_index;
 +
 +      /* In theory, there should be no problems with starting
 +       * re-authentication with something else than EAP-Request/Identity and
 +       * this does indeed work with wpa_supplicant. However, at least Funk
 +       * Supplicant seemed to ignore re-auth if it skipped
 +       * EAP-Request/Identity.
 +       * Re-auth sets currentId == -1, so that can be used here to select
 +       * whether Identity needs to be requested again. */
 +      if (sm->identity == NULL || sm->currentId == -1) {
 +              *vendor = EAP_VENDOR_IETF;
 +              next = EAP_TYPE_IDENTITY;
 +              sm->update_user = TRUE;
 +      } else if (sm->user && idx < EAP_MAX_METHODS &&
 +                 (sm->user->methods[idx].vendor != EAP_VENDOR_IETF ||
 +                  sm->user->methods[idx].method != EAP_TYPE_NONE)) {
 +              *vendor = sm->user->methods[idx].vendor;
 +              next = sm->user->methods[idx].method;
 +              sm->user_eap_method_index++;
 +      } else {
 +              *vendor = EAP_VENDOR_IETF;
 +              next = EAP_TYPE_NONE;
 +      }
 +      wpa_printf(MSG_DEBUG, "EAP: getNextMethod: vendor %d type %d",
 +                 *vendor, next);
 +      return next;
 +}
 +
 +
 +static int eap_sm_Policy_getDecision(struct eap_sm *sm)
 +{
 +      if (!sm->eap_server && sm->identity && !sm->start_reauth) {
 +              wpa_printf(MSG_DEBUG, "EAP: getDecision: -> PASSTHROUGH");
 +              return DECISION_PASSTHROUGH;
 +      }
 +
 +      if (sm->m && sm->currentMethod != EAP_TYPE_IDENTITY &&
 +          sm->m->isSuccess(sm, sm->eap_method_priv)) {
 +              wpa_printf(MSG_DEBUG, "EAP: getDecision: method succeeded -> "
 +                         "SUCCESS");
 +              sm->update_user = TRUE;
 +              return DECISION_SUCCESS;
 +      }
 +
 +      if (sm->m && sm->m->isDone(sm, sm->eap_method_priv) &&
 +          !sm->m->isSuccess(sm, sm->eap_method_priv)) {
 +              wpa_printf(MSG_DEBUG, "EAP: getDecision: method failed -> "
 +                         "FAILURE");
 +              sm->update_user = TRUE;
 +              return DECISION_FAILURE;
 +      }
 +
 +      if ((sm->user == NULL || sm->update_user) && sm->identity &&
 +          !sm->start_reauth) {
 +              /*
 +               * Allow Identity method to be started once to allow identity
 +               * selection hint to be sent from the authentication server,
 +               * but prevent a loop of Identity requests by only allowing
 +               * this to happen once.
 +               */
 +              int id_req = 0;
 +              if (sm->user && sm->currentMethod == EAP_TYPE_IDENTITY &&
 +                  sm->user->methods[0].vendor == EAP_VENDOR_IETF &&
 +                  sm->user->methods[0].method == EAP_TYPE_IDENTITY)
 +                      id_req = 1;
 +              if (eap_user_get(sm, sm->identity, sm->identity_len, 0) != 0) {
 +                      wpa_printf(MSG_DEBUG, "EAP: getDecision: user not "
 +                                 "found from database -> FAILURE");
 +                      return DECISION_FAILURE;
 +              }
 +              if (id_req && sm->user &&
 +                  sm->user->methods[0].vendor == EAP_VENDOR_IETF &&
 +                  sm->user->methods[0].method == EAP_TYPE_IDENTITY) {
 +                      wpa_printf(MSG_DEBUG, "EAP: getDecision: stop "
 +                                 "identity request loop -> FAILURE");
 +                      sm->update_user = TRUE;
 +                      return DECISION_FAILURE;
 +              }
 +              sm->update_user = FALSE;
 +      }
 +      sm->start_reauth = FALSE;
 +
 +      if (sm->user && sm->user_eap_method_index < EAP_MAX_METHODS &&
 +          (sm->user->methods[sm->user_eap_method_index].vendor !=
 +           EAP_VENDOR_IETF ||
 +           sm->user->methods[sm->user_eap_method_index].method !=
 +           EAP_TYPE_NONE)) {
 +              wpa_printf(MSG_DEBUG, "EAP: getDecision: another method "
 +                         "available -> CONTINUE");
 +              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");
 +              return DECISION_CONTINUE;
 +      }
 +
 +      wpa_printf(MSG_DEBUG, "EAP: getDecision: no more methods available -> "
 +                 "FAILURE");
 +      return DECISION_FAILURE;
 +}
 +
 +
 +static Boolean eap_sm_Policy_doPickUp(struct eap_sm *sm, EapType method)
 +{
 +      return method == EAP_TYPE_IDENTITY ? TRUE : FALSE;
 +}
 +
 +
 +/**
 + * eap_server_sm_step - Step EAP server state machine
 + * @sm: Pointer to EAP state machine allocated with eap_server_sm_init()
 + * Returns: 1 if EAP state was changed or 0 if not
 + *
 + * This function advances EAP state machine to a new state to match with the
 + * current variables. This should be called whenever variables used by the EAP
 + * state machine have changed.
 + */
 +int eap_server_sm_step(struct eap_sm *sm)
 +{
 +      int res = 0;
 +      do {
 +              sm->changed = FALSE;
 +              SM_STEP_RUN(EAP);
 +              if (sm->changed)
 +                      res = 1;
 +      } while (sm->changed);
 +      return res;
 +}
 +
 +
 +static void eap_user_free(struct eap_user *user)
 +{
 +      if (user == NULL)
 +              return;
 +      bin_clear_free(user->password, user->password_len);
 +      user->password = NULL;
 +      os_free(user);
 +}
 +
 +
 +/**
 + * eap_server_sm_init - Allocate and initialize EAP server state machine
 + * @eapol_ctx: Context data to be used with eapol_cb calls
 + * @eapol_cb: Pointer to EAPOL callback functions
 + * @conf: EAP configuration
 + * Returns: Pointer to the allocated EAP state machine or %NULL on failure
 + *
 + * This function allocates and initializes an EAP state machine.
 + */
 +struct eap_sm * eap_server_sm_init(void *eapol_ctx,
++                                 const struct eapol_callbacks *eapol_cb,
 +                                 struct eap_config *conf)
 +{
 +      struct eap_sm *sm;
 +
 +      sm = os_zalloc(sizeof(*sm));
 +      if (sm == NULL)
 +              return NULL;
 +      sm->eapol_ctx = eapol_ctx;
 +      sm->eapol_cb = eapol_cb;
 +      sm->MaxRetrans = 5; /* RFC 3748: max 3-5 retransmissions suggested */
 +      sm->ssl_ctx = conf->ssl_ctx;
 +      sm->msg_ctx = conf->msg_ctx;
 +      sm->eap_sim_db_priv = conf->eap_sim_db_priv;
 +      sm->backend_auth = conf->backend_auth;
 +      sm->eap_server = conf->eap_server;
 +      if (conf->pac_opaque_encr_key) {
 +              sm->pac_opaque_encr_key = os_malloc(16);
 +              if (sm->pac_opaque_encr_key) {
 +                      os_memcpy(sm->pac_opaque_encr_key,
 +                                conf->pac_opaque_encr_key, 16);
 +              }
 +      }
 +      if (conf->eap_fast_a_id) {
 +              sm->eap_fast_a_id = os_malloc(conf->eap_fast_a_id_len);
 +              if (sm->eap_fast_a_id) {
 +                      os_memcpy(sm->eap_fast_a_id, conf->eap_fast_a_id,
 +                                conf->eap_fast_a_id_len);
 +                      sm->eap_fast_a_id_len = conf->eap_fast_a_id_len;
 +              }
 +      }
 +      if (conf->eap_fast_a_id_info)
 +              sm->eap_fast_a_id_info = os_strdup(conf->eap_fast_a_id_info);
 +      sm->eap_fast_prov = conf->eap_fast_prov;
 +      sm->pac_key_lifetime = conf->pac_key_lifetime;
 +      sm->pac_key_refresh_time = conf->pac_key_refresh_time;
 +      sm->eap_sim_aka_result_ind = conf->eap_sim_aka_result_ind;
 +      sm->tnc = conf->tnc;
 +      sm->wps = conf->wps;
 +      if (conf->assoc_wps_ie)
 +              sm->assoc_wps_ie = wpabuf_dup(conf->assoc_wps_ie);
 +      if (conf->assoc_p2p_ie)
 +              sm->assoc_p2p_ie = wpabuf_dup(conf->assoc_p2p_ie);
 +      if (conf->peer_addr)
 +              os_memcpy(sm->peer_addr, conf->peer_addr, ETH_ALEN);
 +      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;
++      sm->tls_session_lifetime = conf->tls_session_lifetime;
 +
 +#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");
 +
 +      return sm;
 +}
 +
 +
 +/**
 + * eap_server_sm_deinit - Deinitialize and free an EAP server state machine
 + * @sm: Pointer to EAP state machine allocated with eap_server_sm_init()
 + *
 + * This function deinitializes EAP state machine and frees all allocated
 + * resources.
 + */
 +void eap_server_sm_deinit(struct eap_sm *sm)
 +{
 +      if (sm == NULL)
 +              return;
 +      wpa_printf(MSG_DEBUG, "EAP: Server state machine removed");
 +      if (sm->m && sm->eap_method_priv)
 +              sm->m->reset(sm, sm->eap_method_priv);
 +      wpabuf_free(sm->eap_if.eapReqData);
 +      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);
 +      os_free(sm->pac_opaque_encr_key);
 +      os_free(sm->eap_fast_a_id);
 +      os_free(sm->eap_fast_a_id_info);
 +      wpabuf_free(sm->eap_if.aaaEapReqData);
 +      wpabuf_free(sm->eap_if.aaaEapRespData);
 +      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);
 +      os_free(sm);
 +}
 +
 +
 +/**
 + * eap_sm_notify_cached - Notify EAP state machine of cached PMK
 + * @sm: Pointer to EAP state machine allocated with eap_server_sm_init()
 + *
 + * This function is called when PMKSA caching is used to skip EAP
 + * authentication.
 + */
 +void eap_sm_notify_cached(struct eap_sm *sm)
 +{
 +      if (sm == NULL)
 +              return;
 +
 +      sm->EAP_state = EAP_SUCCESS;
 +}
 +
 +
 +/**
 + * eap_sm_pending_cb - EAP state machine callback for a pending EAP request
 + * @sm: Pointer to EAP state machine allocated with eap_server_sm_init()
 + *
 + * This function is called when data for a pending EAP-Request is received.
 + */
 +void eap_sm_pending_cb(struct eap_sm *sm)
 +{
 +      if (sm == NULL)
 +              return;
 +      wpa_printf(MSG_DEBUG, "EAP: Callback for pending request received");
 +      if (sm->method_pending == METHOD_PENDING_WAIT)
 +              sm->method_pending = METHOD_PENDING_CONT;
 +}
 +
 +
 +/**
 + * eap_sm_method_pending - Query whether EAP method is waiting for pending data
 + * @sm: Pointer to EAP state machine allocated with eap_server_sm_init()
 + * Returns: 1 if method is waiting for pending data or 0 if not
 + */
 +int eap_sm_method_pending(struct eap_sm *sm)
 +{
 +      if (sm == NULL)
 +              return 0;
 +      return sm->method_pending == METHOD_PENDING_WAIT;
 +}
 +
 +
 +/**
 + * eap_get_identity - Get the user identity (from EAP-Response/Identity)
 + * @sm: Pointer to EAP state machine allocated with eap_server_sm_init()
 + * @len: Buffer for returning identity length
 + * Returns: Pointer to the user identity or %NULL if not available
 + */
 +const u8 * eap_get_identity(struct eap_sm *sm, size_t *len)
 +{
 +      *len = sm->identity_len;
 +      return sm->identity;
 +}
 +
 +
 +/**
 + * eap_get_interface - Get pointer to EAP-EAPOL interface data
 + * @sm: Pointer to EAP state machine allocated with eap_server_sm_init()
 + * Returns: Pointer to the EAP-EAPOL interface data
 + */
 +struct eap_eapol_interface * eap_get_interface(struct eap_sm *sm)
 +{
 +      return &sm->eap_if;
 +}
 +
 +
 +/**
 + * eap_server_clear_identity - Clear EAP identity information
 + * @sm: Pointer to EAP state machine allocated with eap_server_sm_init()
 + *
 + * This function can be used to clear the EAP identity information in the EAP
 + * server context. This allows the EAP/Identity method to be used again after
 + * EAPOL-Start or EAPOL-Logoff.
 + */
 +void eap_server_clear_identity(struct eap_sm *sm)
 +{
 +      os_free(sm->identity);
 +      sm->identity = NULL;
 +}
++
++
++#ifdef CONFIG_TESTING_OPTIONS
++void eap_server_mschap_rx_callback(struct eap_sm *sm, const char *source,
++                                 const u8 *username, size_t username_len,
++                                 const u8 *challenge, const u8 *response)
++{
++      char hex_challenge[30], hex_response[90], user[100];
++
++      /* Print out Challenge and Response in format supported by asleap. */
++      if (username)
++              printf_encode(user, sizeof(user), username, username_len);
++      else
++              user[0] = '\0';
++      wpa_snprintf_hex_sep(hex_challenge, sizeof(hex_challenge),
++                           challenge, sizeof(challenge), ':');
++      wpa_snprintf_hex_sep(hex_response, sizeof(hex_response), response, 24,
++                           ':');
++      wpa_printf(MSG_DEBUG, "[%s/user=%s] asleap -C %s -R %s",
++                 source, user, hex_challenge, hex_response);
++}
++#endif /* CONFIG_TESTING_OPTIONS */
index 966f511ddddca280be2ef2aa0200ec6789ed1ff2,0000000000000000000000000000000000000000..ba82be9c3f3adcbb2cc97727e08db0592d177f98
mode 100644,000000..100644
--- /dev/null
@@@ -1,793 -1,0 +1,817 @@@
 +/*
 + * 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;
 +}
 +
 +
++static u8 * eap_eke_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
++{
++      struct eap_eke_data *data = priv;
++      u8 *sid;
++      size_t sid_len;
++
++      if (data->state != SUCCESS)
++              return NULL;
++
++      sid_len = 1 + 2 * data->sess.nonce_len;
++      sid = os_malloc(sid_len);
++      if (sid == NULL)
++              return NULL;
++      sid[0] = EAP_TYPE_EKE;
++      os_memcpy(sid + 1, data->nonce_p, data->sess.nonce_len);
++      os_memcpy(sid + 1 + data->sess.nonce_len, data->nonce_s,
++                data->sess.nonce_len);
++      *len = sid_len;
++
++      return sid;
++}
++
++
 +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;
++      eap->getSessionId = eap_eke_get_session_id;
 +
 +      ret = eap_server_method_register(eap);
 +      if (ret)
 +              eap_server_method_free(eap);
 +      return ret;
 +}
index 6745100d338c0543883c30ad2044d42c07f26a2c,0000000000000000000000000000000000000000..bd9018e78b56b062ad6114f80da75d43f6984d01
mode 100644,000000..100644
--- /dev/null
@@@ -1,1632 -1,0 +1,1632 @@@
-       if (eap_server_tls_ssl_init(sm, &data->ssl, 0)) {
 +/*
 + * EAP-FAST server (RFC 4851)
 + * Copyright (c) 2004-2008, 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_wrap.h"
 +#include "crypto/sha1.h"
 +#include "crypto/tls.h"
 +#include "crypto/random.h"
 +#include "eap_common/eap_tlv_common.h"
 +#include "eap_common/eap_fast_common.h"
 +#include "eap_i.h"
 +#include "eap_tls_common.h"
 +
 +
 +static void eap_fast_reset(struct eap_sm *sm, void *priv);
 +
 +
 +/* Private PAC-Opaque TLV types */
 +#define PAC_OPAQUE_TYPE_PAD 0
 +#define PAC_OPAQUE_TYPE_KEY 1
 +#define PAC_OPAQUE_TYPE_LIFETIME 2
 +#define PAC_OPAQUE_TYPE_IDENTITY 3
 +
 +struct eap_fast_data {
 +      struct eap_ssl_data ssl;
 +      enum {
 +              START, PHASE1, PHASE2_START, PHASE2_ID, PHASE2_METHOD,
 +              CRYPTO_BINDING, REQUEST_PAC, SUCCESS, FAILURE
 +      } state;
 +
 +      int fast_version;
 +      const struct eap_method *phase2_method;
 +      void *phase2_priv;
 +      int force_version;
 +      int peer_version;
 +
 +      u8 crypto_binding_nonce[32];
 +      int final_result;
 +
 +      struct eap_fast_key_block_provisioning *key_block_p;
 +
 +      u8 simck[EAP_FAST_SIMCK_LEN];
 +      u8 cmk[EAP_FAST_CMK_LEN];
 +      int simck_idx;
 +
 +      u8 pac_opaque_encr[16];
 +      u8 *srv_id;
 +      size_t srv_id_len;
 +      char *srv_id_info;
 +
 +      int anon_provisioning;
 +      int send_new_pac; /* server triggered re-keying of Tunnel PAC */
 +      struct wpabuf *pending_phase2_resp;
 +      u8 *identity; /* from PAC-Opaque */
 +      size_t identity_len;
 +      int eap_seq;
 +      int tnc_started;
 +
 +      int pac_key_lifetime;
 +      int pac_key_refresh_time;
 +};
 +
 +
 +static int eap_fast_process_phase2_start(struct eap_sm *sm,
 +                                       struct eap_fast_data *data);
 +
 +
 +static const char * eap_fast_state_txt(int state)
 +{
 +      switch (state) {
 +      case START:
 +              return "START";
 +      case PHASE1:
 +              return "PHASE1";
 +      case PHASE2_START:
 +              return "PHASE2_START";
 +      case PHASE2_ID:
 +              return "PHASE2_ID";
 +      case PHASE2_METHOD:
 +              return "PHASE2_METHOD";
 +      case CRYPTO_BINDING:
 +              return "CRYPTO_BINDING";
 +      case REQUEST_PAC:
 +              return "REQUEST_PAC";
 +      case SUCCESS:
 +              return "SUCCESS";
 +      case FAILURE:
 +              return "FAILURE";
 +      default:
 +              return "Unknown?!";
 +      }
 +}
 +
 +
 +static void eap_fast_state(struct eap_fast_data *data, int state)
 +{
 +      wpa_printf(MSG_DEBUG, "EAP-FAST: %s -> %s",
 +                 eap_fast_state_txt(data->state),
 +                 eap_fast_state_txt(state));
 +      data->state = state;
 +}
 +
 +
 +static EapType eap_fast_req_failure(struct eap_sm *sm,
 +                                  struct eap_fast_data *data)
 +{
 +      /* TODO: send Result TLV(FAILURE) */
 +      eap_fast_state(data, FAILURE);
 +      return EAP_TYPE_NONE;
 +}
 +
 +
 +static int eap_fast_session_ticket_cb(void *ctx, const u8 *ticket, size_t len,
 +                                    const u8 *client_random,
 +                                    const u8 *server_random,
 +                                    u8 *master_secret)
 +{
 +      struct eap_fast_data *data = ctx;
 +      const u8 *pac_opaque;
 +      size_t pac_opaque_len;
 +      u8 *buf, *pos, *end, *pac_key = NULL;
 +      os_time_t lifetime = 0;
 +      struct os_time now;
 +      u8 *identity = NULL;
 +      size_t identity_len = 0;
 +
 +      wpa_printf(MSG_DEBUG, "EAP-FAST: SessionTicket callback");
 +      wpa_hexdump(MSG_DEBUG, "EAP-FAST: SessionTicket (PAC-Opaque)",
 +                  ticket, len);
 +
 +      if (len < 4 || WPA_GET_BE16(ticket) != PAC_TYPE_PAC_OPAQUE) {
 +              wpa_printf(MSG_DEBUG, "EAP-FAST: Ignore invalid "
 +                         "SessionTicket");
 +              return 0;
 +      }
 +
 +      pac_opaque_len = WPA_GET_BE16(ticket + 2);
 +      pac_opaque = ticket + 4;
 +      if (pac_opaque_len < 8 || pac_opaque_len % 8 ||
 +          pac_opaque_len > len - 4) {
 +              wpa_printf(MSG_DEBUG, "EAP-FAST: Ignore invalid PAC-Opaque "
 +                         "(len=%lu left=%lu)",
 +                         (unsigned long) pac_opaque_len,
 +                         (unsigned long) len);
 +              return 0;
 +      }
 +      wpa_hexdump(MSG_DEBUG, "EAP-FAST: Received PAC-Opaque",
 +                  pac_opaque, pac_opaque_len);
 +
 +      buf = os_malloc(pac_opaque_len - 8);
 +      if (buf == NULL) {
 +              wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to allocate memory "
 +                         "for decrypting PAC-Opaque");
 +              return 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);
 +              /*
 +               * This may have been caused by server changing the PAC-Opaque
 +               * encryption key, so just ignore this PAC-Opaque instead of
 +               * failing the authentication completely. Provisioning can now
 +               * be used to provision a new PAC.
 +               */
 +              return 0;
 +      }
 +
 +      end = buf + pac_opaque_len - 8;
 +      wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: Decrypted PAC-Opaque",
 +                      buf, end - buf);
 +
 +      pos = buf;
 +      while (pos + 1 < end) {
 +              if (pos + 2 + pos[1] > end)
 +                      break;
 +
 +              switch (*pos) {
 +              case PAC_OPAQUE_TYPE_PAD:
 +                      goto done;
 +              case PAC_OPAQUE_TYPE_KEY:
 +                      if (pos[1] != EAP_FAST_PAC_KEY_LEN) {
 +                              wpa_printf(MSG_DEBUG, "EAP-FAST: Invalid "
 +                                         "PAC-Key length %d", pos[1]);
 +                              os_free(buf);
 +                              return -1;
 +                      }
 +                      pac_key = pos + 2;
 +                      wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: PAC-Key from "
 +                                      "decrypted PAC-Opaque",
 +                                      pac_key, EAP_FAST_PAC_KEY_LEN);
 +                      break;
 +              case PAC_OPAQUE_TYPE_LIFETIME:
 +                      if (pos[1] != 4) {
 +                              wpa_printf(MSG_DEBUG, "EAP-FAST: Invalid "
 +                                         "PAC-Key lifetime length %d",
 +                                         pos[1]);
 +                              os_free(buf);
 +                              return -1;
 +                      }
 +                      lifetime = WPA_GET_BE32(pos + 2);
 +                      break;
 +              case PAC_OPAQUE_TYPE_IDENTITY:
 +                      identity = pos + 2;
 +                      identity_len = pos[1];
 +                      break;
 +              }
 +
 +              pos += 2 + pos[1];
 +      }
 +done:
 +
 +      if (pac_key == NULL) {
 +              wpa_printf(MSG_DEBUG, "EAP-FAST: No PAC-Key included in "
 +                         "PAC-Opaque");
 +              os_free(buf);
 +              return -1;
 +      }
 +
 +      if (identity) {
 +              wpa_hexdump_ascii(MSG_DEBUG, "EAP-FAST: Identity from "
 +                                "PAC-Opaque", identity, identity_len);
 +              os_free(data->identity);
 +              data->identity = os_malloc(identity_len);
 +              if (data->identity) {
 +                      os_memcpy(data->identity, identity, identity_len);
 +                      data->identity_len = identity_len;
 +              }
 +      }
 +
 +      if (os_get_time(&now) < 0 || lifetime <= 0 || now.sec > lifetime) {
 +              wpa_printf(MSG_DEBUG, "EAP-FAST: PAC-Key not valid anymore "
 +                         "(lifetime=%ld now=%ld)", lifetime, now.sec);
 +              data->send_new_pac = 2;
 +              /*
 +               * Allow PAC to be used to allow a PAC update with some level
 +               * of server authentication (i.e., do not fall back to full TLS
 +               * handshake since we cannot be sure that the peer would be
 +               * able to validate server certificate now). However, reject
 +               * the authentication since the PAC was not valid anymore. Peer
 +               * can connect again with the newly provisioned PAC after this.
 +               */
 +      } else if (lifetime - now.sec < data->pac_key_refresh_time) {
 +              wpa_printf(MSG_DEBUG, "EAP-FAST: PAC-Key soft timeout; send "
 +                         "an update if authentication succeeds");
 +              data->send_new_pac = 1;
 +      }
 +
 +      eap_fast_derive_master_secret(pac_key, server_random, client_random,
 +                                    master_secret);
 +
 +      os_free(buf);
 +
 +      return 1;
 +}
 +
 +
 +static void eap_fast_derive_key_auth(struct eap_sm *sm,
 +                                   struct eap_fast_data *data)
 +{
 +      u8 *sks;
 +
 +      /* RFC 4851, Section 5.1:
 +       * Extra key material after TLS key_block: session_key_seed[40]
 +       */
 +
 +      sks = eap_fast_derive_key(sm->ssl_ctx, data->ssl.conn, "key expansion",
 +                                EAP_FAST_SKS_LEN);
 +      if (sks == NULL) {
 +              wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to derive "
 +                         "session_key_seed");
 +              return;
 +      }
 +
 +      /*
 +       * RFC 4851, Section 5.2:
 +       * S-IMCK[0] = session_key_seed
 +       */
 +      wpa_hexdump_key(MSG_DEBUG,
 +                      "EAP-FAST: session_key_seed (SKS = S-IMCK[0])",
 +                      sks, EAP_FAST_SKS_LEN);
 +      data->simck_idx = 0;
 +      os_memcpy(data->simck, sks, EAP_FAST_SIMCK_LEN);
 +      os_free(sks);
 +}
 +
 +
 +static void eap_fast_derive_key_provisioning(struct eap_sm *sm,
 +                                           struct eap_fast_data *data)
 +{
 +      os_free(data->key_block_p);
 +      data->key_block_p = (struct eap_fast_key_block_provisioning *)
 +              eap_fast_derive_key(sm->ssl_ctx, data->ssl.conn,
 +                                  "key expansion",
 +                                  sizeof(*data->key_block_p));
 +      if (data->key_block_p == NULL) {
 +              wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to derive key block");
 +              return;
 +      }
 +      /*
 +       * RFC 4851, Section 5.2:
 +       * S-IMCK[0] = session_key_seed
 +       */
 +      wpa_hexdump_key(MSG_DEBUG,
 +                      "EAP-FAST: session_key_seed (SKS = S-IMCK[0])",
 +                      data->key_block_p->session_key_seed,
 +                      sizeof(data->key_block_p->session_key_seed));
 +      data->simck_idx = 0;
 +      os_memcpy(data->simck, data->key_block_p->session_key_seed,
 +                EAP_FAST_SIMCK_LEN);
 +      wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: server_challenge",
 +                      data->key_block_p->server_challenge,
 +                      sizeof(data->key_block_p->server_challenge));
 +      wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: client_challenge",
 +                      data->key_block_p->client_challenge,
 +                      sizeof(data->key_block_p->client_challenge));
 +}
 +
 +
 +static int eap_fast_get_phase2_key(struct eap_sm *sm,
 +                                 struct eap_fast_data *data,
 +                                 u8 *isk, size_t isk_len)
 +{
 +      u8 *key;
 +      size_t key_len;
 +
 +      os_memset(isk, 0, isk_len);
 +
 +      if (data->phase2_method == NULL || data->phase2_priv == NULL) {
 +              wpa_printf(MSG_DEBUG, "EAP-FAST: Phase 2 method not "
 +                         "available");
 +              return -1;
 +      }
 +
 +      if (data->phase2_method->getKey == NULL)
 +              return 0;
 +
 +      if ((key = data->phase2_method->getKey(sm, data->phase2_priv,
 +                                             &key_len)) == NULL) {
 +              wpa_printf(MSG_DEBUG, "EAP-FAST: Could not get key material "
 +                         "from Phase 2");
 +              return -1;
 +      }
 +
 +      if (key_len > isk_len)
 +              key_len = isk_len;
 +      if (key_len == 32 &&
 +          data->phase2_method->vendor == EAP_VENDOR_IETF &&
 +          data->phase2_method->method == EAP_TYPE_MSCHAPV2) {
 +              /*
 +               * EAP-FAST uses reverse order for MS-MPPE keys when deriving
 +               * MSK from EAP-MSCHAPv2. Swap the keys here to get the correct
 +               * ISK for EAP-FAST cryptobinding.
 +               */
 +              os_memcpy(isk, key + 16, 16);
 +              os_memcpy(isk + 16, key, 16);
 +      } else
 +              os_memcpy(isk, key, key_len);
 +      os_free(key);
 +
 +      return 0;
 +}
 +
 +
 +static int eap_fast_update_icmk(struct eap_sm *sm, struct eap_fast_data *data)
 +{
 +      u8 isk[32], imck[60];
 +
 +      wpa_printf(MSG_DEBUG, "EAP-FAST: Deriving ICMK[%d] (S-IMCK and CMK)",
 +                 data->simck_idx + 1);
 +
 +      /*
 +       * RFC 4851, Section 5.2:
 +       * IMCK[j] = T-PRF(S-IMCK[j-1], "Inner Methods Compound Keys",
 +       *                 MSK[j], 60)
 +       * S-IMCK[j] = first 40 octets of IMCK[j]
 +       * CMK[j] = last 20 octets of IMCK[j]
 +       */
 +
 +      if (eap_fast_get_phase2_key(sm, data, isk, sizeof(isk)) < 0)
 +              return -1;
 +      wpa_hexdump_key(MSG_MSGDUMP, "EAP-FAST: ISK[j]", isk, sizeof(isk));
 +      sha1_t_prf(data->simck, EAP_FAST_SIMCK_LEN,
 +                 "Inner Methods Compound Keys",
 +                 isk, sizeof(isk), imck, sizeof(imck));
 +      data->simck_idx++;
 +      os_memcpy(data->simck, imck, EAP_FAST_SIMCK_LEN);
 +      wpa_hexdump_key(MSG_MSGDUMP, "EAP-FAST: S-IMCK[j]",
 +                      data->simck, EAP_FAST_SIMCK_LEN);
 +      os_memcpy(data->cmk, imck + EAP_FAST_SIMCK_LEN, EAP_FAST_CMK_LEN);
 +      wpa_hexdump_key(MSG_MSGDUMP, "EAP-FAST: CMK[j]",
 +                      data->cmk, EAP_FAST_CMK_LEN);
 +
 +      return 0;
 +}
 +
 +
 +static void * eap_fast_init(struct eap_sm *sm)
 +{
 +      struct eap_fast_data *data;
 +      u8 ciphers[5] = {
 +              TLS_CIPHER_ANON_DH_AES128_SHA,
 +              TLS_CIPHER_AES128_SHA,
 +              TLS_CIPHER_RSA_DHE_AES128_SHA,
 +              TLS_CIPHER_RC4_SHA,
 +              TLS_CIPHER_NONE
 +      };
 +
 +      data = os_zalloc(sizeof(*data));
 +      if (data == NULL)
 +              return NULL;
 +      data->fast_version = EAP_FAST_VERSION;
 +      data->force_version = -1;
 +      if (sm->user && sm->user->force_version >= 0) {
 +              data->force_version = sm->user->force_version;
 +              wpa_printf(MSG_DEBUG, "EAP-FAST: forcing version %d",
 +                         data->force_version);
 +              data->fast_version = data->force_version;
 +      }
 +      data->state = START;
 +
++      if (eap_server_tls_ssl_init(sm, &data->ssl, 0, EAP_TYPE_FAST)) {
 +              wpa_printf(MSG_INFO, "EAP-FAST: Failed to initialize SSL.");
 +              eap_fast_reset(sm, data);
 +              return NULL;
 +      }
 +
 +      if (tls_connection_set_cipher_list(sm->ssl_ctx, data->ssl.conn,
 +                                         ciphers) < 0) {
 +              wpa_printf(MSG_INFO, "EAP-FAST: Failed to set TLS cipher "
 +                         "suites");
 +              eap_fast_reset(sm, data);
 +              return NULL;
 +      }
 +
 +      if (tls_connection_set_session_ticket_cb(sm->ssl_ctx, data->ssl.conn,
 +                                               eap_fast_session_ticket_cb,
 +                                               data) < 0) {
 +              wpa_printf(MSG_INFO, "EAP-FAST: Failed to set SessionTicket "
 +                         "callback");
 +              eap_fast_reset(sm, data);
 +              return NULL;
 +      }
 +
 +      if (sm->pac_opaque_encr_key == NULL) {
 +              wpa_printf(MSG_INFO, "EAP-FAST: No PAC-Opaque encryption key "
 +                         "configured");
 +              eap_fast_reset(sm, data);
 +              return NULL;
 +      }
 +      os_memcpy(data->pac_opaque_encr, sm->pac_opaque_encr_key,
 +                sizeof(data->pac_opaque_encr));
 +
 +      if (sm->eap_fast_a_id == NULL) {
 +              wpa_printf(MSG_INFO, "EAP-FAST: No A-ID configured");
 +              eap_fast_reset(sm, data);
 +              return NULL;
 +      }
 +      data->srv_id = os_malloc(sm->eap_fast_a_id_len);
 +      if (data->srv_id == NULL) {
 +              eap_fast_reset(sm, data);
 +              return NULL;
 +      }
 +      os_memcpy(data->srv_id, sm->eap_fast_a_id, sm->eap_fast_a_id_len);
 +      data->srv_id_len = sm->eap_fast_a_id_len;
 +
 +      if (sm->eap_fast_a_id_info == NULL) {
 +              wpa_printf(MSG_INFO, "EAP-FAST: No A-ID-Info configured");
 +              eap_fast_reset(sm, data);
 +              return NULL;
 +      }
 +      data->srv_id_info = os_strdup(sm->eap_fast_a_id_info);
 +      if (data->srv_id_info == NULL) {
 +              eap_fast_reset(sm, data);
 +              return NULL;
 +      }
 +
 +      /* PAC-Key lifetime in seconds (hard limit) */
 +      data->pac_key_lifetime = sm->pac_key_lifetime;
 +
 +      /*
 +       * PAC-Key refresh time in seconds (soft limit on remaining hard
 +       * limit). The server will generate a new PAC-Key when this number of
 +       * seconds (or fewer) of the lifetime remains.
 +       */
 +      data->pac_key_refresh_time = sm->pac_key_refresh_time;
 +
 +      return data;
 +}
 +
 +
 +static void eap_fast_reset(struct eap_sm *sm, void *priv)
 +{
 +      struct eap_fast_data *data = priv;
 +      if (data == NULL)
 +              return;
 +      if (data->phase2_priv && data->phase2_method)
 +              data->phase2_method->reset(sm, data->phase2_priv);
 +      eap_server_tls_ssl_deinit(sm, &data->ssl);
 +      os_free(data->srv_id);
 +      os_free(data->srv_id_info);
 +      os_free(data->key_block_p);
 +      wpabuf_free(data->pending_phase2_resp);
 +      os_free(data->identity);
 +      bin_clear_free(data, sizeof(*data));
 +}
 +
 +
 +static struct wpabuf * eap_fast_build_start(struct eap_sm *sm,
 +                                          struct eap_fast_data *data, u8 id)
 +{
 +      struct wpabuf *req;
 +
 +      req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_FAST,
 +                          1 + sizeof(struct pac_tlv_hdr) + data->srv_id_len,
 +                          EAP_CODE_REQUEST, id);
 +      if (req == NULL) {
 +              wpa_printf(MSG_ERROR, "EAP-FAST: Failed to allocate memory for"
 +                         " request");
 +              eap_fast_state(data, FAILURE);
 +              return NULL;
 +      }
 +
 +      wpabuf_put_u8(req, EAP_TLS_FLAGS_START | data->fast_version);
 +
 +      /* RFC 4851, 4.1.1. Authority ID Data */
 +      eap_fast_put_tlv(req, PAC_TYPE_A_ID, data->srv_id, data->srv_id_len);
 +
 +      eap_fast_state(data, PHASE1);
 +
 +      return req;
 +}
 +
 +
 +static int eap_fast_phase1_done(struct eap_sm *sm, struct eap_fast_data *data)
 +{
 +      char cipher[64];
 +
 +      wpa_printf(MSG_DEBUG, "EAP-FAST: Phase1 done, starting Phase2");
 +
 +      if (tls_get_cipher(sm->ssl_ctx, data->ssl.conn, cipher, sizeof(cipher))
 +          < 0) {
 +              wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to get cipher "
 +                         "information");
 +              eap_fast_state(data, FAILURE);
 +              return -1;
 +      }
 +      data->anon_provisioning = os_strstr(cipher, "ADH") != NULL;
 +                  
 +      if (data->anon_provisioning) {
 +              wpa_printf(MSG_DEBUG, "EAP-FAST: Anonymous provisioning");
 +              eap_fast_derive_key_provisioning(sm, data);
 +      } else
 +              eap_fast_derive_key_auth(sm, data);
 +
 +      eap_fast_state(data, PHASE2_START);
 +
 +      return 0;
 +}
 +
 +
 +static struct wpabuf * eap_fast_build_phase2_req(struct eap_sm *sm,
 +                                               struct eap_fast_data *data,
 +                                               u8 id)
 +{
 +      struct wpabuf *req;
 +
 +      if (data->phase2_priv == NULL) {
 +              wpa_printf(MSG_DEBUG, "EAP-FAST: Phase 2 method not "
 +                         "initialized");
 +              return NULL;
 +      }
 +      req = data->phase2_method->buildReq(sm, data->phase2_priv, id);
 +      if (req == NULL)
 +              return NULL;
 +
 +      wpa_hexdump_buf_key(MSG_MSGDUMP, "EAP-FAST: Phase 2 EAP-Request", req);
 +      return eap_fast_tlv_eap_payload(req);
 +}
 +
 +
 +static struct wpabuf * eap_fast_build_crypto_binding(
 +      struct eap_sm *sm, struct eap_fast_data *data)
 +{
 +      struct wpabuf *buf;
 +      struct eap_tlv_result_tlv *result;
 +      struct eap_tlv_crypto_binding_tlv *binding;
 +
 +      buf = wpabuf_alloc(2 * sizeof(*result) + sizeof(*binding));
 +      if (buf == NULL)
 +              return NULL;
 +
 +      if (data->send_new_pac || data->anon_provisioning ||
 +          data->phase2_method)
 +              data->final_result = 0;
 +      else
 +              data->final_result = 1;
 +
 +      if (!data->final_result || data->eap_seq > 1) {
 +              /* Intermediate-Result */
 +              wpa_printf(MSG_DEBUG, "EAP-FAST: Add Intermediate-Result TLV "
 +                         "(status=SUCCESS)");
 +              result = wpabuf_put(buf, sizeof(*result));
 +              result->tlv_type = host_to_be16(
 +                      EAP_TLV_TYPE_MANDATORY |
 +                      EAP_TLV_INTERMEDIATE_RESULT_TLV);
 +              result->length = host_to_be16(2);
 +              result->status = host_to_be16(EAP_TLV_RESULT_SUCCESS);
 +      }
 +
 +      if (data->final_result) {
 +              /* Result TLV */
 +              wpa_printf(MSG_DEBUG, "EAP-FAST: Add Result TLV "
 +                         "(status=SUCCESS)");
 +              result = wpabuf_put(buf, sizeof(*result));
 +              result->tlv_type = host_to_be16(EAP_TLV_TYPE_MANDATORY |
 +                                              EAP_TLV_RESULT_TLV);
 +              result->length = host_to_be16(2);
 +              result->status = host_to_be16(EAP_TLV_RESULT_SUCCESS);
 +      }
 +
 +      /* Crypto-Binding TLV */
 +      binding = wpabuf_put(buf, sizeof(*binding));
 +      binding->tlv_type = host_to_be16(EAP_TLV_TYPE_MANDATORY |
 +                                       EAP_TLV_CRYPTO_BINDING_TLV);
 +      binding->length = host_to_be16(sizeof(*binding) -
 +                                     sizeof(struct eap_tlv_hdr));
 +      binding->version = EAP_FAST_VERSION;
 +      binding->received_version = data->peer_version;
 +      binding->subtype = EAP_TLV_CRYPTO_BINDING_SUBTYPE_REQUEST;
 +      if (random_get_bytes(binding->nonce, sizeof(binding->nonce)) < 0) {
 +              wpabuf_free(buf);
 +              return NULL;
 +      }
 +
 +      /*
 +       * RFC 4851, Section 4.2.8:
 +       * The nonce in a request MUST have its least significant bit set to 0.
 +       */
 +      binding->nonce[sizeof(binding->nonce) - 1] &= ~0x01;
 +
 +      os_memcpy(data->crypto_binding_nonce, binding->nonce,
 +                sizeof(binding->nonce));
 +
 +      /*
 +       * RFC 4851, Section 5.3:
 +       * CMK = CMK[j]
 +       * Compound-MAC = HMAC-SHA1( CMK, Crypto-Binding TLV )
 +       */
 +
 +      hmac_sha1(data->cmk, EAP_FAST_CMK_LEN,
 +                (u8 *) binding, sizeof(*binding),
 +                binding->compound_mac);
 +
 +      wpa_printf(MSG_DEBUG, "EAP-FAST: Add Crypto-Binding TLV: Version %d "
 +                 "Received Version %d SubType %d",
 +                 binding->version, binding->received_version,
 +                 binding->subtype);
 +      wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: NONCE",
 +                  binding->nonce, sizeof(binding->nonce));
 +      wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Compound MAC",
 +                  binding->compound_mac, sizeof(binding->compound_mac));
 +
 +      return buf;
 +}
 +
 +
 +static struct wpabuf * eap_fast_build_pac(struct eap_sm *sm,
 +                                        struct eap_fast_data *data)
 +{
 +      u8 pac_key[EAP_FAST_PAC_KEY_LEN];
 +      u8 *pac_buf, *pac_opaque;
 +      struct wpabuf *buf;
 +      u8 *pos;
 +      size_t buf_len, srv_id_info_len, pac_len;
 +      struct eap_tlv_hdr *pac_tlv;
 +      struct pac_tlv_hdr *pac_info;
 +      struct eap_tlv_result_tlv *result;
 +      struct os_time now;
 +
 +      if (random_get_bytes(pac_key, EAP_FAST_PAC_KEY_LEN) < 0 ||
 +          os_get_time(&now) < 0)
 +              return NULL;
 +      wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: Generated PAC-Key",
 +                      pac_key, EAP_FAST_PAC_KEY_LEN);
 +
 +      pac_len = (2 + EAP_FAST_PAC_KEY_LEN) + (2 + 4) +
 +              (2 + sm->identity_len) + 8;
 +      pac_buf = os_malloc(pac_len);
 +      if (pac_buf == NULL)
 +              return NULL;
 +
 +      srv_id_info_len = os_strlen(data->srv_id_info);
 +
 +      pos = pac_buf;
 +      *pos++ = PAC_OPAQUE_TYPE_KEY;
 +      *pos++ = EAP_FAST_PAC_KEY_LEN;
 +      os_memcpy(pos, pac_key, EAP_FAST_PAC_KEY_LEN);
 +      pos += EAP_FAST_PAC_KEY_LEN;
 +
 +      *pos++ = PAC_OPAQUE_TYPE_LIFETIME;
 +      *pos++ = 4;
 +      WPA_PUT_BE32(pos, now.sec + data->pac_key_lifetime);
 +      pos += 4;
 +
 +      if (sm->identity) {
 +              *pos++ = PAC_OPAQUE_TYPE_IDENTITY;
 +              *pos++ = sm->identity_len;
 +              os_memcpy(pos, sm->identity, sm->identity_len);
 +              pos += sm->identity_len;
 +      }
 +
 +      pac_len = pos - pac_buf;
 +      while (pac_len % 8) {
 +              *pos++ = PAC_OPAQUE_TYPE_PAD;
 +              pac_len++;
 +      }
 +
 +      pac_opaque = os_malloc(pac_len + 8);
 +      if (pac_opaque == NULL) {
 +              os_free(pac_buf);
 +              return NULL;
 +      }
 +      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;
 +      }
 +      os_free(pac_buf);
 +
 +      pac_len += 8;
 +      wpa_hexdump(MSG_DEBUG, "EAP-FAST: PAC-Opaque",
 +                  pac_opaque, pac_len);
 +
 +      buf_len = sizeof(*pac_tlv) +
 +              sizeof(struct pac_tlv_hdr) + EAP_FAST_PAC_KEY_LEN +
 +              sizeof(struct pac_tlv_hdr) + pac_len +
 +              data->srv_id_len + srv_id_info_len + 100 + sizeof(*result);
 +      buf = wpabuf_alloc(buf_len);
 +      if (buf == NULL) {
 +              os_free(pac_opaque);
 +              return NULL;
 +      }
 +
 +      /* Result TLV */
 +      wpa_printf(MSG_DEBUG, "EAP-FAST: Add Result TLV (status=SUCCESS)");
 +      result = wpabuf_put(buf, sizeof(*result));
 +      WPA_PUT_BE16((u8 *) &result->tlv_type,
 +                   EAP_TLV_TYPE_MANDATORY | EAP_TLV_RESULT_TLV);
 +      WPA_PUT_BE16((u8 *) &result->length, 2);
 +      WPA_PUT_BE16((u8 *) &result->status, EAP_TLV_RESULT_SUCCESS);
 +
 +      /* PAC TLV */
 +      wpa_printf(MSG_DEBUG, "EAP-FAST: Add PAC TLV");
 +      pac_tlv = wpabuf_put(buf, sizeof(*pac_tlv));
 +      pac_tlv->tlv_type = host_to_be16(EAP_TLV_TYPE_MANDATORY |
 +                                       EAP_TLV_PAC_TLV);
 +
 +      /* PAC-Key */
 +      eap_fast_put_tlv(buf, PAC_TYPE_PAC_KEY, pac_key, EAP_FAST_PAC_KEY_LEN);
 +
 +      /* PAC-Opaque */
 +      eap_fast_put_tlv(buf, PAC_TYPE_PAC_OPAQUE, pac_opaque, pac_len);
 +      os_free(pac_opaque);
 +
 +      /* PAC-Info */
 +      pac_info = wpabuf_put(buf, sizeof(*pac_info));
 +      pac_info->type = host_to_be16(PAC_TYPE_PAC_INFO);
 +
 +      /* PAC-Lifetime (inside PAC-Info) */
 +      eap_fast_put_tlv_hdr(buf, PAC_TYPE_CRED_LIFETIME, 4);
 +      wpabuf_put_be32(buf, now.sec + data->pac_key_lifetime);
 +
 +      /* A-ID (inside PAC-Info) */
 +      eap_fast_put_tlv(buf, PAC_TYPE_A_ID, data->srv_id, data->srv_id_len);
 +      
 +      /* Note: headers may be misaligned after A-ID */
 +
 +      if (sm->identity) {
 +              eap_fast_put_tlv(buf, PAC_TYPE_I_ID, sm->identity,
 +                               sm->identity_len);
 +      }
 +
 +      /* A-ID-Info (inside PAC-Info) */
 +      eap_fast_put_tlv(buf, PAC_TYPE_A_ID_INFO, data->srv_id_info,
 +                       srv_id_info_len);
 +
 +      /* PAC-Type (inside PAC-Info) */
 +      eap_fast_put_tlv_hdr(buf, PAC_TYPE_PAC_TYPE, 2);
 +      wpabuf_put_be16(buf, PAC_TYPE_TUNNEL_PAC);
 +
 +      /* Update PAC-Info and PAC TLV Length fields */
 +      pos = wpabuf_put(buf, 0);
 +      pac_info->len = host_to_be16(pos - (u8 *) (pac_info + 1));
 +      pac_tlv->length = host_to_be16(pos - (u8 *) (pac_tlv + 1));
 +
 +      return buf;
 +}
 +
 +
 +static int eap_fast_encrypt_phase2(struct eap_sm *sm,
 +                                 struct eap_fast_data *data,
 +                                 struct wpabuf *plain, int piggyback)
 +{
 +      struct wpabuf *encr;
 +
 +      wpa_hexdump_buf_key(MSG_DEBUG, "EAP-FAST: Encrypting Phase 2 TLVs",
 +                          plain);
 +      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 "
 +                         "used=%d)",
 +                         (int) wpabuf_len(encr),
 +                         (int) wpabuf_len(data->ssl.tls_out),
 +                         (int) data->ssl.tls_out_pos);
 +              if (wpabuf_resize(&data->ssl.tls_out, wpabuf_len(encr)) < 0) {
 +                      wpa_printf(MSG_WARNING, "EAP-FAST: Failed to resize "
 +                                 "output buffer");
 +                      wpabuf_free(encr);
 +                      return -1;
 +              }
 +              wpabuf_put_buf(data->ssl.tls_out, encr);
 +              wpabuf_free(encr);
 +      } else {
 +              wpabuf_free(data->ssl.tls_out);
 +              data->ssl.tls_out_pos = 0;
 +              data->ssl.tls_out = encr;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +static struct wpabuf * eap_fast_buildReq(struct eap_sm *sm, void *priv, u8 id)
 +{
 +      struct eap_fast_data *data = priv;
 +      struct wpabuf *req = NULL;
 +      int piggyback = 0;
 +
 +      if (data->ssl.state == FRAG_ACK) {
 +              return eap_server_tls_build_ack(id, EAP_TYPE_FAST,
 +                                              data->fast_version);
 +      }
 +
 +      if (data->ssl.state == WAIT_FRAG_ACK) {
 +              return eap_server_tls_build_msg(&data->ssl, EAP_TYPE_FAST,
 +                                              data->fast_version, id);
 +      }
 +
 +      switch (data->state) {
 +      case START:
 +              return eap_fast_build_start(sm, data, id);
 +      case PHASE1:
 +              if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) {
 +                      if (eap_fast_phase1_done(sm, data) < 0)
 +                              return NULL;
 +                      if (data->state == PHASE2_START) {
 +                              /*
 +                               * Try to generate Phase 2 data to piggyback
 +                               * with the end of Phase 1 to avoid extra
 +                               * roundtrip.
 +                               */
 +                              wpa_printf(MSG_DEBUG, "EAP-FAST: Try to start "
 +                                         "Phase 2");
 +                              if (eap_fast_process_phase2_start(sm, data))
 +                                      break;
 +                              req = eap_fast_build_phase2_req(sm, data, id);
 +                              piggyback = 1;
 +                      }
 +              }
 +              break;
 +      case PHASE2_ID:
 +      case PHASE2_METHOD:
 +              req = eap_fast_build_phase2_req(sm, data, id);
 +              break;
 +      case CRYPTO_BINDING:
 +              req = eap_fast_build_crypto_binding(sm, data);
 +              if (data->phase2_method) {
 +                      /*
 +                       * Include the start of the next EAP method in the
 +                       * sequence in the same message with Crypto-Binding to
 +                       * save a round-trip.
 +                       */
 +                      struct wpabuf *eap;
 +                      eap = eap_fast_build_phase2_req(sm, data, id);
 +                      req = wpabuf_concat(req, eap);
 +                      eap_fast_state(data, PHASE2_METHOD);
 +              }
 +              break;
 +      case REQUEST_PAC:
 +              req = eap_fast_build_pac(sm, data);
 +              break;
 +      default:
 +              wpa_printf(MSG_DEBUG, "EAP-FAST: %s - unexpected state %d",
 +                         __func__, data->state);
 +              return NULL;
 +      }
 +
 +      if (req &&
 +          eap_fast_encrypt_phase2(sm, data, req, piggyback) < 0)
 +              return NULL;
 +
 +      return eap_server_tls_build_msg(&data->ssl, EAP_TYPE_FAST,
 +                                      data->fast_version, id);
 +}
 +
 +
 +static Boolean eap_fast_check(struct eap_sm *sm, void *priv,
 +                            struct wpabuf *respData)
 +{
 +      const u8 *pos;
 +      size_t len;
 +
 +      pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_FAST, respData, &len);
 +      if (pos == NULL || len < 1) {
 +              wpa_printf(MSG_INFO, "EAP-FAST: Invalid frame");
 +              return TRUE;
 +      }
 +
 +      return FALSE;
 +}
 +
 +
 +static int eap_fast_phase2_init(struct eap_sm *sm, struct eap_fast_data *data,
 +                              EapType eap_type)
 +{
 +      if (data->phase2_priv && data->phase2_method) {
 +              data->phase2_method->reset(sm, data->phase2_priv);
 +              data->phase2_method = NULL;
 +              data->phase2_priv = NULL;
 +      }
 +      data->phase2_method = eap_server_get_eap_method(EAP_VENDOR_IETF,
 +                                                      eap_type);
 +      if (!data->phase2_method)
 +              return -1;
 +
 +      if (data->key_block_p) {
 +              sm->auth_challenge = data->key_block_p->server_challenge;
 +              sm->peer_challenge = data->key_block_p->client_challenge;
 +      }
 +      sm->init_phase2 = 1;
 +      data->phase2_priv = data->phase2_method->init(sm);
 +      sm->init_phase2 = 0;
 +      sm->auth_challenge = NULL;
 +      sm->peer_challenge = NULL;
 +
 +      return data->phase2_priv == NULL ? -1 : 0;
 +}
 +
 +
 +static void eap_fast_process_phase2_response(struct eap_sm *sm,
 +                                           struct eap_fast_data *data,
 +                                           u8 *in_data, size_t in_len)
 +{
 +      u8 next_type = EAP_TYPE_NONE;
 +      struct eap_hdr *hdr;
 +      u8 *pos;
 +      size_t left;
 +      struct wpabuf buf;
 +      const struct eap_method *m = data->phase2_method;
 +      void *priv = data->phase2_priv;
 +
 +      if (priv == NULL) {
 +              wpa_printf(MSG_DEBUG, "EAP-FAST: %s - Phase2 not "
 +                         "initialized?!", __func__);
 +              return;
 +      }
 +
 +      hdr = (struct eap_hdr *) in_data;
 +      pos = (u8 *) (hdr + 1);
 +
 +      if (in_len > sizeof(*hdr) && *pos == EAP_TYPE_NAK) {
 +              left = in_len - sizeof(*hdr);
 +              wpa_hexdump(MSG_DEBUG, "EAP-FAST: Phase2 type Nak'ed; "
 +                          "allowed types", pos + 1, left - 1);
 +#ifdef EAP_SERVER_TNC
 +              if (m && m->vendor == EAP_VENDOR_IETF &&
 +                  m->method == EAP_TYPE_TNC) {
 +                      wpa_printf(MSG_DEBUG, "EAP-FAST: Peer Nak'ed required "
 +                                 "TNC negotiation");
 +                      next_type = eap_fast_req_failure(sm, data);
 +                      eap_fast_phase2_init(sm, data, next_type);
 +                      return;
 +              }
 +#endif /* EAP_SERVER_TNC */
 +              eap_sm_process_nak(sm, pos + 1, left - 1);
 +              if (sm->user && sm->user_eap_method_index < EAP_MAX_METHODS &&
 +                  sm->user->methods[sm->user_eap_method_index].method !=
 +                  EAP_TYPE_NONE) {
 +                      next_type = sm->user->methods[
 +                              sm->user_eap_method_index++].method;
 +                      wpa_printf(MSG_DEBUG, "EAP-FAST: try EAP type %d",
 +                                 next_type);
 +              } else {
 +                      next_type = eap_fast_req_failure(sm, data);
 +              }
 +              eap_fast_phase2_init(sm, data, next_type);
 +              return;
 +      }
 +
 +      wpabuf_set(&buf, in_data, in_len);
 +
 +      if (m->check(sm, priv, &buf)) {
 +              wpa_printf(MSG_DEBUG, "EAP-FAST: Phase2 check() asked to "
 +                         "ignore the packet");
 +              eap_fast_req_failure(sm, data);
 +              return;
 +      }
 +
 +      m->process(sm, priv, &buf);
 +
 +      if (!m->isDone(sm, priv))
 +              return;
 +
 +      if (!m->isSuccess(sm, priv)) {
 +              wpa_printf(MSG_DEBUG, "EAP-FAST: Phase2 method failed");
 +              next_type = eap_fast_req_failure(sm, data);
 +              eap_fast_phase2_init(sm, data, next_type);
 +              return;
 +      }
 +
 +      switch (data->state) {
 +      case PHASE2_ID:
 +              if (eap_user_get(sm, sm->identity, sm->identity_len, 1) != 0) {
 +                      wpa_hexdump_ascii(MSG_DEBUG, "EAP-FAST: Phase2 "
 +                                        "Identity not found in the user "
 +                                        "database",
 +                                        sm->identity, sm->identity_len);
 +                      next_type = eap_fast_req_failure(sm, data);
 +                      break;
 +              }
 +
 +              eap_fast_state(data, PHASE2_METHOD);
 +              if (data->anon_provisioning) {
 +                      /*
 +                       * Only EAP-MSCHAPv2 is allowed for anonymous
 +                       * provisioning.
 +                       */
 +                      next_type = EAP_TYPE_MSCHAPV2;
 +                      sm->user_eap_method_index = 0;
 +              } else {
 +                      next_type = sm->user->methods[0].method;
 +                      sm->user_eap_method_index = 1;
 +              }
 +              wpa_printf(MSG_DEBUG, "EAP-FAST: try EAP type %d", next_type);
 +              break;
 +      case PHASE2_METHOD:
 +      case CRYPTO_BINDING:
 +              eap_fast_update_icmk(sm, data);
 +              eap_fast_state(data, CRYPTO_BINDING);
 +              data->eap_seq++;
 +              next_type = EAP_TYPE_NONE;
 +#ifdef EAP_SERVER_TNC
 +              if (sm->tnc && !data->tnc_started) {
 +                      wpa_printf(MSG_DEBUG, "EAP-FAST: Initialize TNC");
 +                      next_type = EAP_TYPE_TNC;
 +                      data->tnc_started = 1;
 +              }
 +#endif /* EAP_SERVER_TNC */
 +              break;
 +      case FAILURE:
 +              break;
 +      default:
 +              wpa_printf(MSG_DEBUG, "EAP-FAST: %s - unexpected state %d",
 +                         __func__, data->state);
 +              break;
 +      }
 +
 +      eap_fast_phase2_init(sm, data, next_type);
 +}
 +
 +
 +static void eap_fast_process_phase2_eap(struct eap_sm *sm,
 +                                      struct eap_fast_data *data,
 +                                      u8 *in_data, size_t in_len)
 +{
 +      struct eap_hdr *hdr;
 +      size_t len;
 +
 +      hdr = (struct eap_hdr *) in_data;
 +      if (in_len < (int) sizeof(*hdr)) {
 +              wpa_printf(MSG_INFO, "EAP-FAST: Too short Phase 2 "
 +                         "EAP frame (len=%lu)", (unsigned long) in_len);
 +              eap_fast_req_failure(sm, data);
 +              return;
 +      }
 +      len = be_to_host16(hdr->length);
 +      if (len > in_len) {
 +              wpa_printf(MSG_INFO, "EAP-FAST: Length mismatch in "
 +                         "Phase 2 EAP frame (len=%lu hdr->length=%lu)",
 +                         (unsigned long) in_len, (unsigned long) len);
 +              eap_fast_req_failure(sm, data);
 +              return;
 +      }
 +      wpa_printf(MSG_DEBUG, "EAP-FAST: Received Phase 2: code=%d "
 +                 "identifier=%d length=%lu", hdr->code, hdr->identifier,
 +                 (unsigned long) len);
 +      switch (hdr->code) {
 +      case EAP_CODE_RESPONSE:
 +              eap_fast_process_phase2_response(sm, data, (u8 *) hdr, len);
 +              break;
 +      default:
 +              wpa_printf(MSG_INFO, "EAP-FAST: Unexpected code=%d in "
 +                         "Phase 2 EAP header", hdr->code);
 +              break;
 +      }
 +}
 +
 +
 +static int eap_fast_parse_tlvs(struct wpabuf *data,
 +                             struct eap_fast_tlv_parse *tlv)
 +{
 +      int mandatory, tlv_type, res;
 +      size_t len;
 +      u8 *pos, *end;
 +
 +      os_memset(tlv, 0, sizeof(*tlv));
 +
 +      pos = wpabuf_mhead(data);
 +      end = pos + wpabuf_len(data);
 +      while (pos + 4 < end) {
 +              mandatory = pos[0] & 0x80;
 +              tlv_type = WPA_GET_BE16(pos) & 0x3fff;
 +              pos += 2;
 +              len = WPA_GET_BE16(pos);
 +              pos += 2;
 +              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 %u%s",
 +                         tlv_type, (unsigned int) len,
 +                         mandatory ? " (mandatory)" : "");
 +
 +              res = eap_fast_parse_tlv(tlv, tlv_type, pos, len);
 +              if (res == -2)
 +                      break;
 +              if (res < 0) {
 +                      if (mandatory) {
 +                              wpa_printf(MSG_DEBUG, "EAP-FAST: Nak unknown "
 +                                         "mandatory TLV type %d", tlv_type);
 +                              /* TODO: generate Nak TLV */
 +                              break;
 +                      } else {
 +                              wpa_printf(MSG_DEBUG, "EAP-FAST: Ignored "
 +                                         "unknown optional TLV type %d",
 +                                         tlv_type);
 +                      }
 +              }
 +
 +              pos += len;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +static int eap_fast_validate_crypto_binding(
 +      struct eap_fast_data *data, struct eap_tlv_crypto_binding_tlv *b,
 +      size_t bind_len)
 +{
 +      u8 cmac[SHA1_MAC_LEN];
 +
 +      wpa_printf(MSG_DEBUG, "EAP-FAST: Reply Crypto-Binding TLV: "
 +                 "Version %d Received Version %d SubType %d",
 +                 b->version, b->received_version, b->subtype);
 +      wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: NONCE",
 +                  b->nonce, sizeof(b->nonce));
 +      wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Compound MAC",
 +                  b->compound_mac, sizeof(b->compound_mac));
 +
 +      if (b->version != EAP_FAST_VERSION ||
 +          b->received_version != EAP_FAST_VERSION) {
 +              wpa_printf(MSG_DEBUG, "EAP-FAST: Unexpected version "
 +                         "in Crypto-Binding: version %d "
 +                         "received_version %d", b->version,
 +                         b->received_version);
 +              return -1;
 +      }
 +
 +      if (b->subtype != EAP_TLV_CRYPTO_BINDING_SUBTYPE_RESPONSE) {
 +              wpa_printf(MSG_DEBUG, "EAP-FAST: Unexpected subtype in "
 +                         "Crypto-Binding: %d", b->subtype);
 +              return -1;
 +      }
 +
 +      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");
 +              return -1;
 +      }
 +
 +      os_memcpy(cmac, b->compound_mac, sizeof(cmac));
 +      os_memset(b->compound_mac, 0, sizeof(cmac));
 +      wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Crypto-Binding TLV for "
 +                  "Compound MAC calculation",
 +                  (u8 *) b, bind_len);
 +      hmac_sha1(data->cmk, EAP_FAST_CMK_LEN, (u8 *) b, bind_len,
 +                b->compound_mac);
 +      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));
 +              wpa_printf(MSG_INFO, "EAP-FAST: Compound MAC did not "
 +                         "match");
 +              return -1;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +static int eap_fast_pac_type(u8 *pac, size_t len, u16 type)
 +{
 +      struct eap_tlv_pac_type_tlv *tlv;
 +
 +      if (pac == NULL || len != sizeof(*tlv))
 +              return 0;
 +
 +      tlv = (struct eap_tlv_pac_type_tlv *) pac;
 +
 +      return be_to_host16(tlv->tlv_type) == PAC_TYPE_PAC_TYPE &&
 +              be_to_host16(tlv->length) == 2 &&
 +              be_to_host16(tlv->pac_type) == type;
 +}
 +
 +
 +static void eap_fast_process_phase2_tlvs(struct eap_sm *sm,
 +                                       struct eap_fast_data *data,
 +                                       struct wpabuf *in_data)
 +{
 +      struct eap_fast_tlv_parse tlv;
 +      int check_crypto_binding = data->state == CRYPTO_BINDING;
 +
 +      if (eap_fast_parse_tlvs(in_data, &tlv) < 0) {
 +              wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to parse received "
 +                         "Phase 2 TLVs");
 +              return;
 +      }
 +
 +      if (tlv.result == EAP_TLV_RESULT_FAILURE) {
 +              wpa_printf(MSG_DEBUG, "EAP-FAST: Result TLV indicated "
 +                         "failure");
 +              eap_fast_state(data, FAILURE);
 +              return;
 +      }
 +
 +      if (data->state == REQUEST_PAC) {
 +              u16 type, len, res;
 +              if (tlv.pac == NULL || tlv.pac_len < 6) {
 +                      wpa_printf(MSG_DEBUG, "EAP-FAST: No PAC "
 +                                 "Acknowledgement received");
 +                      eap_fast_state(data, FAILURE);
 +                      return;
 +              }
 +
 +              type = WPA_GET_BE16(tlv.pac);
 +              len = WPA_GET_BE16(tlv.pac + 2);
 +              res = WPA_GET_BE16(tlv.pac + 4);
 +
 +              if (type != PAC_TYPE_PAC_ACKNOWLEDGEMENT || len != 2 ||
 +                  res != EAP_TLV_RESULT_SUCCESS) {
 +                      wpa_printf(MSG_DEBUG, "EAP-FAST: PAC TLV did not "
 +                                 "contain acknowledgement");
 +                      eap_fast_state(data, FAILURE);
 +                      return;
 +              }
 +
 +              wpa_printf(MSG_DEBUG, "EAP-FAST: PAC-Acknowledgement received "
 +                         "- PAC provisioning succeeded");
 +              eap_fast_state(data, (data->anon_provisioning ||
 +                                    data->send_new_pac == 2) ?
 +                             FAILURE : SUCCESS);
 +              return;
 +      }
 +
 +      if (check_crypto_binding) {
 +              if (tlv.crypto_binding == NULL) {
 +                      wpa_printf(MSG_DEBUG, "EAP-FAST: No Crypto-Binding "
 +                                 "TLV received");
 +                      eap_fast_state(data, FAILURE);
 +                      return;
 +              }
 +
 +              if (data->final_result &&
 +                  tlv.result != EAP_TLV_RESULT_SUCCESS) {
 +                      wpa_printf(MSG_DEBUG, "EAP-FAST: Crypto-Binding TLV "
 +                                 "without Success Result");
 +                      eap_fast_state(data, FAILURE);
 +                      return;
 +              }
 +
 +              if (!data->final_result &&
 +                  tlv.iresult != EAP_TLV_RESULT_SUCCESS) {
 +                      wpa_printf(MSG_DEBUG, "EAP-FAST: Crypto-Binding TLV "
 +                                 "without intermediate Success Result");
 +                      eap_fast_state(data, FAILURE);
 +                      return;
 +              }
 +
 +              if (eap_fast_validate_crypto_binding(data, tlv.crypto_binding,
 +                                                   tlv.crypto_binding_len)) {
 +                      eap_fast_state(data, FAILURE);
 +                      return;
 +              }
 +
 +              wpa_printf(MSG_DEBUG, "EAP-FAST: Valid Crypto-Binding TLV "
 +                         "received");
 +              if (data->final_result) {
 +                      wpa_printf(MSG_DEBUG, "EAP-FAST: Authentication "
 +                                 "completed successfully");
 +              }
 +
 +              if (data->anon_provisioning &&
 +                  sm->eap_fast_prov != ANON_PROV &&
 +                  sm->eap_fast_prov != BOTH_PROV) {
 +                      wpa_printf(MSG_DEBUG, "EAP-FAST: Client is trying to "
 +                                 "use unauthenticated provisioning which is "
 +                                 "disabled");
 +                      eap_fast_state(data, FAILURE);
 +                      return;
 +              }
 +
 +              if (sm->eap_fast_prov != AUTH_PROV &&
 +                  sm->eap_fast_prov != BOTH_PROV &&
 +                  tlv.request_action == EAP_TLV_ACTION_PROCESS_TLV &&
 +                  eap_fast_pac_type(tlv.pac, tlv.pac_len,
 +                                    PAC_TYPE_TUNNEL_PAC)) {
 +                      wpa_printf(MSG_DEBUG, "EAP-FAST: Client is trying to "
 +                                 "use authenticated provisioning which is "
 +                                 "disabled");
 +                      eap_fast_state(data, FAILURE);
 +                      return;
 +              }
 +
 +              if (data->anon_provisioning ||
 +                  (tlv.request_action == EAP_TLV_ACTION_PROCESS_TLV &&
 +                   eap_fast_pac_type(tlv.pac, tlv.pac_len,
 +                                     PAC_TYPE_TUNNEL_PAC))) {
 +                      wpa_printf(MSG_DEBUG, "EAP-FAST: Requested a new "
 +                                 "Tunnel PAC");
 +                      eap_fast_state(data, REQUEST_PAC);
 +              } else if (data->send_new_pac) {
 +                      wpa_printf(MSG_DEBUG, "EAP-FAST: Server triggered "
 +                                 "re-keying of Tunnel PAC");
 +                      eap_fast_state(data, REQUEST_PAC);
 +              } else if (data->final_result)
 +                      eap_fast_state(data, SUCCESS);
 +      }
 +
 +      if (tlv.eap_payload_tlv) {
 +              eap_fast_process_phase2_eap(sm, data, tlv.eap_payload_tlv,
 +                                          tlv.eap_payload_tlv_len);
 +      }
 +}
 +
 +
 +static void eap_fast_process_phase2(struct eap_sm *sm,
 +                                  struct eap_fast_data *data,
 +                                  struct wpabuf *in_buf)
 +{
 +      struct wpabuf *in_decrypted;
 +
 +      wpa_printf(MSG_DEBUG, "EAP-FAST: Received %lu bytes encrypted data for"
 +                 " Phase 2", (unsigned long) wpabuf_len(in_buf));
 +
 +      if (data->pending_phase2_resp) {
 +              wpa_printf(MSG_DEBUG, "EAP-PEAP: Pending Phase 2 response - "
 +                         "skip decryption and use old data");
 +              eap_fast_process_phase2_tlvs(sm, data,
 +                                           data->pending_phase2_resp);
 +              wpabuf_free(data->pending_phase2_resp);
 +              data->pending_phase2_resp = NULL;
 +              return;
 +      }
 +
 +      in_decrypted = tls_connection_decrypt(sm->ssl_ctx, data->ssl.conn,
 +                                            in_buf);
 +      if (in_decrypted == NULL) {
 +              wpa_printf(MSG_INFO, "EAP-FAST: Failed to decrypt Phase 2 "
 +                         "data");
 +              eap_fast_state(data, FAILURE);
 +              return;
 +      }
 +
 +      wpa_hexdump_buf_key(MSG_DEBUG, "EAP-FAST: Decrypted Phase 2 TLVs",
 +                          in_decrypted);
 +
 +      eap_fast_process_phase2_tlvs(sm, data, in_decrypted);
 +
 +      if (sm->method_pending == METHOD_PENDING_WAIT) {
 +              wpa_printf(MSG_DEBUG, "EAP-FAST: Phase2 method is in "
 +                         "pending wait state - save decrypted response");
 +              wpabuf_free(data->pending_phase2_resp);
 +              data->pending_phase2_resp = in_decrypted;
 +              return;
 +      }
 +
 +      wpabuf_free(in_decrypted);
 +}
 +
 +
 +static int eap_fast_process_version(struct eap_sm *sm, void *priv,
 +                                  int peer_version)
 +{
 +      struct eap_fast_data *data = priv;
 +
 +      data->peer_version = peer_version;
 +
 +      if (data->force_version >= 0 && peer_version != data->force_version) {
 +              wpa_printf(MSG_INFO, "EAP-FAST: peer did not select the forced"
 +                         " version (forced=%d peer=%d) - reject",
 +                         data->force_version, peer_version);
 +              return -1;
 +      }
 +
 +      if (peer_version < data->fast_version) {
 +              wpa_printf(MSG_DEBUG, "EAP-FAST: peer ver=%d, own ver=%d; "
 +                         "use version %d",
 +                         peer_version, data->fast_version, peer_version);
 +              data->fast_version = peer_version;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +static int eap_fast_process_phase1(struct eap_sm *sm,
 +                                 struct eap_fast_data *data)
 +{
 +      if (eap_server_tls_phase1(sm, &data->ssl) < 0) {
 +              wpa_printf(MSG_INFO, "EAP-FAST: TLS processing failed");
 +              eap_fast_state(data, FAILURE);
 +              return -1;
 +      }
 +
 +      if (!tls_connection_established(sm->ssl_ctx, data->ssl.conn) ||
 +          wpabuf_len(data->ssl.tls_out) > 0)
 +              return 1;
 +
 +      /*
 +       * Phase 1 was completed with the received message (e.g., when using
 +       * abbreviated handshake), so Phase 2 can be started immediately
 +       * without having to send through an empty message to the peer.
 +       */
 +
 +      return eap_fast_phase1_done(sm, data);
 +}
 +
 +
 +static int eap_fast_process_phase2_start(struct eap_sm *sm,
 +                                       struct eap_fast_data *data)
 +{
 +      u8 next_type;
 +
 +      if (data->identity) {
 +              os_free(sm->identity);
 +              sm->identity = data->identity;
 +              data->identity = NULL;
 +              sm->identity_len = data->identity_len;
 +              data->identity_len = 0;
 +              sm->require_identity_match = 1;
 +              if (eap_user_get(sm, sm->identity, sm->identity_len, 1) != 0) {
 +                      wpa_hexdump_ascii(MSG_DEBUG, "EAP-FAST: "
 +                                        "Phase2 Identity not found "
 +                                        "in the user database",
 +                                        sm->identity, sm->identity_len);
 +                      next_type = eap_fast_req_failure(sm, data);
 +              } else {
 +                      wpa_printf(MSG_DEBUG, "EAP-FAST: Identity already "
 +                                 "known - skip Phase 2 Identity Request");
 +                      next_type = sm->user->methods[0].method;
 +                      sm->user_eap_method_index = 1;
 +              }
 +
 +              eap_fast_state(data, PHASE2_METHOD);
 +      } else {
 +              eap_fast_state(data, PHASE2_ID);
 +              next_type = EAP_TYPE_IDENTITY;
 +      }
 +
 +      return eap_fast_phase2_init(sm, data, next_type);
 +}
 +
 +
 +static void eap_fast_process_msg(struct eap_sm *sm, void *priv,
 +                               const struct wpabuf *respData)
 +{
 +      struct eap_fast_data *data = priv;
 +
 +      switch (data->state) {
 +      case PHASE1:
 +              if (eap_fast_process_phase1(sm, data))
 +                      break;
 +
 +              /* fall through to PHASE2_START */
 +      case PHASE2_START:
 +              eap_fast_process_phase2_start(sm, data);
 +              break;
 +      case PHASE2_ID:
 +      case PHASE2_METHOD:
 +      case CRYPTO_BINDING:
 +      case REQUEST_PAC:
 +              eap_fast_process_phase2(sm, data, data->ssl.tls_in);
 +              break;
 +      default:
 +              wpa_printf(MSG_DEBUG, "EAP-FAST: Unexpected state %d in %s",
 +                         data->state, __func__);
 +              break;
 +      }
 +}
 +
 +
 +static void eap_fast_process(struct eap_sm *sm, void *priv,
 +                           struct wpabuf *respData)
 +{
 +      struct eap_fast_data *data = priv;
 +      if (eap_server_tls_process(sm, &data->ssl, respData, data,
 +                                 EAP_TYPE_FAST, eap_fast_process_version,
 +                                 eap_fast_process_msg) < 0)
 +              eap_fast_state(data, FAILURE);
 +}
 +
 +
 +static Boolean eap_fast_isDone(struct eap_sm *sm, void *priv)
 +{
 +      struct eap_fast_data *data = priv;
 +      return data->state == SUCCESS || data->state == FAILURE;
 +}
 +
 +
 +static u8 * eap_fast_getKey(struct eap_sm *sm, void *priv, size_t *len)
 +{
 +      struct eap_fast_data *data = priv;
 +      u8 *eapKeyData;
 +
 +      if (data->state != SUCCESS)
 +              return NULL;
 +
 +      eapKeyData = os_malloc(EAP_FAST_KEY_LEN);
 +      if (eapKeyData == NULL)
 +              return NULL;
 +
 +      eap_fast_derive_eap_msk(data->simck, eapKeyData);
 +      *len = EAP_FAST_KEY_LEN;
 +
 +      return eapKeyData;
 +}
 +
 +
 +static u8 * eap_fast_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
 +{
 +      struct eap_fast_data *data = priv;
 +      u8 *eapKeyData;
 +
 +      if (data->state != SUCCESS)
 +              return NULL;
 +
 +      eapKeyData = os_malloc(EAP_EMSK_LEN);
 +      if (eapKeyData == NULL)
 +              return NULL;
 +
 +      eap_fast_derive_eap_emsk(data->simck, eapKeyData);
 +      *len = EAP_EMSK_LEN;
 +
 +      return eapKeyData;
 +}
 +
 +
 +static Boolean eap_fast_isSuccess(struct eap_sm *sm, void *priv)
 +{
 +      struct eap_fast_data *data = priv;
 +      return data->state == SUCCESS;
 +}
 +
 +
 +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;
 +      int ret;
 +
 +      eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
 +                                    EAP_VENDOR_IETF, EAP_TYPE_FAST, "FAST");
 +      if (eap == NULL)
 +              return -1;
 +
 +      eap->init = eap_fast_init;
 +      eap->reset = eap_fast_reset;
 +      eap->buildReq = eap_fast_buildReq;
 +      eap->check = eap_fast_check;
 +      eap->process = eap_fast_process;
 +      eap->isDone = eap_fast_isDone;
 +      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)
 +              eap_server_method_free(eap);
 +      return ret;
 +}
index 05848d2eaac554f5b921eb78c1e80524833d0d7b,0000000000000000000000000000000000000000..98d74e0d717e276e3d9c4070a51a78f8311a9ca6
mode 100644,000000..100644
--- /dev/null
@@@ -1,582 -1,0 +1,595 @@@
 +/*
 + * hostapd / EAP-MSCHAPv2 (draft-kamath-pppext-eap-mschapv2-00.txt) server
 + * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
 + *
 + * This software may be distributed under the terms of the BSD license.
 + * See README for more details.
 + */
 +
 +#include "includes.h"
 +
 +#include "common.h"
 +#include "crypto/ms_funcs.h"
 +#include "crypto/random.h"
 +#include "eap_i.h"
 +
 +
 +struct eap_mschapv2_hdr {
 +      u8 op_code; /* MSCHAPV2_OP_* */
 +      u8 mschapv2_id; /* must be changed for challenges, but not for
 +                       * success/failure */
 +      u8 ms_length[2]; /* Note: misaligned; length - 5 */
 +      /* followed by data */
 +} STRUCT_PACKED;
 +
 +#define MSCHAPV2_OP_CHALLENGE 1
 +#define MSCHAPV2_OP_RESPONSE 2
 +#define MSCHAPV2_OP_SUCCESS 3
 +#define MSCHAPV2_OP_FAILURE 4
 +#define MSCHAPV2_OP_CHANGE_PASSWORD 7
 +
 +#define MSCHAPV2_RESP_LEN 49
 +
 +#define ERROR_RESTRICTED_LOGON_HOURS 646
 +#define ERROR_ACCT_DISABLED 647
 +#define ERROR_PASSWD_EXPIRED 648
 +#define ERROR_NO_DIALIN_PERMISSION 649
 +#define ERROR_AUTHENTICATION_FAILURE 691
 +#define ERROR_CHANGING_PASSWORD 709
 +
 +#define PASSWD_CHANGE_CHAL_LEN 16
 +#define MSCHAPV2_KEY_LEN 16
 +
 +
 +#define CHALLENGE_LEN 16
 +
 +struct eap_mschapv2_data {
 +      u8 auth_challenge[CHALLENGE_LEN];
 +      int auth_challenge_from_tls;
 +      u8 *peer_challenge;
 +      u8 auth_response[20];
 +      enum { CHALLENGE, SUCCESS_REQ, FAILURE_REQ, SUCCESS, FAILURE } state;
 +      u8 resp_mschapv2_id;
 +      u8 master_key[16];
 +      int master_key_valid;
 +};
 +
 +
 +static void * eap_mschapv2_init(struct eap_sm *sm)
 +{
 +      struct eap_mschapv2_data *data;
 +
 +      data = os_zalloc(sizeof(*data));
 +      if (data == NULL)
 +              return NULL;
 +      data->state = CHALLENGE;
 +
 +      if (sm->auth_challenge) {
 +              os_memcpy(data->auth_challenge, sm->auth_challenge,
 +                        CHALLENGE_LEN);
 +              data->auth_challenge_from_tls = 1;
 +      }
 +
 +      if (sm->peer_challenge) {
 +              data->peer_challenge = os_malloc(CHALLENGE_LEN);
 +              if (data->peer_challenge == NULL) {
 +                      os_free(data);
 +                      return NULL;
 +              }
 +              os_memcpy(data->peer_challenge, sm->peer_challenge,
 +                        CHALLENGE_LEN);
 +      }
 +
 +      return data;
 +}
 +
 +
 +static void eap_mschapv2_reset(struct eap_sm *sm, void *priv)
 +{
 +      struct eap_mschapv2_data *data = priv;
 +      if (data == NULL)
 +              return;
 +
 +      os_free(data->peer_challenge);
 +      bin_clear_free(data, sizeof(*data));
 +}
 +
 +
 +static struct wpabuf * eap_mschapv2_build_challenge(
 +      struct eap_sm *sm, struct eap_mschapv2_data *data, u8 id)
 +{
 +      struct wpabuf *req;
 +      struct eap_mschapv2_hdr *ms;
 +      size_t ms_len;
 +
 +      if (!data->auth_challenge_from_tls &&
 +          random_get_bytes(data->auth_challenge, CHALLENGE_LEN)) {
 +              wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to get random "
 +                         "data");
 +              data->state = FAILURE;
 +              return NULL;
 +      }
 +
 +      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) {
 +              wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to allocate memory"
 +                         " for request");
 +              data->state = FAILURE;
 +              return NULL;
 +      }
 +
 +      ms = wpabuf_put(req, sizeof(*ms));
 +      ms->op_code = MSCHAPV2_OP_CHALLENGE;
 +      ms->mschapv2_id = id;
 +      WPA_PUT_BE16(ms->ms_length, ms_len);
 +
 +      wpabuf_put_u8(req, CHALLENGE_LEN);
 +      if (!data->auth_challenge_from_tls)
 +              wpabuf_put_data(req, data->auth_challenge, CHALLENGE_LEN);
 +      else
 +              wpabuf_put(req, CHALLENGE_LEN);
 +      wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: Challenge",
 +                  data->auth_challenge, CHALLENGE_LEN);
 +      wpabuf_put_data(req, sm->server_id, sm->server_id_len);
 +
 +      return req;
 +}
 +
 +
 +static struct wpabuf * eap_mschapv2_build_success_req(
 +      struct eap_sm *sm, struct eap_mschapv2_data *data, u8 id)
 +{
 +      struct wpabuf *req;
 +      struct eap_mschapv2_hdr *ms;
 +      u8 *msg;
 +      char *message = "OK";
 +      size_t ms_len;
 +
 +      ms_len = sizeof(*ms) + 2 + 2 * sizeof(data->auth_response) + 1 + 2 +
 +              os_strlen(message);
 +      req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len,
 +                          EAP_CODE_REQUEST, id);
 +      if (req == NULL) {
 +              wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to allocate memory"
 +                         " for request");
 +              data->state = FAILURE;
 +              return NULL;
 +      }
 +
 +      ms = wpabuf_put(req, sizeof(*ms));
 +      ms->op_code = MSCHAPV2_OP_SUCCESS;
 +      ms->mschapv2_id = data->resp_mschapv2_id;
 +      WPA_PUT_BE16(ms->ms_length, ms_len);
 +      msg = (u8 *) (ms + 1);
 +
 +      wpabuf_put_u8(req, 'S');
 +      wpabuf_put_u8(req, '=');
 +      wpa_snprintf_hex_uppercase(
 +              wpabuf_put(req, sizeof(data->auth_response) * 2),
 +              sizeof(data->auth_response) * 2 + 1,
 +              data->auth_response, sizeof(data->auth_response));
 +      wpabuf_put_u8(req, ' ');
 +      wpabuf_put_u8(req, 'M');
 +      wpabuf_put_u8(req, '=');
 +      wpabuf_put_data(req, message, os_strlen(message));
 +
 +      wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: Success Request Message",
 +                        msg, ms_len - sizeof(*ms));
 +
 +      return req;
 +}
 +
 +
 +static struct wpabuf * eap_mschapv2_build_failure_req(
 +      struct eap_sm *sm, struct eap_mschapv2_data *data, u8 id)
 +{
 +      struct wpabuf *req;
 +      struct eap_mschapv2_hdr *ms;
 +      char *message = "E=691 R=0 C=00000000000000000000000000000000 V=3 "
 +              "M=FAILED";
 +      size_t ms_len;
 +
 +      ms_len = sizeof(*ms) + os_strlen(message);
 +      req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len,
 +                          EAP_CODE_REQUEST, id);
 +      if (req == NULL) {
 +              wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to allocate memory"
 +                         " for request");
 +              data->state = FAILURE;
 +              return NULL;
 +      }
 +
 +      ms = wpabuf_put(req, sizeof(*ms));
 +      ms->op_code = MSCHAPV2_OP_FAILURE;
 +      ms->mschapv2_id = data->resp_mschapv2_id;
 +      WPA_PUT_BE16(ms->ms_length, ms_len);
 +
 +      wpabuf_put_data(req, message, os_strlen(message));
 +
 +      wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: Failure Request Message",
 +                        (u8 *) message, os_strlen(message));
 +
 +      return req;
 +}
 +
 +
 +static struct wpabuf * eap_mschapv2_buildReq(struct eap_sm *sm, void *priv,
 +                                           u8 id)
 +{
 +      struct eap_mschapv2_data *data = priv;
 +
 +      switch (data->state) {
 +      case CHALLENGE:
 +              return eap_mschapv2_build_challenge(sm, data, id);
 +      case SUCCESS_REQ:
 +              return eap_mschapv2_build_success_req(sm, data, id);
 +      case FAILURE_REQ:
 +              return eap_mschapv2_build_failure_req(sm, data, id);
 +      default:
 +              wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Unknown state %d in "
 +                         "buildReq", data->state);
 +              break;
 +      }
 +      return NULL;
 +}
 +
 +
 +static Boolean eap_mschapv2_check(struct eap_sm *sm, void *priv,
 +                                struct wpabuf *respData)
 +{
 +      struct eap_mschapv2_data *data = priv;
 +      struct eap_mschapv2_hdr *resp;
 +      const u8 *pos;
 +      size_t len;
 +
 +      pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData,
 +                             &len);
 +      if (pos == NULL || len < 1) {
 +              wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Invalid frame");
 +              return TRUE;
 +      }
 +
 +      resp = (struct eap_mschapv2_hdr *) pos;
 +      if (data->state == CHALLENGE &&
 +          resp->op_code != MSCHAPV2_OP_RESPONSE) {
 +              wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Expected Response - "
 +                         "ignore op %d", resp->op_code);
 +              return TRUE;
 +      }
 +
 +      if (data->state == SUCCESS_REQ &&
 +          resp->op_code != MSCHAPV2_OP_SUCCESS &&
 +          resp->op_code != MSCHAPV2_OP_FAILURE) {
 +              wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Expected Success or "
 +                         "Failure - ignore op %d", resp->op_code);
 +              return TRUE;
 +      }
 +
 +      if (data->state == FAILURE_REQ &&
 +          resp->op_code != MSCHAPV2_OP_FAILURE) {
 +              wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Expected Failure "
 +                         "- ignore op %d", resp->op_code);
 +              return TRUE;
 +      }
 +
 +      return FALSE;
 +}
 +
 +
 +static void eap_mschapv2_process_response(struct eap_sm *sm,
 +                                        struct eap_mschapv2_data *data,
 +                                        struct wpabuf *respData)
 +{
 +      struct eap_mschapv2_hdr *resp;
 +      const u8 *pos, *end, *peer_challenge, *nt_response, *name;
 +      u8 flags;
 +      size_t len, name_len, i;
 +      u8 expected[24];
 +      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);
 +      if (pos == NULL || len < 1)
 +              return; /* Should not happen - frame already validated */
 +
 +      end = pos + len;
 +      resp = (struct eap_mschapv2_hdr *) pos;
 +      pos = (u8 *) (resp + 1);
 +
 +      if (len < sizeof(*resp) + 1 + 49 ||
 +          resp->op_code != MSCHAPV2_OP_RESPONSE ||
 +          pos[0] != 49) {
 +              wpa_hexdump_buf(MSG_DEBUG, "EAP-MSCHAPV2: Invalid response",
 +                              respData);
 +              data->state = FAILURE;
 +              return;
 +      }
 +      data->resp_mschapv2_id = resp->mschapv2_id;
 +      pos++;
 +      peer_challenge = pos;
 +      pos += 16 + 8;
 +      nt_response = pos;
 +      pos += 24;
 +      flags = *pos++;
 +      name = pos;
 +      name_len = end - name;
 +
 +      if (data->peer_challenge) {
 +              wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Using pre-configured "
 +                         "Peer-Challenge");
 +              peer_challenge = data->peer_challenge;
 +      }
 +      wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: Peer-Challenge",
 +                  peer_challenge, 16);
 +      wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: NT-Response", nt_response, 24);
 +      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). */
 +      username = sm->identity;
 +      username_len = sm->identity_len;
 +      for (i = 0; i < username_len; i++) {
 +              if (username[i] == '\\') {
 +                      username_len -= i + 1;
 +                      username += i + 1;
 +                      break;
 +              }
 +      }
 +
 +      user = name;
 +      user_len = name_len;
 +      for (i = 0; i < user_len; i++) {
 +              if (user[i] == '\\') {
 +                      user_len -= i + 1;
 +                      user += i + 1;
 +                      break;
 +              }
 +      }
 +
++#ifdef CONFIG_TESTING_OPTIONS
++      {
++              u8 challenge[8];
++
++              if (challenge_hash(peer_challenge, data->auth_challenge,
++                                 username, username_len, challenge) == 0) {
++                      eap_server_mschap_rx_callback(sm, "EAP-MSCHAPV2",
++                                                    username, username_len,
++                                                    challenge, nt_response);
++              }
++      }
++#endif /* CONFIG_TESTING_OPTIONS */
++
 +      if (username_len != user_len ||
 +          os_memcmp(username, user, username_len) != 0) {
 +              wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Mismatch in user names");
 +              wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Expected user "
 +                                "name", username, username_len);
 +              wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Received user "
 +                                "name", user, user_len);
 +              data->state = FAILURE;
 +              return;
 +      }
 +
 +      wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: User name",
 +                        username, username_len);
 +
 +      if (sm->user->password_hash) {
 +              res = generate_nt_response_pwhash(data->auth_challenge,
 +                                                peer_challenge,
 +                                                username, username_len,
 +                                                sm->user->password,
 +                                                expected);
 +      } else {
 +              res = generate_nt_response(data->auth_challenge,
 +                                         peer_challenge,
 +                                         username, username_len,
 +                                         sm->user->password,
 +                                         sm->user->password_len,
 +                                         expected);
 +      }
 +      if (res) {
 +              data->state = FAILURE;
 +              return;
 +      }
 +
 +      if (os_memcmp_const(nt_response, expected, 24) == 0) {
 +              const u8 *pw_hash;
 +              u8 pw_hash_buf[16], pw_hash_hash[16];
 +
 +              wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Correct NT-Response");
 +              data->state = SUCCESS_REQ;
 +
 +              /* Authenticator response is not really needed yet, but
 +               * calculate it here so that peer_challenge and username need
 +               * not be saved. */
 +              if (sm->user->password_hash) {
 +                      pw_hash = sm->user->password;
 +              } else {
 +                      if (nt_password_hash(sm->user->password,
 +                                           sm->user->password_len,
 +                                           pw_hash_buf) < 0) {
 +                              data->state = FAILURE;
 +                              return;
 +                      }
 +                      pw_hash = pw_hash_buf;
 +              }
 +              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);
 +      } else {
 +              wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: Expected NT-Response",
 +                          expected, 24);
 +              wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Invalid NT-Response");
 +              data->state = FAILURE_REQ;
 +      }
 +}
 +
 +
 +static void eap_mschapv2_process_success_resp(struct eap_sm *sm,
 +                                            struct eap_mschapv2_data *data,
 +                                            struct wpabuf *respData)
 +{
 +      struct eap_mschapv2_hdr *resp;
 +      const u8 *pos;
 +      size_t len;
 +
 +      pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData,
 +                             &len);
 +      if (pos == NULL || len < 1)
 +              return; /* Should not happen - frame already validated */
 +
 +      resp = (struct eap_mschapv2_hdr *) pos;
 +
 +      if (resp->op_code == MSCHAPV2_OP_SUCCESS) {
 +              wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received Success Response"
 +                         " - authentication completed successfully");
 +              data->state = SUCCESS;
 +      } else {
 +              wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Did not receive Success "
 +                         "Response - peer rejected authentication");
 +              data->state = FAILURE;
 +      }
 +}
 +
 +
 +static void eap_mschapv2_process_failure_resp(struct eap_sm *sm,
 +                                            struct eap_mschapv2_data *data,
 +                                            struct wpabuf *respData)
 +{
 +      struct eap_mschapv2_hdr *resp;
 +      const u8 *pos;
 +      size_t len;
 +
 +      pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData,
 +                             &len);
 +      if (pos == NULL || len < 1)
 +              return; /* Should not happen - frame already validated */
 +
 +      resp = (struct eap_mschapv2_hdr *) pos;
 +
 +      if (resp->op_code == MSCHAPV2_OP_FAILURE) {
 +              wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received Failure Response"
 +                         " - authentication failed");
 +      } else {
 +              wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Did not receive Failure "
 +                         "Response - authentication failed");
 +      }
 +
 +      data->state = FAILURE;
 +}
 +
 +
 +static void eap_mschapv2_process(struct eap_sm *sm, void *priv,
 +                               struct wpabuf *respData)
 +{
 +      struct eap_mschapv2_data *data = priv;
 +
 +      if (sm->user == NULL || sm->user->password == NULL) {
 +              wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Password not configured");
 +              data->state = FAILURE;
 +              return;
 +      }
 +
 +      switch (data->state) {
 +      case CHALLENGE:
 +              eap_mschapv2_process_response(sm, data, respData);
 +              break;
 +      case SUCCESS_REQ:
 +              eap_mschapv2_process_success_resp(sm, data, respData);
 +              break;
 +      case FAILURE_REQ:
 +              eap_mschapv2_process_failure_resp(sm, data, respData);
 +              break;
 +      default:
 +              wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Unknown state %d in "
 +                         "process", data->state);
 +              break;
 +      }
 +}
 +
 +
 +static Boolean eap_mschapv2_isDone(struct eap_sm *sm, void *priv)
 +{
 +      struct eap_mschapv2_data *data = priv;
 +      return data->state == SUCCESS || data->state == FAILURE;
 +}
 +
 +
 +static u8 * eap_mschapv2_getKey(struct eap_sm *sm, void *priv, size_t *len)
 +{
 +      struct eap_mschapv2_data *data = priv;
 +      u8 *key;
 +
 +      if (data->state != SUCCESS || !data->master_key_valid)
 +              return NULL;
 +
 +      *len = 2 * MSCHAPV2_KEY_LEN;
 +      key = os_malloc(*len);
 +      if (key == NULL)
 +              return NULL;
 +      /* MSK = server MS-MPPE-Recv-Key | MS-MPPE-Send-Key */
 +      get_asymetric_start_key(data->master_key, key, MSCHAPV2_KEY_LEN, 0, 1);
 +      get_asymetric_start_key(data->master_key, key + MSCHAPV2_KEY_LEN,
 +                              MSCHAPV2_KEY_LEN, 1, 1);
 +      wpa_hexdump_key(MSG_DEBUG, "EAP-MSCHAPV2: Derived key", key, *len);
 +
 +      return key;
 +}
 +
 +
 +static Boolean eap_mschapv2_isSuccess(struct eap_sm *sm, void *priv)
 +{
 +      struct eap_mschapv2_data *data = priv;
 +      return data->state == SUCCESS;
 +}
 +
 +
 +int eap_server_mschapv2_register(void)
 +{
 +      struct eap_method *eap;
 +      int ret;
 +
 +      eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
 +                                    EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2,
 +                                    "MSCHAPV2");
 +      if (eap == NULL)
 +              return -1;
 +
 +      eap->init = eap_mschapv2_init;
 +      eap->reset = eap_mschapv2_reset;
 +      eap->buildReq = eap_mschapv2_buildReq;
 +      eap->check = eap_mschapv2_check;
 +      eap->process = eap_mschapv2_process;
 +      eap->isDone = eap_mschapv2_isDone;
 +      eap->getKey = eap_mschapv2_getKey;
 +      eap->isSuccess = eap_mschapv2_isSuccess;
 +
 +      ret = eap_server_method_register(eap);
 +      if (ret)
 +              eap_server_method_free(eap);
 +      return ret;
 +}
index faa0fd2f238789614dd7b949a38cfb06a436979e,0000000000000000000000000000000000000000..51062b0987e458372852cd98964324370defb4dc
mode 100644,000000..100644
--- /dev/null
@@@ -1,1270 -1,0 +1,1375 @@@
-       if (eap_server_tls_ssl_init(sm, &data->ssl, 0)) {
 +/*
 + * hostapd / EAP-PEAP (draft-josefsson-pppext-eap-tls-eap-10.txt)
 + * Copyright (c) 2004-2008, 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/sha1.h"
 +#include "crypto/tls.h"
 +#include "crypto/random.h"
 +#include "eap_i.h"
 +#include "eap_tls_common.h"
 +#include "eap_common/eap_tlv_common.h"
 +#include "eap_common/eap_peap_common.h"
 +#include "tncs.h"
 +
 +
 +/* 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
 + */
 +#define EAP_PEAP_VERSION 1
 +
 +
 +static void eap_peap_reset(struct eap_sm *sm, void *priv);
 +
 +
 +struct eap_peap_data {
 +      struct eap_ssl_data ssl;
 +      enum {
 +              START, PHASE1, PHASE1_ID2, PHASE2_START, PHASE2_ID,
 +              PHASE2_METHOD, PHASE2_SOH,
 +              PHASE2_TLV, SUCCESS_REQ, FAILURE_REQ, SUCCESS, FAILURE
 +      } state;
 +
 +      int peap_version;
 +      int recv_version;
 +      const struct eap_method *phase2_method;
 +      void *phase2_priv;
 +      int force_version;
 +      struct wpabuf *pending_phase2_resp;
 +      enum { TLV_REQ_NONE, TLV_REQ_SUCCESS, TLV_REQ_FAILURE } tlv_request;
 +      int crypto_binding_sent;
 +      int crypto_binding_used;
 +      enum { NO_BINDING, OPTIONAL_BINDING, REQUIRE_BINDING } crypto_binding;
 +      u8 binding_nonce[32];
 +      u8 ipmk[40];
 +      u8 cmk[20];
 +      u8 *phase2_key;
 +      size_t phase2_key_len;
 +      struct wpabuf *soh_response;
 +};
 +
 +
 +static const char * eap_peap_state_txt(int state)
 +{
 +      switch (state) {
 +      case START:
 +              return "START";
 +      case PHASE1:
 +              return "PHASE1";
 +      case PHASE1_ID2:
 +              return "PHASE1_ID2";
 +      case PHASE2_START:
 +              return "PHASE2_START";
 +      case PHASE2_ID:
 +              return "PHASE2_ID";
 +      case PHASE2_METHOD:
 +              return "PHASE2_METHOD";
 +      case PHASE2_SOH:
 +              return "PHASE2_SOH";
 +      case PHASE2_TLV:
 +              return "PHASE2_TLV";
 +      case SUCCESS_REQ:
 +              return "SUCCESS_REQ";
 +      case FAILURE_REQ:
 +              return "FAILURE_REQ";
 +      case SUCCESS:
 +              return "SUCCESS";
 +      case FAILURE:
 +              return "FAILURE";
 +      default:
 +              return "Unknown?!";
 +      }
 +}
 +
 +
 +static void eap_peap_state(struct eap_peap_data *data, int state)
 +{
 +      wpa_printf(MSG_DEBUG, "EAP-PEAP: %s -> %s",
 +                 eap_peap_state_txt(data->state),
 +                 eap_peap_state_txt(state));
 +      data->state = state;
++      if (state == FAILURE || state == FAILURE_REQ)
++              tls_connection_remove_session(data->ssl.conn);
++}
++
++
++static void eap_peap_valid_session(struct eap_sm *sm,
++                                 struct eap_peap_data *data)
++{
++      struct wpabuf *buf;
++
++      if (!sm->tls_session_lifetime ||
++          tls_connection_resumed(sm->ssl_ctx, data->ssl.conn))
++              return;
++
++      buf = wpabuf_alloc(1 + 1 + sm->identity_len);
++      if (!buf)
++              return;
++      wpabuf_put_u8(buf, EAP_TYPE_PEAP);
++      if (sm->identity) {
++              u8 id_len;
++
++              if (sm->identity_len <= 255)
++                      id_len = sm->identity_len;
++              else
++                      id_len = 255;
++              wpabuf_put_u8(buf, id_len);
++              wpabuf_put_data(buf, sm->identity, id_len);
++      } else {
++              wpabuf_put_u8(buf, 0);
++      }
++      tls_connection_set_success_data(data->ssl.conn, buf);
 +}
 +
 +
 +static void eap_peap_req_success(struct eap_sm *sm,
 +                               struct eap_peap_data *data)
 +{
 +      if (data->state == FAILURE || data->state == FAILURE_REQ) {
 +              eap_peap_state(data, FAILURE);
 +              return;
 +      }
 +
 +      if (data->peap_version == 0) {
 +              data->tlv_request = TLV_REQ_SUCCESS;
 +              eap_peap_state(data, PHASE2_TLV);
 +      } else {
 +              eap_peap_state(data, SUCCESS_REQ);
 +      }
 +}
 +
 +
 +static void eap_peap_req_failure(struct eap_sm *sm,
 +                               struct eap_peap_data *data)
 +{
 +      if (data->state == FAILURE || data->state == FAILURE_REQ ||
 +          data->state == SUCCESS_REQ || data->tlv_request != TLV_REQ_NONE) {
 +              eap_peap_state(data, FAILURE);
 +              return;
 +      }
 +
 +      if (data->peap_version == 0) {
 +              data->tlv_request = TLV_REQ_FAILURE;
 +              eap_peap_state(data, PHASE2_TLV);
 +      } else {
 +              eap_peap_state(data, FAILURE_REQ);
 +      }
 +}
 +
 +
 +static void * eap_peap_init(struct eap_sm *sm)
 +{
 +      struct eap_peap_data *data;
 +
 +      data = os_zalloc(sizeof(*data));
 +      if (data == NULL)
 +              return NULL;
 +      data->peap_version = EAP_PEAP_VERSION;
 +      data->force_version = -1;
 +      if (sm->user && sm->user->force_version >= 0) {
 +              data->force_version = sm->user->force_version;
 +              wpa_printf(MSG_DEBUG, "EAP-PEAP: forcing version %d",
 +                         data->force_version);
 +              data->peap_version = data->force_version;
 +      }
 +      data->state = START;
 +      data->crypto_binding = OPTIONAL_BINDING;
 +
-                               EapType eap_type)
++      if (eap_server_tls_ssl_init(sm, &data->ssl, 0, EAP_TYPE_PEAP)) {
 +              wpa_printf(MSG_INFO, "EAP-PEAP: Failed to initialize SSL.");
 +              eap_peap_reset(sm, data);
 +              return NULL;
 +      }
 +
 +      return data;
 +}
 +
 +
 +static void eap_peap_reset(struct eap_sm *sm, void *priv)
 +{
 +      struct eap_peap_data *data = priv;
 +      if (data == NULL)
 +              return;
 +      if (data->phase2_priv && data->phase2_method)
 +              data->phase2_method->reset(sm, data->phase2_priv);
 +      eap_server_tls_ssl_deinit(sm, &data->ssl);
 +      wpabuf_free(data->pending_phase2_resp);
 +      os_free(data->phase2_key);
 +      wpabuf_free(data->soh_response);
 +      bin_clear_free(data, sizeof(*data));
 +}
 +
 +
 +static struct wpabuf * eap_peap_build_start(struct eap_sm *sm,
 +                                          struct eap_peap_data *data, u8 id)
 +{
 +      struct wpabuf *req;
 +
 +      req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PEAP, 1,
 +                          EAP_CODE_REQUEST, id);
 +      if (req == NULL) {
 +              wpa_printf(MSG_ERROR, "EAP-PEAP: Failed to allocate memory for"
 +                         " request");
 +              eap_peap_state(data, FAILURE);
 +              return NULL;
 +      }
 +
 +      wpabuf_put_u8(req, EAP_TLS_FLAGS_START | data->peap_version);
 +
 +      eap_peap_state(data, PHASE1);
 +
 +      return req;
 +}
 +
 +
 +static struct wpabuf * eap_peap_build_phase2_req(struct eap_sm *sm,
 +                                               struct eap_peap_data *data,
 +                                               u8 id)
 +{
 +      struct wpabuf *buf, *encr_req, msgbuf;
 +      const u8 *req;
 +      size_t req_len;
 +
 +      if (data->phase2_method == NULL || data->phase2_priv == NULL) {
 +              wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 method not ready");
 +              return NULL;
 +      }
 +      buf = data->phase2_method->buildReq(sm, data->phase2_priv, id);
 +      if (buf == NULL)
 +              return NULL;
 +
 +      req = wpabuf_head(buf);
 +      req_len = wpabuf_len(buf);
 +      wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: Encrypting Phase 2 data",
 +                      req, req_len);
 +
 +      if (data->peap_version == 0 &&
 +          data->phase2_method->method != EAP_TYPE_TLV) {
 +              req += sizeof(struct eap_hdr);
 +              req_len -= sizeof(struct eap_hdr);
 +      }
 +
 +      wpabuf_set(&msgbuf, req, req_len);
 +      encr_req = eap_server_tls_encrypt(sm, &data->ssl, &msgbuf);
 +      wpabuf_free(buf);
 +
 +      return encr_req;
 +}
 +
 +
 +#ifdef EAP_SERVER_TNC
 +static struct wpabuf * eap_peap_build_phase2_soh(struct eap_sm *sm,
 +                                               struct eap_peap_data *data,
 +                                               u8 id)
 +{
 +      struct wpabuf *buf1, *buf, *encr_req, msgbuf;
 +      const u8 *req;
 +      size_t req_len;
 +
 +      buf1 = tncs_build_soh_request();
 +      if (buf1 == NULL)
 +              return NULL;
 +
 +      buf = eap_msg_alloc(EAP_VENDOR_MICROSOFT, 0x21, wpabuf_len(buf1),
 +                          EAP_CODE_REQUEST, id);
 +      if (buf == NULL) {
 +              wpabuf_free(buf1);
 +              return NULL;
 +      }
 +      wpabuf_put_buf(buf, buf1);
 +      wpabuf_free(buf1);
 +
 +      req = wpabuf_head(buf);
 +      req_len = wpabuf_len(buf);
 +
 +      wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: Encrypting Phase 2 SOH data",
 +                      req, req_len);
 +
 +      req += sizeof(struct eap_hdr);
 +      req_len -= sizeof(struct eap_hdr);
 +      wpabuf_set(&msgbuf, req, req_len);
 +
 +      encr_req = eap_server_tls_encrypt(sm, &data->ssl, &msgbuf);
 +      wpabuf_free(buf);
 +
 +      return encr_req;
 +}
 +#endif /* EAP_SERVER_TNC */
 +
 +
 +static void eap_peap_get_isk(struct eap_peap_data *data,
 +                           u8 *isk, size_t isk_len)
 +{
 +      size_t key_len;
 +
 +      os_memset(isk, 0, isk_len);
 +      if (data->phase2_key == NULL)
 +              return;
 +
 +      key_len = data->phase2_key_len;
 +      if (key_len > isk_len)
 +              key_len = isk_len;
 +      os_memcpy(isk, data->phase2_key, key_len);
 +}
 +
 +
 +static int eap_peap_derive_cmk(struct eap_sm *sm, struct eap_peap_data *data)
 +{
 +      u8 *tk;
 +      u8 isk[32], imck[60];
 +
 +      /*
 +       * Tunnel key (TK) is the first 60 octets of the key generated by
 +       * phase 1 of PEAP (based on TLS).
 +       */
 +      tk = eap_server_tls_derive_key(sm, &data->ssl, "client EAP encryption",
 +                                     EAP_TLS_KEY_LEN);
 +      if (tk == NULL)
 +              return -1;
 +      wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: TK", tk, 60);
 +
 +      eap_peap_get_isk(data, isk, sizeof(isk));
 +      wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: ISK", isk, sizeof(isk));
 +
 +      /*
 +       * IPMK Seed = "Inner Methods Compound Keys" | ISK
 +       * TempKey = First 40 octets of TK
 +       * IPMK|CMK = PRF+(TempKey, IPMK Seed, 60)
 +       * (note: draft-josefsson-pppext-eap-tls-eap-10.txt includes a space
 +       * in the end of the label just before ISK; is that just a typo?)
 +       */
 +      wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: TempKey", tk, 40);
 +      if (peap_prfplus(data->peap_version, tk, 40,
 +                       "Inner Methods Compound Keys",
 +                       isk, sizeof(isk), imck, sizeof(imck)) < 0) {
 +              os_free(tk);
 +              return -1;
 +      }
 +      wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IMCK (IPMKj)",
 +                      imck, sizeof(imck));
 +
 +      os_free(tk);
 +
 +      /* TODO: fast-connect: IPMK|CMK = TK */
 +      os_memcpy(data->ipmk, imck, 40);
 +      wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IPMK (S-IPMKj)", data->ipmk, 40);
 +      os_memcpy(data->cmk, imck + 40, 20);
 +      wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: CMK (CMKj)", data->cmk, 20);
 +
 +      return 0;
 +}
 +
 +
 +static struct wpabuf * eap_peap_build_phase2_tlv(struct eap_sm *sm,
 +                                               struct eap_peap_data *data,
 +                                               u8 id)
 +{
 +      struct wpabuf *buf, *encr_req;
 +      size_t mlen;
 +
 +      mlen = 6; /* Result TLV */
 +      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);
 +#endif /* EAP_SERVER_TNC */
 +      }
 +
 +      buf = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TLV, mlen,
 +                          EAP_CODE_REQUEST, id);
 +      if (buf == NULL)
 +              return NULL;
 +
 +      wpabuf_put_u8(buf, 0x80); /* Mandatory */
 +      wpabuf_put_u8(buf, EAP_TLV_RESULT_TLV);
 +      /* Length */
 +      wpabuf_put_be16(buf, 2);
 +      /* Status */
 +      wpabuf_put_be16(buf, data->tlv_request == TLV_REQ_SUCCESS ?
 +                      EAP_TLV_RESULT_SUCCESS : EAP_TLV_RESULT_FAILURE);
 +
 +      if (data->peap_version == 0 && data->tlv_request == TLV_REQ_SUCCESS &&
 +          data->crypto_binding != NO_BINDING) {
 +              u8 *mac;
 +              u8 eap_type = EAP_TYPE_PEAP;
 +              const u8 *addr[2];
 +              size_t len[2];
 +              u16 tlv_type;
 +
 +#ifdef EAP_SERVER_TNC
 +              if (data->soh_response) {
 +                      wpa_printf(MSG_DEBUG, "EAP-PEAP: Adding MS-SOH "
 +                                 "Response TLV");
 +                      wpabuf_put_buf(buf, data->soh_response);
 +                      wpabuf_free(data->soh_response);
 +                      data->soh_response = NULL;
 +              }
 +#endif /* EAP_SERVER_TNC */
 +
 +              if (eap_peap_derive_cmk(sm, data) < 0 ||
 +                  random_get_bytes(data->binding_nonce, 32)) {
 +                      wpabuf_free(buf);
 +                      return NULL;
 +              }
 +
 +              /* Compound_MAC: HMAC-SHA1-160(cryptobinding TLV | EAP type) */
 +              addr[0] = wpabuf_put(buf, 0);
 +              len[0] = 60;
 +              addr[1] = &eap_type;
 +              len[1] = 1;
 +
 +              tlv_type = EAP_TLV_CRYPTO_BINDING_TLV;
 +              wpabuf_put_be16(buf, tlv_type);
 +              wpabuf_put_be16(buf, 56);
 +
 +              wpabuf_put_u8(buf, 0); /* Reserved */
 +              wpabuf_put_u8(buf, data->peap_version); /* Version */
 +              wpabuf_put_u8(buf, data->recv_version); /* RecvVersion */
 +              wpabuf_put_u8(buf, 0); /* SubType: 0 = Request, 1 = Response */
 +              wpabuf_put_data(buf, data->binding_nonce, 32); /* Nonce */
 +              mac = wpabuf_put(buf, 20); /* Compound_MAC */
 +              wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC CMK",
 +                          data->cmk, 20);
 +              wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC data 1",
 +                          addr[0], len[0]);
 +              wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC data 2",
 +                          addr[1], len[1]);
 +              hmac_sha1_vector(data->cmk, 20, 2, addr, len, mac);
 +              wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC",
 +                          mac, SHA1_MAC_LEN);
 +              data->crypto_binding_sent = 1;
 +      }
 +
 +      wpa_hexdump_buf_key(MSG_DEBUG, "EAP-PEAP: Encrypting Phase 2 TLV data",
 +                          buf);
 +
 +      encr_req = eap_server_tls_encrypt(sm, &data->ssl, buf);
 +      wpabuf_free(buf);
 +
 +      return encr_req;
 +}
 +
 +
 +static struct wpabuf * eap_peap_build_phase2_term(struct eap_sm *sm,
 +                                                struct eap_peap_data *data,
 +                                                u8 id, int success)
 +{
 +      struct wpabuf *encr_req, msgbuf;
 +      size_t req_len;
 +      struct eap_hdr *hdr;
 +
 +      req_len = sizeof(*hdr);
 +      hdr = os_zalloc(req_len);
 +      if (hdr == NULL)
 +              return NULL;
 +
 +      hdr->code = success ? EAP_CODE_SUCCESS : EAP_CODE_FAILURE;
 +      hdr->identifier = id;
 +      hdr->length = host_to_be16(req_len);
 +
 +      wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: Encrypting Phase 2 data",
 +                      (u8 *) hdr, req_len);
 +
 +      wpabuf_set(&msgbuf, hdr, req_len);
 +      encr_req = eap_server_tls_encrypt(sm, &data->ssl, &msgbuf);
 +      os_free(hdr);
 +
 +      return encr_req;
 +}
 +
 +
 +static struct wpabuf * eap_peap_buildReq(struct eap_sm *sm, void *priv, u8 id)
 +{
 +      struct eap_peap_data *data = priv;
 +
 +      if (data->ssl.state == FRAG_ACK) {
 +              return eap_server_tls_build_ack(id, EAP_TYPE_PEAP,
 +                                              data->peap_version);
 +      }
 +
 +      if (data->ssl.state == WAIT_FRAG_ACK) {
 +              return eap_server_tls_build_msg(&data->ssl, EAP_TYPE_PEAP,
 +                                              data->peap_version, id);
 +      }
 +
 +      switch (data->state) {
 +      case START:
 +              return eap_peap_build_start(sm, data, id);
 +      case PHASE1:
 +      case PHASE1_ID2:
 +              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);
 +              }
 +              break;
 +      case PHASE2_ID:
 +      case PHASE2_METHOD:
 +              wpabuf_free(data->ssl.tls_out);
 +              data->ssl.tls_out_pos = 0;
 +              data->ssl.tls_out = eap_peap_build_phase2_req(sm, data, id);
 +              break;
 +#ifdef EAP_SERVER_TNC
 +      case PHASE2_SOH:
 +              wpabuf_free(data->ssl.tls_out);
 +              data->ssl.tls_out_pos = 0;
 +              data->ssl.tls_out = eap_peap_build_phase2_soh(sm, data, id);
 +              break;
 +#endif /* EAP_SERVER_TNC */
 +      case PHASE2_TLV:
 +              wpabuf_free(data->ssl.tls_out);
 +              data->ssl.tls_out_pos = 0;
 +              data->ssl.tls_out = eap_peap_build_phase2_tlv(sm, data, id);
 +              break;
 +      case SUCCESS_REQ:
 +              wpabuf_free(data->ssl.tls_out);
 +              data->ssl.tls_out_pos = 0;
 +              data->ssl.tls_out = eap_peap_build_phase2_term(sm, data, id,
 +                                                             1);
 +              break;
 +      case FAILURE_REQ:
 +              wpabuf_free(data->ssl.tls_out);
 +              data->ssl.tls_out_pos = 0;
 +              data->ssl.tls_out = eap_peap_build_phase2_term(sm, data, id,
 +                                                             0);
 +              break;
 +      default:
 +              wpa_printf(MSG_DEBUG, "EAP-PEAP: %s - unexpected state %d",
 +                         __func__, data->state);
 +              return NULL;
 +      }
 +
 +      return eap_server_tls_build_msg(&data->ssl, EAP_TYPE_PEAP,
 +                                      data->peap_version, id);
 +}
 +
 +
 +static Boolean eap_peap_check(struct eap_sm *sm, void *priv,
 +                            struct wpabuf *respData)
 +{
 +      const u8 *pos;
 +      size_t len;
 +
 +      pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PEAP, respData, &len);
 +      if (pos == NULL || len < 1) {
 +              wpa_printf(MSG_INFO, "EAP-PEAP: Invalid frame");
 +              return TRUE;
 +      }
 +
 +      return FALSE;
 +}
 +
 +
 +static int eap_peap_phase2_init(struct eap_sm *sm, struct eap_peap_data *data,
-       data->phase2_method = eap_server_get_eap_method(EAP_VENDOR_IETF,
-                                                       eap_type);
++                              int vendor, EapType eap_type)
 +{
 +      if (data->phase2_priv && data->phase2_method) {
 +              data->phase2_method->reset(sm, data->phase2_priv);
 +              data->phase2_method = NULL;
 +              data->phase2_priv = NULL;
 +      }
-                       if (data->tlv_request == TLV_REQ_SUCCESS)
++      data->phase2_method = eap_server_get_eap_method(vendor, eap_type);
 +      if (!data->phase2_method)
 +              return -1;
 +
 +      sm->init_phase2 = 1;
 +      data->phase2_priv = data->phase2_method->init(sm);
 +      sm->init_phase2 = 0;
 +      return 0;
 +}
 +
 +
 +static int eap_tlv_validate_cryptobinding(struct eap_sm *sm,
 +                                        struct eap_peap_data *data,
 +                                        const u8 *crypto_tlv,
 +                                        size_t crypto_tlv_len)
 +{
 +      u8 buf[61], mac[SHA1_MAC_LEN];
 +      const u8 *pos;
 +
 +      if (crypto_tlv_len != 4 + 56) {
 +              wpa_printf(MSG_DEBUG, "EAP-PEAP: Invalid cryptobinding TLV "
 +                         "length %d", (int) crypto_tlv_len);
 +              return -1;
 +      }
 +
 +      pos = crypto_tlv;
 +      pos += 4; /* TLV header */
 +      if (pos[1] != data->peap_version) {
 +              wpa_printf(MSG_DEBUG, "EAP-PEAP: Cryptobinding TLV Version "
 +                         "mismatch (was %d; expected %d)",
 +                         pos[1], data->peap_version);
 +              return -1;
 +      }
 +
 +      if (pos[3] != 1) {
 +              wpa_printf(MSG_DEBUG, "EAP-PEAP: Unexpected Cryptobinding TLV "
 +                         "SubType %d", pos[3]);
 +              return -1;
 +      }
 +      pos += 4;
 +      pos += 32; /* Nonce */
 +
 +      /* Compound_MAC: HMAC-SHA1-160(cryptobinding TLV | EAP type) */
 +      os_memcpy(buf, crypto_tlv, 60);
 +      os_memset(buf + 4 + 4 + 32, 0, 20); /* Compound_MAC */
 +      buf[60] = EAP_TYPE_PEAP;
 +      hmac_sha1(data->cmk, 20, buf, sizeof(buf), mac);
 +
 +      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);
 +              wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Cryptobinding seed data",
 +                          buf, 61);
 +              return -1;
 +      }
 +
 +      wpa_printf(MSG_DEBUG, "EAP-PEAP: Valid cryptobinding TLV received");
 +
 +      return 0;
 +}
 +
 +
 +static void eap_peap_process_phase2_tlv(struct eap_sm *sm,
 +                                      struct eap_peap_data *data,
 +                                      struct wpabuf *in_data)
 +{
 +      const u8 *pos;
 +      size_t left;
 +      const u8 *result_tlv = NULL, *crypto_tlv = NULL;
 +      size_t result_tlv_len = 0, crypto_tlv_len = 0;
 +      int tlv_type, mandatory, tlv_len;
 +
 +      pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_TLV, in_data, &left);
 +      if (pos == NULL) {
 +              wpa_printf(MSG_DEBUG, "EAP-PEAP: Invalid EAP-TLV header");
 +              return;
 +      }
 +
 +      /* Parse TLVs */
 +      wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Received TLVs", pos, left);
 +      while (left >= 4) {
 +              mandatory = !!(pos[0] & 0x80);
 +              tlv_type = pos[0] & 0x3f;
 +              tlv_type = (tlv_type << 8) | pos[1];
 +              tlv_len = ((int) pos[2] << 8) | pos[3];
 +              pos += 4;
 +              left -= 4;
 +              if ((size_t) tlv_len > left) {
 +                      wpa_printf(MSG_DEBUG, "EAP-PEAP: TLV underrun "
 +                                 "(tlv_len=%d left=%lu)", tlv_len,
 +                                 (unsigned long) left);
 +                      eap_peap_state(data, FAILURE);
 +                      return;
 +              }
 +              switch (tlv_type) {
 +              case EAP_TLV_RESULT_TLV:
 +                      result_tlv = pos;
 +                      result_tlv_len = tlv_len;
 +                      break;
 +              case EAP_TLV_CRYPTO_BINDING_TLV:
 +                      crypto_tlv = pos;
 +                      crypto_tlv_len = tlv_len;
 +                      break;
 +              default:
 +                      wpa_printf(MSG_DEBUG, "EAP-PEAP: Unsupported TLV Type "
 +                                 "%d%s", tlv_type,
 +                                 mandatory ? " (mandatory)" : "");
 +                      if (mandatory) {
 +                              eap_peap_state(data, FAILURE);
 +                              return;
 +                      }
 +                      /* Ignore this TLV, but process other TLVs */
 +                      break;
 +              }
 +
 +              pos += tlv_len;
 +              left -= tlv_len;
 +      }
 +      if (left) {
 +              wpa_printf(MSG_DEBUG, "EAP-PEAP: Last TLV too short in "
 +                         "Request (left=%lu)", (unsigned long) left);
 +              eap_peap_state(data, FAILURE);
 +              return;
 +      }
 +
 +      /* Process supported TLVs */
 +      if (crypto_tlv && data->crypto_binding_sent) {
 +              wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Cryptobinding TLV",
 +                          crypto_tlv, crypto_tlv_len);
 +              if (eap_tlv_validate_cryptobinding(sm, data, crypto_tlv - 4,
 +                                                 crypto_tlv_len + 4) < 0) {
 +                      eap_peap_state(data, FAILURE);
 +                      return;
 +              }
 +              data->crypto_binding_used = 1;
 +      } else if (!crypto_tlv && data->crypto_binding_sent &&
 +                 data->crypto_binding == REQUIRE_BINDING) {
 +              wpa_printf(MSG_DEBUG, "EAP-PEAP: No cryptobinding TLV");
 +              eap_peap_state(data, FAILURE);
 +              return;
 +      }
 +
 +      if (result_tlv) {
 +              int status;
 +              const char *requested;
 +
 +              wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Result TLV",
 +                          result_tlv, result_tlv_len);
 +              if (result_tlv_len < 2) {
 +                      wpa_printf(MSG_INFO, "EAP-PEAP: Too short Result TLV "
 +                                 "(len=%lu)",
 +                                 (unsigned long) result_tlv_len);
 +                      eap_peap_state(data, FAILURE);
 +                      return;
 +              }
 +              requested = data->tlv_request == TLV_REQ_SUCCESS ? "Success" :
 +                      "Failure";
 +              status = WPA_GET_BE16(result_tlv);
 +              if (status == EAP_TLV_RESULT_SUCCESS) {
 +                      wpa_printf(MSG_INFO, "EAP-PEAP: TLV Result - Success "
 +                                 "- requested %s", requested);
-                       else
++                      if (data->tlv_request == TLV_REQ_SUCCESS) {
 +                              eap_peap_state(data, SUCCESS);
-       u8 next_type;
++                              eap_peap_valid_session(sm, data);
++                      } else {
 +                              eap_peap_state(data, FAILURE);
++                      }
 +                      
 +              } else if (status == EAP_TLV_RESULT_FAILURE) {
 +                      wpa_printf(MSG_INFO, "EAP-PEAP: TLV Result - Failure "
 +                                 "- requested %s", requested);
 +                      eap_peap_state(data, FAILURE);
 +              } else {
 +                      wpa_printf(MSG_INFO, "EAP-PEAP: Unknown TLV Result "
 +                                 "Status %d", status);
 +                      eap_peap_state(data, FAILURE);
 +              }
 +      }
 +}
 +
 +
 +#ifdef EAP_SERVER_TNC
 +static void eap_peap_process_phase2_soh(struct eap_sm *sm,
 +                                      struct eap_peap_data *data,
 +                                      struct wpabuf *in_data)
 +{
 +      const u8 *pos, *vpos;
 +      size_t left;
 +      const u8 *soh_tlv = NULL;
 +      size_t soh_tlv_len = 0;
 +      int tlv_type, mandatory, tlv_len, vtlv_len;
-       wpa_printf(MSG_DEBUG, "EAP-PEAP: try EAP type %d", next_type);
-       eap_peap_phase2_init(sm, data, next_type);
++      u32 next_type;
 +      u32 vendor_id;
 +
 +      pos = eap_hdr_validate(EAP_VENDOR_MICROSOFT, 0x21, in_data, &left);
 +      if (pos == NULL) {
 +              wpa_printf(MSG_DEBUG, "EAP-PEAP: Not a valid SoH EAP "
 +                         "Extensions Method header - skip TNC");
 +              goto auth_method;
 +      }
 +
 +      /* Parse TLVs */
 +      wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Received TLVs (SoH)", pos, left);
 +      while (left >= 4) {
 +              mandatory = !!(pos[0] & 0x80);
 +              tlv_type = pos[0] & 0x3f;
 +              tlv_type = (tlv_type << 8) | pos[1];
 +              tlv_len = ((int) pos[2] << 8) | pos[3];
 +              pos += 4;
 +              left -= 4;
 +              if ((size_t) tlv_len > left) {
 +                      wpa_printf(MSG_DEBUG, "EAP-PEAP: TLV underrun "
 +                                 "(tlv_len=%d left=%lu)", tlv_len,
 +                                 (unsigned long) left);
 +                      eap_peap_state(data, FAILURE);
 +                      return;
 +              }
 +              switch (tlv_type) {
 +              case EAP_TLV_VENDOR_SPECIFIC_TLV:
 +                      if (tlv_len < 4) {
 +                              wpa_printf(MSG_DEBUG, "EAP-PEAP: Too short "
 +                                         "vendor specific TLV (len=%d)",
 +                                         (int) tlv_len);
 +                              eap_peap_state(data, FAILURE);
 +                              return;
 +                      }
 +
 +                      vendor_id = WPA_GET_BE32(pos);
 +                      if (vendor_id != EAP_VENDOR_MICROSOFT) {
 +                              if (mandatory) {
 +                                      eap_peap_state(data, FAILURE);
 +                                      return;
 +                              }
 +                              break;
 +                      }
 +
 +                      vpos = pos + 4;
 +                      mandatory = !!(vpos[0] & 0x80);
 +                      tlv_type = vpos[0] & 0x3f;
 +                      tlv_type = (tlv_type << 8) | vpos[1];
 +                      vtlv_len = ((int) vpos[2] << 8) | vpos[3];
 +                      vpos += 4;
 +                      if (vpos + vtlv_len > pos + left) {
 +                              wpa_printf(MSG_DEBUG, "EAP-PEAP: Vendor TLV "
 +                                         "underrun");
 +                              eap_peap_state(data, FAILURE);
 +                              return;
 +                      }
 +
 +                      if (tlv_type == 1) {
 +                              soh_tlv = vpos;
 +                              soh_tlv_len = vtlv_len;
 +                              break;
 +                      }
 +
 +                      wpa_printf(MSG_DEBUG, "EAP-PEAP: Unsupported MS-TLV "
 +                                 "Type %d%s", tlv_type,
 +                                 mandatory ? " (mandatory)" : "");
 +                      if (mandatory) {
 +                              eap_peap_state(data, FAILURE);
 +                              return;
 +                      }
 +                      /* Ignore this TLV, but process other TLVs */
 +                      break;
 +              default:
 +                      wpa_printf(MSG_DEBUG, "EAP-PEAP: Unsupported TLV Type "
 +                                 "%d%s", tlv_type,
 +                                 mandatory ? " (mandatory)" : "");
 +                      if (mandatory) {
 +                              eap_peap_state(data, FAILURE);
 +                              return;
 +                      }
 +                      /* Ignore this TLV, but process other TLVs */
 +                      break;
 +              }
 +
 +              pos += tlv_len;
 +              left -= tlv_len;
 +      }
 +      if (left) {
 +              wpa_printf(MSG_DEBUG, "EAP-PEAP: Last TLV too short in "
 +                         "Request (left=%lu)", (unsigned long) left);
 +              eap_peap_state(data, FAILURE);
 +              return;
 +      }
 +
 +      /* Process supported TLVs */
 +      if (soh_tlv) {
 +              int failure = 0;
 +              wpabuf_free(data->soh_response);
 +              data->soh_response = tncs_process_soh(soh_tlv, soh_tlv_len,
 +                                                    &failure);
 +              if (failure) {
 +                      eap_peap_state(data, FAILURE);
 +                      return;
 +              }
 +      } else {
 +              wpa_printf(MSG_DEBUG, "EAP-PEAP: No SoH TLV received");
 +              eap_peap_state(data, FAILURE);
 +              return;
 +      }
 +
 +auth_method:
 +      eap_peap_state(data, PHASE2_METHOD);
 +      next_type = sm->user->methods[0].method;
 +      sm->user_eap_method_index = 1;
-       u8 next_type = EAP_TYPE_NONE;
++      wpa_printf(MSG_DEBUG, "EAP-PEAP: try EAP vendor %d type %d",
++                 sm->user->methods[0].vendor, next_type);
++      eap_peap_phase2_init(sm, data, sm->user->methods[0].vendor, next_type);
 +}
 +#endif /* EAP_SERVER_TNC */
 +
 +
 +static void eap_peap_process_phase2_response(struct eap_sm *sm,
 +                                           struct eap_peap_data *data,
 +                                           struct wpabuf *in_data)
 +{
-                   sm->user->methods[sm->user_eap_method_index].method !=
-                   EAP_TYPE_NONE) {
++      int next_vendor = EAP_VENDOR_IETF;
++      u32 next_type = EAP_TYPE_NONE;
 +      const struct eap_hdr *hdr;
 +      const u8 *pos;
 +      size_t left;
 +
 +      if (data->state == PHASE2_TLV) {
 +              eap_peap_process_phase2_tlv(sm, data, in_data);
 +              return;
 +      }
 +
 +#ifdef EAP_SERVER_TNC
 +      if (data->state == PHASE2_SOH) {
 +              eap_peap_process_phase2_soh(sm, data, in_data);
 +              return;
 +      }
 +#endif /* EAP_SERVER_TNC */
 +
 +      if (data->phase2_priv == NULL) {
 +              wpa_printf(MSG_DEBUG, "EAP-PEAP: %s - Phase2 not "
 +                         "initialized?!", __func__);
 +              return;
 +      }
 +
 +      hdr = wpabuf_head(in_data);
 +      pos = (const u8 *) (hdr + 1);
 +
 +      if (wpabuf_len(in_data) > sizeof(*hdr) && *pos == EAP_TYPE_NAK) {
 +              left = wpabuf_len(in_data) - sizeof(*hdr);
 +              wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Phase2 type Nak'ed; "
 +                          "allowed types", pos + 1, left - 1);
 +              eap_sm_process_nak(sm, pos + 1, left - 1);
 +              if (sm->user && sm->user_eap_method_index < EAP_MAX_METHODS &&
-                       wpa_printf(MSG_DEBUG, "EAP-PEAP: try EAP type %d",
-                                  next_type);
++                  (sm->user->methods[sm->user_eap_method_index].vendor !=
++                   EAP_VENDOR_IETF ||
++                   sm->user->methods[sm->user_eap_method_index].method !=
++                   EAP_TYPE_NONE)) {
++                      next_vendor = sm->user->methods[
++                              sm->user_eap_method_index].vendor;
 +                      next_type = sm->user->methods[
 +                              sm->user_eap_method_index++].method;
-               eap_peap_phase2_init(sm, data, next_type);
++                      wpa_printf(MSG_DEBUG,
++                                 "EAP-PEAP: try EAP vendor %d type 0x%x",
++                                 next_vendor, next_type);
 +              } else {
 +                      eap_peap_req_failure(sm, data);
++                      next_vendor = EAP_VENDOR_IETF;
 +                      next_type = EAP_TYPE_NONE;
 +              }
-               eap_peap_phase2_init(sm, data, next_type);
++              eap_peap_phase2_init(sm, data, next_vendor, next_type);
 +              return;
 +      }
 +
 +      if (data->phase2_method->check(sm, data->phase2_priv, in_data)) {
 +              wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase2 check() asked to "
 +                         "ignore the packet");
 +              return;
 +      }
 +
 +      data->phase2_method->process(sm, data->phase2_priv, in_data);
 +
 +      if (sm->method_pending == METHOD_PENDING_WAIT) {
 +              wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase2 method is in "
 +                         "pending wait state - save decrypted response");
 +              wpabuf_free(data->pending_phase2_resp);
 +              data->pending_phase2_resp = wpabuf_dup(in_data);
 +      }
 +
 +      if (!data->phase2_method->isDone(sm, data->phase2_priv))
 +              return;
 +
 +      if (!data->phase2_method->isSuccess(sm, data->phase2_priv)) {
 +              wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase2 method failed");
 +              eap_peap_req_failure(sm, data);
++              next_vendor = EAP_VENDOR_IETF;
 +              next_type = EAP_TYPE_NONE;
-                       eap_peap_phase2_init(sm, data, EAP_TYPE_NONE);
++              eap_peap_phase2_init(sm, data, next_vendor, next_type);
 +              return;
 +      }
 +
 +      os_free(data->phase2_key);
 +      if (data->phase2_method->getKey) {
 +              data->phase2_key = data->phase2_method->getKey(
 +                      sm, data->phase2_priv, &data->phase2_key_len);
 +              if (data->phase2_key == NULL) {
 +                      wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase2 getKey "
 +                                 "failed");
 +                      eap_peap_req_failure(sm, data);
-               wpa_printf(MSG_DEBUG, "EAP-PEAP: try EAP type %d", next_type);
++                      eap_peap_phase2_init(sm, data, EAP_VENDOR_IETF,
++                                           EAP_TYPE_NONE);
 +                      return;
 +              }
 +      }
 +
 +      switch (data->state) {
 +      case PHASE1_ID2:
 +      case PHASE2_ID:
 +      case PHASE2_SOH:
 +              if (eap_user_get(sm, sm->identity, sm->identity_len, 1) != 0) {
 +                      wpa_hexdump_ascii(MSG_DEBUG, "EAP_PEAP: Phase2 "
 +                                        "Identity not found in the user "
 +                                        "database",
 +                                        sm->identity, sm->identity_len);
 +                      eap_peap_req_failure(sm, data);
++                      next_vendor = EAP_VENDOR_IETF;
 +                      next_type = EAP_TYPE_NONE;
 +                      break;
 +              }
 +
 +#ifdef EAP_SERVER_TNC
 +              if (data->state != PHASE2_SOH && sm->tnc &&
 +                  data->peap_version == 0) {
 +                      eap_peap_state(data, PHASE2_SOH);
 +                      wpa_printf(MSG_DEBUG, "EAP-PEAP: Try to initialize "
 +                                 "TNC (NAP SOH)");
++                      next_vendor = EAP_VENDOR_IETF;
 +                      next_type = EAP_TYPE_NONE;
 +                      break;
 +              }
 +#endif /* EAP_SERVER_TNC */
 +
 +              eap_peap_state(data, PHASE2_METHOD);
++              next_vendor = sm->user->methods[0].vendor;
 +              next_type = sm->user->methods[0].method;
 +              sm->user_eap_method_index = 1;
-       eap_peap_phase2_init(sm, data, next_type);
++              wpa_printf(MSG_DEBUG, "EAP-PEAP: try EAP vendor %d type 0x%x",
++                         next_vendor, next_type);
 +              break;
 +      case PHASE2_METHOD:
 +              eap_peap_req_success(sm, data);
++              next_vendor = EAP_VENDOR_IETF;
 +              next_type = EAP_TYPE_NONE;
 +              break;
 +      case FAILURE:
 +              break;
 +      default:
 +              wpa_printf(MSG_DEBUG, "EAP-PEAP: %s - unexpected state %d",
 +                         __func__, data->state);
 +              break;
 +      }
 +
-               eap_peap_phase2_init(sm, data, EAP_TYPE_IDENTITY);
++      eap_peap_phase2_init(sm, data, next_vendor, next_type);
 +}
 +
 +
 +static void eap_peap_process_phase2(struct eap_sm *sm,
 +                                  struct eap_peap_data *data,
 +                                  const struct wpabuf *respData,
 +                                  struct wpabuf *in_buf)
 +{
 +      struct wpabuf *in_decrypted;
 +      const struct eap_hdr *hdr;
 +      size_t len;
 +
 +      wpa_printf(MSG_DEBUG, "EAP-PEAP: received %lu bytes encrypted data for"
 +                 " Phase 2", (unsigned long) wpabuf_len(in_buf));
 +
 +      if (data->pending_phase2_resp) {
 +              wpa_printf(MSG_DEBUG, "EAP-PEAP: Pending Phase 2 response - "
 +                         "skip decryption and use old data");
 +              eap_peap_process_phase2_response(sm, data,
 +                                               data->pending_phase2_resp);
 +              wpabuf_free(data->pending_phase2_resp);
 +              data->pending_phase2_resp = NULL;
 +              return;
 +      }
 +
 +      in_decrypted = tls_connection_decrypt(sm->ssl_ctx, data->ssl.conn,
 +                                            in_buf);
 +      if (in_decrypted == NULL) {
 +              wpa_printf(MSG_INFO, "EAP-PEAP: Failed to decrypt Phase 2 "
 +                         "data");
 +              eap_peap_state(data, FAILURE);
 +              return;
 +      }
 +
 +      wpa_hexdump_buf_key(MSG_DEBUG, "EAP-PEAP: Decrypted Phase 2 EAP",
 +                          in_decrypted);
 +
 +      if (data->peap_version == 0 && data->state != PHASE2_TLV) {
 +              const struct eap_hdr *resp;
 +              struct eap_hdr *nhdr;
 +              struct wpabuf *nbuf =
 +                      wpabuf_alloc(sizeof(struct eap_hdr) +
 +                                   wpabuf_len(in_decrypted));
 +              if (nbuf == NULL) {
 +                      wpabuf_free(in_decrypted);
 +                      return;
 +              }
 +
 +              resp = wpabuf_head(respData);
 +              nhdr = wpabuf_put(nbuf, sizeof(*nhdr));
 +              nhdr->code = resp->code;
 +              nhdr->identifier = resp->identifier;
 +              nhdr->length = host_to_be16(sizeof(struct eap_hdr) +
 +                                          wpabuf_len(in_decrypted));
 +              wpabuf_put_buf(nbuf, in_decrypted);
 +              wpabuf_free(in_decrypted);
 +
 +              in_decrypted = nbuf;
 +      }
 +
 +      hdr = wpabuf_head(in_decrypted);
 +      if (wpabuf_len(in_decrypted) < (int) sizeof(*hdr)) {
 +              wpa_printf(MSG_INFO, "EAP-PEAP: Too short Phase 2 "
 +                         "EAP frame (len=%lu)",
 +                         (unsigned long) wpabuf_len(in_decrypted));
 +              wpabuf_free(in_decrypted);
 +              eap_peap_req_failure(sm, data);
 +              return;
 +      }
 +      len = be_to_host16(hdr->length);
 +      if (len > wpabuf_len(in_decrypted)) {
 +              wpa_printf(MSG_INFO, "EAP-PEAP: Length mismatch in "
 +                         "Phase 2 EAP frame (len=%lu hdr->length=%lu)",
 +                         (unsigned long) wpabuf_len(in_decrypted),
 +                         (unsigned long) len);
 +              wpabuf_free(in_decrypted);
 +              eap_peap_req_failure(sm, data);
 +              return;
 +      }
 +      wpa_printf(MSG_DEBUG, "EAP-PEAP: received Phase 2: code=%d "
 +                 "identifier=%d length=%lu", hdr->code, hdr->identifier,
 +                 (unsigned long) len);
 +      switch (hdr->code) {
 +      case EAP_CODE_RESPONSE:
 +              eap_peap_process_phase2_response(sm, data, in_decrypted);
 +              break;
 +      case EAP_CODE_SUCCESS:
 +              wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 Success");
 +              if (data->state == SUCCESS_REQ) {
 +                      eap_peap_state(data, SUCCESS);
++                      eap_peap_valid_session(sm, data);
 +              }
 +              break;
 +      case EAP_CODE_FAILURE:
 +              wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 Failure");
 +              eap_peap_state(data, FAILURE);
 +              break;
 +      default:
 +              wpa_printf(MSG_INFO, "EAP-PEAP: Unexpected code=%d in "
 +                         "Phase 2 EAP header", hdr->code);
 +              break;
 +      }
 +
 +      wpabuf_free(in_decrypted);
 +}
 +
 +
 +static int eap_peap_process_version(struct eap_sm *sm, void *priv,
 +                                  int peer_version)
 +{
 +      struct eap_peap_data *data = priv;
 +
 +      data->recv_version = peer_version;
 +      if (data->force_version >= 0 && peer_version != data->force_version) {
 +              wpa_printf(MSG_INFO, "EAP-PEAP: peer did not select the forced"
 +                         " version (forced=%d peer=%d) - reject",
 +                         data->force_version, peer_version);
 +              return -1;
 +      }
 +      if (peer_version < data->peap_version) {
 +              wpa_printf(MSG_DEBUG, "EAP-PEAP: peer ver=%d, own ver=%d; "
 +                         "use version %d",
 +                         peer_version, data->peap_version, peer_version);
 +              data->peap_version = peer_version;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +static void eap_peap_process_msg(struct eap_sm *sm, void *priv,
 +                               const struct wpabuf *respData)
 +{
 +      struct eap_peap_data *data = priv;
 +
 +      switch (data->state) {
 +      case PHASE1:
 +              if (eap_server_tls_phase1(sm, &data->ssl) < 0) {
 +                      eap_peap_state(data, FAILURE);
 +                      break;
 +              }
 +              break;
 +      case PHASE2_START:
 +              eap_peap_state(data, PHASE2_ID);
-                                  eap_peap_process_msg) < 0)
++              eap_peap_phase2_init(sm, data, EAP_VENDOR_IETF,
++                                   EAP_TYPE_IDENTITY);
 +              break;
 +      case PHASE1_ID2:
 +      case PHASE2_ID:
 +      case PHASE2_METHOD:
 +      case PHASE2_SOH:
 +      case PHASE2_TLV:
 +              eap_peap_process_phase2(sm, data, respData, data->ssl.tls_in);
 +              break;
 +      case SUCCESS_REQ:
 +              eap_peap_state(data, SUCCESS);
++              eap_peap_valid_session(sm, data);
 +              break;
 +      case FAILURE_REQ:
 +              eap_peap_state(data, FAILURE);
 +              break;
 +      default:
 +              wpa_printf(MSG_DEBUG, "EAP-PEAP: Unexpected state %d in %s",
 +                         data->state, __func__);
 +              break;
 +      }
 +}
 +
 +
 +static void eap_peap_process(struct eap_sm *sm, void *priv,
 +                           struct wpabuf *respData)
 +{
 +      struct eap_peap_data *data = priv;
++      const struct wpabuf *buf;
++      const u8 *pos;
++      u8 id_len;
++
 +      if (eap_server_tls_process(sm, &data->ssl, respData, data,
 +                                 EAP_TYPE_PEAP, eap_peap_process_version,
++                                 eap_peap_process_msg) < 0) {
 +              eap_peap_state(data, FAILURE);
++              return;
++      }
++
++      if (data->state == SUCCESS ||
++          !tls_connection_established(sm->ssl_ctx, data->ssl.conn) ||
++          !tls_connection_resumed(sm->ssl_ctx, data->ssl.conn))
++              return;
++
++      buf = tls_connection_get_success_data(data->ssl.conn);
++      if (!buf || wpabuf_len(buf) < 2) {
++              wpa_printf(MSG_DEBUG,
++                         "EAP-PEAP: No success data in resumed session - reject attempt");
++              eap_peap_state(data, FAILURE);
++              return;
++      }
++
++      pos = wpabuf_head(buf);
++      if (*pos != EAP_TYPE_PEAP) {
++              wpa_printf(MSG_DEBUG,
++                         "EAP-PEAP: Resumed session for another EAP type (%u) - reject attempt",
++                         *pos);
++              eap_peap_state(data, FAILURE);
++              return;
++      }
++
++      pos++;
++      id_len = *pos++;
++      wpa_hexdump_ascii(MSG_DEBUG, "EAP-PEAP: Identity from cached session",
++                        pos, id_len);
++      os_free(sm->identity);
++      sm->identity = os_malloc(id_len ? id_len : 1);
++      if (!sm->identity) {
++              sm->identity_len = 0;
++              eap_peap_state(data, FAILURE);
++              return;
++      }
++
++      os_memcpy(sm->identity, pos, id_len);
++      sm->identity_len = id_len;
++
++      if (eap_user_get(sm, sm->identity, sm->identity_len, 1) != 0) {
++              wpa_hexdump_ascii(MSG_DEBUG, "EAP-PEAP: Phase2 Identity not found in the user database",
++                                sm->identity, sm->identity_len);
++              eap_peap_state(data, FAILURE);
++              return;
++      }
++
++      wpa_printf(MSG_DEBUG,
++                 "EAP-PEAP: Resuming previous session - skip Phase2");
++      eap_peap_state(data, SUCCESS_REQ);
++      tls_connection_set_success_data_resumed(data->ssl.conn);
 +}
 +
 +
 +static Boolean eap_peap_isDone(struct eap_sm *sm, void *priv)
 +{
 +      struct eap_peap_data *data = priv;
 +      return data->state == SUCCESS || data->state == FAILURE;
 +}
 +
 +
 +static u8 * eap_peap_getKey(struct eap_sm *sm, void *priv, size_t *len)
 +{
 +      struct eap_peap_data *data = priv;
 +      u8 *eapKeyData;
 +
 +      if (data->state != SUCCESS)
 +              return NULL;
 +
 +      if (data->crypto_binding_used) {
 +              u8 csk[128];
 +              /*
 +               * Note: It looks like Microsoft implementation requires null
 +               * termination for this label while the one used for deriving
 +               * IPMK|CMK did not use null termination.
 +               */
 +              if (peap_prfplus(data->peap_version, data->ipmk, 40,
 +                               "Session Key Generating Function",
 +                               (u8 *) "\00", 1, csk, sizeof(csk)) < 0)
 +                      return NULL;
 +              wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: CSK", csk, sizeof(csk));
 +              eapKeyData = os_malloc(EAP_TLS_KEY_LEN);
 +              if (eapKeyData) {
 +                      os_memcpy(eapKeyData, csk, EAP_TLS_KEY_LEN);
 +                      *len = EAP_TLS_KEY_LEN;
 +                      wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Derived key",
 +                                  eapKeyData, EAP_TLS_KEY_LEN);
 +              } else {
 +                      wpa_printf(MSG_DEBUG, "EAP-PEAP: Failed to derive "
 +                                 "key");
 +              }
 +
 +              return eapKeyData;
 +      }
 +
 +      /* TODO: PEAPv1 - different label in some cases */
 +      eapKeyData = eap_server_tls_derive_key(sm, &data->ssl,
 +                                             "client EAP encryption",
 +                                             EAP_TLS_KEY_LEN);
 +      if (eapKeyData) {
 +              *len = EAP_TLS_KEY_LEN;
 +              wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Derived key",
 +                          eapKeyData, EAP_TLS_KEY_LEN);
 +      } else {
 +              wpa_printf(MSG_DEBUG, "EAP-PEAP: Failed to derive key");
 +      }
 +
 +      return eapKeyData;
 +}
 +
 +
 +static Boolean eap_peap_isSuccess(struct eap_sm *sm, void *priv)
 +{
 +      struct eap_peap_data *data = priv;
 +      return data->state == SUCCESS;
 +}
 +
 +
 +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;
 +      int ret;
 +
 +      eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
 +                                    EAP_VENDOR_IETF, EAP_TYPE_PEAP, "PEAP");
 +      if (eap == NULL)
 +              return -1;
 +
 +      eap->init = eap_peap_init;
 +      eap->reset = eap_peap_reset;
 +      eap->buildReq = eap_peap_buildReq;
 +      eap->check = eap_peap_check;
 +      eap->process = eap_peap_process;
 +      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)
 +              eap_server_method_free(eap);
 +      return ret;
 +}
index 943af0d15078de4eebe0709fda1e878f6b109232,0000000000000000000000000000000000000000..cb83ff7305bd0644656d421261ba3a2ea40671df
mode 100644,000000..100644
--- /dev/null
@@@ -1,1077 -1,0 +1,1129 @@@
-       wpabuf_put_u8(data->outbuf, EAP_PWD_PREP_NONE);
 +/*
 + * hostapd / EAP-pwd (RFC 5931) server
 + * Copyright (c) 2010, Dan Harkins <dharkins@lounge.org>
 + *
 + * 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/sha256.h"
++#include "crypto/ms_funcs.h"
 +#include "eap_server/eap_i.h"
 +#include "eap_common/eap_pwd_common.h"
 +
 +
 +struct eap_pwd_data {
 +      enum {
 +              PWD_ID_Req, PWD_Commit_Req, PWD_Confirm_Req, SUCCESS, FAILURE
 +      } state;
 +      u8 *id_peer;
 +      size_t id_peer_len;
 +      u8 *id_server;
 +      size_t id_server_len;
 +      u8 *password;
 +      size_t password_len;
++      int password_hash;
 +      u32 token;
 +      u16 group_num;
 +      EAP_PWD_group *grp;
 +
 +      struct wpabuf *inbuf;
 +      size_t in_frag_pos;
 +      struct wpabuf *outbuf;
 +      size_t out_frag_pos;
 +      size_t mtu;
 +
 +      BIGNUM *k;
 +      BIGNUM *private_value;
 +      BIGNUM *peer_scalar;
 +      BIGNUM *my_scalar;
 +      EC_POINT *my_element;
 +      EC_POINT *peer_element;
 +
 +      u8 my_confirm[SHA256_MAC_LEN];
 +
 +      u8 msk[EAP_MSK_LEN];
 +      u8 emsk[EAP_EMSK_LEN];
 +      u8 session_id[1 + SHA256_MAC_LEN];
 +
 +      BN_CTX *bnctx;
 +};
 +
 +
 +static const char * eap_pwd_state_txt(int state)
 +{
 +      switch (state) {
 +        case PWD_ID_Req:
 +              return "PWD-ID-Req";
 +        case PWD_Commit_Req:
 +              return "PWD-Commit-Req";
 +        case PWD_Confirm_Req:
 +              return "PWD-Confirm-Req";
 +        case SUCCESS:
 +              return "SUCCESS";
 +        case FAILURE:
 +              return "FAILURE";
 +        default:
 +              return "PWD-Unk";
 +      }
 +}
 +
 +
 +static void eap_pwd_state(struct eap_pwd_data *data, int state)
 +{
 +      wpa_printf(MSG_DEBUG, "EAP-pwd: %s -> %s",
 +                 eap_pwd_state_txt(data->state), eap_pwd_state_txt(state));
 +      data->state = state;
 +}
 +
 +
 +static void * eap_pwd_init(struct eap_sm *sm)
 +{
 +      struct eap_pwd_data *data;
 +
 +      if (sm->user == NULL || sm->user->password == NULL ||
 +          sm->user->password_len == 0) {
 +              wpa_printf(MSG_INFO, "EAP-PWD (server): Password is not "
 +                         "configured");
 +              return NULL;
 +      }
 +
 +      data = os_zalloc(sizeof(*data));
 +      if (data == NULL)
 +              return NULL;
 +
 +      data->group_num = sm->pwd_group;
 +      wpa_printf(MSG_DEBUG, "EAP-pwd: Selected group number %d",
 +                 data->group_num);
 +      data->state = PWD_ID_Req;
 +
 +      data->id_server = (u8 *) os_strdup("server");
 +      if (data->id_server)
 +              data->id_server_len = os_strlen((char *) data->id_server);
 +
 +      data->password = os_malloc(sm->user->password_len);
 +      if (data->password == NULL) {
 +              wpa_printf(MSG_INFO, "EAP-PWD: Memory allocation password "
 +                         "fail");
 +              bin_clear_free(data->id_server, data->id_server_len);
 +              os_free(data);
 +              return NULL;
 +      }
 +      data->password_len = sm->user->password_len;
 +      os_memcpy(data->password, sm->user->password, data->password_len);
++      data->password_hash = sm->user->password_hash;
 +
 +      data->bnctx = BN_CTX_new();
 +      if (data->bnctx == NULL) {
 +              wpa_printf(MSG_INFO, "EAP-PWD: bn context allocation fail");
 +              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;
 +      /* use default MTU from RFC 5931 if not configured otherwise */
 +      data->mtu = sm->fragment_size > 0 ? sm->fragment_size : 1020;
 +
 +      return data;
 +}
 +
 +
 +static void eap_pwd_reset(struct eap_sm *sm, void *priv)
 +{
 +      struct eap_pwd_data *data = priv;
 +
 +      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_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_clear_free(data->grp->pwe);
 +              BN_clear_free(data->grp->order);
 +              BN_clear_free(data->grp->prime);
 +              os_free(data->grp);
 +      }
 +      wpabuf_free(data->inbuf);
 +      wpabuf_free(data->outbuf);
 +      bin_clear_free(data, sizeof(*data));
 +}
 +
 +
 +static void eap_pwd_build_id_req(struct eap_sm *sm, struct eap_pwd_data *data,
 +                               u8 id)
 +{
 +      wpa_printf(MSG_DEBUG, "EAP-pwd: ID/Request");
 +      /*
 +       * if we're fragmenting then we already have an id request, just return
 +       */
 +      if (data->out_frag_pos)
 +              return;
 +
 +      data->outbuf = wpabuf_alloc(sizeof(struct eap_pwd_id) +
 +                                  data->id_server_len);
 +      if (data->outbuf == NULL) {
 +              eap_pwd_state(data, FAILURE);
 +              return;
 +      }
 +
 +      /* an lfsr is good enough to generate unpredictable tokens */
 +      data->token = os_random();
 +      wpabuf_put_be16(data->outbuf, data->group_num);
 +      wpabuf_put_u8(data->outbuf, EAP_PWD_DEFAULT_RAND_FUNC);
 +      wpabuf_put_u8(data->outbuf, EAP_PWD_DEFAULT_PRF);
 +      wpabuf_put_data(data->outbuf, &data->token, sizeof(data->token));
-       if (compute_password_element(data->grp, data->group_num,
-                                    data->password, data->password_len,
-                                    data->id_server, data->id_server_len,
-                                    data->id_peer, data->id_peer_len,
-                                    (u8 *) &data->token)) {
++      wpabuf_put_u8(data->outbuf, data->password_hash ? EAP_PWD_PREP_MS :
++                    EAP_PWD_PREP_NONE);
 +      wpabuf_put_data(data->outbuf, data->id_server, data->id_server_len);
 +}
 +
 +
 +static void eap_pwd_build_commit_req(struct eap_sm *sm,
 +                                   struct eap_pwd_data *data, u8 id)
 +{
 +      BIGNUM *mask = NULL, *x = NULL, *y = NULL;
 +      u8 *scalar = NULL, *element = NULL;
 +      u16 offset;
 +
 +      wpa_printf(MSG_DEBUG, "EAP-pwd: Commit/Request");
 +      /*
 +       * if we're fragmenting then we already have an commit request, just
 +       * return
 +       */
 +      if (data->out_frag_pos)
 +              return;
 +
 +      if (((data->private_value = BN_new()) == NULL) ||
 +          ((data->my_element = EC_POINT_new(data->grp->group)) == NULL) ||
 +          ((data->my_scalar = BN_new()) == NULL) ||
 +          ((mask = BN_new()) == NULL)) {
 +              wpa_printf(MSG_INFO, "EAP-PWD (server): scalar allocation "
 +                         "fail");
 +              goto fin;
 +      }
 +
 +      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)) {
 +              wpa_printf(MSG_INFO, "EAP-PWD (server): element allocation "
 +                         "fail");
 +              eap_pwd_state(data, FAILURE);
 +              goto fin;
 +      }
 +
 +      if (!EC_POINT_invert(data->grp->group, data->my_element, data->bnctx))
 +      {
 +              wpa_printf(MSG_INFO, "EAP-PWD (server): element inversion "
 +                         "fail");
 +              goto fin;
 +      }
 +      BN_clear_free(mask);
 +
 +      if (((x = BN_new()) == NULL) ||
 +          ((y = BN_new()) == NULL)) {
 +              wpa_printf(MSG_INFO, "EAP-PWD (server): point allocation "
 +                         "fail");
 +              goto fin;
 +      }
 +      if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
 +                                               data->my_element, x, y,
 +                                               data->bnctx)) {
 +              wpa_printf(MSG_INFO, "EAP-PWD (server): point assignment "
 +                         "fail");
 +              goto fin;
 +      }
 +
 +      if (((scalar = os_malloc(BN_num_bytes(data->grp->order))) == NULL) ||
 +          ((element = os_malloc(BN_num_bytes(data->grp->prime) * 2)) ==
 +           NULL)) {
 +              wpa_printf(MSG_INFO, "EAP-PWD (server): data allocation fail");
 +              goto fin;
 +      }
 +
 +      /*
 +       * bignums occupy as little memory as possible so one that is
 +       * sufficiently smaller than the prime or order might need pre-pending
 +       * with zeros.
 +       */
 +      os_memset(scalar, 0, BN_num_bytes(data->grp->order));
 +      os_memset(element, 0, BN_num_bytes(data->grp->prime) * 2);
 +      offset = BN_num_bytes(data->grp->order) -
 +              BN_num_bytes(data->my_scalar);
 +      BN_bn2bin(data->my_scalar, scalar + offset);
 +
 +      offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x);
 +      BN_bn2bin(x, element + offset);
 +      offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y);
 +      BN_bn2bin(y, element + BN_num_bytes(data->grp->prime) + offset);
 +
 +      data->outbuf = wpabuf_alloc(2 * BN_num_bytes(data->grp->prime) +
 +                                  BN_num_bytes(data->grp->order));
 +      if (data->outbuf == NULL)
 +              goto fin;
 +
 +      /* We send the element as (x,y) followed by the scalar */
 +      wpabuf_put_data(data->outbuf, element,
 +                      2 * BN_num_bytes(data->grp->prime));
 +      wpabuf_put_data(data->outbuf, scalar, BN_num_bytes(data->grp->order));
 +
 +fin:
 +      os_free(scalar);
 +      os_free(element);
 +      BN_clear_free(x);
 +      BN_clear_free(y);
 +      if (data->outbuf == NULL)
 +              eap_pwd_state(data, FAILURE);
 +}
 +
 +
 +static void eap_pwd_build_confirm_req(struct eap_sm *sm,
 +                                    struct eap_pwd_data *data, u8 id)
 +{
 +      BIGNUM *x = NULL, *y = NULL;
 +      struct crypto_hash *hash;
 +      u8 conf[SHA256_MAC_LEN], *cruft = NULL, *ptr;
 +      u16 grp;
 +      int offset;
 +
 +      wpa_printf(MSG_DEBUG, "EAP-pwd: Confirm/Request");
 +      /*
 +       * if we're fragmenting then we already have an confirm request, just
 +       * return
 +       */
 +      if (data->out_frag_pos)
 +              return;
 +
 +      /* Each component of the cruft will be at most as big as the prime */
 +      if (((cruft = os_malloc(BN_num_bytes(data->grp->prime))) == NULL) ||
 +          ((x = BN_new()) == NULL) || ((y = BN_new()) == NULL)) {
 +              wpa_printf(MSG_INFO, "EAP-PWD (server): debug allocation "
 +                         "fail");
 +              goto fin;
 +      }
 +
 +      /*
 +       * commit is H(k | server_element | server_scalar | peer_element |
 +       *             peer_scalar | ciphersuite)
 +       */
 +      hash = eap_pwd_h_init();
 +      if (hash == NULL)
 +              goto fin;
 +
 +      /*
 +       * Zero the memory each time because this is mod prime math and some
 +       * value may start with a few zeros and the previous one did not.
 +       *
 +       * First is k
 +       */
 +      os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
 +      offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(data->k);
 +      BN_bn2bin(data->k, cruft + offset);
 +      eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
 +
 +      /* server element: x, y */
 +      if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
 +                                               data->my_element, x, y,
 +                                               data->bnctx)) {
 +              wpa_printf(MSG_INFO, "EAP-PWD (server): confirm point "
 +                         "assignment fail");
 +              goto fin;
 +      }
 +
 +      os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
 +      offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x);
 +      BN_bn2bin(x, cruft + offset);
 +      eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
 +      os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
 +      offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y);
 +      BN_bn2bin(y, cruft + offset);
 +      eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
 +
 +      /* server scalar */
 +      os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
 +      offset = BN_num_bytes(data->grp->order) -
 +              BN_num_bytes(data->my_scalar);
 +      BN_bn2bin(data->my_scalar, cruft + offset);
 +      eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order));
 +
 +      /* peer element: x, y */
 +      if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
 +                                               data->peer_element, x, y,
 +                                               data->bnctx)) {
 +              wpa_printf(MSG_INFO, "EAP-PWD (server): confirm point "
 +                         "assignment fail");
 +              goto fin;
 +      }
 +
 +      os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
 +      offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x);
 +      BN_bn2bin(x, cruft + offset);
 +      eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
 +      os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
 +      offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y);
 +      BN_bn2bin(y, cruft + offset);
 +      eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
 +
 +      /* peer scalar */
 +      os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
 +      offset = BN_num_bytes(data->grp->order) -
 +              BN_num_bytes(data->peer_scalar);
 +      BN_bn2bin(data->peer_scalar, cruft + offset);
 +      eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order));
 +
 +      /* ciphersuite */
 +      grp = htons(data->group_num);
 +      os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
 +      ptr = cruft;
 +      os_memcpy(ptr, &grp, sizeof(u16));
 +      ptr += sizeof(u16);
 +      *ptr = EAP_PWD_DEFAULT_RAND_FUNC;
 +      ptr += sizeof(u8);
 +      *ptr = EAP_PWD_DEFAULT_PRF;
 +      ptr += sizeof(u8);
 +      eap_pwd_h_update(hash, cruft, ptr - cruft);
 +
 +      /* all done with the random function */
 +      eap_pwd_h_final(hash, conf);
 +      os_memcpy(data->my_confirm, conf, SHA256_MAC_LEN);
 +
 +      data->outbuf = wpabuf_alloc(SHA256_MAC_LEN);
 +      if (data->outbuf == NULL)
 +              goto fin;
 +
 +      wpabuf_put_data(data->outbuf, conf, SHA256_MAC_LEN);
 +
 +fin:
 +      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);
 +}
 +
 +
 +static struct wpabuf *
 +eap_pwd_build_req(struct eap_sm *sm, void *priv, u8 id)
 +{
 +      struct eap_pwd_data *data = priv;
 +      struct wpabuf *req;
 +      u8 lm_exch;
 +      const u8 *buf;
 +      u16 totlen = 0;
 +      size_t len;
 +
 +      /*
 +       * if we're buffering response fragments then just ACK
 +       */
 +      if (data->in_frag_pos) {
 +              wpa_printf(MSG_DEBUG, "EAP-pwd: ACKing a fragment!!");
 +              req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD,
 +                                  EAP_PWD_HDR_SIZE, EAP_CODE_REQUEST, id);
 +              if (req == NULL) {
 +                      eap_pwd_state(data, FAILURE);
 +                      return NULL;
 +              }
 +              switch (data->state) {
 +              case PWD_ID_Req:
 +                      wpabuf_put_u8(req, EAP_PWD_OPCODE_ID_EXCH);
 +                      break;
 +              case PWD_Commit_Req:
 +                      wpabuf_put_u8(req, EAP_PWD_OPCODE_COMMIT_EXCH);
 +                      break;
 +              case PWD_Confirm_Req:
 +                      wpabuf_put_u8(req, EAP_PWD_OPCODE_CONFIRM_EXCH);
 +                      break;
 +              default:
 +                      eap_pwd_state(data, FAILURE);   /* just to be sure */
 +                      wpabuf_free(req);
 +                      return NULL;
 +              }
 +              return req;
 +      }
 +
 +      /*
 +       * build the data portion of a request
 +       */
 +      switch (data->state) {
 +      case PWD_ID_Req:
 +              eap_pwd_build_id_req(sm, data, id);
 +              lm_exch = EAP_PWD_OPCODE_ID_EXCH;
 +              break;
 +      case PWD_Commit_Req:
 +              eap_pwd_build_commit_req(sm, data, id);
 +              lm_exch = EAP_PWD_OPCODE_COMMIT_EXCH;
 +              break;
 +      case PWD_Confirm_Req:
 +              eap_pwd_build_confirm_req(sm, data, id);
 +              lm_exch = EAP_PWD_OPCODE_CONFIRM_EXCH;
 +              break;
 +      default:
 +              wpa_printf(MSG_INFO, "EAP-pwd: Unknown state %d in build_req",
 +                         data->state);
 +              eap_pwd_state(data, FAILURE);
 +              lm_exch = 0;    /* hush now, sweet compiler */
 +              break;
 +      }
 +
 +      if (data->state == FAILURE)
 +              return NULL;
 +
 +      /*
 +       * determine whether that data needs to be fragmented
 +       */
 +      len = wpabuf_len(data->outbuf) - data->out_frag_pos;
 +      if ((len + EAP_PWD_HDR_SIZE) > data->mtu) {
 +              len = data->mtu - EAP_PWD_HDR_SIZE;
 +              EAP_PWD_SET_MORE_BIT(lm_exch);
 +              /*
 +               * if this is the first fragment, need to set the M bit
 +               * and add the total length to the eap_pwd_hdr
 +               */
 +              if (data->out_frag_pos == 0) {
 +                      EAP_PWD_SET_LENGTH_BIT(lm_exch);
 +                      totlen = wpabuf_len(data->outbuf) +
 +                              EAP_PWD_HDR_SIZE + sizeof(u16);
 +                      len -= sizeof(u16);
 +                      wpa_printf(MSG_DEBUG, "EAP-pwd: Fragmenting output, "
 +                                 "total length = %d", totlen);
 +              }
 +              wpa_printf(MSG_DEBUG, "EAP-pwd: Send a %d byte fragment",
 +                         (int) len);
 +      }
 +
 +      /*
 +       * alloc an eap request and populate it with the data
 +       */
 +      req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD,
 +                          EAP_PWD_HDR_SIZE + len +
 +                          (totlen ? sizeof(u16) : 0),
 +                          EAP_CODE_REQUEST, id);
 +      if (req == NULL) {
 +              eap_pwd_state(data, FAILURE);
 +              return NULL;
 +      }
 +
 +      wpabuf_put_u8(req, lm_exch);
 +      if (EAP_PWD_GET_LENGTH_BIT(lm_exch))
 +              wpabuf_put_be16(req, totlen);
 +
 +      buf = wpabuf_head_u8(data->outbuf);
 +      wpabuf_put_data(req, buf + data->out_frag_pos, len);
 +      data->out_frag_pos += len;
 +      /*
 +       * either not fragged or last fragment, either way free up the data
 +       */
 +      if (data->out_frag_pos >= wpabuf_len(data->outbuf)) {
 +              wpabuf_free(data->outbuf);
 +              data->outbuf = NULL;
 +              data->out_frag_pos = 0;
 +      }
 +
 +      return req;
 +}
 +
 +
 +static Boolean eap_pwd_check(struct eap_sm *sm, void *priv,
 +                           struct wpabuf *respData)
 +{
 +      struct eap_pwd_data *data = priv;
 +      const u8 *pos;
 +      size_t len;
 +
 +      pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PWD, respData, &len);
 +      if (pos == NULL || len < 1) {
 +              wpa_printf(MSG_INFO, "EAP-pwd: Invalid frame");
 +              return TRUE;
 +      }
 +
 +      wpa_printf(MSG_DEBUG, "EAP-pwd: Received frame: exch = %d, len = %d",
 +                 EAP_PWD_GET_EXCHANGE(*pos), (int) len);
 +
 +      if (data->state == PWD_ID_Req &&
 +          ((EAP_PWD_GET_EXCHANGE(*pos)) == EAP_PWD_OPCODE_ID_EXCH))
 +              return FALSE;
 +
 +      if (data->state == PWD_Commit_Req &&
 +          ((EAP_PWD_GET_EXCHANGE(*pos)) == EAP_PWD_OPCODE_COMMIT_EXCH))
 +              return FALSE;
 +
 +      if (data->state == PWD_Confirm_Req &&
 +          ((EAP_PWD_GET_EXCHANGE(*pos)) == EAP_PWD_OPCODE_CONFIRM_EXCH))
 +              return FALSE;
 +
 +      wpa_printf(MSG_INFO, "EAP-pwd: Unexpected opcode=%d in state=%d",
 +                 *pos, data->state);
 +
 +      return TRUE;
 +}
 +
 +
 +static void eap_pwd_process_id_resp(struct eap_sm *sm,
 +                                  struct eap_pwd_data *data,
 +                                  const u8 *payload, size_t payload_len)
 +{
 +      struct eap_pwd_id *id;
++      const u8 *password;
++      size_t password_len;
++      u8 pwhashhash[16];
++      int res;
 +
 +      if (payload_len < sizeof(struct eap_pwd_id)) {
 +              wpa_printf(MSG_INFO, "EAP-pwd: Invalid ID response");
 +              return;
 +      }
 +
 +      id = (struct eap_pwd_id *) payload;
 +      if ((data->group_num != be_to_host16(id->group_num)) ||
 +          (id->random_function != EAP_PWD_DEFAULT_RAND_FUNC) ||
 +          (os_memcmp(id->token, (u8 *)&data->token, sizeof(data->token))) ||
 +          (id->prf != EAP_PWD_DEFAULT_PRF)) {
 +              wpa_printf(MSG_INFO, "EAP-pwd: peer changed parameters");
 +              eap_pwd_state(data, FAILURE);
 +              return;
 +      }
 +      data->id_peer = os_malloc(payload_len - sizeof(struct eap_pwd_id));
 +      if (data->id_peer == NULL) {
 +              wpa_printf(MSG_INFO, "EAP-PWD: memory allocation id fail");
 +              return;
 +      }
 +      data->id_peer_len = payload_len - sizeof(struct eap_pwd_id);
 +      os_memcpy(data->id_peer, id->identity, data->id_peer_len);
 +      wpa_hexdump_ascii(MSG_DEBUG, "EAP-PWD (server): peer sent id of",
 +                        data->id_peer, data->id_peer_len);
 +
 +      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;
 +      }
++
++      if (data->password_hash) {
++              res = hash_nt_password_hash(data->password, pwhashhash);
++              if (res)
++                      return;
++              password = pwhashhash;
++              password_len = sizeof(pwhashhash);
++      } else {
++              password = data->password;
++              password_len = data->password_len;
++      }
++
++      res = compute_password_element(data->grp, data->group_num,
++                                     password, password_len,
++                                     data->id_server, data->id_server_len,
++                                     data->id_peer, data->id_peer_len,
++                                     (u8 *) &data->token);
++      os_memset(pwhashhash, 0, sizeof(pwhashhash));
++      if (res) {
 +              wpa_printf(MSG_INFO, "EAP-PWD (server): unable to compute "
 +                         "PWE");
 +              return;
 +      }
 +      wpa_printf(MSG_DEBUG, "EAP-PWD (server): computed %d bit PWE...",
 +                 BN_num_bits(data->grp->prime));
 +
 +      eap_pwd_state(data, PWD_Commit_Req);
 +}
 +
 +
 +static void
 +eap_pwd_process_commit_resp(struct eap_sm *sm, struct eap_pwd_data *data,
 +                          const u8 *payload, size_t payload_len)
 +{
 +      u8 *ptr;
 +      BIGNUM *x = NULL, *y = NULL, *cofactor = NULL;
 +      EC_POINT *K = NULL, *point = NULL;
 +      int res = 0;
++      size_t prime_len, order_len;
 +
 +      wpa_printf(MSG_DEBUG, "EAP-pwd: Received commit response");
 +
++      prime_len = BN_num_bytes(data->grp->prime);
++      order_len = BN_num_bytes(data->grp->order);
++
++      if (payload_len != 2 * prime_len + order_len) {
++              wpa_printf(MSG_INFO,
++                         "EAP-pwd: Unexpected Commit payload length %u (expected %u)",
++                         (unsigned int) payload_len,
++                         (unsigned int) (2 * prime_len + order_len));
++              goto fin;
++      }
++
 +      if (((data->peer_scalar = BN_new()) == NULL) ||
 +          ((data->k = BN_new()) == NULL) ||
 +          ((cofactor = BN_new()) == NULL) ||
 +          ((x = BN_new()) == NULL) ||
 +          ((y = BN_new()) == NULL) ||
 +          ((point = EC_POINT_new(data->grp->group)) == NULL) ||
 +          ((K = EC_POINT_new(data->grp->group)) == NULL) ||
 +          ((data->peer_element = EC_POINT_new(data->grp->group)) == NULL)) {
 +              wpa_printf(MSG_INFO, "EAP-PWD (server): peer data allocation "
 +                         "fail");
 +              goto fin;
 +      }
 +
 +      if (!EC_GROUP_get_cofactor(data->grp->group, cofactor, NULL)) {
 +              wpa_printf(MSG_INFO, "EAP-PWD (server): unable to get "
 +                         "cofactor for curve");
 +              goto fin;
 +      }
 +
 +      /* element, x then y, followed by scalar */
 +      ptr = (u8 *) payload;
 +      BN_bin2bn(ptr, BN_num_bytes(data->grp->prime), x);
 +      ptr += BN_num_bytes(data->grp->prime);
 +      BN_bin2bn(ptr, BN_num_bytes(data->grp->prime), y);
 +      ptr += BN_num_bytes(data->grp->prime);
 +      BN_bin2bn(ptr, BN_num_bytes(data->grp->order), data->peer_scalar);
 +      if (!EC_POINT_set_affine_coordinates_GFp(data->grp->group,
 +                                               data->peer_element, x, y,
 +                                               data->bnctx)) {
 +              wpa_printf(MSG_INFO, "EAP-PWD (server): setting peer element "
 +                         "fail");
 +              goto fin;
 +      }
 +
 +      /* check to ensure peer's element is not in a small sub-group */
 +      if (BN_cmp(cofactor, BN_value_one())) {
 +              if (!EC_POINT_mul(data->grp->group, point, NULL,
 +                                data->peer_element, cofactor, NULL)) {
 +                      wpa_printf(MSG_INFO, "EAP-PWD (server): cannot "
 +                                 "multiply peer element by order");
 +                      goto fin;
 +              }
 +              if (EC_POINT_is_at_infinity(data->grp->group, point)) {
 +                      wpa_printf(MSG_INFO, "EAP-PWD (server): peer element "
 +                                 "is at infinity!\n");
 +                      goto fin;
 +              }
 +      }
 +
 +      /* compute the shared key, k */
 +      if ((!EC_POINT_mul(data->grp->group, K, NULL, data->grp->pwe,
 +                         data->peer_scalar, data->bnctx)) ||
 +          (!EC_POINT_add(data->grp->group, K, K, data->peer_element,
 +                         data->bnctx)) ||
 +          (!EC_POINT_mul(data->grp->group, K, NULL, K, data->private_value,
 +                         data->bnctx))) {
 +              wpa_printf(MSG_INFO, "EAP-PWD (server): computing shared key "
 +                         "fail");
 +              goto fin;
 +      }
 +
 +      /* ensure that the shared key isn't in a small sub-group */
 +      if (BN_cmp(cofactor, BN_value_one())) {
 +              if (!EC_POINT_mul(data->grp->group, K, NULL, K, cofactor,
 +                                NULL)) {
 +                      wpa_printf(MSG_INFO, "EAP-PWD (server): cannot "
 +                                 "multiply shared key point by order!\n");
 +                      goto fin;
 +              }
 +      }
 +
 +      /*
 +       * This check is strictly speaking just for the case above where
 +       * co-factor > 1 but it was suggested that even though this is probably
 +       * never going to happen it is a simple and safe check "just to be
 +       * sure" so let's be safe.
 +       */
 +      if (EC_POINT_is_at_infinity(data->grp->group, K)) {
 +              wpa_printf(MSG_INFO, "EAP-PWD (server): shared key point is "
 +                         "at infinity");
 +              goto fin;
 +      }
 +      if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, K, data->k,
 +                                               NULL, data->bnctx)) {
 +              wpa_printf(MSG_INFO, "EAP-PWD (server): unable to extract "
 +                         "shared secret from secret point");
 +              goto fin;
 +      }
 +      res = 1;
 +
 +fin:
 +      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);
 +      else
 +              eap_pwd_state(data, FAILURE);
 +}
 +
 +
 +static void
 +eap_pwd_process_confirm_resp(struct eap_sm *sm, struct eap_pwd_data *data,
 +                           const u8 *payload, size_t payload_len)
 +{
 +      BIGNUM *x = NULL, *y = NULL;
 +      struct crypto_hash *hash;
 +      u32 cs;
 +      u16 grp;
 +      u8 conf[SHA256_MAC_LEN], *cruft = NULL, *ptr;
 +      int offset;
 +
++      if (payload_len != SHA256_MAC_LEN) {
++              wpa_printf(MSG_INFO,
++                         "EAP-pwd: Unexpected Confirm payload length %u (expected %u)",
++                         (unsigned int) payload_len, SHA256_MAC_LEN);
++              goto fin;
++      }
++
 +      /* build up the ciphersuite: group | random_function | prf */
 +      grp = htons(data->group_num);
 +      ptr = (u8 *) &cs;
 +      os_memcpy(ptr, &grp, sizeof(u16));
 +      ptr += sizeof(u16);
 +      *ptr = EAP_PWD_DEFAULT_RAND_FUNC;
 +      ptr += sizeof(u8);
 +      *ptr = EAP_PWD_DEFAULT_PRF;
 +
 +      /* each component of the cruft will be at most as big as the prime */
 +      if (((cruft = os_malloc(BN_num_bytes(data->grp->prime))) == NULL) ||
 +          ((x = BN_new()) == NULL) || ((y = BN_new()) == NULL)) {
 +              wpa_printf(MSG_INFO, "EAP-PWD (peer): allocation fail");
 +              goto fin;
 +      }
 +
 +      /*
 +       * commit is H(k | peer_element | peer_scalar | server_element |
 +       *             server_scalar | ciphersuite)
 +       */
 +      hash = eap_pwd_h_init();
 +      if (hash == NULL)
 +              goto fin;
 +
 +      /* k */
 +      os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
 +      offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(data->k);
 +      BN_bn2bin(data->k, cruft + offset);
 +      eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
 +
 +      /* peer element: x, y */
 +      if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
 +                                               data->peer_element, x, y,
 +                                               data->bnctx)) {
 +              wpa_printf(MSG_INFO, "EAP-PWD (server): confirm point "
 +                         "assignment fail");
 +              goto fin;
 +      }
 +      os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
 +      offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x);
 +      BN_bn2bin(x, cruft + offset);
 +      eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
 +      os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
 +      offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y);
 +      BN_bn2bin(y, cruft + offset);
 +      eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
 +
 +      /* peer scalar */
 +      os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
 +      offset = BN_num_bytes(data->grp->order) -
 +              BN_num_bytes(data->peer_scalar);
 +      BN_bn2bin(data->peer_scalar, cruft + offset);
 +      eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order));
 +
 +      /* server element: x, y */
 +      if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
 +                                               data->my_element, x, y,
 +                                               data->bnctx)) {
 +              wpa_printf(MSG_INFO, "EAP-PWD (server): confirm point "
 +                         "assignment fail");
 +              goto fin;
 +      }
 +
 +      os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
 +      offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x);
 +      BN_bn2bin(x, cruft + offset);
 +      eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
 +      os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
 +      offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y);
 +      BN_bn2bin(y, cruft + offset);
 +      eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
 +
 +      /* server scalar */
 +      os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
 +      offset = BN_num_bytes(data->grp->order) -
 +              BN_num_bytes(data->my_scalar);
 +      BN_bn2bin(data->my_scalar, cruft + offset);
 +      eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order));
 +
 +      /* ciphersuite */
 +      os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
 +      eap_pwd_h_update(hash, (u8 *) &cs, sizeof(u32));
 +
 +      /* all done */
 +      eap_pwd_h_final(hash, conf);
 +
 +      ptr = (u8 *) payload;
 +      if (os_memcmp_const(conf, ptr, SHA256_MAC_LEN)) {
 +              wpa_printf(MSG_INFO, "EAP-PWD (server): confirm did not "
 +                         "verify");
 +              goto fin;
 +      }
 +
 +      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,
 +                       data->session_id) < 0)
 +              eap_pwd_state(data, FAILURE);
 +      else
 +              eap_pwd_state(data, SUCCESS);
 +
 +fin:
 +      bin_clear_free(cruft, BN_num_bytes(data->grp->prime));
 +      BN_clear_free(x);
 +      BN_clear_free(y);
 +}
 +
 +
 +static void eap_pwd_process(struct eap_sm *sm, void *priv,
 +                          struct wpabuf *respData)
 +{
 +      struct eap_pwd_data *data = priv;
 +      const u8 *pos;
 +      size_t len;
 +      u8 lm_exch;
 +      u16 tot_len;
 +
 +      pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PWD, respData, &len);
 +      if ((pos == NULL) || (len < 1)) {
 +              wpa_printf(MSG_INFO, "Bad EAP header! pos %s and len = %d",
 +                         (pos == NULL) ? "is NULL" : "is not NULL",
 +                         (int) len);
 +              return;
 +      }
 +
 +      lm_exch = *pos;
 +      pos++;            /* skip over the bits and the exch */
 +      len--;
 +
 +      /*
 +       * if we're fragmenting then this should be an ACK with no data,
 +       * just return and continue fragmenting in the "build" section above
 +       */
 +      if (data->out_frag_pos) {
 +              if (len > 1)
 +                      wpa_printf(MSG_INFO, "EAP-pwd: Bad response! "
 +                                 "Fragmenting but not an ACK");
 +              else
 +                      wpa_printf(MSG_DEBUG, "EAP-pwd: received ACK from "
 +                                 "peer");
 +              return;
 +      }
 +      /*
 +       * if we're receiving fragmented packets then we need to buffer...
 +       *
 +       * the first fragment has a total length
 +       */
 +      if (EAP_PWD_GET_LENGTH_BIT(lm_exch)) {
++              if (len < 2) {
++                      wpa_printf(MSG_DEBUG,
++                                 "EAP-pwd: Frame too short to contain Total-Length field");
++                      return;
++              }
 +              tot_len = WPA_GET_BE16(pos);
 +              wpa_printf(MSG_DEBUG, "EAP-pwd: Incoming fragments, total "
 +                         "length = %d", tot_len);
 +              if (tot_len > 15000)
 +                      return;
++              if (data->inbuf) {
++                      wpa_printf(MSG_DEBUG,
++                                 "EAP-pwd: Unexpected new fragment start when previous fragment is still in use");
++                      return;
++              }
 +              data->inbuf = wpabuf_alloc(tot_len);
 +              if (data->inbuf == NULL) {
 +                      wpa_printf(MSG_INFO, "EAP-pwd: Out of memory to "
 +                                 "buffer fragments!");
 +                      return;
 +              }
++              data->in_frag_pos = 0;
 +              pos += sizeof(u16);
 +              len -= sizeof(u16);
 +      }
 +      /*
 +       * the first and all intermediate fragments have the M bit set
 +       */
 +      if (EAP_PWD_GET_MORE_BIT(lm_exch)) {
 +              if ((data->in_frag_pos + len) > wpabuf_size(data->inbuf)) {
 +                      wpa_printf(MSG_DEBUG, "EAP-pwd: Buffer overflow "
 +                                 "attack detected! (%d+%d > %d)",
 +                                 (int) data->in_frag_pos, (int) len,
 +                                 (int) wpabuf_size(data->inbuf));
 +                      eap_pwd_state(data, FAILURE);
 +                      return;
 +              }
 +              wpabuf_put_data(data->inbuf, pos, len);
 +              data->in_frag_pos += len;
 +              wpa_printf(MSG_DEBUG, "EAP-pwd: Got a %d byte fragment",
 +                         (int) len);
 +              return;
 +      }
 +      /*
 +       * last fragment won't have the M bit set (but we're obviously
 +       * buffering fragments so that's how we know it's the last)
 +       */
 +      if (data->in_frag_pos) {
 +              wpabuf_put_data(data->inbuf, pos, len);
 +              data->in_frag_pos += len;
 +              pos = wpabuf_head_u8(data->inbuf);
 +              len = data->in_frag_pos;
 +              wpa_printf(MSG_DEBUG, "EAP-pwd: Last fragment, %d bytes",
 +                         (int) len);
 +      }
 +      switch (EAP_PWD_GET_EXCHANGE(lm_exch)) {
 +      case EAP_PWD_OPCODE_ID_EXCH:
 +              eap_pwd_process_id_resp(sm, data, pos, len);
 +              break;
 +      case EAP_PWD_OPCODE_COMMIT_EXCH:
 +              eap_pwd_process_commit_resp(sm, data, pos, len);
 +              break;
 +      case EAP_PWD_OPCODE_CONFIRM_EXCH:
 +              eap_pwd_process_confirm_resp(sm, data, pos, len);
 +              break;
 +      }
 +      /*
 +       * if we had been buffering fragments, here's a great place
 +       * to clean up
 +       */
 +      if (data->in_frag_pos) {
 +              wpabuf_free(data->inbuf);
 +              data->inbuf = NULL;
 +              data->in_frag_pos = 0;
 +      }
 +}
 +
 +
 +static u8 * eap_pwd_getkey(struct eap_sm *sm, void *priv, size_t *len)
 +{
 +      struct eap_pwd_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_pwd_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
 +{
 +      struct eap_pwd_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_pwd_is_success(struct eap_sm *sm, void *priv)
 +{
 +      struct eap_pwd_data *data = priv;
 +      return data->state == SUCCESS;
 +}
 +
 +
 +static Boolean eap_pwd_is_done(struct eap_sm *sm, void *priv)
 +{
 +      struct eap_pwd_data *data = priv;
 +      return (data->state == SUCCESS) || (data->state == FAILURE);
 +}
 +
 +
 +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;
 +      int ret;
 +      struct timeval tp;
 +      struct timezone tz;
 +      u32 sr;
 +
 +      sr = 0xdeaddada;
 +      (void) gettimeofday(&tp, &tz);
 +      sr ^= (tp.tv_sec ^ tp.tv_usec);
 +      srandom(sr);
 +
 +      eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
 +                                    EAP_VENDOR_IETF, EAP_TYPE_PWD,
 +                                    "PWD");
 +      if (eap == NULL)
 +              return -1;
 +
 +      eap->init = eap_pwd_init;
 +      eap->reset = eap_pwd_reset;
 +      eap->buildReq = eap_pwd_build_req;
 +      eap->check = eap_pwd_check;
 +      eap->process = eap_pwd_process;
 +      eap->isDone = eap_pwd_is_done;
 +      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)
 +              eap_server_method_free(eap);
 +      return ret;
 +}
 +
index 58cfe8ac64a027d415df29d9ce0c0f00729f1d5d,0000000000000000000000000000000000000000..bd18a4ba654ce528fdf135fc9a8aab2893fa8fae
mode 100644,000000..100644
--- /dev/null
@@@ -1,412 -1,0 +1,462 @@@
-       if (eap_server_tls_ssl_init(sm, &data->ssl, 1)) {
 +/*
 + * hostapd / EAP-TLS (RFC 2716)
 + * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
 + *
 + * This software may be distributed under the terms of the BSD license.
 + * See README for more details.
 + */
 +
 +#include "includes.h"
 +
 +#include "common.h"
 +#include "eap_i.h"
 +#include "eap_tls_common.h"
 +#include "crypto/tls.h"
 +
 +
 +static void eap_tls_reset(struct eap_sm *sm, void *priv);
 +
 +
 +struct eap_tls_data {
 +      struct eap_ssl_data ssl;
 +      enum { START, CONTINUE, SUCCESS, FAILURE } state;
 +      int established;
 +      u8 eap_type;
 +};
 +
 +
 +static const char * eap_tls_state_txt(int state)
 +{
 +      switch (state) {
 +      case START:
 +              return "START";
 +      case CONTINUE:
 +              return "CONTINUE";
 +      case SUCCESS:
 +              return "SUCCESS";
 +      case FAILURE:
 +              return "FAILURE";
 +      default:
 +              return "Unknown?!";
 +      }
 +}
 +
 +
 +static void eap_tls_state(struct eap_tls_data *data, int state)
 +{
 +      wpa_printf(MSG_DEBUG, "EAP-TLS: %s -> %s",
 +                 eap_tls_state_txt(data->state),
 +                 eap_tls_state_txt(state));
 +      data->state = state;
++      if (state == FAILURE)
++              tls_connection_remove_session(data->ssl.conn);
++}
++
++
++static void eap_tls_valid_session(struct eap_sm *sm, struct eap_tls_data *data)
++{
++      struct wpabuf *buf;
++
++      if (!sm->tls_session_lifetime)
++              return;
++
++      buf = wpabuf_alloc(1);
++      if (!buf)
++              return;
++      wpabuf_put_u8(buf, data->eap_type);
++      tls_connection_set_success_data(data->ssl.conn, buf);
 +}
 +
 +
 +static void * eap_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)) {
++      if (eap_server_tls_ssl_init(sm, &data->ssl, 1, EAP_TYPE_TLS)) {
 +              wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL.");
 +              eap_tls_reset(sm, data);
 +              return NULL;
 +      }
 +
 +      data->eap_type = EAP_TYPE_TLS;
 +
 +      return data;
 +}
 +
 +
 +#ifdef EAP_SERVER_UNAUTH_TLS
 +static void * eap_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)) {
++      if (eap_server_tls_ssl_init(sm, &data->ssl, 0, EAP_UNAUTH_TLS_TYPE)) {
 +              wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL.");
 +              eap_tls_reset(sm, data);
 +              return NULL;
 +      }
 +
 +      data->eap_type = EAP_UNAUTH_TLS_TYPE;
 +      return data;
 +}
 +#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;
 +
-           0)
++      if (eap_server_tls_ssl_init(sm, &data->ssl, 0,
++                                  EAP_WFA_UNAUTH_TLS_TYPE)) {
 +              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;
 +      if (data == NULL)
 +              return;
 +      eap_server_tls_ssl_deinit(sm, &data->ssl);
 +      os_free(data);
 +}
 +
 +
 +static struct wpabuf * eap_tls_build_start(struct eap_sm *sm,
 +                                         struct eap_tls_data *data, u8 id)
 +{
 +      struct wpabuf *req;
 +
 +      req = eap_tls_msg_alloc(data->eap_type, 1, EAP_CODE_REQUEST, id);
 +      if (req == NULL) {
 +              wpa_printf(MSG_ERROR, "EAP-TLS: Failed to allocate memory for "
 +                         "request");
 +              eap_tls_state(data, FAILURE);
 +              return NULL;
 +      }
 +
 +      wpabuf_put_u8(req, EAP_TLS_FLAGS_START);
 +
 +      eap_tls_state(data, CONTINUE);
 +
 +      return req;
 +}
 +
 +
 +static struct wpabuf * eap_tls_buildReq(struct eap_sm *sm, void *priv, u8 id)
 +{
 +      struct eap_tls_data *data = priv;
 +      struct wpabuf *res;
 +
 +      if (data->ssl.state == FRAG_ACK) {
 +              return eap_server_tls_build_ack(id, data->eap_type, 0);
 +      }
 +
 +      if (data->ssl.state == WAIT_FRAG_ACK) {
 +              res = eap_server_tls_build_msg(&data->ssl, data->eap_type, 0,
 +                                             id);
 +              goto check_established;
 +      }
 +
 +      switch (data->state) {
 +      case START:
 +              return eap_tls_build_start(sm, data, id);
 +      case CONTINUE:
 +              if (tls_connection_established(sm->ssl_ctx, data->ssl.conn))
 +                      data->established = 1;
 +              break;
 +      default:
 +              wpa_printf(MSG_DEBUG, "EAP-TLS: %s - unexpected state %d",
 +                         __func__, data->state);
 +              return NULL;
 +      }
 +
 +      res = eap_server_tls_build_msg(&data->ssl, data->eap_type, 0, id);
 +
 +check_established:
 +      if (data->established && data->ssl.state != WAIT_FRAG_ACK) {
 +              /* TLS handshake has been completed and there are no more
 +               * fragments waiting to be sent out. */
 +              wpa_printf(MSG_DEBUG, "EAP-TLS: Done");
 +              eap_tls_state(data, SUCCESS);
++              eap_tls_valid_session(sm, data);
 +      }
 +
 +      return res;
 +}
 +
 +
 +static Boolean eap_tls_check(struct eap_sm *sm, void *priv,
 +                           struct wpabuf *respData)
 +{
 +      struct eap_tls_data *data = priv;
 +      const u8 *pos;
 +      size_t len;
 +
 +      if (data->eap_type == EAP_UNAUTH_TLS_TYPE)
 +              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);
 +      if (pos == NULL || len < 1) {
 +              wpa_printf(MSG_INFO, "EAP-TLS: Invalid frame");
 +              return TRUE;
 +      }
 +
 +      return FALSE;
 +}
 +
 +
 +static void eap_tls_process_msg(struct eap_sm *sm, void *priv,
 +                              const struct wpabuf *respData)
 +{
 +      struct eap_tls_data *data = priv;
 +      if (data->state == SUCCESS && wpabuf_len(data->ssl.tls_in) == 0) {
 +              wpa_printf(MSG_DEBUG, "EAP-TLS: Client acknowledged final TLS "
 +                         "handshake message");
 +              return;
 +      }
 +      if (eap_server_tls_phase1(sm, &data->ssl) < 0)
 +              eap_tls_state(data, FAILURE);
 +}
 +
 +
 +static void eap_tls_process(struct eap_sm *sm, void *priv,
 +                          struct wpabuf *respData)
 +{
 +      struct eap_tls_data *data = priv;
++      const struct wpabuf *buf;
++      const u8 *pos;
++
 +      if (eap_server_tls_process(sm, &data->ssl, respData, data,
 +                                 data->eap_type, NULL, eap_tls_process_msg) <
++          0) {
 +              eap_tls_state(data, FAILURE);
++              return;
++      }
++
++      if (!tls_connection_established(sm->ssl_ctx, data->ssl.conn) ||
++          !tls_connection_resumed(sm->ssl_ctx, data->ssl.conn))
++              return;
++
++      buf = tls_connection_get_success_data(data->ssl.conn);
++      if (!buf || wpabuf_len(buf) < 1) {
++              wpa_printf(MSG_DEBUG,
++                         "EAP-TLS: No success data in resumed session - reject attempt");
++              eap_tls_state(data, FAILURE);
++              return;
++      }
++
++      pos = wpabuf_head(buf);
++      if (*pos != data->eap_type) {
++              wpa_printf(MSG_DEBUG,
++                         "EAP-TLS: Resumed session for another EAP type (%u) - reject attempt",
++                         *pos);
++              eap_tls_state(data, FAILURE);
++              return;
++      }
++
++      wpa_printf(MSG_DEBUG,
++                 "EAP-TLS: Resuming previous session");
++      eap_tls_state(data, SUCCESS);
++      tls_connection_set_success_data_resumed(data->ssl.conn);
 +}
 +
 +
 +static Boolean eap_tls_isDone(struct eap_sm *sm, void *priv)
 +{
 +      struct eap_tls_data *data = priv;
 +      return data->state == SUCCESS || data->state == FAILURE;
 +}
 +
 +
 +static u8 * eap_tls_getKey(struct eap_sm *sm, void *priv, size_t *len)
 +{
 +      struct eap_tls_data *data = priv;
 +      u8 *eapKeyData;
 +
 +      if (data->state != SUCCESS)
 +              return NULL;
 +
 +      eapKeyData = eap_server_tls_derive_key(sm, &data->ssl,
 +                                             "client EAP encryption",
 +                                             EAP_TLS_KEY_LEN);
 +      if (eapKeyData) {
 +              *len = EAP_TLS_KEY_LEN;
 +              wpa_hexdump(MSG_DEBUG, "EAP-TLS: Derived key",
 +                          eapKeyData, EAP_TLS_KEY_LEN);
 +      } else {
 +              wpa_printf(MSG_DEBUG, "EAP-TLS: Failed to derive key");
 +      }
 +
 +      return eapKeyData;
 +}
 +
 +
 +static u8 * eap_tls_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
 +{
 +      struct eap_tls_data *data = priv;
 +      u8 *eapKeyData, *emsk;
 +
 +      if (data->state != SUCCESS)
 +              return NULL;
 +
 +      eapKeyData = eap_server_tls_derive_key(sm, &data->ssl,
 +                                             "client EAP encryption",
 +                                             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-TLS: Derived EMSK",
 +                          emsk, EAP_EMSK_LEN);
 +      } else {
 +              wpa_printf(MSG_DEBUG, "EAP-TLS: Failed to derive EMSK");
 +      }
 +
 +      return emsk;
 +}
 +
 +
 +static Boolean eap_tls_isSuccess(struct eap_sm *sm, void *priv)
 +{
 +      struct eap_tls_data *data = priv;
 +      return data->state == SUCCESS;
 +}
 +
 +
 +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;
 +      int ret;
 +
 +      eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
 +                                    EAP_VENDOR_IETF, EAP_TYPE_TLS, "TLS");
 +      if (eap == NULL)
 +              return -1;
 +
 +      eap->init = eap_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;
 +      eap->getSessionId = eap_tls_get_session_id;
 +
 +      ret = eap_server_method_register(eap);
 +      if (ret)
 +              eap_server_method_free(eap);
 +      return ret;
 +}
 +
 +
 +#ifdef EAP_SERVER_UNAUTH_TLS
 +int eap_server_unauth_tls_register(void)
 +{
 +      struct eap_method *eap;
 +      int ret;
 +
 +      eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
 +                                    EAP_VENDOR_UNAUTH_TLS,
 +                                    EAP_VENDOR_TYPE_UNAUTH_TLS,
 +                                    "UNAUTH-TLS");
 +      if (eap == NULL)
 +              return -1;
 +
 +      eap->init = eap_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 /* 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 56916c45ac69a000749564b258266d9eddbc5a7f,0000000000000000000000000000000000000000..05677b70e88738d85ee62c78e8352585dd00f920
mode 100644,000000..100644
--- /dev/null
@@@ -1,495 -1,0 +1,480 @@@
-                           int verify_peer)
 +/*
 + * EAP-TLS/PEAP/TTLS/FAST server common functions
 + * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
 + *
 + * This software may be distributed under the terms of the BSD license.
 + * See README for more details.
 + */
 +
 +#include "includes.h"
 +
 +#include "common.h"
 +#include "crypto/sha1.h"
 +#include "crypto/tls.h"
 +#include "eap_i.h"
 +#include "eap_tls_common.h"
 +
 +
 +static void eap_server_tls_free_in_buf(struct eap_ssl_data *data);
 +
 +
 +struct wpabuf * eap_tls_msg_alloc(EapType type, size_t payload_len,
 +                                u8 code, u8 identifier)
 +{
 +      if (type == EAP_UNAUTH_TLS_TYPE)
 +              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,
-       if (tls_connection_set_verify(sm->ssl_ctx, data->conn, verify_peer)) {
++                          int verify_peer, int eap_type)
 +{
++      u8 session_ctx[8];
++      unsigned int flags = 0;
++
 +      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;
 +
 +      data->conn = tls_connection_init(sm->ssl_ctx);
 +      if (data->conn == NULL) {
 +              wpa_printf(MSG_INFO, "SSL: Failed to initialize new TLS "
 +                         "connection");
 +              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 */
 +
-       struct tls_keys keys;
-       u8 *rnd = NULL, *out;
++      if (eap_type != EAP_TYPE_FAST)
++              flags |= TLS_CONN_DISABLE_SESSION_TICKET;
++      os_memcpy(session_ctx, "hostapd", 7);
++      session_ctx[7] = (u8) eap_type;
++      if (tls_connection_set_verify(sm->ssl_ctx, data->conn, verify_peer,
++                                    flags, session_ctx,
++                                    sizeof(session_ctx))) {
 +              wpa_printf(MSG_INFO, "SSL: Failed to configure verification "
 +                         "of TLS peer certificate");
 +              tls_connection_deinit(sm->ssl_ctx, data->conn);
 +              data->conn = NULL;
 +              return -1;
 +      }
 +
 +      data->tls_out_limit = sm->fragment_size > 0 ? sm->fragment_size : 1398;
 +      if (data->phase2) {
 +              /* Limit the fragment size in the inner TLS authentication
 +               * since the outer authentication with EAP-PEAP does not yet
 +               * support fragmentation */
 +              if (data->tls_out_limit > 100)
 +                      data->tls_out_limit -= 100;
 +      }
 +      return 0;
 +}
 +
 +
 +void eap_server_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data)
 +{
 +      tls_connection_deinit(sm->ssl_ctx, data->conn);
 +      eap_server_tls_free_in_buf(data);
 +      wpabuf_free(data->tls_out);
 +      data->tls_out = NULL;
 +}
 +
 +
 +u8 * eap_server_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data,
 +                             char *label, size_t len)
 +{
-       if (tls_connection_prf(sm->ssl_ctx, data->conn, label, 0, out, len) ==
-           0)
-               return out;
-       if (tls_connection_get_keys(sm->ssl_ctx, data->conn, &keys))
-               goto fail;
-       if (keys.client_random == NULL || keys.server_random == NULL ||
-           keys.master_key == NULL)
-               goto fail;
-       rnd = os_malloc(keys.client_random_len + keys.server_random_len);
-       if (rnd == NULL)
-               goto fail;
-       os_memcpy(rnd, keys.client_random, keys.client_random_len);
-       os_memcpy(rnd + keys.client_random_len, keys.server_random,
-                 keys.server_random_len);
-       if (tls_prf_sha1_md5(keys.master_key, keys.master_key_len,
-                            label, rnd, keys.client_random_len +
-                            keys.server_random_len, out, len))
-               goto fail;
++      u8 *out;
 +
 +      out = os_malloc(len);
 +      if (out == NULL)
 +              return NULL;
 +
-       os_free(rnd);
++      if (tls_connection_prf(sm->ssl_ctx, data->conn, label, 0, 0,
++                             out, len)) {
++              os_free(out);
++              return NULL;
++      }
 +
- fail:
-       os_free(out);
-       os_free(rnd);
-       return NULL;
 +      return out;
-       struct tls_keys keys;
 +}
 +
 +
 +/**
 + * 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)
 +{
-       if (tls_connection_get_keys(sm->ssl_ctx, data->conn, &keys))
++      struct tls_random keys;
 +      u8 *out;
 +
++      if (tls_connection_get_random(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)
 +{
 +      struct wpabuf *req;
 +      u8 flags;
 +      size_t send_len, plen;
 +
 +      wpa_printf(MSG_DEBUG, "SSL: Generating Request");
 +      if (data->tls_out == NULL) {
 +              wpa_printf(MSG_ERROR, "SSL: tls_out NULL in %s", __func__);
 +              return NULL;
 +      }
 +
 +      flags = version;
 +      send_len = wpabuf_len(data->tls_out) - data->tls_out_pos;
 +      if (1 + send_len > data->tls_out_limit) {
 +              send_len = data->tls_out_limit - 1;
 +              flags |= EAP_TLS_FLAGS_MORE_FRAGMENTS;
 +              if (data->tls_out_pos == 0) {
 +                      flags |= EAP_TLS_FLAGS_LENGTH_INCLUDED;
 +                      send_len -= 4;
 +              }
 +      }
 +
 +      plen = 1 + send_len;
 +      if (flags & EAP_TLS_FLAGS_LENGTH_INCLUDED)
 +              plen += 4;
 +
 +      req = eap_tls_msg_alloc(eap_type, plen, EAP_CODE_REQUEST, id);
 +      if (req == NULL)
 +              return NULL;
 +
 +      wpabuf_put_u8(req, flags); /* Flags */
 +      if (flags & EAP_TLS_FLAGS_LENGTH_INCLUDED)
 +              wpabuf_put_be32(req, wpabuf_len(data->tls_out));
 +
 +      wpabuf_put_data(req, wpabuf_head_u8(data->tls_out) + data->tls_out_pos,
 +                      send_len);
 +      data->tls_out_pos += send_len;
 +
 +      if (data->tls_out_pos == wpabuf_len(data->tls_out)) {
 +              wpa_printf(MSG_DEBUG, "SSL: Sending out %lu bytes "
 +                         "(message sent completely)",
 +                         (unsigned long) send_len);
 +              wpabuf_free(data->tls_out);
 +              data->tls_out = NULL;
 +              data->tls_out_pos = 0;
 +              data->state = MSG;
 +      } else {
 +              wpa_printf(MSG_DEBUG, "SSL: Sending out %lu bytes "
 +                         "(%lu more to send)", (unsigned long) send_len,
 +                         (unsigned long) wpabuf_len(data->tls_out) -
 +                         data->tls_out_pos);
 +              data->state = WAIT_FRAG_ACK;
 +      }
 +
 +      return req;
 +}
 +
 +
 +struct wpabuf * eap_server_tls_build_ack(u8 id, int eap_type, int version)
 +{
 +      struct wpabuf *req;
 +
 +      req = eap_tls_msg_alloc(eap_type, 1, EAP_CODE_REQUEST, id);
 +      if (req == NULL)
 +              return NULL;
 +      wpa_printf(MSG_DEBUG, "SSL: Building ACK");
 +      wpabuf_put_u8(req, version); /* Flags */
 +      return req;
 +}
 +
 +
 +static int eap_server_tls_process_cont(struct eap_ssl_data *data,
 +                                     const u8 *buf, size_t len)
 +{
 +      /* Process continuation of a pending message */
 +      if (len > wpabuf_tailroom(data->tls_in)) {
 +              wpa_printf(MSG_DEBUG, "SSL: Fragment overflow");
 +              return -1;
 +      }
 +
 +      wpabuf_put_data(data->tls_in, buf, len);
 +      wpa_printf(MSG_DEBUG, "SSL: Received %lu bytes, waiting for %lu "
 +                 "bytes more", (unsigned long) len,
 +                 (unsigned long) wpabuf_tailroom(data->tls_in));
 +
 +      return 0;
 +}
 +
 +
 +static int eap_server_tls_process_fragment(struct eap_ssl_data *data,
 +                                         u8 flags, u32 message_length,
 +                                         const u8 *buf, size_t len)
 +{
 +      /* Process a fragment that is not the last one of the message */
 +      if (data->tls_in == NULL && !(flags & EAP_TLS_FLAGS_LENGTH_INCLUDED)) {
 +              wpa_printf(MSG_DEBUG, "SSL: No Message Length field in a "
 +                         "fragmented packet");
 +              return -1;
 +      }
 +
 +      if (data->tls_in == NULL) {
 +              /* First fragment of the message */
 +
 +              /* Limit length to avoid rogue peers from causing large
 +               * memory allocations. */
 +              if (message_length > 65536) {
 +                      wpa_printf(MSG_INFO, "SSL: Too long TLS fragment (size"
 +                                 " over 64 kB)");
 +                      return -1;
 +              }
 +
 +              if (len > message_length) {
 +                      wpa_printf(MSG_INFO, "SSL: Too much data (%d bytes) in "
 +                                 "first fragment of frame (TLS Message "
 +                                 "Length %d bytes)",
 +                                 (int) len, (int) message_length);
 +                      return -1;
 +              }
 +
 +              data->tls_in = wpabuf_alloc(message_length);
 +              if (data->tls_in == NULL) {
 +                      wpa_printf(MSG_DEBUG, "SSL: No memory for message");
 +                      return -1;
 +              }
 +              wpabuf_put_data(data->tls_in, buf, len);
 +              wpa_printf(MSG_DEBUG, "SSL: Received %lu bytes in first "
 +                         "fragment, waiting for %lu bytes more",
 +                         (unsigned long) len,
 +                         (unsigned long) wpabuf_tailroom(data->tls_in));
 +      }
 +
 +      return 0;
 +}
 +
 +
 +int eap_server_tls_phase1(struct eap_sm *sm, struct eap_ssl_data *data)
 +{
 +      if (data->tls_out) {
 +              /* This should not happen.. */
 +              wpa_printf(MSG_INFO, "SSL: pending tls_out data when "
 +                         "processing new message");
 +              wpabuf_free(data->tls_out);
 +              WPA_ASSERT(data->tls_out == NULL);
 +      }
 +
 +      data->tls_out = tls_connection_server_handshake(sm->ssl_ctx,
 +                                                      data->conn,
 +                                                      data->tls_in, NULL);
 +      if (data->tls_out == NULL) {
 +              wpa_printf(MSG_INFO, "SSL: TLS processing failed");
 +              return -1;
 +      }
 +      if (tls_connection_get_failed(sm->ssl_ctx, data->conn)) {
 +              /* TLS processing has failed - return error */
 +              wpa_printf(MSG_DEBUG, "SSL: Failed - tls_out available to "
 +                         "report error");
 +              return -1;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +static int eap_server_tls_reassemble(struct eap_ssl_data *data, u8 flags,
 +                                   const u8 **pos, size_t *left)
 +{
 +      unsigned int tls_msg_len = 0;
 +      const u8 *end = *pos + *left;
 +
 +      if (flags & EAP_TLS_FLAGS_LENGTH_INCLUDED) {
 +              if (*left < 4) {
 +                      wpa_printf(MSG_INFO, "SSL: Short frame with TLS "
 +                                 "length");
 +                      return -1;
 +              }
 +              tls_msg_len = WPA_GET_BE32(*pos);
 +              wpa_printf(MSG_DEBUG, "SSL: TLS Message Length: %d",
 +                         tls_msg_len);
 +              *pos += 4;
 +              *left -= 4;
 +
 +              if (*left > tls_msg_len) {
 +                      wpa_printf(MSG_INFO, "SSL: TLS Message Length (%d "
 +                                 "bytes) smaller than this fragment (%d "
 +                                 "bytes)", (int) tls_msg_len, (int) *left);
 +                      return -1;
 +              }
 +      }
 +
 +      wpa_printf(MSG_DEBUG, "SSL: Received packet: Flags 0x%x "
 +                 "Message Length %u", flags, tls_msg_len);
 +
 +      if (data->state == WAIT_FRAG_ACK) {
 +              if (*left != 0) {
 +                      wpa_printf(MSG_DEBUG, "SSL: Unexpected payload in "
 +                                 "WAIT_FRAG_ACK state");
 +                      return -1;
 +              }
 +              wpa_printf(MSG_DEBUG, "SSL: Fragment acknowledged");
 +              return 1;
 +      }
 +
 +      if (data->tls_in &&
 +          eap_server_tls_process_cont(data, *pos, end - *pos) < 0)
 +              return -1;
 +              
 +      if (flags & EAP_TLS_FLAGS_MORE_FRAGMENTS) {
 +              if (eap_server_tls_process_fragment(data, flags, tls_msg_len,
 +                                                  *pos, end - *pos) < 0)
 +                      return -1;
 +
 +              data->state = FRAG_ACK;
 +              return 1;
 +      }
 +
 +      if (data->state == FRAG_ACK) {
 +              wpa_printf(MSG_DEBUG, "SSL: All fragments received");
 +              data->state = MSG;
 +      }
 +
 +      if (data->tls_in == NULL) {
 +              /* Wrap unfragmented messages as wpabuf without extra copy */
 +              wpabuf_set(&data->tmpbuf, *pos, end - *pos);
 +              data->tls_in = &data->tmpbuf;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +static void eap_server_tls_free_in_buf(struct eap_ssl_data *data)
 +{
 +      if (data->tls_in != &data->tmpbuf)
 +              wpabuf_free(data->tls_in);
 +      data->tls_in = NULL;
 +}
 +
 +
 +struct wpabuf * eap_server_tls_encrypt(struct eap_sm *sm,
 +                                     struct eap_ssl_data *data,
 +                                     const struct wpabuf *plain)
 +{
 +      struct wpabuf *buf;
 +
 +      buf = tls_connection_encrypt(sm->ssl_ctx, data->conn,
 +                                   plain);
 +      if (buf == NULL) {
 +              wpa_printf(MSG_INFO, "SSL: Failed to encrypt Phase 2 data");
 +              return NULL;
 +      }
 +
 +      return buf;
 +}
 +
 +
 +int eap_server_tls_process(struct eap_sm *sm, struct eap_ssl_data *data,
 +                         struct wpabuf *respData, void *priv, int eap_type,
 +                         int (*proc_version)(struct eap_sm *sm, void *priv,
 +                                             int peer_version),
 +                         void (*proc_msg)(struct eap_sm *sm, void *priv,
 +                                          const struct wpabuf *respData))
 +{
 +      const u8 *pos;
 +      u8 flags;
 +      size_t left;
 +      int ret, res = 0;
 +
 +      if (eap_type == EAP_UNAUTH_TLS_TYPE)
 +              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);
 +      if (pos == NULL || left < 1)
 +              return 0; /* Should not happen - frame already validated */
 +      flags = *pos++;
 +      left--;
 +      wpa_printf(MSG_DEBUG, "SSL: Received packet(len=%lu) - Flags 0x%02x",
 +                 (unsigned long) wpabuf_len(respData), flags);
 +
 +      if (proc_version &&
 +          proc_version(sm, priv, flags & EAP_TLS_VERSION_MASK) < 0)
 +              return -1;
 +
 +      ret = eap_server_tls_reassemble(data, flags, &pos, &left);
 +      if (ret < 0) {
 +              res = -1;
 +              goto done;
 +      } else if (ret == 1)
 +              return 0;
 +
 +      if (proc_msg)
 +              proc_msg(sm, priv, respData);
 +
 +      if (tls_connection_get_write_alerts(sm->ssl_ctx, data->conn) > 1) {
 +              wpa_printf(MSG_INFO, "SSL: Locally detected fatal error in "
 +                         "TLS processing");
 +              res = -1;
 +      }
 +
 +done:
 +      eap_server_tls_free_in_buf(data);
 +
 +      return res;
 +}
index 12a31b07a63b46d9443ed6372080a4f92cf779dc,0000000000000000000000000000000000000000..53ffa1ec6785aec89c1cd316ba02ee1d3f2f55e3
mode 100644,000000..100644
--- /dev/null
@@@ -1,1253 -1,0 +1,1360 @@@
-       if (eap_server_tls_ssl_init(sm, &data->ssl, 0)) {
 +/*
 + * hostapd / EAP-TTLS (RFC 5281)
 + * Copyright (c) 2004-2011, Jouni Malinen <j@w1.fi>
 + *
 + * This software may be distributed under the terms of the BSD license.
 + * See README for more details.
 + */
 +
 +#include "includes.h"
 +
 +#include "common.h"
 +#include "crypto/ms_funcs.h"
 +#include "crypto/sha1.h"
 +#include "crypto/tls.h"
 +#include "eap_server/eap_i.h"
 +#include "eap_server/eap_tls_common.h"
 +#include "eap_common/chap.h"
 +#include "eap_common/eap_ttls.h"
 +
 +
 +#define EAP_TTLS_VERSION 0
 +
 +
 +static void eap_ttls_reset(struct eap_sm *sm, void *priv);
 +
 +
 +struct eap_ttls_data {
 +      struct eap_ssl_data ssl;
 +      enum {
 +              START, PHASE1, PHASE2_START, PHASE2_METHOD,
 +              PHASE2_MSCHAPV2_RESP, SUCCESS, FAILURE
 +      } state;
 +
 +      int ttls_version;
 +      const struct eap_method *phase2_method;
 +      void *phase2_priv;
 +      int mschapv2_resp_ok;
 +      u8 mschapv2_auth_response[20];
 +      u8 mschapv2_ident;
 +      struct wpabuf *pending_phase2_eap_resp;
 +      int tnc_started;
 +};
 +
 +
 +static const char * eap_ttls_state_txt(int state)
 +{
 +      switch (state) {
 +      case START:
 +              return "START";
 +      case PHASE1:
 +              return "PHASE1";
 +      case PHASE2_START:
 +              return "PHASE2_START";
 +      case PHASE2_METHOD:
 +              return "PHASE2_METHOD";
 +      case PHASE2_MSCHAPV2_RESP:
 +              return "PHASE2_MSCHAPV2_RESP";
 +      case SUCCESS:
 +              return "SUCCESS";
 +      case FAILURE:
 +              return "FAILURE";
 +      default:
 +              return "Unknown?!";
 +      }
 +}
 +
 +
 +static void eap_ttls_state(struct eap_ttls_data *data, int state)
 +{
 +      wpa_printf(MSG_DEBUG, "EAP-TTLS: %s -> %s",
 +                 eap_ttls_state_txt(data->state),
 +                 eap_ttls_state_txt(state));
 +      data->state = state;
++      if (state == FAILURE)
++              tls_connection_remove_session(data->ssl.conn);
++}
++
++
++static void eap_ttls_valid_session(struct eap_sm *sm,
++                                 struct eap_ttls_data *data)
++{
++      struct wpabuf *buf;
++
++      if (!sm->tls_session_lifetime)
++              return;
++
++      buf = wpabuf_alloc(1 + 1 + sm->identity_len);
++      if (!buf)
++              return;
++      wpabuf_put_u8(buf, EAP_TYPE_TTLS);
++      if (sm->identity) {
++              u8 id_len;
++
++              if (sm->identity_len <= 255)
++                      id_len = sm->identity_len;
++              else
++                      id_len = 255;
++              wpabuf_put_u8(buf, id_len);
++              wpabuf_put_data(buf, sm->identity, id_len);
++      } else {
++              wpabuf_put_u8(buf, 0);
++      }
++      tls_connection_set_success_data(data->ssl.conn, buf);
 +}
 +
 +
 +static u8 * eap_ttls_avp_hdr(u8 *avphdr, u32 avp_code, u32 vendor_id,
 +                           int mandatory, size_t len)
 +{
 +      struct ttls_avp_vendor *avp;
 +      u8 flags;
 +      size_t hdrlen;
 +
 +      avp = (struct ttls_avp_vendor *) avphdr;
 +      flags = mandatory ? AVP_FLAGS_MANDATORY : 0;
 +      if (vendor_id) {
 +              flags |= AVP_FLAGS_VENDOR;
 +              hdrlen = sizeof(*avp);
 +              avp->vendor_id = host_to_be32(vendor_id);
 +      } else {
 +              hdrlen = sizeof(struct ttls_avp);
 +      }
 +
 +      avp->avp_code = host_to_be32(avp_code);
 +      avp->avp_length = host_to_be32(((u32) flags << 24) |
 +                                     ((u32) (hdrlen + len)));
 +
 +      return avphdr + hdrlen;
 +}
 +
 +
 +static struct wpabuf * eap_ttls_avp_encapsulate(struct wpabuf *resp,
 +                                              u32 avp_code, int mandatory)
 +{
 +      struct wpabuf *avp;
 +      u8 *pos;
 +
 +      avp = wpabuf_alloc(sizeof(struct ttls_avp) + wpabuf_len(resp) + 4);
 +      if (avp == NULL) {
 +              wpabuf_free(resp);
 +              return NULL;
 +      }
 +
 +      pos = eap_ttls_avp_hdr(wpabuf_mhead(avp), avp_code, 0, mandatory,
 +                             wpabuf_len(resp));
 +      os_memcpy(pos, wpabuf_head(resp), wpabuf_len(resp));
 +      pos += wpabuf_len(resp);
 +      AVP_PAD((const u8 *) wpabuf_head(avp), pos);
 +      wpabuf_free(resp);
 +      wpabuf_put(avp, pos - (u8 *) wpabuf_head(avp));
 +      return avp;
 +}
 +
 +
 +struct eap_ttls_avp {
 +       /* Note: eap is allocated memory; caller is responsible for freeing
 +        * it. All the other pointers are pointing to the packet data, i.e.,
 +        * they must not be freed separately. */
 +      u8 *eap;
 +      size_t eap_len;
 +      u8 *user_name;
 +      size_t user_name_len;
 +      u8 *user_password;
 +      size_t user_password_len;
 +      u8 *chap_challenge;
 +      size_t chap_challenge_len;
 +      u8 *chap_password;
 +      size_t chap_password_len;
 +      u8 *mschap_challenge;
 +      size_t mschap_challenge_len;
 +      u8 *mschap_response;
 +      size_t mschap_response_len;
 +      u8 *mschap2_response;
 +      size_t mschap2_response_len;
 +};
 +
 +
 +static int eap_ttls_avp_parse(struct wpabuf *buf, struct eap_ttls_avp *parse)
 +{
 +      struct ttls_avp *avp;
 +      u8 *pos;
 +      int left;
 +
 +      pos = wpabuf_mhead(buf);
 +      left = wpabuf_len(buf);
 +      os_memset(parse, 0, sizeof(*parse));
 +
 +      while (left > 0) {
 +              u32 avp_code, avp_length, vendor_id = 0;
 +              u8 avp_flags, *dpos;
 +              size_t pad, dlen;
 +              avp = (struct ttls_avp *) pos;
 +              avp_code = be_to_host32(avp->avp_code);
 +              avp_length = be_to_host32(avp->avp_length);
 +              avp_flags = (avp_length >> 24) & 0xff;
 +              avp_length &= 0xffffff;
 +              wpa_printf(MSG_DEBUG, "EAP-TTLS: AVP: code=%d flags=0x%02x "
 +                         "length=%d", (int) avp_code, avp_flags,
 +                         (int) avp_length);
 +              if ((int) avp_length > left) {
 +                      wpa_printf(MSG_WARNING, "EAP-TTLS: AVP overflow "
 +                                 "(len=%d, left=%d) - dropped",
 +                                 (int) avp_length, left);
 +                      goto fail;
 +              }
 +              if (avp_length < sizeof(*avp)) {
 +                      wpa_printf(MSG_WARNING, "EAP-TTLS: Invalid AVP length "
 +                                 "%d", avp_length);
 +                      goto fail;
 +              }
 +              dpos = (u8 *) (avp + 1);
 +              dlen = avp_length - sizeof(*avp);
 +              if (avp_flags & AVP_FLAGS_VENDOR) {
 +                      if (dlen < 4) {
 +                              wpa_printf(MSG_WARNING, "EAP-TTLS: vendor AVP "
 +                                         "underflow");
 +                              goto fail;
 +                      }
 +                      vendor_id = be_to_host32(* (be32 *) dpos);
 +                      wpa_printf(MSG_DEBUG, "EAP-TTLS: AVP vendor_id %d",
 +                                 (int) vendor_id);
 +                      dpos += 4;
 +                      dlen -= 4;
 +              }
 +
 +              wpa_hexdump(MSG_DEBUG, "EAP-TTLS: AVP data", dpos, dlen);
 +
 +              if (vendor_id == 0 && avp_code == RADIUS_ATTR_EAP_MESSAGE) {
 +                      wpa_printf(MSG_DEBUG, "EAP-TTLS: AVP - EAP Message");
 +                      if (parse->eap == NULL) {
 +                              parse->eap = os_malloc(dlen);
 +                              if (parse->eap == NULL) {
 +                                      wpa_printf(MSG_WARNING, "EAP-TTLS: "
 +                                                 "failed to allocate memory "
 +                                                 "for Phase 2 EAP data");
 +                                      goto fail;
 +                              }
 +                              os_memcpy(parse->eap, dpos, dlen);
 +                              parse->eap_len = dlen;
 +                      } else {
 +                              u8 *neweap = os_realloc(parse->eap,
 +                                                      parse->eap_len + dlen);
 +                              if (neweap == NULL) {
 +                                      wpa_printf(MSG_WARNING, "EAP-TTLS: "
 +                                                 "failed to allocate memory "
 +                                                 "for Phase 2 EAP data");
 +                                      goto fail;
 +                              }
 +                              os_memcpy(neweap + parse->eap_len, dpos, dlen);
 +                              parse->eap = neweap;
 +                              parse->eap_len += dlen;
 +                      }
 +              } else if (vendor_id == 0 &&
 +                         avp_code == RADIUS_ATTR_USER_NAME) {
 +                      wpa_hexdump_ascii(MSG_DEBUG, "EAP-TTLS: User-Name",
 +                                        dpos, dlen);
 +                      parse->user_name = dpos;
 +                      parse->user_name_len = dlen;
 +              } else if (vendor_id == 0 &&
 +                         avp_code == RADIUS_ATTR_USER_PASSWORD) {
 +                      u8 *password = dpos;
 +                      size_t password_len = dlen;
 +                      while (password_len > 0 &&
 +                             password[password_len - 1] == '\0') {
 +                              password_len--;
 +                      }
 +                      wpa_hexdump_ascii_key(MSG_DEBUG, "EAP-TTLS: "
 +                                            "User-Password (PAP)",
 +                                            password, password_len);
 +                      parse->user_password = password;
 +                      parse->user_password_len = password_len;
 +              } else if (vendor_id == 0 &&
 +                         avp_code == RADIUS_ATTR_CHAP_CHALLENGE) {
 +                      wpa_hexdump(MSG_DEBUG,
 +                                  "EAP-TTLS: CHAP-Challenge (CHAP)",
 +                                  dpos, dlen);
 +                      parse->chap_challenge = dpos;
 +                      parse->chap_challenge_len = dlen;
 +              } else if (vendor_id == 0 &&
 +                         avp_code == RADIUS_ATTR_CHAP_PASSWORD) {
 +                      wpa_hexdump(MSG_DEBUG,
 +                                  "EAP-TTLS: CHAP-Password (CHAP)",
 +                                  dpos, dlen);
 +                      parse->chap_password = dpos;
 +                      parse->chap_password_len = dlen;
 +              } else if (vendor_id == RADIUS_VENDOR_ID_MICROSOFT &&
 +                         avp_code == RADIUS_ATTR_MS_CHAP_CHALLENGE) {
 +                      wpa_hexdump(MSG_DEBUG,
 +                                  "EAP-TTLS: MS-CHAP-Challenge",
 +                                  dpos, dlen);
 +                      parse->mschap_challenge = dpos;
 +                      parse->mschap_challenge_len = dlen;
 +              } else if (vendor_id == RADIUS_VENDOR_ID_MICROSOFT &&
 +                         avp_code == RADIUS_ATTR_MS_CHAP_RESPONSE) {
 +                      wpa_hexdump(MSG_DEBUG,
 +                                  "EAP-TTLS: MS-CHAP-Response (MSCHAP)",
 +                                  dpos, dlen);
 +                      parse->mschap_response = dpos;
 +                      parse->mschap_response_len = dlen;
 +              } else if (vendor_id == RADIUS_VENDOR_ID_MICROSOFT &&
 +                         avp_code == RADIUS_ATTR_MS_CHAP2_RESPONSE) {
 +                      wpa_hexdump(MSG_DEBUG,
 +                                  "EAP-TTLS: MS-CHAP2-Response (MSCHAPV2)",
 +                                  dpos, dlen);
 +                      parse->mschap2_response = dpos;
 +                      parse->mschap2_response_len = dlen;
 +              } else if (avp_flags & AVP_FLAGS_MANDATORY) {
 +                      wpa_printf(MSG_WARNING, "EAP-TTLS: Unsupported "
 +                                 "mandatory AVP code %d vendor_id %d - "
 +                                 "dropped", (int) avp_code, (int) vendor_id);
 +                      goto fail;
 +              } else {
 +                      wpa_printf(MSG_DEBUG, "EAP-TTLS: Ignoring unsupported "
 +                                 "AVP code %d vendor_id %d",
 +                                 (int) avp_code, (int) vendor_id);
 +              }
 +
 +              pad = (4 - (avp_length & 3)) & 3;
 +              pos += avp_length + pad;
 +              left -= avp_length + pad;
 +      }
 +
 +      return 0;
 +
 +fail:
 +      os_free(parse->eap);
 +      parse->eap = NULL;
 +      return -1;
 +}
 +
 +
 +static u8 * eap_ttls_implicit_challenge(struct eap_sm *sm,
 +                                      struct eap_ttls_data *data, size_t len)
 +{
 +      return eap_server_tls_derive_key(sm, &data->ssl, "ttls challenge",
 +                                       len);
 +}
 +
 +
 +static void * eap_ttls_init(struct eap_sm *sm)
 +{
 +      struct eap_ttls_data *data;
 +
 +      data = os_zalloc(sizeof(*data));
 +      if (data == NULL)
 +              return NULL;
 +      data->ttls_version = EAP_TTLS_VERSION;
 +      data->state = START;
 +
-                                  eap_ttls_process_msg) < 0)
++      if (eap_server_tls_ssl_init(sm, &data->ssl, 0, EAP_TYPE_TTLS)) {
 +              wpa_printf(MSG_INFO, "EAP-TTLS: Failed to initialize SSL.");
 +              eap_ttls_reset(sm, data);
 +              return NULL;
 +      }
 +
 +      return data;
 +}
 +
 +
 +static void eap_ttls_reset(struct eap_sm *sm, void *priv)
 +{
 +      struct eap_ttls_data *data = priv;
 +      if (data == NULL)
 +              return;
 +      if (data->phase2_priv && data->phase2_method)
 +              data->phase2_method->reset(sm, data->phase2_priv);
 +      eap_server_tls_ssl_deinit(sm, &data->ssl);
 +      wpabuf_free(data->pending_phase2_eap_resp);
 +      bin_clear_free(data, sizeof(*data));
 +}
 +
 +
 +static struct wpabuf * eap_ttls_build_start(struct eap_sm *sm,
 +                                          struct eap_ttls_data *data, u8 id)
 +{     
 +      struct wpabuf *req;
 +
 +      req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TTLS, 1,
 +                          EAP_CODE_REQUEST, id);
 +      if (req == NULL) {
 +              wpa_printf(MSG_ERROR, "EAP-TTLS: Failed to allocate memory for"
 +                         " request");
 +              eap_ttls_state(data, FAILURE);
 +              return NULL;
 +      }
 +
 +      wpabuf_put_u8(req, EAP_TLS_FLAGS_START | data->ttls_version);
 +
 +      eap_ttls_state(data, PHASE1);
 +
 +      return req;
 +}
 +
 +
 +static struct wpabuf * eap_ttls_build_phase2_eap_req(
 +      struct eap_sm *sm, struct eap_ttls_data *data, u8 id)
 +{
 +      struct wpabuf *buf, *encr_req;
 +
 +
 +      buf = data->phase2_method->buildReq(sm, data->phase2_priv, id);
 +      if (buf == NULL)
 +              return NULL;
 +
 +      wpa_hexdump_buf_key(MSG_DEBUG,
 +                          "EAP-TTLS/EAP: Encapsulate Phase 2 data", buf);
 +
 +      buf = eap_ttls_avp_encapsulate(buf, RADIUS_ATTR_EAP_MESSAGE, 1);
 +      if (buf == NULL) {
 +              wpa_printf(MSG_DEBUG, "EAP-TTLS/EAP: Failed to encapsulate "
 +                         "packet");
 +              return NULL;
 +      }
 +
 +      wpa_hexdump_buf_key(MSG_DEBUG, "EAP-TTLS/EAP: Encrypt encapsulated "
 +                          "Phase 2 data", buf);
 +
 +      encr_req = eap_server_tls_encrypt(sm, &data->ssl, buf);
 +      wpabuf_free(buf);
 +
 +      return encr_req;
 +}
 +
 +
 +static struct wpabuf * eap_ttls_build_phase2_mschapv2(
 +      struct eap_sm *sm, struct eap_ttls_data *data)
 +{
 +      struct wpabuf *encr_req, msgbuf;
 +      u8 *req, *pos, *end;
 +      int ret;
 +
 +      pos = req = os_malloc(100);
 +      if (req == NULL)
 +              return NULL;
 +      end = req + 100;
 +
 +      if (data->mschapv2_resp_ok) {
 +              pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_MS_CHAP2_SUCCESS,
 +                                     RADIUS_VENDOR_ID_MICROSOFT, 1, 43);
 +              *pos++ = data->mschapv2_ident;
 +              ret = os_snprintf((char *) pos, end - pos, "S=");
 +              if (!os_snprintf_error(end - pos, ret))
 +                      pos += ret;
 +              pos += wpa_snprintf_hex_uppercase(
 +                      (char *) pos, end - pos, data->mschapv2_auth_response,
 +                      sizeof(data->mschapv2_auth_response));
 +      } else {
 +              pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_MS_CHAP_ERROR,
 +                                     RADIUS_VENDOR_ID_MICROSOFT, 1, 6);
 +              os_memcpy(pos, "Failed", 6);
 +              pos += 6;
 +              AVP_PAD(req, pos);
 +      }
 +
 +      wpabuf_set(&msgbuf, req, pos - req);
 +      wpa_hexdump_buf_key(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Encrypting Phase 2 "
 +                          "data", &msgbuf);
 +
 +      encr_req = eap_server_tls_encrypt(sm, &data->ssl, &msgbuf);
 +      os_free(req);
 +
 +      return encr_req;
 +}
 +
 +
 +static struct wpabuf * eap_ttls_buildReq(struct eap_sm *sm, void *priv, u8 id)
 +{
 +      struct eap_ttls_data *data = priv;
 +
 +      if (data->ssl.state == FRAG_ACK) {
 +              return eap_server_tls_build_ack(id, EAP_TYPE_TTLS,
 +                                              data->ttls_version);
 +      }
 +
 +      if (data->ssl.state == WAIT_FRAG_ACK) {
 +              return eap_server_tls_build_msg(&data->ssl, EAP_TYPE_TTLS,
 +                                              data->ttls_version, id);
 +      }
 +
 +      switch (data->state) {
 +      case START:
 +              return eap_ttls_build_start(sm, data, id);
 +      case PHASE1:
 +              if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) {
 +                      wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase1 done, "
 +                                 "starting Phase2");
 +                      eap_ttls_state(data, PHASE2_START);
 +              }
 +              break;
 +      case PHASE2_METHOD:
 +              wpabuf_free(data->ssl.tls_out);
 +              data->ssl.tls_out_pos = 0;
 +              data->ssl.tls_out = eap_ttls_build_phase2_eap_req(sm, data,
 +                                                                id);
 +              break;
 +      case PHASE2_MSCHAPV2_RESP:
 +              wpabuf_free(data->ssl.tls_out);
 +              data->ssl.tls_out_pos = 0;
 +              data->ssl.tls_out = eap_ttls_build_phase2_mschapv2(sm, data);
 +              break;
 +      default:
 +              wpa_printf(MSG_DEBUG, "EAP-TTLS: %s - unexpected state %d",
 +                         __func__, data->state);
 +              return NULL;
 +      }
 +
 +      return eap_server_tls_build_msg(&data->ssl, EAP_TYPE_TTLS,
 +                                      data->ttls_version, id);
 +}
 +
 +
 +static Boolean eap_ttls_check(struct eap_sm *sm, void *priv,
 +                            struct wpabuf *respData)
 +{
 +      const u8 *pos;
 +      size_t len;
 +
 +      pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_TTLS, respData, &len);
 +      if (pos == NULL || len < 1) {
 +              wpa_printf(MSG_INFO, "EAP-TTLS: Invalid frame");
 +              return TRUE;
 +      }
 +
 +      return FALSE;
 +}
 +
 +
 +static void eap_ttls_process_phase2_pap(struct eap_sm *sm,
 +                                      struct eap_ttls_data *data,
 +                                      const u8 *user_password,
 +                                      size_t user_password_len)
 +{
 +      if (!sm->user || !sm->user->password || sm->user->password_hash ||
 +          !(sm->user->ttls_auth & EAP_TTLS_AUTH_PAP)) {
 +              wpa_printf(MSG_DEBUG, "EAP-TTLS/PAP: No plaintext user "
 +                         "password configured");
 +              eap_ttls_state(data, FAILURE);
 +              return;
 +      }
 +
 +      if (sm->user->password_len != user_password_len ||
 +          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;
 +      }
 +
 +      wpa_printf(MSG_DEBUG, "EAP-TTLS/PAP: Correct user password");
 +      eap_ttls_state(data, SUCCESS);
++      eap_ttls_valid_session(sm, data);
 +}
 +
 +
 +static void eap_ttls_process_phase2_chap(struct eap_sm *sm,
 +                                       struct eap_ttls_data *data,
 +                                       const u8 *challenge,
 +                                       size_t challenge_len,
 +                                       const u8 *password,
 +                                       size_t password_len)
 +{
 +      u8 *chal, hash[CHAP_MD5_LEN];
 +
 +      if (challenge == NULL || password == NULL ||
 +          challenge_len != EAP_TTLS_CHAP_CHALLENGE_LEN ||
 +          password_len != 1 + EAP_TTLS_CHAP_PASSWORD_LEN) {
 +              wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP: Invalid CHAP attributes "
 +                         "(challenge len %lu password len %lu)",
 +                         (unsigned long) challenge_len,
 +                         (unsigned long) password_len);
 +              eap_ttls_state(data, FAILURE);
 +              return;
 +      }
 +
 +      if (!sm->user || !sm->user->password || sm->user->password_hash ||
 +          !(sm->user->ttls_auth & EAP_TTLS_AUTH_CHAP)) {
 +              wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP: No plaintext user "
 +                         "password configured");
 +              eap_ttls_state(data, FAILURE);
 +              return;
 +      }
 +
 +      chal = eap_ttls_implicit_challenge(sm, data,
 +                                         EAP_TTLS_CHAP_CHALLENGE_LEN + 1);
 +      if (chal == NULL) {
 +              wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP: Failed to generate "
 +                         "challenge from TLS data");
 +              eap_ttls_state(data, FAILURE);
 +              return;
 +      }
 +
 +      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);
 +              eap_ttls_state(data, FAILURE);
 +              return;
 +      }
 +      os_free(chal);
 +
 +      /* MD5(Ident + Password + Challenge) */
 +      chap_md5(password[0], sm->user->password, sm->user->password_len,
 +               challenge, challenge_len, hash);
 +
 +      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);
++              eap_ttls_valid_session(sm, data);
 +      } else {
 +              wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP: Invalid user password");
 +              eap_ttls_state(data, FAILURE);
 +      }
 +}
 +
 +
 +static void eap_ttls_process_phase2_mschap(struct eap_sm *sm,
 +                                         struct eap_ttls_data *data,
 +                                         u8 *challenge, size_t challenge_len,
 +                                         u8 *response, size_t response_len)
 +{
 +      u8 *chal, nt_response[24];
 +
 +      if (challenge == NULL || response == NULL ||
 +          challenge_len != EAP_TTLS_MSCHAP_CHALLENGE_LEN ||
 +          response_len != EAP_TTLS_MSCHAP_RESPONSE_LEN) {
 +              wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP: Invalid MS-CHAP "
 +                         "attributes (challenge len %lu response len %lu)",
 +                         (unsigned long) challenge_len,
 +                         (unsigned long) response_len);
 +              eap_ttls_state(data, FAILURE);
 +              return;
 +      }
 +
 +      if (!sm->user || !sm->user->password ||
 +          !(sm->user->ttls_auth & EAP_TTLS_AUTH_MSCHAP)) {
 +              wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP: No user password "
 +                         "configured");
 +              eap_ttls_state(data, FAILURE);
 +              return;
 +      }
 +
 +      chal = eap_ttls_implicit_challenge(sm, data,
 +                                         EAP_TTLS_MSCHAP_CHALLENGE_LEN + 1);
 +      if (chal == NULL) {
 +              wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP: Failed to generate "
 +                         "challenge from TLS data");
 +              eap_ttls_state(data, FAILURE);
 +              return;
 +      }
 +
++#ifdef CONFIG_TESTING_OPTIONS
++      eap_server_mschap_rx_callback(sm, "TTLS-MSCHAP",
++                                    sm->identity, sm->identity_len,
++                                    challenge, response + 2 + 24);
++#endif /* CONFIG_TESTING_OPTIONS */
++
 +      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);
 +              eap_ttls_state(data, FAILURE);
 +              return;
 +      }
 +      os_free(chal);
 +
 +      if (sm->user->password_hash)
 +              challenge_response(challenge, sm->user->password, nt_response);
 +      else
 +              nt_challenge_response(challenge, sm->user->password,
 +                                    sm->user->password_len, nt_response);
 +
 +      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);
++              eap_ttls_valid_session(sm, data);
 +      } else {
 +              wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP: Invalid NT-Response");
 +              wpa_hexdump(MSG_MSGDUMP, "EAP-TTLS/MSCHAP: Received",
 +                          response + 2 + 24, 24);
 +              wpa_hexdump(MSG_MSGDUMP, "EAP-TTLS/MSCHAP: Expected",
 +                          nt_response, 24);
 +              eap_ttls_state(data, FAILURE);
 +      }
 +}
 +
 +
 +static void eap_ttls_process_phase2_mschapv2(struct eap_sm *sm,
 +                                           struct eap_ttls_data *data,
 +                                           u8 *challenge,
 +                                           size_t challenge_len,
 +                                           u8 *response, size_t response_len)
 +{
 +      u8 *chal, *username, nt_response[24], *rx_resp, *peer_challenge,
 +              *auth_challenge;
 +      size_t username_len, i;
 +
 +      if (challenge == NULL || response == NULL ||
 +          challenge_len != EAP_TTLS_MSCHAPV2_CHALLENGE_LEN ||
 +          response_len != EAP_TTLS_MSCHAPV2_RESPONSE_LEN) {
 +              wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Invalid MS-CHAP2 "
 +                         "attributes (challenge len %lu response len %lu)",
 +                         (unsigned long) challenge_len,
 +                         (unsigned long) response_len);
 +              eap_ttls_state(data, FAILURE);
 +              return;
 +      }
 +
 +      if (!sm->user || !sm->user->password ||
 +          !(sm->user->ttls_auth & EAP_TTLS_AUTH_MSCHAPV2)) {
 +              wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: No user password "
 +                         "configured");
 +              eap_ttls_state(data, FAILURE);
 +              return;
 +      }
 +
 +      if (sm->identity == NULL) {
 +              wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: No user identity "
 +                         "known");
 +              eap_ttls_state(data, FAILURE);
 +              return;
 +      }
 +
 +      /* MSCHAPv2 does not include optional domain name in the
 +       * challenge-response calculation, so remove domain prefix
 +       * (if present). */
 +      username = sm->identity;
 +      username_len = sm->identity_len;
 +      for (i = 0; i < username_len; i++) {
 +              if (username[i] == '\\') {
 +                      username_len -= i + 1;
 +                      username += i + 1;
 +                      break;
 +              }
 +      }
 +
 +      chal = eap_ttls_implicit_challenge(
 +              sm, data, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN + 1);
 +      if (chal == NULL) {
 +              wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Failed to generate "
 +                         "challenge from TLS data");
 +              eap_ttls_state(data, FAILURE);
 +              return;
 +      }
 +
 +      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);
 +              eap_ttls_state(data, FAILURE);
 +              return;
 +      }
 +      os_free(chal);
 +
 +      auth_challenge = challenge;
 +      peer_challenge = response + 2;
 +
 +      wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-TTLS/MSCHAPV2: User",
 +                        username, username_len);
 +      wpa_hexdump(MSG_MSGDUMP, "EAP-TTLS/MSCHAPV2: auth_challenge",
 +                  auth_challenge, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN);
 +      wpa_hexdump(MSG_MSGDUMP, "EAP-TTLS/MSCHAPV2: peer_challenge",
 +                  peer_challenge, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN);
 +
 +      if (sm->user->password_hash) {
 +              generate_nt_response_pwhash(auth_challenge, peer_challenge,
 +                                          username, username_len,
 +                                          sm->user->password,
 +                                          nt_response);
 +      } else {
 +              generate_nt_response(auth_challenge, peer_challenge,
 +                                   username, username_len,
 +                                   sm->user->password,
 +                                   sm->user->password_len,
 +                                   nt_response);
 +      }
 +
 +      rx_resp = response + 2 + EAP_TTLS_MSCHAPV2_CHALLENGE_LEN + 8;
++#ifdef CONFIG_TESTING_OPTIONS
++      {
++              u8 challenge2[8];
++
++              if (challenge_hash(peer_challenge, auth_challenge,
++                                 username, username_len, challenge2) == 0) {
++                      eap_server_mschap_rx_callback(sm, "TTLS-MSCHAPV2",
++                                                    username, username_len,
++                                                    challenge2, rx_resp);
++              }
++      }
++#endif /* CONFIG_TESTING_OPTIONS */
 +      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;
 +
 +              if (sm->user->password_hash) {
 +                      generate_authenticator_response_pwhash(
 +                              sm->user->password,
 +                              peer_challenge, auth_challenge,
 +                              username, username_len, nt_response,
 +                              data->mschapv2_auth_response);
 +              } else {
 +                      generate_authenticator_response(
 +                              sm->user->password, sm->user->password_len,
 +                              peer_challenge, auth_challenge,
 +                              username, username_len, nt_response,
 +                              data->mschapv2_auth_response);
 +              }
 +      } else {
 +              wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Invalid "
 +                         "NT-Response");
 +              wpa_hexdump(MSG_MSGDUMP, "EAP-TTLS/MSCHAPV2: Received",
 +                          rx_resp, 24);
 +              wpa_hexdump(MSG_MSGDUMP, "EAP-TTLS/MSCHAPV2: Expected",
 +                          nt_response, 24);
 +              data->mschapv2_resp_ok = 0;
 +      }
 +      eap_ttls_state(data, PHASE2_MSCHAPV2_RESP);
 +      data->mschapv2_ident = response[0];
 +}
 +
 +
 +static int eap_ttls_phase2_eap_init(struct eap_sm *sm,
 +                                  struct eap_ttls_data *data,
 +                                  EapType eap_type)
 +{
 +      if (data->phase2_priv && data->phase2_method) {
 +              data->phase2_method->reset(sm, data->phase2_priv);
 +              data->phase2_method = NULL;
 +              data->phase2_priv = NULL;
 +      }
 +      data->phase2_method = eap_server_get_eap_method(EAP_VENDOR_IETF,
 +                                                      eap_type);
 +      if (!data->phase2_method)
 +              return -1;
 +
 +      sm->init_phase2 = 1;
 +      data->phase2_priv = data->phase2_method->init(sm);
 +      sm->init_phase2 = 0;
 +      return data->phase2_priv == NULL ? -1 : 0;
 +}
 +
 +
 +static void eap_ttls_process_phase2_eap_response(struct eap_sm *sm,
 +                                               struct eap_ttls_data *data,
 +                                               u8 *in_data, size_t in_len)
 +{
 +      u8 next_type = EAP_TYPE_NONE;
 +      struct eap_hdr *hdr;
 +      u8 *pos;
 +      size_t left;
 +      struct wpabuf buf;
 +      const struct eap_method *m = data->phase2_method;
 +      void *priv = data->phase2_priv;
 +
 +      if (priv == NULL) {
 +              wpa_printf(MSG_DEBUG, "EAP-TTLS/EAP: %s - Phase2 not "
 +                         "initialized?!", __func__);
 +              return;
 +      }
 +
 +      hdr = (struct eap_hdr *) in_data;
 +      pos = (u8 *) (hdr + 1);
 +
 +      if (in_len > sizeof(*hdr) && *pos == EAP_TYPE_NAK) {
 +              left = in_len - sizeof(*hdr);
 +              wpa_hexdump(MSG_DEBUG, "EAP-TTLS/EAP: Phase2 type Nak'ed; "
 +                          "allowed types", pos + 1, left - 1);
 +              eap_sm_process_nak(sm, pos + 1, left - 1);
 +              if (sm->user && sm->user_eap_method_index < EAP_MAX_METHODS &&
 +                  sm->user->methods[sm->user_eap_method_index].method !=
 +                  EAP_TYPE_NONE) {
 +                      next_type = sm->user->methods[
 +                              sm->user_eap_method_index++].method;
 +                      wpa_printf(MSG_DEBUG, "EAP-TTLS: try EAP type %d",
 +                                 next_type);
 +                      if (eap_ttls_phase2_eap_init(sm, data, next_type)) {
 +                              wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to "
 +                                         "initialize EAP type %d",
 +                                         next_type);
 +                              eap_ttls_state(data, FAILURE);
 +                              return;
 +                      }
 +              } else {
 +                      eap_ttls_state(data, FAILURE);
 +              }
 +              return;
 +      }
 +
 +      wpabuf_set(&buf, in_data, in_len);
 +
 +      if (m->check(sm, priv, &buf)) {
 +              wpa_printf(MSG_DEBUG, "EAP-TTLS/EAP: Phase2 check() asked to "
 +                         "ignore the packet");
 +              return;
 +      }
 +
 +      m->process(sm, priv, &buf);
 +
 +      if (sm->method_pending == METHOD_PENDING_WAIT) {
 +              wpa_printf(MSG_DEBUG, "EAP-TTLS/EAP: Phase2 method is in "
 +                         "pending wait state - save decrypted response");
 +              wpabuf_free(data->pending_phase2_eap_resp);
 +              data->pending_phase2_eap_resp = wpabuf_dup(&buf);
 +      }
 +
 +      if (!m->isDone(sm, priv))
 +              return;
 +
 +      if (!m->isSuccess(sm, priv)) {
 +              wpa_printf(MSG_DEBUG, "EAP-TTLS/EAP: Phase2 method failed");
 +              eap_ttls_state(data, FAILURE);
 +              return;
 +      }
 +
 +      switch (data->state) {
 +      case PHASE2_START:
 +              if (eap_user_get(sm, sm->identity, sm->identity_len, 1) != 0) {
 +                      wpa_hexdump_ascii(MSG_DEBUG, "EAP_TTLS: Phase2 "
 +                                        "Identity not found in the user "
 +                                        "database",
 +                                        sm->identity, sm->identity_len);
 +                      eap_ttls_state(data, FAILURE);
 +                      break;
 +              }
 +
 +              eap_ttls_state(data, PHASE2_METHOD);
 +              next_type = sm->user->methods[0].method;
 +              sm->user_eap_method_index = 1;
 +              wpa_printf(MSG_DEBUG, "EAP-TTLS: try EAP type %d", next_type);
 +              if (eap_ttls_phase2_eap_init(sm, data, next_type)) {
 +                      wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to initialize "
 +                                 "EAP type %d", next_type);
 +                      eap_ttls_state(data, FAILURE);
 +              }
 +              break;
 +      case PHASE2_METHOD:
 +              eap_ttls_state(data, SUCCESS);
++              eap_ttls_valid_session(sm, data);
 +              break;
 +      case FAILURE:
 +              break;
 +      default:
 +              wpa_printf(MSG_DEBUG, "EAP-TTLS: %s - unexpected state %d",
 +                         __func__, data->state);
 +              break;
 +      }
 +}
 +
 +
 +static void eap_ttls_process_phase2_eap(struct eap_sm *sm,
 +                                      struct eap_ttls_data *data,
 +                                      const u8 *eap, size_t eap_len)
 +{
 +      struct eap_hdr *hdr;
 +      size_t len;
 +
 +      if (data->state == PHASE2_START) {
 +              wpa_printf(MSG_DEBUG, "EAP-TTLS/EAP: initializing Phase 2");
 +              if (eap_ttls_phase2_eap_init(sm, data, EAP_TYPE_IDENTITY) < 0)
 +              {
 +                      wpa_printf(MSG_DEBUG, "EAP-TTLS/EAP: failed to "
 +                                 "initialize EAP-Identity");
 +                      return;
 +              }
 +      }
 +
 +      if (eap_len < sizeof(*hdr)) {
 +              wpa_printf(MSG_DEBUG, "EAP-TTLS/EAP: too short Phase 2 EAP "
 +                         "packet (len=%lu)", (unsigned long) eap_len);
 +              return;
 +      }
 +
 +      hdr = (struct eap_hdr *) eap;
 +      len = be_to_host16(hdr->length);
 +      wpa_printf(MSG_DEBUG, "EAP-TTLS/EAP: received Phase 2 EAP: code=%d "
 +                 "identifier=%d length=%lu", hdr->code, hdr->identifier,
 +                 (unsigned long) len);
 +      if (len > eap_len) {
 +              wpa_printf(MSG_INFO, "EAP-TTLS/EAP: Length mismatch in Phase 2"
 +                         " EAP frame (hdr len=%lu, data len in AVP=%lu)",
 +                         (unsigned long) len, (unsigned long) eap_len);
 +              return;
 +      }
 +
 +      switch (hdr->code) {
 +      case EAP_CODE_RESPONSE:
 +              eap_ttls_process_phase2_eap_response(sm, data, (u8 *) hdr,
 +                                                   len);
 +              break;
 +      default:
 +              wpa_printf(MSG_INFO, "EAP-TTLS/EAP: Unexpected code=%d in "
 +                         "Phase 2 EAP header", hdr->code);
 +              break;
 +      }
 +}
 +
 +
 +static void eap_ttls_process_phase2(struct eap_sm *sm,
 +                                  struct eap_ttls_data *data,
 +                                  struct wpabuf *in_buf)
 +{
 +      struct wpabuf *in_decrypted;
 +      struct eap_ttls_avp parse;
 +
 +      wpa_printf(MSG_DEBUG, "EAP-TTLS: received %lu bytes encrypted data for"
 +                 " Phase 2", (unsigned long) wpabuf_len(in_buf));
 +
 +      if (data->pending_phase2_eap_resp) {
 +              wpa_printf(MSG_DEBUG, "EAP-TTLS: Pending Phase 2 EAP response "
 +                         "- skip decryption and use old data");
 +              eap_ttls_process_phase2_eap(
 +                      sm, data, wpabuf_head(data->pending_phase2_eap_resp),
 +                      wpabuf_len(data->pending_phase2_eap_resp));
 +              wpabuf_free(data->pending_phase2_eap_resp);
 +              data->pending_phase2_eap_resp = NULL;
 +              return;
 +      }
 +
 +      in_decrypted = tls_connection_decrypt(sm->ssl_ctx, data->ssl.conn,
 +                                            in_buf);
 +      if (in_decrypted == NULL) {
 +              wpa_printf(MSG_INFO, "EAP-TTLS: Failed to decrypt Phase 2 "
 +                         "data");
 +              eap_ttls_state(data, FAILURE);
 +              return;
 +      }
 +
 +      wpa_hexdump_buf_key(MSG_DEBUG, "EAP-TTLS: Decrypted Phase 2 EAP",
 +                          in_decrypted);
 +
 +      if (eap_ttls_avp_parse(in_decrypted, &parse) < 0) {
 +              wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to parse AVPs");
 +              wpabuf_free(in_decrypted);
 +              eap_ttls_state(data, FAILURE);
 +              return;
 +      }
 +
 +      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) {
 +                      eap_ttls_state(data, FAILURE);
 +                      goto done;
 +              }
 +              os_memcpy(sm->identity, parse.user_name, parse.user_name_len);
 +              sm->identity_len = parse.user_name_len;
 +              if (eap_user_get(sm, parse.user_name, parse.user_name_len, 1)
 +                  != 0) {
 +                      wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase2 Identity not "
 +                                 "found in the user database");
 +                      eap_ttls_state(data, FAILURE);
 +                      goto done;
 +              }
 +      }
 +
 +#ifdef EAP_SERVER_TNC
 +      if (data->tnc_started && parse.eap == NULL) {
 +              wpa_printf(MSG_DEBUG, "EAP-TTLS: TNC started but no EAP "
 +                         "response from peer");
 +              eap_ttls_state(data, FAILURE);
 +              goto done;
 +      }
 +#endif /* EAP_SERVER_TNC */
 +
 +      if (parse.eap) {
 +              eap_ttls_process_phase2_eap(sm, data, parse.eap,
 +                                          parse.eap_len);
 +      } else if (parse.user_password) {
 +              eap_ttls_process_phase2_pap(sm, data, parse.user_password,
 +                                          parse.user_password_len);
 +      } else if (parse.chap_password) {
 +              eap_ttls_process_phase2_chap(sm, data,
 +                                           parse.chap_challenge,
 +                                           parse.chap_challenge_len,
 +                                           parse.chap_password,
 +                                           parse.chap_password_len);
 +      } else if (parse.mschap_response) {
 +              eap_ttls_process_phase2_mschap(sm, data,
 +                                             parse.mschap_challenge,
 +                                             parse.mschap_challenge_len,
 +                                             parse.mschap_response,
 +                                             parse.mschap_response_len);
 +      } else if (parse.mschap2_response) {
 +              eap_ttls_process_phase2_mschapv2(sm, data,
 +                                               parse.mschap_challenge,
 +                                               parse.mschap_challenge_len,
 +                                               parse.mschap2_response,
 +                                               parse.mschap2_response_len);
 +      }
 +
 +done:
 +      wpabuf_free(in_decrypted);
 +      os_free(parse.eap);
 +}
 +
 +
 +static void eap_ttls_start_tnc(struct eap_sm *sm, struct eap_ttls_data *data)
 +{
 +#ifdef EAP_SERVER_TNC
 +      if (!sm->tnc || data->state != SUCCESS || data->tnc_started)
 +              return;
 +
 +      wpa_printf(MSG_DEBUG, "EAP-TTLS: Initialize TNC");
 +      if (eap_ttls_phase2_eap_init(sm, data, EAP_TYPE_TNC)) {
 +              wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to initialize TNC");
 +              eap_ttls_state(data, FAILURE);
 +              return;
 +      }
 +
 +      data->tnc_started = 1;
 +      eap_ttls_state(data, PHASE2_METHOD);
 +#endif /* EAP_SERVER_TNC */
 +}
 +
 +
 +static int eap_ttls_process_version(struct eap_sm *sm, void *priv,
 +                                  int peer_version)
 +{
 +      struct eap_ttls_data *data = priv;
 +      if (peer_version < data->ttls_version) {
 +              wpa_printf(MSG_DEBUG, "EAP-TTLS: peer ver=%d, own ver=%d; "
 +                         "use version %d",
 +                         peer_version, data->ttls_version, peer_version);
 +              data->ttls_version = peer_version;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +static void eap_ttls_process_msg(struct eap_sm *sm, void *priv,
 +                               const struct wpabuf *respData)
 +{
 +      struct eap_ttls_data *data = priv;
 +
 +      switch (data->state) {
 +      case PHASE1:
 +              if (eap_server_tls_phase1(sm, &data->ssl) < 0)
 +                      eap_ttls_state(data, FAILURE);
 +              break;
 +      case PHASE2_START:
 +      case PHASE2_METHOD:
 +              eap_ttls_process_phase2(sm, data, data->ssl.tls_in);
 +              eap_ttls_start_tnc(sm, data);
 +              break;
 +      case PHASE2_MSCHAPV2_RESP:
 +              if (data->mschapv2_resp_ok && wpabuf_len(data->ssl.tls_in) ==
 +                  0) {
 +                      wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Peer "
 +                                 "acknowledged response");
 +                      eap_ttls_state(data, SUCCESS);
++                      eap_ttls_valid_session(sm, data);
 +              } else if (!data->mschapv2_resp_ok) {
 +                      wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Peer "
 +                                 "acknowledged error");
 +                      eap_ttls_state(data, FAILURE);
 +              } else {
 +                      wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Unexpected "
 +                                 "frame from peer (payload len %lu, "
 +                                 "expected empty frame)",
 +                                 (unsigned long)
 +                                 wpabuf_len(data->ssl.tls_in));
 +                      eap_ttls_state(data, FAILURE);
 +              }
 +              eap_ttls_start_tnc(sm, data);
 +              break;
 +      default:
 +              wpa_printf(MSG_DEBUG, "EAP-TTLS: Unexpected state %d in %s",
 +                         data->state, __func__);
 +              break;
 +      }
 +}
 +
 +
 +static void eap_ttls_process(struct eap_sm *sm, void *priv,
 +                           struct wpabuf *respData)
 +{
 +      struct eap_ttls_data *data = priv;
++      const struct wpabuf *buf;
++      const u8 *pos;
++      u8 id_len;
++
 +      if (eap_server_tls_process(sm, &data->ssl, respData, data,
 +                                 EAP_TYPE_TTLS, eap_ttls_process_version,
++                                 eap_ttls_process_msg) < 0) {
++              eap_ttls_state(data, FAILURE);
++              return;
++      }
++
++      if (!tls_connection_established(sm->ssl_ctx, data->ssl.conn) ||
++          !tls_connection_resumed(sm->ssl_ctx, data->ssl.conn))
++              return;
++
++      buf = tls_connection_get_success_data(data->ssl.conn);
++      if (!buf || wpabuf_len(buf) < 1) {
++              wpa_printf(MSG_DEBUG,
++                         "EAP-TTLS: No success data in resumed session - reject attempt");
++              eap_ttls_state(data, FAILURE);
++              return;
++      }
++
++      pos = wpabuf_head(buf);
++      if (*pos != EAP_TYPE_TTLS) {
++              wpa_printf(MSG_DEBUG,
++                         "EAP-TTLS: Resumed session for another EAP type (%u) - reject attempt",
++                         *pos);
++              eap_ttls_state(data, FAILURE);
++              return;
++      }
++
++      pos++;
++      id_len = *pos++;
++      wpa_hexdump_ascii(MSG_DEBUG, "EAP-TTLS: Identity from cached session",
++                        pos, id_len);
++      os_free(sm->identity);
++      sm->identity = os_malloc(id_len ? id_len : 1);
++      if (!sm->identity) {
++              sm->identity_len = 0;
 +              eap_ttls_state(data, FAILURE);
++              return;
++      }
++
++      os_memcpy(sm->identity, pos, id_len);
++      sm->identity_len = id_len;
++
++      if (eap_user_get(sm, sm->identity, sm->identity_len, 1) != 0) {
++              wpa_hexdump_ascii(MSG_DEBUG, "EAP-TTLS: Phase2 Identity not found in the user database",
++                                sm->identity, sm->identity_len);
++              eap_ttls_state(data, FAILURE);
++              return;
++      }
++
++      wpa_printf(MSG_DEBUG,
++                 "EAP-TTLS: Resuming previous session - skip Phase2");
++      eap_ttls_state(data, SUCCESS);
++      tls_connection_set_success_data_resumed(data->ssl.conn);
 +}
 +
 +
 +static Boolean eap_ttls_isDone(struct eap_sm *sm, void *priv)
 +{
 +      struct eap_ttls_data *data = priv;
 +      return data->state == SUCCESS || data->state == FAILURE;
 +}
 +
 +
 +static u8 * eap_ttls_getKey(struct eap_sm *sm, void *priv, size_t *len)
 +{
 +      struct eap_ttls_data *data = priv;
 +      u8 *eapKeyData;
 +
 +      if (data->state != SUCCESS)
 +              return NULL;
 +
 +      eapKeyData = eap_server_tls_derive_key(sm, &data->ssl,
 +                                             "ttls keying material",
 +                                             EAP_TLS_KEY_LEN);
 +      if (eapKeyData) {
 +              *len = EAP_TLS_KEY_LEN;
 +              wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Derived key",
 +                              eapKeyData, EAP_TLS_KEY_LEN);
 +      } else {
 +              wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to derive key");
 +      }
 +
 +      return eapKeyData;
 +}
 +
 +
 +static Boolean eap_ttls_isSuccess(struct eap_sm *sm, void *priv)
 +{
 +      struct eap_ttls_data *data = priv;
 +      return data->state == SUCCESS;
 +}
 +
 +
 +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;
 +      int ret;
 +
 +      eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
 +                                    EAP_VENDOR_IETF, EAP_TYPE_TTLS, "TTLS");
 +      if (eap == NULL)
 +              return -1;
 +
 +      eap->init = eap_ttls_init;
 +      eap->reset = eap_ttls_reset;
 +      eap->buildReq = eap_ttls_buildReq;
 +      eap->check = eap_ttls_check;
 +      eap->process = eap_ttls_process;
 +      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)
 +              eap_server_method_free(eap);
 +      return ret;
 +}
index ddf90b859ee402319566f78d012e2f3f4e652d2a,0000000000000000000000000000000000000000..dc943eb207d74ccd288983aa6745bc83f03498e9
mode 100644,000000..100644
--- /dev/null
@@@ -1,94 -1,0 +1,94 @@@
-                           int verify_peer);
 +/*
 + * EAP-TLS/PEAP/TTLS/FAST server common functions
 + * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
 + *
 + * This software may be distributed under the terms of the BSD license.
 + * See README for more details.
 + */
 +
 +#ifndef EAP_TLS_COMMON_H
 +#define EAP_TLS_COMMON_H
 +
 +/**
 + * struct eap_ssl_data - TLS data for EAP methods
 + */
 +struct eap_ssl_data {
 +      /**
 +       * conn - TLS connection context data from tls_connection_init()
 +       */
 +      struct tls_connection *conn;
 +
 +      /**
 +       * tls_out - TLS message to be sent out in fragments
 +       */
 +      struct wpabuf *tls_out;
 +
 +      /**
 +       * tls_out_pos - The current position in the outgoing TLS message
 +       */
 +      size_t tls_out_pos;
 +
 +      /**
 +       * tls_out_limit - Maximum fragment size for outgoing TLS messages
 +       */
 +      size_t tls_out_limit;
 +
 +      /**
 +       * tls_in - Received TLS message buffer for re-assembly
 +       */
 +      struct wpabuf *tls_in;
 +
 +      /**
 +       * phase2 - Whether this TLS connection is used in EAP phase 2 (tunnel)
 +       */
 +      int phase2;
 +
 +      /**
 +       * eap - EAP state machine allocated with eap_server_sm_init()
 +       */
 +      struct eap_sm *eap;
 +
 +      enum { MSG, FRAG_ACK, WAIT_FRAG_ACK } state;
 +      struct wpabuf tmpbuf;
 +};
 +
 +
 +/* EAP TLS Flags */
 +#define EAP_TLS_FLAGS_LENGTH_INCLUDED 0x80
 +#define EAP_TLS_FLAGS_MORE_FRAGMENTS 0x40
 +#define EAP_TLS_FLAGS_START 0x20
 +#define EAP_TLS_VERSION_MASK 0x07
 +
 + /* could be up to 128 bytes, but only the first 64 bytes are used */
 +#define EAP_TLS_KEY_LEN 64
 +
 +/* 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,
 +                                u8 code, u8 identifier);
 +int eap_server_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data,
++                          int verify_peer, int eap_type);
 +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);
 +int eap_server_tls_phase1(struct eap_sm *sm, struct eap_ssl_data *data);
 +struct wpabuf * eap_server_tls_encrypt(struct eap_sm *sm,
 +                                     struct eap_ssl_data *data,
 +                                     const struct wpabuf *plain);
 +int eap_server_tls_process(struct eap_sm *sm, struct eap_ssl_data *data,
 +                         struct wpabuf *respData, void *priv, int eap_type,
 +                         int (*proc_version)(struct eap_sm *sm, void *priv,
 +                                             int peer_version),
 +                         void (*proc_msg)(struct eap_sm *sm, void *priv,
 +                                          const struct wpabuf *respData));
 +
 +#endif /* EAP_TLS_COMMON_H */
index 0df6eb56416b64a8e6528a3fdbb54a75cd07fbd8,0000000000000000000000000000000000000000..ff33d286223fd464de4bde68ed83f80099bce382
mode 100644,000000..100644
--- /dev/null
@@@ -1,1226 -1,0 +1,1321 @@@
-  * Copyright (c) 2002-2014, Jouni Malinen <j@w1.fi>
 +/*
 + * IEEE 802.1X-2004 Authenticator - EAPOL state machine
- static struct eapol_callbacks eapol_cb;
++ * 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.
 + */
 +
 +#include "includes.h"
 +
 +#include "common.h"
 +#include "eloop.h"
 +#include "state_machine.h"
 +#include "common/eapol_common.h"
 +#include "eap_common/eap_defs.h"
 +#include "eap_common/eap_common.h"
 +#include "eap_server/eap.h"
 +#include "eapol_auth_sm.h"
 +#include "eapol_auth_sm_i.h"
 +
 +#define STATE_MACHINE_DATA struct eapol_state_machine
 +#define STATE_MACHINE_DEBUG_PREFIX "IEEE 802.1X"
 +#define STATE_MACHINE_ADDR sm->addr
 +
- static struct eapol_callbacks eapol_cb =
++static const struct eapol_callbacks eapol_cb;
 +
 +/* EAPOL state machines are described in IEEE Std 802.1X-2004, Chap. 8.2 */
 +
 +#define setPortAuthorized() \
 +sm->eapol->cb.set_port_authorized(sm->eapol->conf.ctx, sm->sta, 1)
 +#define setPortUnauthorized() \
 +sm->eapol->cb.set_port_authorized(sm->eapol->conf.ctx, sm->sta, 0)
 +
 +/* procedures */
 +#define txCannedFail() eapol_auth_tx_canned_eap(sm, 0)
 +#define txCannedSuccess() eapol_auth_tx_canned_eap(sm, 1)
 +#define txReq() eapol_auth_tx_req(sm)
 +#define abortAuth() sm->eapol->cb.abort_auth(sm->eapol->conf.ctx, sm->sta)
 +#define txKey() sm->eapol->cb.tx_key(sm->eapol->conf.ctx, sm->sta)
 +#define processKey() do { } while (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,
 +                            const u8 *addr, eapol_logger_level level,
 +                            const char *txt)
 +{
 +      if (eapol->cb.logger == NULL)
 +              return;
 +      eapol->cb.logger(eapol->conf.ctx, addr, level, txt);
 +}
 +
 +
 +static void eapol_auth_vlogger(struct eapol_authenticator *eapol,
 +                             const u8 *addr, eapol_logger_level level,
 +                             const char *fmt, ...)
 +{
 +      char *format;
 +      int maxlen;
 +      va_list ap;
 +
 +      if (eapol->cb.logger == NULL)
 +              return;
 +
 +      maxlen = os_strlen(fmt) + 100;
 +      format = os_malloc(maxlen);
 +      if (!format)
 +              return;
 +
 +      va_start(ap, fmt);
 +      vsnprintf(format, maxlen, fmt, ap);
 +      va_end(ap);
 +
 +      eapol_auth_logger(eapol, addr, level, format);
 +
 +      os_free(format);
 +}
 +
 +
 +static void eapol_auth_tx_canned_eap(struct eapol_state_machine *sm,
 +                                   int success)
 +{
 +      struct eap_hdr eap;
 +
 +      os_memset(&eap, 0, sizeof(eap));
 +
 +      eap.code = success ? EAP_CODE_SUCCESS : EAP_CODE_FAILURE;
 +      eap.identifier = ++sm->last_eap_id;
 +      eap.length = host_to_be16(sizeof(eap));
 +
 +      eapol_auth_vlogger(sm->eapol, sm->addr, EAPOL_LOGGER_DEBUG,
 +                         "Sending canned EAP packet %s (identifier %d)",
 +                         success ? "SUCCESS" : "FAILURE", eap.identifier);
 +      sm->eapol->cb.eapol_send(sm->eapol->conf.ctx, sm->sta,
 +                               IEEE802_1X_TYPE_EAP_PACKET,
 +                               (u8 *) &eap, sizeof(eap));
 +      sm->dot1xAuthEapolFramesTx++;
 +}
 +
 +
 +static void eapol_auth_tx_req(struct eapol_state_machine *sm)
 +{
 +      if (sm->eap_if->eapReqData == NULL ||
 +          wpabuf_len(sm->eap_if->eapReqData) < sizeof(struct eap_hdr)) {
 +              eapol_auth_logger(sm->eapol, sm->addr,
 +                                EAPOL_LOGGER_DEBUG,
 +                                "TxReq called, but there is no EAP request "
 +                                "from authentication server");
 +              return;
 +      }
 +
 +      if (sm->flags & EAPOL_SM_WAIT_START) {
 +              wpa_printf(MSG_DEBUG, "EAPOL: Drop EAPOL TX to " MACSTR
 +                         " while waiting for EAPOL-Start",
 +                         MAC2STR(sm->addr));
 +              return;
 +      }
 +
 +      sm->last_eap_id = eap_get_id(sm->eap_if->eapReqData);
 +      eapol_auth_vlogger(sm->eapol, sm->addr, EAPOL_LOGGER_DEBUG,
 +                         "Sending EAP Packet (identifier %d)",
 +                         sm->last_eap_id);
 +      sm->eapol->cb.eapol_send(sm->eapol->conf.ctx, sm->sta,
 +                               IEEE802_1X_TYPE_EAP_PACKET,
 +                               wpabuf_head(sm->eap_if->eapReqData),
 +                               wpabuf_len(sm->eap_if->eapReqData));
 +      sm->dot1xAuthEapolFramesTx++;
 +      if (eap_get_type(sm->eap_if->eapReqData) == EAP_TYPE_IDENTITY)
 +              sm->dot1xAuthEapolReqIdFramesTx++;
 +      else
 +              sm->dot1xAuthEapolReqFramesTx++;
 +}
 +
 +
 +/**
 + * eapol_port_timers_tick - Port Timers state machine
 + * @eloop_ctx: struct eapol_state_machine *
 + * @timeout_ctx: Not used
 + *
 + * This statemachine is implemented as a function that will be called
 + * once a second as a registered event loop timeout.
 + */
 +static void eapol_port_timers_tick(void *eloop_ctx, void *timeout_ctx)
 +{
 +      struct eapol_state_machine *state = timeout_ctx;
 +
 +      if (state->aWhile > 0) {
 +              state->aWhile--;
 +              if (state->aWhile == 0) {
 +                      wpa_printf(MSG_DEBUG, "IEEE 802.1X: " MACSTR
 +                                 " - aWhile --> 0",
 +                                 MAC2STR(state->addr));
 +              }
 +      }
 +
 +      if (state->quietWhile > 0) {
 +              state->quietWhile--;
 +              if (state->quietWhile == 0) {
 +                      wpa_printf(MSG_DEBUG, "IEEE 802.1X: " MACSTR
 +                                 " - quietWhile --> 0",
 +                                 MAC2STR(state->addr));
 +              }
 +      }
 +
 +      if (state->reAuthWhen > 0) {
 +              state->reAuthWhen--;
 +              if (state->reAuthWhen == 0) {
 +                      wpa_printf(MSG_DEBUG, "IEEE 802.1X: " MACSTR
 +                                 " - reAuthWhen --> 0",
 +                                 MAC2STR(state->addr));
 +              }
 +      }
 +
 +      if (state->eap_if->retransWhile > 0) {
 +              state->eap_if->retransWhile--;
 +              if (state->eap_if->retransWhile == 0) {
 +                      wpa_printf(MSG_DEBUG, "IEEE 802.1X: " MACSTR
 +                                 " - (EAP) retransWhile --> 0",
 +                                 MAC2STR(state->addr));
 +              }
 +      }
 +
 +      eapol_sm_step_run(state);
 +
 +      eloop_register_timeout(1, 0, eapol_port_timers_tick, eloop_ctx, state);
 +}
 +
 +
 +
 +/* Authenticator PAE state machine */
 +
 +SM_STATE(AUTH_PAE, INITIALIZE)
 +{
 +      SM_ENTRY_MA(AUTH_PAE, INITIALIZE, auth_pae);
 +      sm->portMode = Auto;
++
++      /*
++       * Clearing keyRun here is not specified in IEEE Std 802.1X-2004, but
++       * it looks like this would be logical thing to do here since the
++       * EAPOL-Key exchange is not possible in this state. It is possible to
++       * get here on disconnection event without advancing to the
++       * AUTHENTICATING state to clear keyRun before the IEEE 802.11 RSN
++       * authenticator state machine runs and that may advance from
++       * AUTHENTICATION2 to INITPMK if keyRun = TRUE has been left from the
++       * last association. This can be avoided by clearing keyRun here.
++       */
++      sm->keyRun = FALSE;
 +}
 +
 +
 +SM_STATE(AUTH_PAE, DISCONNECTED)
 +{
 +      int from_initialize = sm->auth_pae_state == AUTH_PAE_INITIALIZE;
 +
 +      if (sm->eapolLogoff) {
 +              if (sm->auth_pae_state == AUTH_PAE_CONNECTING)
 +                      sm->authEapLogoffsWhileConnecting++;
 +              else if (sm->auth_pae_state == AUTH_PAE_AUTHENTICATED)
 +                      sm->authAuthEapLogoffWhileAuthenticated++;
 +      }
 +
 +      SM_ENTRY_MA(AUTH_PAE, DISCONNECTED, auth_pae);
 +
 +      sm->authPortStatus = Unauthorized;
 +      setPortUnauthorized();
 +      sm->reAuthCount = 0;
 +      sm->eapolLogoff = FALSE;
 +      if (!from_initialize) {
 +              sm->eapol->cb.finished(sm->eapol->conf.ctx, sm->sta, 0,
 +                                     sm->flags & EAPOL_SM_PREAUTH,
 +                                     sm->remediation);
 +      }
 +}
 +
 +
 +SM_STATE(AUTH_PAE, RESTART)
 +{
 +      if (sm->auth_pae_state == AUTH_PAE_AUTHENTICATED) {
 +              if (sm->reAuthenticate)
 +                      sm->authAuthReauthsWhileAuthenticated++;
 +              if (sm->eapolStart)
 +                      sm->authAuthEapStartsWhileAuthenticated++;
 +              if (sm->eapolLogoff)
 +                      sm->authAuthEapLogoffWhileAuthenticated++;
 +      }
 +
 +      SM_ENTRY_MA(AUTH_PAE, RESTART, auth_pae);
 +
 +      sm->eap_if->eapRestart = TRUE;
 +}
 +
 +
 +SM_STATE(AUTH_PAE, CONNECTING)
 +{
 +      if (sm->auth_pae_state != AUTH_PAE_CONNECTING)
 +              sm->authEntersConnecting++;
 +
 +      SM_ENTRY_MA(AUTH_PAE, CONNECTING, auth_pae);
 +
 +      sm->reAuthenticate = FALSE;
 +      sm->reAuthCount++;
 +}
 +
 +
 +SM_STATE(AUTH_PAE, HELD)
 +{
 +      if (sm->auth_pae_state == AUTH_PAE_AUTHENTICATING && sm->authFail)
 +              sm->authAuthFailWhileAuthenticating++;
 +
 +      SM_ENTRY_MA(AUTH_PAE, HELD, auth_pae);
 +
 +      sm->authPortStatus = Unauthorized;
 +      setPortUnauthorized();
 +      sm->quietWhile = sm->quietPeriod;
 +      sm->eapolLogoff = FALSE;
 +
 +      eapol_auth_vlogger(sm->eapol, sm->addr, EAPOL_LOGGER_WARNING,
 +                         "authentication failed - EAP type: %d (%s)",
 +                         sm->eap_type_authsrv,
 +                         eap_server_get_name(0, sm->eap_type_authsrv));
 +      if (sm->eap_type_authsrv != sm->eap_type_supp) {
 +              eapol_auth_vlogger(sm->eapol, sm->addr, EAPOL_LOGGER_INFO,
 +                                 "Supplicant used different EAP type: "
 +                                 "%d (%s)", sm->eap_type_supp,
 +                                 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->remediation);
 +}
 +
 +
 +SM_STATE(AUTH_PAE, AUTHENTICATED)
 +{
 +      char *extra = "";
 +
 +      if (sm->auth_pae_state == AUTH_PAE_AUTHENTICATING && sm->authSuccess)
 +              sm->authAuthSuccessesWhileAuthenticating++;
 +                                                      
 +      SM_ENTRY_MA(AUTH_PAE, AUTHENTICATED, auth_pae);
 +
 +      sm->authPortStatus = Authorized;
 +      setPortAuthorized();
 +      sm->reAuthCount = 0;
 +      if (sm->flags & EAPOL_SM_PREAUTH)
 +              extra = " (pre-authentication)";
 +      else if (sm->flags & EAPOL_SM_FROM_PMKSA_CACHE)
 +              extra = " (PMKSA cache)";
 +      eapol_auth_vlogger(sm->eapol, sm->addr, EAPOL_LOGGER_INFO,
 +                         "authenticated - EAP type: %d (%s)%s",
 +                         sm->eap_type_authsrv,
 +                         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->remediation);
 +}
 +
 +
 +SM_STATE(AUTH_PAE, AUTHENTICATING)
 +{
 +      SM_ENTRY_MA(AUTH_PAE, AUTHENTICATING, auth_pae);
 +
 +      sm->eapolStart = FALSE;
 +      sm->authSuccess = FALSE;
 +      sm->authFail = FALSE;
 +      sm->authTimeout = FALSE;
 +      sm->authStart = TRUE;
 +      sm->keyRun = FALSE;
 +      sm->keyDone = FALSE;
 +}
 +
 +
 +SM_STATE(AUTH_PAE, ABORTING)
 +{
 +      if (sm->auth_pae_state == AUTH_PAE_AUTHENTICATING) {
 +              if (sm->authTimeout)
 +                      sm->authAuthTimeoutsWhileAuthenticating++;
 +              if (sm->eapolStart)
 +                      sm->authAuthEapStartsWhileAuthenticating++;
 +              if (sm->eapolLogoff)
 +                      sm->authAuthEapLogoffWhileAuthenticating++;
 +      }
 +
 +      SM_ENTRY_MA(AUTH_PAE, ABORTING, auth_pae);
 +
 +      sm->authAbort = TRUE;
 +      sm->keyRun = FALSE;
 +      sm->keyDone = FALSE;
 +}
 +
 +
 +SM_STATE(AUTH_PAE, FORCE_AUTH)
 +{
 +      SM_ENTRY_MA(AUTH_PAE, FORCE_AUTH, auth_pae);
 +
 +      sm->authPortStatus = Authorized;
 +      setPortAuthorized();
 +      sm->portMode = ForceAuthorized;
 +      sm->eapolStart = FALSE;
 +      txCannedSuccess();
 +}
 +
 +
 +SM_STATE(AUTH_PAE, FORCE_UNAUTH)
 +{
 +      SM_ENTRY_MA(AUTH_PAE, FORCE_UNAUTH, auth_pae);
 +
 +      sm->authPortStatus = Unauthorized;
 +      setPortUnauthorized();
 +      sm->portMode = ForceUnauthorized;
 +      sm->eapolStart = FALSE;
 +      txCannedFail();
 +}
 +
 +
 +SM_STEP(AUTH_PAE)
 +{
 +      if ((sm->portControl == Auto && sm->portMode != sm->portControl) ||
 +          sm->initialize || !sm->eap_if->portEnabled)
 +              SM_ENTER_GLOBAL(AUTH_PAE, INITIALIZE);
 +      else if (sm->portControl == ForceAuthorized &&
 +               sm->portMode != sm->portControl &&
 +               !(sm->initialize || !sm->eap_if->portEnabled))
 +              SM_ENTER_GLOBAL(AUTH_PAE, FORCE_AUTH);
 +      else if (sm->portControl == ForceUnauthorized &&
 +               sm->portMode != sm->portControl &&
 +               !(sm->initialize || !sm->eap_if->portEnabled))
 +              SM_ENTER_GLOBAL(AUTH_PAE, FORCE_UNAUTH);
 +      else {
 +              switch (sm->auth_pae_state) {
 +              case AUTH_PAE_INITIALIZE:
 +                      SM_ENTER(AUTH_PAE, DISCONNECTED);
 +                      break;
 +              case AUTH_PAE_DISCONNECTED:
 +                      SM_ENTER(AUTH_PAE, RESTART);
 +                      break;
 +              case AUTH_PAE_RESTART:
 +                      if (!sm->eap_if->eapRestart)
 +                              SM_ENTER(AUTH_PAE, CONNECTING);
 +                      break;
 +              case AUTH_PAE_HELD:
 +                      if (sm->quietWhile == 0)
 +                              SM_ENTER(AUTH_PAE, RESTART);
 +                      break;
 +              case AUTH_PAE_CONNECTING:
 +                      if (sm->eapolLogoff || sm->reAuthCount > sm->reAuthMax)
 +                              SM_ENTER(AUTH_PAE, DISCONNECTED);
 +                      else if ((sm->eap_if->eapReq &&
 +                                sm->reAuthCount <= sm->reAuthMax) ||
 +                               sm->eap_if->eapSuccess || sm->eap_if->eapFail)
 +                              SM_ENTER(AUTH_PAE, AUTHENTICATING);
 +                      break;
 +              case AUTH_PAE_AUTHENTICATED:
 +                      if (sm->eapolStart || sm->reAuthenticate)
 +                              SM_ENTER(AUTH_PAE, RESTART);
 +                      else if (sm->eapolLogoff || !sm->portValid)
 +                              SM_ENTER(AUTH_PAE, DISCONNECTED);
 +                      break;
 +              case AUTH_PAE_AUTHENTICATING:
 +                      if (sm->authSuccess && sm->portValid)
 +                              SM_ENTER(AUTH_PAE, AUTHENTICATED);
 +                      else if (sm->authFail ||
 +                               (sm->keyDone && !sm->portValid))
 +                              SM_ENTER(AUTH_PAE, HELD);
 +                      else if (sm->eapolStart || sm->eapolLogoff ||
 +                               sm->authTimeout)
 +                              SM_ENTER(AUTH_PAE, ABORTING);
 +                      break;
 +              case AUTH_PAE_ABORTING:
 +                      if (sm->eapolLogoff && !sm->authAbort)
 +                              SM_ENTER(AUTH_PAE, DISCONNECTED);
 +                      else if (!sm->eapolLogoff && !sm->authAbort)
 +                              SM_ENTER(AUTH_PAE, RESTART);
 +                      break;
 +              case AUTH_PAE_FORCE_AUTH:
 +                      if (sm->eapolStart)
 +                              SM_ENTER(AUTH_PAE, FORCE_AUTH);
 +                      break;
 +              case AUTH_PAE_FORCE_UNAUTH:
 +                      if (sm->eapolStart)
 +                              SM_ENTER(AUTH_PAE, FORCE_UNAUTH);
 +                      break;
 +              }
 +      }
 +}
 +
 +
 +
 +/* Backend Authentication state machine */
 +
 +SM_STATE(BE_AUTH, INITIALIZE)
 +{
 +      SM_ENTRY_MA(BE_AUTH, INITIALIZE, be_auth);
 +
 +      abortAuth();
 +      sm->eap_if->eapNoReq = FALSE;
 +      sm->authAbort = FALSE;
 +}
 +
 +
 +SM_STATE(BE_AUTH, REQUEST)
 +{
 +      SM_ENTRY_MA(BE_AUTH, REQUEST, be_auth);
 +
 +      txReq();
 +      sm->eap_if->eapReq = FALSE;
 +      sm->backendOtherRequestsToSupplicant++;
 +
 +      /*
 +       * Clearing eapolEap here is not specified in IEEE Std 802.1X-2004, but
 +       * it looks like this would be logical thing to do there since the old
 +       * EAP response would not be valid anymore after the new EAP request
 +       * was sent out.
 +       *
 +       * A race condition has been reported, in which hostapd ended up
 +       * sending out EAP-Response/Identity as a response to the first
 +       * EAP-Request from the main EAP method. This can be avoided by
 +       * clearing eapolEap here.
 +       */
 +      sm->eapolEap = FALSE;
 +}
 +
 +
 +SM_STATE(BE_AUTH, RESPONSE)
 +{
 +      SM_ENTRY_MA(BE_AUTH, RESPONSE, be_auth);
 +
 +      sm->authTimeout = FALSE;
 +      sm->eapolEap = FALSE;
 +      sm->eap_if->eapNoReq = FALSE;
 +      sm->aWhile = sm->serverTimeout;
 +      sm->eap_if->eapResp = TRUE;
 +      /* sendRespToServer(); */
 +      sm->backendResponses++;
 +}
 +
 +
 +SM_STATE(BE_AUTH, SUCCESS)
 +{
 +      SM_ENTRY_MA(BE_AUTH, SUCCESS, be_auth);
 +
 +      txReq();
 +      sm->authSuccess = TRUE;
 +      sm->keyRun = TRUE;
 +}
 +
 +
 +SM_STATE(BE_AUTH, FAIL)
 +{
 +      SM_ENTRY_MA(BE_AUTH, FAIL, be_auth);
 +
 +      txReq();
 +      sm->authFail = TRUE;
 +}
 +
 +
 +SM_STATE(BE_AUTH, TIMEOUT)
 +{
 +      SM_ENTRY_MA(BE_AUTH, TIMEOUT, be_auth);
 +
 +      sm->authTimeout = TRUE;
 +}
 +
 +
 +SM_STATE(BE_AUTH, IDLE)
 +{
 +      SM_ENTRY_MA(BE_AUTH, IDLE, be_auth);
 +
 +      sm->authStart = FALSE;
 +}
 +
 +
 +SM_STATE(BE_AUTH, IGNORE)
 +{
 +      SM_ENTRY_MA(BE_AUTH, IGNORE, be_auth);
 +
 +      sm->eap_if->eapNoReq = FALSE;
 +}
 +
 +
 +SM_STEP(BE_AUTH)
 +{
 +      if (sm->portControl != Auto || sm->initialize || sm->authAbort) {
 +              SM_ENTER_GLOBAL(BE_AUTH, INITIALIZE);
 +              return;
 +      }
 +
 +      switch (sm->be_auth_state) {
 +      case BE_AUTH_INITIALIZE:
 +              SM_ENTER(BE_AUTH, IDLE);
 +              break;
 +      case BE_AUTH_REQUEST:
 +              if (sm->eapolEap)
 +                      SM_ENTER(BE_AUTH, RESPONSE);
 +              else if (sm->eap_if->eapReq)
 +                      SM_ENTER(BE_AUTH, REQUEST);
 +              else if (sm->eap_if->eapTimeout)
 +                      SM_ENTER(BE_AUTH, TIMEOUT);
 +              break;
 +      case BE_AUTH_RESPONSE:
 +              if (sm->eap_if->eapNoReq)
 +                      SM_ENTER(BE_AUTH, IGNORE);
 +              if (sm->eap_if->eapReq) {
 +                      sm->backendAccessChallenges++;
 +                      SM_ENTER(BE_AUTH, REQUEST);
 +              } else if (sm->aWhile == 0)
 +                      SM_ENTER(BE_AUTH, TIMEOUT);
 +              else if (sm->eap_if->eapFail) {
 +                      sm->backendAuthFails++;
 +                      SM_ENTER(BE_AUTH, FAIL);
 +              } else if (sm->eap_if->eapSuccess) {
 +                      sm->backendAuthSuccesses++;
 +                      SM_ENTER(BE_AUTH, SUCCESS);
 +              }
 +              break;
 +      case BE_AUTH_SUCCESS:
 +              SM_ENTER(BE_AUTH, IDLE);
 +              break;
 +      case BE_AUTH_FAIL:
 +              SM_ENTER(BE_AUTH, IDLE);
 +              break;
 +      case BE_AUTH_TIMEOUT:
 +              SM_ENTER(BE_AUTH, IDLE);
 +              break;
 +      case BE_AUTH_IDLE:
 +              if (sm->eap_if->eapFail && sm->authStart)
 +                      SM_ENTER(BE_AUTH, FAIL);
 +              else if (sm->eap_if->eapReq && sm->authStart)
 +                      SM_ENTER(BE_AUTH, REQUEST);
 +              else if (sm->eap_if->eapSuccess && sm->authStart)
 +                      SM_ENTER(BE_AUTH, SUCCESS);
 +              break;
 +      case BE_AUTH_IGNORE:
 +              if (sm->eapolEap)
 +                      SM_ENTER(BE_AUTH, RESPONSE);
 +              else if (sm->eap_if->eapReq)
 +                      SM_ENTER(BE_AUTH, REQUEST);
 +              else if (sm->eap_if->eapTimeout)
 +                      SM_ENTER(BE_AUTH, TIMEOUT);
 +              break;
 +      }
 +}
 +
 +
 +
 +/* Reauthentication Timer state machine */
 +
 +SM_STATE(REAUTH_TIMER, INITIALIZE)
 +{
 +      SM_ENTRY_MA(REAUTH_TIMER, INITIALIZE, reauth_timer);
 +
 +      sm->reAuthWhen = sm->reAuthPeriod;
 +}
 +
 +
 +SM_STATE(REAUTH_TIMER, REAUTHENTICATE)
 +{
 +      SM_ENTRY_MA(REAUTH_TIMER, REAUTHENTICATE, reauth_timer);
 +
 +      sm->reAuthenticate = TRUE;
 +      sm->eapol->cb.eapol_event(sm->eapol->conf.ctx, sm->sta,
 +                                EAPOL_AUTH_REAUTHENTICATE);
 +}
 +
 +
 +SM_STEP(REAUTH_TIMER)
 +{
 +      if (sm->portControl != Auto || sm->initialize ||
 +          sm->authPortStatus == Unauthorized || !sm->reAuthEnabled) {
 +              SM_ENTER_GLOBAL(REAUTH_TIMER, INITIALIZE);
 +              return;
 +      }
 +
 +      switch (sm->reauth_timer_state) {
 +      case REAUTH_TIMER_INITIALIZE:
 +              if (sm->reAuthWhen == 0)
 +                      SM_ENTER(REAUTH_TIMER, REAUTHENTICATE);
 +              break;
 +      case REAUTH_TIMER_REAUTHENTICATE:
 +              SM_ENTER(REAUTH_TIMER, INITIALIZE);
 +              break;
 +      }
 +}
 +
 +
 +
 +/* Authenticator Key Transmit state machine */
 +
 +SM_STATE(AUTH_KEY_TX, NO_KEY_TRANSMIT)
 +{
 +      SM_ENTRY_MA(AUTH_KEY_TX, NO_KEY_TRANSMIT, auth_key_tx);
 +}
 +
 +
 +SM_STATE(AUTH_KEY_TX, KEY_TRANSMIT)
 +{
 +      SM_ENTRY_MA(AUTH_KEY_TX, KEY_TRANSMIT, auth_key_tx);
 +
 +      txKey();
 +      sm->eap_if->eapKeyAvailable = FALSE;
 +      sm->keyDone = TRUE;
 +}
 +
 +
 +SM_STEP(AUTH_KEY_TX)
 +{
 +      if (sm->initialize || sm->portControl != Auto) {
 +              SM_ENTER_GLOBAL(AUTH_KEY_TX, NO_KEY_TRANSMIT);
 +              return;
 +      }
 +
 +      switch (sm->auth_key_tx_state) {
 +      case AUTH_KEY_TX_NO_KEY_TRANSMIT:
 +              if (sm->keyTxEnabled && sm->eap_if->eapKeyAvailable &&
 +                  sm->keyRun && !(sm->flags & EAPOL_SM_USES_WPA))
 +                      SM_ENTER(AUTH_KEY_TX, KEY_TRANSMIT);
 +              break;
 +      case AUTH_KEY_TX_KEY_TRANSMIT:
 +              if (!sm->keyTxEnabled || !sm->keyRun)
 +                      SM_ENTER(AUTH_KEY_TX, NO_KEY_TRANSMIT);
 +              else if (sm->eap_if->eapKeyAvailable)
 +                      SM_ENTER(AUTH_KEY_TX, KEY_TRANSMIT);
 +              break;
 +      }
 +}
 +
 +
 +
 +/* Key Receive state machine */
 +
 +SM_STATE(KEY_RX, NO_KEY_RECEIVE)
 +{
 +      SM_ENTRY_MA(KEY_RX, NO_KEY_RECEIVE, key_rx);
 +}
 +
 +
 +SM_STATE(KEY_RX, KEY_RECEIVE)
 +{
 +      SM_ENTRY_MA(KEY_RX, KEY_RECEIVE, key_rx);
 +
 +      processKey();
 +      sm->rxKey = FALSE;
 +}
 +
 +
 +SM_STEP(KEY_RX)
 +{
 +      if (sm->initialize || !sm->eap_if->portEnabled) {
 +              SM_ENTER_GLOBAL(KEY_RX, NO_KEY_RECEIVE);
 +              return;
 +      }
 +
 +      switch (sm->key_rx_state) {
 +      case KEY_RX_NO_KEY_RECEIVE:
 +              if (sm->rxKey)
 +                      SM_ENTER(KEY_RX, KEY_RECEIVE);
 +              break;
 +      case KEY_RX_KEY_RECEIVE:
 +              if (sm->rxKey)
 +                      SM_ENTER(KEY_RX, KEY_RECEIVE);
 +              break;
 +      }
 +}
 +
 +
 +
 +/* Controlled Directions state machine */
 +
 +SM_STATE(CTRL_DIR, FORCE_BOTH)
 +{
 +      SM_ENTRY_MA(CTRL_DIR, FORCE_BOTH, ctrl_dir);
 +      sm->operControlledDirections = Both;
 +}
 +
 +
 +SM_STATE(CTRL_DIR, IN_OR_BOTH)
 +{
 +      SM_ENTRY_MA(CTRL_DIR, IN_OR_BOTH, ctrl_dir);
 +      sm->operControlledDirections = sm->adminControlledDirections;
 +}
 +
 +
 +SM_STEP(CTRL_DIR)
 +{
 +      if (sm->initialize) {
 +              SM_ENTER_GLOBAL(CTRL_DIR, IN_OR_BOTH);
 +              return;
 +      }
 +
 +      switch (sm->ctrl_dir_state) {
 +      case CTRL_DIR_FORCE_BOTH:
 +              if (sm->eap_if->portEnabled && sm->operEdge)
 +                      SM_ENTER(CTRL_DIR, IN_OR_BOTH);
 +              break;
 +      case CTRL_DIR_IN_OR_BOTH:
 +              if (sm->operControlledDirections !=
 +                  sm->adminControlledDirections)
 +                      SM_ENTER(CTRL_DIR, IN_OR_BOTH);
 +              if (!sm->eap_if->portEnabled || !sm->operEdge)
 +                      SM_ENTER(CTRL_DIR, FORCE_BOTH);
 +              break;
 +      }
 +}
 +
 +
 +
 +struct eapol_state_machine *
 +eapol_auth_alloc(struct eapol_authenticator *eapol, const u8 *addr,
 +               int flags, const struct wpabuf *assoc_wps_ie,
 +               const struct wpabuf *assoc_p2p_ie, void *sta_ctx,
 +               const char *identity, const char *radius_cui)
 +{
 +      struct eapol_state_machine *sm;
 +      struct eap_config eap_conf;
 +
 +      if (eapol == NULL)
 +              return NULL;
 +
 +      sm = os_zalloc(sizeof(*sm));
 +      if (sm == NULL) {
 +              wpa_printf(MSG_DEBUG, "IEEE 802.1X state machine allocation "
 +                         "failed");
 +              return NULL;
 +      }
 +      sm->radius_identifier = -1;
 +      os_memcpy(sm->addr, addr, ETH_ALEN);
 +      sm->flags = flags;
 +
 +      sm->eapol = eapol;
 +      sm->sta = sta_ctx;
 +
 +      /* Set default values for state machine constants */
 +      sm->auth_pae_state = AUTH_PAE_INITIALIZE;
 +      sm->quietPeriod = AUTH_PAE_DEFAULT_quietPeriod;
 +      sm->reAuthMax = AUTH_PAE_DEFAULT_reAuthMax;
 +
 +      sm->be_auth_state = BE_AUTH_INITIALIZE;
 +      sm->serverTimeout = BE_AUTH_DEFAULT_serverTimeout;
 +
 +      sm->reauth_timer_state = REAUTH_TIMER_INITIALIZE;
 +      sm->reAuthPeriod = eapol->conf.eap_reauth_period;
 +      sm->reAuthEnabled = eapol->conf.eap_reauth_period > 0 ? TRUE : FALSE;
 +
 +      sm->auth_key_tx_state = AUTH_KEY_TX_NO_KEY_TRANSMIT;
 +
 +      sm->key_rx_state = KEY_RX_NO_KEY_RECEIVE;
 +
 +      sm->ctrl_dir_state = CTRL_DIR_IN_OR_BOTH;
 +
 +      sm->portControl = Auto;
 +
 +      if (!eapol->conf.wpa &&
 +          (eapol->default_wep_key || eapol->conf.individual_wep_key_len > 0))
 +              sm->keyTxEnabled = TRUE;
 +      else
 +              sm->keyTxEnabled = FALSE;
 +      if (eapol->conf.wpa)
 +              sm->portValid = FALSE;
 +      else
 +              sm->portValid = TRUE;
 +
 +      os_memset(&eap_conf, 0, sizeof(eap_conf));
 +      eap_conf.eap_server = eapol->conf.eap_server;
 +      eap_conf.ssl_ctx = eapol->conf.ssl_ctx;
 +      eap_conf.msg_ctx = eapol->conf.msg_ctx;
 +      eap_conf.eap_sim_db_priv = eapol->conf.eap_sim_db_priv;
 +      eap_conf.pac_opaque_encr_key = eapol->conf.pac_opaque_encr_key;
 +      eap_conf.eap_fast_a_id = eapol->conf.eap_fast_a_id;
 +      eap_conf.eap_fast_a_id_len = eapol->conf.eap_fast_a_id_len;
 +      eap_conf.eap_fast_a_id_info = eapol->conf.eap_fast_a_id_info;
 +      eap_conf.eap_fast_prov = eapol->conf.eap_fast_prov;
 +      eap_conf.pac_key_lifetime = eapol->conf.pac_key_lifetime;
 +      eap_conf.pac_key_refresh_time = eapol->conf.pac_key_refresh_time;
 +      eap_conf.eap_sim_aka_result_ind = eapol->conf.eap_sim_aka_result_ind;
 +      eap_conf.tnc = eapol->conf.tnc;
 +      eap_conf.wps = eapol->conf.wps;
 +      eap_conf.assoc_wps_ie = assoc_wps_ie;
 +      eap_conf.assoc_p2p_ie = assoc_p2p_ie;
 +      eap_conf.peer_addr = 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;
++      eap_conf.tls_session_lifetime = eapol->conf.tls_session_lifetime;
 +      sm->eap = eap_server_sm_init(sm, &eapol_cb, &eap_conf);
 +      if (sm->eap == NULL) {
 +              eapol_auth_free(sm);
 +              return NULL;
 +      }
 +      sm->eap_if = eap_get_interface(sm->eap);
 +
 +      eapol_auth_initialize(sm);
 +
 +      if (identity) {
 +              sm->identity = (u8 *) os_strdup(identity);
 +              if (sm->identity)
 +                      sm->identity_len = os_strlen(identity);
 +      }
 +      if (radius_cui)
 +              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;
 +}
 +
 +
 +void eapol_auth_free(struct eapol_state_machine *sm)
 +{
 +      if (sm == NULL)
 +              return;
 +
 +      eloop_cancel_timeout(eapol_port_timers_tick, NULL, sm);
 +      eloop_cancel_timeout(eapol_sm_step_cb, sm, NULL);
 +      if (sm->eap)
 +              eap_server_sm_deinit(sm->eap);
 +      os_free(sm);
 +}
 +
 +
 +static int eapol_sm_sta_entry_alive(struct eapol_authenticator *eapol,
 +                                  const u8 *addr)
 +{
 +      return eapol->cb.sta_entry_alive(eapol->conf.ctx, addr);
 +}
 +
 +
 +static void eapol_sm_step_run(struct eapol_state_machine *sm)
 +{
 +      struct eapol_authenticator *eapol = sm->eapol;
 +      u8 addr[ETH_ALEN];
 +      unsigned int prev_auth_pae, prev_be_auth, prev_reauth_timer,
 +              prev_auth_key_tx, prev_key_rx, prev_ctrl_dir;
 +      int max_steps = 100;
 +
 +      os_memcpy(addr, sm->addr, ETH_ALEN);
 +
 +      /*
 +       * Allow EAPOL state machines to run as long as there are state
 +       * changes, but exit and return here through event loop if more than
 +       * 100 steps is needed as a precaution against infinite loops inside
 +       * eloop callback.
 +       */
 +restart:
 +      prev_auth_pae = sm->auth_pae_state;
 +      prev_be_auth = sm->be_auth_state;
 +      prev_reauth_timer = sm->reauth_timer_state;
 +      prev_auth_key_tx = sm->auth_key_tx_state;
 +      prev_key_rx = sm->key_rx_state;
 +      prev_ctrl_dir = sm->ctrl_dir_state;
 +
 +      SM_STEP_RUN(AUTH_PAE);
 +      if (sm->initializing || eapol_sm_sta_entry_alive(eapol, addr))
 +              SM_STEP_RUN(BE_AUTH);
 +      if (sm->initializing || eapol_sm_sta_entry_alive(eapol, addr))
 +              SM_STEP_RUN(REAUTH_TIMER);
 +      if (sm->initializing || eapol_sm_sta_entry_alive(eapol, addr))
 +              SM_STEP_RUN(AUTH_KEY_TX);
 +      if (sm->initializing || eapol_sm_sta_entry_alive(eapol, addr))
 +              SM_STEP_RUN(KEY_RX);
 +      if (sm->initializing || eapol_sm_sta_entry_alive(eapol, addr))
 +              SM_STEP_RUN(CTRL_DIR);
 +
 +      if (prev_auth_pae != sm->auth_pae_state ||
 +          prev_be_auth != sm->be_auth_state ||
 +          prev_reauth_timer != sm->reauth_timer_state ||
 +          prev_auth_key_tx != sm->auth_key_tx_state ||
 +          prev_key_rx != sm->key_rx_state ||
 +          prev_ctrl_dir != sm->ctrl_dir_state) {
 +              if (--max_steps > 0)
 +                      goto restart;
 +              /* Re-run from eloop timeout */
 +              eapol_auth_step(sm);
 +              return;
 +      }
 +
 +      if (eapol_sm_sta_entry_alive(eapol, addr) && sm->eap) {
 +              if (eap_server_sm_step(sm->eap)) {
 +                      if (--max_steps > 0)
 +                              goto restart;
 +                      /* Re-run from eloop timeout */
 +                      eapol_auth_step(sm);
 +                      return;
 +              }
 +
 +              /* TODO: find a better location for this */
 +              if (sm->eap_if->aaaEapResp) {
 +                      sm->eap_if->aaaEapResp = FALSE;
 +                      if (sm->eap_if->aaaEapRespData == NULL) {
 +                              wpa_printf(MSG_DEBUG, "EAPOL: aaaEapResp set, "
 +                                         "but no aaaEapRespData available");
 +                              return;
 +                      }
 +                      sm->eapol->cb.aaa_send(
 +                              sm->eapol->conf.ctx, sm->sta,
 +                              wpabuf_head(sm->eap_if->aaaEapRespData),
 +                              wpabuf_len(sm->eap_if->aaaEapRespData));
 +              }
 +      }
 +
 +      if (eapol_sm_sta_entry_alive(eapol, addr))
 +              sm->eapol->cb.eapol_event(sm->eapol->conf.ctx, sm->sta,
 +                                        EAPOL_AUTH_SM_CHANGE);
 +}
 +
 +
 +static void eapol_sm_step_cb(void *eloop_ctx, void *timeout_ctx)
 +{
 +      struct eapol_state_machine *sm = eloop_ctx;
 +      eapol_sm_step_run(sm);
 +}
 +
 +
 +/**
 + * eapol_auth_step - Advance EAPOL state machines
 + * @sm: EAPOL state machine
 + *
 + * This function is called to advance EAPOL state machines after any change
 + * that could affect their state.
 + */
 +void eapol_auth_step(struct eapol_state_machine *sm)
 +{
 +      /*
 +       * Run eapol_sm_step_run from a registered timeout to make sure that
 +       * other possible timeouts/events are processed and to avoid long
 +       * function call chains.
 +       */
 +
 +      eloop_register_timeout(0, 0, eapol_sm_step_cb, sm, NULL);
 +}
 +
 +
 +static void eapol_auth_initialize(struct eapol_state_machine *sm)
 +{
 +      sm->initializing = TRUE;
 +      /* Initialize the state machines by asserting initialize and then
 +       * deasserting it after one step */
 +      sm->initialize = TRUE;
 +      eapol_sm_step_run(sm);
 +      sm->initialize = FALSE;
 +      eapol_sm_step_run(sm);
 +      sm->initializing = FALSE;
 +
 +      /* Start one second tick for port timers state machine */
 +      eloop_cancel_timeout(eapol_port_timers_tick, NULL, sm);
 +      eloop_register_timeout(1, 0, eapol_port_timers_tick, NULL, sm);
 +}
 +
 +
 +static int eapol_sm_get_eap_user(void *ctx, const u8 *identity,
 +                               size_t identity_len, int phase2,
 +                               struct eap_user *user)
 +{
 +      struct eapol_state_machine *sm = ctx;
 +      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;
 +}
 +
 +
 +static const char * eapol_sm_get_eap_req_id_text(void *ctx, size_t *len)
 +{
 +      struct eapol_state_machine *sm = ctx;
 +      *len = sm->eapol->conf.eap_req_id_text_len;
 +      return sm->eapol->conf.eap_req_id_text;
 +}
 +
 +
 +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 const struct eapol_callbacks eapol_cb =
 +{
 +      eapol_sm_get_eap_user,
 +      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,
 +};
 +
 +
 +int eapol_auth_eap_pending_cb(struct eapol_state_machine *sm, void *ctx)
 +{
 +      if (sm == NULL || ctx == NULL || ctx != sm->eap)
 +              return -1;
 +
 +      eap_sm_pending_cb(sm->eap);
 +      eapol_auth_step(sm);
 +
 +      return 0;
 +}
 +
 +
++void eapol_auth_reauthenticate(struct eapol_state_machine *sm)
++{
++      wpa_printf(MSG_DEBUG, "EAPOL: External reauthentication trigger for "
++                 MACSTR, MAC2STR(sm->addr));
++      sm->reAuthenticate = TRUE;
++      eapol_auth_step(sm);
++}
++
++
++int eapol_auth_set_conf(struct eapol_state_machine *sm, const char *param,
++                      const char *value)
++{
++      wpa_printf(MSG_DEBUG, "EAPOL: External configuration operation for "
++                 MACSTR " - param=%s value=%s",
++                 MAC2STR(sm->addr), param, value);
++
++      if (os_strcasecmp(param, "AdminControlledDirections") == 0) {
++              if (os_strcmp(value, "Both") == 0)
++                      sm->adminControlledDirections = Both;
++              else if (os_strcmp(value, "In") == 0)
++                      sm->adminControlledDirections = In;
++              else
++                      return -1;
++              eapol_auth_step(sm);
++              return 0;
++      }
++
++      if (os_strcasecmp(param, "AdminControlledPortControl") == 0) {
++              if (os_strcmp(value, "ForceAuthorized") == 0)
++                      sm->portControl = ForceAuthorized;
++              else if (os_strcmp(value, "ForceUnauthorized") == 0)
++                      sm->portControl = ForceUnauthorized;
++              else if (os_strcmp(value, "Auto") == 0)
++                      sm->portControl = Auto;
++              else
++                      return -1;
++              eapol_auth_step(sm);
++              return 0;
++      }
++
++      if (os_strcasecmp(param, "quietPeriod") == 0) {
++              sm->quietPeriod = atoi(value);
++              return 0;
++      }
++
++      if (os_strcasecmp(param, "serverTimeout") == 0) {
++              sm->serverTimeout = atoi(value);
++              return 0;
++      }
++
++      if (os_strcasecmp(param, "reAuthPeriod") == 0) {
++              sm->reAuthPeriod = atoi(value);
++              return 0;
++      }
++
++      if (os_strcasecmp(param, "reAuthEnabled") == 0) {
++              if (os_strcmp(value, "TRUE") == 0)
++                      sm->reAuthEnabled = TRUE;
++              else if (os_strcmp(value, "FALSE") == 0)
++                      sm->reAuthEnabled = FALSE;
++              else
++                      return -1;
++              eapol_auth_step(sm);
++              return 0;
++      }
++
++      if (os_strcasecmp(param, "KeyTransmissionEnabled") == 0) {
++              if (os_strcmp(value, "TRUE") == 0)
++                      sm->keyTxEnabled = TRUE;
++              else if (os_strcmp(value, "FALSE") == 0)
++                      sm->keyTxEnabled = FALSE;
++              else
++                      return -1;
++              eapol_auth_step(sm);
++              return 0;
++      }
++
++      return -1;
++}
++
++
 +static int eapol_auth_conf_clone(struct eapol_auth_config *dst,
 +                               struct eapol_auth_config *src)
 +{
 +      dst->ctx = src->ctx;
 +      dst->eap_reauth_period = src->eap_reauth_period;
 +      dst->wpa = src->wpa;
 +      dst->individual_wep_key_len = src->individual_wep_key_len;
 +      dst->eap_server = src->eap_server;
 +      dst->ssl_ctx = src->ssl_ctx;
 +      dst->msg_ctx = src->msg_ctx;
 +      dst->eap_sim_db_priv = src->eap_sim_db_priv;
 +      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)
 +                      return -1;
 +              os_memcpy(dst->eap_req_id_text, src->eap_req_id_text,
 +                        src->eap_req_id_text_len);
 +              dst->eap_req_id_text_len = src->eap_req_id_text_len;
 +      } else {
 +              dst->eap_req_id_text = NULL;
 +              dst->eap_req_id_text_len = 0;
 +      }
 +      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)
 +                      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;
 +      } else
 +              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)
 +                      goto fail;
 +      } else
 +              dst->eap_fast_a_id_info = NULL;
 +      dst->eap_fast_prov = src->eap_fast_prov;
 +      dst->pac_key_lifetime = src->pac_key_lifetime;
 +      dst->pac_key_refresh_time = src->pac_key_refresh_time;
 +      dst->eap_sim_aka_result_ind = src->eap_sim_aka_result_ind;
 +      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;
++      dst->tls_session_lifetime = src->tls_session_lifetime;
 +
 +      return 0;
 +
 +fail:
 +      eapol_auth_conf_free(dst);
 +      return -1;
 +}
 +
 +
 +static void eapol_auth_conf_free(struct eapol_auth_config *conf)
 +{
 +      os_free(conf->eap_req_id_text);
 +      conf->eap_req_id_text = NULL;
 +      os_free(conf->pac_opaque_encr_key);
 +      conf->pac_opaque_encr_key = NULL;
 +      os_free(conf->eap_fast_a_id);
 +      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;
 +}
 +
 +
 +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)
 +              return NULL;
 +
 +      if (eapol_auth_conf_clone(&eapol->conf, conf) < 0) {
 +              os_free(eapol);
 +              return NULL;
 +      }
 +
 +      if (conf->individual_wep_key_len > 0) {
 +              /* use key0 in individual key and key1 in broadcast key */
 +              eapol->default_wep_key_idx = 1;
 +      }
 +
 +      eapol->cb.eapol_send = cb->eapol_send;
 +      eapol->cb.aaa_send = cb->aaa_send;
 +      eapol->cb.finished = cb->finished;
 +      eapol->cb.get_eap_user = cb->get_eap_user;
 +      eapol->cb.sta_entry_alive = cb->sta_entry_alive;
 +      eapol->cb.logger = cb->logger;
 +      eapol->cb.set_port_authorized = cb->set_port_authorized;
 +      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;
 +}
 +
 +
 +void eapol_auth_deinit(struct eapol_authenticator *eapol)
 +{
 +      if (eapol == NULL)
 +              return;
 +
 +      eapol_auth_conf_free(&eapol->conf);
 +      os_free(eapol->default_wep_key);
 +      os_free(eapol);
 +}
index ebed19adefc7b84fc1d45578c102dc00249c41aa,0000000000000000000000000000000000000000..e1974e4354dac56c33b864034fe648106913dd80
mode 100644,000000..100644
--- /dev/null
@@@ -1,98 -1,0 +1,102 @@@
-  * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
 +/*
 + * IEEE 802.1X-2004 Authenticator - EAPOL state machine
++ * 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.
 + */
 +
 +#ifndef EAPOL_AUTH_SM_H
 +#define EAPOL_AUTH_SM_H
 +
 +#define EAPOL_SM_PREAUTH BIT(0)
 +#define EAPOL_SM_WAIT_START BIT(1)
 +#define EAPOL_SM_USES_WPA BIT(2)
 +#define EAPOL_SM_FROM_PMKSA_CACHE BIT(3)
 +
 +struct eapol_auth_config {
 +      int eap_reauth_period;
 +      int wpa;
 +      int individual_wep_key_len;
 +      int eap_server;
 +      void *ssl_ctx;
 +      void *msg_ctx;
 +      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 */
++      unsigned int tls_session_lifetime;
 +      u8 *pac_opaque_encr_key;
 +      u8 *eap_fast_a_id;
 +      size_t eap_fast_a_id_len;
 +      char *eap_fast_a_id_info;
 +      int eap_fast_prov;
 +      int pac_key_lifetime;
 +      int pac_key_refresh_time;
 +      int eap_sim_aka_result_ind;
 +      int tnc;
 +      struct wps_context *wps;
 +      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
 +} eapol_logger_level;
 +
 +enum eapol_event {
 +      EAPOL_AUTH_SM_CHANGE,
 +      EAPOL_AUTH_REAUTHENTICATE
 +};
 +
 +struct eapol_auth_cb {
 +      void (*eapol_send)(void *ctx, void *sta_ctx, u8 type, const u8 *data,
 +                         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,
 +                       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);
 +      void (*logger)(void *ctx, const u8 *addr, eapol_logger_level level,
 +                     const char *txt);
 +      void (*set_port_authorized)(void *ctx, void *sta_ctx, int authorized);
 +      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);
 +};
 +
 +
 +struct eapol_authenticator * eapol_auth_init(struct eapol_auth_config *conf,
 +                                           struct eapol_auth_cb *cb);
 +void eapol_auth_deinit(struct eapol_authenticator *eapol);
 +struct eapol_state_machine *
 +eapol_auth_alloc(struct eapol_authenticator *eapol, const u8 *addr,
 +               int flags, const struct wpabuf *assoc_wps_ie,
 +               const struct wpabuf *assoc_p2p_ie, void *sta_ctx,
 +               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);
 +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);
++void eapol_auth_reauthenticate(struct eapol_state_machine *sm);
++int eapol_auth_set_conf(struct eapol_state_machine *sm, const char *param,
++                      const char *value);
 +
 +#endif /* EAPOL_AUTH_SM_H */
index 9cc234a82b09e2fb4c0333f791aac7836bd8cbd6,0000000000000000000000000000000000000000..09cf4f6b922256d93f26af9e4aa3bf610cd5b41f
mode 100644,000000..100644
--- /dev/null
@@@ -1,2139 -1,0 +1,2150 @@@
-       int send_start = sm->SUPP_PAE_state == SUPP_PAE_CONNECTING;
 +/*
 + * EAPOL supplicant state machines
 + * Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi>
 + *
 + * This software may be distributed under the terms of the BSD license.
 + * See README for more details.
 + */
 +
 +#include "includes.h"
 +
 +#include "common.h"
 +#include "state_machine.h"
 +#include "wpabuf.h"
 +#include "eloop.h"
 +#include "crypto/crypto.h"
 +#include "crypto/md5.h"
 +#include "common/eapol_common.h"
 +#include "eap_peer/eap.h"
 +#include "eap_peer/eap_proxy.h"
 +#include "eapol_supp_sm.h"
 +
 +#define STATE_MACHINE_DATA struct eapol_sm
 +#define STATE_MACHINE_DEBUG_PREFIX "EAPOL"
 +
 +
 +/* IEEE 802.1X-2004 - Supplicant - EAPOL state machines */
 +
 +/**
 + * struct eapol_sm - Internal data for EAPOL state machines
 + */
 +struct eapol_sm {
 +      /* Timers */
 +      unsigned int authWhile;
 +      unsigned int heldWhile;
 +      unsigned int startWhen;
 +      unsigned int idleWhile; /* for EAP state machine */
 +      int timer_tick_enabled;
 +
 +      /* Global variables */
 +      Boolean eapFail;
 +      Boolean eapolEap;
 +      Boolean eapSuccess;
 +      Boolean initialize;
 +      Boolean keyDone;
 +      Boolean keyRun;
 +      PortControl portControl;
 +      Boolean portEnabled;
 +      PortStatus suppPortStatus;  /* dot1xSuppControlledPortStatus */
 +      Boolean portValid;
 +      Boolean suppAbort;
 +      Boolean suppFail;
 +      Boolean suppStart;
 +      Boolean suppSuccess;
 +      Boolean suppTimeout;
 +
 +      /* Supplicant PAE state machine */
 +      enum {
 +              SUPP_PAE_UNKNOWN = 0,
 +              SUPP_PAE_DISCONNECTED = 1,
 +              SUPP_PAE_LOGOFF = 2,
 +              SUPP_PAE_CONNECTING = 3,
 +              SUPP_PAE_AUTHENTICATING = 4,
 +              SUPP_PAE_AUTHENTICATED = 5,
 +              /* unused(6) */
 +              SUPP_PAE_HELD = 7,
 +              SUPP_PAE_RESTART = 8,
 +              SUPP_PAE_S_FORCE_AUTH = 9,
 +              SUPP_PAE_S_FORCE_UNAUTH = 10
 +      } SUPP_PAE_state; /* dot1xSuppPaeState */
 +      /* Variables */
 +      Boolean userLogoff;
 +      Boolean logoffSent;
 +      unsigned int startCount;
 +      Boolean eapRestart;
 +      PortControl sPortMode;
 +      /* Constants */
 +      unsigned int heldPeriod; /* dot1xSuppHeldPeriod */
 +      unsigned int startPeriod; /* dot1xSuppStartPeriod */
 +      unsigned int maxStart; /* dot1xSuppMaxStart */
 +
 +      /* Key Receive state machine */
 +      enum {
 +              KEY_RX_UNKNOWN = 0,
 +              KEY_RX_NO_KEY_RECEIVE, KEY_RX_KEY_RECEIVE
 +      } KEY_RX_state;
 +      /* Variables */
 +      Boolean rxKey;
 +
 +      /* Supplicant Backend state machine */
 +      enum {
 +              SUPP_BE_UNKNOWN = 0,
 +              SUPP_BE_INITIALIZE = 1,
 +              SUPP_BE_IDLE = 2,
 +              SUPP_BE_REQUEST = 3,
 +              SUPP_BE_RECEIVE = 4,
 +              SUPP_BE_RESPONSE = 5,
 +              SUPP_BE_FAIL = 6,
 +              SUPP_BE_TIMEOUT = 7, 
 +              SUPP_BE_SUCCESS = 8
 +      } SUPP_BE_state; /* dot1xSuppBackendPaeState */
 +      /* Variables */
 +      Boolean eapNoResp;
 +      Boolean eapReq;
 +      Boolean eapResp;
 +      /* Constants */
 +      unsigned int authPeriod; /* dot1xSuppAuthPeriod */
 +
 +      /* Statistics */
 +      unsigned int dot1xSuppEapolFramesRx;
 +      unsigned int dot1xSuppEapolFramesTx;
 +      unsigned int dot1xSuppEapolStartFramesTx;
 +      unsigned int dot1xSuppEapolLogoffFramesTx;
 +      unsigned int dot1xSuppEapolRespFramesTx;
 +      unsigned int dot1xSuppEapolReqIdFramesRx;
 +      unsigned int dot1xSuppEapolReqFramesRx;
 +      unsigned int dot1xSuppInvalidEapolFramesRx;
 +      unsigned int dot1xSuppEapLengthErrorFramesRx;
 +      unsigned int dot1xSuppLastEapolFrameVersion;
 +      unsigned char dot1xSuppLastEapolFrameSource[6];
 +
 +      /* Miscellaneous variables (not defined in IEEE 802.1X-2004) */
 +      Boolean changed;
 +      struct eap_sm *eap;
 +      struct eap_peer_config *config;
 +      Boolean initial_req;
 +      u8 *last_rx_key;
 +      size_t last_rx_key_len;
 +      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;
 +      struct eapol_ctx *ctx;
 +      enum { EAPOL_CB_IN_PROGRESS = 0, EAPOL_CB_SUCCESS, EAPOL_CB_FAILURE }
 +              cb_status;
 +      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;
 +#endif /* CONFIG_EAP_PROXY */
 +};
 +
 +
 +static void eapol_sm_txLogoff(struct eapol_sm *sm);
 +static void eapol_sm_txStart(struct eapol_sm *sm);
 +static void eapol_sm_processKey(struct eapol_sm *sm);
 +static void eapol_sm_getSuppRsp(struct eapol_sm *sm);
 +static void eapol_sm_txSuppRsp(struct eapol_sm *sm);
 +static void eapol_sm_abortSupp(struct eapol_sm *sm);
 +static void eapol_sm_abort_cached(struct eapol_sm *sm);
 +static void eapol_sm_step_timeout(void *eloop_ctx, void *timeout_ctx);
 +static void eapol_sm_set_port_authorized(struct eapol_sm *sm);
 +static void eapol_sm_set_port_unauthorized(struct eapol_sm *sm);
 +
 +
 +/* Port Timers state machine - implemented as a function that will be called
 + * once a second as a registered event loop timeout */
 +static void eapol_port_timers_tick(void *eloop_ctx, void *timeout_ctx)
 +{
 +      struct eapol_sm *sm = timeout_ctx;
 +
 +      if (sm->authWhile > 0) {
 +              sm->authWhile--;
 +              if (sm->authWhile == 0)
 +                      wpa_printf(MSG_DEBUG, "EAPOL: authWhile --> 0");
 +      }
 +      if (sm->heldWhile > 0) {
 +              sm->heldWhile--;
 +              if (sm->heldWhile == 0)
 +                      wpa_printf(MSG_DEBUG, "EAPOL: heldWhile --> 0");
 +      }
 +      if (sm->startWhen > 0) {
 +              sm->startWhen--;
 +              if (sm->startWhen == 0)
 +                      wpa_printf(MSG_DEBUG, "EAPOL: startWhen --> 0");
 +      }
 +      if (sm->idleWhile > 0) {
 +              sm->idleWhile--;
 +              if (sm->idleWhile == 0)
 +                      wpa_printf(MSG_DEBUG, "EAPOL: idleWhile --> 0");
 +      }
 +
 +      if (sm->authWhile | sm->heldWhile | sm->startWhen | sm->idleWhile) {
 +              eloop_register_timeout(1, 0, eapol_port_timers_tick, eloop_ctx,
 +                                     sm);
 +      } else {
 +              wpa_printf(MSG_DEBUG, "EAPOL: disable timer tick");
 +              sm->timer_tick_enabled = 0;
 +      }
 +      eapol_sm_step(sm);
 +}
 +
 +
 +static void eapol_enable_timer_tick(struct eapol_sm *sm)
 +{
 +      if (sm->timer_tick_enabled)
 +              return;
 +      wpa_printf(MSG_DEBUG, "EAPOL: enable timer tick");
 +      sm->timer_tick_enabled = 1;
 +      eloop_cancel_timeout(eapol_port_timers_tick, NULL, sm);
 +      eloop_register_timeout(1, 0, eapol_port_timers_tick, NULL, sm);
 +}
 +
 +
 +SM_STATE(SUPP_PAE, LOGOFF)
 +{
 +      SM_ENTRY(SUPP_PAE, LOGOFF);
 +      eapol_sm_txLogoff(sm);
 +      sm->logoffSent = TRUE;
 +      eapol_sm_set_port_unauthorized(sm);
 +}
 +
 +
 +SM_STATE(SUPP_PAE, DISCONNECTED)
 +{
 +      SM_ENTRY(SUPP_PAE, DISCONNECTED);
 +      sm->sPortMode = Auto;
 +      sm->startCount = 0;
 +      sm->eapTriggerStart = FALSE;
 +      sm->logoffSent = FALSE;
 +      eapol_sm_set_port_unauthorized(sm);
 +      sm->suppAbort = TRUE;
 +
 +      sm->unicast_key_received = FALSE;
 +      sm->broadcast_key_received = FALSE;
 +
 +      /*
 +       * IEEE Std 802.1X-2004 does not clear heldWhile here, but doing so
 +       * allows the timer tick to be stopped more quickly when the port is
 +       * not enabled. Since this variable is used only within HELD state,
 +       * clearing it on initialization does not change actual state machine
 +       * behavior.
 +       */
 +      sm->heldWhile = 0;
 +}
 +
 +
 +SM_STATE(SUPP_PAE, CONNECTING)
 +{
- static struct eapol_callbacks eapol_cb =
++      int send_start = sm->SUPP_PAE_state == SUPP_PAE_CONNECTING ||
++              sm->SUPP_PAE_state == SUPP_PAE_HELD;
 +      SM_ENTRY(SUPP_PAE, CONNECTING);
 +
 +      if (sm->eapTriggerStart)
 +              send_start = 1;
 +      sm->eapTriggerStart = FALSE;
 +
 +      if (send_start) {
 +              sm->startWhen = sm->startPeriod;
 +              sm->startCount++;
 +      } else {
 +              /*
 +               * Do not send EAPOL-Start immediately since in most cases,
 +               * Authenticator is going to start authentication immediately
 +               * after association and an extra EAPOL-Start is just going to
 +               * delay authentication. Use a short timeout to send the first
 +               * EAPOL-Start if Authenticator does not start authentication.
 +               */
 +              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;
 +      if (send_start)
 +              eapol_sm_txStart(sm);
 +}
 +
 +
 +SM_STATE(SUPP_PAE, AUTHENTICATING)
 +{
 +      SM_ENTRY(SUPP_PAE, AUTHENTICATING);
 +      sm->startCount = 0;
 +      sm->suppSuccess = FALSE;
 +      sm->suppFail = FALSE;
 +      sm->suppTimeout = FALSE;
 +      sm->keyRun = FALSE;
 +      sm->keyDone = FALSE;
 +      sm->suppStart = TRUE;
 +}
 +
 +
 +SM_STATE(SUPP_PAE, HELD)
 +{
 +      SM_ENTRY(SUPP_PAE, HELD);
 +      sm->heldWhile = sm->heldPeriod;
 +      eapol_enable_timer_tick(sm);
 +      eapol_sm_set_port_unauthorized(sm);
 +      sm->cb_status = EAPOL_CB_FAILURE;
 +}
 +
 +
 +SM_STATE(SUPP_PAE, AUTHENTICATED)
 +{
 +      SM_ENTRY(SUPP_PAE, AUTHENTICATED);
 +      eapol_sm_set_port_authorized(sm);
 +      sm->cb_status = EAPOL_CB_SUCCESS;
 +}
 +
 +
 +SM_STATE(SUPP_PAE, RESTART)
 +{
 +      SM_ENTRY(SUPP_PAE, RESTART);
 +      sm->eapRestart = TRUE;
 +}
 +
 +
 +SM_STATE(SUPP_PAE, S_FORCE_AUTH)
 +{
 +      SM_ENTRY(SUPP_PAE, S_FORCE_AUTH);
 +      eapol_sm_set_port_authorized(sm);
 +      sm->sPortMode = ForceAuthorized;
 +}
 +
 +
 +SM_STATE(SUPP_PAE, S_FORCE_UNAUTH)
 +{
 +      SM_ENTRY(SUPP_PAE, S_FORCE_UNAUTH);
 +      eapol_sm_set_port_unauthorized(sm);
 +      sm->sPortMode = ForceUnauthorized;
 +      eapol_sm_txLogoff(sm);
 +}
 +
 +
 +SM_STEP(SUPP_PAE)
 +{
 +      if ((sm->userLogoff && !sm->logoffSent) &&
 +          !(sm->initialize || !sm->portEnabled))
 +              SM_ENTER_GLOBAL(SUPP_PAE, LOGOFF);
 +      else if (((sm->portControl == Auto) &&
 +                (sm->sPortMode != sm->portControl)) ||
 +               sm->initialize || !sm->portEnabled)
 +              SM_ENTER_GLOBAL(SUPP_PAE, DISCONNECTED);
 +      else if ((sm->portControl == ForceAuthorized) &&
 +               (sm->sPortMode != sm->portControl) &&
 +               !(sm->initialize || !sm->portEnabled))
 +              SM_ENTER_GLOBAL(SUPP_PAE, S_FORCE_AUTH);
 +      else if ((sm->portControl == ForceUnauthorized) &&
 +               (sm->sPortMode != sm->portControl) &&
 +               !(sm->initialize || !sm->portEnabled))
 +              SM_ENTER_GLOBAL(SUPP_PAE, S_FORCE_UNAUTH);
 +      else switch (sm->SUPP_PAE_state) {
 +      case SUPP_PAE_UNKNOWN:
 +              break;
 +      case SUPP_PAE_LOGOFF:
 +              if (!sm->userLogoff)
 +                      SM_ENTER(SUPP_PAE, DISCONNECTED);
 +              break;
 +      case SUPP_PAE_DISCONNECTED:
 +              SM_ENTER(SUPP_PAE, CONNECTING);
 +              break;
 +      case SUPP_PAE_CONNECTING:
 +              if (sm->startWhen == 0 && sm->startCount < sm->maxStart)
 +                      SM_ENTER(SUPP_PAE, CONNECTING);
 +              else if (sm->startWhen == 0 &&
 +                       sm->startCount >= sm->maxStart &&
 +                       sm->portValid)
 +                      SM_ENTER(SUPP_PAE, AUTHENTICATED);
 +              else if (sm->eapSuccess || sm->eapFail)
 +                      SM_ENTER(SUPP_PAE, AUTHENTICATING);
 +              else if (sm->eapolEap)
 +                      SM_ENTER(SUPP_PAE, RESTART);
 +              else if (sm->startWhen == 0 &&
 +                       sm->startCount >= sm->maxStart &&
 +                       !sm->portValid)
 +                      SM_ENTER(SUPP_PAE, HELD);
 +              break;
 +      case SUPP_PAE_AUTHENTICATING:
 +              if (sm->eapSuccess && !sm->portValid &&
 +                  sm->conf.accept_802_1x_keys &&
 +                  sm->conf.required_keys == 0) {
 +                      wpa_printf(MSG_DEBUG, "EAPOL: IEEE 802.1X for "
 +                                 "plaintext connection; no EAPOL-Key frames "
 +                                 "required");
 +                      sm->portValid = TRUE;
 +                      if (sm->ctx->eapol_done_cb)
 +                              sm->ctx->eapol_done_cb(sm->ctx->ctx);
 +              }
 +              if (sm->eapSuccess && sm->portValid)
 +                      SM_ENTER(SUPP_PAE, AUTHENTICATED);
 +              else if (sm->eapFail || (sm->keyDone && !sm->portValid))
 +                      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)
 +                      SM_ENTER(SUPP_PAE, CONNECTING);
 +              else if (sm->eapolEap)
 +                      SM_ENTER(SUPP_PAE, RESTART);
 +              break;
 +      case SUPP_PAE_AUTHENTICATED:
 +              if (sm->eapolEap && sm->portValid)
 +                      SM_ENTER(SUPP_PAE, RESTART);
 +              else if (!sm->portValid)
 +                      SM_ENTER(SUPP_PAE, DISCONNECTED);
 +              break;
 +      case SUPP_PAE_RESTART:
 +              if (!sm->eapRestart)
 +                      SM_ENTER(SUPP_PAE, AUTHENTICATING);
 +              break;
 +      case SUPP_PAE_S_FORCE_AUTH:
 +              break;
 +      case SUPP_PAE_S_FORCE_UNAUTH:
 +              break;
 +      }
 +}
 +
 +
 +SM_STATE(KEY_RX, NO_KEY_RECEIVE)
 +{
 +      SM_ENTRY(KEY_RX, NO_KEY_RECEIVE);
 +}
 +
 +
 +SM_STATE(KEY_RX, KEY_RECEIVE)
 +{
 +      SM_ENTRY(KEY_RX, KEY_RECEIVE);
 +      eapol_sm_processKey(sm);
 +      sm->rxKey = FALSE;
 +}
 +
 +
 +SM_STEP(KEY_RX)
 +{
 +      if (sm->initialize || !sm->portEnabled)
 +              SM_ENTER_GLOBAL(KEY_RX, NO_KEY_RECEIVE);
 +      switch (sm->KEY_RX_state) {
 +      case KEY_RX_UNKNOWN:
 +              break;
 +      case KEY_RX_NO_KEY_RECEIVE:
 +              if (sm->rxKey)
 +                      SM_ENTER(KEY_RX, KEY_RECEIVE);
 +              break;
 +      case KEY_RX_KEY_RECEIVE:
 +              if (sm->rxKey)
 +                      SM_ENTER(KEY_RX, KEY_RECEIVE);
 +              break;
 +      }
 +}
 +
 +
 +SM_STATE(SUPP_BE, REQUEST)
 +{
 +      SM_ENTRY(SUPP_BE, REQUEST);
 +      sm->authWhile = 0;
 +      sm->eapReq = TRUE;
 +      eapol_sm_getSuppRsp(sm);
 +}
 +
 +
 +SM_STATE(SUPP_BE, RESPONSE)
 +{
 +      SM_ENTRY(SUPP_BE, RESPONSE);
 +      eapol_sm_txSuppRsp(sm);
 +      sm->eapResp = FALSE;
 +}
 +
 +
 +SM_STATE(SUPP_BE, SUCCESS)
 +{
 +      SM_ENTRY(SUPP_BE, SUCCESS);
 +      sm->keyRun = TRUE;
 +      sm->suppSuccess = TRUE;
 +
 +#ifdef CONFIG_EAP_PROXY
 +      if (sm->use_eap_proxy) {
 +              if (eap_proxy_key_available(sm->eap_proxy)) {
 +                      /* New key received - clear IEEE 802.1X EAPOL-Key replay
 +                       * counter */
 +                      sm->replay_counter_valid = FALSE;
 +              }
 +              return;
 +      }
 +#endif /* CONFIG_EAP_PROXY */
 +
 +      if (eap_key_available(sm->eap)) {
 +              /* New key received - clear IEEE 802.1X EAPOL-Key replay
 +               * counter */
 +              sm->replay_counter_valid = FALSE;
 +      }
 +}
 +
 +
 +SM_STATE(SUPP_BE, FAIL)
 +{
 +      SM_ENTRY(SUPP_BE, FAIL);
 +      sm->suppFail = TRUE;
 +}
 +
 +
 +SM_STATE(SUPP_BE, TIMEOUT)
 +{
 +      SM_ENTRY(SUPP_BE, TIMEOUT);
 +      sm->suppTimeout = TRUE;
 +}
 +
 +
 +SM_STATE(SUPP_BE, IDLE)
 +{
 +      SM_ENTRY(SUPP_BE, IDLE);
 +      sm->suppStart = FALSE;
 +      sm->initial_req = TRUE;
 +}
 +
 +
 +SM_STATE(SUPP_BE, INITIALIZE)
 +{
 +      SM_ENTRY(SUPP_BE, INITIALIZE);
 +      eapol_sm_abortSupp(sm);
 +      sm->suppAbort = FALSE;
 +
 +      /*
 +       * IEEE Std 802.1X-2004 does not clear authWhile here, but doing so
 +       * allows the timer tick to be stopped more quickly when the port is
 +       * not enabled. Since this variable is used only within RECEIVE state,
 +       * clearing it on initialization does not change actual state machine
 +       * behavior.
 +       */
 +      sm->authWhile = 0;
 +}
 +
 +
 +SM_STATE(SUPP_BE, RECEIVE)
 +{
 +      SM_ENTRY(SUPP_BE, RECEIVE);
 +      sm->authWhile = sm->authPeriod;
 +      eapol_enable_timer_tick(sm);
 +      sm->eapolEap = FALSE;
 +      sm->eapNoResp = FALSE;
 +      sm->initial_req = FALSE;
 +}
 +
 +
 +SM_STEP(SUPP_BE)
 +{
 +      if (sm->initialize || sm->suppAbort)
 +              SM_ENTER_GLOBAL(SUPP_BE, INITIALIZE);
 +      else switch (sm->SUPP_BE_state) {
 +      case SUPP_BE_UNKNOWN:
 +              break;
 +      case SUPP_BE_REQUEST:
 +              /*
 +               * IEEE Std 802.1X-2004 has transitions from REQUEST to FAIL
 +               * and SUCCESS based on eapFail and eapSuccess, respectively.
 +               * However, IEEE Std 802.1X-2004 is also specifying that
 +               * eapNoResp should be set in conjunction with eapSuccess and
 +               * eapFail which would mean that more than one of the
 +               * transitions here would be activated at the same time.
 +               * Skipping RESPONSE and/or RECEIVE states in these cases can
 +               * cause problems and the direct transitions to do not seem
 +               * correct. Because of this, the conditions for these
 +               * transitions are verified only after eapNoResp. They are
 +               * unlikely to be used since eapNoResp should always be set if
 +               * either of eapSuccess or eapFail is set.
 +               */
 +              if (sm->eapResp && sm->eapNoResp) {
 +                      wpa_printf(MSG_DEBUG, "EAPOL: SUPP_BE REQUEST: both "
 +                                 "eapResp and eapNoResp set?!");
 +              }
 +              if (sm->eapResp)
 +                      SM_ENTER(SUPP_BE, RESPONSE);
 +              else if (sm->eapNoResp)
 +                      SM_ENTER(SUPP_BE, RECEIVE);
 +              else if (sm->eapFail)
 +                      SM_ENTER(SUPP_BE, FAIL);
 +              else if (sm->eapSuccess)
 +                      SM_ENTER(SUPP_BE, SUCCESS);
 +              break;
 +      case SUPP_BE_RESPONSE:
 +              SM_ENTER(SUPP_BE, RECEIVE);
 +              break;
 +      case SUPP_BE_SUCCESS:
 +              SM_ENTER(SUPP_BE, IDLE);
 +              break;
 +      case SUPP_BE_FAIL:
 +              SM_ENTER(SUPP_BE, IDLE);
 +              break;
 +      case SUPP_BE_TIMEOUT:
 +              SM_ENTER(SUPP_BE, IDLE);
 +              break;
 +      case SUPP_BE_IDLE:
 +              if (sm->eapFail && sm->suppStart)
 +                      SM_ENTER(SUPP_BE, FAIL);
 +              else if (sm->eapolEap && sm->suppStart)
 +                      SM_ENTER(SUPP_BE, REQUEST);
 +              else if (sm->eapSuccess && sm->suppStart)
 +                      SM_ENTER(SUPP_BE, SUCCESS);
 +              break;
 +      case SUPP_BE_INITIALIZE:
 +              SM_ENTER(SUPP_BE, IDLE);
 +              break;
 +      case SUPP_BE_RECEIVE:
 +              if (sm->eapolEap)
 +                      SM_ENTER(SUPP_BE, REQUEST);
 +              else if (sm->eapFail)
 +                      SM_ENTER(SUPP_BE, FAIL);
 +              else if (sm->authWhile == 0)
 +                      SM_ENTER(SUPP_BE, TIMEOUT);
 +              else if (sm->eapSuccess)
 +                      SM_ENTER(SUPP_BE, SUCCESS);
 +              break;
 +      }
 +}
 +
 +
 +static void eapol_sm_txLogoff(struct eapol_sm *sm)
 +{
 +      wpa_printf(MSG_DEBUG, "EAPOL: txLogoff");
 +      sm->ctx->eapol_send(sm->ctx->eapol_send_ctx,
 +                          IEEE802_1X_TYPE_EAPOL_LOGOFF, (u8 *) "", 0);
 +      sm->dot1xSuppEapolLogoffFramesTx++;
 +      sm->dot1xSuppEapolFramesTx++;
 +}
 +
 +
 +static void eapol_sm_txStart(struct eapol_sm *sm)
 +{
 +      wpa_printf(MSG_DEBUG, "EAPOL: txStart");
 +      sm->ctx->eapol_send(sm->ctx->eapol_send_ctx,
 +                          IEEE802_1X_TYPE_EAPOL_START, (u8 *) "", 0);
 +      sm->dot1xSuppEapolStartFramesTx++;
 +      sm->dot1xSuppEapolFramesTx++;
 +}
 +
 +
 +#define IEEE8021X_ENCR_KEY_LEN 32
 +#define IEEE8021X_SIGN_KEY_LEN 32
 +
 +struct eap_key_data {
 +      u8 encr_key[IEEE8021X_ENCR_KEY_LEN];
 +      u8 sign_key[IEEE8021X_SIGN_KEY_LEN];
 +};
 +
 +
 +static void eapol_sm_processKey(struct eapol_sm *sm)
 +{
 +#ifndef CONFIG_FIPS
 +      struct ieee802_1x_hdr *hdr;
 +      struct ieee802_1x_eapol_key *key;
 +      struct eap_key_data keydata;
 +      u8 orig_key_sign[IEEE8021X_KEY_SIGN_LEN], datakey[32];
++#ifndef CONFIG_NO_RC4
 +      u8 ekey[IEEE8021X_KEY_IV_LEN + IEEE8021X_ENCR_KEY_LEN];
++#endif /* CONFIG_NO_RC4 */
 +      int key_len, res, sign_key_len, encr_key_len;
 +      u16 rx_key_length;
 +      size_t plen;
 +
 +      wpa_printf(MSG_DEBUG, "EAPOL: processKey");
 +      if (sm->last_rx_key == NULL)
 +              return;
 +
 +      if (!sm->conf.accept_802_1x_keys) {
 +              wpa_printf(MSG_WARNING, "EAPOL: Received IEEE 802.1X EAPOL-Key"
 +                         " even though this was not accepted - "
 +                         "ignoring this packet");
 +              return;
 +      }
 +
 +      if (sm->last_rx_key_len < sizeof(*hdr) + sizeof(*key))
 +              return;
 +      hdr = (struct ieee802_1x_hdr *) sm->last_rx_key;
 +      key = (struct ieee802_1x_eapol_key *) (hdr + 1);
 +      plen = be_to_host16(hdr->length);
 +      if (sizeof(*hdr) + plen > sm->last_rx_key_len || plen < sizeof(*key)) {
 +              wpa_printf(MSG_WARNING, "EAPOL: Too short EAPOL-Key frame");
 +              return;
 +      }
 +      rx_key_length = WPA_GET_BE16(key->key_length);
 +      wpa_printf(MSG_DEBUG, "EAPOL: RX IEEE 802.1X ver=%d type=%d len=%d "
 +                 "EAPOL-Key: type=%d key_length=%d key_index=0x%x",
 +                 hdr->version, hdr->type, be_to_host16(hdr->length),
 +                 key->type, rx_key_length, key->key_index);
 +
 +      eapol_sm_notify_lower_layer_success(sm, 1);
 +      sign_key_len = IEEE8021X_SIGN_KEY_LEN;
 +      encr_key_len = IEEE8021X_ENCR_KEY_LEN;
 +      res = eapol_sm_get_key(sm, (u8 *) &keydata, sizeof(keydata));
 +      if (res < 0) {
 +              wpa_printf(MSG_DEBUG, "EAPOL: Could not get master key for "
 +                         "decrypting EAPOL-Key keys");
 +              return;
 +      }
 +      if (res == 16) {
 +              /* LEAP derives only 16 bytes of keying material. */
 +              res = eapol_sm_get_key(sm, (u8 *) &keydata, 16);
 +              if (res) {
 +                      wpa_printf(MSG_DEBUG, "EAPOL: Could not get LEAP "
 +                                 "master key for decrypting EAPOL-Key keys");
 +                      return;
 +              }
 +              sign_key_len = 16;
 +              encr_key_len = 16;
 +              os_memcpy(keydata.sign_key, keydata.encr_key, 16);
 +      } else if (res) {
 +              wpa_printf(MSG_DEBUG, "EAPOL: Could not get enough master key "
 +                         "data for decrypting EAPOL-Key keys (res=%d)", res);
 +              return;
 +      }
 +
 +      /* The key replay_counter must increase when same master key */
 +      if (sm->replay_counter_valid &&
 +          os_memcmp(sm->last_replay_counter, key->replay_counter,
 +                    IEEE8021X_REPLAY_COUNTER_LEN) >= 0) {
 +              wpa_printf(MSG_WARNING, "EAPOL: EAPOL-Key replay counter did "
 +                         "not increase - ignoring key");
 +              wpa_hexdump(MSG_DEBUG, "EAPOL: last replay counter",
 +                          sm->last_replay_counter,
 +                          IEEE8021X_REPLAY_COUNTER_LEN);
 +              wpa_hexdump(MSG_DEBUG, "EAPOL: received replay counter",
 +                          key->replay_counter, IEEE8021X_REPLAY_COUNTER_LEN);
 +              return;
 +      }
 +
 +      /* Verify key signature (HMAC-MD5) */
 +      os_memcpy(orig_key_sign, key->key_signature, IEEE8021X_KEY_SIGN_LEN);
 +      os_memset(key->key_signature, 0, IEEE8021X_KEY_SIGN_LEN);
 +      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_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,
 +                        IEEE8021X_KEY_SIGN_LEN);
 +              return;
 +      }
 +      wpa_printf(MSG_DEBUG, "EAPOL: EAPOL-Key key signature verified");
 +
 +      key_len = plen - sizeof(*key);
 +      if (key_len > 32 || rx_key_length > 32) {
 +              wpa_printf(MSG_WARNING, "EAPOL: Too long key data length %d",
 +                         key_len ? key_len : rx_key_length);
 +              return;
 +      }
 +      if (key_len == rx_key_length) {
++#ifdef CONFIG_NO_RC4
++              if (encr_key_len) {
++                      /* otherwise unused */
++              }
++              wpa_printf(MSG_ERROR, "EAPOL: RC4 not supported in the build");
++              return;
++#else /* CONFIG_NO_RC4 */
 +              os_memcpy(ekey, key->key_iv, IEEE8021X_KEY_IV_LEN);
 +              os_memcpy(ekey + IEEE8021X_KEY_IV_LEN, keydata.encr_key,
 +                        encr_key_len);
 +              os_memcpy(datakey, key + 1, key_len);
 +              rc4_skip(ekey, IEEE8021X_KEY_IV_LEN + encr_key_len, 0,
 +                       datakey, key_len);
 +              wpa_hexdump_key(MSG_DEBUG, "EAPOL: Decrypted(RC4) key",
 +                              datakey, key_len);
++#endif /* CONFIG_NO_RC4 */
 +      } else if (key_len == 0) {
 +              /*
 +               * IEEE 802.1X-2004 specifies that least significant Key Length
 +               * octets from MS-MPPE-Send-Key are used as the key if the key
 +               * data is not present. This seems to be meaning the beginning
 +               * of the MS-MPPE-Send-Key. In addition, MS-MPPE-Send-Key in
 +               * Supplicant corresponds to MS-MPPE-Recv-Key in Authenticator.
 +               * Anyway, taking the beginning of the keying material from EAP
 +               * seems to interoperate with Authenticators.
 +               */
 +              key_len = rx_key_length;
 +              os_memcpy(datakey, keydata.encr_key, key_len);
 +              wpa_hexdump_key(MSG_DEBUG, "EAPOL: using part of EAP keying "
 +                              "material data encryption key",
 +                              datakey, key_len);
 +      } else {
 +              wpa_printf(MSG_DEBUG, "EAPOL: Invalid key data length %d "
 +                         "(key_length=%d)", key_len, rx_key_length);
 +              return;
 +      }
 +
 +      sm->replay_counter_valid = TRUE;
 +      os_memcpy(sm->last_replay_counter, key->replay_counter,
 +                IEEE8021X_REPLAY_COUNTER_LEN);
 +
 +      wpa_printf(MSG_DEBUG, "EAPOL: Setting dynamic WEP key: %s keyidx %d "
 +                 "len %d",
 +                 key->key_index & IEEE8021X_KEY_INDEX_FLAG ?
 +                 "unicast" : "broadcast",
 +                 key->key_index & IEEE8021X_KEY_INDEX_MASK, key_len);
 +
 +      if (sm->ctx->set_wep_key &&
 +          sm->ctx->set_wep_key(sm->ctx->ctx,
 +                               key->key_index & IEEE8021X_KEY_INDEX_FLAG,
 +                               key->key_index & IEEE8021X_KEY_INDEX_MASK,
 +                               datakey, key_len) < 0) {
 +              wpa_printf(MSG_WARNING, "EAPOL: Failed to set WEP key to the "
 +                         " driver.");
 +      } else {
 +              if (key->key_index & IEEE8021X_KEY_INDEX_FLAG)
 +                      sm->unicast_key_received = TRUE;
 +              else
 +                      sm->broadcast_key_received = TRUE;
 +
 +              if ((sm->unicast_key_received ||
 +                   !(sm->conf.required_keys & EAPOL_REQUIRE_KEY_UNICAST)) &&
 +                  (sm->broadcast_key_received ||
 +                   !(sm->conf.required_keys & EAPOL_REQUIRE_KEY_BROADCAST)))
 +              {
 +                      wpa_printf(MSG_DEBUG, "EAPOL: all required EAPOL-Key "
 +                                 "frames received");
 +                      sm->portValid = TRUE;
 +                      if (sm->ctx->eapol_done_cb)
 +                              sm->ctx->eapol_done_cb(sm->ctx->ctx);
 +              }
 +      }
 +#endif /* CONFIG_FIPS */
 +}
 +
 +
 +static void eapol_sm_getSuppRsp(struct eapol_sm *sm)
 +{
 +      wpa_printf(MSG_DEBUG, "EAPOL: getSuppRsp");
 +      /* EAP layer processing; no special code is needed, since Supplicant
 +       * Backend state machine is waiting for eapNoResp or eapResp to be set
 +       * and these are only set in the EAP state machine when the processing
 +       * has finished. */
 +}
 +
 +
 +static void eapol_sm_txSuppRsp(struct eapol_sm *sm)
 +{
 +      struct wpabuf *resp;
 +
 +      wpa_printf(MSG_DEBUG, "EAPOL: txSuppRsp");
 +
 +#ifdef CONFIG_EAP_PROXY
 +      if (sm->use_eap_proxy) {
 +              /* Get EAP Response from EAP Proxy */
 +              resp = eap_proxy_get_eapRespData(sm->eap_proxy);
 +              if (resp == NULL) {
 +                      wpa_printf(MSG_WARNING, "EAPOL: txSuppRsp - EAP Proxy "
 +                                 "response data not available");
 +                      return;
 +              }
 +      } else
 +#endif /* CONFIG_EAP_PROXY */
 +
 +      resp = eap_get_eapRespData(sm->eap);
 +      if (resp == NULL) {
 +              wpa_printf(MSG_WARNING, "EAPOL: txSuppRsp - EAP response data "
 +                         "not available");
 +              return;
 +      }
 +
 +      /* Send EAP-Packet from the EAP layer to the Authenticator */
 +      sm->ctx->eapol_send(sm->ctx->eapol_send_ctx,
 +                          IEEE802_1X_TYPE_EAP_PACKET, wpabuf_head(resp),
 +                          wpabuf_len(resp));
 +
 +      /* eapRespData is not used anymore, so free it here */
 +      wpabuf_free(resp);
 +
 +      if (sm->initial_req)
 +              sm->dot1xSuppEapolReqIdFramesRx++;
 +      else
 +              sm->dot1xSuppEapolReqFramesRx++;
 +      sm->dot1xSuppEapolRespFramesTx++;
 +      sm->dot1xSuppEapolFramesTx++;
 +}
 +
 +
 +static void eapol_sm_abortSupp(struct eapol_sm *sm)
 +{
 +      /* release system resources that may have been allocated for the
 +       * authentication session */
 +      os_free(sm->last_rx_key);
 +      sm->last_rx_key = NULL;
 +      wpabuf_free(sm->eapReqData);
 +      sm->eapReqData = NULL;
 +      eap_sm_abort(sm->eap);
 +}
 +
 +
 +static void eapol_sm_step_timeout(void *eloop_ctx, void *timeout_ctx)
 +{
 +      eapol_sm_step(timeout_ctx);
 +}
 +
 +
 +static void eapol_sm_set_port_authorized(struct eapol_sm *sm)
 +{
 +      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)
 +{
 +      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);
 +}
 +
 +
 +/**
 + * eapol_sm_step - EAPOL state machine step function
 + * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
 + *
 + * This function is called to notify the state machine about changed external
 + * variables. It will step through the EAPOL state machines in loop to process
 + * all triggered state changes.
 + */
 +void eapol_sm_step(struct eapol_sm *sm)
 +{
 +      int i;
 +
 +      /* In theory, it should be ok to run this in loop until !changed.
 +       * However, it is better to use a limit on number of iterations to
 +       * allow events (e.g., SIGTERM) to stop the program cleanly if the
 +       * state machine were to generate a busy loop. */
 +      for (i = 0; i < 100; i++) {
 +              sm->changed = FALSE;
 +              SM_STEP_RUN(SUPP_PAE);
 +              SM_STEP_RUN(KEY_RX);
 +              SM_STEP_RUN(SUPP_BE);
 +#ifdef CONFIG_EAP_PROXY
 +              if (sm->use_eap_proxy) {
 +                      /* Drive the EAP proxy state machine */
 +                      if (eap_proxy_sm_step(sm->eap_proxy, sm->eap))
 +                              sm->changed = TRUE;
 +              } else
 +#endif /* CONFIG_EAP_PROXY */
 +              if (eap_peer_sm_step(sm->eap))
 +                      sm->changed = TRUE;
 +              if (!sm->changed)
 +                      break;
 +      }
 +
 +      if (sm->changed) {
 +              /* restart EAPOL state machine step from timeout call in order
 +               * to allow other events to be processed. */
 +              eloop_cancel_timeout(eapol_sm_step_timeout, NULL, sm);
 +              eloop_register_timeout(0, 0, eapol_sm_step_timeout, NULL, sm);
 +      }
 +
 +      if (sm->ctx->cb && sm->cb_status != EAPOL_CB_IN_PROGRESS) {
 +              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, result, sm->ctx->cb_ctx);
 +      }
 +}
 +
 +
 +#ifdef CONFIG_CTRL_IFACE
 +static const char *eapol_supp_pae_state(int state)
 +{
 +      switch (state) {
 +      case SUPP_PAE_LOGOFF:
 +              return "LOGOFF";
 +      case SUPP_PAE_DISCONNECTED:
 +              return "DISCONNECTED";
 +      case SUPP_PAE_CONNECTING:
 +              return "CONNECTING";
 +      case SUPP_PAE_AUTHENTICATING:
 +              return "AUTHENTICATING";
 +      case SUPP_PAE_HELD:
 +              return "HELD";
 +      case SUPP_PAE_AUTHENTICATED:
 +              return "AUTHENTICATED";
 +      case SUPP_PAE_RESTART:
 +              return "RESTART";
 +      default:
 +              return "UNKNOWN";
 +      }
 +}
 +
 +
 +static const char *eapol_supp_be_state(int state)
 +{
 +      switch (state) {
 +      case SUPP_BE_REQUEST:
 +              return "REQUEST";
 +      case SUPP_BE_RESPONSE:
 +              return "RESPONSE";
 +      case SUPP_BE_SUCCESS:
 +              return "SUCCESS";
 +      case SUPP_BE_FAIL:
 +              return "FAIL";
 +      case SUPP_BE_TIMEOUT:
 +              return "TIMEOUT";
 +      case SUPP_BE_IDLE:
 +              return "IDLE";
 +      case SUPP_BE_INITIALIZE:
 +              return "INITIALIZE";
 +      case SUPP_BE_RECEIVE:
 +              return "RECEIVE";
 +      default:
 +              return "UNKNOWN";
 +      }
 +}
 +
 +
 +static const char * eapol_port_status(PortStatus status)
 +{
 +      if (status == Authorized)
 +              return "Authorized";
 +      else
 +              return "Unauthorized";
 +}
 +#endif /* CONFIG_CTRL_IFACE */
 +
 +
 +#if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG)
 +static const char * eapol_port_control(PortControl ctrl)
 +{
 +      switch (ctrl) {
 +      case Auto:
 +              return "Auto";
 +      case ForceUnauthorized:
 +              return "ForceUnauthorized";
 +      case ForceAuthorized:
 +              return "ForceAuthorized";
 +      default:
 +              return "Unknown";
 +      }
 +}
 +#endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */
 +
 +
 +/**
 + * eapol_sm_configure - Set EAPOL variables
 + * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
 + * @heldPeriod: dot1xSuppHeldPeriod
 + * @authPeriod: dot1xSuppAuthPeriod
 + * @startPeriod: dot1xSuppStartPeriod
 + * @maxStart: dot1xSuppMaxStart
 + *
 + * Set configurable EAPOL state machine variables. Each variable can be set to
 + * the given value or ignored if set to -1 (to set only some of the variables).
 + */
 +void eapol_sm_configure(struct eapol_sm *sm, int heldPeriod, int authPeriod,
 +                      int startPeriod, int maxStart)
 +{
 +      if (sm == NULL)
 +              return;
 +      if (heldPeriod >= 0)
 +              sm->heldPeriod = heldPeriod;
 +      if (authPeriod >= 0)
 +              sm->authPeriod = authPeriod;
 +      if (startPeriod >= 0)
 +              sm->startPeriod = startPeriod;
 +      if (maxStart >= 0)
 +              sm->maxStart = maxStart;
 +}
 +
 +
 +/**
 + * eapol_sm_get_method_name - Get EAPOL method name
 + * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
 + * Returns: Static string containing name of current eap method or NULL
 + */
 +const char * eapol_sm_get_method_name(struct eapol_sm *sm)
 +{
 +      if (sm->SUPP_PAE_state != SUPP_PAE_AUTHENTICATED ||
 +          sm->suppPortStatus != Authorized)
 +              return NULL;
 +
 +      return eap_sm_get_method_name(sm->eap);
 +}
 +
 +
 +#ifdef CONFIG_CTRL_IFACE
 +/**
 + * eapol_sm_get_status - Get EAPOL state machine status
 + * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
 + * @buf: Buffer for status information
 + * @buflen: Maximum buffer length
 + * @verbose: Whether to include verbose status information
 + * Returns: Number of bytes written to buf.
 + *
 + * Query EAPOL state machine for status information. This function fills in a
 + * text area with current status information from the EAPOL state machine. If
 + * the buffer (buf) is not large enough, status information will be truncated
 + * to fit the buffer.
 + */
 +int eapol_sm_get_status(struct eapol_sm *sm, char *buf, size_t buflen,
 +                      int verbose)
 +{
 +      int len, ret;
 +      if (sm == NULL)
 +              return 0;
 +
 +      len = os_snprintf(buf, buflen,
 +                        "Supplicant PAE state=%s\n"
 +                        "suppPortStatus=%s\n",
 +                        eapol_supp_pae_state(sm->SUPP_PAE_state),
 +                        eapol_port_status(sm->suppPortStatus));
 +      if (os_snprintf_error(buflen, len))
 +              return 0;
 +
 +      if (verbose) {
 +              ret = os_snprintf(buf + len, buflen - len,
 +                                "heldPeriod=%u\n"
 +                                "authPeriod=%u\n"
 +                                "startPeriod=%u\n"
 +                                "maxStart=%u\n"
 +                                "portControl=%s\n"
 +                                "Supplicant Backend state=%s\n",
 +                                sm->heldPeriod,
 +                                sm->authPeriod,
 +                                sm->startPeriod,
 +                                sm->maxStart,
 +                                eapol_port_control(sm->portControl),
 +                                eapol_supp_be_state(sm->SUPP_BE_state));
 +              if (os_snprintf_error(buflen - len, ret))
 +                      return len;
 +              len += ret;
 +      }
 +
 +#ifdef CONFIG_EAP_PROXY
 +      if (sm->use_eap_proxy)
 +              len += eap_proxy_sm_get_status(sm->eap_proxy,
 +                                             buf + len, buflen - len,
 +                                             verbose);
 +      else
 +#endif /* CONFIG_EAP_PROXY */
 +      len += eap_sm_get_status(sm->eap, buf + len, buflen - len, verbose);
 +
 +      return len;
 +}
 +
 +
 +/**
 + * eapol_sm_get_mib - Get EAPOL state machine MIBs
 + * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
 + * @buf: Buffer for MIB information
 + * @buflen: Maximum buffer length
 + * Returns: Number of bytes written to buf.
 + *
 + * Query EAPOL state machine for MIB information. This function fills in a
 + * text area with current MIB information from the EAPOL state machine. If
 + * the buffer (buf) is not large enough, MIB information will be truncated to
 + * fit the buffer.
 + */
 +int eapol_sm_get_mib(struct eapol_sm *sm, char *buf, size_t buflen)
 +{
 +      size_t len;
 +      int ret;
 +
 +      if (sm == NULL)
 +              return 0;
 +      ret = os_snprintf(buf, buflen,
 +                        "dot1xSuppPaeState=%d\n"
 +                        "dot1xSuppHeldPeriod=%u\n"
 +                        "dot1xSuppAuthPeriod=%u\n"
 +                        "dot1xSuppStartPeriod=%u\n"
 +                        "dot1xSuppMaxStart=%u\n"
 +                        "dot1xSuppSuppControlledPortStatus=%s\n"
 +                        "dot1xSuppBackendPaeState=%d\n",
 +                        sm->SUPP_PAE_state,
 +                        sm->heldPeriod,
 +                        sm->authPeriod,
 +                        sm->startPeriod,
 +                        sm->maxStart,
 +                        sm->suppPortStatus == Authorized ?
 +                        "Authorized" : "Unauthorized",
 +                        sm->SUPP_BE_state);
 +
 +      if (os_snprintf_error(buflen, ret))
 +              return 0;
 +      len = ret;
 +
 +      ret = os_snprintf(buf + len, buflen - len,
 +                        "dot1xSuppEapolFramesRx=%u\n"
 +                        "dot1xSuppEapolFramesTx=%u\n"
 +                        "dot1xSuppEapolStartFramesTx=%u\n"
 +                        "dot1xSuppEapolLogoffFramesTx=%u\n"
 +                        "dot1xSuppEapolRespFramesTx=%u\n"
 +                        "dot1xSuppEapolReqIdFramesRx=%u\n"
 +                        "dot1xSuppEapolReqFramesRx=%u\n"
 +                        "dot1xSuppInvalidEapolFramesRx=%u\n"
 +                        "dot1xSuppEapLengthErrorFramesRx=%u\n"
 +                        "dot1xSuppLastEapolFrameVersion=%u\n"
 +                        "dot1xSuppLastEapolFrameSource=" MACSTR "\n",
 +                        sm->dot1xSuppEapolFramesRx,
 +                        sm->dot1xSuppEapolFramesTx,
 +                        sm->dot1xSuppEapolStartFramesTx,
 +                        sm->dot1xSuppEapolLogoffFramesTx,
 +                        sm->dot1xSuppEapolRespFramesTx,
 +                        sm->dot1xSuppEapolReqIdFramesRx,
 +                        sm->dot1xSuppEapolReqFramesRx,
 +                        sm->dot1xSuppInvalidEapolFramesRx,
 +                        sm->dot1xSuppEapLengthErrorFramesRx,
 +                        sm->dot1xSuppLastEapolFrameVersion,
 +                        MAC2STR(sm->dot1xSuppLastEapolFrameSource));
 +
 +      if (os_snprintf_error(buflen - len, ret))
 +              return len;
 +      len += ret;
 +
 +      return len;
 +}
 +#endif /* CONFIG_CTRL_IFACE */
 +
 +
 +/**
 + * eapol_sm_rx_eapol - Process received EAPOL frames
 + * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
 + * @src: Source MAC address of the EAPOL packet
 + * @buf: Pointer to the beginning of the EAPOL data (EAPOL header)
 + * @len: Length of the EAPOL frame
 + * Returns: 1 = EAPOL frame processed, 0 = not for EAPOL state machine,
 + * -1 failure
 + */
 +int eapol_sm_rx_eapol(struct eapol_sm *sm, const u8 *src, const u8 *buf,
 +                    size_t len)
 +{
 +      const struct ieee802_1x_hdr *hdr;
 +      const struct ieee802_1x_eapol_key *key;
 +      int data_len;
 +      int res = 1;
 +      size_t plen;
 +
 +      if (sm == NULL)
 +              return 0;
 +      sm->dot1xSuppEapolFramesRx++;
 +      if (len < sizeof(*hdr)) {
 +              sm->dot1xSuppInvalidEapolFramesRx++;
 +              return 0;
 +      }
 +      hdr = (const struct ieee802_1x_hdr *) buf;
 +      sm->dot1xSuppLastEapolFrameVersion = hdr->version;
 +      os_memcpy(sm->dot1xSuppLastEapolFrameSource, src, ETH_ALEN);
 +      if (hdr->version < EAPOL_VERSION) {
 +              /* TODO: backwards compatibility */
 +      }
 +      plen = be_to_host16(hdr->length);
 +      if (plen > len - sizeof(*hdr)) {
 +              sm->dot1xSuppEapLengthErrorFramesRx++;
 +              return 0;
 +      }
 +#ifdef CONFIG_WPS
 +      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)) {
 +              const struct eap_hdr *ehdr =
 +                      (const struct eap_hdr *) (hdr + 1);
 +              u16 elen;
 +
 +              elen = be_to_host16(ehdr->length);
 +              if (elen > plen && elen <= len - sizeof(*hdr)) {
 +                      /*
 +                       * Buffalo WHR-G125 Ver.1.47 seems to send EAP-WPS
 +                       * packets with too short EAPOL header length field
 +                       * (14 octets). This is fixed in firmware Ver.1.49.
 +                       * As a workaround, fix the EAPOL header based on the
 +                       * correct length in the EAP packet.
 +                       */
 +                      wpa_printf(MSG_DEBUG, "EAPOL: Workaround - fix EAPOL "
 +                                 "payload length based on EAP header: "
 +                                 "%d -> %d", (int) plen, elen);
 +                      plen = elen;
 +              }
 +      }
 +#endif /* CONFIG_WPS */
 +      data_len = plen + sizeof(*hdr);
 +
 +      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
 +                       * EAPOL state machines.
 +                       */
 +                      eapol_sm_abort_cached(sm);
 +              }
 +              wpabuf_free(sm->eapReqData);
 +              sm->eapReqData = wpabuf_alloc_copy(hdr + 1, plen);
 +              if (sm->eapReqData) {
 +                      wpa_printf(MSG_DEBUG, "EAPOL: Received EAP-Packet "
 +                                 "frame");
 +                      sm->eapolEap = TRUE;
 +#ifdef CONFIG_EAP_PROXY
 +                      if (sm->use_eap_proxy) {
 +                              eap_proxy_packet_update(
 +                                      sm->eap_proxy,
 +                                      wpabuf_mhead_u8(sm->eapReqData),
 +                                      wpabuf_len(sm->eapReqData));
 +                              wpa_printf(MSG_DEBUG, "EAPOL: eap_proxy "
 +                                         "EAP Req updated");
 +                      }
 +#endif /* CONFIG_EAP_PROXY */
 +                      eapol_sm_step(sm);
 +              }
 +              break;
 +      case IEEE802_1X_TYPE_EAPOL_KEY:
 +              if (plen < sizeof(*key)) {
 +                      wpa_printf(MSG_DEBUG, "EAPOL: Too short EAPOL-Key "
 +                                 "frame received");
 +                      break;
 +              }
 +              key = (const struct ieee802_1x_eapol_key *) (hdr + 1);
 +              if (key->type == EAPOL_KEY_TYPE_WPA ||
 +                  key->type == EAPOL_KEY_TYPE_RSN) {
 +                      /* WPA Supplicant takes care of this frame. */
 +                      wpa_printf(MSG_DEBUG, "EAPOL: Ignoring WPA EAPOL-Key "
 +                                 "frame in EAPOL state machines");
 +                      res = 0;
 +                      break;
 +              }
 +              if (key->type != EAPOL_KEY_TYPE_RC4) {
 +                      wpa_printf(MSG_DEBUG, "EAPOL: Ignored unknown "
 +                                 "EAPOL-Key type %d", key->type);
 +                      break;
 +              }
 +              os_free(sm->last_rx_key);
 +              sm->last_rx_key = os_malloc(data_len);
 +              if (sm->last_rx_key) {
 +                      wpa_printf(MSG_DEBUG, "EAPOL: Received EAPOL-Key "
 +                                 "frame");
 +                      os_memcpy(sm->last_rx_key, buf, data_len);
 +                      sm->last_rx_key_len = data_len;
 +                      sm->rxKey = TRUE;
 +                      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);
 +              sm->dot1xSuppInvalidEapolFramesRx++;
 +              break;
 +      }
 +
 +      return res;
 +}
 +
 +
 +/**
 + * eapol_sm_notify_tx_eapol_key - Notification about transmitted EAPOL packet
 + * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
 + *
 + * Notify EAPOL state machine about transmitted EAPOL packet from an external
 + * component, e.g., WPA. This will update the statistics.
 + */
 +void eapol_sm_notify_tx_eapol_key(struct eapol_sm *sm)
 +{
 +      if (sm)
 +              sm->dot1xSuppEapolFramesTx++;
 +}
 +
 +
 +/**
 + * eapol_sm_notify_portEnabled - Notification about portEnabled change
 + * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
 + * @enabled: New portEnabled value
 + *
 + * Notify EAPOL state machine about new portEnabled value.
 + */
 +void eapol_sm_notify_portEnabled(struct eapol_sm *sm, Boolean enabled)
 +{
 +      if (sm == NULL)
 +              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);
 +}
 +
 +
 +/**
 + * eapol_sm_notify_portValid - Notification about portValid change
 + * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
 + * @valid: New portValid value
 + *
 + * Notify EAPOL state machine about new portValid value.
 + */
 +void eapol_sm_notify_portValid(struct eapol_sm *sm, Boolean valid)
 +{
 +      if (sm == NULL)
 +              return;
 +      wpa_printf(MSG_DEBUG, "EAPOL: External notification - "
 +                 "portValid=%d", valid);
 +      sm->portValid = valid;
 +      eapol_sm_step(sm);
 +}
 +
 +
 +/**
 + * eapol_sm_notify_eap_success - Notification of external EAP success trigger
 + * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
 + * @success: %TRUE = set success, %FALSE = clear success
 + *
 + * Notify the EAPOL state machine that external event has forced EAP state to
 + * success (success = %TRUE). This can be cleared by setting success = %FALSE.
 + *
 + * This function is called to update EAP state when WPA-PSK key handshake has
 + * been completed successfully since WPA-PSK does not use EAP state machine.
 + */
 +void eapol_sm_notify_eap_success(struct eapol_sm *sm, Boolean success)
 +{
 +      if (sm == NULL)
 +              return;
 +      wpa_printf(MSG_DEBUG, "EAPOL: External notification - "
 +                 "EAP success=%d", success);
 +      sm->eapSuccess = success;
 +      sm->altAccept = success;
 +      if (success)
 +              eap_notify_success(sm->eap);
 +      eapol_sm_step(sm);
 +}
 +
 +
 +/**
 + * eapol_sm_notify_eap_fail - Notification of external EAP failure trigger
 + * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
 + * @fail: %TRUE = set failure, %FALSE = clear failure
 + *
 + * Notify EAPOL state machine that external event has forced EAP state to
 + * failure (fail = %TRUE). This can be cleared by setting fail = %FALSE.
 + */
 +void eapol_sm_notify_eap_fail(struct eapol_sm *sm, Boolean fail)
 +{
 +      if (sm == NULL)
 +              return;
 +      wpa_printf(MSG_DEBUG, "EAPOL: External notification - "
 +                 "EAP fail=%d", fail);
 +      sm->eapFail = fail;
 +      sm->altReject = fail;
 +      eapol_sm_step(sm);
 +}
 +
 +
 +/**
 + * eapol_sm_notify_config - Notification of EAPOL configuration change
 + * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
 + * @config: Pointer to current network EAP configuration
 + * @conf: Pointer to EAPOL configuration data
 + *
 + * Notify EAPOL state machine that configuration has changed. config will be
 + * stored as a backpointer to network configuration. This can be %NULL to clear
 + * the stored pointed. conf will be copied to local EAPOL/EAP configuration
 + * data. If conf is %NULL, this part of the configuration change will be
 + * skipped.
 + */
 +void eapol_sm_notify_config(struct eapol_sm *sm,
 +                          struct eap_peer_config *config,
 +                          const struct eapol_config *conf)
 +{
 +      if (sm == NULL)
 +              return;
 +
 +      sm->config = config;
 +#ifdef CONFIG_EAP_PROXY
 +      sm->use_eap_proxy = eap_proxy_notify_config(sm->eap_proxy, config) > 0;
 +#endif /* CONFIG_EAP_PROXY */
 +
 +      if (conf == NULL)
 +              return;
 +
 +      sm->conf.accept_802_1x_keys = conf->accept_802_1x_keys;
 +      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 */
 +              return;
 +      }
 +#endif /* CONFIG_EAP_PROXY */
 +      if (sm->eap) {
 +              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);
 +      }
 +}
 +
 +
 +/**
 + * eapol_sm_get_key - Get master session key (MSK) from EAP
 + * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
 + * @key: Pointer for key buffer
 + * @len: Number of bytes to copy to key
 + * Returns: 0 on success (len of key available), maximum available key len
 + * (>0) if key is available but it is shorter than len, or -1 on failure.
 + *
 + * Fetch EAP keying material (MSK, eapKeyData) from EAP state machine. The key
 + * is available only after a successful authentication.
 + */
 +int eapol_sm_get_key(struct eapol_sm *sm, u8 *key, size_t len)
 +{
 +      const u8 *eap_key;
 +      size_t eap_len;
 +
 +#ifdef CONFIG_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");
 +                      return -1;
 +              }
 +              eap_key = eap_proxy_get_eapKeyData(sm->eap_proxy, &eap_len);
 +              if (eap_key == NULL) {
 +                      wpa_printf(MSG_DEBUG, "EAPOL: Failed to get "
 +                                 "eapKeyData");
 +                      return -1;
 +              }
 +              goto key_fetched;
 +      }
 +#endif /* CONFIG_EAP_PROXY */
 +      if (sm == NULL || !eap_key_available(sm->eap)) {
 +              wpa_printf(MSG_DEBUG, "EAPOL: EAP key not available");
 +              return -1;
 +      }
 +      eap_key = eap_get_eapKeyData(sm->eap, &eap_len);
 +      if (eap_key == NULL) {
 +              wpa_printf(MSG_DEBUG, "EAPOL: Failed to get eapKeyData");
 +              return -1;
 +      }
 +#ifdef CONFIG_EAP_PROXY
 +key_fetched:
 +#endif /* CONFIG_EAP_PROXY */
 +      if (len > eap_len) {
 +              wpa_printf(MSG_DEBUG, "EAPOL: Requested key length (%lu) not "
 +                         "available (len=%lu)",
 +                         (unsigned long) len, (unsigned long) eap_len);
 +              return eap_len;
 +      }
 +      os_memcpy(key, eap_key, len);
 +      wpa_printf(MSG_DEBUG, "EAPOL: Successfully fetched key (len=%lu)",
 +                 (unsigned long) len);
 +      return 0;
 +}
 +
 +
 +/**
 + * 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
 + *
 + * Notify EAPOL state machines that user requested logon/logoff.
 + */
 +void eapol_sm_notify_logoff(struct eapol_sm *sm, Boolean logoff)
 +{
 +      if (sm) {
 +              sm->userLogoff = logoff;
 +              if (!logoff) {
 +                      /* If there is a delayed txStart queued, start now. */
 +                      sm->startWhen = 0;
 +              }
 +              eapol_sm_step(sm);
 +      }
 +}
 +
 +
 +/**
 + * eapol_sm_notify_pmkid_attempt - Notification of successful PMKSA caching
 + * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
 + *
 + * Notify EAPOL state machines that PMKSA caching was successful. This is used
 + * to move EAPOL and EAP state machines into authenticated/successful state.
 + */
 +void eapol_sm_notify_cached(struct eapol_sm *sm)
 +{
 +      if (sm == NULL)
 +              return;
 +      wpa_printf(MSG_DEBUG, "EAPOL: PMKSA caching was used - skip EAPOL");
 +      sm->eapSuccess = TRUE;
 +      eap_notify_success(sm->eap);
 +      eapol_sm_step(sm);
 +}
 +
 +
 +/**
 + * eapol_sm_notify_pmkid_attempt - Notification of PMKSA caching
 + * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
 + *
 + * Notify EAPOL state machines if PMKSA caching is used.
 + */
 +void eapol_sm_notify_pmkid_attempt(struct eapol_sm *sm)
 +{
 +      if (sm == NULL)
 +              return;
 +      wpa_printf(MSG_DEBUG, "RSN: Trying to use cached PMKSA");
 +      sm->cached_pmk = TRUE;
 +}
 +
 +
 +static void eapol_sm_abort_cached(struct eapol_sm *sm)
 +{
 +      wpa_printf(MSG_DEBUG, "RSN: Authenticator did not accept PMKID, "
 +                 "doing full EAP authentication");
 +      if (sm == NULL)
 +              return;
 +      sm->cached_pmk = FALSE;
 +      sm->SUPP_PAE_state = SUPP_PAE_CONNECTING;
 +      eapol_sm_set_port_unauthorized(sm);
 +
 +      /* Make sure we do not start sending EAPOL-Start frames first, but
 +       * instead move to RESTART state to start EAPOL authentication. */
 +      sm->startWhen = 3;
 +      eapol_enable_timer_tick(sm);
 +
 +      if (sm->ctx->aborted_cached)
 +              sm->ctx->aborted_cached(sm->ctx->ctx);
 +}
 +
 +
 +/**
 + * eapol_sm_register_scard_ctx - Notification of smart card context
 + * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
 + * @ctx: Context data for smart card operations
 + *
 + * Notify EAPOL state machines of context data for smart card operations. This
 + * context data will be used as a parameter for scard_*() functions.
 + */
 +void eapol_sm_register_scard_ctx(struct eapol_sm *sm, void *ctx)
 +{
 +      if (sm) {
 +              sm->ctx->scard_ctx = ctx;
 +              eap_register_scard_ctx(sm->eap, ctx);
 +      }
 +}
 +
 +
 +/**
 + * eapol_sm_notify_portControl - Notification of portControl changes
 + * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
 + * @portControl: New value for portControl variable
 + *
 + * Notify EAPOL state machines that portControl variable has changed.
 + */
 +void eapol_sm_notify_portControl(struct eapol_sm *sm, PortControl portControl)
 +{
 +      if (sm == NULL)
 +              return;
 +      wpa_printf(MSG_DEBUG, "EAPOL: External notification - "
 +                 "portControl=%s", eapol_port_control(portControl));
 +      sm->portControl = portControl;
 +      eapol_sm_step(sm);
 +}
 +
 +
 +/**
 + * eapol_sm_notify_ctrl_attached - Notification of attached monitor
 + * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
 + *
 + * Notify EAPOL state machines that a monitor was attached to the control
 + * interface to trigger re-sending of pending requests for user input.
 + */
 +void eapol_sm_notify_ctrl_attached(struct eapol_sm *sm)
 +{
 +      if (sm == NULL)
 +              return;
 +      eap_sm_notify_ctrl_attached(sm->eap);
 +}
 +
 +
 +/**
 + * eapol_sm_notify_ctrl_response - Notification of received user input
 + * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
 + *
 + * Notify EAPOL state machines that a control response, i.e., user
 + * input, was received in order to trigger retrying of a pending EAP request.
 + */
 +void eapol_sm_notify_ctrl_response(struct eapol_sm *sm)
 +{
 +      if (sm == NULL)
 +              return;
 +      if (sm->eapReqData && !sm->eapReq) {
 +              wpa_printf(MSG_DEBUG, "EAPOL: received control response (user "
 +                         "input) notification - retrying pending EAP "
 +                         "Request");
 +              sm->eapolEap = TRUE;
 +              sm->eapReq = TRUE;
 +              eapol_sm_step(sm);
 +      }
 +}
 +
 +
 +/**
 + * eapol_sm_request_reauth - Request reauthentication
 + * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
 + *
 + * This function can be used to request EAPOL reauthentication, e.g., when the
 + * current PMKSA entry is nearing expiration.
 + */
 +void eapol_sm_request_reauth(struct eapol_sm *sm)
 +{
 +      if (sm == NULL || sm->SUPP_PAE_state != SUPP_PAE_AUTHENTICATED)
 +              return;
 +      eapol_sm_txStart(sm);
 +}
 +
 +
 +/**
 + * eapol_sm_notify_lower_layer_success - Notification of lower layer success
 + * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
 + * @in_eapol_sm: Whether the caller is already running inside EAPOL state
 + * machine loop (eapol_sm_step())
 + *
 + * Notify EAPOL (and EAP) state machines that a lower layer has detected a
 + * successful authentication. This is used to recover from dropped EAP-Success
 + * messages.
 + */
 +void eapol_sm_notify_lower_layer_success(struct eapol_sm *sm, int in_eapol_sm)
 +{
 +      if (sm == NULL)
 +              return;
 +      eap_notify_lower_layer_success(sm->eap);
 +      if (!in_eapol_sm)
 +              eapol_sm_step(sm);
 +}
 +
 +
 +/**
 + * eapol_sm_invalidate_cached_session - Mark cached EAP session data invalid
 + * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
 + */
 +void eapol_sm_invalidate_cached_session(struct eapol_sm *sm)
 +{
 +      if (sm)
 +              eap_invalidate_cached_session(sm->eap);
 +}
 +
 +
 +static struct eap_peer_config * eapol_sm_get_config(void *ctx)
 +{
 +      struct eapol_sm *sm = ctx;
 +      return sm ? sm->config : NULL;
 +}
 +
 +
 +static struct wpabuf * eapol_sm_get_eapReqData(void *ctx)
 +{
 +      struct eapol_sm *sm = ctx;
 +      if (sm == NULL || sm->eapReqData == NULL)
 +              return NULL;
 +
 +      return sm->eapReqData;
 +}
 +
 +
 +static Boolean eapol_sm_get_bool(void *ctx, enum eapol_bool_var variable)
 +{
 +      struct eapol_sm *sm = ctx;
 +      if (sm == NULL)
 +              return FALSE;
 +      switch (variable) {
 +      case EAPOL_eapSuccess:
 +              return sm->eapSuccess;
 +      case EAPOL_eapRestart:
 +              return sm->eapRestart;
 +      case EAPOL_eapFail:
 +              return sm->eapFail;
 +      case EAPOL_eapResp:
 +              return sm->eapResp;
 +      case EAPOL_eapNoResp:
 +              return sm->eapNoResp;
 +      case EAPOL_eapReq:
 +              return sm->eapReq;
 +      case EAPOL_portEnabled:
 +              return sm->portEnabled;
 +      case EAPOL_altAccept:
 +              return sm->altAccept;
 +      case EAPOL_altReject:
 +              return sm->altReject;
 +      case EAPOL_eapTriggerStart:
 +              return sm->eapTriggerStart;
 +      }
 +      return FALSE;
 +}
 +
 +
 +static void eapol_sm_set_bool(void *ctx, enum eapol_bool_var variable,
 +                            Boolean value)
 +{
 +      struct eapol_sm *sm = ctx;
 +      if (sm == NULL)
 +              return;
 +      switch (variable) {
 +      case EAPOL_eapSuccess:
 +              sm->eapSuccess = value;
 +              break;
 +      case EAPOL_eapRestart:
 +              sm->eapRestart = value;
 +              break;
 +      case EAPOL_eapFail:
 +              sm->eapFail = value;
 +              break;
 +      case EAPOL_eapResp:
 +              sm->eapResp = value;
 +              break;
 +      case EAPOL_eapNoResp:
 +              sm->eapNoResp = value;
 +              break;
 +      case EAPOL_eapReq:
 +              sm->eapReq = value;
 +              break;
 +      case EAPOL_portEnabled:
 +              sm->portEnabled = value;
 +              break;
 +      case EAPOL_altAccept:
 +              sm->altAccept = value;
 +              break;
 +      case EAPOL_altReject:
 +              sm->altReject = value;
 +              break;
 +      case EAPOL_eapTriggerStart:
 +              sm->eapTriggerStart = value;
 +              break;
 +      }
 +}
 +
 +
 +static unsigned int eapol_sm_get_int(void *ctx, enum eapol_int_var variable)
 +{
 +      struct eapol_sm *sm = ctx;
 +      if (sm == NULL)
 +              return 0;
 +      switch (variable) {
 +      case EAPOL_idleWhile:
 +              return sm->idleWhile;
 +      }
 +      return 0;
 +}
 +
 +
 +static void eapol_sm_set_int(void *ctx, enum eapol_int_var variable,
 +                           unsigned int value)
 +{
 +      struct eapol_sm *sm = ctx;
 +      if (sm == NULL)
 +              return;
 +      switch (variable) {
 +      case EAPOL_idleWhile:
 +              sm->idleWhile = value;
 +              if (sm->idleWhile > 0)
 +                      eapol_enable_timer_tick(sm);
 +              break;
 +      }
 +}
 +
 +
 +static void eapol_sm_set_config_blob(void *ctx, struct wpa_config_blob *blob)
 +{
 +#ifndef CONFIG_NO_CONFIG_BLOBS
 +      struct eapol_sm *sm = ctx;
 +      if (sm && sm->ctx && sm->ctx->set_config_blob)
 +              sm->ctx->set_config_blob(sm->ctx->ctx, blob);
 +#endif /* CONFIG_NO_CONFIG_BLOBS */
 +}
 +
 +
 +static const struct wpa_config_blob *
 +eapol_sm_get_config_blob(void *ctx, const char *name)
 +{
 +#ifndef CONFIG_NO_CONFIG_BLOBS
 +      struct eapol_sm *sm = ctx;
 +      if (sm && sm->ctx && sm->ctx->get_config_blob)
 +              return sm->ctx->get_config_blob(sm->ctx->ctx, name);
 +      else
 +              return NULL;
 +#else /* CONFIG_NO_CONFIG_BLOBS */
 +      return NULL;
 +#endif /* CONFIG_NO_CONFIG_BLOBS */
 +}
 +
 +
 +static void eapol_sm_notify_pending(void *ctx)
 +{
 +      struct eapol_sm *sm = ctx;
 +      if (sm == NULL)
 +              return;
 +      if (sm->eapReqData && !sm->eapReq) {
 +              wpa_printf(MSG_DEBUG, "EAPOL: received notification from EAP "
 +                         "state machine - retrying pending EAP Request");
 +              sm->eapolEap = TRUE;
 +              sm->eapReq = TRUE;
 +              eapol_sm_step(sm);
 +      }
 +}
 +
 +
 +#if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG)
 +static void eapol_sm_eap_param_needed(void *ctx, enum wpa_ctrl_req_type field,
 +                                    const char *txt)
 +{
 +      struct eapol_sm *sm = ctx;
 +      wpa_printf(MSG_DEBUG, "EAPOL: EAP parameter needed");
 +      if (sm->ctx->eap_param_needed)
 +              sm->ctx->eap_param_needed(sm->ctx->ctx, field, txt);
 +}
 +#else /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */
 +#define eapol_sm_eap_param_needed NULL
 +#endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */
 +
 +static void eapol_sm_notify_cert(void *ctx, int depth, const char *subject,
 +                               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, altsubject,
 +                               num_altsubject, cert_hash, cert);
 +}
 +
 +
 +static void eapol_sm_notify_status(void *ctx, const char *status,
 +                                 const char *parameter)
 +{
 +      struct eapol_sm *sm = ctx;
 +
 +      if (sm->ctx->status_cb)
 +              sm->ctx->status_cb(sm->ctx->ctx, status, parameter);
 +}
 +
 +
 +#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;
 +
 +      if (sm->ctx->set_anon_id)
 +              sm->ctx->set_anon_id(sm->ctx->ctx, id, len);
 +}
 +
 +
++static const struct eapol_callbacks eapol_cb =
 +{
 +      eapol_sm_get_config,
 +      eapol_sm_get_bool,
 +      eapol_sm_set_bool,
 +      eapol_sm_get_int,
 +      eapol_sm_set_int,
 +      eapol_sm_get_eapReqData,
 +      eapol_sm_set_config_blob,
 +      eapol_sm_get_config_blob,
 +      eapol_sm_notify_pending,
 +      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
 +};
 +
 +
 +/**
 + * eapol_sm_init - Initialize EAPOL state machine
 + * @ctx: Pointer to EAPOL context data; this needs to be an allocated buffer
 + * and EAPOL state machine will free it in eapol_sm_deinit()
 + * Returns: Pointer to the allocated EAPOL state machine or %NULL on failure
 + *
 + * Allocate and initialize an EAPOL state machine.
 + */
 +struct eapol_sm *eapol_sm_init(struct eapol_ctx *ctx)
 +{
 +      struct eapol_sm *sm;
 +      struct eap_config conf;
 +      sm = os_zalloc(sizeof(*sm));
 +      if (sm == NULL)
 +              return NULL;
 +      sm->ctx = ctx;
 +
 +      sm->portControl = Auto;
 +
 +      /* Supplicant PAE state machine */
 +      sm->heldPeriod = 60;
 +      sm->startPeriod = 30;
 +      sm->maxStart = 3;
 +
 +      /* Supplicant Backend state machine */
 +      sm->authPeriod = 30;
 +
 +      os_memset(&conf, 0, sizeof(conf));
 +      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;
 +
 +      sm->eap = eap_peer_sm_init(sm, &eapol_cb, sm->ctx->msg_ctx, &conf);
 +      if (sm->eap == NULL) {
 +              os_free(sm);
 +              return NULL;
 +      }
 +
 +#ifdef CONFIG_EAP_PROXY
 +      sm->use_eap_proxy = FALSE;
 +      sm->eap_proxy = eap_proxy_init(sm, &eapol_cb, sm->ctx->msg_ctx);
 +      if (sm->eap_proxy == NULL) {
 +              wpa_printf(MSG_ERROR, "Unable to initialize EAP Proxy");
 +      }
 +#endif /* CONFIG_EAP_PROXY */
 +
 +      /* Initialize EAPOL state machines */
 +      sm->force_authorized_update = TRUE;
 +      sm->initialize = TRUE;
 +      eapol_sm_step(sm);
 +      sm->initialize = FALSE;
 +      eapol_sm_step(sm);
 +
 +      sm->timer_tick_enabled = 1;
 +      eloop_register_timeout(1, 0, eapol_port_timers_tick, NULL, sm);
 +
 +      return sm;
 +}
 +
 +
 +/**
 + * eapol_sm_deinit - Deinitialize EAPOL state machine
 + * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
 + *
 + * Deinitialize and free EAPOL state machine.
 + */
 +void eapol_sm_deinit(struct eapol_sm *sm)
 +{
 +      if (sm == NULL)
 +              return;
 +      eloop_cancel_timeout(eapol_sm_step_timeout, NULL, sm);
 +      eloop_cancel_timeout(eapol_port_timers_tick, NULL, sm);
 +      eap_peer_sm_deinit(sm->eap);
 +#ifdef CONFIG_EAP_PROXY
 +      eap_proxy_deinit(sm->eap_proxy);
 +#endif /* CONFIG_EAP_PROXY */
 +      os_free(sm->last_rx_key);
 +      wpabuf_free(sm->eapReqData);
 +      os_free(sm->ctx);
 +      os_free(sm);
 +}
 +
 +
 +void eapol_sm_set_ext_pw_ctx(struct eapol_sm *sm,
 +                           struct ext_password_data *ext)
 +{
 +      if (sm && sm->eap)
 +              eap_sm_set_ext_pw_ctx(sm->eap, ext);
 +}
 +
 +
 +int eapol_sm_failed(struct eapol_sm *sm)
 +{
 +      if (sm == NULL)
 +              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 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9c41962fd7e164b57562bc36f6fd30ba38c1b9b0
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,8 @@@
++all:
++      @echo Nothing to be made.
++
++clean:
++      rm -f *~ *.o *.d
++
++install:
++      @echo Nothing to be made.
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2880870213e61c7368dd440274ed35f07128f58a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,225 @@@
++/*
++ * FST module implementation
++ * 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 "fst/fst.h"
++#include "fst/fst_internal.h"
++#include "fst/fst_defs.h"
++#include "fst/fst_ctrl_iface.h"
++
++struct dl_list fst_global_ctrls_list;
++
++
++static void fst_ctrl_iface_notify_peer_state_change(struct fst_iface *iface,
++                                                  Boolean connected,
++                                                  const u8 *peer_addr)
++{
++      union fst_event_extra extra;
++
++      extra.peer_state.connected = connected;
++      os_strlcpy(extra.peer_state.ifname, fst_iface_get_name(iface),
++                 sizeof(extra.peer_state.ifname));
++      os_memcpy(extra.peer_state.addr, peer_addr, ETH_ALEN);
++
++      foreach_fst_ctrl_call(on_event, EVENT_PEER_STATE_CHANGED,
++                            iface, NULL, &extra);
++}
++
++
++struct fst_iface * fst_attach(const char *ifname, const u8 *own_addr,
++                            const struct fst_wpa_obj *iface_obj,
++                            const struct fst_iface_cfg *cfg)
++{
++      struct fst_group *g;
++      struct fst_group *group = NULL;
++      struct fst_iface *iface = NULL;
++      Boolean new_group = FALSE;
++
++      WPA_ASSERT(ifname != NULL);
++      WPA_ASSERT(iface_obj != NULL);
++      WPA_ASSERT(cfg != NULL);
++
++      foreach_fst_group(g) {
++              if (os_strcmp(cfg->group_id, fst_group_get_id(g)) == 0) {
++                      group = g;
++                      break;
++              }
++      }
++
++      if (!group) {
++              group = fst_group_create(cfg->group_id);
++              if (!group) {
++                      fst_printf(MSG_ERROR, "%s: FST group cannot be created",
++                                 cfg->group_id);
++                      return NULL;
++              }
++              new_group = TRUE;
++      }
++
++      iface = fst_iface_create(group, ifname, own_addr, iface_obj, cfg);
++      if (!iface) {
++              fst_printf_group(group, MSG_ERROR, "cannot create iface for %s",
++                               ifname);
++              if (new_group)
++                      fst_group_delete(group);
++              return NULL;
++      }
++
++      fst_group_attach_iface(group, iface);
++      fst_group_update_ie(group);
++
++      foreach_fst_ctrl_call(on_iface_added, iface);
++
++      fst_printf_iface(iface, MSG_DEBUG,
++                       "iface attached to group %s (prio=%d, llt=%d)",
++                       cfg->group_id, cfg->priority, cfg->llt);
++
++      return iface;
++}
++
++
++void fst_detach(struct fst_iface *iface)
++{
++      struct fst_group *group = fst_iface_get_group(iface);
++
++      fst_printf_iface(iface, MSG_DEBUG, "iface detached from group %s",
++                       fst_group_get_id(group));
++      fst_session_global_on_iface_detached(iface);
++      foreach_fst_ctrl_call(on_iface_removed, iface);
++      fst_group_detach_iface(group, iface);
++      fst_iface_delete(iface);
++      fst_group_update_ie(group);
++      fst_group_delete_if_empty(group);
++}
++
++
++int fst_global_init(void)
++{
++      dl_list_init(&fst_global_groups_list);
++      dl_list_init(&fst_global_ctrls_list);
++      fst_session_global_init();
++      return 0;
++}
++
++
++void fst_global_deinit(void)
++{
++      struct fst_group *group;
++      struct fst_ctrl_handle *h;
++
++      fst_session_global_deinit();
++      while ((group = fst_first_group()) != NULL)
++              fst_group_delete(group);
++      while ((h = dl_list_first(&fst_global_ctrls_list,
++                                struct fst_ctrl_handle,
++                                global_ctrls_lentry)))
++              fst_global_del_ctrl(h);
++}
++
++
++struct fst_ctrl_handle * fst_global_add_ctrl(const struct fst_ctrl *ctrl)
++{
++      struct fst_ctrl_handle *h;
++
++      if (!ctrl)
++              return NULL;
++
++      h = os_zalloc(sizeof(*h));
++      if (!h)
++              return NULL;
++
++      if (ctrl->init && ctrl->init()) {
++              os_free(h);
++              return NULL;
++      }
++
++      h->ctrl = *ctrl;
++      dl_list_add_tail(&fst_global_ctrls_list, &h->global_ctrls_lentry);
++
++      return h;
++}
++
++
++void fst_global_del_ctrl(struct fst_ctrl_handle *h)
++{
++      dl_list_del(&h->global_ctrls_lentry);
++      if (h->ctrl.deinit)
++              h->ctrl.deinit();
++      os_free(h);
++}
++
++
++void fst_rx_action(struct fst_iface *iface, const struct ieee80211_mgmt *mgmt,
++                 size_t len)
++{
++      if (fst_iface_is_connected(iface, mgmt->sa))
++              fst_session_on_action_rx(iface, mgmt, len);
++      else
++              wpa_printf(MSG_DEBUG,
++                         "FST: Ignore FST Action frame - no FST connection with "
++                         MACSTR, MAC2STR(mgmt->sa));
++}
++
++
++void fst_notify_peer_connected(struct fst_iface *iface, const u8 *addr)
++{
++      if (is_zero_ether_addr(addr))
++              return;
++
++#ifndef HOSTAPD
++      fst_group_update_ie(fst_iface_get_group(iface));
++#endif /* HOSTAPD */
++
++      fst_printf_iface(iface, MSG_DEBUG, MACSTR " became connected",
++                       MAC2STR(addr));
++
++      fst_ctrl_iface_notify_peer_state_change(iface, TRUE, addr);
++}
++
++
++void fst_notify_peer_disconnected(struct fst_iface *iface, const u8 *addr)
++{
++      if (is_zero_ether_addr(addr))
++              return;
++
++#ifndef HOSTAPD
++      fst_group_update_ie(fst_iface_get_group(iface));
++#endif /* HOSTAPD */
++
++      fst_printf_iface(iface, MSG_DEBUG, MACSTR " became disconnected",
++                       MAC2STR(addr));
++
++      fst_ctrl_iface_notify_peer_state_change(iface, FALSE, addr);
++}
++
++
++Boolean fst_are_ifaces_aggregated(struct fst_iface *iface1,
++                                struct fst_iface *iface2)
++{
++      return fst_iface_get_group(iface1) == fst_iface_get_group(iface2);
++}
++
++
++enum mb_band_id fst_hw_mode_to_band(enum hostapd_hw_mode mode)
++{
++      switch (mode) {
++      case HOSTAPD_MODE_IEEE80211B:
++      case HOSTAPD_MODE_IEEE80211G:
++              return MB_BAND_ID_WIFI_2_4GHZ;
++      case HOSTAPD_MODE_IEEE80211A:
++              return MB_BAND_ID_WIFI_5GHZ;
++      case HOSTAPD_MODE_IEEE80211AD:
++              return MB_BAND_ID_WIFI_60GHZ;
++      default:
++              WPA_ASSERT(0);
++              return MB_BAND_ID_WIFI_2_4GHZ;
++      }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0c0e435b974b5d20126f146f2caf4cccf12b77e7
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,296 @@@
++/*
++ * FST module - interface 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 FST_H
++#define FST_H
++
++#ifdef CONFIG_FST
++
++#include "common/defs.h"
++#include "fst/fst_ctrl_iface.h"
++
++/* FST module hostap integration API */
++
++#define US_IN_MS           1000
++#define LLT_UNIT_US        32 /* See 10.32.2.2  Transitioning between states */
++
++#define FST_LLT_MS_TO_VAL(m) (((u32) (m)) * US_IN_MS / LLT_UNIT_US)
++#define FST_LLT_VAL_TO_MS(v) (((u32) (v)) * LLT_UNIT_US / US_IN_MS)
++
++#define FST_MAX_LLT_MS       FST_LLT_VAL_TO_MS(-1)
++#define FST_MAX_PRIO_VALUE   ((u8) -1)
++#define FST_MAX_GROUP_ID_LEN IFNAMSIZ
++
++#define FST_DEFAULT_LLT_CFG_VALUE 50
++
++struct hostapd_hw_modes;
++struct ieee80211_mgmt;
++struct fst_iface;
++struct fst_group;
++struct fst_session;
++struct fst_get_peer_ctx;
++struct fst_ctrl_handle;
++
++struct fst_wpa_obj {
++      void *ctx;
++
++      /**
++       * get_bssid - Get BSSID of the interface
++       * @ctx: User context %ctx
++       * Returns: BSSID for success, %NULL for failure.
++       *
++       * NOTE: For AP it returns the own BSSID, while for STA - the BSSID of
++       * the associated AP.
++       */
++      const u8 * (*get_bssid)(void *ctx);
++
++      /**
++       * get_channel_info - Get current channel info
++       * @ctx: User context %ctx
++       * @hw_mode: OUT, current HW mode
++       * @channel: OUT, current channel
++       */
++      void (*get_channel_info)(void *ctx, enum hostapd_hw_mode *hw_mode,
++                               u8 *channel);
++
++      /**
++       * get_hw_modes - Get hardware modes
++       * @ctx: User context %ctx
++       * @modes: OUT, pointer on array of hw modes
++       *
++       * Returns: Number of hw modes available.
++       */
++      int (*get_hw_modes)(void *ctx, struct hostapd_hw_modes **modes);
++
++      /**
++       * set_ies - Set interface's MB IE
++       * @ctx: User context %ctx
++       * @fst_ies: MB IE buffer (owned by FST module)
++       */
++      void (*set_ies)(void *ctx, const struct wpabuf *fst_ies);
++
++      /**
++       * send_action - Send FST Action frame via the interface
++       * @ctx: User context %ctx
++       * @addr: Address of the destination STA
++       * @data: Action frame buffer
++       * Returns: 0 for success, negative error code for failure.
++       */
++      int (*send_action)(void *ctx, const u8 *addr, struct wpabuf *data);
++
++      /**
++       * get_mb_ie - Get last MB IE received from STA
++       * @ctx: User context %ctx
++       * @addr: Address of the STA
++       * Returns: MB IE buffer, %NULL if no MB IE received from the STA
++       */
++      const struct wpabuf * (*get_mb_ie)(void *ctx, const u8 *addr);
++
++      /**
++       * update_mb_ie - Update last MB IE received from STA
++       * @ctx: User context %ctx
++       * @addr: Address of the STA
++       * @buf: Buffer that contains the MB IEs data
++       * @size: Size of data in %buf
++       */
++      void (*update_mb_ie)(void *ctx, const u8 *addr,
++                           const u8 *buf, size_t size);
++
++      /**
++       * get_peer_first - Get MAC address of the 1st connected STA
++       * @ctx: User context %ctx
++       * @get_ctx: Context to be used for %get_peer_next call
++       * @mb_only: %TRUE if only multi-band capable peer should be reported
++       * Returns: Address of the 1st connected STA, %NULL if no STAs connected
++       */
++      const u8 * (*get_peer_first)(void *ctx,
++                                   struct fst_get_peer_ctx **get_ctx,
++                                   Boolean mb_only);
++      /**
++       * get_peer_next - Get MAC address of the next connected STA
++       * @ctx: User context %ctx
++       * @get_ctx: Context received from %get_peer_first or previous
++       *           %get_peer_next call
++       * @mb_only: %TRUE if only multi-band capable peer should be reported
++       * Returns: Address of the next connected STA, %NULL if no more STAs
++       *          connected
++       */
++      const u8 * (*get_peer_next)(void *ctx,
++                                  struct fst_get_peer_ctx **get_ctx,
++                                  Boolean mb_only);
++};
++
++/**
++ * fst_global_init - Global FST module initiator
++ * Returns: 0 for success, negative error code for failure.
++ * Note: The purpose of this function is to allocate and initiate global
++ *       FST module data structures (linked lists, static data etc.)
++ *       This function should be called prior to the 1st %fst_attach call.
++ */
++int fst_global_init(void);
++
++/**
++ * fst_global_deinit - Global FST module de-initiator
++ * Note: The purpose of this function is to deallocate and de-initiate global
++ *       FST module data structures (linked lists, static data etc.)
++ */
++void fst_global_deinit(void);
++
++/**
++ * struct fst_ctrl - Notification interface for FST module
++ */
++struct fst_ctrl {
++      /**
++       * init - Initialize the notification interface
++       * Returns: 0 for success, negative error code for failure.
++       */
++      int (*init)(void);
++
++      /**
++       * deinit - Deinitialize the notification interface
++       */
++      void (*deinit)(void);
++
++      /**
++       * on_group_created - Notify about FST group creation
++       * Returns: 0 for success, negative error code for failure.
++       */
++      int (*on_group_created)(struct fst_group *g);
++
++      /**
++       * on_group_deleted - Notify about FST group deletion
++       */
++      void (*on_group_deleted)(struct fst_group *g);
++
++      /**
++       * on_iface_added - Notify about interface addition
++       * Returns: 0 for success, negative error code for failure.
++       */
++      int (*on_iface_added)(struct fst_iface *i);
++
++      /**
++       * on_iface_removed - Notify about interface removal
++       */
++      void (*on_iface_removed)(struct fst_iface *i);
++
++      /**
++       * on_session_added - Notify about FST session addition
++       * Returns: 0 for success, negative error code for failure.
++       */
++      int (*on_session_added)(struct fst_session *s);
++
++      /**
++       * on_session_removed - Notify about FST session removal
++       */
++      void (*on_session_removed)(struct fst_session *s);
++
++      /**
++       * on_event - Notify about FST event
++       * @event_type: Event type
++       * @i: Interface object that relates to the event or NULL
++       * @g: Group object that relates to the event or NULL
++       * @extra - Event specific data (see fst_ctrl_iface.h for more info)
++       */
++      void (*on_event)(enum fst_event_type event_type, struct fst_iface *i,
++                       struct fst_session *s,
++                       const union fst_event_extra *extra);
++};
++
++struct fst_ctrl_handle * fst_global_add_ctrl(const struct fst_ctrl *ctrl);
++void fst_global_del_ctrl(struct fst_ctrl_handle *h);
++
++/**
++ * NOTE: These values have to be read from configuration file
++ */
++struct fst_iface_cfg {
++      char group_id[FST_MAX_GROUP_ID_LEN + 1];
++      u8 priority;
++      u32 llt;
++};
++
++/**
++ * fst_attach - Attach interface to an FST group according to configuration read
++ * @ifname: Interface name
++ * @own_addr: Own interface MAC address
++ * @iface_obj: Callbacks to be used by FST module to communicate with
++ *             hostapd/wpa_supplicant
++ * @cfg: FST-related interface configuration read from the configuration file
++ * Returns: FST interface object for success, %NULL for failure.
++ */
++struct fst_iface * fst_attach(const char *ifname,
++                            const u8 *own_addr,
++                            const struct fst_wpa_obj *iface_obj,
++                            const struct fst_iface_cfg *cfg);
++
++/**
++ * fst_detach - Detach an interface
++ * @iface: FST interface object
++ */
++void fst_detach(struct fst_iface *iface);
++
++/* FST module inputs */
++/**
++ * fst_rx_action - FST Action frames handler
++ * @iface: FST interface object
++ * @mgmt: Action frame arrived
++ * @len: Action frame length
++ */
++void fst_rx_action(struct fst_iface *iface, const struct ieee80211_mgmt *mgmt,
++                 size_t len);
++
++/**
++ * fst_notify_peer_connected - FST STA connect handler
++ * @iface: FST interface object
++ * @addr: Address of the connected STA
++ */
++void fst_notify_peer_connected(struct fst_iface *iface, const u8 *addr);
++
++/**
++ * fst_notify_peer_disconnected - FST STA disconnect handler
++ * @iface: FST interface object
++ * @addr: Address of the disconnected STA
++ */
++void fst_notify_peer_disconnected(struct fst_iface *iface, const u8 *addr);
++
++/* FST module auxiliary routines */
++
++/**
++ * fst_are_ifaces_aggregated - Determines whether 2 interfaces belong to the
++ *                             same FST group
++ * @iface1: 1st FST interface object
++ * @iface1: 2nd FST interface object
++ *
++ * Returns: %TRUE if the interfaces belong to the same FST group,
++ *          %FALSE otherwise
++ */
++Boolean fst_are_ifaces_aggregated(struct fst_iface *iface1,
++                                struct fst_iface *iface2);
++
++#else /* CONFIG_FST */
++
++static inline int fst_global_init(void)
++{
++      return 0;
++}
++
++static inline int fst_global_start(void)
++{
++      return 0;
++}
++
++static inline void fst_global_stop(void)
++{
++}
++
++static inline void fst_global_deinit(void)
++{
++}
++
++#endif /* CONFIG_FST */
++
++#endif /* FST_H */
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..dc7b2a7d72021bb3f0af1301117e9390244d5d70
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,69 @@@
++/*
++ * FST module implementation
++ * 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 "common/defs.h"
++#include "fst_ctrl_defs.h"
++#include "fst_ctrl_aux.h"
++
++
++static const char *session_event_names[] = {
++      [EVENT_FST_ESTABLISHED] FST_PVAL_EVT_TYPE_ESTABLISHED,
++      [EVENT_FST_SETUP] FST_PVAL_EVT_TYPE_SETUP,
++      [EVENT_FST_SESSION_STATE_CHANGED] FST_PVAL_EVT_TYPE_SESSION_STATE,
++};
++
++static const char *reason_names[] = {
++      [REASON_TEARDOWN] FST_CS_PVAL_REASON_TEARDOWN,
++      [REASON_SETUP] FST_CS_PVAL_REASON_SETUP,
++      [REASON_SWITCH] FST_CS_PVAL_REASON_SWITCH,
++      [REASON_STT] FST_CS_PVAL_REASON_STT,
++      [REASON_REJECT] FST_CS_PVAL_REASON_REJECT,
++      [REASON_ERROR_PARAMS] FST_CS_PVAL_REASON_ERROR_PARAMS,
++      [REASON_RESET] FST_CS_PVAL_REASON_RESET,
++      [REASON_DETACH_IFACE] FST_CS_PVAL_REASON_DETACH_IFACE,
++};
++
++static const char *session_state_names[] = {
++      [FST_SESSION_STATE_INITIAL] FST_CS_PVAL_STATE_INITIAL,
++      [FST_SESSION_STATE_SETUP_COMPLETION] FST_CS_PVAL_STATE_SETUP_COMPLETION,
++      [FST_SESSION_STATE_TRANSITION_DONE] FST_CS_PVAL_STATE_TRANSITION_DONE,
++      [FST_SESSION_STATE_TRANSITION_CONFIRMED]
++      FST_CS_PVAL_STATE_TRANSITION_CONFIRMED,
++};
++
++
++/* helpers */
++const char * fst_get_str_name(unsigned index, const char *names[],
++                            size_t names_size)
++{
++      if (index >= names_size || !names[index])
++              return FST_NAME_UNKNOWN;
++      return names[index];
++}
++
++
++const char * fst_session_event_type_name(enum fst_event_type event)
++{
++      return fst_get_str_name(event, session_event_names,
++                              ARRAY_SIZE(session_event_names));
++}
++
++
++const char * fst_reason_name(enum fst_reason reason)
++{
++      return fst_get_str_name(reason, reason_names, ARRAY_SIZE(reason_names));
++}
++
++
++const char * fst_session_state_name(enum fst_session_state state)
++{
++      return fst_get_str_name(state, session_state_names,
++                              ARRAY_SIZE(session_state_names));
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e2133f5062bd1615ac89e7a8874cbd9be995025c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,91 @@@
++/*
++ * FST module - miscellaneous 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 FST_CTRL_AUX_H
++#define FST_CTRL_AUX_H
++
++#include "common/defs.h"
++
++/* FST module control interface API */
++#define FST_INVALID_SESSION_ID ((u32) -1)
++#define FST_MAX_GROUP_ID_SIZE   32
++#define FST_MAX_INTERFACE_SIZE  32
++
++enum fst_session_state {
++      FST_SESSION_STATE_INITIAL,
++      FST_SESSION_STATE_SETUP_COMPLETION,
++      FST_SESSION_STATE_TRANSITION_DONE,
++      FST_SESSION_STATE_TRANSITION_CONFIRMED,
++      FST_SESSION_STATE_LAST
++};
++
++enum fst_event_type {
++      EVENT_FST_IFACE_STATE_CHANGED,  /* An interface has been either attached
++                                       * to or detached from an FST group */
++      EVENT_FST_ESTABLISHED,          /* FST Session has been established */
++      EVENT_FST_SETUP,                /* FST Session request received */
++      EVENT_FST_SESSION_STATE_CHANGED,/* FST Session state has been changed */
++      EVENT_PEER_STATE_CHANGED        /* FST related generic event occurred,
++                                       * see struct fst_hostap_event_data for
++                                       *  more info */
++};
++
++enum fst_initiator {
++      FST_INITIATOR_UNDEFINED,
++      FST_INITIATOR_LOCAL,
++      FST_INITIATOR_REMOTE,
++};
++
++union fst_event_extra {
++      struct fst_event_extra_iface_state {
++              Boolean attached;
++              char ifname[FST_MAX_INTERFACE_SIZE];
++              char group_id[FST_MAX_GROUP_ID_SIZE];
++      } iface_state; /* for EVENT_FST_IFACE_STATE_CHANGED */
++      struct fst_event_extra_peer_state {
++              Boolean connected;
++              char ifname[FST_MAX_INTERFACE_SIZE];
++              u8 addr[ETH_ALEN];
++      } peer_state; /* for EVENT_PEER_STATE_CHANGED */
++      struct fst_event_extra_session_state {
++              enum fst_session_state old_state;
++              enum fst_session_state new_state;
++              union fst_session_state_switch_extra {
++                      struct {
++                              enum fst_reason {
++                                      REASON_TEARDOWN,
++                                      REASON_SETUP,
++                                      REASON_SWITCH,
++                                      REASON_STT,
++                                      REASON_REJECT,
++                                      REASON_ERROR_PARAMS,
++                                      REASON_RESET,
++                                      REASON_DETACH_IFACE,
++                              } reason;
++                              u8 reject_code; /* REASON_REJECT */
++                              /* REASON_SWITCH,
++                               * REASON_TEARDOWN,
++                               * REASON_REJECT
++                               */
++                              enum fst_initiator initiator;
++                      } to_initial;
++              } extra;
++      } session_state; /* for EVENT_FST_SESSION_STATE_CHANGED */
++};
++
++/* helpers - prints enum in string form */
++#define FST_NAME_UNKNOWN "UNKNOWN"
++
++const char * fst_get_str_name(unsigned index, const char *names[],
++                            size_t names_size);
++
++const char * fst_session_event_type_name(enum fst_event_type);
++const char * fst_reason_name(enum fst_reason reason);
++const char * fst_session_state_name(enum fst_session_state state);
++
++#endif /* FST_CTRL_AUX_H */
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..67353890ffcf9f50d1ec9b6871128a195b54c211
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,109 @@@
++/*
++ * FST module - shared Control interface 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 FST_CTRL_DEFS_H
++#define FST_CTRL_DEFS_H
++
++/* Undefined value */
++#define FST_CTRL_PVAL_NONE                     "NONE"
++
++/* FST-ATTACH parameters */
++#define FST_ATTACH_CMD_PNAME_LLT      "llt"      /* pval = desired LLT */
++#define FST_ATTACH_CMD_PNAME_PRIORITY "priority" /* pval = desired priority */
++
++/* FST-MANAGER parameters */
++/* FST Session states */
++#define FST_CS_PVAL_STATE_INITIAL              "INITIAL"
++#define FST_CS_PVAL_STATE_SETUP_COMPLETION     "SETUP_COMPLETION"
++#define FST_CS_PVAL_STATE_TRANSITION_DONE      "TRANSITION_DONE"
++#define FST_CS_PVAL_STATE_TRANSITION_CONFIRMED "TRANSITION_CONFIRMED"
++
++/* FST Session reset reasons */
++#define FST_CS_PVAL_REASON_TEARDOWN     "REASON_TEARDOWN"
++#define FST_CS_PVAL_REASON_SETUP        "REASON_SETUP"
++#define FST_CS_PVAL_REASON_SWITCH       "REASON_SWITCH"
++#define FST_CS_PVAL_REASON_STT          "REASON_STT"
++#define FST_CS_PVAL_REASON_REJECT       "REASON_REJECT"
++#define FST_CS_PVAL_REASON_ERROR_PARAMS "REASON_ERROR_PARAMS"
++#define FST_CS_PVAL_REASON_RESET        "REASON_RESET"
++#define FST_CS_PVAL_REASON_DETACH_IFACE "REASON_DETACH_IFACE"
++
++/* FST Session responses */
++#define FST_CS_PVAL_RESPONSE_ACCEPT "ACCEPT"
++#define FST_CS_PVAL_RESPONSE_REJECT "REJECT"
++
++/* FST Session action initiator */
++#define FST_CS_PVAL_INITIATOR_LOCAL  "LOCAL"
++#define FST_CS_PVAL_INITIATOR_REMOTE "REMOTE"
++
++/* FST-CLI subcommands and parameter names */
++#define FST_CMD_LIST_GROUPS      "list_groups"
++#define FST_CMD_LIST_IFACES      "list_ifaces"
++#define FST_CMD_IFACE_PEERS      "iface_peers"
++#define FST_CMD_GET_PEER_MBIES   "get_peer_mbies"
++#define FST_CMD_LIST_SESSIONS    "list_sessions"
++#define FST_CMD_SESSION_ADD      "session_add"
++#define FST_CMD_SESSION_REMOVE   "session_remove"
++#define FST_CMD_SESSION_GET      "session_get"
++#define FST_CSG_PNAME_OLD_PEER_ADDR  "old_peer_addr" /* pval = address string */
++#define FST_CSG_PNAME_NEW_PEER_ADDR  "new_peer_addr" /* pval = address string */
++#define FST_CSG_PNAME_OLD_IFNAME "old_ifname" /* pval = ifname */
++#define FST_CSG_PNAME_NEW_IFNAME "new_ifname" /* pval = ifname */
++#define FST_CSG_PNAME_LLT        "llt"        /* pval = numeric llt value */
++#define FST_CSG_PNAME_STATE      "state"      /* pval = FST_CS_PVAL_STATE_... */
++#define FST_CMD_SESSION_SET      "session_set"
++#define FST_CSS_PNAME_OLD_PEER_ADDR  FST_CSG_PNAME_OLD_PEER_ADDR
++#define FST_CSS_PNAME_NEW_PEER_ADDR  FST_CSG_PNAME_NEW_PEER_ADDR
++#define FST_CSS_PNAME_OLD_IFNAME     FST_CSG_PNAME_OLD_IFNAME
++#define FST_CSS_PNAME_NEW_IFNAME     FST_CSG_PNAME_NEW_IFNAME
++#define FST_CSS_PNAME_LLT            FST_CSG_PNAME_LLT
++#define FST_CMD_SESSION_INITIATE "session_initiate"
++#define FST_CMD_SESSION_RESPOND  "session_respond"
++#define FST_CMD_SESSION_TRANSFER "session_transfer"
++#define FST_CMD_SESSION_TEARDOWN "session_teardown"
++
++#ifdef CONFIG_FST_TEST
++#define FST_CTR_PVAL_BAD_NEW_BAND        "bad_new_band"
++
++#define FST_CMD_TEST_REQUEST    "test_request"
++#define FST_CTR_IS_SUPPORTED        "is_supported"
++#define FST_CTR_SEND_SETUP_REQUEST  "send_setup_request"
++#define FST_CTR_SEND_SETUP_RESPONSE "send_setup_response"
++#define FST_CTR_SEND_ACK_REQUEST    "send_ack_request"
++#define FST_CTR_SEND_ACK_RESPONSE   "send_ack_response"
++#define FST_CTR_SEND_TEAR_DOWN      "send_tear_down"
++#define FST_CTR_GET_FSTS_ID         "get_fsts_id"
++#define FST_CTR_GET_LOCAL_MBIES     "get_local_mbies"
++#endif /* CONFIG_FST_TEST */
++
++/* Events */
++#define FST_CTRL_EVENT_IFACE "FST-EVENT-IFACE"
++#define FST_CEI_PNAME_IFNAME       "ifname"
++#define FST_CEI_PNAME_GROUP        "group"
++#define FST_CEI_PNAME_ATTACHED     "attached"
++#define FST_CEI_PNAME_DETACHED     "detached"
++#define FST_CTRL_EVENT_PEER "FST-EVENT-PEER"
++#define FST_CEP_PNAME_IFNAME       "ifname"
++#define FST_CEP_PNAME_ADDR         "peer_addr"
++#define FST_CEP_PNAME_CONNECTED    "connected"
++#define FST_CEP_PNAME_DISCONNECTED "disconnected"
++#define FST_CTRL_EVENT_SESSION "FST-EVENT-SESSION"
++#define FST_CES_PNAME_SESSION_ID "session_id"
++#define FST_CES_PNAME_EVT_TYPE   "event_type"
++#define FST_PVAL_EVT_TYPE_SESSION_STATE "EVENT_FST_SESSION_STATE"
++/* old_state/new_state: pval = FST_CS_PVAL_STATE_... */
++#define FST_CES_PNAME_OLD_STATE   "old_state"
++#define FST_CES_PNAME_NEW_STATE   "new_state"
++#define FST_CES_PNAME_REASON      "reason" /* pval = FST_CS_PVAL_REASON_... */
++#define FST_CES_PNAME_REJECT_CODE "reject_code" /* pval = u8 code */
++/* pval = FST_CS_PVAL_INITIATOR_... */
++#define FST_CES_PNAME_INITIATOR   "initiator"
++#define FST_PVAL_EVT_TYPE_ESTABLISHED "EVENT_FST_ESTABLISHED"
++#define FST_PVAL_EVT_TYPE_SETUP "EVENT_FST_SETUP"
++
++#endif /* FST_CTRL_DEFS_H */
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d0907188a389e175248be1ac44139f451d08d863
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,948 @@@
++/*
++ * FST module - Control Interface implementation
++ * 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 "common/defs.h"
++#include "list.h"
++#include "fst/fst.h"
++#include "fst/fst_internal.h"
++#include "fst_ctrl_defs.h"
++#include "fst_ctrl_iface.h"
++
++
++static struct fst_group * get_fst_group_by_id(const char *id)
++{
++      struct fst_group *g;
++
++      foreach_fst_group(g) {
++              const char *group_id = fst_group_get_id(g);
++
++              if (os_strncmp(group_id, id, os_strlen(group_id)) == 0)
++                      return g;
++      }
++
++      return NULL;
++}
++
++
++/* notifications */
++static Boolean format_session_state_extra(const union fst_event_extra *extra,
++                                        char *buffer, size_t size)
++{
++      int len;
++      char reject_str[32] = FST_CTRL_PVAL_NONE;
++      const char *initiator = FST_CTRL_PVAL_NONE;
++      const struct fst_event_extra_session_state *ss;
++
++      ss = &extra->session_state;
++      if (ss->new_state != FST_SESSION_STATE_INITIAL)
++              return TRUE;
++
++      switch (ss->extra.to_initial.reason) {
++      case REASON_REJECT:
++              if (ss->extra.to_initial.reject_code != WLAN_STATUS_SUCCESS)
++                      os_snprintf(reject_str, sizeof(reject_str), "%u",
++                                  ss->extra.to_initial.reject_code);
++              /* no break */
++      case REASON_TEARDOWN:
++      case REASON_SWITCH:
++              switch (ss->extra.to_initial.initiator) {
++              case FST_INITIATOR_LOCAL:
++                      initiator = FST_CS_PVAL_INITIATOR_LOCAL;
++                      break;
++              case FST_INITIATOR_REMOTE:
++                      initiator = FST_CS_PVAL_INITIATOR_REMOTE;
++                      break;
++              default:
++                      break;
++              }
++              break;
++      default:
++              break;
++      }
++
++      len = os_snprintf(buffer, size,
++                        FST_CES_PNAME_REASON "=%s "
++                        FST_CES_PNAME_REJECT_CODE "=%s "
++                        FST_CES_PNAME_INITIATOR "=%s",
++                        fst_reason_name(ss->extra.to_initial.reason),
++                        reject_str, initiator);
++
++      return !os_snprintf_error(size, len);
++}
++
++
++static void fst_ctrl_iface_notify(struct fst_iface *f, u32 session_id,
++                                enum fst_event_type event_type,
++                                const union fst_event_extra *extra)
++{
++      struct fst_group *g;
++      char extra_str[128] = "";
++      const struct fst_event_extra_session_state *ss;
++      const struct fst_event_extra_iface_state *is;
++      const struct fst_event_extra_peer_state *ps;
++
++      /*
++       * FST can use any of interface objects as it only sends messages
++       * on global Control Interface, so we just pick the 1st one.
++       */
++
++      if (!f) {
++              foreach_fst_group(g) {
++                      f = fst_group_first_iface(g);
++                      if (f)
++                              break;
++              }
++              if (!f)
++                      return;
++      }
++
++      WPA_ASSERT(f->iface_obj.ctx);
++
++      switch (event_type) {
++      case EVENT_FST_IFACE_STATE_CHANGED:
++              if (!extra)
++                      return;
++              is = &extra->iface_state;
++              wpa_msg_global_only(f->iface_obj.ctx, MSG_INFO,
++                                  FST_CTRL_EVENT_IFACE " %s "
++                                  FST_CEI_PNAME_IFNAME "=%s "
++                                  FST_CEI_PNAME_GROUP "=%s",
++                                  is->attached ? FST_CEI_PNAME_ATTACHED :
++                                  FST_CEI_PNAME_DETACHED,
++                                  is->ifname, is->group_id);
++              break;
++      case EVENT_PEER_STATE_CHANGED:
++              if (!extra)
++                      return;
++              ps = &extra->peer_state;
++              wpa_msg_global_only(fst_iface_get_wpa_obj_ctx(f), MSG_INFO,
++                                  FST_CTRL_EVENT_PEER " %s "
++                                  FST_CEP_PNAME_IFNAME "=%s "
++                                  FST_CEP_PNAME_ADDR "=" MACSTR,
++                                  ps->connected ? FST_CEP_PNAME_CONNECTED :
++                                  FST_CEP_PNAME_DISCONNECTED,
++                                  ps->ifname, MAC2STR(ps->addr));
++              break;
++      case EVENT_FST_SESSION_STATE_CHANGED:
++              if (!extra)
++                      return;
++              if (!format_session_state_extra(extra, extra_str,
++                                              sizeof(extra_str))) {
++                      fst_printf(MSG_ERROR,
++                                 "CTRL: Cannot format STATE_CHANGE extra");
++                      extra_str[0] = 0;
++              }
++              ss = &extra->session_state;
++              wpa_msg_global_only(fst_iface_get_wpa_obj_ctx(f), MSG_INFO,
++                                  FST_CTRL_EVENT_SESSION " "
++                                  FST_CES_PNAME_SESSION_ID "=%u "
++                                  FST_CES_PNAME_EVT_TYPE "=%s "
++                                  FST_CES_PNAME_OLD_STATE "=%s "
++                                  FST_CES_PNAME_NEW_STATE "=%s %s",
++                                  session_id,
++                                  fst_session_event_type_name(event_type),
++                                  fst_session_state_name(ss->old_state),
++                                  fst_session_state_name(ss->new_state),
++                                  extra_str);
++              break;
++      case EVENT_FST_ESTABLISHED:
++      case EVENT_FST_SETUP:
++              wpa_msg_global_only(fst_iface_get_wpa_obj_ctx(f), MSG_INFO,
++                                  FST_CTRL_EVENT_SESSION " "
++                                  FST_CES_PNAME_SESSION_ID "=%u "
++                                  FST_CES_PNAME_EVT_TYPE "=%s",
++                                  session_id,
++                                  fst_session_event_type_name(event_type));
++              break;
++      }
++}
++
++
++/* command processors */
++
++/* fst session_get */
++static int session_get(const char *session_id, char *buf, size_t buflen)
++{
++      struct fst_session *s;
++      struct fst_iface *new_iface, *old_iface;
++      const u8 *old_peer_addr, *new_peer_addr;
++      u32 id;
++
++      id = strtoul(session_id, NULL, 0);
++
++      s = fst_session_get_by_id(id);
++      if (!s) {
++              fst_printf(MSG_WARNING, "CTRL: Cannot find session %u", id);
++              return os_snprintf(buf, buflen, "FAIL\n");
++      }
++
++      old_peer_addr = fst_session_get_peer_addr(s, TRUE);
++      new_peer_addr = fst_session_get_peer_addr(s, FALSE);
++      new_iface = fst_session_get_iface(s, FALSE);
++      old_iface = fst_session_get_iface(s, TRUE);
++
++      return os_snprintf(buf, buflen,
++                         FST_CSG_PNAME_OLD_PEER_ADDR "=" MACSTR "\n"
++                         FST_CSG_PNAME_NEW_PEER_ADDR "=" MACSTR "\n"
++                         FST_CSG_PNAME_NEW_IFNAME "=%s\n"
++                         FST_CSG_PNAME_OLD_IFNAME "=%s\n"
++                         FST_CSG_PNAME_LLT "=%u\n"
++                         FST_CSG_PNAME_STATE "=%s\n",
++                         MAC2STR(old_peer_addr),
++                         MAC2STR(new_peer_addr),
++                         new_iface ? fst_iface_get_name(new_iface) :
++                         FST_CTRL_PVAL_NONE,
++                         old_iface ? fst_iface_get_name(old_iface) :
++                         FST_CTRL_PVAL_NONE,
++                         fst_session_get_llt(s),
++                         fst_session_state_name(fst_session_get_state(s)));
++}
++
++
++/* fst session_set */
++static int session_set(const char *session_id, char *buf, size_t buflen)
++{
++      struct fst_session *s;
++      char *p, *q;
++      u32 id;
++      int ret;
++
++      id = strtoul(session_id, &p, 0);
++
++      s = fst_session_get_by_id(id);
++      if (!s) {
++              fst_printf(MSG_WARNING, "CTRL: Cannot find session %u", id);
++              return os_snprintf(buf, buflen, "FAIL\n");
++      }
++
++      if (*p != ' ' || !(q = os_strchr(p + 1, '=')))
++              return os_snprintf(buf, buflen, "FAIL\n");
++      p++;
++
++      if (os_strncasecmp(p, FST_CSS_PNAME_OLD_IFNAME, q - p) == 0) {
++              ret = fst_session_set_str_ifname(s, q + 1, TRUE);
++      } else if (os_strncasecmp(p, FST_CSS_PNAME_NEW_IFNAME, q - p) == 0) {
++              ret = fst_session_set_str_ifname(s, q + 1, FALSE);
++      } else if (os_strncasecmp(p, FST_CSS_PNAME_OLD_PEER_ADDR, q - p) == 0) {
++              ret = fst_session_set_str_peer_addr(s, q + 1, TRUE);
++      } else if (os_strncasecmp(p, FST_CSS_PNAME_NEW_PEER_ADDR, q - p) == 0) {
++              ret = fst_session_set_str_peer_addr(s, q + 1, FALSE);
++      } else if (os_strncasecmp(p, FST_CSS_PNAME_LLT, q - p) == 0) {
++              ret = fst_session_set_str_llt(s, q + 1);
++      } else {
++              fst_printf(MSG_ERROR, "CTRL: Unknown parameter: %s", p);
++              return os_snprintf(buf, buflen, "FAIL\n");
++      }
++
++      return os_snprintf(buf, buflen, "%s\n", ret ? "FAIL" : "OK");
++}
++
++
++/* fst session_add/remove */
++static int session_add(const char *group_id, char *buf, size_t buflen)
++{
++      struct fst_group *g;
++      struct fst_session *s;
++
++      g = get_fst_group_by_id(group_id);
++      if (!g) {
++              fst_printf(MSG_WARNING, "CTRL: Cannot find group '%s'",
++                         group_id);
++              return os_snprintf(buf, buflen, "FAIL\n");
++      }
++
++      s = fst_session_create(g);
++      if (!s) {
++              fst_printf(MSG_ERROR,
++                         "CTRL: Cannot create session for group '%s'",
++                         group_id);
++              return os_snprintf(buf, buflen, "FAIL\n");
++      }
++
++      return os_snprintf(buf, buflen, "%u\n", fst_session_get_id(s));
++}
++
++
++static int session_remove(const char *session_id, char *buf, size_t buflen)
++{
++      struct fst_session *s;
++      struct fst_group *g;
++      u32 id;
++
++      id = strtoul(session_id, NULL, 0);
++
++      s = fst_session_get_by_id(id);
++      if (!s) {
++              fst_printf(MSG_WARNING, "CTRL: Cannot find session %u", id);
++              return os_snprintf(buf, buflen, "FAIL\n");
++      }
++
++      g = fst_session_get_group(s);
++      fst_session_reset(s);
++      fst_session_delete(s);
++      fst_group_delete_if_empty(g);
++
++      return os_snprintf(buf, buflen, "OK\n");
++}
++
++
++/* fst session_initiate */
++static int session_initiate(const char *session_id, char *buf, size_t buflen)
++{
++      struct fst_session *s;
++      u32 id;
++
++      id = strtoul(session_id, NULL, 0);
++
++      s = fst_session_get_by_id(id);
++      if (!s) {
++              fst_printf(MSG_WARNING, "CTRL: Cannot find session %u", id);
++              return os_snprintf(buf, buflen, "FAIL\n");
++      }
++
++      if (fst_session_initiate_setup(s)) {
++              fst_printf(MSG_WARNING, "CTRL: Cannot initiate session %u", id);
++              return os_snprintf(buf, buflen, "FAIL\n");
++      }
++
++      return os_snprintf(buf, buflen, "OK\n");
++}
++
++
++/* fst session_respond */
++static int session_respond(const char *session_id, char *buf, size_t buflen)
++{
++      struct fst_session *s;
++      char *p;
++      u32 id;
++      u8 status_code;
++
++      id = strtoul(session_id, &p, 0);
++
++      s = fst_session_get_by_id(id);
++      if (!s) {
++              fst_printf(MSG_WARNING, "CTRL: Cannot find session %u", id);
++              return os_snprintf(buf, buflen, "FAIL\n");
++      }
++
++      if (*p != ' ')
++              return os_snprintf(buf, buflen, "FAIL\n");
++      p++;
++
++      if (!os_strcasecmp(p, FST_CS_PVAL_RESPONSE_ACCEPT)) {
++              status_code = WLAN_STATUS_SUCCESS;
++      } else if (!os_strcasecmp(p, FST_CS_PVAL_RESPONSE_REJECT)) {
++              status_code = WLAN_STATUS_PENDING_ADMITTING_FST_SESSION;
++      } else {
++              fst_printf(MSG_WARNING,
++                         "CTRL: session %u: unknown response status: %s",
++                         id, p);
++              return os_snprintf(buf, buflen, "FAIL\n");
++      }
++
++      if (fst_session_respond(s, status_code)) {
++              fst_printf(MSG_WARNING, "CTRL: Cannot respond to session %u",
++                         id);
++              return os_snprintf(buf, buflen, "FAIL\n");
++      }
++
++      fst_printf(MSG_INFO, "CTRL: session %u responded", id);
++
++      return os_snprintf(buf, buflen, "OK\n");
++}
++
++
++/* fst session_transfer */
++static int session_transfer(const char *session_id, char *buf, size_t buflen)
++{
++      struct fst_session *s;
++      u32 id;
++
++      id = strtoul(session_id, NULL, 0);
++
++      s = fst_session_get_by_id(id);
++      if (!s) {
++              fst_printf(MSG_WARNING, "CTRL: Cannot find session %u", id);
++              return os_snprintf(buf, buflen, "FAIL\n");
++      }
++
++      if (fst_session_initiate_switch(s)) {
++              fst_printf(MSG_WARNING,
++                         "CTRL: Cannot initiate ST for session %u", id);
++              return os_snprintf(buf, buflen, "FAIL\n");
++      }
++
++      return os_snprintf(buf, buflen, "OK\n");
++}
++
++
++/* fst session_teardown */
++static int session_teardown(const char *session_id, char *buf, size_t buflen)
++{
++      struct fst_session *s;
++      u32 id;
++
++      id = strtoul(session_id, NULL, 0);
++
++      s = fst_session_get_by_id(id);
++      if (!s) {
++              fst_printf(MSG_WARNING, "CTRL: Cannot find session %u", id);
++              return os_snprintf(buf, buflen, "FAIL\n");
++      }
++
++      if (fst_session_tear_down_setup(s)) {
++              fst_printf(MSG_WARNING, "CTRL: Cannot tear down session %u",
++                         id);
++              return os_snprintf(buf, buflen, "FAIL\n");
++      }
++
++      return os_snprintf(buf, buflen, "OK\n");
++}
++
++
++#ifdef CONFIG_FST_TEST
++/* fst test_request */
++static int test_request(const char *request, char *buf, size_t buflen)
++{
++      const char *p = request;
++      int ret;
++
++      if (!os_strncasecmp(p, FST_CTR_SEND_SETUP_REQUEST,
++                          os_strlen(FST_CTR_SEND_SETUP_REQUEST))) {
++              ret = fst_test_req_send_fst_request(
++                      p + os_strlen(FST_CTR_SEND_SETUP_REQUEST));
++      } else if (!os_strncasecmp(p, FST_CTR_SEND_SETUP_RESPONSE,
++                                 os_strlen(FST_CTR_SEND_SETUP_RESPONSE))) {
++              ret = fst_test_req_send_fst_response(
++                      p + os_strlen(FST_CTR_SEND_SETUP_RESPONSE));
++      } else if (!os_strncasecmp(p, FST_CTR_SEND_ACK_REQUEST,
++                                 os_strlen(FST_CTR_SEND_ACK_REQUEST))) {
++              ret = fst_test_req_send_ack_request(
++                      p + os_strlen(FST_CTR_SEND_ACK_REQUEST));
++      } else if (!os_strncasecmp(p, FST_CTR_SEND_ACK_RESPONSE,
++                                 os_strlen(FST_CTR_SEND_ACK_RESPONSE))) {
++              ret = fst_test_req_send_ack_response(
++                      p + os_strlen(FST_CTR_SEND_ACK_RESPONSE));
++      } else if (!os_strncasecmp(p, FST_CTR_SEND_TEAR_DOWN,
++                                 os_strlen(FST_CTR_SEND_TEAR_DOWN))) {
++              ret = fst_test_req_send_tear_down(
++                      p + os_strlen(FST_CTR_SEND_TEAR_DOWN));
++      } else if (!os_strncasecmp(p, FST_CTR_GET_FSTS_ID,
++                                 os_strlen(FST_CTR_GET_FSTS_ID))) {
++              u32 fsts_id = fst_test_req_get_fsts_id(
++                      p + os_strlen(FST_CTR_GET_FSTS_ID));
++              if (fsts_id != FST_FSTS_ID_NOT_FOUND)
++                      return os_snprintf(buf, buflen, "%u\n", fsts_id);
++              return os_snprintf(buf, buflen, "FAIL\n");
++      } else if (!os_strncasecmp(p, FST_CTR_GET_LOCAL_MBIES,
++                                 os_strlen(FST_CTR_GET_LOCAL_MBIES))) {
++              return fst_test_req_get_local_mbies(
++                      p + os_strlen(FST_CTR_GET_LOCAL_MBIES), buf, buflen);
++      } else if (!os_strncasecmp(p, FST_CTR_IS_SUPPORTED,
++                                 os_strlen(FST_CTR_IS_SUPPORTED))) {
++              ret = 0;
++      } else {
++              fst_printf(MSG_ERROR, "CTRL: Unknown parameter: %s", p);
++              return os_snprintf(buf, buflen, "FAIL\n");
++      }
++
++      return os_snprintf(buf, buflen, "%s\n", ret ? "FAIL" : "OK");
++}
++#endif /* CONFIG_FST_TEST */
++
++
++/* fst list_sessions */
++struct list_sessions_cb_ctx {
++      char *buf;
++      size_t buflen;
++      size_t reply_len;
++};
++
++
++static void list_session_enum_cb(struct fst_group *g, struct fst_session *s,
++                               void *ctx)
++{
++      struct list_sessions_cb_ctx *c = ctx;
++      int ret;
++
++      ret = os_snprintf(c->buf, c->buflen, " %u", fst_session_get_id(s));
++
++      c->buf += ret;
++      c->buflen -= ret;
++      c->reply_len += ret;
++}
++
++
++static int list_sessions(const char *group_id, char *buf, size_t buflen)
++{
++      struct list_sessions_cb_ctx ctx;
++      struct fst_group *g;
++
++      g = get_fst_group_by_id(group_id);
++      if (!g) {
++              fst_printf(MSG_WARNING, "CTRL: Cannot find group '%s'",
++                         group_id);
++              return os_snprintf(buf, buflen, "FAIL\n");
++      }
++
++      ctx.buf = buf;
++      ctx.buflen = buflen;
++      ctx.reply_len = 0;
++
++      fst_session_enum(g, list_session_enum_cb, &ctx);
++
++      ctx.reply_len += os_snprintf(buf + ctx.reply_len, ctx.buflen, "\n");
++
++      return ctx.reply_len;
++}
++
++
++/* fst iface_peers */
++static int iface_peers(const char *group_id, char *buf, size_t buflen)
++{
++      const char *ifname;
++      struct fst_group *g;
++      struct fst_iface *f;
++      struct fst_get_peer_ctx *ctx;
++      const u8 *addr;
++      unsigned found = 0;
++      int ret = 0;
++
++      g = get_fst_group_by_id(group_id);
++      if (!g) {
++              fst_printf(MSG_WARNING, "CTRL: Cannot find group '%s'",
++                         group_id);
++              return os_snprintf(buf, buflen, "FAIL\n");
++      }
++
++      ifname = os_strchr(group_id, ' ');
++      if (!ifname)
++              return os_snprintf(buf, buflen, "FAIL\n");
++      ifname++;
++
++      foreach_fst_group_iface(g, f) {
++              const char *in = fst_iface_get_name(f);
++
++              if (os_strncmp(ifname, in, os_strlen(in)) == 0) {
++                      found = 1;
++                      break;
++              }
++      }
++
++      if (!found)
++              return os_snprintf(buf, buflen, "FAIL\n");
++
++      addr = fst_iface_get_peer_first(f, &ctx, FALSE);
++      for (; addr != NULL; addr = fst_iface_get_peer_next(f, &ctx, FALSE)) {
++              int res;
++
++              res = os_snprintf(buf + ret, buflen - ret, MACSTR "\n",
++                                MAC2STR(addr));
++              if (os_snprintf_error(buflen - ret, res))
++                      break;
++              ret += res;
++      }
++
++      return ret;
++}
++
++
++static int get_peer_mbies(const char *params, char *buf, size_t buflen)
++{
++      char *endp;
++      char ifname[FST_MAX_INTERFACE_SIZE];
++      u8 peer_addr[ETH_ALEN];
++      struct fst_group *g;
++      struct fst_iface *iface = NULL;
++      const struct wpabuf *mbies;
++
++      if (fst_read_next_text_param(params, ifname, sizeof(ifname), &endp) ||
++          !*ifname)
++              goto problem;
++
++      while (isspace(*endp))
++              endp++;
++      if (fst_read_peer_addr(endp, peer_addr))
++              goto problem;
++
++      foreach_fst_group(g) {
++              iface = fst_group_get_iface_by_name(g, ifname);
++              if (iface)
++                      break;
++      }
++      if (!iface)
++              goto problem;
++
++      mbies = fst_iface_get_peer_mb_ie(iface, peer_addr);
++      if (!mbies)
++              goto problem;
++
++      return wpa_snprintf_hex(buf, buflen, wpabuf_head(mbies),
++                              wpabuf_len(mbies));
++
++problem:
++      return os_snprintf(buf, buflen, "FAIL\n");
++}
++
++
++/* fst list_ifaces */
++static int list_ifaces(const char *group_id, char *buf, size_t buflen)
++{
++      struct fst_group *g;
++      struct fst_iface *f;
++      int ret = 0;
++
++      g = get_fst_group_by_id(group_id);
++      if (!g) {
++              fst_printf(MSG_WARNING, "CTRL: Cannot find group '%s'",
++                         group_id);
++              return os_snprintf(buf, buflen, "FAIL\n");
++      }
++
++      foreach_fst_group_iface(g, f) {
++              int res;
++              const u8 *iface_addr = fst_iface_get_addr(f);
++
++              res = os_snprintf(buf + ret, buflen - ret,
++                                "%s|" MACSTR "|%u|%u\n",
++                                fst_iface_get_name(f),
++                                MAC2STR(iface_addr),
++                                fst_iface_get_priority(f),
++                                fst_iface_get_llt(f));
++              if (os_snprintf_error(buflen - ret, res))
++                      break;
++              ret += res;
++      }
++
++      return ret;
++}
++
++
++/* fst list_groups */
++static int list_groups(const char *cmd, char *buf, size_t buflen)
++{
++      struct fst_group *g;
++      int ret = 0;
++
++      foreach_fst_group(g) {
++              int res;
++
++              res = os_snprintf(buf + ret, buflen - ret, "%s\n",
++                                fst_group_get_id(g));
++              if (os_snprintf_error(buflen - ret, res))
++                      break;
++              ret += res;
++      }
++
++      return ret;
++}
++
++
++static const char * band_freq(enum mb_band_id band)
++{
++      static const char *band_names[] = {
++              [MB_BAND_ID_WIFI_2_4GHZ] "2.4GHZ",
++              [MB_BAND_ID_WIFI_5GHZ] "5GHZ",
++              [MB_BAND_ID_WIFI_60GHZ] "60GHZ",
++      };
++
++      return fst_get_str_name(band, band_names, ARRAY_SIZE(band_names));
++}
++
++
++static int print_band(unsigned num, struct fst_iface *iface, const u8 *addr,
++                    char *buf, size_t buflen)
++{
++      const struct wpabuf *wpabuf;
++      enum hostapd_hw_mode hw_mode;
++      u8 channel;
++      int ret = 0;
++
++      fst_iface_get_channel_info(iface, &hw_mode, &channel);
++
++      ret += os_snprintf(buf + ret, buflen - ret, "band%u_frequency=%s\n",
++                         num, band_freq(fst_hw_mode_to_band(hw_mode)));
++      ret += os_snprintf(buf + ret, buflen - ret, "band%u_iface=%s\n",
++                         num, fst_iface_get_name(iface));
++      wpabuf = fst_iface_get_peer_mb_ie(iface, addr);
++      if (wpabuf) {
++              ret += os_snprintf(buf + ret, buflen - ret, "band%u_mb_ies=",
++                                 num);
++              ret += wpa_snprintf_hex(buf + ret, buflen - ret,
++                                      wpabuf_head(wpabuf),
++                                      wpabuf_len(wpabuf));
++              ret += os_snprintf(buf + ret, buflen - ret, "\n");
++      }
++      ret += os_snprintf(buf + ret, buflen - ret, "band%u_fst_group_id=%s\n",
++                         num, fst_iface_get_group_id(iface));
++      ret += os_snprintf(buf + ret, buflen - ret, "band%u_fst_priority=%u\n",
++                         num, fst_iface_get_priority(iface));
++      ret += os_snprintf(buf + ret, buflen - ret, "band%u_fst_llt=%u\n",
++                         num, fst_iface_get_llt(iface));
++
++      return ret;
++}
++
++
++static void fst_ctrl_iface_on_iface_state_changed(struct fst_iface *i,
++                                                Boolean attached)
++{
++      union fst_event_extra extra;
++
++      os_memset(&extra, 0, sizeof(extra));
++      extra.iface_state.attached = attached;
++      os_strlcpy(extra.iface_state.ifname, fst_iface_get_name(i),
++                 sizeof(extra.iface_state.ifname));
++      os_strlcpy(extra.iface_state.group_id, fst_iface_get_group_id(i),
++                 sizeof(extra.iface_state.group_id));
++
++      fst_ctrl_iface_notify(i, FST_INVALID_SESSION_ID,
++                            EVENT_FST_IFACE_STATE_CHANGED, &extra);
++}
++
++
++static int fst_ctrl_iface_on_iface_added(struct fst_iface *i)
++{
++      fst_ctrl_iface_on_iface_state_changed(i, TRUE);
++      return 0;
++}
++
++
++static void fst_ctrl_iface_on_iface_removed(struct fst_iface *i)
++{
++      fst_ctrl_iface_on_iface_state_changed(i, FALSE);
++}
++
++
++static void fst_ctrl_iface_on_event(enum fst_event_type event_type,
++                                  struct fst_iface *i, struct fst_session *s,
++                                  const union fst_event_extra *extra)
++{
++      u32 session_id = s ? fst_session_get_id(s) : FST_INVALID_SESSION_ID;
++
++      fst_ctrl_iface_notify(i, session_id, event_type, extra);
++}
++
++
++static const struct fst_ctrl ctrl_cli = {
++      .on_iface_added = fst_ctrl_iface_on_iface_added,
++      .on_iface_removed =  fst_ctrl_iface_on_iface_removed,
++      .on_event = fst_ctrl_iface_on_event,
++};
++
++const struct fst_ctrl *fst_ctrl_cli = &ctrl_cli;
++
++
++int fst_ctrl_iface_mb_info(const u8 *addr, char *buf, size_t buflen)
++{
++      struct fst_group *g;
++      struct fst_iface *f;
++      unsigned num = 0;
++      int ret = 0;
++
++      foreach_fst_group(g) {
++              foreach_fst_group_iface(g, f) {
++                      if (fst_iface_is_connected(f, addr)) {
++                              ret += print_band(num++, f, addr,
++                                                buf + ret, buflen - ret);
++                      }
++              }
++      }
++
++      return ret;
++}
++
++
++/* fst ctrl processor */
++int fst_ctrl_iface_receive(const char *cmd, char *reply, size_t reply_size)
++{
++      static const struct fst_command {
++              const char *name;
++              unsigned has_param;
++              int (*process)(const char *group_id, char *buf, size_t buflen);
++      } commands[] = {
++              { FST_CMD_LIST_GROUPS, 0, list_groups},
++              { FST_CMD_LIST_IFACES, 1, list_ifaces},
++              { FST_CMD_IFACE_PEERS, 1, iface_peers},
++              { FST_CMD_GET_PEER_MBIES, 1, get_peer_mbies},
++              { FST_CMD_LIST_SESSIONS, 1, list_sessions},
++              { FST_CMD_SESSION_ADD, 1, session_add},
++              { FST_CMD_SESSION_REMOVE, 1, session_remove},
++              { FST_CMD_SESSION_GET, 1, session_get},
++              { FST_CMD_SESSION_SET, 1, session_set},
++              { FST_CMD_SESSION_INITIATE, 1, session_initiate},
++              { FST_CMD_SESSION_RESPOND, 1, session_respond},
++              { FST_CMD_SESSION_TRANSFER, 1, session_transfer},
++              { FST_CMD_SESSION_TEARDOWN, 1, session_teardown},
++#ifdef CONFIG_FST_TEST
++              { FST_CMD_TEST_REQUEST, 1, test_request },
++#endif /* CONFIG_FST_TEST */
++              { NULL, 0, NULL }
++      };
++      const struct fst_command *c;
++      const char *p;
++      const char *temp;
++      Boolean non_spaces_found;
++
++      for (c = commands; c->name; c++) {
++              if (os_strncasecmp(cmd, c->name, os_strlen(c->name)) != 0)
++                      continue;
++              p = cmd + os_strlen(c->name);
++              if (c->has_param) {
++                      if (!isspace(p[0]))
++                              return os_snprintf(reply, reply_size, "FAIL\n");
++                      p++;
++                      temp = p;
++                      non_spaces_found = FALSE;
++                      while (*temp) {
++                              if (!isspace(*temp)) {
++                                      non_spaces_found = TRUE;
++                                      break;
++                              }
++                              temp++;
++                      }
++                      if (!non_spaces_found)
++                              return os_snprintf(reply, reply_size, "FAIL\n");
++              }
++              return c->process(p, reply, reply_size);
++      }
++
++      return os_snprintf(reply, reply_size, "UNKNOWN FST COMMAND\n");
++}
++
++
++int fst_read_next_int_param(const char *params, Boolean *valid, char **endp)
++{
++      int ret = -1;
++      const char *curp;
++
++      *valid = FALSE;
++      *endp = (char *) params;
++      curp = params;
++      if (*curp) {
++              ret = (int) strtol(curp, endp, 0);
++              if (!**endp || isspace(**endp))
++                      *valid = TRUE;
++      }
++
++      return ret;
++}
++
++
++int fst_read_next_text_param(const char *params, char *buf, size_t buflen,
++                           char **endp)
++{
++      size_t max_chars_to_copy;
++      char *cur_dest;
++
++      *endp = (char *) params;
++      while (isspace(**endp))
++              (*endp)++;
++      if (!**endp || buflen <= 1)
++              return -EINVAL;
++
++      max_chars_to_copy = buflen - 1;
++      /* We need 1 byte for the terminating zero */
++      cur_dest = buf;
++      while (**endp && !isspace(**endp) && max_chars_to_copy > 0) {
++              *cur_dest = **endp;
++              (*endp)++;
++              cur_dest++;
++              max_chars_to_copy--;
++      }
++      *cur_dest = 0;
++
++      return 0;
++}
++
++
++int fst_read_peer_addr(const char *mac, u8 *peer_addr)
++{
++      if (hwaddr_aton(mac, peer_addr)) {
++              fst_printf(MSG_WARNING, "Bad peer_mac %s: invalid addr string",
++                         mac);
++              return -1;
++      }
++
++      if (is_zero_ether_addr(peer_addr) ||
++          is_multicast_ether_addr(peer_addr)) {
++              fst_printf(MSG_WARNING, "Bad peer_mac %s: not a unicast addr",
++                         mac);
++              return -1;
++      }
++
++      return 0;
++}
++
++
++int fst_parse_attach_command(const char *cmd, char *ifname, size_t ifname_size,
++                           struct fst_iface_cfg *cfg)
++{
++      char *pos;
++      char *endp;
++      Boolean is_valid;
++      int val;
++
++      if (fst_read_next_text_param(cmd, ifname, ifname_size, &endp) ||
++          fst_read_next_text_param(endp, cfg->group_id, sizeof(cfg->group_id),
++                                   &endp))
++              return -EINVAL;
++
++      cfg->llt = FST_DEFAULT_LLT_CFG_VALUE;
++      cfg->priority = 0;
++      pos = os_strstr(endp, FST_ATTACH_CMD_PNAME_LLT);
++      if (pos) {
++              pos += os_strlen(FST_ATTACH_CMD_PNAME_LLT);
++              if (*pos == '=') {
++                      val = fst_read_next_int_param(pos + 1, &is_valid,
++                                                    &endp);
++                      if (is_valid)
++                              cfg->llt = val;
++              }
++      }
++      pos = os_strstr(endp, FST_ATTACH_CMD_PNAME_PRIORITY);
++      if (pos) {
++              pos += os_strlen(FST_ATTACH_CMD_PNAME_PRIORITY);
++              if (*pos == '=') {
++                      val = fst_read_next_int_param(pos + 1, &is_valid,
++                                                    &endp);
++                      if (is_valid)
++                              cfg->priority = (u8) val;
++              }
++      }
++
++      return 0;
++}
++
++
++int fst_parse_detach_command(const char *cmd, char *ifname, size_t ifname_size)
++{
++      char *endp;
++
++      return fst_read_next_text_param(cmd, ifname, ifname_size, &endp);
++}
++
++
++int fst_iface_detach(const char *ifname)
++{
++      struct fst_group *g;
++
++      foreach_fst_group(g) {
++              struct fst_iface *f;
++
++              f = fst_group_get_iface_by_name(g, ifname);
++              if (f) {
++                      fst_detach(f);
++                      return 0;
++              }
++      }
++
++      return -EINVAL;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4d0cd9fce4e00c2f3010dc3f43860c030cb68180
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,45 @@@
++/*
++ * FST module - internal Control interface 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 FST_CTRL_IFACE_H
++#define FST_CTRL_IFACE_H
++
++#include "fst/fst_ctrl_aux.h"
++
++#ifdef CONFIG_FST
++
++/* receiver */
++int fst_ctrl_iface_mb_info(const u8 *addr, char *buf, size_t buflen);
++
++int fst_ctrl_iface_receive(const char *txtaddr, char *buf, size_t buflen);
++
++extern const struct fst_ctrl *fst_ctrl_cli;
++
++#else /* CONFIG_FST */
++
++static inline int
++fst_ctrl_iface_mb_info(const u8 *addr, char *buf, size_t buflen)
++{
++      return 0;
++}
++
++#endif /* CONFIG_FST */
++
++int fst_read_next_int_param(const char *params, Boolean *valid, char **endp);
++int fst_read_next_text_param(const char *params, char *buf, size_t buflen,
++                           char **endp);
++int fst_read_peer_addr(const char *mac, u8 *peer_addr);
++
++struct fst_iface_cfg;
++
++int fst_parse_attach_command(const char *cmd, char *ifname, size_t ifname_size,
++                           struct fst_iface_cfg *cfg);
++int fst_parse_detach_command(const char *cmd, char *ifname, size_t ifname_size);
++int fst_iface_detach(const char *ifname);
++
++#endif /* CTRL_IFACE_FST_H */
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8ddcc61376b2d2f9be46716781dd02070183b0ac
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,87 @@@
++/*
++ * FST module - FST related 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 IEEE_80211_FST_DEFS_H
++#define IEEE_80211_FST_DEFS_H
++
++/* IEEE Std 802.11ad */
++
++#define MB_STA_CHANNEL_ALL 0
++
++enum session_type {
++      SESSION_TYPE_BSS = 0, /*  Infrastructure BSS */
++      SESSION_TYPE_IBSS = 1,
++      SESSION_TYPE_DLS = 2,
++      SESSION_TYPE_TDLS = 3,
++      SESSION_TYPE_PBSS = 4
++};
++
++#define SESSION_CONTROL(session_type, switch_intent) \
++      (((u8) ((session_type) & 0x7)) | ((switch_intent) ? 0x10 : 0x00))
++
++#define GET_SESSION_CONTROL_TYPE(session_control) \
++      ((u8) ((session_control) & 0x7))
++
++#define GET_SESSION_CONTROL_SWITCH_INTENT(session_control) \
++      (((session_control) & 0x10) >> 4)
++
++/* 8.4.2.147  Session Transition element */
++struct session_transition_ie {
++      u8 element_id;
++      u8 length;
++      u32 fsts_id;
++      u8 session_control;
++      u8 new_band_id;
++      u8 new_band_setup;
++      u8 new_band_op;
++      u8 old_band_id;
++      u8 old_band_setup;
++      u8 old_band_op;
++} STRUCT_PACKED;
++
++struct fst_setup_req {
++      u8 action;
++      u8 dialog_token;
++      u32 llt;
++      struct session_transition_ie stie;
++      /* Multi-band (optional) */
++      /* Wakeup Schedule (optional) */
++      /* Awake Window (optional) */
++      /* Switching Stream (optional) */
++} STRUCT_PACKED;
++
++struct fst_setup_res {
++      u8 action;
++      u8 dialog_token;
++      u8 status_code;
++      struct session_transition_ie stie;
++      /* Multi-band (optional) */
++      /* Wakeup Schedule (optional) */
++      /* Awake Window (optional) */
++      /* Switching Stream (optional) */
++      /* Timeout Interval (optional) */
++} STRUCT_PACKED;
++
++struct fst_ack_req {
++      u8 action;
++      u8 dialog_token;
++      u32 fsts_id;
++} STRUCT_PACKED;
++
++struct fst_ack_res {
++      u8 action;
++      u8 dialog_token;
++      u32 fsts_id;
++} STRUCT_PACKED;
++
++struct fst_tear_down {
++      u8 action;
++      u32 fsts_id;
++} STRUCT_PACKED;
++
++#endif /* IEEE_80211_FST_DEFS_H */
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f6c7be9435f412938c12e126f835d8a07fdd8f9b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,462 @@@
++/*
++ * FST module - FST group object implementation
++ * 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 "common/defs.h"
++#include "common/ieee802_11_defs.h"
++#include "common/ieee802_11_common.h"
++#include "drivers/driver.h"
++#include "fst/fst_internal.h"
++#include "fst/fst_defs.h"
++
++
++struct dl_list fst_global_groups_list;
++
++#ifndef HOSTAPD
++static Boolean fst_has_fst_peer(struct fst_iface *iface, Boolean *has_peer)
++{
++      const u8 *bssid;
++
++      bssid = fst_iface_get_bssid(iface);
++      if (!bssid) {
++              *has_peer = FALSE;
++              return FALSE;
++      }
++
++      *has_peer = TRUE;
++      return fst_iface_get_peer_mb_ie(iface, bssid) != NULL;
++}
++#endif /* HOSTAPD */
++
++
++static void fst_dump_mb_ies(const char *group_id, const char *ifname,
++                          struct wpabuf *mbies)
++{
++      const u8 *p = wpabuf_head(mbies);
++      size_t s = wpabuf_len(mbies);
++
++      while (s >= 2) {
++              const struct multi_band_ie *mbie =
++                      (const struct multi_band_ie *) p;
++              WPA_ASSERT(mbie->eid == WLAN_EID_MULTI_BAND);
++              WPA_ASSERT(2 + mbie->len >= sizeof(*mbie));
++
++              fst_printf(MSG_WARNING,
++                         "%s: %s: mb_ctrl=%u band_id=%u op_class=%u chan=%u bssid="
++                         MACSTR
++                         " beacon_int=%u tsf_offs=[%u %u %u %u %u %u %u %u] mb_cc=0x%02x tmout=%u",
++                         group_id, ifname,
++                         mbie->mb_ctrl, mbie->band_id, mbie->op_class,
++                         mbie->chan, MAC2STR(mbie->bssid), mbie->beacon_int,
++                         mbie->tsf_offs[0], mbie->tsf_offs[1],
++                         mbie->tsf_offs[2], mbie->tsf_offs[3],
++                         mbie->tsf_offs[4], mbie->tsf_offs[5],
++                         mbie->tsf_offs[6], mbie->tsf_offs[7],
++                         mbie->mb_connection_capability,
++                         mbie->fst_session_tmout);
++
++              p += 2 + mbie->len;
++              s -= 2 + mbie->len;
++      }
++}
++
++
++static void fst_fill_mb_ie(struct wpabuf *buf, const u8 *bssid,
++                         const u8 *own_addr, enum mb_band_id band, u8 channel)
++{
++      struct multi_band_ie *mbie;
++      size_t len = sizeof(*mbie);
++
++      if (own_addr)
++              len += ETH_ALEN;
++
++      mbie = wpabuf_put(buf, len);
++
++      os_memset(mbie, 0, len);
++
++      mbie->eid = WLAN_EID_MULTI_BAND;
++      mbie->len = len - 2;
++#ifdef HOSTAPD
++      mbie->mb_ctrl = MB_STA_ROLE_AP;
++      mbie->mb_connection_capability = MB_CONNECTION_CAPABILITY_AP;
++#else /* HOSTAPD */
++      mbie->mb_ctrl = MB_STA_ROLE_NON_PCP_NON_AP;
++      mbie->mb_connection_capability = 0;
++#endif /* HOSTAPD */
++      if (bssid)
++              os_memcpy(mbie->bssid, bssid, ETH_ALEN);
++      mbie->band_id = band;
++      mbie->op_class = 0;  /* means all */
++      mbie->chan = channel;
++      mbie->fst_session_tmout = FST_DEFAULT_SESSION_TIMEOUT_TU;
++
++      if (own_addr) {
++              mbie->mb_ctrl |= MB_CTRL_STA_MAC_PRESENT;
++              os_memcpy(&mbie[1], own_addr, ETH_ALEN);
++      }
++}
++
++
++static unsigned fst_fill_iface_mb_ies(struct fst_iface *f, struct wpabuf *buf)
++{
++      const  u8 *bssid;
++
++      bssid = fst_iface_get_bssid(f);
++      if (bssid) {
++              enum hostapd_hw_mode hw_mode;
++              u8 channel;
++
++              if (buf) {
++                      fst_iface_get_channel_info(f, &hw_mode, &channel);
++                      fst_fill_mb_ie(buf, bssid, fst_iface_get_addr(f),
++                                     fst_hw_mode_to_band(hw_mode), channel);
++              }
++              return 1;
++      } else {
++              unsigned bands[MB_BAND_ID_WIFI_60GHZ + 1] = {};
++              struct hostapd_hw_modes *modes;
++              enum mb_band_id b;
++              int num_modes = fst_iface_get_hw_modes(f, &modes);
++              int ret = 0;
++
++              while (num_modes--) {
++                      b = fst_hw_mode_to_band(modes->mode);
++                      modes++;
++                      if (b >= ARRAY_SIZE(bands) || bands[b]++)
++                              continue;
++                      ret++;
++                      if (buf)
++                              fst_fill_mb_ie(buf, NULL, fst_iface_get_addr(f),
++                                             b, MB_STA_CHANNEL_ALL);
++              }
++              return ret;
++      }
++}
++
++
++static struct wpabuf * fst_group_create_mb_ie(struct fst_group *g,
++                                            struct fst_iface *i)
++{
++      struct wpabuf *buf;
++      struct fst_iface *f;
++      unsigned int nof_mbies = 0;
++      unsigned int nof_ifaces_added = 0;
++#ifndef HOSTAPD
++      Boolean has_peer;
++      Boolean has_fst_peer;
++
++      foreach_fst_group_iface(g, f) {
++              has_fst_peer = fst_has_fst_peer(f, &has_peer);
++              if (has_peer && !has_fst_peer)
++                      return NULL;
++      }
++#endif /* HOSTAPD */
++
++      foreach_fst_group_iface(g, f) {
++              if (f == i)
++                      continue;
++              nof_mbies += fst_fill_iface_mb_ies(f, NULL);
++      }
++
++      buf = wpabuf_alloc(nof_mbies *
++                         (sizeof(struct multi_band_ie) + ETH_ALEN));
++      if (!buf) {
++              fst_printf_iface(i, MSG_ERROR,
++                               "cannot allocate mem for %u MB IEs",
++                               nof_mbies);
++              return NULL;
++      }
++
++      /* The list is sorted in descending order by priorities, so MB IEs will
++       * be arranged in the same order, as required by spec (see corresponding
++       * comment in.fst_attach().
++       */
++      foreach_fst_group_iface(g, f) {
++              if (f == i)
++                      continue;
++
++              fst_fill_iface_mb_ies(f, buf);
++              ++nof_ifaces_added;
++
++              fst_printf_iface(i, MSG_DEBUG, "added to MB IE");
++      }
++
++      if (!nof_ifaces_added) {
++              wpabuf_free(buf);
++              buf = NULL;
++              fst_printf_iface(i, MSG_INFO,
++                               "cannot add MB IE: no backup ifaces");
++      } else {
++              fst_dump_mb_ies(fst_group_get_id(g), fst_iface_get_name(i),
++                              buf);
++      }
++
++      return buf;
++}
++
++
++static const u8 * fst_mbie_get_peer_addr(const struct multi_band_ie *mbie)
++{
++      const u8 *peer_addr = NULL;
++
++      switch (MB_CTRL_ROLE(mbie->mb_ctrl)) {
++      case MB_STA_ROLE_AP:
++              peer_addr = mbie->bssid;
++              break;
++      case MB_STA_ROLE_NON_PCP_NON_AP:
++              if (mbie->mb_ctrl & MB_CTRL_STA_MAC_PRESENT &&
++                  (size_t) 2 + mbie->len >= sizeof(*mbie) + ETH_ALEN)
++                      peer_addr = (const u8 *) &mbie[1];
++              break;
++      default:
++              break;
++      }
++
++      return peer_addr;
++}
++
++
++static struct fst_iface *
++fst_group_get_new_iface_by_mbie_and_band_id(struct fst_group *g,
++                                          const u8 *mb_ies_buff,
++                                          size_t mb_ies_size,
++                                          u8 band_id,
++                                          u8 *iface_peer_addr)
++{
++      while (mb_ies_size >= 2) {
++              const struct multi_band_ie *mbie =
++                      (const struct multi_band_ie *) mb_ies_buff;
++
++              if (mbie->eid != WLAN_EID_MULTI_BAND ||
++                  (size_t) 2 + mbie->len < sizeof(*mbie))
++                      break;
++
++              if (mbie->band_id == band_id) {
++                      struct fst_iface *iface;
++
++                      foreach_fst_group_iface(g, iface) {
++                              const u8 *peer_addr =
++                                      fst_mbie_get_peer_addr(mbie);
++
++                              if (peer_addr &&
++                                  fst_iface_is_connected(iface, peer_addr) &&
++                                  band_id == fst_iface_get_band_id(iface)) {
++                                      os_memcpy(iface_peer_addr, peer_addr,
++                                                ETH_ALEN);
++                                      return iface;
++                              }
++                      }
++                      break;
++              }
++
++              mb_ies_buff += 2 + mbie->len;
++              mb_ies_size -= 2 + mbie->len;
++      }
++
++      return NULL;
++}
++
++
++struct fst_iface * fst_group_get_iface_by_name(struct fst_group *g,
++                                             const char *ifname)
++{
++      struct fst_iface *f;
++
++      foreach_fst_group_iface(g, f) {
++              const char *in = fst_iface_get_name(f);
++
++              if (os_strncmp(in, ifname, os_strlen(in)) == 0)
++                      return f;
++      }
++
++      return NULL;
++}
++
++
++u8 fst_group_assign_dialog_token(struct fst_group *g)
++{
++      g->dialog_token++;
++      if (g->dialog_token == 0)
++              g->dialog_token++;
++      return g->dialog_token;
++}
++
++
++u32 fst_group_assign_fsts_id(struct fst_group *g)
++{
++      g->fsts_id++;
++      return g->fsts_id;
++}
++
++
++static Boolean
++fst_group_does_iface_appear_in_other_mbies(struct fst_group *g,
++                                         struct fst_iface *iface,
++                                         struct fst_iface *other,
++                                         u8 *peer_addr)
++{
++      struct fst_get_peer_ctx *ctx;
++      const u8 *addr;
++      const u8 *iface_addr;
++      enum mb_band_id  iface_band_id;
++
++      WPA_ASSERT(g == fst_iface_get_group(iface));
++      WPA_ASSERT(g == fst_iface_get_group(other));
++
++      iface_addr = fst_iface_get_addr(iface);
++      iface_band_id = fst_iface_get_band_id(iface);
++
++      addr = fst_iface_get_peer_first(other, &ctx, TRUE);
++      for (; addr; addr = fst_iface_get_peer_next(other, &ctx, TRUE)) {
++              const struct wpabuf *mbies;
++              u8 other_iface_peer_addr[ETH_ALEN];
++              struct fst_iface *other_new_iface;
++
++              mbies = fst_iface_get_peer_mb_ie(other, addr);
++              if (!mbies)
++                      continue;
++
++              other_new_iface = fst_group_get_new_iface_by_mbie_and_band_id(
++                      g, wpabuf_head(mbies), wpabuf_len(mbies),
++                      iface_band_id, other_iface_peer_addr);
++              if (other_new_iface == iface &&
++                  os_memcmp(iface_addr, other_iface_peer_addr,
++                            ETH_ALEN) != 0) {
++                      os_memcpy(peer_addr, addr, ETH_ALEN);
++                      return TRUE;
++              }
++      }
++
++      return FALSE;
++}
++
++
++struct fst_iface *
++fst_group_find_new_iface_by_stie(struct fst_group *g,
++                               struct fst_iface *iface,
++                               const u8 *peer_addr,
++                               const struct session_transition_ie *stie,
++                               u8 *iface_peer_addr)
++{
++      struct fst_iface *i;
++
++      foreach_fst_group_iface(g, i) {
++              if (i == iface ||
++                  stie->new_band_id != fst_iface_get_band_id(i))
++                      continue;
++              if (fst_group_does_iface_appear_in_other_mbies(g, iface, i,
++                      iface_peer_addr))
++                      return i;
++              break;
++      }
++      return NULL;
++}
++
++
++struct fst_iface *
++fst_group_get_new_iface_by_stie_and_mbie(
++      struct fst_group *g, const u8 *mb_ies_buff, size_t mb_ies_size,
++      const struct session_transition_ie *stie, u8 *iface_peer_addr)
++{
++      return fst_group_get_new_iface_by_mbie_and_band_id(
++              g, mb_ies_buff, mb_ies_size, stie->new_band_id,
++              iface_peer_addr);
++}
++
++
++struct fst_group * fst_group_create(const char *group_id)
++{
++      struct fst_group *g;
++
++      g = os_zalloc(sizeof(*g));
++      if (g == NULL) {
++              fst_printf(MSG_ERROR, "%s: Cannot alloc group", group_id);
++              return NULL;
++      }
++
++      dl_list_init(&g->ifaces);
++      os_strlcpy(g->group_id, group_id, sizeof(g->group_id));
++
++      dl_list_add_tail(&fst_global_groups_list, &g->global_groups_lentry);
++      fst_printf_group(g, MSG_DEBUG, "instance created");
++
++      foreach_fst_ctrl_call(on_group_created, g);
++
++      return g;
++}
++
++
++void fst_group_attach_iface(struct fst_group *g, struct fst_iface *i)
++{
++      struct dl_list *list = &g->ifaces;
++      struct fst_iface *f;
++
++      /*
++       * Add new interface to the list.
++       * The list is sorted in descending order by priority to allow
++       * multiple MB IEs creation according to the spec (see 10.32 Multi-band
++       * operation, 10.32.1 General), as they should be ordered according to
++       * priorities.
++       */
++      foreach_fst_group_iface(g, f) {
++              if (fst_iface_get_priority(f) < fst_iface_get_priority(i))
++                      break;
++              list = &f->group_lentry;
++      }
++      dl_list_add(list, &i->group_lentry);
++}
++
++
++void fst_group_detach_iface(struct fst_group *g, struct fst_iface *i)
++{
++      dl_list_del(&i->group_lentry);
++}
++
++
++void fst_group_delete(struct fst_group *group)
++{
++      struct fst_session *s;
++
++      dl_list_del(&group->global_groups_lentry);
++      WPA_ASSERT(dl_list_empty(&group->ifaces));
++      foreach_fst_ctrl_call(on_group_deleted, group);
++      fst_printf_group(group, MSG_DEBUG, "instance deleted");
++      while ((s = fst_session_global_get_first_by_group(group)) != NULL)
++              fst_session_delete(s);
++      os_free(group);
++}
++
++
++Boolean fst_group_delete_if_empty(struct fst_group *group)
++{
++      Boolean is_empty = !fst_group_has_ifaces(group) &&
++              !fst_session_global_get_first_by_group(group);
++
++      if (is_empty)
++              fst_group_delete(group);
++
++      return is_empty;
++}
++
++
++void fst_group_update_ie(struct fst_group *g)
++{
++      struct fst_iface *i;
++
++      foreach_fst_group_iface(g, i) {
++              struct wpabuf *mbie = fst_group_create_mb_ie(g, i);
++
++              if (!mbie)
++                      fst_printf_iface(i, MSG_WARNING, "cannot create MB IE");
++
++              fst_iface_attach_mbie(i, mbie);
++              fst_iface_set_ies(i, mbie);
++              fst_printf_iface(i, MSG_DEBUG, "multi-band IE set to %p", mbie);
++      }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3a87c0bc91c91720ceebacfd826d237d43efa97a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,75 @@@
++/*
++ * FST module - FST group object 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 FST_GROUP_H
++#define FST_GROUP_H
++
++struct fst_group {
++      char group_id[IFNAMSIZ + 1];
++      struct dl_list ifaces;
++      u8 dialog_token;
++      u32 fsts_id;
++      struct dl_list global_groups_lentry;
++};
++
++struct session_transition_ie;
++
++#define foreach_fst_group_iface(g, i) \
++      dl_list_for_each((i), &(g)->ifaces, struct fst_iface, group_lentry)
++
++struct fst_group * fst_group_create(const char *group_id);
++void fst_group_attach_iface(struct fst_group *g, struct fst_iface *i);
++void fst_group_detach_iface(struct fst_group *g, struct fst_iface *i);
++void fst_group_delete(struct fst_group *g);
++
++void fst_group_update_ie(struct fst_group *g);
++
++static inline Boolean fst_group_has_ifaces(struct fst_group *g)
++{
++      return !dl_list_empty(&g->ifaces);
++}
++
++static inline struct fst_iface * fst_group_first_iface(struct fst_group *g)
++{
++      return dl_list_first(&g->ifaces, struct fst_iface, group_lentry);
++}
++
++static inline const char * fst_group_get_id(struct fst_group *g)
++{
++      return g->group_id;
++}
++
++Boolean fst_group_delete_if_empty(struct fst_group *group);
++struct fst_iface * fst_group_get_iface_by_name(struct fst_group *g,
++                                             const char *ifname);
++struct fst_iface *
++fst_group_find_new_iface_by_stie(struct fst_group *g,
++                               struct fst_iface *iface,
++                               const u8 *peer_addr,
++                               const struct session_transition_ie *stie,
++                               u8 *iface_peer_addr);
++struct fst_iface *
++fst_group_get_new_iface_by_stie_and_mbie(
++      struct fst_group *g, const u8 *mb_ies_buff, size_t mb_ies_size,
++      const struct session_transition_ie *stie, u8 *iface_peer_addr);
++u8  fst_group_assign_dialog_token(struct fst_group *g);
++u32 fst_group_assign_fsts_id(struct fst_group *g);
++
++extern struct dl_list fst_global_groups_list;
++
++#define foreach_fst_group(g) \
++      dl_list_for_each((g), &fst_global_groups_list, \
++                       struct fst_group, global_groups_lentry)
++
++static inline struct fst_group * fst_first_group(void)
++{
++      return dl_list_first(&fst_global_groups_list, struct fst_group,
++                           global_groups_lentry);
++}
++
++#endif /* FST_GROUP_H */
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5a92d2c33e420f41e562e33a8741995cb10f01c6
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,79 @@@
++/*
++ * FST module - FST interface object implementation
++ * 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 "fst/fst_internal.h"
++#include "fst/fst_defs.h"
++
++
++struct fst_iface * fst_iface_create(struct fst_group *g, const char *ifname,
++                                  const u8 *own_addr,
++                                  const struct fst_wpa_obj *iface_obj,
++                                  const struct fst_iface_cfg *cfg)
++{
++      struct fst_iface *i;
++
++      i = os_zalloc(sizeof(*i));
++      if (!i) {
++              fst_printf_group(g, MSG_ERROR, "cannot allocate iface for %s",
++                              ifname);
++              return NULL;
++      }
++
++      i->cfg = *cfg;
++      i->iface_obj = *iface_obj;
++      i->group = g;
++      os_strlcpy(i->ifname, ifname, sizeof(i->ifname));
++      os_memcpy(i->own_addr, own_addr, sizeof(i->own_addr));
++
++      if (!i->cfg.llt) {
++              fst_printf_iface(i, MSG_WARNING, "Zero llt adjusted");
++              i->cfg.llt = FST_DEFAULT_LLT_CFG_VALUE;
++      }
++
++      return i;
++}
++
++
++void fst_iface_delete(struct fst_iface *i)
++{
++      fst_iface_set_ies(i, NULL);
++      wpabuf_free(i->mb_ie);
++      os_free(i);
++}
++
++
++Boolean fst_iface_is_connected(struct fst_iface *iface, const u8 *addr)
++{
++      struct fst_get_peer_ctx *ctx;
++      const u8 *a = fst_iface_get_peer_first(iface, &ctx, TRUE);
++
++      for (; a != NULL; a = fst_iface_get_peer_next(iface, &ctx, TRUE))
++              if (os_memcmp(addr, a, ETH_ALEN) == 0)
++                      return TRUE;
++
++      return FALSE;
++}
++
++
++void fst_iface_attach_mbie(struct fst_iface *i, struct wpabuf *mbie)
++{
++      wpabuf_free(i->mb_ie);
++      i->mb_ie = mbie;
++}
++
++
++enum mb_band_id fst_iface_get_band_id(struct fst_iface *i)
++{
++      enum hostapd_hw_mode hw_mode;
++      u8 channel;
++
++      fst_iface_get_channel_info(i, &hw_mode, &channel);
++      return fst_hw_mode_to_band(hw_mode);
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4670d894f7cbd3efb61a2a9a5a5e40ae7ef8dddb
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,135 @@@
++/*
++ * FST module - FST interface object 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 FST_IFACE_H
++#define FST_IFACE_H
++
++#include "utils/includes.h"
++#include "utils/common.h"
++#include "list.h"
++#include "fst.h"
++
++struct fst_iface {
++      struct fst_group *group;
++      struct fst_wpa_obj iface_obj;
++      u8 own_addr[ETH_ALEN];
++      struct wpabuf *mb_ie;
++      char ifname[IFNAMSIZ + 1];
++      struct fst_iface_cfg cfg;
++      struct dl_list group_lentry;
++};
++
++struct fst_iface * fst_iface_create(struct fst_group *g, const char *ifname,
++                                  const u8 *own_addr,
++                                  const struct fst_wpa_obj *iface_obj,
++                                  const struct fst_iface_cfg *cfg);
++void fst_iface_delete(struct fst_iface *i);
++
++static inline struct fst_group * fst_iface_get_group(struct fst_iface *i)
++{
++      return i->group;
++}
++
++static inline const char * fst_iface_get_name(struct fst_iface *i)
++{
++      return i->ifname;
++}
++
++static inline const u8 * fst_iface_get_addr(struct fst_iface *i)
++{
++      return i->own_addr;
++}
++
++static inline const char * fst_iface_get_group_id(struct fst_iface *i)
++{
++      return i->cfg.group_id;
++}
++
++static inline u8 fst_iface_get_priority(struct fst_iface *i)
++{
++      return i->cfg.priority;
++}
++
++static inline u32 fst_iface_get_llt(struct fst_iface *i)
++{
++      return i->cfg.llt;
++}
++
++static inline const struct wpabuf * fst_iface_get_mbie(struct fst_iface *i)
++{
++      return i->mb_ie;
++}
++
++static inline const u8 * fst_iface_get_bssid(struct fst_iface *i)
++{
++      return i->iface_obj.get_bssid(i->iface_obj.ctx);
++}
++
++static inline void fst_iface_get_channel_info(struct fst_iface *i,
++                                            enum hostapd_hw_mode *hw_mode,
++                                            u8 *channel)
++{
++      i->iface_obj.get_channel_info(i->iface_obj.ctx, hw_mode, channel);
++}
++
++static inline int fst_iface_get_hw_modes(struct fst_iface *i,
++                                       struct hostapd_hw_modes **modes)
++{
++      return i->iface_obj.get_hw_modes(i->iface_obj.ctx, modes);
++}
++
++static inline void fst_iface_set_ies(struct fst_iface *i,
++                                   const struct wpabuf *fst_ies)
++{
++      i->iface_obj.set_ies(i->iface_obj.ctx, fst_ies);
++}
++
++static inline int fst_iface_send_action(struct fst_iface *i,
++                                      const u8 *addr, struct wpabuf *data)
++{
++      return i->iface_obj.send_action(i->iface_obj.ctx, addr, data);
++}
++
++static inline const struct wpabuf *
++fst_iface_get_peer_mb_ie(struct fst_iface *i, const u8 *addr)
++{
++      return i->iface_obj.get_mb_ie(i->iface_obj.ctx, addr);
++}
++
++static inline void fst_iface_update_mb_ie(struct fst_iface *i,
++                                        const u8 *addr,
++                                        const u8 *buf, size_t size)
++{
++      return i->iface_obj.update_mb_ie(i->iface_obj.ctx, addr, buf, size);
++}
++
++static inline const u8 * fst_iface_get_peer_first(struct fst_iface *i,
++                                                struct fst_get_peer_ctx **ctx,
++                                                Boolean mb_only)
++{
++      return i->iface_obj.get_peer_first(i->iface_obj.ctx, ctx, mb_only);
++}
++
++static inline const u8 * fst_iface_get_peer_next(struct fst_iface *i,
++                                               struct fst_get_peer_ctx **ctx,
++                                               Boolean mb_only)
++{
++      return i->iface_obj.get_peer_next(i->iface_obj.ctx, ctx, mb_only);
++}
++
++Boolean fst_iface_is_connected(struct fst_iface *iface, const u8 *addr);
++void fst_iface_attach_mbie(struct fst_iface *i, struct wpabuf *mbie);
++enum mb_band_id fst_iface_get_band_id(struct fst_iface *i);
++
++static inline void * fst_iface_get_wpa_obj_ctx(struct fst_iface *i)
++{
++      return i->iface_obj.ctx;
++}
++
++#endif /* FST_IFACE_H */
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9fe32b85409b8b801bc5b4a09bc157c9c0a82343
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,49 @@@
++/*
++ * FST module - auxiliary 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 FST_INTERNAL_H
++#define FST_INTERNAL_H
++
++#include "utils/includes.h"
++#include "utils/common.h"
++#include "common/defs.h"
++#include "common/ieee802_11_defs.h"
++#include "fst/fst_iface.h"
++#include "fst/fst_group.h"
++#include "fst/fst_session.h"
++
++#define fst_printf(level, format, ...) \
++      wpa_printf((level), "FST: " format, ##__VA_ARGS__)
++
++#define fst_printf_group(group, level, format, ...) \
++      wpa_printf((level), "FST: %s: " format, \
++                 fst_group_get_id(group), ##__VA_ARGS__)
++
++#define fst_printf_iface(iface, level, format, ...) \
++      fst_printf_group(fst_iface_get_group(iface), (level), "%s: " format, \
++                       fst_iface_get_name(iface), ##__VA_ARGS__)
++
++enum mb_band_id fst_hw_mode_to_band(enum hostapd_hw_mode mode);
++
++struct fst_ctrl_handle {
++      struct fst_ctrl ctrl;
++      struct dl_list global_ctrls_lentry;
++};
++
++extern struct dl_list fst_global_ctrls_list;
++
++#define foreach_fst_ctrl_call(clb, ...) \
++      do { \
++              struct fst_ctrl_handle *__fst_ctrl_h; \
++              dl_list_for_each(__fst_ctrl_h, &fst_global_ctrls_list, \
++                      struct fst_ctrl_handle, global_ctrls_lentry) \
++                      if (__fst_ctrl_h->ctrl.clb) \
++                              __fst_ctrl_h->ctrl.clb(__VA_ARGS__);\
++      } while (0)
++
++#endif /* FST_INTERNAL_H */
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..55fa69495e99ad4e07644661d81adfcdcde29f18
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1620 @@@
++/*
++ * FST module - FST Session implementation
++ * 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 "common/defs.h"
++#include "fst/fst_internal.h"
++#include "fst/fst_defs.h"
++#include "fst/fst_ctrl_iface.h"
++#ifdef CONFIG_FST_TEST
++#include "fst/fst_ctrl_defs.h"
++#endif /* CONFIG_FST_TEST */
++
++#define US_80211_TU 1024
++
++#define US_TO_TU(m) ((m) * / US_80211_TU)
++#define TU_TO_US(m) ((m) * US_80211_TU)
++
++#define FST_LLT_SWITCH_IMMEDIATELY 0
++
++#define fst_printf_session(s, level, format, ...) \
++      fst_printf((level), "%u (0x%08x): [" MACSTR "," MACSTR "] :" format, \
++                 (s)->id, (s)->data.fsts_id, \
++                 MAC2STR((s)->data.old_peer_addr), \
++                 MAC2STR((s)->data.new_peer_addr), \
++                 ##__VA_ARGS__)
++
++#define fst_printf_siface(s, iface, level, format, ...) \
++      fst_printf_session((s), (level), "%s: " format, \
++                         fst_iface_get_name(iface), ##__VA_ARGS__)
++
++#define fst_printf_sframe(s, is_old, level, format, ...) \
++      fst_printf_siface((s), \
++              (is_old) ? (s)->data.old_iface : (s)->data.new_iface, \
++              (level), format, ##__VA_ARGS__)
++
++#define FST_LLT_MS_DEFAULT 50
++#define FST_ACTION_MAX_SUPPORTED   FST_ACTION_ON_CHANNEL_TUNNEL
++
++const char * const fst_action_names[] = {
++      [FST_ACTION_SETUP_REQUEST]     = "Setup Request",
++      [FST_ACTION_SETUP_RESPONSE]    = "Setup Response",
++      [FST_ACTION_TEAR_DOWN]         = "Tear Down",
++      [FST_ACTION_ACK_REQUEST]       = "Ack Request",
++      [FST_ACTION_ACK_RESPONSE]      = "Ack Response",
++      [FST_ACTION_ON_CHANNEL_TUNNEL] = "On Channel Tunnel",
++};
++
++struct fst_session {
++      struct {
++              /* Session configuration that can be zeroed on reset */
++              u8 old_peer_addr[ETH_ALEN];
++              u8 new_peer_addr[ETH_ALEN];
++              struct fst_iface *new_iface;
++              struct fst_iface *old_iface;
++              u32 llt_ms;
++              u8 pending_setup_req_dlgt;
++              u32 fsts_id; /* FSTS ID, see spec, 8.4.2.147
++                            * Session Transition element */
++      } data;
++      /* Session object internal fields which won't be zeroed on reset */
++      struct dl_list global_sessions_lentry;
++      u32 id; /* Session object ID used to identify
++               * specific session object */
++      struct fst_group *group;
++      enum fst_session_state state;
++      Boolean stt_armed;
++};
++
++static struct dl_list global_sessions_list;
++static u32 global_session_id = 0;
++
++#define foreach_fst_session(s) \
++      dl_list_for_each((s), &global_sessions_list, \
++                       struct fst_session, global_sessions_lentry)
++
++#define foreach_fst_session_safe(s, temp) \
++      dl_list_for_each_safe((s), (temp), &global_sessions_list, \
++                            struct fst_session, global_sessions_lentry)
++
++
++static void fst_session_global_inc_id(void)
++{
++      global_session_id++;
++      if (global_session_id == FST_INVALID_SESSION_ID)
++              global_session_id++;
++}
++
++
++int fst_session_global_init(void)
++{
++      dl_list_init(&global_sessions_list);
++      return 0;
++}
++
++
++void fst_session_global_deinit(void)
++{
++      WPA_ASSERT(dl_list_empty(&global_sessions_list));
++}
++
++
++static inline void fst_session_notify_ctrl(struct fst_session *s,
++                                         enum fst_event_type event_type,
++                                         union fst_event_extra *extra)
++{
++      foreach_fst_ctrl_call(on_event, event_type, NULL, s, extra);
++}
++
++
++static void fst_session_set_state(struct fst_session *s,
++                                enum fst_session_state state,
++                                union fst_session_state_switch_extra *extra)
++{
++      if (s->state != state) {
++              union fst_event_extra evext = {
++                      .session_state = {
++                              .old_state = s->state,
++                              .new_state = state,
++                      },
++              };
++
++              if (extra)
++                      evext.session_state.extra = *extra;
++              fst_session_notify_ctrl(s, EVENT_FST_SESSION_STATE_CHANGED,
++                                      &evext);
++              fst_printf_session(s, MSG_INFO, "State: %s => %s",
++                                 fst_session_state_name(s->state),
++                                 fst_session_state_name(state));
++              s->state = state;
++      }
++}
++
++
++static u32 fst_find_free_session_id(void)
++{
++      u32 i, id = FST_INVALID_SESSION_ID;
++      struct fst_session *s;
++
++      for (i = 0; i < (u32) -1; i++) {
++              Boolean in_use = FALSE;
++
++              foreach_fst_session(s) {
++                      if (s->id == global_session_id) {
++                              fst_session_global_inc_id();
++                              in_use = TRUE;
++                              break;
++                      }
++              }
++              if (!in_use) {
++                      id = global_session_id;
++                      fst_session_global_inc_id();
++                      break;
++              }
++      }
++
++      return id;
++}
++
++
++static void fst_session_timeout_handler(void *eloop_data, void *user_ctx)
++{
++      struct fst_session *s = user_ctx;
++      union fst_session_state_switch_extra extra = {
++              .to_initial = {
++                      .reason = REASON_STT,
++              },
++      };
++
++      fst_printf_session(s, MSG_WARNING, "Session State Timeout");
++      fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &extra);
++}
++
++
++static void fst_session_stt_arm(struct fst_session *s)
++{
++      eloop_register_timeout(0, TU_TO_US(FST_DEFAULT_SESSION_TIMEOUT_TU),
++                             fst_session_timeout_handler, NULL, s);
++      s->stt_armed = TRUE;
++}
++
++
++static void fst_session_stt_disarm(struct fst_session *s)
++{
++      if (s->stt_armed) {
++              eloop_cancel_timeout(fst_session_timeout_handler, NULL, s);
++              s->stt_armed = FALSE;
++      }
++}
++
++
++static Boolean fst_session_is_in_transition(struct fst_session *s)
++{
++      /* See spec, 10.32.2.2  Transitioning between states */
++      return s->stt_armed;
++}
++
++
++static int fst_session_is_in_progress(struct fst_session *s)
++{
++      return s->state != FST_SESSION_STATE_INITIAL;
++}
++
++
++static int fst_session_is_ready_pending(struct fst_session *s)
++{
++      return s->state == FST_SESSION_STATE_SETUP_COMPLETION &&
++              fst_session_is_in_transition(s);
++}
++
++
++static int fst_session_is_ready(struct fst_session *s)
++{
++      return s->state == FST_SESSION_STATE_SETUP_COMPLETION &&
++              !fst_session_is_in_transition(s);
++}
++
++
++static int fst_session_is_switch_requested(struct fst_session *s)
++{
++      return s->state == FST_SESSION_STATE_TRANSITION_DONE &&
++              fst_session_is_in_transition(s);
++}
++
++
++static struct fst_session *
++fst_find_session_in_progress(const u8 *peer_addr, struct fst_group *g)
++{
++      struct fst_session *s;
++
++      foreach_fst_session(s) {
++              if (s->group == g &&
++                  (os_memcmp(s->data.old_peer_addr, peer_addr,
++                             ETH_ALEN) == 0 ||
++                   os_memcmp(s->data.new_peer_addr, peer_addr,
++                             ETH_ALEN) == 0) &&
++                  fst_session_is_in_progress(s))
++                      return s;
++      }
++
++      return NULL;
++}
++
++
++static void fst_session_reset_ex(struct fst_session *s, enum fst_reason reason)
++{
++      union fst_session_state_switch_extra evext = {
++              .to_initial = {
++                      .reason = reason,
++              },
++      };
++
++      if (s->state == FST_SESSION_STATE_SETUP_COMPLETION ||
++          s->state == FST_SESSION_STATE_TRANSITION_DONE)
++              fst_session_tear_down_setup(s);
++      fst_session_stt_disarm(s);
++      os_memset(&s->data, 0, sizeof(s->data));
++      fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &evext);
++}
++
++
++static int fst_session_send_action(struct fst_session *s, Boolean old_iface,
++                                 const void *payload, size_t size,
++                                 const struct wpabuf *extra_buf)
++{
++      size_t len;
++      int res;
++      struct wpabuf *buf;
++      u8 action;
++      struct fst_iface *iface =
++              old_iface ? s->data.old_iface : s->data.new_iface;
++
++      WPA_ASSERT(payload != NULL);
++      WPA_ASSERT(size != 0);
++
++      action = *(const u8 *) payload;
++
++      WPA_ASSERT(action <= FST_ACTION_MAX_SUPPORTED);
++
++      if (!iface) {
++              fst_printf_session(s, MSG_ERROR,
++                                 "no %s interface for FST Action '%s' sending",
++                                 old_iface ? "old" : "new",
++                                 fst_action_names[action]);
++              return -1;
++      }
++
++      len = sizeof(u8) /* category */ + size;
++      if (extra_buf)
++              len += wpabuf_size(extra_buf);
++
++      buf = wpabuf_alloc(len);
++      if (!buf) {
++              fst_printf_session(s, MSG_ERROR,
++                                 "cannot allocate buffer of %zu bytes for FST Action '%s' sending",
++                                 len, fst_action_names[action]);
++              return -1;
++      }
++
++      wpabuf_put_u8(buf, WLAN_ACTION_FST);
++      wpabuf_put_data(buf, payload, size);
++      if (extra_buf)
++              wpabuf_put_buf(buf, extra_buf);
++
++      res = fst_iface_send_action(iface,
++                                  old_iface ? s->data.old_peer_addr :
++                                  s->data.new_peer_addr, buf);
++      if (res < 0)
++              fst_printf_siface(s, iface, MSG_ERROR,
++                                "failed to send FST Action '%s'",
++                                fst_action_names[action]);
++      else
++              fst_printf_siface(s, iface, MSG_DEBUG, "FST Action '%s' sent",
++                                fst_action_names[action]);
++      wpabuf_free(buf);
++
++      return res;
++}
++
++
++static int fst_session_send_tear_down(struct fst_session *s)
++{
++      struct fst_tear_down td;
++      int res;
++
++      if (!fst_session_is_in_progress(s)) {
++              fst_printf_session(s, MSG_ERROR, "No FST setup to tear down");
++              return -1;
++      }
++
++      WPA_ASSERT(s->data.old_iface != NULL);
++      WPA_ASSERT(s->data.new_iface != NULL);
++
++      os_memset(&td, 0, sizeof(td));
++
++      td.action = FST_ACTION_TEAR_DOWN;
++      td.fsts_id = host_to_le32(s->data.fsts_id);
++
++      res = fst_session_send_action(s, TRUE, &td, sizeof(td), NULL);
++      if (!res)
++              fst_printf_sframe(s, TRUE, MSG_INFO, "FST TearDown sent");
++      else
++              fst_printf_sframe(s, TRUE, MSG_ERROR,
++                                "failed to send FST TearDown");
++
++      return res;
++}
++
++
++static void fst_session_handle_setup_request(struct fst_iface *iface,
++                                           const struct ieee80211_mgmt *mgmt,
++                                           size_t frame_len)
++{
++      struct fst_session *s;
++      const struct fst_setup_req *req;
++      struct fst_iface *new_iface = NULL;
++      struct fst_group *g;
++      u8 new_iface_peer_addr[ETH_ALEN];
++      const struct wpabuf *peer_mbies;
++      size_t plen;
++
++      if (frame_len < IEEE80211_HDRLEN + 1 + sizeof(*req))  {
++              fst_printf_iface(iface, MSG_WARNING,
++                               "FST Request dropped: too short (%zu < %zu)",
++                               frame_len,
++                               IEEE80211_HDRLEN + 1 + sizeof(*req));
++              return;
++      }
++      plen = frame_len - IEEE80211_HDRLEN - 1;
++      req = (const struct fst_setup_req *)
++              (((const u8 *) mgmt) + IEEE80211_HDRLEN + 1);
++      if (req->stie.element_id != WLAN_EID_SESSION_TRANSITION ||
++          req->stie.length < 11) {
++              fst_printf_iface(iface, MSG_WARNING,
++                               "FST Request dropped: invalid STIE");
++              return;
++      }
++
++      if (req->stie.new_band_id == req->stie.old_band_id) {
++              fst_printf_iface(iface, MSG_WARNING,
++                               "FST Request dropped: new and old band IDs are the same");
++              return;
++      }
++
++      g = fst_iface_get_group(iface);
++
++      if (plen > sizeof(*req)) {
++              fst_iface_update_mb_ie(iface, mgmt->sa, (const u8 *) (req + 1),
++                                     plen - sizeof(*req));
++              fst_printf_iface(iface, MSG_INFO,
++                               "FST Request: MB IEs updated for " MACSTR,
++                               MAC2STR(mgmt->sa));
++      }
++
++      peer_mbies = fst_iface_get_peer_mb_ie(iface, mgmt->sa);
++      if (peer_mbies) {
++              new_iface = fst_group_get_new_iface_by_stie_and_mbie(
++                      g, wpabuf_head(peer_mbies), wpabuf_len(peer_mbies),
++                      &req->stie, new_iface_peer_addr);
++              if (new_iface)
++                      fst_printf_iface(iface, MSG_INFO,
++                                       "FST Request: new iface (%s:" MACSTR
++                                       ") found by MB IEs",
++                                       fst_iface_get_name(new_iface),
++                                       MAC2STR(new_iface_peer_addr));
++      }
++
++      if (!new_iface) {
++              new_iface = fst_group_find_new_iface_by_stie(
++                      g, iface, mgmt->sa, &req->stie,
++                      new_iface_peer_addr);
++              if (new_iface)
++                      fst_printf_iface(iface, MSG_INFO,
++                                       "FST Request: new iface (%s:" MACSTR
++                                       ") found by others",
++                                       fst_iface_get_name(new_iface),
++                                       MAC2STR(new_iface_peer_addr));
++      }
++
++      if (!new_iface) {
++              fst_printf_iface(iface, MSG_WARNING,
++                               "FST Request dropped: new iface not found");
++              return;
++      }
++
++      s = fst_find_session_in_progress(mgmt->sa, g);
++      if (s) {
++              union fst_session_state_switch_extra evext = {
++                      .to_initial = {
++                              .reason = REASON_SETUP,
++                      },
++              };
++
++              /*
++               * 10.32.2.2  Transitioning between states:
++               * Upon receipt of an FST Setup Request frame, the responder
++               * shall respond with an FST Setup Response frame unless it has
++               * a pending FST Setup Request frame addressed to the initiator
++               * and the responder has a numerically larger MAC address than
++               * the initiator’s MAC address, in which case, the responder
++               * shall delete the received FST Setup Request.
++               */
++              if (os_memcmp(mgmt->da, mgmt->sa, ETH_ALEN) > 0) {
++                      fst_printf_session(s, MSG_WARNING,
++                                         "FST Request dropped due to MAC comparison (our MAC is "
++                                         MACSTR ")",
++                                         MAC2STR(mgmt->da));
++                      return;
++              }
++
++              if (!fst_session_is_ready_pending(s)) {
++                      fst_printf_session(s, MSG_WARNING,
++                                         "FST Request from " MACSTR
++                                         " dropped due to inappropriate state %s",
++                                         MAC2STR(mgmt->da),
++                                         fst_session_state_name(s->state));
++                      return;
++              }
++
++
++              /*
++               * If FST Setup Request arrived with the same FSTS ID as one we
++               * initialized before, it means the other side either didn't
++               * receive our FST Request or skipped it for some reason (for
++               * example, due to numerical MAC comparison).
++               *
++               * In this case, there's no need to tear down the session.
++               * Moreover, as FSTS ID is the same, the other side will
++               * associate this tear down with the session it initiated that
++               * will break the sync.
++               */
++              if (le_to_host32(req->stie.fsts_id) != s->data.fsts_id)
++                      fst_session_send_tear_down(s);
++              else
++                      fst_printf_session(s, MSG_WARNING,
++                                         "Skipping TearDown as the FST request has the same FSTS ID as initiated");
++              fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &evext);
++              fst_session_stt_disarm(s);
++              fst_printf_session(s, MSG_WARNING, "reset due to FST request");
++      }
++
++      s = fst_session_create(g);
++      if (!s) {
++              fst_printf(MSG_WARNING,
++                         "FST Request dropped: cannot create session for %s and %s",
++                         fst_iface_get_name(iface),
++                         fst_iface_get_name(new_iface));
++              return;
++      }
++
++      fst_session_set_iface(s, iface, TRUE);
++      fst_session_set_peer_addr(s, mgmt->sa, TRUE);
++      fst_session_set_iface(s, new_iface, FALSE);
++      fst_session_set_peer_addr(s, new_iface_peer_addr, FALSE);
++      fst_session_set_llt(s, FST_LLT_VAL_TO_MS(le_to_host32(req->llt)));
++      s->data.pending_setup_req_dlgt = req->dialog_token;
++      s->data.fsts_id = le_to_host32(req->stie.fsts_id);
++
++      fst_session_stt_arm(s);
++
++      fst_session_notify_ctrl(s, EVENT_FST_SETUP, NULL);
++
++      fst_session_set_state(s, FST_SESSION_STATE_SETUP_COMPLETION, NULL);
++}
++
++
++static void fst_session_handle_setup_response(struct fst_session *s,
++                                            struct fst_iface *iface,
++                                            const struct ieee80211_mgmt *mgmt,
++                                            size_t frame_len)
++{
++      const struct fst_setup_res *res;
++      size_t plen = frame_len - IEEE80211_HDRLEN - 1;
++      enum hostapd_hw_mode hw_mode;
++      u8 channel;
++      union fst_session_state_switch_extra evext = {
++              .to_initial = {0},
++      };
++
++      if (iface != s->data.old_iface) {
++              fst_printf_session(s, MSG_WARNING,
++                                 "FST Response dropped: %s is not the old iface",
++                                 fst_iface_get_name(iface));
++              return;
++      }
++
++      if (!fst_session_is_ready_pending(s)) {
++              fst_printf_session(s, MSG_WARNING,
++                                 "FST Response dropped due to wrong state: %s",
++                                 fst_session_state_name(s->state));
++              return;
++      }
++
++      if (plen < sizeof(*res)) {
++              fst_printf_session(s, MSG_WARNING,
++                                 "Too short FST Response dropped");
++              return;
++      }
++      res = (const struct fst_setup_res *)
++              (((const u8 *) mgmt) + IEEE80211_HDRLEN + 1);
++      if (res->stie.element_id != WLAN_EID_SESSION_TRANSITION ||
++          res->stie.length < 11) {
++              fst_printf_iface(iface, MSG_WARNING,
++                               "FST Response dropped: invalid STIE");
++              return;
++      }
++
++      if (res->dialog_token != s->data.pending_setup_req_dlgt)  {
++              fst_printf_session(s, MSG_WARNING,
++                                 "FST Response dropped due to wrong dialog token (%u != %u)",
++                                 s->data.pending_setup_req_dlgt,
++                                 res->dialog_token);
++              return;
++      }
++
++      if (res->status_code == WLAN_STATUS_SUCCESS &&
++          le_to_host32(res->stie.fsts_id) != s->data.fsts_id) {
++              fst_printf_session(s, MSG_WARNING,
++                                 "FST Response dropped due to wrong FST Session ID (%u)",
++                                 le_to_host32(res->stie.fsts_id));
++              return;
++      }
++
++      fst_session_stt_disarm(s);
++
++      if (res->status_code != WLAN_STATUS_SUCCESS) {
++              /*
++               * 10.32.2.2  Transitioning between states
++               * The initiator shall set the STT to the value of the
++               * FSTSessionTimeOut field at ... and at each ACK frame sent in
++               * response to a received FST Setup Response with the Status
++               * Code field equal to PENDING_ADMITTING_FST_SESSION or
++               * PENDING_GAP_IN_BA_WINDOW.
++               */
++              evext.to_initial.reason = REASON_REJECT;
++              evext.to_initial.reject_code = res->status_code;
++              evext.to_initial.initiator = FST_INITIATOR_REMOTE;
++              fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &evext);
++              fst_printf_session(s, MSG_WARNING,
++                                 "FST Setup rejected by remote side with status %u",
++                                 res->status_code);
++              return;
++      }
++
++      fst_iface_get_channel_info(s->data.new_iface, &hw_mode, &channel);
++
++      if (fst_hw_mode_to_band(hw_mode) != res->stie.new_band_id) {
++              evext.to_initial.reason = REASON_ERROR_PARAMS;
++              fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &evext);
++              fst_printf_session(s, MSG_WARNING,
++                                 "invalid FST Setup parameters");
++              fst_session_tear_down_setup(s);
++              return;
++      }
++
++      fst_printf_session(s, MSG_INFO,
++                         "%s: FST Setup established for %s (llt=%u)",
++                         fst_iface_get_name(s->data.old_iface),
++                         fst_iface_get_name(s->data.new_iface),
++                         s->data.llt_ms);
++
++      fst_session_notify_ctrl(s, EVENT_FST_ESTABLISHED, NULL);
++
++      if (s->data.llt_ms == FST_LLT_SWITCH_IMMEDIATELY)
++              fst_session_initiate_switch(s);
++}
++
++
++static void fst_session_handle_tear_down(struct fst_session *s,
++                                       struct fst_iface *iface,
++                                       const struct ieee80211_mgmt *mgmt,
++                                       size_t frame_len)
++{
++      const struct fst_tear_down *td;
++      size_t plen = frame_len - IEEE80211_HDRLEN - 1;
++      union fst_session_state_switch_extra evext = {
++              .to_initial = {
++                      .reason = REASON_TEARDOWN,
++                      .initiator = FST_INITIATOR_REMOTE,
++              },
++      };
++
++      if (plen < sizeof(*td)) {
++              fst_printf_session(s, MSG_WARNING,
++                                 "Too short FST Tear Down dropped");
++              return;
++      }
++      td = (const struct fst_tear_down *)
++              (((const u8 *) mgmt) + IEEE80211_HDRLEN + 1);
++
++      if (le_to_host32(td->fsts_id) != s->data.fsts_id) {
++              fst_printf_siface(s, iface, MSG_WARNING,
++                                "tear down for wrong FST Setup ID (%u)",
++                                le_to_host32(td->fsts_id));
++              return;
++      }
++
++      fst_session_stt_disarm(s);
++
++      fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &evext);
++}
++
++
++static void fst_session_handle_ack_request(struct fst_session *s,
++                                         struct fst_iface *iface,
++                                         const struct ieee80211_mgmt *mgmt,
++                                         size_t frame_len)
++{
++      const struct fst_ack_req *req;
++      size_t plen = frame_len - IEEE80211_HDRLEN - 1;
++      struct fst_ack_res res;
++      union fst_session_state_switch_extra evext = {
++              .to_initial = {
++                      .reason = REASON_SWITCH,
++                      .initiator = FST_INITIATOR_REMOTE,
++              },
++      };
++
++      if (!fst_session_is_ready(s) && !fst_session_is_switch_requested(s)) {
++              fst_printf_siface(s, iface, MSG_ERROR,
++                                "cannot initiate switch due to wrong session state (%s)",
++                                fst_session_state_name(s->state));
++              return;
++      }
++
++      WPA_ASSERT(s->data.new_iface != NULL);
++
++      if (iface != s->data.new_iface) {
++              fst_printf_siface(s, iface, MSG_ERROR,
++                                "Ack received on wrong interface");
++              return;
++      }
++
++      if (plen < sizeof(*req)) {
++              fst_printf_session(s, MSG_WARNING,
++                                 "Too short FST Ack Request dropped");
++              return;
++      }
++      req = (const struct fst_ack_req *)
++              (((const u8 *) mgmt) + IEEE80211_HDRLEN + 1);
++
++      if (le_to_host32(req->fsts_id) != s->data.fsts_id) {
++              fst_printf_siface(s, iface, MSG_WARNING,
++                                "Ack for wrong FST Setup ID (%u)",
++                                le_to_host32(req->fsts_id));
++              return;
++      }
++
++      os_memset(&res, 0, sizeof(res));
++
++      res.action = FST_ACTION_ACK_RESPONSE;
++      res.dialog_token = req->dialog_token;
++      res.fsts_id = req->fsts_id;
++
++      if (!fst_session_send_action(s, FALSE, &res, sizeof(res), NULL)) {
++              fst_printf_sframe(s, FALSE, MSG_INFO, "FST Ack Response sent");
++              fst_session_stt_disarm(s);
++              fst_session_set_state(s, FST_SESSION_STATE_TRANSITION_DONE,
++                                    NULL);
++              fst_session_set_state(s, FST_SESSION_STATE_TRANSITION_CONFIRMED,
++                                    NULL);
++              fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &evext);
++      }
++}
++
++
++static void
++fst_session_handle_ack_response(struct fst_session *s,
++                              struct fst_iface *iface,
++                              const struct ieee80211_mgmt *mgmt,
++                              size_t frame_len)
++{
++      const struct fst_ack_res *res;
++      size_t plen = frame_len - IEEE80211_HDRLEN - 1;
++      union fst_session_state_switch_extra evext = {
++              .to_initial = {
++                      .reason = REASON_SWITCH,
++                      .initiator = FST_INITIATOR_LOCAL,
++              },
++      };
++
++      if (!fst_session_is_switch_requested(s)) {
++              fst_printf_siface(s, iface, MSG_ERROR,
++                                "Ack Response in inappropriate session state (%s)",
++                                fst_session_state_name(s->state));
++              return;
++      }
++
++      WPA_ASSERT(s->data.new_iface != NULL);
++
++      if (iface != s->data.new_iface) {
++              fst_printf_siface(s, iface, MSG_ERROR,
++                                "Ack response received on wrong interface");
++              return;
++      }
++
++      if (plen < sizeof(*res)) {
++              fst_printf_session(s, MSG_WARNING,
++                                 "Too short FST Ack Response dropped");
++              return;
++      }
++      res = (const struct fst_ack_res *)
++              (((const u8 *) mgmt) + IEEE80211_HDRLEN + 1);
++
++      if (le_to_host32(res->fsts_id) != s->data.fsts_id) {
++              fst_printf_siface(s, iface, MSG_ERROR,
++                                "Ack response for wrong FST Setup ID (%u)",
++                                le_to_host32(res->fsts_id));
++              return;
++      }
++
++      fst_session_set_state(s, FST_SESSION_STATE_TRANSITION_CONFIRMED, NULL);
++      fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &evext);
++
++      fst_session_stt_disarm(s);
++}
++
++
++struct fst_session * fst_session_create(struct fst_group *g)
++{
++      struct fst_session *s;
++      u32 id;
++
++      WPA_ASSERT(!is_zero_ether_addr(own_addr));
++
++      id = fst_find_free_session_id();
++      if (id == FST_INVALID_SESSION_ID) {
++              fst_printf(MSG_ERROR, "Cannot assign new session ID");
++              return NULL;
++      }
++
++      s = os_zalloc(sizeof(*s));
++      if (!s) {
++              fst_printf(MSG_ERROR, "Cannot allocate new session object");
++              return NULL;
++      }
++
++      s->id = id;
++      s->group = g;
++      s->state = FST_SESSION_STATE_INITIAL;
++
++      s->data.llt_ms = FST_LLT_MS_DEFAULT;
++
++      fst_printf(MSG_INFO, "Session %u created", s->id);
++
++      dl_list_add_tail(&global_sessions_list, &s->global_sessions_lentry);
++
++      foreach_fst_ctrl_call(on_session_added, s);
++
++      return s;
++}
++
++
++void fst_session_set_iface(struct fst_session *s, struct fst_iface *iface,
++                         Boolean is_old)
++{
++      if (is_old)
++              s->data.old_iface = iface;
++      else
++              s->data.new_iface = iface;
++
++}
++
++
++void fst_session_set_llt(struct fst_session *s, u32 llt)
++{
++      s->data.llt_ms = llt;
++}
++
++
++void fst_session_set_peer_addr(struct fst_session *s, const u8 *addr,
++                             Boolean is_old)
++{
++      u8 *a = is_old ? s->data.old_peer_addr : s->data.new_peer_addr;
++
++      os_memcpy(a, addr, ETH_ALEN);
++}
++
++
++int fst_session_initiate_setup(struct fst_session *s)
++{
++      struct fst_setup_req req;
++      int res;
++      u32 fsts_id;
++      u8 dialog_token;
++      struct fst_session *_s;
++
++      if (fst_session_is_in_progress(s)) {
++              fst_printf_session(s, MSG_ERROR, "Session in progress");
++              return -EINVAL;
++      }
++
++      if (is_zero_ether_addr(s->data.old_peer_addr)) {
++              fst_printf_session(s, MSG_ERROR, "No old peer MAC address");
++              return -EINVAL;
++      }
++
++      if (is_zero_ether_addr(s->data.new_peer_addr)) {
++              fst_printf_session(s, MSG_ERROR, "No new peer MAC address");
++              return -EINVAL;
++      }
++
++      if (!s->data.old_iface) {
++              fst_printf_session(s, MSG_ERROR, "No old interface defined");
++              return -EINVAL;
++      }
++
++      if (!s->data.new_iface) {
++              fst_printf_session(s, MSG_ERROR, "No new interface defined");
++              return -EINVAL;
++      }
++
++      if (s->data.new_iface == s->data.old_iface) {
++              fst_printf_session(s, MSG_ERROR,
++                                 "Same interface set as old and new");
++              return -EINVAL;
++      }
++
++      if (!fst_iface_is_connected(s->data.old_iface, s->data.old_peer_addr)) {
++              fst_printf_session(s, MSG_ERROR,
++                                 "The preset old peer address is not connected");
++              return -EINVAL;
++      }
++
++      if (!fst_iface_is_connected(s->data.new_iface, s->data.new_peer_addr)) {
++              fst_printf_session(s, MSG_ERROR,
++                                 "The preset new peer address is not connected");
++              return -EINVAL;
++      }
++
++      _s = fst_find_session_in_progress(s->data.old_peer_addr, s->group);
++      if (_s) {
++              fst_printf_session(s, MSG_ERROR,
++                                 "There is another session in progress (old): %u",
++                                 _s->id);
++              return -EINVAL;
++      }
++
++      _s = fst_find_session_in_progress(s->data.new_peer_addr, s->group);
++      if (_s) {
++              fst_printf_session(s, MSG_ERROR,
++                                 "There is another session in progress (new): %u",
++                                 _s->id);
++              return -EINVAL;
++      }
++
++      dialog_token = fst_group_assign_dialog_token(s->group);
++      fsts_id = fst_group_assign_fsts_id(s->group);
++
++      os_memset(&req, 0, sizeof(req));
++
++      fst_printf_siface(s, s->data.old_iface, MSG_INFO,
++              "initiating FST setup for %s (llt=%u ms)",
++              fst_iface_get_name(s->data.new_iface), s->data.llt_ms);
++
++      req.action = FST_ACTION_SETUP_REQUEST;
++      req.dialog_token = dialog_token;
++      req.llt = host_to_le32(FST_LLT_MS_TO_VAL(s->data.llt_ms));
++      /* 8.4.2.147 Session Transition element */
++      req.stie.element_id = WLAN_EID_SESSION_TRANSITION;
++      req.stie.length = sizeof(req.stie) - 2;
++      req.stie.fsts_id = host_to_le32(fsts_id);
++      req.stie.session_control = SESSION_CONTROL(SESSION_TYPE_BSS, 0);
++
++      req.stie.new_band_id = fst_iface_get_band_id(s->data.new_iface);
++      req.stie.new_band_op = 1;
++      req.stie.new_band_setup = 0;
++
++      req.stie.old_band_id = fst_iface_get_band_id(s->data.old_iface);
++      req.stie.old_band_op = 1;
++      req.stie.old_band_setup = 0;
++
++      res = fst_session_send_action(s, TRUE, &req, sizeof(req),
++                                    fst_iface_get_mbie(s->data.old_iface));
++      if (!res) {
++              s->data.fsts_id = fsts_id;
++              s->data.pending_setup_req_dlgt = dialog_token;
++              fst_printf_sframe(s, TRUE, MSG_INFO, "FST Setup Request sent");
++              fst_session_set_state(s, FST_SESSION_STATE_SETUP_COMPLETION,
++                                    NULL);
++
++              fst_session_stt_arm(s);
++      }
++
++      return res;
++}
++
++
++int fst_session_respond(struct fst_session *s, u8 status_code)
++{
++      struct fst_setup_res res;
++      enum hostapd_hw_mode hw_mode;
++      u8 channel;
++
++      if (!fst_session_is_ready_pending(s)) {
++              fst_printf_session(s, MSG_ERROR, "incorrect state: %s",
++                                 fst_session_state_name(s->state));
++              return -EINVAL;
++      }
++
++      if (is_zero_ether_addr(s->data.old_peer_addr)) {
++              fst_printf_session(s, MSG_ERROR, "No peer MAC address");
++              return -EINVAL;
++      }
++
++      if (!s->data.old_iface) {
++              fst_printf_session(s, MSG_ERROR, "No old interface defined");
++              return -EINVAL;
++      }
++
++      if (!s->data.new_iface) {
++              fst_printf_session(s, MSG_ERROR, "No new interface defined");
++              return -EINVAL;
++      }
++
++      if (s->data.new_iface == s->data.old_iface) {
++              fst_printf_session(s, MSG_ERROR,
++                                 "Same interface set as old and new");
++              return -EINVAL;
++      }
++
++      if (!fst_iface_is_connected(s->data.old_iface, s->data.old_peer_addr)) {
++              fst_printf_session(s, MSG_ERROR,
++                                 "The preset peer address is not in the peer list");
++              return -EINVAL;
++      }
++
++      fst_session_stt_disarm(s);
++
++      os_memset(&res, 0, sizeof(res));
++
++      res.action = FST_ACTION_SETUP_RESPONSE;
++      res.dialog_token = s->data.pending_setup_req_dlgt;
++      res.status_code = status_code;
++
++      res.stie.element_id = WLAN_EID_SESSION_TRANSITION;
++      res.stie.length = sizeof(res.stie) - 2;
++
++      if (status_code == WLAN_STATUS_SUCCESS) {
++              res.stie.fsts_id = s->data.fsts_id;
++              res.stie.session_control = SESSION_CONTROL(SESSION_TYPE_BSS, 0);
++
++              fst_iface_get_channel_info(s->data.new_iface, &hw_mode,
++                                         &channel);
++              res.stie.new_band_id = fst_hw_mode_to_band(hw_mode);
++              res.stie.new_band_op = 1;
++              res.stie.new_band_setup = 0;
++
++              fst_iface_get_channel_info(s->data.old_iface, &hw_mode,
++                                         &channel);
++              res.stie.old_band_id = fst_hw_mode_to_band(hw_mode);
++              res.stie.old_band_op = 1;
++              res.stie.old_band_setup = 0;
++
++              fst_printf_session(s, MSG_INFO,
++                                 "%s: FST Setup Request accepted for %s (llt=%u)",
++                                 fst_iface_get_name(s->data.old_iface),
++                                 fst_iface_get_name(s->data.new_iface),
++                                 s->data.llt_ms);
++      } else {
++              fst_printf_session(s, MSG_WARNING,
++                                 "%s: FST Setup Request rejected with code %d",
++                                 fst_iface_get_name(s->data.old_iface),
++                                 status_code);
++      }
++
++      if (fst_session_send_action(s, TRUE, &res, sizeof(res),
++                                  fst_iface_get_mbie(s->data.old_iface))) {
++              fst_printf_sframe(s, TRUE, MSG_ERROR,
++                                "cannot send FST Setup Response with code %d",
++                                status_code);
++              return -EINVAL;
++      }
++
++      fst_printf_sframe(s, TRUE, MSG_INFO, "FST Setup Response sent");
++
++      if (status_code != WLAN_STATUS_SUCCESS) {
++              union fst_session_state_switch_extra evext = {
++                      .to_initial = {
++                              .reason = REASON_REJECT,
++                              .reject_code = status_code,
++                              .initiator = FST_INITIATOR_LOCAL,
++                      },
++              };
++              fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &evext);
++      }
++
++      return 0;
++}
++
++
++int fst_session_initiate_switch(struct fst_session *s)
++{
++      struct fst_ack_req req;
++      int res;
++      u8 dialog_token;
++
++      if (!fst_session_is_ready(s)) {
++              fst_printf_session(s, MSG_ERROR,
++                                 "cannot initiate switch due to wrong setup state (%d)",
++                                 s->state);
++              return -1;
++      }
++
++      dialog_token = fst_group_assign_dialog_token(s->group);
++
++      WPA_ASSERT(s->data.new_iface != NULL);
++      WPA_ASSERT(s->data.old_iface != NULL);
++
++      fst_printf_session(s, MSG_INFO, "initiating FST switch: %s => %s",
++                         fst_iface_get_name(s->data.old_iface),
++                         fst_iface_get_name(s->data.new_iface));
++
++      os_memset(&req, 0, sizeof(req));
++
++      req.action = FST_ACTION_ACK_REQUEST;
++      req.dialog_token = dialog_token;
++      req.fsts_id = host_to_le32(s->data.fsts_id);
++
++      res = fst_session_send_action(s, FALSE, &req, sizeof(req), NULL);
++      if (!res) {
++              fst_printf_sframe(s, FALSE, MSG_INFO, "FST Ack Request sent");
++              fst_session_set_state(s, FST_SESSION_STATE_TRANSITION_DONE,
++                                    NULL);
++              fst_session_stt_arm(s);
++      } else {
++              fst_printf_sframe(s, FALSE, MSG_ERROR,
++                                "Cannot send FST Ack Request");
++      }
++
++      return res;
++}
++
++
++void fst_session_handle_action(struct fst_session *s,
++                             struct fst_iface *iface,
++                             const struct ieee80211_mgmt *mgmt,
++                             size_t frame_len)
++{
++      switch (mgmt->u.action.u.fst_action.action) {
++      case FST_ACTION_SETUP_REQUEST:
++              WPA_ASSERT(0);
++              break;
++      case FST_ACTION_SETUP_RESPONSE:
++              fst_session_handle_setup_response(s, iface, mgmt, frame_len);
++              break;
++      case FST_ACTION_TEAR_DOWN:
++              fst_session_handle_tear_down(s, iface, mgmt, frame_len);
++              break;
++      case FST_ACTION_ACK_REQUEST:
++              fst_session_handle_ack_request(s, iface, mgmt, frame_len);
++              break;
++      case FST_ACTION_ACK_RESPONSE:
++              fst_session_handle_ack_response(s, iface, mgmt, frame_len);
++              break;
++      case FST_ACTION_ON_CHANNEL_TUNNEL:
++      default:
++              fst_printf_sframe(s, FALSE, MSG_ERROR,
++                                "Unsupported FST Action frame");
++              break;
++      }
++}
++
++
++int fst_session_tear_down_setup(struct fst_session *s)
++{
++      int res;
++      union fst_session_state_switch_extra evext = {
++              .to_initial = {
++                      .reason = REASON_TEARDOWN,
++                      .initiator = FST_INITIATOR_LOCAL,
++              },
++      };
++
++      res = fst_session_send_tear_down(s);
++
++      fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &evext);
++
++      return res;
++}
++
++
++void fst_session_reset(struct fst_session *s)
++{
++      fst_session_reset_ex(s, REASON_RESET);
++}
++
++
++void fst_session_delete(struct fst_session *s)
++{
++      fst_printf(MSG_INFO, "Session %u deleted", s->id);
++      dl_list_del(&s->global_sessions_lentry);
++      foreach_fst_ctrl_call(on_session_removed, s);
++      os_free(s);
++}
++
++
++struct fst_group * fst_session_get_group(struct fst_session *s)
++{
++      return s->group;
++}
++
++
++struct fst_iface * fst_session_get_iface(struct fst_session *s, Boolean is_old)
++{
++      return is_old ? s->data.old_iface : s->data.new_iface;
++}
++
++
++u32 fst_session_get_id(struct fst_session *s)
++{
++      return s->id;
++}
++
++
++const u8 * fst_session_get_peer_addr(struct fst_session *s, Boolean is_old)
++{
++      return is_old ? s->data.old_peer_addr : s->data.new_peer_addr;
++}
++
++
++u32 fst_session_get_llt(struct fst_session *s)
++{
++      return s->data.llt_ms;
++}
++
++
++enum fst_session_state fst_session_get_state(struct fst_session *s)
++{
++      return s->state;
++}
++
++
++struct fst_session * fst_session_get_by_id(u32 id)
++{
++      struct fst_session *s;
++
++      foreach_fst_session(s) {
++              if (id == s->id)
++                      return s;
++      }
++
++      return NULL;
++}
++
++
++void fst_session_enum(struct fst_group *g, fst_session_enum_clb clb, void *ctx)
++{
++      struct fst_session *s;
++
++      foreach_fst_session(s) {
++              if (!g || s->group == g)
++                      clb(s->group, s, ctx);
++      }
++}
++
++
++void fst_session_on_action_rx(struct fst_iface *iface,
++                            const struct ieee80211_mgmt *mgmt,
++                            size_t len)
++{
++      struct fst_session *s;
++
++      if (len < IEEE80211_HDRLEN + 2 ||
++          mgmt->u.action.category != WLAN_ACTION_FST) {
++              fst_printf_iface(iface, MSG_ERROR,
++                               "invalid Action frame received");
++              return;
++      }
++
++      if (mgmt->u.action.u.fst_action.action <= FST_ACTION_MAX_SUPPORTED) {
++              fst_printf_iface(iface, MSG_DEBUG,
++                               "FST Action '%s' received!",
++                               fst_action_names[mgmt->u.action.u.fst_action.action]);
++      } else {
++              fst_printf_iface(iface, MSG_WARNING,
++                               "unknown FST Action (%u) received!",
++                               mgmt->u.action.u.fst_action.action);
++              return;
++      }
++
++      if (mgmt->u.action.u.fst_action.action == FST_ACTION_SETUP_REQUEST) {
++              fst_session_handle_setup_request(iface, mgmt, len);
++              return;
++      }
++
++      s = fst_find_session_in_progress(mgmt->sa, fst_iface_get_group(iface));
++      if (s) {
++              fst_session_handle_action(s, iface, mgmt, len);
++      } else {
++              fst_printf_iface(iface, MSG_WARNING,
++                               "FST Action '%s' dropped: no session in progress found",
++                               fst_action_names[mgmt->u.action.u.fst_action.action]);
++      }
++}
++
++
++int fst_session_set_str_ifname(struct fst_session *s, const char *ifname,
++                             Boolean is_old)
++{
++      struct fst_group *g = fst_session_get_group(s);
++      struct fst_iface *i;
++
++      i = fst_group_get_iface_by_name(g, ifname);
++      if (!i) {
++              fst_printf_session(s, MSG_WARNING,
++                                 "Cannot set iface %s: no such iface within group '%s'",
++                                 ifname, fst_group_get_id(g));
++              return -1;
++      }
++
++      fst_session_set_iface(s, i, is_old);
++
++      return 0;
++}
++
++
++int fst_session_set_str_peer_addr(struct fst_session *s, const char *mac,
++                                Boolean is_old)
++{
++      u8 peer_addr[ETH_ALEN];
++      int res = fst_read_peer_addr(mac, peer_addr);
++
++      if (res)
++              return res;
++
++      fst_session_set_peer_addr(s, peer_addr, is_old);
++
++      return 0;
++}
++
++
++int fst_session_set_str_llt(struct fst_session *s, const char *llt_str)
++{
++      char *endp;
++      long int llt = strtol(llt_str, &endp, 0);
++
++      if (*endp || llt < 0 || (unsigned long int) llt > FST_MAX_LLT_MS) {
++              fst_printf_session(s, MSG_WARNING,
++                                 "Cannot set llt %s: Invalid llt value (1..%u expected)",
++                                 llt_str, FST_MAX_LLT_MS);
++              return -1;
++      }
++      fst_session_set_llt(s, (u32) llt);
++
++      return 0;
++}
++
++
++void fst_session_global_on_iface_detached(struct fst_iface *iface)
++{
++      struct fst_session *s;
++
++      foreach_fst_session(s) {
++              if (fst_session_is_in_progress(s) &&
++                  (s->data.new_iface == iface ||
++                   s->data.old_iface == iface))
++                      fst_session_reset_ex(s, REASON_DETACH_IFACE);
++      }
++}
++
++
++struct fst_session * fst_session_global_get_first_by_group(struct fst_group *g)
++{
++      struct fst_session *s;
++
++      foreach_fst_session(s) {
++              if (s->group == g)
++                      return s;
++      }
++
++      return NULL;
++}
++
++
++#ifdef CONFIG_FST_TEST
++
++static int get_group_fill_session(struct fst_group **g, struct fst_session *s)
++{
++      const u8 *old_addr, *new_addr;
++      struct fst_get_peer_ctx *ctx;
++
++      os_memset(s, 0, sizeof(*s));
++      foreach_fst_group(*g) {
++              s->data.new_iface = fst_group_first_iface(*g);
++              if (s->data.new_iface)
++                      break;
++      }
++      if (!s->data.new_iface)
++              return -EINVAL;
++
++      s->data.old_iface = dl_list_entry(s->data.new_iface->group_lentry.next,
++                                        struct fst_iface, group_lentry);
++      if (!s->data.old_iface)
++              return -EINVAL;
++
++      old_addr = fst_iface_get_peer_first(s->data.old_iface, &ctx, TRUE);
++      if (!old_addr)
++              return -EINVAL;
++
++      new_addr = fst_iface_get_peer_first(s->data.new_iface, &ctx, TRUE);
++      if (!new_addr)
++              return -EINVAL;
++
++      os_memcpy(s->data.old_peer_addr, old_addr, ETH_ALEN);
++      os_memcpy(s->data.new_peer_addr, new_addr, ETH_ALEN);
++
++      return 0;
++}
++
++
++#define FST_MAX_COMMAND_WORD_NAME_LENGTH 16
++
++int fst_test_req_send_fst_request(const char *params)
++{
++      int fsts_id;
++      Boolean is_valid;
++      char *endp;
++      struct fst_setup_req req;
++      struct fst_session s;
++      struct fst_group *g;
++      enum hostapd_hw_mode hw_mode;
++      u8 channel;
++      char additional_param[FST_MAX_COMMAND_WORD_NAME_LENGTH];
++
++      if (params[0] != ' ')
++              return -EINVAL;
++      params++;
++      fsts_id = fst_read_next_int_param(params, &is_valid, &endp);
++      if (!is_valid)
++              return -EINVAL;
++
++      if (get_group_fill_session(&g, &s))
++              return -EINVAL;
++
++      req.action = FST_ACTION_SETUP_REQUEST;
++      req.dialog_token = g->dialog_token;
++      req.llt = host_to_le32(FST_LLT_MS_DEFAULT);
++      /* 8.4.2.147 Session Transition element */
++      req.stie.element_id = WLAN_EID_SESSION_TRANSITION;
++      req.stie.length = sizeof(req.stie) - 2;
++      req.stie.fsts_id = host_to_le32(fsts_id);
++      req.stie.session_control = SESSION_CONTROL(SESSION_TYPE_BSS, 0);
++
++      fst_iface_get_channel_info(s.data.new_iface, &hw_mode, &channel);
++      req.stie.new_band_id = fst_hw_mode_to_band(hw_mode);
++      req.stie.new_band_op = 1;
++      req.stie.new_band_setup = 0;
++
++      fst_iface_get_channel_info(s.data.old_iface, &hw_mode, &channel);
++      req.stie.old_band_id = fst_hw_mode_to_band(hw_mode);
++      req.stie.old_band_op = 1;
++      req.stie.old_band_setup = 0;
++
++      if (!fst_read_next_text_param(endp, additional_param,
++                                     sizeof(additional_param), &endp)) {
++              if (!os_strcasecmp(additional_param, FST_CTR_PVAL_BAD_NEW_BAND))
++                      req.stie.new_band_id = req.stie.old_band_id;
++      }
++
++      return fst_session_send_action(&s, TRUE, &req, sizeof(req),
++                                     s.data.old_iface->mb_ie);
++}
++
++
++int fst_test_req_send_fst_response(const char *params)
++{
++      int fsts_id;
++      Boolean is_valid;
++      char *endp;
++      struct fst_setup_res res;
++      struct fst_session s;
++      struct fst_group *g;
++      enum hostapd_hw_mode hw_mode;
++      u8 status_code;
++      u8 channel;
++      char response[FST_MAX_COMMAND_WORD_NAME_LENGTH];
++      struct fst_session *_s;
++
++      if (params[0] != ' ')
++              return -EINVAL;
++      params++;
++      fsts_id = fst_read_next_int_param(params, &is_valid, &endp);
++      if (!is_valid)
++              return -EINVAL;
++
++      if (get_group_fill_session(&g, &s))
++              return -EINVAL;
++
++      status_code = WLAN_STATUS_SUCCESS;
++      if (!fst_read_next_text_param(endp, response, sizeof(response),
++                                    &endp)) {
++              if (!os_strcasecmp(response, FST_CS_PVAL_RESPONSE_REJECT))
++                      status_code = WLAN_STATUS_PENDING_ADMITTING_FST_SESSION;
++      }
++
++      os_memset(&res, 0, sizeof(res));
++
++      res.action = FST_ACTION_SETUP_RESPONSE;
++      /*
++       * If some session has just received an FST Setup Request, then
++       * use the correct dialog token copied from this request.
++       */
++      _s = fst_find_session_in_progress(fst_session_get_peer_addr(&s, TRUE),
++                                        g);
++      res.dialog_token = (_s && fst_session_is_ready_pending(_s)) ?
++              _s->data.pending_setup_req_dlgt : g->dialog_token;
++      res.status_code  = status_code;
++
++      res.stie.element_id = WLAN_EID_SESSION_TRANSITION;
++      res.stie.length = sizeof(res.stie) - 2;
++
++      if (res.status_code == WLAN_STATUS_SUCCESS) {
++              res.stie.fsts_id = fsts_id;
++              res.stie.session_control = SESSION_CONTROL(SESSION_TYPE_BSS, 0);
++
++              fst_iface_get_channel_info(s.data.new_iface, &hw_mode,
++                                          &channel);
++              res.stie.new_band_id = fst_hw_mode_to_band(hw_mode);
++              res.stie.new_band_op = 1;
++              res.stie.new_band_setup = 0;
++
++              fst_iface_get_channel_info(s.data.old_iface, &hw_mode,
++                                         &channel);
++              res.stie.old_band_id = fst_hw_mode_to_band(hw_mode);
++              res.stie.old_band_op = 1;
++              res.stie.old_band_setup = 0;
++      }
++
++      if (!fst_read_next_text_param(endp, response, sizeof(response),
++                                    &endp)) {
++              if (!os_strcasecmp(response, FST_CTR_PVAL_BAD_NEW_BAND))
++                      res.stie.new_band_id = res.stie.old_band_id;
++      }
++
++      return fst_session_send_action(&s, TRUE, &res, sizeof(res),
++                                     s.data.old_iface->mb_ie);
++}
++
++
++int fst_test_req_send_ack_request(const char *params)
++{
++      int fsts_id;
++      Boolean is_valid;
++      char *endp;
++      struct fst_ack_req req;
++      struct fst_session s;
++      struct fst_group *g;
++
++      if (params[0] != ' ')
++              return -EINVAL;
++      params++;
++      fsts_id = fst_read_next_int_param(params, &is_valid, &endp);
++      if (!is_valid)
++              return -EINVAL;
++
++      if (get_group_fill_session(&g, &s))
++              return -EINVAL;
++
++      os_memset(&req, 0, sizeof(req));
++      req.action = FST_ACTION_ACK_REQUEST;
++      req.dialog_token = g->dialog_token;
++      req.fsts_id = fsts_id;
++
++      return fst_session_send_action(&s, FALSE, &req, sizeof(req), NULL);
++}
++
++
++int fst_test_req_send_ack_response(const char *params)
++{
++      int fsts_id;
++      Boolean is_valid;
++      char *endp;
++      struct fst_ack_res res;
++      struct fst_session s;
++      struct fst_group *g;
++
++      if (params[0] != ' ')
++              return -EINVAL;
++      params++;
++      fsts_id = fst_read_next_int_param(params, &is_valid, &endp);
++      if (!is_valid)
++              return -EINVAL;
++
++      if (get_group_fill_session(&g, &s))
++              return -EINVAL;
++
++      os_memset(&res, 0, sizeof(res));
++      res.action = FST_ACTION_ACK_RESPONSE;
++      res.dialog_token = g->dialog_token;
++      res.fsts_id = fsts_id;
++
++      return fst_session_send_action(&s, FALSE, &res, sizeof(res), NULL);
++}
++
++
++int fst_test_req_send_tear_down(const char *params)
++{
++      int fsts_id;
++      Boolean is_valid;
++      char *endp;
++      struct fst_tear_down td;
++      struct fst_session s;
++      struct fst_group *g;
++
++      if (params[0] != ' ')
++              return -EINVAL;
++      params++;
++      fsts_id = fst_read_next_int_param(params, &is_valid, &endp);
++      if (!is_valid)
++              return -EINVAL;
++
++      if (get_group_fill_session(&g, &s))
++              return -EINVAL;
++
++      os_memset(&td, 0, sizeof(td));
++      td.action = FST_ACTION_TEAR_DOWN;
++      td.fsts_id = fsts_id;
++
++      return fst_session_send_action(&s, TRUE, &td, sizeof(td), NULL);
++}
++
++
++u32 fst_test_req_get_fsts_id(const char *params)
++{
++      int sid;
++      Boolean is_valid;
++      char *endp;
++      struct fst_session *s;
++
++      if (params[0] != ' ')
++              return FST_FSTS_ID_NOT_FOUND;
++      params++;
++      sid = fst_read_next_int_param(params, &is_valid, &endp);
++      if (!is_valid)
++              return FST_FSTS_ID_NOT_FOUND;
++
++      s = fst_session_get_by_id(sid);
++      if (!s)
++              return FST_FSTS_ID_NOT_FOUND;
++
++      return s->data.fsts_id;
++}
++
++
++int fst_test_req_get_local_mbies(const char *request, char *buf, size_t buflen)
++{
++      char *endp;
++      char ifname[FST_MAX_COMMAND_WORD_NAME_LENGTH];
++      struct fst_group *g;
++      struct fst_iface *iface;
++
++      if (request[0] != ' ')
++              return -EINVAL;
++      request++;
++      if (fst_read_next_text_param(request, ifname, sizeof(ifname), &endp) ||
++          !*ifname)
++              goto problem;
++      g = dl_list_first(&fst_global_groups_list, struct fst_group,
++                        global_groups_lentry);
++      if (!g)
++              goto problem;
++      iface = fst_group_get_iface_by_name(g, ifname);
++      if (!iface || !iface->mb_ie)
++              goto problem;
++      return wpa_snprintf_hex(buf, buflen, wpabuf_head(iface->mb_ie),
++                              wpabuf_len(iface->mb_ie));
++
++problem:
++      return os_snprintf(buf, buflen, "FAIL\n");
++}
++
++#endif /* CONFIG_FST_TEST */
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1162de4b367a189f28093fc6c09270394db9c713
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,80 @@@
++/*
++ * FST module - FST Session related 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 FST_SESSION_H
++#define FST_SESSION_H
++
++#define FST_DEFAULT_SESSION_TIMEOUT_TU 255 /* u8 */
++
++struct fst_iface;
++struct fst_group;
++struct fst_session;
++enum fst_session_state;
++
++int  fst_session_global_init(void);
++void fst_session_global_deinit(void);
++void fst_session_global_on_iface_detached(struct fst_iface *iface);
++struct fst_session *
++fst_session_global_get_first_by_group(struct fst_group *g);
++
++struct fst_session * fst_session_create(struct fst_group *g);
++void fst_session_set_iface(struct fst_session *s, struct fst_iface *iface,
++                         Boolean is_old);
++void fst_session_set_llt(struct fst_session *s, u32 llt);
++void fst_session_set_peer_addr(struct fst_session *s, const u8 *addr,
++                             Boolean is_old);
++int fst_session_initiate_setup(struct fst_session *s);
++int fst_session_respond(struct fst_session *s, u8 status_code);
++int fst_session_initiate_switch(struct fst_session *s);
++void fst_session_handle_action(struct fst_session *s, struct fst_iface *iface,
++                             const struct ieee80211_mgmt *mgmt,
++                             size_t frame_len);
++int fst_session_tear_down_setup(struct fst_session *s);
++void fst_session_reset(struct fst_session *s);
++void fst_session_delete(struct fst_session *s);
++
++struct fst_group * fst_session_get_group(struct fst_session *s);
++struct fst_iface * fst_session_get_iface(struct fst_session *s, Boolean is_old);
++const u8 * fst_session_get_peer_addr(struct fst_session *s, Boolean is_old);
++u32 fst_session_get_id(struct fst_session *s);
++u32 fst_session_get_llt(struct fst_session *s);
++enum fst_session_state fst_session_get_state(struct fst_session *s);
++
++struct fst_session *fst_session_get_by_id(u32 id);
++
++typedef void (*fst_session_enum_clb)(struct fst_group *g, struct fst_session *s,
++                                   void *ctx);
++
++void fst_session_enum(struct fst_group *g, fst_session_enum_clb clb, void *ctx);
++
++void fst_session_on_action_rx(struct fst_iface *iface,
++                            const struct ieee80211_mgmt *mgmt, size_t len);
++
++
++int fst_session_set_str_ifname(struct fst_session *s, const char *ifname,
++                             Boolean is_old);
++int fst_session_set_str_peer_addr(struct fst_session *s, const char *mac,
++                                Boolean is_old);
++int fst_session_set_str_llt(struct fst_session *s, const char *llt_str);
++
++#ifdef CONFIG_FST_TEST
++
++#define FST_FSTS_ID_NOT_FOUND ((u32) -1)
++
++int fst_test_req_send_fst_request(const char *params);
++int fst_test_req_send_fst_response(const char *params);
++int fst_test_req_send_ack_request(const char *params);
++int fst_test_req_send_ack_response(const char *params);
++int fst_test_req_send_tear_down(const char *params);
++u32 fst_test_req_get_fsts_id(const char *params);
++int fst_test_req_get_local_mbies(const char *request, char *buf,
++                               size_t buflen);
++
++#endif /* CONFIG_FST_TEST */
++
++#endif /* FST_SESSION_H */
index 6adb3dc2049fb2c1d33befbfa302e5631a887525,0000000000000000000000000000000000000000..767706c01d6b318de1977fa0cf98f302997565cd
mode 100644,000000..100644
--- /dev/null
@@@ -1,5336 -1,0 +1,5402 @@@
- #define P2P_PEER_EXPIRATION_INTERVAL (P2P_PEER_EXPIRATION_AGE / 2)
 +/*
 + * Wi-Fi Direct - P2P module
 + * 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 "common.h"
 +#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"
 +
 +
 +static void p2p_state_timeout(void *eloop_ctx, void *timeout_ctx);
 +static void p2p_device_free(struct p2p_data *p2p, struct p2p_device *dev);
 +static void p2p_process_presence_req(struct p2p_data *p2p, const u8 *da,
 +                                   const u8 *sa, const u8 *data, size_t len,
 +                                   int rx_freq);
 +static void p2p_process_presence_resp(struct p2p_data *p2p, const u8 *da,
 +                                    const u8 *sa, const u8 *data,
 +                                    size_t len);
 +static void p2p_ext_listen_timeout(void *eloop_ctx, void *timeout_ctx);
 +static void p2p_scan_timeout(void *eloop_ctx, void *timeout_ctx);
 +
 +
 +/*
 + * p2p_scan recovery timeout
 + *
 + * Many drivers are using 30 second timeout on scan results. Allow a bit larger
 + * timeout for this to avoid hitting P2P timeout unnecessarily.
 + */
 +#define P2P_SCAN_TIMEOUT 35
 +
 +/**
 + * P2P_PEER_EXPIRATION_AGE - Number of seconds after which inactive peer
 + * entries will be removed
 + */
 +#ifndef P2P_PEER_EXPIRATION_AGE
 +#define P2P_PEER_EXPIRATION_AGE 60
 +#endif /* P2P_PEER_EXPIRATION_AGE */
 +
- static void p2p_expire_peers(struct p2p_data *p2p)
 +
- static void p2p_expiration_timeout(void *eloop_ctx, void *timeout_ctx)
- {
-       struct p2p_data *p2p = eloop_ctx;
-       p2p_expire_peers(p2p);
-       eloop_register_timeout(P2P_PEER_EXPIRATION_INTERVAL, 0,
-                              p2p_expiration_timeout, p2p, NULL);
- }
++void p2p_expire_peers(struct p2p_data *p2p)
 +{
 +      struct p2p_device *dev, *n;
 +      struct os_reltime now;
 +      size_t i;
 +
 +      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)) {
 +                      /*
 +                       * 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_reltime(&dev->last_seen);
 +                      continue;
 +              }
 +
 +              for (i = 0; i < p2p->num_groups; i++) {
 +                      if (p2p_group_is_client_connected(
 +                                  p2p->groups[i], dev->info.p2p_device_addr))
 +                              break;
 +              }
 +              if (i < p2p->num_groups) {
 +                      /*
 +                       * The peer is connected as a client in a group where
 +                       * we are the GO, so do not expire the peer entry.
 +                       */
 +                      os_get_reltime(&dev->last_seen);
 +                      continue;
 +              }
 +
 +              p2p_dbg(p2p, "Expiring old peer entry " MACSTR,
 +                      MAC2STR(dev->info.p2p_device_addr));
 +              dl_list_del(&dev->list);
 +              p2p_device_free(p2p, dev);
 +      }
 +}
 +
 +
-       ies = p2p_build_probe_resp_ies(p2p);
 +static const char * p2p_state_txt(int state)
 +{
 +      switch (state) {
 +      case P2P_IDLE:
 +              return "IDLE";
 +      case P2P_SEARCH:
 +              return "SEARCH";
 +      case P2P_CONNECT:
 +              return "CONNECT";
 +      case P2P_CONNECT_LISTEN:
 +              return "CONNECT_LISTEN";
 +      case P2P_GO_NEG:
 +              return "GO_NEG";
 +      case P2P_LISTEN_ONLY:
 +              return "LISTEN_ONLY";
 +      case P2P_WAIT_PEER_CONNECT:
 +              return "WAIT_PEER_CONNECT";
 +      case P2P_WAIT_PEER_IDLE:
 +              return "WAIT_PEER_IDLE";
 +      case P2P_SD_DURING_FIND:
 +              return "SD_DURING_FIND";
 +      case P2P_PROVISIONING:
 +              return "PROVISIONING";
 +      case P2P_PD_DURING_FIND:
 +              return "PD_DURING_FIND";
 +      case P2P_INVITE:
 +              return "INVITE";
 +      case P2P_INVITE_LISTEN:
 +              return "INVITE_LISTEN";
 +      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;
 +
 +      if (!addr || !p2p)
 +              return 0;
 +
 +      dev = p2p_get_device(p2p, addr);
 +      if (dev)
 +              return dev->wps_prov_info;
 +      else
 +              return 0;
 +}
 +
 +
 +void p2p_clear_provisioning_info(struct p2p_data *p2p, const u8 *addr)
 +{
 +      struct p2p_device *dev = NULL;
 +
 +      if (!addr || !p2p)
 +              return;
 +
 +      dev = p2p_get_device(p2p, addr);
 +      if (dev)
 +              dev->wps_prov_info = 0;
 +}
 +
 +
 +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;
 +      }
 +}
 +
 +
 +void p2p_set_timeout(struct p2p_data *p2p, unsigned int sec, unsigned int usec)
 +{
 +      p2p_dbg(p2p, "Set timeout (state=%s): %u.%06u sec",
 +              p2p_state_txt(p2p->state), sec, usec);
 +      eloop_cancel_timeout(p2p_state_timeout, p2p, NULL);
 +      eloop_register_timeout(sec, usec, p2p_state_timeout, p2p, NULL);
 +}
 +
 +
 +void p2p_clear_timeout(struct p2p_data *p2p)
 +{
 +      p2p_dbg(p2p, "Clear timeout (state=%s)", p2p_state_txt(p2p->state));
 +      eloop_cancel_timeout(p2p_state_timeout, p2p, NULL);
 +}
 +
 +
 +void p2p_go_neg_failed(struct p2p_data *p2p, int status)
 +{
 +      struct p2p_go_neg_results res;
 +      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;
 +      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);
 +}
 +
 +
 +static void p2p_listen_in_find(struct p2p_data *p2p, int dev_disc)
 +{
 +      unsigned int r, tu;
 +      int freq;
 +      struct wpabuf *ies;
 +
 +      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;
 +      }
 +
 +      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)
 +              tu = p2p->max_disc_tu;
 +      if (!dev_disc && tu < 100)
 +              tu = 100; /* Need to wait in non-device discovery use cases */
 +      if (p2p->cfg->max_listen && 1024 * tu / 1000 > p2p->cfg->max_listen)
 +              tu = p2p->cfg->max_listen * 1000 / 1024;
 +
 +      if (tu == 0) {
 +              p2p_dbg(p2p, "Skip listen state since duration was 0 TU");
 +              p2p_set_timeout(p2p, 0, 0);
 +              return;
 +      }
 +
-       ies = p2p_build_probe_resp_ies(p2p);
++      ies = p2p_build_probe_resp_ies(p2p, NULL, 0);
 +      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");
 +              p2p->pending_listen_freq = 0;
 +      }
 +      wpabuf_free(ies);
 +}
 +
 +
 +int p2p_listen(struct p2p_data *p2p, unsigned int timeout)
 +{
 +      int freq;
 +      struct wpabuf *ies;
 +
 +      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_sec = timeout / 1000;
 +      p2p->pending_listen_usec = (timeout % 1000) * 1000;
 +
 +      if (p2p->p2p_scan_running) {
 +              if (p2p->start_after_scan == P2P_AFTER_SCAN_CONNECT) {
 +                      p2p_dbg(p2p, "p2p_scan running - connect is already pending - skip listen");
 +                      return 0;
 +              }
 +              p2p_dbg(p2p, "p2p_scan running - delay start of listen state");
 +              p2p->start_after_scan = P2P_AFTER_SCAN_LISTEN;
 +              return 0;
 +      }
 +
-                                const u8 *gi, size_t gi_len)
++      ies = p2p_build_probe_resp_ies(p2p, NULL, 0);
 +      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;
 +              wpabuf_free(ies);
 +              return -1;
 +      }
 +      wpabuf_free(ies);
 +
 +      p2p_set_state(p2p, P2P_LISTEN_ONLY);
 +
 +      return 0;
 +}
 +
 +
 +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) {
 +              dev->flags &= ~P2P_DEV_REPORTED;
 +              dev->sd_reqs = 0;
 +      }
 +}
 +
 +
 +/**
 + * p2p_get_device - Fetch a peer entry
 + * @p2p: P2P module context from p2p_init()
 + * @addr: P2P Device Address of the peer
 + * Returns: Pointer to the device entry or %NULL if not found
 + */
 +struct p2p_device * p2p_get_device(struct p2p_data *p2p, const u8 *addr)
 +{
 +      struct p2p_device *dev;
 +      dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) {
 +              if (os_memcmp(dev->info.p2p_device_addr, addr, ETH_ALEN) == 0)
 +                      return dev;
 +      }
 +      return NULL;
 +}
 +
 +
 +/**
 + * p2p_get_device_interface - Fetch a peer entry based on P2P Interface Address
 + * @p2p: P2P module context from p2p_init()
 + * @addr: P2P Interface Address of the peer
 + * Returns: Pointer to the device entry or %NULL if not found
 + */
 +struct p2p_device * p2p_get_device_interface(struct p2p_data *p2p,
 +                                           const u8 *addr)
 +{
 +      struct p2p_device *dev;
 +      dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) {
 +              if (os_memcmp(dev->interface_addr, addr, ETH_ALEN) == 0)
 +                      return dev;
 +      }
 +      return NULL;
 +}
 +
 +
 +/**
 + * p2p_create_device - Create a peer entry
 + * @p2p: P2P module context from p2p_init()
 + * @addr: P2P Device Address of the peer
 + * Returns: Pointer to the device entry or %NULL on failure
 + *
 + * If there is already an entry for the peer, it will be returned instead of
 + * creating a new one.
 + */
 +static struct p2p_device * p2p_create_device(struct p2p_data *p2p,
 +                                           const u8 *addr)
 +{
 +      struct p2p_device *dev, *oldest = NULL;
 +      size_t count = 0;
 +
 +      dev = p2p_get_device(p2p, addr);
 +      if (dev)
 +              return dev;
 +
 +      dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) {
 +              count++;
 +              if (oldest == NULL ||
 +                  os_reltime_before(&dev->last_seen, &oldest->last_seen))
 +                      oldest = dev;
 +      }
 +      if (count + 1 > p2p->cfg->max_peers && oldest) {
 +              p2p_dbg(p2p, "Remove oldest peer entry to make room for a new peer");
 +              dl_list_del(&oldest->list);
 +              p2p_device_free(p2p, oldest);
 +      }
 +
 +      dev = os_zalloc(sizeof(*dev));
 +      if (dev == NULL)
 +              return NULL;
 +      dl_list_add(&p2p->devices, &dev->list);
 +      os_memcpy(dev->info.p2p_device_addr, addr, ETH_ALEN);
 +
 +      return dev;
 +}
 +
 +
 +static void p2p_copy_client_info(struct p2p_device *dev,
 +                               struct p2p_client_info *cli)
 +{
 +      os_memcpy(dev->info.device_name, cli->dev_name, cli->dev_name_len);
 +      dev->info.device_name[cli->dev_name_len] = '\0';
 +      dev->info.dev_capab = cli->dev_capab;
 +      dev->info.config_methods = cli->config_methods;
 +      os_memcpy(dev->info.pri_dev_type, cli->pri_dev_type, 8);
 +      dev->info.wps_sec_dev_type_list_len = 8 * cli->num_sec_dev_types;
 +      os_memcpy(dev->info.wps_sec_dev_type_list, cli->sec_dev_types,
 +                dev->info.wps_sec_dev_type_list_len);
 +}
 +
 +
 +static int p2p_add_group_clients(struct p2p_data *p2p, const u8 *go_dev_addr,
 +                               const u8 *go_interface_addr, int freq,
-               os_get_reltime(&dev->last_seen);
++                               const u8 *gi, size_t gi_len,
++                               struct os_reltime *rx_time)
 +{
 +      struct p2p_group_info info;
 +      size_t c;
 +      struct p2p_device *dev;
 +
 +      if (gi == NULL)
 +              return 0;
 +
 +      if (p2p_group_info_parse(gi, gi_len, &info) < 0)
 +              return -1;
 +
 +      /*
 +       * Clear old data for this group; if the devices are still in the
 +       * group, the information will be restored in the loop following this.
 +       */
 +      dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) {
 +              if (os_memcmp(dev->member_in_go_iface, go_interface_addr,
 +                            ETH_ALEN) == 0) {
 +                      os_memset(dev->member_in_go_iface, 0, ETH_ALEN);
 +                      os_memset(dev->member_in_go_dev, 0, ETH_ALEN);
 +              }
 +      }
 +
 +      for (c = 0; c < info.num_clients; c++) {
 +              struct p2p_client_info *cli = &info.client[c];
 +              if (os_memcmp(cli->p2p_device_addr, p2p->cfg->dev_addr,
 +                            ETH_ALEN) == 0)
 +                      continue; /* ignore our own entry */
 +              dev = p2p_get_device(p2p, cli->p2p_device_addr);
 +              if (dev) {
 +                      if (dev->flags & (P2P_DEV_GROUP_CLIENT_ONLY |
 +                                        P2P_DEV_PROBE_REQ_ONLY)) {
 +                              /*
 +                               * Update information since we have not
 +                               * received this directly from the client.
 +                               */
 +                              p2p_copy_client_info(dev, cli);
 +                      } else {
 +                              /*
 +                               * Need to update P2P Client Discoverability
 +                               * flag since it is valid only in P2P Group
 +                               * Info attribute.
 +                               */
 +                              dev->info.dev_capab &=
 +                                      ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY;
 +                              dev->info.dev_capab |=
 +                                      cli->dev_capab &
 +                                      P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY;
 +                      }
 +                      if (dev->flags & P2P_DEV_PROBE_REQ_ONLY) {
 +                              dev->flags &= ~P2P_DEV_PROBE_REQ_ONLY;
 +                      }
 +              } else {
 +                      dev = p2p_create_device(p2p, cli->p2p_device_addr);
 +                      if (dev == NULL)
 +                              continue;
 +                      dev->flags |= P2P_DEV_GROUP_CLIENT_ONLY;
 +                      p2p_copy_client_info(dev, cli);
 +                      dev->oper_freq = freq;
 +                      p2p->cfg->dev_found(p2p->cfg->cb_ctx,
 +                                          dev->info.p2p_device_addr,
 +                                          &dev->info, 1);
 +                      dev->flags |= P2P_DEV_REPORTED | P2P_DEV_REPORTED_ONCE;
 +              }
 +
 +              os_memcpy(dev->interface_addr, cli->p2p_interface_addr,
 +                        ETH_ALEN);
-        * entry is newer than the one previously stored.
++              os_memcpy(&dev->last_seen, rx_time, sizeof(struct os_reltime));
 +              os_memcpy(dev->member_in_go_dev, go_dev_addr, ETH_ALEN);
 +              os_memcpy(dev->member_in_go_iface, go_interface_addr,
 +                        ETH_ALEN);
++              dev->flags |= P2P_DEV_LAST_SEEN_AS_GROUP_CLIENT;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +static void p2p_copy_wps_info(struct p2p_data *p2p, struct p2p_device *dev,
 +                            int probe_req, const struct p2p_message *msg)
 +{
 +      os_memcpy(dev->info.device_name, msg->device_name,
 +                sizeof(dev->info.device_name));
 +
 +      if (msg->manufacturer &&
 +          msg->manufacturer_len < sizeof(dev->info.manufacturer)) {
 +              os_memset(dev->info.manufacturer, 0,
 +                        sizeof(dev->info.manufacturer));
 +              os_memcpy(dev->info.manufacturer, msg->manufacturer,
 +                        msg->manufacturer_len);
 +      }
 +
 +      if (msg->model_name &&
 +          msg->model_name_len < sizeof(dev->info.model_name)) {
 +              os_memset(dev->info.model_name, 0,
 +                        sizeof(dev->info.model_name));
 +              os_memcpy(dev->info.model_name, msg->model_name,
 +                        msg->model_name_len);
 +      }
 +
 +      if (msg->model_number &&
 +          msg->model_number_len < sizeof(dev->info.model_number)) {
 +              os_memset(dev->info.model_number, 0,
 +                        sizeof(dev->info.model_number));
 +              os_memcpy(dev->info.model_number, msg->model_number,
 +                        msg->model_number_len);
 +      }
 +
 +      if (msg->serial_number &&
 +          msg->serial_number_len < sizeof(dev->info.serial_number)) {
 +              os_memset(dev->info.serial_number, 0,
 +                        sizeof(dev->info.serial_number));
 +              os_memcpy(dev->info.serial_number, msg->serial_number,
 +                        msg->serial_number_len);
 +      }
 +
 +      if (msg->pri_dev_type)
 +              os_memcpy(dev->info.pri_dev_type, msg->pri_dev_type,
 +                        sizeof(dev->info.pri_dev_type));
 +      else if (msg->wps_pri_dev_type)
 +              os_memcpy(dev->info.pri_dev_type, msg->wps_pri_dev_type,
 +                        sizeof(dev->info.pri_dev_type));
 +
 +      if (msg->wps_sec_dev_type_list) {
 +              os_memcpy(dev->info.wps_sec_dev_type_list,
 +                        msg->wps_sec_dev_type_list,
 +                        msg->wps_sec_dev_type_list_len);
 +              dev->info.wps_sec_dev_type_list_len =
 +                      msg->wps_sec_dev_type_list_len;
 +      }
 +
 +      if (msg->capability) {
 +              /*
 +               * P2P Client Discoverability bit is reserved in all frames
 +               * that use this function, so do not change its value here.
 +               */
 +              dev->info.dev_capab &= P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY;
 +              dev->info.dev_capab |= msg->capability[0] &
 +                      ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY;
 +              dev->info.group_capab = msg->capability[1];
 +      }
 +
 +      if (msg->ext_listen_timing) {
 +              dev->ext_listen_period = WPA_GET_LE16(msg->ext_listen_timing);
 +              dev->ext_listen_interval =
 +                      WPA_GET_LE16(msg->ext_listen_timing + 2);
 +      }
 +
 +      if (!probe_req) {
 +              u16 new_config_methods;
 +              new_config_methods = msg->config_methods ?
 +                      msg->config_methods : msg->wps_config_methods;
 +              if (new_config_methods &&
 +                  dev->info.config_methods != new_config_methods) {
 +                      p2p_dbg(p2p, "Update peer " MACSTR
 +                              " config_methods 0x%x -> 0x%x",
 +                              MAC2STR(dev->info.p2p_device_addr),
 +                              dev->info.config_methods,
 +                              new_config_methods);
 +                      dev->info.config_methods = new_config_methods;
 +              }
 +      }
 +}
 +
 +
 +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()
 + * @addr: Source address of Beacon or Probe Response frame (may be either
 + *    P2P Device Address or P2P Interface Address)
 + * @level: Signal level (signal strength of the received frame from the peer)
 + * @freq: Frequency on which the Beacon or Probe Response frame was received
 + * @rx_time: Time when the result was received
 + * @ies: IEs from the Beacon or Probe Response frame
 + * @ies_len: Length of ies buffer in octets
 + * @scan_res: Whether this was based on scan results
 + * Returns: 0 on success, -1 on failure
 + *
 + * If the scan result is for a GO, the clients in the group will also be added
 + * to the peer table. This function can also be used with some other frames
 + * like Provision Discovery Request that contains P2P Capability and P2P Device
 + * Info attributes.
 + */
 +int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq,
 +                 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_reltime time_now;
 +
 +      os_memset(&msg, 0, sizeof(msg));
 +      if (p2p_parse_ies(ies, ies_len, &msg)) {
 +              p2p_dbg(p2p, "Failed to parse P2P IE for a device entry");
 +              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 (!is_zero_ether_addr(p2p->peer_filter) &&
 +          os_memcmp(p2p_dev_addr, p2p->peer_filter, ETH_ALEN) != 0) {
 +              p2p_dbg(p2p, "Do not add peer filter for " MACSTR
 +                      " due to peer filter", MAC2STR(p2p_dev_addr));
 +              p2p_parse_free(&msg);
 +              return 0;
 +      }
 +
 +      dev = p2p_create_device(p2p, p2p_dev_addr);
 +      if (dev == NULL) {
 +              p2p_parse_free(&msg);
 +              return -1;
 +      }
 +
 +      if (rx_time == NULL) {
 +              os_get_reltime(&time_now);
 +              rx_time = &time_now;
 +      }
 +
 +      /*
 +       * Update the device entry only if the new peer
-           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)",
++       * entry is newer than the one previously stored, or if
++       * the device was previously seen as a P2P Client in a group
++       * and the new entry isn't older than a threshold.
 +       */
 +      if (dev->last_seen.sec > 0 &&
-                       (unsigned int) dev->last_seen.usec);
++          os_reltime_before(rx_time, &dev->last_seen) &&
++          (!(dev->flags & P2P_DEV_LAST_SEEN_AS_GROUP_CLIENT) ||
++           os_reltime_expired(&dev->last_seen, rx_time,
++                              P2P_DEV_GROUP_CLIENT_RESP_THRESHOLD))) {
++              p2p_dbg(p2p,
++                      "Do not update peer entry based on old frame (rx_time=%u.%06u last_seen=%u.%06u flags=0x%x)",
 +                      (unsigned int) rx_time->sec,
 +                      (unsigned int) rx_time->usec,
 +                      (unsigned int) dev->last_seen.sec,
-       dev->flags &= ~(P2P_DEV_PROBE_REQ_ONLY | P2P_DEV_GROUP_CLIENT_ONLY);
++                      (unsigned int) dev->last_seen.usec,
++                      dev->flags);
 +              p2p_parse_free(&msg);
 +              return -1;
 +      }
 +
 +      os_memcpy(&dev->last_seen, rx_time, sizeof(struct os_reltime));
 +
-                                     msg.group_info, msg.group_info_len);
++      dev->flags &= ~(P2P_DEV_PROBE_REQ_ONLY | P2P_DEV_GROUP_CLIENT_ONLY |
++                      P2P_DEV_LAST_SEEN_AS_GROUP_CLIENT);
 +
 +      if (os_memcmp(addr, p2p_dev_addr, ETH_ALEN) != 0)
 +              os_memcpy(dev->interface_addr, addr, ETH_ALEN);
 +      if (msg.ssid &&
++          msg.ssid[1] <= sizeof(dev->oper_ssid) &&
 +          (msg.ssid[1] != P2P_WILDCARD_SSID_LEN ||
 +           os_memcmp(msg.ssid + 2, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN)
 +           != 0)) {
 +              os_memcpy(dev->oper_ssid, msg.ssid + 2, msg.ssid[1]);
 +              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;
 +              if (*msg.ds_params == 14)
 +                      ds_freq = 2484;
 +              else
 +                      ds_freq = 2407 + *msg.ds_params * 5;
 +              if (freq != ds_freq) {
 +                      p2p_dbg(p2p, "Update Listen frequency based on DS Parameter Set IE: %d -> %d MHz",
 +                              freq, ds_freq);
 +                      freq = ds_freq;
 +              }
 +      }
 +
 +      if (dev->listen_freq && dev->listen_freq != freq && scan_res) {
 +              p2p_dbg(p2p, "Update Listen frequency based on scan results ("
 +                      MACSTR " %d -> %d MHz (DS param %d)",
 +                      MAC2STR(dev->info.p2p_device_addr), dev->listen_freq,
 +                      freq, msg.ds_params ? *msg.ds_params : -1);
 +      }
 +      if (scan_res) {
 +              dev->listen_freq = freq;
 +              if (msg.group_info)
 +                      dev->oper_freq = freq;
 +      }
 +      dev->info.level = level;
 +
 +      p2p_copy_wps_info(p2p, dev, 0, &msg);
 +
 +      for (i = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) {
 +              wpabuf_free(dev->info.wps_vendor_ext[i]);
 +              dev->info.wps_vendor_ext[i] = NULL;
 +      }
 +
 +      for (i = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) {
 +              if (msg.wps_vendor_ext[i] == NULL)
 +                      break;
 +              dev->info.wps_vendor_ext[i] = wpabuf_alloc_copy(
 +                      msg.wps_vendor_ext[i], msg.wps_vendor_ext_len[i]);
 +              if (dev->info.wps_vendor_ext[i] == NULL)
 +                      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);
 +      }
 +
 +      if (scan_res) {
 +              p2p_add_group_clients(p2p, p2p_dev_addr, addr, freq,
-       for (i = 0; str[i] && i < adv_len; i++) {
++                                    msg.group_info, msg.group_info_len,
++                                    rx_time);
 +      }
 +
 +      p2p_parse_free(&msg);
 +
 +      p2p_update_peer_vendor_elems(dev, ies, ies_len);
 +
 +      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)",
 +              freq, (unsigned int) rx_time->sec,
 +              (unsigned int) rx_time->usec);
 +      if (dev->flags & P2P_DEV_USER_REJECTED) {
 +              p2p_dbg(p2p, "Do not report rejected device");
 +              return 0;
 +      }
 +
 +      if (dev->info.config_methods == 0 &&
 +          (freq == 2412 || freq == 2437 || freq == 2462)) {
 +              /*
 +               * If we have only seen a Beacon frame from a GO, we do not yet
 +               * know what WPS config methods it supports. Since some
 +               * applications use config_methods value from P2P-DEVICE-FOUND
 +               * events, postpone reporting this peer until we've fully
 +               * discovered its capabilities.
 +               *
 +               * At least for now, do this only if the peer was detected on
 +               * one of the social channels since that peer can be easily be
 +               * found again and there are no limitations of having to use
 +               * passive scan on this channels, so this can be done through
 +               * Probe Response frame that includes the config_methods
 +               * information.
 +               */
 +              p2p_dbg(p2p, "Do not report peer " MACSTR
 +                      " with unknown config methods", MAC2STR(addr));
 +              return 0;
 +      }
 +
 +      p2p->cfg->dev_found(p2p->cfg->cb_ctx, addr, &dev->info,
 +                          !(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;
 +}
 +
 +
 +static void p2p_device_free(struct p2p_data *p2p, struct p2p_device *dev)
 +{
 +      int i;
 +
 +      if (p2p->go_neg_peer == dev) {
 +              /*
 +               * If GO Negotiation is in progress, report that it has failed.
 +               */
 +              p2p_go_neg_failed(p2p, -1);
 +      }
 +      if (p2p->invite_peer == dev)
 +              p2p->invite_peer = NULL;
 +      if (p2p->sd_peer == dev)
 +              p2p->sd_peer = NULL;
 +      if (p2p->pending_client_disc_go == dev)
 +              p2p->pending_client_disc_go = NULL;
 +
 +      /* dev_lost() device, but only if it was previously dev_found() */
 +      if (dev->flags & P2P_DEV_REPORTED_ONCE)
 +              p2p->cfg->dev_lost(p2p->cfg->cb_ctx,
 +                                 dev->info.p2p_device_addr);
 +
 +      for (i = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) {
 +              wpabuf_free(dev->info.wps_vendor_ext[i]);
 +              dev->info.wps_vendor_ext[i] = NULL;
 +      }
 +
 +      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);
 +}
 +
 +
 +static int p2p_get_next_prog_freq(struct p2p_data *p2p)
 +{
 +      struct p2p_channels *c;
 +      struct p2p_reg_class *cla;
 +      size_t cl, ch;
 +      int found = 0;
 +      u8 reg_class;
 +      u8 channel;
 +      int freq;
 +
 +      c = &p2p->cfg->channels;
 +      for (cl = 0; cl < c->reg_classes; cl++) {
 +              cla = &c->reg_class[cl];
 +              if (cla->reg_class != p2p->last_prog_scan_class)
 +                      continue;
 +              for (ch = 0; ch < cla->channels; ch++) {
 +                      if (cla->channel[ch] == p2p->last_prog_scan_chan) {
 +                              found = 1;
 +                              break;
 +                      }
 +              }
 +              if (found)
 +                      break;
 +      }
 +
 +      if (!found) {
 +              /* Start from beginning */
 +              reg_class = c->reg_class[0].reg_class;
 +              channel = c->reg_class[0].channel[0];
 +      } else {
 +              /* Pick the next channel */
 +              ch++;
 +              if (ch == cla->channels) {
 +                      cl++;
 +                      if (cl == c->reg_classes)
 +                              cl = 0;
 +                      ch = 0;
 +              }
 +              reg_class = c->reg_class[cl].reg_class;
 +              channel = c->reg_class[cl].channel[ch];
 +      }
 +
 +      freq = p2p_channel_to_freq(reg_class, channel);
 +      p2p_dbg(p2p, "Next progressive search channel: reg_class %u channel %u -> %d MHz",
 +              reg_class, channel, freq);
 +      p2p->last_prog_scan_class = reg_class;
 +      p2p->last_prog_scan_chan = channel;
 +
 +      if (freq == 2412 || freq == 2437 || freq == 2462)
 +              return 0; /* No need to add social channels */
 +      return freq;
 +}
 +
 +
 +static void p2p_search(struct p2p_data *p2p)
 +{
 +      int freq = 0;
 +      enum p2p_scan_type type;
 +      u16 pw_id = DEV_PW_DEFAULT;
 +      int res;
 +
 +      if (p2p->drv_in_listen) {
 +              p2p_dbg(p2p, "Driver is still in Listen state - wait for it to end before continuing");
 +              return;
 +      }
 +      p2p->cfg->stop_listen(p2p->cfg->cb_ctx);
 +
 +      if (p2p->find_type == P2P_FIND_PROGRESSIVE &&
 +          (freq = p2p_get_next_prog_freq(p2p)) > 0) {
 +              type = P2P_SCAN_SOCIAL_PLUS_ONE;
 +              p2p_dbg(p2p, "Starting search (+ freq %u)", freq);
 +      } else {
 +              type = P2P_SCAN_SOCIAL;
 +              p2p_dbg(p2p, "Starting search");
 +      }
 +
 +      res = p2p->cfg->p2p_scan(p2p->cfg->cb_ctx, type, freq,
 +                               p2p->num_req_dev_types, p2p->req_dev_types,
 +                               p2p->find_dev_id, pw_id);
 +      if (res < 0) {
 +              p2p_dbg(p2p, "Scan request schedule failed");
 +              p2p_continue_find(p2p);
 +      }
 +}
 +
 +
 +static void p2p_find_timeout(void *eloop_ctx, void *timeout_ctx)
 +{
 +      struct p2p_data *p2p = eloop_ctx;
 +      p2p_dbg(p2p, "Find timeout -> stop");
 +      p2p_stop_find(p2p);
 +}
 +
 +
 +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;
 +      enum p2p_after_scan op;
 +
 +      if (p2p->after_scan_tx) {
 +              p2p->after_scan_tx_in_progress = 1;
 +              p2p_dbg(p2p, "Send pending Action frame at p2p_scan completion");
 +              p2p->cfg->send_action(p2p->cfg->cb_ctx,
 +                                    p2p->after_scan_tx->freq,
 +                                    p2p->after_scan_tx->dst,
 +                                    p2p->after_scan_tx->src,
 +                                    p2p->after_scan_tx->bssid,
 +                                    (u8 *) (p2p->after_scan_tx + 1),
 +                                    p2p->after_scan_tx->len,
 +                                    p2p->after_scan_tx->wait_time);
 +              os_free(p2p->after_scan_tx);
 +              p2p->after_scan_tx = NULL;
 +              return 1;
 +      }
 +
 +      op = p2p->start_after_scan;
 +      p2p->start_after_scan = P2P_AFTER_SCAN_NOTHING;
 +      switch (op) {
 +      case P2P_AFTER_SCAN_NOTHING:
 +              break;
 +      case P2P_AFTER_SCAN_LISTEN:
 +              p2p_dbg(p2p, "Start previously requested Listen state");
 +              p2p_listen(p2p, p2p->pending_listen_sec * 1000 +
 +                         p2p->pending_listen_usec / 1000);
 +              return 1;
 +      case P2P_AFTER_SCAN_CONNECT:
 +              p2p_dbg(p2p, "Start previously requested connect with " MACSTR,
 +                      MAC2STR(p2p->after_scan_peer));
 +              dev = p2p_get_device(p2p, p2p->after_scan_peer);
 +              if (dev == NULL) {
 +                      p2p_dbg(p2p, "Peer not known anymore");
 +                      break;
 +              }
 +              p2p_connect_send(p2p, dev);
 +              return 1;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +static void p2p_scan_timeout(void *eloop_ctx, void *timeout_ctx)
 +{
 +      struct p2p_data *p2p = eloop_ctx;
 +      int running;
 +      p2p_dbg(p2p, "p2p_scan timeout (running=%d)", p2p->p2p_scan_running);
 +      running = p2p->p2p_scan_running;
 +      /* Make sure we recover from missed scan results callback */
 +      p2p->p2p_scan_running = 0;
 +
 +      if (running)
 +              p2p_run_after_scan(p2p);
 +}
 +
 +
 +static void p2p_free_req_dev_types(struct p2p_data *p2p)
 +{
 +      p2p->num_req_dev_types = 0;
 +      os_free(p2p->req_dev_types);
 +      p2p->req_dev_types = NULL;
 +}
 +
 +
 +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);
++      if (adv_len >= sizeof(str_buf))
++              return 0;
 +
-               int i;
++      for (i = 0; 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,
 +           u8 seek_count, const char **seek, int freq)
 +{
 +      int res;
 +
 +      p2p_dbg(p2p, "Starting find (type=%d)", type);
 +      os_get_reltime(&p2p->find_start);
 +      if (p2p->p2p_scan_running) {
 +              p2p_dbg(p2p, "p2p_scan is already running");
 +      }
 +
 +      p2p_free_req_dev_types(p2p);
 +      if (req_dev_types && num_req_dev_types) {
 +              p2p->req_dev_types = os_malloc(num_req_dev_types *
 +                                             WPS_DEV_TYPE_LEN);
 +              if (p2p->req_dev_types == NULL)
 +                      return -1;
 +              os_memcpy(p2p->req_dev_types, req_dev_types,
 +                        num_req_dev_types * WPS_DEV_TYPE_LEN);
 +              p2p->num_req_dev_types = num_req_dev_types;
 +      }
 +
 +      if (dev_id) {
 +              os_memcpy(p2p->find_dev_id_buf, dev_id, ETH_ALEN);
 +              p2p->find_dev_id = p2p->find_dev_id_buf;
 +      } 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_dbg(p2p, "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];
-               p2p->p2ps_seek_count = seek_count;
++              int i, count = 0;
 +
-                       /* 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);
 +              for (i = 0; i < seek_count; i++) {
 +                      if (!p2ps_gen_hash(p2p, seek[i], buf))
 +                              continue;
 +
-               os_memcpy(&p2p->query_hash, p2p->wild_card_hash, P2PS_HASH_LEN);
++                      p2p_dbg(p2p, "Seek service %s hash " MACSTR,
++                              seek[i], MAC2STR(buf));
++                      os_memcpy(&p2p->p2ps_seek_hash[count * P2PS_HASH_LEN],
++                                buf, P2PS_HASH_LEN);
++                      count++;
 +              }
++
++              p2p->p2ps_seek_count = count;
 +              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;
-       const int op_classes_5ghz[] = { 124, 115, 0 };
++              os_memcpy(&p2p->p2ps_seek_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);
 +      p2p->find_type = type;
 +      p2p_device_clear_reported(p2p);
 +      p2p_set_state(p2p, P2P_SEARCH);
 +      p2p->search_delay = search_delay;
 +      p2p->in_search_delay = 0;
 +      eloop_cancel_timeout(p2p_find_timeout, p2p, NULL);
 +      p2p->last_p2p_find_timeout = timeout;
 +      if (timeout)
 +              eloop_register_timeout(timeout, 0, p2p_find_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,
 +                                       p2p->req_dev_types, dev_id,
 +                                       DEV_PW_DEFAULT);
 +              break;
 +      case P2P_FIND_ONLY_SOCIAL:
 +              res = p2p->cfg->p2p_scan(p2p->cfg->cb_ctx, P2P_SCAN_SOCIAL, 0,
 +                                       p2p->num_req_dev_types,
 +                                       p2p->req_dev_types, dev_id,
 +                                       DEV_PW_DEFAULT);
 +              break;
 +      default:
 +              return -1;
 +      }
 +
 +      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);
 +      }
 +
 +      return res;
 +}
 +
 +
 +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_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;
 +      if (p2p->go_neg_peer)
 +              p2p->go_neg_peer->flags &= ~P2P_DEV_PEER_WAITING_RESPONSE;
 +      p2p->go_neg_peer = NULL;
 +      p2p->sd_peer = NULL;
 +      p2p->invite_peer = NULL;
 +      p2p_stop_listen_for_freq(p2p, freq);
 +      p2p->send_action_in_progress = 0;
 +}
 +
 +
 +void p2p_stop_listen_for_freq(struct p2p_data *p2p, int freq)
 +{
 +      if (freq > 0 && p2p->drv_in_listen == freq && p2p->in_listen) {
 +              p2p_dbg(p2p, "Skip stop_listen since we are on correct channel for response");
 +              return;
 +      }
 +      if (p2p->in_listen) {
 +              p2p->in_listen = 0;
 +              p2p_clear_timeout(p2p);
 +      }
 +      if (p2p->drv_in_listen) {
 +              /*
 +               * The driver may not deliver callback to p2p_listen_end()
 +               * when the operation gets canceled, so clear the internal
 +               * variable that is tracking driver state.
 +               */
 +              p2p_dbg(p2p, "Clear drv_in_listen (%d)", p2p->drv_in_listen);
 +              p2p->drv_in_listen = 0;
 +      }
 +      p2p->cfg->stop_listen(p2p->cfg->cb_ctx);
 +}
 +
 +
 +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, 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) &&
 +          (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;
 +      }
 +
 +      p2p->op_reg_class = op_class;
 +      p2p->op_channel = op_channel;
 +
 +      if (force_freq) {
 +              p2p->channels.reg_classes = 1;
 +              p2p->channels.reg_class[0].channels = 1;
 +              p2p->channels.reg_class[0].reg_class = p2p->op_reg_class;
 +              p2p->channels.reg_class[0].channel[0] = p2p->op_channel;
 +      } else {
 +              os_memcpy(&p2p->channels, &p2p->cfg->channels,
 +                        sizeof(struct p2p_channels));
 +      }
 +
 +      return 0;
 +}
 +
 +
 +static void p2p_prepare_channel_best(struct p2p_data *p2p)
 +{
 +      u8 op_class, op_channel;
- struct wpabuf * p2p_build_probe_resp_ies(struct p2p_data *p2p)
++      const int op_classes_5ghz[] = { 124, 125, 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) &&
 +          p2p_freq_to_channel(p2p->best_freq_overall, &op_class, &op_channel)
 +          == 0) {
 +              p2p_dbg(p2p, "Select best overall channel as operating channel preference");
 +              p2p->op_reg_class = op_class;
 +              p2p->op_channel = op_channel;
 +      } else if (!p2p->cfg->cfg_op_channel && p2p->best_freq_5 > 0 &&
 +                 p2p_supported_freq(p2p, p2p->best_freq_5) &&
 +                 p2p_freq_to_channel(p2p->best_freq_5, &op_class, &op_channel)
 +                 == 0) {
 +              p2p_dbg(p2p, "Select best 5 GHz channel as operating channel preference");
 +              p2p->op_reg_class = op_class;
 +              p2p->op_channel = op_channel;
 +      } else if (!p2p->cfg->cfg_op_channel && p2p->best_freq_24 > 0 &&
 +                 p2p_supported_freq(p2p, p2p->best_freq_24) &&
 +                 p2p_freq_to_channel(p2p->best_freq_24, &op_class,
 +                                     &op_channel) == 0) {
 +              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 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,
 +                sizeof(struct p2p_channels));
 +}
 +
 +
 +/**
 + * p2p_prepare_channel - Select operating channel for GO Negotiation
 + * @p2p: P2P module context from p2p_init()
 + * @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
 + * Negotiation prior to having received peer information. The selected channel
 + * may be further optimized in p2p_reselect_channel() once the peer information
 + * is available.
 + */
 +int p2p_prepare_channel(struct p2p_data *p2p, struct p2p_device *dev,
 +                      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, 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)" : "");
 +
 +      if (force_freq)
 +              dev->flags |= P2P_DEV_FORCE_FREQ;
 +      else
 +              dev->flags &= ~P2P_DEV_FORCE_FREQ;
 +
 +      return 0;
 +}
 +
 +
 +static void p2p_set_dev_persistent(struct p2p_device *dev,
 +                                 int persistent_group)
 +{
 +      switch (persistent_group) {
 +      case 0:
 +              dev->flags &= ~(P2P_DEV_PREFER_PERSISTENT_GROUP |
 +                              P2P_DEV_PREFER_PERSISTENT_RECONN);
 +              break;
 +      case 1:
 +              dev->flags |= P2P_DEV_PREFER_PERSISTENT_GROUP;
 +              dev->flags &= ~P2P_DEV_PREFER_PERSISTENT_RECONN;
 +              break;
 +      case 2:
 +              dev->flags |= P2P_DEV_PREFER_PERSISTENT_GROUP |
 +                      P2P_DEV_PREFER_PERSISTENT_RECONN;
 +              break;
 +      }
 +}
 +
 +
 +int p2p_connect(struct p2p_data *p2p, const u8 *peer_addr,
 +              enum p2p_wps_method wps_method,
 +              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, 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 "
 +              "oob_pw_id=%u",
 +              MAC2STR(peer_addr), go_intent, MAC2STR(own_interface_addr),
 +              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)) {
 +              p2p_dbg(p2p, "Cannot connect to unknown P2P Device " MACSTR,
 +                      MAC2STR(peer_addr));
 +              return -1;
 +      }
 +
 +      if (p2p_prepare_channel(p2p, dev, force_freq, pref_freq,
 +                              go_intent == 15) < 0)
 +              return -1;
 +
 +      if (dev->flags & P2P_DEV_GROUP_CLIENT_ONLY) {
 +              if (!(dev->info.dev_capab &
 +                    P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY)) {
 +                      p2p_dbg(p2p, "Cannot connect to P2P Device " MACSTR
 +                              " that is in a group and is not discoverable",
 +                              MAC2STR(peer_addr));
 +                      return -1;
 +              }
 +              if (dev->oper_freq <= 0) {
 +                      p2p_dbg(p2p, "Cannot connect to P2P Device " MACSTR
 +                              " with incomplete information",
 +                              MAC2STR(peer_addr));
 +                      return -1;
 +              }
 +
 +              /*
 +               * First, try to connect directly. If the peer does not
 +               * acknowledge frames, assume it is sleeping and use device
 +               * discoverability via the GO at that point.
 +               */
 +      }
 +
 +      p2p->ssid_set = 0;
 +      if (force_ssid) {
 +              wpa_hexdump_ascii(MSG_DEBUG, "P2P: Forced SSID",
 +                                force_ssid, force_ssid_len);
 +              os_memcpy(p2p->ssid, force_ssid, force_ssid_len);
 +              p2p->ssid_len = force_ssid_len;
 +              p2p->ssid_set = 1;
 +      }
 +
 +      dev->flags &= ~P2P_DEV_NOT_YET_READY;
 +      dev->flags &= ~P2P_DEV_USER_REJECTED;
 +      dev->flags &= ~P2P_DEV_WAIT_GO_NEG_RESPONSE;
 +      dev->flags &= ~P2P_DEV_WAIT_GO_NEG_CONFIRM;
 +      if (pd_before_go_neg)
 +              dev->flags |= P2P_DEV_PD_BEFORE_GO_NEG;
 +      else {
 +              dev->flags &= ~P2P_DEV_PD_BEFORE_GO_NEG;
 +              /*
 +               * Assign dialog token and tie breaker here to use the same
 +               * values in each retry within the same GO Negotiation exchange.
 +               */
 +              dev->dialog_token++;
 +              if (dev->dialog_token == 0)
 +                      dev->dialog_token = 1;
 +              dev->tie_breaker = p2p->next_tie_breaker;
 +              p2p->next_tie_breaker = !p2p->next_tie_breaker;
 +      }
 +      dev->connect_reqs = 0;
 +      dev->go_neg_req_sent = 0;
 +      dev->go_state = UNKNOWN_GO;
 +      p2p_set_dev_persistent(dev, persistent_group);
 +      p2p->go_intent = go_intent;
 +      os_memcpy(p2p->intended_addr, own_interface_addr, ETH_ALEN);
 +
 +      if (p2p->state != P2P_IDLE)
 +              p2p_stop_find(p2p);
 +
 +      if (p2p->after_scan_tx) {
 +              /*
 +               * We need to drop the pending frame to avoid issues with the
 +               * new GO Negotiation, e.g., when the pending frame was from a
 +               * previous attempt at starting a GO Negotiation.
 +               */
 +              p2p_dbg(p2p, "Dropped previous pending Action frame TX that was waiting for p2p_scan completion");
 +              os_free(p2p->after_scan_tx);
 +              p2p->after_scan_tx = NULL;
 +      }
 +
 +      dev->wps_method = wps_method;
 +      dev->oob_pw_id = oob_pw_id;
 +      dev->status = P2P_SC_SUCCESS;
 +
 +      if (p2p->p2p_scan_running) {
 +              p2p_dbg(p2p, "p2p_scan running - delay connect send");
 +              p2p->start_after_scan = P2P_AFTER_SCAN_CONNECT;
 +              os_memcpy(p2p->after_scan_peer, peer_addr, ETH_ALEN);
 +              return 0;
 +      }
 +      p2p->start_after_scan = P2P_AFTER_SCAN_NOTHING;
 +
 +      return p2p_connect_send(p2p, dev);
 +}
 +
 +
 +int p2p_authorize(struct p2p_data *p2p, const u8 *peer_addr,
 +                enum p2p_wps_method wps_method,
 +                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, 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 oob_pw_id=%u",
 +              MAC2STR(peer_addr), go_intent, MAC2STR(own_interface_addr),
 +              wps_method, persistent_group, oob_pw_id);
 +
 +      dev = p2p_get_device(p2p, peer_addr);
 +      if (dev == NULL) {
 +              p2p_dbg(p2p, "Cannot authorize unknown P2P Device " MACSTR,
 +                      MAC2STR(peer_addr));
 +              return -1;
 +      }
 +
 +      if (p2p_prepare_channel(p2p, dev, force_freq, pref_freq, go_intent ==
 +                              15) < 0)
 +              return -1;
 +
 +      p2p->ssid_set = 0;
 +      if (force_ssid) {
 +              wpa_hexdump_ascii(MSG_DEBUG, "P2P: Forced SSID",
 +                                force_ssid, force_ssid_len);
 +              os_memcpy(p2p->ssid, force_ssid, force_ssid_len);
 +              p2p->ssid_len = force_ssid_len;
 +              p2p->ssid_set = 1;
 +      }
 +
 +      dev->flags &= ~P2P_DEV_NOT_YET_READY;
 +      dev->flags &= ~P2P_DEV_USER_REJECTED;
 +      dev->go_neg_req_sent = 0;
 +      dev->go_state = UNKNOWN_GO;
 +      p2p_set_dev_persistent(dev, persistent_group);
 +      p2p->go_intent = go_intent;
 +      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;
 +}
 +
 +
 +void p2p_add_dev_info(struct p2p_data *p2p, const u8 *addr,
 +                    struct p2p_device *dev, struct p2p_message *msg)
 +{
 +      os_get_reltime(&dev->last_seen);
 +
 +      p2p_copy_wps_info(p2p, dev, 0, msg);
 +
 +      if (msg->listen_channel) {
 +              int freq;
 +              freq = p2p_channel_to_freq(msg->listen_channel[3],
 +                                         msg->listen_channel[4]);
 +              if (freq < 0) {
 +                      p2p_dbg(p2p, "Unknown peer Listen channel: "
 +                              "country=%c%c(0x%02x) reg_class=%u channel=%u",
 +                              msg->listen_channel[0],
 +                              msg->listen_channel[1],
 +                              msg->listen_channel[2],
 +                              msg->listen_channel[3],
 +                              msg->listen_channel[4]);
 +              } else {
 +                      p2p_dbg(p2p, "Update peer " MACSTR
 +                              " Listen channel: %u -> %u MHz",
 +                              MAC2STR(dev->info.p2p_device_addr),
 +                              dev->listen_freq, freq);
 +                      dev->listen_freq = freq;
 +              }
 +      }
 +
 +      if (msg->wfd_subelems) {
 +              wpabuf_free(dev->info.wfd_subelems);
 +              dev->info.wfd_subelems = wpabuf_dup(msg->wfd_subelems);
 +      }
 +
 +      if (dev->flags & P2P_DEV_PROBE_REQ_ONLY) {
 +              dev->flags &= ~P2P_DEV_PROBE_REQ_ONLY;
 +              p2p_dbg(p2p, "Completed device entry based on data from GO Negotiation Request");
 +      } else {
 +              p2p_dbg(p2p, "Created device entry based on GO Neg Req: "
 +                      MACSTR " dev_capab=0x%x group_capab=0x%x name='%s' "
 +                      "listen_freq=%d",
 +                      MAC2STR(dev->info.p2p_device_addr),
 +                      dev->info.dev_capab, dev->info.group_capab,
 +                      dev->info.device_name, dev->listen_freq);
 +      }
 +
 +      dev->flags &= ~P2P_DEV_GROUP_CLIENT_ONLY;
 +
 +      if (dev->flags & P2P_DEV_USER_REJECTED) {
 +              p2p_dbg(p2p, "Do not report rejected device");
 +              return;
 +      }
 +
 +      p2p->cfg->dev_found(p2p->cfg->cb_ctx, addr, &dev->info,
 +                          !(dev->flags & P2P_DEV_REPORTED_ONCE));
 +      dev->flags |= P2P_DEV_REPORTED | P2P_DEV_REPORTED_ONCE;
 +}
 +
 +
 +void p2p_build_ssid(struct p2p_data *p2p, u8 *ssid, size_t *ssid_len)
 +{
 +      os_memcpy(ssid, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN);
 +      p2p_random((char *) &ssid[P2P_WILDCARD_SSID_LEN], 2);
 +      os_memcpy(&ssid[P2P_WILDCARD_SSID_LEN + 2],
 +                p2p->cfg->ssid_postfix, p2p->cfg->ssid_postfix_len);
 +      *ssid_len = P2P_WILDCARD_SSID_LEN + 2 + p2p->cfg->ssid_postfix_len;
 +}
 +
 +
 +int p2p_go_params(struct p2p_data *p2p, struct p2p_go_neg_results *params)
 +{
 +      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;
 +}
 +
 +
 +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;
 +
 +      p2p_dbg(p2p, "GO Negotiation with " MACSTR " completed (%s will be GO)",
 +              MAC2STR(peer->info.p2p_device_addr), go ? "local end" : "peer");
 +
 +      os_memset(&res, 0, sizeof(res));
 +      res.role_go = go;
 +      os_memcpy(res.peer_device_addr, peer->info.p2p_device_addr, ETH_ALEN);
 +      os_memcpy(res.peer_interface_addr, peer->intended_addr, ETH_ALEN);
 +      res.wps_method = peer->wps_method;
 +      if (peer->flags & P2P_DEV_PREFER_PERSISTENT_GROUP) {
 +              if (peer->flags & P2P_DEV_PREFER_PERSISTENT_RECONN)
 +                      res.persistent_group = 2;
 +              else
 +                      res.persistent_group = 1;
 +      }
 +
 +      if (go) {
 +              /* Setup AP mode for WPS provisioning */
 +              res.freq = p2p_channel_to_freq(p2p->op_reg_class,
 +                                             p2p->op_channel);
 +              os_memcpy(res.ssid, p2p->ssid, p2p->ssid_len);
 +              res.ssid_len = p2p->ssid_len;
 +              p2p_random(res.passphrase, p2p->cfg->passphrase_len);
 +      } else {
 +              res.freq = peer->oper_freq;
 +              if (p2p->ssid_len) {
 +                      os_memcpy(res.ssid, p2p->ssid, p2p->ssid_len);
 +                      res.ssid_len = p2p->ssid_len;
 +              }
 +      }
 +
 +      p2p_channels_dump(p2p, "own channels", &p2p->channels);
 +      p2p_channels_dump(p2p, "peer channels", &peer->channels);
 +      p2p_channels_intersect(&p2p->channels, &peer->channels,
 +                             &intersection);
 +      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);
 +}
 +
 +
 +static void p2p_rx_p2p_action(struct p2p_data *p2p, const u8 *sa,
 +                            const u8 *data, size_t len, int rx_freq)
 +{
 +      p2p_dbg(p2p, "RX P2P Public Action from " MACSTR, MAC2STR(sa));
 +      wpa_hexdump(MSG_MSGDUMP, "P2P: P2P Public Action contents", data, len);
 +
 +      if (len < 1)
 +              return;
 +
 +      switch (data[0]) {
 +      case P2P_GO_NEG_REQ:
 +              p2p_process_go_neg_req(p2p, sa, data + 1, len - 1, rx_freq);
 +              break;
 +      case P2P_GO_NEG_RESP:
 +              p2p_process_go_neg_resp(p2p, sa, data + 1, len - 1, rx_freq);
 +              break;
 +      case P2P_GO_NEG_CONF:
 +              p2p_process_go_neg_conf(p2p, sa, data + 1, len - 1);
 +              break;
 +      case P2P_INVITATION_REQ:
 +              p2p_process_invitation_req(p2p, sa, data + 1, len - 1,
 +                                         rx_freq);
 +              break;
 +      case P2P_INVITATION_RESP:
 +              p2p_process_invitation_resp(p2p, sa, data + 1, len - 1);
 +              break;
 +      case P2P_PROV_DISC_REQ:
 +              p2p_process_prov_disc_req(p2p, sa, data + 1, len - 1, rx_freq);
 +              break;
 +      case P2P_PROV_DISC_RESP:
 +              p2p_process_prov_disc_resp(p2p, sa, data + 1, len - 1);
 +              break;
 +      case P2P_DEV_DISC_REQ:
 +              p2p_process_dev_disc_req(p2p, sa, data + 1, len - 1, rx_freq);
 +              break;
 +      case P2P_DEV_DISC_RESP:
 +              p2p_process_dev_disc_resp(p2p, sa, data + 1, len - 1);
 +              break;
 +      default:
 +              p2p_dbg(p2p, "Unsupported P2P Public Action frame type %d",
 +                      data[0]);
 +              break;
 +      }
 +}
 +
 +
 +static void p2p_rx_action_public(struct p2p_data *p2p, const u8 *da,
 +                               const u8 *sa, const u8 *bssid, const u8 *data,
 +                               size_t len, int freq)
 +{
 +      if (len < 1)
 +              return;
 +
 +      switch (data[0]) {
 +      case WLAN_PA_VENDOR_SPECIFIC:
 +              data++;
 +              len--;
 +              if (len < 4)
 +                      return;
 +              if (WPA_GET_BE32(data) != P2P_IE_VENDOR_TYPE)
 +                      return;
 +
 +              data += 4;
 +              len -= 4;
 +
 +              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);
 +              break;
 +      case WLAN_PA_GAS_INITIAL_RESP:
 +              p2p_rx_gas_initial_resp(p2p, sa, data + 1, len - 1, freq);
 +              break;
 +      case WLAN_PA_GAS_COMEBACK_REQ:
 +              p2p_rx_gas_comeback_req(p2p, sa, data + 1, len - 1, freq);
 +              break;
 +      case WLAN_PA_GAS_COMEBACK_RESP:
 +              p2p_rx_gas_comeback_resp(p2p, sa, data + 1, len - 1, freq);
 +              break;
 +      }
 +}
 +
 +
 +void p2p_rx_action(struct p2p_data *p2p, const u8 *da, const u8 *sa,
 +                 const u8 *bssid, u8 category,
 +                 const u8 *data, size_t len, int freq)
 +{
 +      if (category == WLAN_ACTION_PUBLIC) {
 +              p2p_rx_action_public(p2p, da, sa, bssid, data, len, freq);
 +              return;
 +      }
 +
 +      if (category != WLAN_ACTION_VENDOR_SPECIFIC)
 +              return;
 +
 +      if (len < 4)
 +              return;
 +
 +      if (WPA_GET_BE32(data) != P2P_IE_VENDOR_TYPE)
 +              return;
 +      data += 4;
 +      len -= 4;
 +
 +      /* P2P action frame */
 +      p2p_dbg(p2p, "RX P2P Action from " MACSTR, MAC2STR(sa));
 +      wpa_hexdump(MSG_MSGDUMP, "P2P: P2P Action contents", data, len);
 +
 +      if (len < 1)
 +              return;
 +      switch (data[0]) {
 +      case P2P_NOA:
 +              p2p_dbg(p2p, "Received P2P Action - Notice of Absence");
 +              /* TODO */
 +              break;
 +      case P2P_PRESENCE_REQ:
 +              p2p_process_presence_req(p2p, da, sa, data + 1, len - 1, freq);
 +              break;
 +      case P2P_PRESENCE_RESP:
 +              p2p_process_presence_resp(p2p, da, sa, data + 1, len - 1);
 +              break;
 +      case P2P_GO_DISC_REQ:
 +              p2p_process_go_disc_req(p2p, da, sa, data + 1, len - 1, freq);
 +              break;
 +      default:
 +              p2p_dbg(p2p, "Received P2P Action - unknown type %u", data[0]);
 +              break;
 +      }
 +}
 +
 +
 +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);
 +}
 +
 +
 +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_dev_pw_id);
 +}
 +
 +
 +static void p2p_add_dev_from_probe_req(struct p2p_data *p2p, const u8 *addr,
 +                                     const u8 *ie, size_t ie_len)
 +{
 +      struct p2p_message msg;
 +      struct p2p_device *dev;
 +
 +      os_memset(&msg, 0, sizeof(msg));
 +      if (p2p_parse_ies(ie, ie_len, &msg) < 0 || msg.p2p_attributes == NULL)
 +      {
 +              p2p_parse_free(&msg);
 +              return; /* not a P2P probe */
 +      }
 +
 +      if (msg.ssid == NULL || msg.ssid[1] != P2P_WILDCARD_SSID_LEN ||
 +          os_memcmp(msg.ssid + 2, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN)
 +          != 0) {
 +              /* The Probe Request is not part of P2P Device Discovery. It is
 +               * not known whether the source address of the frame is the P2P
 +               * Device Address or P2P Interface Address. Do not add a new
 +               * peer entry based on this frames.
 +               */
 +              p2p_parse_free(&msg);
 +              return;
 +      }
 +
 +      dev = p2p_get_device(p2p, addr);
 +      if (dev) {
 +              if (dev->country[0] == 0 && msg.listen_channel)
 +                      os_memcpy(dev->country, msg.listen_channel, 3);
 +              os_get_reltime(&dev->last_seen);
 +              p2p_parse_free(&msg);
 +              return; /* already known */
 +      }
 +
 +      dev = p2p_create_device(p2p, addr);
 +      if (dev == NULL) {
 +              p2p_parse_free(&msg);
 +              return;
 +      }
 +
 +      os_get_reltime(&dev->last_seen);
 +      dev->flags |= P2P_DEV_PROBE_REQ_ONLY;
 +
 +      if (msg.listen_channel) {
 +              os_memcpy(dev->country, msg.listen_channel, 3);
 +              dev->listen_freq = p2p_channel_to_freq(msg.listen_channel[3],
 +                                                     msg.listen_channel[4]);
 +      }
 +
 +      p2p_copy_wps_info(p2p, dev, 1, &msg);
 +
 +      if (msg.wfd_subelems) {
 +              wpabuf_free(dev->info.wfd_subelems);
 +              dev->info.wfd_subelems = wpabuf_dup(msg.wfd_subelems);
 +      }
 +
 +      p2p_parse_free(&msg);
 +
 +      p2p_dbg(p2p, "Created device entry based on Probe Req: " MACSTR
 +              " dev_capab=0x%x group_capab=0x%x name='%s' listen_freq=%d",
 +              MAC2STR(dev->info.p2p_device_addr), dev->info.dev_capab,
 +              dev->info.group_capab, dev->info.device_name,
 +              dev->listen_freq);
 +}
 +
 +
 +struct p2p_device * p2p_add_dev_from_go_neg_req(struct p2p_data *p2p,
 +                                              const u8 *addr,
 +                                              struct p2p_message *msg)
 +{
 +      struct p2p_device *dev;
 +
 +      dev = p2p_get_device(p2p, addr);
 +      if (dev) {
 +              os_get_reltime(&dev->last_seen);
 +              return dev; /* already known */
 +      }
 +
 +      dev = p2p_create_device(p2p, addr);
 +      if (dev == NULL)
 +              return NULL;
 +
 +      p2p_add_dev_info(p2p, addr, dev, msg);
 +
 +      return dev;
 +}
 +
 +
 +static int dev_type_match(const u8 *dev_type, const u8 *req_dev_type)
 +{
 +      if (os_memcmp(dev_type, req_dev_type, WPS_DEV_TYPE_LEN) == 0)
 +              return 1;
 +      if (os_memcmp(dev_type, req_dev_type, 2) == 0 &&
 +          WPA_GET_BE32(&req_dev_type[2]) == 0 &&
 +          WPA_GET_BE16(&req_dev_type[6]) == 0)
 +              return 1; /* Category match with wildcard OUI/sub-category */
 +      return 0;
 +}
 +
 +
 +int dev_type_list_match(const u8 *dev_type, const u8 *req_dev_type[],
 +                      size_t num_req_dev_type)
 +{
 +      size_t i;
 +      for (i = 0; i < num_req_dev_type; i++) {
 +              if (dev_type_match(dev_type, req_dev_type[i]))
 +                      return 1;
 +      }
 +      return 0;
 +}
 +
 +
 +/**
 + * p2p_match_dev_type - Match local device type with requested type
 + * @p2p: P2P module context from p2p_init()
 + * @wps: WPS TLVs from Probe Request frame (concatenated WPS IEs)
 + * Returns: 1 on match, 0 on mismatch
 + *
 + * This function can be used to match the Requested Device Type attribute in
 + * WPS IE with the local device types for deciding whether to reply to a Probe
 + * Request frame.
 + */
 +int p2p_match_dev_type(struct p2p_data *p2p, struct wpabuf *wps)
 +{
 +      struct wps_parse_attr attr;
 +      size_t i;
 +
 +      if (wps_parse_msg(wps, &attr))
 +              return 1; /* assume no Requested Device Type attributes */
 +
 +      if (attr.num_req_dev_type == 0)
 +              return 1; /* no Requested Device Type attributes -> match */
 +
 +      if (dev_type_list_match(p2p->cfg->pri_dev_type, attr.req_dev_type,
 +                              attr.num_req_dev_type))
 +              return 1; /* Own Primary Device Type matches */
 +
 +      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 */
 +      }
 +
 +      /* No matching device type found */
 +      return 0;
 +}
 +
 +
-       if (p2p->query_count)
++struct wpabuf * p2p_build_probe_resp_ies(struct p2p_data *p2p,
++                                       const u8 *query_hash,
++                                       u8 query_count)
 +{
 +      struct wpabuf *buf;
 +      u8 *len;
 +      int pw_id = -1;
 +      size_t extra = 0;
 +
 +#ifdef CONFIG_WIFI_DISPLAY
 +      if (p2p->wfd_ie_probe_resp)
 +              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) {
-               p2p_buf_add_service_instance(buf, p2p, p2p->query_count,
-                                            p2p->query_hash,
++      if (query_count)
 +              extra += MAX_SVC_ADV_IE_LEN;
 +
 +      buf = wpabuf_alloc(1000 + extra);
 +      if (buf == NULL)
 +              return NULL;
 +
 +      if (p2p->go_neg_peer) {
 +              /* Advertise immediate availability of WPS credential */
 +              pw_id = p2p_wps_method_pw_id(p2p->go_neg_peer->wps_method);
 +      }
 +
 +      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 &
 +                             ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY, 0);
 +      if (p2p->ext_listen_interval)
 +              p2p_buf_add_ext_listen_timing(buf, p2p->ext_listen_period,
 +                                            p2p->ext_listen_interval);
 +      p2p_buf_add_device_info(buf, p2p, NULL);
 +      p2p_buf_update_ie_hdr(buf, len);
 +
-       /* 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;
++      if (query_count) {
++              p2p_buf_add_service_instance(buf, p2p, query_count, 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;
++      int any_wfa;
 +
 +      p2p_dbg(p2p, "ASP find - ASP list: %p", p2p->p2ps_adv_list);
 +
-               p2p_dbg(p2p, "ASP hash: %x =? %x", hash[0], adv_data->hash[0]);
++      /* Wildcard org.wi-fi.wfds matches any WFA spec defined service */
++      any_wfa = os_memcmp(hash, p2p->wild_card_hash, P2PS_HASH_LEN) == 0;
 +
 +      adv_data = p2p->p2ps_adv_list;
 +      while (adv_data) {
-                       return 1;
 +              if (os_memcmp(hash, adv_data->hash, P2PS_HASH_LEN) == 0)
-               const u8 *bssid, const u8 *ie, size_t ie_len)
++                      return 1; /* exact hash match */
++              if (any_wfa &&
++                  os_strncmp(adv_data->svc_name, P2PS_WILD_HASH_STR,
++                             os_strlen(P2PS_WILD_HASH_STR)) == 0)
++                      return 1; /* WFA service match */
 +              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,
-       p2p->p2ps_svc_found = 0;
++              const u8 *bssid, const u8 *ie, size_t ie_len,
++              unsigned int rx_freq)
 +{
 +      struct ieee802_11_elems elems;
 +      struct wpabuf *buf;
 +      struct ieee80211_mgmt *resp;
 +      struct p2p_message msg;
 +      struct wpabuf *ies;
++      u8 channel, op_class;
 +
 +      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;
 +      }
 +
 +      if (dst && !is_broadcast_ether_addr(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;
 +      }
 +
 +      if (elems.ssid == NULL || elems.ssid_len != P2P_WILDCARD_SSID_LEN ||
 +          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;
 +      }
 +
-               u8 *dest = p2p->query_hash;
 +      if (msg.service_hash && msg.service_hash_count) {
 +              const u8 *hash = msg.service_hash;
-               p2p->query_count = 0;
 +              u8 i;
++              int p2ps_svc_found = 0;
++
++              p2p_dbg(p2p, "in_listen=%d drv_in_listen=%d when received P2PS Probe Request at %u MHz; own Listen channel %u, pending listen freq %u MHz",
++                      p2p->in_listen, p2p->drv_in_listen, rx_freq,
++                      p2p->cfg->channel, p2p->pending_listen_freq);
++
++              if (!p2p->in_listen && !p2p->drv_in_listen &&
++                  p2p->pending_listen_freq && rx_freq &&
++                  rx_freq != p2p->pending_listen_freq) {
++                      p2p_dbg(p2p, "Do not reply to Probe Request frame that was received on %u MHz while waiting to start Listen state on %u MHz",
++                              rx_freq, p2p->pending_listen_freq);
++                      p2p_parse_free(&msg);
++                      return P2P_PREQ_NOT_LISTEN;
++              }
 +
-                               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;
-                               }
 +              for (i = 0; i < msg.service_hash_count; i++) {
 +                      if (p2p_service_find_asp(p2p, hash)) {
-               p2p_dbg(p2p, "ASP adv found: %d", p2p->p2ps_svc_found);
++                              p2p_dbg(p2p, "Service Hash match found: "
++                                      MACSTR, MAC2STR(hash));
++                              p2ps_svc_found = 1;
++                              break;
 +                      }
 +                      hash += P2PS_HASH_LEN;
 +              }
 +
-               if (!p2p->p2ps_svc_found) {
 +              /* Probed hash unknown */
-               p2p->query_count = 0;
++              if (!p2ps_svc_found) {
++                      p2p_dbg(p2p, "No Service Hash match found");
 +                      p2p_parse_free(&msg);
 +                      return P2P_PREQ_NOT_PROCESSED;
 +              }
 +      } else {
 +              /* This is not a P2PS Probe Request */
-       p2p_parse_free(&msg);
 +              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;
 +      }
 +
 +      /* Check Requested Device Type match */
 +      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;
 +      }
-       ies = p2p_build_probe_resp_ies(p2p);
 +
 +      if (!p2p->cfg->send_probe_resp) {
 +              /* Response generated elsewhere */
 +              p2p_dbg(p2p, "Probe Resp generated elsewhere - do not generate additional response");
++              p2p_parse_free(&msg);
 +              return P2P_PREQ_NOT_PROCESSED;
 +      }
 +
 +      p2p_dbg(p2p, "Reply to P2P Probe Request in Listen state");
 +
 +      /*
 +       * We do not really have a specific BSS that this frame is advertising,
 +       * so build a frame that has some information in valid format. This is
 +       * really only used for discovery purposes, not to learn exact BSS
 +       * parameters.
 +       */
-       resp = NULL;
-       resp = wpabuf_put(buf, resp->u.probe_resp.variable - (u8 *) resp);
++      ies = p2p_build_probe_resp_ies(p2p, msg.service_hash,
++                                     msg.service_hash_count);
++      p2p_parse_free(&msg);
 +      if (ies == NULL)
 +              return P2P_PREQ_NOT_PROCESSED;
 +
 +      buf = wpabuf_alloc(200 + wpabuf_len(ies));
 +      if (buf == NULL) {
 +              wpabuf_free(ies);
 +              return P2P_PREQ_NOT_PROCESSED;
 +      }
 +
-       wpabuf_put_u8(buf, p2p->cfg->channel);
++      resp = wpabuf_put(buf, offsetof(struct ieee80211_mgmt,
++                                      u.probe_resp.variable));
 +
 +      resp->frame_control = host_to_le16((WLAN_FC_TYPE_MGMT << 2) |
 +                                         (WLAN_FC_STYPE_PROBE_RESP << 4));
 +      os_memcpy(resp->da, addr, ETH_ALEN);
 +      os_memcpy(resp->sa, p2p->cfg->dev_addr, ETH_ALEN);
 +      os_memcpy(resp->bssid, p2p->cfg->dev_addr, ETH_ALEN);
 +      resp->u.probe_resp.beacon_int = host_to_le16(100);
 +      /* hardware or low-level driver will setup seq_ctrl and timestamp */
 +      resp->u.probe_resp.capab_info =
 +              host_to_le16(WLAN_CAPABILITY_SHORT_PREAMBLE |
 +                           WLAN_CAPABILITY_PRIVACY |
 +                           WLAN_CAPABILITY_SHORT_SLOT_TIME);
 +
 +      wpabuf_put_u8(buf, WLAN_EID_SSID);
 +      wpabuf_put_u8(buf, P2P_WILDCARD_SSID_LEN);
 +      wpabuf_put_data(buf, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN);
 +
 +      wpabuf_put_u8(buf, WLAN_EID_SUPP_RATES);
 +      wpabuf_put_u8(buf, 8);
 +      wpabuf_put_u8(buf, (60 / 5) | 0x80);
 +      wpabuf_put_u8(buf, 90 / 5);
 +      wpabuf_put_u8(buf, (120 / 5) | 0x80);
 +      wpabuf_put_u8(buf, 180 / 5);
 +      wpabuf_put_u8(buf, (240 / 5) | 0x80);
 +      wpabuf_put_u8(buf, 360 / 5);
 +      wpabuf_put_u8(buf, 480 / 5);
 +      wpabuf_put_u8(buf, 540 / 5);
 +
++      if (!rx_freq) {
++              channel = p2p->cfg->channel;
++      } else if (p2p_freq_to_channel(rx_freq, &op_class, &channel)) {
++              wpabuf_free(ies);
++              wpabuf_free(buf);
++              return P2P_PREQ_NOT_PROCESSED;
++      }
++
 +      wpabuf_put_u8(buf, WLAN_EID_DS_PARAMS);
 +      wpabuf_put_u8(buf, 1);
-       p2p->cfg->send_probe_resp(p2p->cfg->cb_ctx, buf);
++      wpabuf_put_u8(buf, channel);
 +
 +      wpabuf_put_buf(buf, ies);
 +      wpabuf_free(ies);
 +
-       return P2P_PREQ_NOT_PROCESSED;
++      p2p->cfg->send_probe_resp(p2p->cfg->cb_ctx, buf, rx_freq);
 +
 +      wpabuf_free(buf);
 +
-                const u8 *bssid, const u8 *ie, size_t ie_len)
++      return P2P_PREQ_PROCESSED;
 +}
 +
 +
 +enum p2p_probe_req_status
 +p2p_probe_req_rx(struct p2p_data *p2p, const u8 *addr, const u8 *dst,
-       res = p2p_reply_probe(p2p, addr, dst, bssid, ie, ie_len);
-       p2p->query_count = 0;
++               const u8 *bssid, const u8 *ie, size_t ie_len,
++               unsigned int rx_freq)
 +{
 +      enum p2p_probe_req_status res;
 +
 +      p2p_add_dev_from_probe_req(p2p, addr, ie, ie_len);
 +
-               return P2P_PREQ_PROCESSED;
++      res = p2p_reply_probe(p2p, addr, dst, bssid, ie, ie_len, rx_freq);
++      if (res != P2P_PREQ_PROCESSED && res != P2P_PREQ_NOT_PROCESSED)
++              return res;
 +
++      /*
++       * Activate a pending GO Negotiation/Invite flow if a received Probe
++       * Request frame is from an expected peer. Some devices may share the
++       * same address for P2P and non-P2P STA running simultaneously. The
++       * P2P_PREQ_PROCESSED and P2P_PREQ_NOT_PROCESSED p2p_reply_probe()
++       * return values verified above ensure we are handling a Probe Request
++       * frame from a P2P peer.
++       */
 +      if ((p2p->state == P2P_CONNECT || p2p->state == P2P_CONNECT_LISTEN) &&
 +          p2p->go_neg_peer &&
 +          os_memcmp(addr, p2p->go_neg_peer->info.p2p_device_addr, ETH_ALEN)
 +          == 0 &&
 +          !(p2p->go_neg_peer->flags & P2P_DEV_WAIT_GO_NEG_CONFIRM)) {
 +              /* Received a Probe Request from GO Negotiation peer */
 +              p2p_dbg(p2p, "Found GO Negotiation peer - try to start GO negotiation from timeout");
 +              eloop_cancel_timeout(p2p_go_neg_start, p2p, NULL);
 +              eloop_register_timeout(0, 0, p2p_go_neg_start, p2p, NULL);
-               return P2P_PREQ_PROCESSED;
++              return res;
 +      }
 +
 +      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);
-                       const char *svc_info)
++              return res;
 +      }
 +
 +      return res;
 +}
 +
 +
 +static int p2p_assoc_req_ie_wlan_ap(struct p2p_data *p2p, const u8 *bssid,
 +                                  u8 *buf, size_t len, struct wpabuf *p2p_ie)
 +{
 +      struct wpabuf *tmp;
 +      u8 *lpos;
 +      size_t tmplen;
 +      int res;
 +      u8 group_capab;
++      struct p2p_message msg;
 +
 +      if (p2p_ie == NULL)
 +              return 0; /* WLAN AP is not a P2P manager */
 +
++      os_memset(&msg, 0, sizeof(msg));
++      if (p2p_parse_p2p_ie(p2p_ie, &msg) < 0)
++              return 0;
++
++      p2p_dbg(p2p, "BSS P2P manageability %s",
++              msg.manageability ? "enabled" : "disabled");
++
++      if (!msg.manageability)
++              return 0;
++
 +      /*
 +       * (Re)Association Request - P2P IE
 +       * P2P Capability attribute (shall be present)
 +       * P2P Interface attribute (present if concurrent device and
 +       *      P2P Management is enabled)
 +       */
 +      tmp = wpabuf_alloc(200);
 +      if (tmp == NULL)
 +              return -1;
 +
 +      lpos = p2p_buf_add_ie_hdr(tmp);
 +      group_capab = 0;
 +      if (p2p->num_groups > 0) {
 +              group_capab |= P2P_GROUP_CAPAB_GROUP_OWNER;
 +              if ((p2p->dev_capab & P2P_DEV_CAPAB_CONCURRENT_OPER) &&
 +                  (p2p->dev_capab & P2P_DEV_CAPAB_INFRA_MANAGED) &&
 +                  p2p->cross_connect)
 +                      group_capab |= P2P_GROUP_CAPAB_CROSS_CONN;
 +      }
 +      p2p_buf_add_capability(tmp, p2p->dev_capab, group_capab);
 +      if ((p2p->dev_capab & P2P_DEV_CAPAB_CONCURRENT_OPER) &&
 +          (p2p->dev_capab & P2P_DEV_CAPAB_INFRA_MANAGED))
 +              p2p_buf_add_p2p_interface(tmp, p2p);
 +      p2p_buf_update_ie_hdr(tmp, lpos);
 +
 +      tmplen = wpabuf_len(tmp);
 +      if (tmplen > len)
 +              res = -1;
 +      else {
 +              os_memcpy(buf, wpabuf_head(tmp), tmplen);
 +              res = tmplen;
 +      }
 +      wpabuf_free(tmp);
 +
 +      return res;
 +}
 +
 +
 +int p2p_assoc_req_ie(struct p2p_data *p2p, const u8 *bssid, u8 *buf,
 +                   size_t len, int p2p_group, struct wpabuf *p2p_ie)
 +{
 +      struct wpabuf *tmp;
 +      u8 *lpos;
 +      struct p2p_device *peer;
 +      size_t tmplen;
 +      int res;
 +      size_t extra = 0;
 +
 +      if (!p2p_group)
 +              return p2p_assoc_req_ie_wlan_ap(p2p, bssid, buf, len, p2p_ie);
 +
 +#ifdef CONFIG_WIFI_DISPLAY
 +      if (p2p->wfd_ie_assoc_req)
 +              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)
 +       * Extended Listen Timing (may be present)
 +       * P2P Device Info attribute (shall be present)
 +       */
 +      tmp = wpabuf_alloc(200 + extra);
 +      if (tmp == NULL)
 +              return -1;
 +
 +#ifdef CONFIG_WIFI_DISPLAY
 +      if (p2p->wfd_ie_assoc_req)
 +              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);
 +      p2p_buf_add_capability(tmp, p2p->dev_capab, 0);
 +      if (p2p->ext_listen_interval)
 +              p2p_buf_add_ext_listen_timing(tmp, p2p->ext_listen_period,
 +                                            p2p->ext_listen_interval);
 +      p2p_buf_add_device_info(tmp, p2p, peer);
 +      p2p_buf_update_ie_hdr(tmp, lpos);
 +
 +      tmplen = wpabuf_len(tmp);
 +      if (tmplen > len)
 +              res = -1;
 +      else {
 +              os_memcpy(buf, wpabuf_head(tmp), tmplen);
 +              res = tmplen;
 +      }
 +      wpabuf_free(tmp);
 +
 +      return res;
 +}
 +
 +
 +int p2p_scan_result_text(const u8 *ies, size_t ies_len, char *buf, char *end)
 +{
 +      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 0;
 +
 +      ret = p2p_attr_text(p2p_ie, buf, end);
 +      wpabuf_free(p2p_ie);
 +      return ret;
 +}
 +
 +
 +struct p2ps_advertisement *
 +p2p_service_p2ps_id(struct p2p_data *p2p, u32 adv_id)
 +{
 +      struct p2ps_advertisement *adv_data;
 +
 +      if (!p2p)
 +              return NULL;
 +
 +      adv_data = p2p->p2ps_adv_list;
 +      while (adv_data) {
 +              if (adv_data->id == adv_id)
 +                      return adv_data;
 +              adv_data = adv_data->next;
 +      }
 +
 +      return NULL;
 +}
 +
 +
 +int p2p_service_del_asp(struct p2p_data *p2p, u32 adv_id)
 +{
 +      struct p2ps_advertisement *adv_data;
 +      struct p2ps_advertisement **prior;
 +
 +      if (!p2p)
 +              return -1;
 +
 +      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,
-       if (!p2p || !adv_str || !adv_str[0])
++                      const char *svc_info, const u8 *cpt_priority)
 +{
 +      struct p2ps_advertisement *adv_data, *tmp, **prev;
 +      u8 buf[P2PS_HASH_LEN];
 +      size_t adv_data_len, adv_len, info_len = 0;
++      int i;
 +
-               "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);
++      if (!p2p || !adv_str || !adv_str[0] || !cpt_priority)
 +              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);
 +
++      for (i = 0; cpt_priority[i] && i < P2PS_FEATURE_CAPAB_CPT_MAX; i++) {
++              adv_data->cpt_priority[i] = cpt_priority[i];
++              adv_data->cpt_mask |= cpt_priority[i];
++      }
++
 +      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,
-       eloop_register_timeout(P2P_PEER_EXPIRATION_INTERVAL, 0,
-                              p2p_expiration_timeout, p2p, NULL);
++              "Added ASP advertisement adv_id=0x%x config_methods=0x%x svc_state=0x%x adv_str='%s' cpt_mask=0x%x",
++              adv_id, adv_data->config_methods, svc_state, adv_str,
++              adv_data->cpt_mask);
 +
 +      return 0;
 +}
 +
 +
++void p2p_service_flush_asp(struct p2p_data *p2p)
++{
++      struct p2ps_advertisement *adv, *prev;
++
++      if (!p2p)
++              return;
++
++      adv = p2p->p2ps_adv_list;
++      while (adv) {
++              prev = adv;
++              adv = adv->next;
++              os_free(prev);
++      }
++
++      p2p->p2ps_adv_list = NULL;
++      p2p_dbg(p2p, "All ASP advertisements flushed");
++}
++
++
 +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)
 +{
 +      p2p->go_neg_peer = NULL;
 +      p2p_clear_timeout(p2p);
 +      p2p_set_state(p2p, P2P_IDLE);
 +}
 +
 +
 +void p2p_wps_success_cb(struct p2p_data *p2p, const u8 *mac_addr)
 +{
 +      if (p2p->go_neg_peer == NULL) {
 +              p2p_dbg(p2p, "No pending Group Formation - ignore WPS registration success notification");
 +              return; /* No pending Group Formation */
 +      }
 +
 +      if (os_memcmp(mac_addr, p2p->go_neg_peer->intended_addr, ETH_ALEN) !=
 +          0) {
 +              p2p_dbg(p2p, "Ignore WPS registration success notification for "
 +                      MACSTR " (GO Negotiation peer " MACSTR ")",
 +                      MAC2STR(mac_addr),
 +                      MAC2STR(p2p->go_neg_peer->intended_addr));
 +              return; /* Ignore unexpected peer address */
 +      }
 +
 +      p2p_dbg(p2p, "Group Formation completed successfully with " MACSTR,
 +              MAC2STR(mac_addr));
 +
 +      p2p_clear_go_neg(p2p);
 +}
 +
 +
 +void p2p_group_formation_failed(struct p2p_data *p2p)
 +{
 +      if (p2p->go_neg_peer == NULL) {
 +              p2p_dbg(p2p, "No pending Group Formation - ignore group formation failure notification");
 +              return; /* No pending Group Formation */
 +      }
 +
 +      p2p_dbg(p2p, "Group Formation failed with " MACSTR,
 +              MAC2STR(p2p->go_neg_peer->intended_addr));
 +
 +      p2p_clear_go_neg(p2p);
 +}
 +
 +
 +struct p2p_data * p2p_init(const struct p2p_config *cfg)
 +{
 +      struct p2p_data *p2p;
 +
 +      if (cfg->max_peers < 1 ||
 +          cfg->passphrase_len < 8 || cfg->passphrase_len > 63)
 +              return NULL;
 +
 +      p2p = os_zalloc(sizeof(*p2p) + sizeof(*cfg));
 +      if (p2p == NULL)
 +              return NULL;
 +      p2p->cfg = (struct p2p_config *) (p2p + 1);
 +      os_memcpy(p2p->cfg, cfg, sizeof(*cfg));
 +      if (cfg->dev_name)
 +              p2p->cfg->dev_name = os_strdup(cfg->dev_name);
 +      if (cfg->manufacturer)
 +              p2p->cfg->manufacturer = os_strdup(cfg->manufacturer);
 +      if (cfg->model_name)
 +              p2p->cfg->model_name = os_strdup(cfg->model_name);
 +      if (cfg->model_number)
 +              p2p->cfg->model_number = os_strdup(cfg->model_number);
 +      if (cfg->serial_number)
 +              p2p->cfg->serial_number = os_strdup(cfg->serial_number);
 +      if (cfg->pref_chan) {
 +              p2p->cfg->pref_chan = os_malloc(cfg->num_pref_chan *
 +                                              sizeof(struct p2p_channel));
 +              if (p2p->cfg->pref_chan) {
 +                      os_memcpy(p2p->cfg->pref_chan, cfg->pref_chan,
 +                                cfg->num_pref_chan *
 +                                sizeof(struct p2p_channel));
 +              } else
 +                      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;
 +
 +      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;
 +      p2p->dev_capab |= P2P_DEV_CAPAB_INVITATION_PROCEDURE;
 +      if (cfg->concurrent_operations)
 +              p2p->dev_capab |= P2P_DEV_CAPAB_CONCURRENT_OPER;
 +      p2p->dev_capab |= P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY;
 +
 +      dl_list_init(&p2p->devices);
 +
-       struct p2ps_advertisement *adv, *prev;
 +      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;
 +}
 +
 +
 +void p2p_deinit(struct p2p_data *p2p)
 +{
-       eloop_cancel_timeout(p2p_expiration_timeout, p2p, NULL);
 +#ifdef CONFIG_WIFI_DISPLAY
 +      wpabuf_free(p2p->wfd_ie_beacon);
 +      wpabuf_free(p2p->wfd_ie_probe_req);
 +      wpabuf_free(p2p->wfd_ie_probe_resp);
 +      wpabuf_free(p2p->wfd_ie_assoc_req);
 +      wpabuf_free(p2p->wfd_ie_invitation);
 +      wpabuf_free(p2p->wfd_ie_prov_disc_req);
 +      wpabuf_free(p2p->wfd_ie_prov_disc_resp);
 +      wpabuf_free(p2p->wfd_ie_go_neg);
 +      wpabuf_free(p2p->wfd_dev_info);
 +      wpabuf_free(p2p->wfd_assoc_bssid);
 +      wpabuf_free(p2p->wfd_coupled_sink_info);
 +#endif /* CONFIG_WIFI_DISPLAY */
 +
-       os_free(p2p->p2ps_prov);
 +      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);
 +      os_free(p2p->cfg->manufacturer);
 +      os_free(p2p->cfg->model_name);
 +      os_free(p2p->cfg->model_number);
 +      os_free(p2p->cfg->serial_number);
 +      os_free(p2p->cfg->pref_chan);
 +      os_free(p2p->groups);
-       adv = p2p->p2ps_adv_list;
-       while (adv) {
-               prev = adv;
-               adv = adv->next;
-               os_free(prev);
-       }
++      p2ps_prov_free(p2p);
 +      wpabuf_free(p2p->sd_resp);
 +      os_free(p2p->after_scan_tx);
 +      p2p_remove_wps_vendor_extensions(p2p);
 +      os_free(p2p->no_go_freq.range);
-                         "flags=%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n"
++      p2p_service_flush_asp(p2p);
 +
 +      os_free(p2p);
 +}
 +
 +
 +void p2p_flush(struct p2p_data *p2p)
 +{
 +      struct p2p_device *dev, *prev;
 +      p2p_stop_find(p2p);
 +      dl_list_for_each_safe(dev, prev, &p2p->devices, struct p2p_device,
 +                            list) {
 +              dl_list_del(&dev->list);
 +              p2p_device_free(p2p, dev);
 +      }
 +      p2p_free_sd_queries(p2p);
 +      os_free(p2p->after_scan_tx);
 +      p2p->after_scan_tx = NULL;
++      p2p->ssid_set = 0;
 +}
 +
 +
 +int p2p_unauthorize(struct p2p_data *p2p, const u8 *addr)
 +{
 +      struct p2p_device *dev;
 +
 +      dev = p2p_get_device(p2p, addr);
 +      if (dev == NULL)
 +              return -1;
 +
 +      p2p_dbg(p2p, "Unauthorizing " MACSTR, MAC2STR(addr));
 +
 +      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;
 +
 +      /* Check if after_scan_tx is for this peer. If so free it */
 +      if (p2p->after_scan_tx &&
 +          os_memcmp(addr, p2p->after_scan_tx->dst, ETH_ALEN) == 0) {
 +              os_free(p2p->after_scan_tx);
 +              p2p->after_scan_tx = NULL;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +int p2p_set_dev_name(struct p2p_data *p2p, const char *dev_name)
 +{
 +      os_free(p2p->cfg->dev_name);
 +      if (dev_name) {
 +              p2p->cfg->dev_name = os_strdup(dev_name);
 +              if (p2p->cfg->dev_name == NULL)
 +                      return -1;
 +      } else
 +              p2p->cfg->dev_name = NULL;
 +      return 0;
 +}
 +
 +
 +int p2p_set_manufacturer(struct p2p_data *p2p, const char *manufacturer)
 +{
 +      os_free(p2p->cfg->manufacturer);
 +      p2p->cfg->manufacturer = NULL;
 +      if (manufacturer) {
 +              p2p->cfg->manufacturer = os_strdup(manufacturer);
 +              if (p2p->cfg->manufacturer == NULL)
 +                      return -1;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +int p2p_set_model_name(struct p2p_data *p2p, const char *model_name)
 +{
 +      os_free(p2p->cfg->model_name);
 +      p2p->cfg->model_name = NULL;
 +      if (model_name) {
 +              p2p->cfg->model_name = os_strdup(model_name);
 +              if (p2p->cfg->model_name == NULL)
 +                      return -1;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +int p2p_set_model_number(struct p2p_data *p2p, const char *model_number)
 +{
 +      os_free(p2p->cfg->model_number);
 +      p2p->cfg->model_number = NULL;
 +      if (model_number) {
 +              p2p->cfg->model_number = os_strdup(model_number);
 +              if (p2p->cfg->model_number == NULL)
 +                      return -1;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +int p2p_set_serial_number(struct p2p_data *p2p, const char *serial_number)
 +{
 +      os_free(p2p->cfg->serial_number);
 +      p2p->cfg->serial_number = NULL;
 +      if (serial_number) {
 +              p2p->cfg->serial_number = os_strdup(serial_number);
 +              if (p2p->cfg->serial_number == NULL)
 +                      return -1;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +void p2p_set_config_methods(struct p2p_data *p2p, u16 config_methods)
 +{
 +      p2p->cfg->config_methods = config_methods;
 +}
 +
 +
 +void p2p_set_uuid(struct p2p_data *p2p, const u8 *uuid)
 +{
 +      os_memcpy(p2p->cfg->uuid, uuid, 16);
 +}
 +
 +
 +int p2p_set_pri_dev_type(struct p2p_data *p2p, const u8 *pri_dev_type)
 +{
 +      os_memcpy(p2p->cfg->pri_dev_type, pri_dev_type, 8);
 +      return 0;
 +}
 +
 +
 +int p2p_set_sec_dev_types(struct p2p_data *p2p, const u8 dev_types[][8],
 +                        size_t num_dev_types)
 +{
 +      if (num_dev_types > P2P_SEC_DEVICE_TYPES)
 +              num_dev_types = P2P_SEC_DEVICE_TYPES;
 +      p2p->cfg->num_sec_dev_types = num_dev_types;
 +      os_memcpy(p2p->cfg->sec_dev_type, dev_types, num_dev_types * 8);
 +      return 0;
 +}
 +
 +
 +void p2p_remove_wps_vendor_extensions(struct p2p_data *p2p)
 +{
 +      int i;
 +
 +      for (i = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) {
 +              wpabuf_free(p2p->wps_vendor_ext[i]);
 +              p2p->wps_vendor_ext[i] = NULL;
 +      }
 +}
 +
 +
 +int p2p_add_wps_vendor_extension(struct p2p_data *p2p,
 +                               const struct wpabuf *vendor_ext)
 +{
 +      int i;
 +
 +      if (vendor_ext == NULL)
 +              return -1;
 +
 +      for (i = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) {
 +              if (p2p->wps_vendor_ext[i] == NULL)
 +                      break;
 +      }
 +      if (i >= P2P_MAX_WPS_VENDOR_EXT)
 +              return -1;
 +
 +      p2p->wps_vendor_ext[i] = wpabuf_dup(vendor_ext);
 +      if (p2p->wps_vendor_ext[i] == NULL)
 +              return -1;
 +
 +      return 0;
 +}
 +
 +
 +int p2p_set_country(struct p2p_data *p2p, const char *country)
 +{
 +      os_memcpy(p2p->cfg->country, country, 3);
 +      return 0;
 +}
 +
 +
 +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 == 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);
 +}
 +
 +
 +static void p2p_sd_cb(struct p2p_data *p2p, int success)
 +{
 +      p2p_dbg(p2p, "Service Discovery Query TX callback: success=%d",
 +              success);
 +      p2p->pending_action_state = P2P_NO_PENDING_ACTION;
 +
 +      if (!success) {
 +              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");
 +              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);
 +}
 +
 +
 +/**
 + * p2p_retry_pd - Retry any pending provision disc requests in IDLE state
 + * @p2p: P2P module context from p2p_init()
 + */
 +static void p2p_retry_pd(struct p2p_data *p2p)
 +{
 +      struct p2p_device *dev;
 +
 +      /*
 +       * Retry the prov disc req attempt only for the peer that the user had
 +       * requested.
 +       */
 +
 +      dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) {
 +              if (os_memcmp(p2p->pending_pd_devaddr,
 +                            dev->info.p2p_device_addr, ETH_ALEN) != 0)
 +                      continue;
 +              if (!dev->req_config_methods)
 +                      continue;
 +
 +              p2p_dbg(p2p, "Send pending Provision Discovery Request to "
 +                      MACSTR " (config methods 0x%x)",
 +                      MAC2STR(dev->info.p2p_device_addr),
 +                      dev->req_config_methods);
 +              p2p_send_prov_disc_req(p2p, dev,
 +                                     dev->flags & P2P_DEV_PD_FOR_JOIN,
 +                                     p2p->pd_force_freq);
 +              return;
 +      }
 +}
 +
 +
 +static void p2p_prov_disc_cb(struct p2p_data *p2p, int success)
 +{
 +      p2p_dbg(p2p, "Provision Discovery Request TX callback: success=%d",
 +              success);
 +
 +      /*
 +       * Postpone resetting the pending action state till after we actually
 +       * time out. This allows us to take some action like notifying any
 +       * interested parties about no response to the request.
 +       *
 +       * When the timer (below) goes off we check in IDLE, SEARCH, or
 +       * LISTEN_ONLY state, which are the only allowed states to issue a PD
 +       * requests in, if this was still pending and then raise notification.
 +       */
 +
 +      if (!success) {
 +              p2p->pending_action_state = P2P_NO_PENDING_ACTION;
 +
 +              if (p2p->user_initiated_pd &&
 +                  (p2p->state == P2P_SEARCH || p2p->state == P2P_LISTEN_ONLY))
 +              {
 +                      /* Retry request from timeout to avoid busy loops */
 +                      p2p->pending_action_state = P2P_PENDING_PD;
 +                      p2p_set_timeout(p2p, 0, 50000);
 +              } else if (p2p->state != P2P_IDLE)
 +                      p2p_continue_find(p2p);
 +              else if (p2p->user_initiated_pd) {
 +                      p2p->pending_action_state = P2P_PENDING_PD;
 +                      p2p_set_timeout(p2p, 0, 300000);
 +              }
 +              return;
 +      }
 +
 +      /*
 +       * This postponing, of resetting pending_action_state, needs to be
 +       * done only for user initiated PD requests and not internal ones.
 +       */
 +      if (p2p->user_initiated_pd)
 +              p2p->pending_action_state = P2P_PENDING_PD;
 +      else
 +              p2p->pending_action_state = P2P_NO_PENDING_ACTION;
 +
 +      /* Wait for response from the peer */
 +      if (p2p->state == P2P_SEARCH)
 +              p2p_set_state(p2p, P2P_PD_DURING_FIND);
 +      p2p_set_timeout(p2p, 0, 200000);
 +}
 +
 +
 +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_reltime *rx_time, int level, const u8 *ies,
 +                       size_t ies_len)
 +{
 +      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
 +               * stale information, update P2P peers only based on results
 +               * that have based on frames received after the last p2p_find
 +               * operation was started.
 +               */
 +              p2p_dbg(p2p, "Ignore old scan result for " MACSTR
 +                      " (rx_time=%u.%06u)",
 +                      MAC2STR(bssid), (unsigned int) rx_time->sec,
 +                      (unsigned int) rx_time->usec);
 +              return 0;
 +      }
 +
 +      p2p_add_device(p2p, bssid, freq, rx_time, level, ies, ies_len, 1);
 +
 +      return 0;
 +}
 +
 +
 +void p2p_scan_res_handled(struct p2p_data *p2p)
 +{
 +      if (!p2p->p2p_scan_running) {
 +              p2p_dbg(p2p, "p2p_scan was not running, but scan results received");
 +      }
 +      p2p->p2p_scan_running = 0;
 +      eloop_cancel_timeout(p2p_scan_timeout, p2p, NULL);
 +
 +      if (p2p_run_after_scan(p2p))
 +              return;
 +      if (p2p->state == P2P_SEARCH)
 +              p2p_continue_find(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
 +      if (p2p->wfd_ie_probe_req)
 +              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);
 +
 +      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)
 +              p2p_buf_add_listen_channel(ies, p2p->cfg->country,
 +                                         p2p->cfg->reg_class,
 +                                         p2p->cfg->channel);
 +      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);
 +}
 +
 +
 +size_t p2p_scan_ie_buf_len(struct p2p_data *p2p)
 +{
 +      size_t len = 100;
 +
 +#ifdef CONFIG_WIFI_DISPLAY
 +      if (p2p && p2p->wfd_ie_probe_req)
 +              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;
 +}
 +
 +
 +int p2p_ie_text(struct wpabuf *p2p_ie, char *buf, char *end)
 +{
 +      return p2p_attr_text(p2p_ie, buf, end);
 +}
 +
 +
 +static void p2p_go_neg_req_cb(struct p2p_data *p2p, int success)
 +{
 +      struct p2p_device *dev = p2p->go_neg_peer;
 +      int timeout;
 +
 +      p2p_dbg(p2p, "GO Negotiation Request TX callback: success=%d", success);
 +
 +      if (dev == NULL) {
 +              p2p_dbg(p2p, "No pending GO Negotiation");
 +              return;
 +      }
 +
 +      if (success) {
 +              if (dev->flags & P2P_DEV_USER_REJECTED) {
 +                      p2p_set_state(p2p, P2P_IDLE);
 +                      return;
 +              }
 +      } else if (dev->go_neg_req_sent) {
 +              /* Cancel the increment from p2p_connect_send() on failure */
 +              dev->go_neg_req_sent--;
 +      }
 +
 +      if (!success &&
 +          (dev->info.dev_capab & P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY) &&
 +          !is_zero_ether_addr(dev->member_in_go_dev)) {
 +              p2p_dbg(p2p, "Peer " MACSTR " did not acknowledge request - try to use device discoverability through its GO",
 +                      MAC2STR(dev->info.p2p_device_addr));
 +              p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
 +              p2p_send_dev_disc_req(p2p, dev);
 +              return;
 +      }
 +
 +      /*
 +       * Use P2P find, if needed, to find the other device from its listen
 +       * channel.
 +       */
 +      p2p_set_state(p2p, P2P_CONNECT);
 +      timeout = success ? 500000 : 100000;
 +      if (!success && p2p->go_neg_peer &&
 +          (p2p->go_neg_peer->flags & P2P_DEV_PEER_WAITING_RESPONSE)) {
 +              unsigned int r;
 +              /*
 +               * Peer is expected to wait our response and we will skip the
 +               * listen phase. Add some randomness to the wait time here to
 +               * make it less likely to hit cases where we could end up in
 +               * sync with peer not listening.
 +               */
 +              if (os_get_random((u8 *) &r, sizeof(r)) < 0)
 +                      r = 0;
 +              timeout += r % 100000;
 +      }
 +      p2p_set_timeout(p2p, 0, timeout);
 +}
 +
 +
 +static void p2p_go_neg_resp_cb(struct p2p_data *p2p, int success)
 +{
 +      p2p_dbg(p2p, "GO Negotiation Response TX callback: success=%d",
 +              success);
 +      if (!p2p->go_neg_peer && p2p->state == P2P_PROVISIONING) {
 +              p2p_dbg(p2p, "Ignore TX callback event - GO Negotiation is not running anymore");
 +              return;
 +      }
 +      p2p_set_state(p2p, P2P_CONNECT);
 +      p2p_set_timeout(p2p, 0, 500000);
 +}
 +
 +
 +static void p2p_go_neg_resp_failure_cb(struct p2p_data *p2p, int success,
 +                                     const u8 *addr)
 +{
 +      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->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);
 +}
 +
 +
 +static void p2p_go_neg_conf_cb(struct p2p_data *p2p,
 +                             enum p2p_send_action_result result)
 +{
 +      struct p2p_device *dev;
 +
 +      p2p_dbg(p2p, "GO Negotiation Confirm TX callback: result=%d", result);
 +      if (result == P2P_SEND_ACTION_FAILED) {
 +              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
 +               * immediately after having received the frame, we may not see
 +               * an Ack for retries, so just dropping a single frame may
 +               * trigger this. To allow the group formation to succeed if the
 +               * peer did indeed receive the frame, continue regardless of
 +               * the TX status.
 +               */
 +              p2p_dbg(p2p, "Assume GO Negotiation Confirm TX was actually received by the peer even though Ack was not reported");
 +      }
 +
 +      p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
 +
 +      if (dev == NULL)
 +              return;
 +
 +      p2p_go_complete(p2p, dev);
 +}
 +
 +
 +void p2p_send_action_cb(struct p2p_data *p2p, unsigned int freq, const u8 *dst,
 +                      const u8 *src, const u8 *bssid,
 +                      enum p2p_send_action_result result)
 +{
 +      enum p2p_pending_action_state state;
 +      int success;
 +
 +      p2p_dbg(p2p, "Action frame TX callback (state=%d freq=%u dst=" MACSTR
 +              " src=" MACSTR " bssid=" MACSTR " result=%d p2p_state=%s)",
 +              p2p->pending_action_state, freq, MAC2STR(dst), MAC2STR(src),
 +              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->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);
 +              break;
 +      case P2P_PENDING_GO_NEG_RESPONSE:
 +              p2p_go_neg_resp_cb(p2p, success);
 +              break;
 +      case P2P_PENDING_GO_NEG_RESPONSE_FAILURE:
 +              p2p_go_neg_resp_failure_cb(p2p, success, dst);
 +              break;
 +      case P2P_PENDING_GO_NEG_CONFIRM:
 +              p2p_go_neg_conf_cb(p2p, result);
 +              break;
 +      case P2P_PENDING_SD:
 +              p2p_sd_cb(p2p, success);
 +              break;
 +      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;
 +      case P2P_PENDING_INVITATION_RESPONSE:
 +              p2p_invitation_resp_cb(p2p, success);
 +              break;
 +      case P2P_PENDING_DEV_DISC_REQUEST:
 +              p2p_dev_disc_req_cb(p2p, success);
 +              break;
 +      case P2P_PENDING_DEV_DISC_RESPONSE:
 +              p2p_dev_disc_resp_cb(p2p, success);
 +              break;
 +      case P2P_PENDING_GO_DISC_REQ:
 +              p2p_go_disc_req_cb(p2p, success);
 +              break;
 +      }
 +
 +      p2p->after_scan_tx_in_progress = 0;
 +}
 +
 +
 +void p2p_listen_cb(struct p2p_data *p2p, unsigned int freq,
 +                 unsigned int duration)
 +{
 +      if (freq == p2p->pending_client_disc_freq) {
 +              p2p_dbg(p2p, "Client discoverability remain-awake completed");
 +              p2p->pending_client_disc_freq = 0;
 +              return;
 +      }
 +
 +      if (freq != p2p->pending_listen_freq) {
 +              p2p_dbg(p2p, "Unexpected listen callback for freq=%u duration=%u (pending_listen_freq=%u)",
 +                      freq, duration, p2p->pending_listen_freq);
 +              return;
 +      }
 +
 +      p2p_dbg(p2p, "Starting Listen timeout(%u,%u) on freq=%u based on callback",
 +              p2p->pending_listen_sec, p2p->pending_listen_usec,
 +              p2p->pending_listen_freq);
 +      p2p->in_listen = 1;
 +      p2p->drv_in_listen = freq;
 +      if (p2p->pending_listen_sec || p2p->pending_listen_usec) {
 +              /*
 +               * Add 20 msec extra wait to avoid race condition with driver
 +               * remain-on-channel end event, i.e., give driver more time to
 +               * complete the operation before our timeout expires.
 +               */
 +              p2p_set_timeout(p2p, p2p->pending_listen_sec,
 +                              p2p->pending_listen_usec + 20000);
 +      }
 +
 +      p2p->pending_listen_freq = 0;
 +}
 +
 +
 +int p2p_listen_end(struct p2p_data *p2p, unsigned int freq)
 +{
 +      p2p_dbg(p2p, "Driver ended Listen state (freq=%u)", freq);
 +      p2p->drv_in_listen = 0;
 +      if (p2p->in_listen)
 +              return 0; /* Internal timeout will trigger the next step */
 +
 +      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, -1);
 +                      return 0;
 +              }
 +
 +              p2p_set_state(p2p, P2P_CONNECT);
 +              p2p_connect_send(p2p, p2p->go_neg_peer);
 +              return 1;
 +      } else if (p2p->state == P2P_SEARCH) {
 +              if (p2p->p2p_scan_running) {
 +                       /*
 +                        * Search is already in progress. This can happen if
 +                        * an Action frame RX is reported immediately after
 +                        * the end of a remain-on-channel operation and the
 +                        * response frame to that is sent using an offchannel
 +                        * operation while in p2p_find. Avoid an attempt to
 +                        * restart a scan here.
 +                        */
 +                      p2p_dbg(p2p, "p2p_scan already in progress - do not try to start a new one");
 +                      return 1;
 +              }
 +              if (p2p->pending_listen_freq) {
 +                      /*
 +                       * Better wait a bit if the driver is unable to start
 +                       * offchannel operation for some reason. p2p_search()
 +                       * will be started from internal timeout.
 +                       */
 +                      p2p_dbg(p2p, "Listen operation did not seem to start - delay search phase to avoid busy loop");
 +                      p2p_set_timeout(p2p, 0, 100000);
 +                      return 1;
 +              }
 +              if (p2p->search_delay) {
 +                      p2p_dbg(p2p, "Delay search operation by %u ms",
 +                              p2p->search_delay);
 +                      p2p_set_timeout(p2p, p2p->search_delay / 1000,
 +                                      (p2p->search_delay % 1000) * 1000);
 +                      return 1;
 +              }
 +              p2p_search(p2p);
 +              return 1;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +static void p2p_timeout_connect(struct p2p_data *p2p)
 +{
 +      p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
 +      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, -1);
 +              return;
 +      }
 +      if (p2p->go_neg_peer &&
 +          (p2p->go_neg_peer->flags & P2P_DEV_PEER_WAITING_RESPONSE) &&
 +          p2p->go_neg_peer->connect_reqs < 120) {
 +              p2p_dbg(p2p, "Peer expected to wait our response - skip listen");
 +              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);
 +}
 +
 +
 +static void p2p_timeout_connect_listen(struct p2p_data *p2p)
 +{
 +      if (p2p->go_neg_peer) {
 +              if (p2p->drv_in_listen) {
 +                      p2p_dbg(p2p, "Driver is still in Listen state; wait for it to complete");
 +                      return;
 +              }
 +
 +              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, -1);
 +                      return;
 +              }
 +
 +              p2p_set_state(p2p, P2P_CONNECT);
 +              p2p_connect_send(p2p, p2p->go_neg_peer);
 +      } else
 +              p2p_set_state(p2p, P2P_IDLE);
 +}
 +
 +
 +static void p2p_timeout_wait_peer_connect(struct p2p_data *p2p)
 +{
 +      p2p_set_state(p2p, P2P_WAIT_PEER_IDLE);
 +
 +      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);
 +}
 +
 +
 +static void p2p_timeout_wait_peer_idle(struct p2p_data *p2p)
 +{
 +      struct p2p_device *dev = p2p->go_neg_peer;
 +
 +      if (dev == NULL) {
 +              p2p_dbg(p2p, "Unknown GO Neg peer - stop GO Neg wait");
 +              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);
 +}
 +
 +
 +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 = NULL;
 +      }
 +      p2p_continue_find(p2p);
 +}
 +
 +
 +static void p2p_timeout_prov_disc_during_find(struct p2p_data *p2p)
 +{
 +      p2p_dbg(p2p, "Provision Discovery Request timeout");
 +      p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
 +      p2p_continue_find(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;
 +
 +      /*
 +       * For user initiated PD requests that we have not gotten any responses
 +       * for while in IDLE state, we retry them a couple of times before
 +       * giving up.
 +       */
 +      if (!p2p->user_initiated_pd)
 +              return;
 +
 +      p2p_dbg(p2p, "User initiated Provision Discovery Request timeout");
 +
 +      if (p2p->pd_retries) {
 +              p2p->pd_retries--;
 +              p2p_retry_pd(p2p);
 +      } else {
 +              struct p2p_device *dev;
 +              int for_join = 0;
 +
 +              dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) {
 +                      if (os_memcmp(p2p->pending_pd_devaddr,
 +                                    dev->info.p2p_device_addr, ETH_ALEN) != 0)
 +                              continue;
 +                      if (dev->req_config_methods &&
 +                          (dev->flags & P2P_DEV_PD_FOR_JOIN))
 +                              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,
 +                                               adv_id, adv_mac, NULL);
 +              p2p_reset_pending_pd(p2p);
 +      }
 +}
 +
 +
 +static void p2p_timeout_invite(struct p2p_data *p2p)
 +{
 +      p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
 +      p2p_set_state(p2p, P2P_INVITE_LISTEN);
 +      if (p2p->inv_role == P2P_INVITE_ROLE_ACTIVE_GO) {
 +              /*
 +               * Better remain on operating channel instead of listen channel
 +               * when running a group.
 +               */
 +              p2p_dbg(p2p, "Inviting in active GO role - wait on operating channel");
 +              p2p_set_timeout(p2p, 0, 100000);
 +              return;
 +      }
 +      p2p_listen_in_find(p2p, 0);
 +}
 +
 +
 +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_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,
 +                                      0, 0);
 +              }
 +              p2p_set_state(p2p, P2P_IDLE);
 +      }
 +}
 +
 +
 +static void p2p_state_timeout(void *eloop_ctx, void *timeout_ctx)
 +{
 +      struct p2p_data *p2p = eloop_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:
 +              /* Check if we timed out waiting for PD req */
 +              if (p2p->pending_action_state == P2P_PENDING_PD)
 +                      p2p_timeout_prov_disc_req(p2p);
 +              break;
 +      case P2P_SEARCH:
 +              /* Check if we timed out waiting for PD req */
 +              if (p2p->pending_action_state == P2P_PENDING_PD)
 +                      p2p_timeout_prov_disc_req(p2p);
 +              if (p2p->search_delay && !p2p->in_search_delay) {
 +                      p2p_dbg(p2p, "Delay search operation by %u ms",
 +                              p2p->search_delay);
 +                      p2p->in_search_delay = 1;
 +                      p2p_set_timeout(p2p, p2p->search_delay / 1000,
 +                                      (p2p->search_delay % 1000) * 1000);
 +                      break;
 +              }
 +              p2p->in_search_delay = 0;
 +              p2p_search(p2p);
 +              break;
 +      case P2P_CONNECT:
 +              p2p_timeout_connect(p2p);
 +              break;
 +      case P2P_CONNECT_LISTEN:
 +              p2p_timeout_connect_listen(p2p);
 +              break;
 +      case P2P_GO_NEG:
 +              break;
 +      case P2P_LISTEN_ONLY:
 +              /* Check if we timed out waiting for PD req */
 +              if (p2p->pending_action_state == P2P_PENDING_PD)
 +                      p2p_timeout_prov_disc_req(p2p);
 +
 +              if (p2p->ext_listen_only) {
 +                      p2p_dbg(p2p, "Extended Listen Timing - Listen State completed");
 +                      p2p->ext_listen_only = 0;
 +                      p2p_set_state(p2p, P2P_IDLE);
 +              }
 +              break;
 +      case P2P_WAIT_PEER_CONNECT:
 +              p2p_timeout_wait_peer_connect(p2p);
 +              break;
 +      case P2P_WAIT_PEER_IDLE:
 +              p2p_timeout_wait_peer_idle(p2p);
 +              break;
 +      case P2P_SD_DURING_FIND:
 +              p2p_timeout_sd_during_find(p2p);
 +              break;
 +      case P2P_PROVISIONING:
 +              break;
 +      case P2P_PD_DURING_FIND:
 +              p2p_timeout_prov_disc_during_find(p2p);
 +              break;
 +      case P2P_INVITE:
 +              p2p_timeout_invite(p2p);
 +              break;
 +      case P2P_INVITE_LISTEN:
 +              p2p_timeout_invite_listen(p2p);
 +              break;
 +      }
 +}
 +
 +
 +int p2p_reject(struct p2p_data *p2p, const u8 *peer_addr)
 +{
 +      struct p2p_device *dev;
 +
 +      dev = p2p_get_device(p2p, peer_addr);
 +      p2p_dbg(p2p, "Local request to reject connection attempts by peer "
 +              MACSTR, MAC2STR(peer_addr));
 +      if (dev == NULL) {
 +              p2p_dbg(p2p, "Peer " MACSTR " unknown", MAC2STR(peer_addr));
 +              return -1;
 +      }
 +      dev->status = P2P_SC_FAIL_REJECTED_BY_USER;
 +      dev->flags |= P2P_DEV_USER_REJECTED;
 +      return 0;
 +}
 +
 +
 +const char * p2p_wps_method_text(enum p2p_wps_method method)
 +{
 +      switch (method) {
 +      case WPS_NOT_READY:
 +              return "not-ready";
 +      case WPS_PIN_DISPLAY:
 +              return "Display";
 +      case WPS_PIN_KEYPAD:
 +              return "Keypad";
 +      case WPS_PBC:
 +              return "PBC";
 +      case WPS_NFC:
 +              return "NFC";
 +      case WPS_P2PS:
 +              return "P2PS";
 +      }
 +
 +      return "??";
 +}
 +
 +
 +static const char * p2p_go_state_text(enum p2p_go_state go_state)
 +{
 +      switch (go_state) {
 +      case UNKNOWN_GO:
 +              return "unknown";
 +      case LOCAL_GO:
 +              return "local";
 +      case  REMOTE_GO:
 +              return "remote";
 +      }
 +
 +      return "??";
 +}
 +
 +
 +const struct p2p_peer_info * p2p_get_peer_info(struct p2p_data *p2p,
 +                                             const u8 *addr, int next)
 +{
 +      struct p2p_device *dev;
 +
 +      if (addr)
 +              dev = p2p_get_device(p2p, addr);
 +      else
 +              dev = dl_list_first(&p2p->devices, struct p2p_device, list);
 +
 +      if (dev && next) {
 +              dev = dl_list_first(&dev->list, struct p2p_device, list);
 +              if (&dev->list == &p2p->devices)
 +                      dev = NULL;
 +      }
 +
 +      if (dev == NULL)
 +              return NULL;
 +
 +      return &dev->info;
 +}
 +
 +
 +int p2p_get_peer_info_txt(const struct p2p_peer_info *info,
 +                        char *buf, size_t buflen)
 +{
 +      struct p2p_device *dev;
 +      int res;
 +      char *pos, *end;
 +      struct os_reltime now;
 +
 +      if (info == NULL)
 +              return -1;
 +
 +      dev = (struct p2p_device *) (((u8 *) info) -
 +                                   offsetof(struct p2p_device, info));
 +
 +      pos = buf;
 +      end = buf + buflen;
 +
 +      os_get_reltime(&now);
 +      res = os_snprintf(pos, end - pos,
 +                        "age=%d\n"
 +                        "listen_freq=%d\n"
 +                        "wps_method=%s\n"
 +                        "interface_addr=" MACSTR "\n"
 +                        "member_in_go_dev=" MACSTR "\n"
 +                        "member_in_go_iface=" MACSTR "\n"
 +                        "go_neg_req_sent=%d\n"
 +                        "go_state=%s\n"
 +                        "dialog_token=%u\n"
 +                        "intended_addr=" MACSTR "\n"
 +                        "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"
 +                        "status=%d\n"
 +                        "invitation_reqs=%u\n",
 +                        (int) (now.sec - dev->last_seen.sec),
 +                        dev->listen_freq,
 +                        p2p_wps_method_text(dev->wps_method),
 +                        MAC2STR(dev->interface_addr),
 +                        MAC2STR(dev->member_in_go_dev),
 +                        MAC2STR(dev->member_in_go_iface),
 +                        dev->go_neg_req_sent,
 +                        p2p_go_state_text(dev->go_state),
 +                        dev->dialog_token,
 +                        MAC2STR(dev->intended_addr),
 +                        dev->country[0] ? dev->country[0] : '_',
 +                        dev->country[1] ? dev->country[1] : '_',
 +                        dev->oper_freq,
 +                        dev->req_config_methods,
 +                        dev->flags & P2P_DEV_PROBE_REQ_ONLY ?
 +                        "[PROBE_REQ_ONLY]" : "",
 +                        dev->flags & P2P_DEV_REPORTED ? "[REPORTED]" : "",
 +                        dev->flags & P2P_DEV_NOT_YET_READY ?
 +                        "[NOT_YET_READY]" : "",
 +                        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 ?
 +                        "[PEER_WAITING_RESPONSE]" : "",
 +                        dev->flags & P2P_DEV_PREFER_PERSISTENT_GROUP ?
 +                        "[PREFER_PERSISTENT_GROUP]" : "",
 +                        dev->flags & P2P_DEV_WAIT_GO_NEG_RESPONSE ?
 +                        "[WAIT_GO_NEG_RESPONSE]" : "",
 +                        dev->flags & P2P_DEV_WAIT_GO_NEG_CONFIRM ?
 +                        "[WAIT_GO_NEG_CONFIRM]" : "",
 +                        dev->flags & P2P_DEV_GROUP_CLIENT_ONLY ?
 +                        "[GROUP_CLIENT_ONLY]" : "",
 +                        dev->flags & P2P_DEV_FORCE_FREQ ?
 +                        "[FORCE_FREQ]" : "",
 +                        dev->flags & P2P_DEV_PD_FOR_JOIN ?
 +                        "[PD_FOR_JOIN]" : "",
++                        dev->flags & P2P_DEV_LAST_SEEN_AS_GROUP_CLIENT ?
++                        "[LAST_SEEN_AS_GROUP_CLIENT]" : "",
 +                        dev->status,
 +                        dev->invitation_reqs);
 +      if (os_snprintf_error(end - pos, res))
 +              return pos - buf;
 +      pos += res;
 +
 +      if (dev->ext_listen_period) {
 +              res = os_snprintf(pos, end - pos,
 +                                "ext_listen_period=%u\n"
 +                                "ext_listen_interval=%u\n",
 +                                dev->ext_listen_period,
 +                                dev->ext_listen_interval);
 +              if (os_snprintf_error(end - pos, res))
 +                      return pos - buf;
 +              pos += res;
 +      }
 +
 +      if (dev->oper_ssid_len) {
 +              res = os_snprintf(pos, end - pos,
 +                                "oper_ssid=%s\n",
 +                                wpa_ssid_txt(dev->oper_ssid,
 +                                             dev->oper_ssid_len));
 +              if (os_snprintf_error(end - pos, res))
 +                      return pos - buf;
 +              pos += res;
 +      }
 +
 +#ifdef CONFIG_WIFI_DISPLAY
 +      if (dev->info.wfd_subelems) {
 +              res = os_snprintf(pos, end - pos, "wfd_subelems=");
 +              if (os_snprintf_error(end - pos, res))
 +                      return pos - buf;
 +              pos += res;
 +
 +              pos += wpa_snprintf_hex(pos, end - pos,
 +                                      wpabuf_head(dev->info.wfd_subelems),
 +                                      wpabuf_len(dev->info.wfd_subelems));
 +
 +              res = os_snprintf(pos, end - pos, "\n");
 +              if (os_snprintf_error(end - pos, res))
 +                      return pos - buf;
 +              pos += res;
 +      }
 +#endif /* CONFIG_WIFI_DISPLAY */
 +
 +      return pos - buf;
 +}
 +
 +
 +int p2p_peer_known(struct p2p_data *p2p, const u8 *addr)
 +{
 +      return p2p_get_device(p2p, addr) != NULL;
 +}
 +
 +
 +void p2p_set_client_discoverability(struct p2p_data *p2p, int enabled)
 +{
 +      if (enabled) {
 +              p2p_dbg(p2p, "Client discoverability enabled");
 +              p2p->dev_capab |= P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY;
 +      } else {
 +              p2p_dbg(p2p, "Client discoverability disabled");
 +              p2p->dev_capab &= ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY;
 +      }
 +}
 +
 +
 +static struct wpabuf * p2p_build_presence_req(u32 duration1, u32 interval1,
 +                                            u32 duration2, u32 interval2)
 +{
 +      struct wpabuf *req;
 +      struct p2p_noa_desc desc1, desc2, *ptr1 = NULL, *ptr2 = NULL;
 +      u8 *len;
 +
 +      req = wpabuf_alloc(100);
 +      if (req == NULL)
 +              return NULL;
 +
 +      if (duration1 || interval1) {
 +              os_memset(&desc1, 0, sizeof(desc1));
 +              desc1.count_type = 1;
 +              desc1.duration = duration1;
 +              desc1.interval = interval1;
 +              ptr1 = &desc1;
 +
 +              if (duration2 || interval2) {
 +                      os_memset(&desc2, 0, sizeof(desc2));
 +                      desc2.count_type = 2;
 +                      desc2.duration = duration2;
 +                      desc2.interval = interval2;
 +                      ptr2 = &desc2;
 +              }
 +      }
 +
 +      p2p_buf_add_action_hdr(req, P2P_PRESENCE_REQ, 1);
 +      len = p2p_buf_add_ie_hdr(req);
 +      p2p_buf_add_noa(req, 0, 0, 0, ptr1, ptr2);
 +      p2p_buf_update_ie_hdr(req, len);
 +
 +      return req;
 +}
 +
 +
 +int p2p_presence_req(struct p2p_data *p2p, const u8 *go_interface_addr,
 +                   const u8 *own_interface_addr, unsigned int freq,
 +                   u32 duration1, u32 interval1, u32 duration2,
 +                   u32 interval2)
 +{
 +      struct wpabuf *req;
 +
 +      p2p_dbg(p2p, "Send Presence Request to GO " MACSTR
 +              " (own interface " MACSTR ") freq=%u dur1=%u int1=%u "
 +              "dur2=%u int2=%u",
 +              MAC2STR(go_interface_addr), MAC2STR(own_interface_addr),
 +              freq, duration1, interval1, duration2, interval2);
 +
 +      req = p2p_build_presence_req(duration1, interval1, duration2,
 +                                   interval2);
 +      if (req == NULL)
 +              return -1;
 +
 +      p2p->pending_action_state = P2P_NO_PENDING_ACTION;
 +      if (p2p_send_action(p2p, freq, go_interface_addr, own_interface_addr,
 +                          go_interface_addr,
 +                          wpabuf_head(req), wpabuf_len(req), 200) < 0) {
 +              p2p_dbg(p2p, "Failed to send Action frame");
 +      }
 +      wpabuf_free(req);
 +
 +      return 0;
 +}
 +
 +
 +static struct wpabuf * p2p_build_presence_resp(u8 status, const u8 *noa,
 +                                             size_t noa_len, u8 dialog_token)
 +{
 +      struct wpabuf *resp;
 +      u8 *len;
 +
 +      resp = wpabuf_alloc(100 + noa_len);
 +      if (resp == NULL)
 +              return NULL;
 +
 +      p2p_buf_add_action_hdr(resp, P2P_PRESENCE_RESP, dialog_token);
 +      len = p2p_buf_add_ie_hdr(resp);
 +      p2p_buf_add_status(resp, status);
 +      if (noa) {
 +              wpabuf_put_u8(resp, P2P_ATTR_NOTICE_OF_ABSENCE);
 +              wpabuf_put_le16(resp, noa_len);
 +              wpabuf_put_data(resp, noa, noa_len);
 +      } else
 +              p2p_buf_add_noa(resp, 0, 0, 0, NULL, NULL);
 +      p2p_buf_update_ie_hdr(resp, len);
 +
 +      return resp;
 +}
 +
 +
 +static void p2p_process_presence_req(struct p2p_data *p2p, const u8 *da,
 +                                   const u8 *sa, const u8 *data, size_t len,
 +                                   int rx_freq)
 +{
 +      struct p2p_message msg;
 +      u8 status;
 +      struct wpabuf *resp;
 +      size_t g;
 +      struct p2p_group *group = NULL;
 +      int parsed = 0;
 +      u8 noa[50];
 +      int noa_len;
 +
 +      p2p_dbg(p2p, "Received P2P Action - P2P Presence Request");
 +
 +      for (g = 0; g < p2p->num_groups; g++) {
 +              if (os_memcmp(da, p2p_group_get_interface_addr(p2p->groups[g]),
 +                            ETH_ALEN) == 0) {
 +                      group = p2p->groups[g];
 +                      break;
 +              }
 +      }
 +      if (group == NULL) {
 +              p2p_dbg(p2p, "Ignore P2P Presence Request for unknown group "
 +                      MACSTR, MAC2STR(da));
 +              return;
 +      }
 +
 +      if (p2p_parse(data, len, &msg) < 0) {
 +              p2p_dbg(p2p, "Failed to parse P2P Presence Request");
 +              status = P2P_SC_FAIL_INVALID_PARAMS;
 +              goto fail;
 +      }
 +      parsed = 1;
 +
 +      if (msg.noa == NULL) {
 +              p2p_dbg(p2p, "No NoA attribute in P2P Presence Request");
 +              status = P2P_SC_FAIL_INVALID_PARAMS;
 +              goto fail;
 +      }
 +
 +      status = p2p_group_presence_req(group, sa, msg.noa, msg.noa_len);
 +
 +fail:
 +      if (p2p->cfg->get_noa)
 +              noa_len = p2p->cfg->get_noa(p2p->cfg->cb_ctx, da, noa,
 +                                          sizeof(noa));
 +      else
 +              noa_len = -1;
 +      resp = p2p_build_presence_resp(status, noa_len > 0 ? noa : NULL,
 +                                     noa_len > 0 ? noa_len : 0,
 +                                     msg.dialog_token);
 +      if (parsed)
 +              p2p_parse_free(&msg);
 +      if (resp == NULL)
 +              return;
 +
 +      p2p->pending_action_state = P2P_NO_PENDING_ACTION;
 +      if (p2p_send_action(p2p, rx_freq, sa, da, da,
 +                          wpabuf_head(resp), wpabuf_len(resp), 200) < 0) {
 +              p2p_dbg(p2p, "Failed to send Action frame");
 +      }
 +      wpabuf_free(resp);
 +}
 +
 +
 +static void p2p_process_presence_resp(struct p2p_data *p2p, const u8 *da,
 +                                    const u8 *sa, const u8 *data, size_t len)
 +{
 +      struct p2p_message msg;
 +
 +      p2p_dbg(p2p, "Received P2P Action - P2P Presence Response");
 +
 +      if (p2p_parse(data, len, &msg) < 0) {
 +              p2p_dbg(p2p, "Failed to parse P2P Presence Response");
 +              return;
 +      }
 +
 +      if (msg.status == NULL || msg.noa == NULL) {
 +              p2p_dbg(p2p, "No Status or NoA attribute in P2P Presence Response");
 +              p2p_parse_free(&msg);
 +              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);
 +              p2p_parse_free(&msg);
 +              return;
 +      }
 +
 +      p2p_dbg(p2p, "P2P Presence Request was accepted");
 +      wpa_hexdump(MSG_DEBUG, "P2P: P2P Presence Response - NoA",
 +                  msg.noa, msg.noa_len);
 +      /* TODO: process NoA */
 +      p2p_parse_free(&msg);
 +}
 +
 +
 +static void p2p_ext_listen_timeout(void *eloop_ctx, void *timeout_ctx)
 +{
 +      struct p2p_data *p2p = eloop_ctx;
 +
 +      if (p2p->ext_listen_interval) {
 +              /* Schedule next extended listen timeout */
 +              eloop_register_timeout(p2p->ext_listen_interval_sec,
 +                                     p2p->ext_listen_interval_usec,
 +                                     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
 +               * command may fail is something else (e.g., a scan) was
 +               * running at an inconvenient time. As a workaround, allow new
 +               * Extended Listen operation to be started.
 +               */
 +              p2p_dbg(p2p, "Previous Extended Listen operation had not been completed - try again");
 +              p2p->ext_listen_only = 0;
 +              p2p_set_state(p2p, P2P_IDLE);
 +      }
 +
 +      if (p2p->state != P2P_IDLE) {
 +              p2p_dbg(p2p, "Skip Extended Listen timeout in active state (%s)", p2p_state_txt(p2p->state));
 +              return;
 +      }
 +
 +      p2p_dbg(p2p, "Extended Listen timeout");
 +      p2p->ext_listen_only = 1;
 +      if (p2p_listen(p2p, p2p->ext_listen_period) < 0) {
 +              p2p_dbg(p2p, "Failed to start Listen state for Extended Listen Timing");
 +              p2p->ext_listen_only = 0;
 +      }
 +}
 +
 +
 +int p2p_ext_listen(struct p2p_data *p2p, unsigned int period,
 +                 unsigned int interval)
 +{
 +      if (period > 65535 || interval > 65535 || period > interval ||
 +          (period == 0 && interval > 0) || (period > 0 && interval == 0)) {
 +              p2p_dbg(p2p, "Invalid Extended Listen Timing request: period=%u interval=%u",
 +                      period, interval);
 +              return -1;
 +      }
 +
 +      eloop_cancel_timeout(p2p_ext_listen_timeout, p2p, NULL);
 +
 +      if (interval == 0) {
 +              p2p_dbg(p2p, "Disabling Extended Listen Timing");
 +              p2p->ext_listen_period = 0;
 +              p2p->ext_listen_interval = 0;
 +              return 0;
 +      }
 +
 +      p2p_dbg(p2p, "Enabling Extended Listen Timing: period %u msec, interval %u msec",
 +              period, interval);
 +      p2p->ext_listen_period = period;
 +      p2p->ext_listen_interval = interval;
 +      p2p->ext_listen_interval_sec = interval / 1000;
 +      p2p->ext_listen_interval_usec = (interval % 1000) * 1000;
 +
 +      eloop_register_timeout(p2p->ext_listen_interval_sec,
 +                             p2p->ext_listen_interval_usec,
 +                             p2p_ext_listen_timeout, p2p, NULL);
 +
 +      return 0;
 +}
 +
 +
 +void p2p_deauth_notif(struct p2p_data *p2p, const u8 *bssid, u16 reason_code,
 +                    const u8 *ie, size_t ie_len)
 +{
 +      struct p2p_message msg;
 +
 +      if (bssid == NULL || ie == NULL)
 +              return;
 +
 +      os_memset(&msg, 0, sizeof(msg));
 +      if (p2p_parse_ies(ie, ie_len, &msg))
 +              return;
 +      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",
 +              MAC2STR(bssid), reason_code, *msg.minor_reason_code);
 +
 +      p2p_parse_free(&msg);
 +}
 +
 +
 +void p2p_disassoc_notif(struct p2p_data *p2p, const u8 *bssid, u16 reason_code,
 +                      const u8 *ie, size_t ie_len)
 +{
 +      struct p2p_message msg;
 +
 +      if (bssid == NULL || ie == NULL)
 +              return;
 +
 +      os_memset(&msg, 0, sizeof(msg));
 +      if (p2p_parse_ies(ie, ie_len, &msg))
 +              return;
 +      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",
 +              MAC2STR(bssid), reason_code, *msg.minor_reason_code);
 +
 +      p2p_parse_free(&msg);
 +}
 +
 +
 +void p2p_set_managed_oper(struct p2p_data *p2p, int enabled)
 +{
 +      if (enabled) {
 +              p2p_dbg(p2p, "Managed P2P Device operations enabled");
 +              p2p->dev_capab |= P2P_DEV_CAPAB_INFRA_MANAGED;
 +      } else {
 +              p2p_dbg(p2p, "Managed P2P Device operations disabled");
 +              p2p->dev_capab &= ~P2P_DEV_CAPAB_INFRA_MANAGED;
 +      }
 +}
 +
 +
 +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);
 +
 +      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));
 +      if (postfix == NULL) {
 +              p2p->cfg->ssid_postfix_len = 0;
 +              return 0;
 +      }
 +      if (len > sizeof(p2p->cfg->ssid_postfix))
 +              return -1;
 +      os_memcpy(p2p->cfg->ssid_postfix, postfix, len);
 +      p2p->cfg->ssid_postfix_len = len;
 +      return 0;
 +}
 +
 +
 +int p2p_set_oper_channel(struct p2p_data *p2p, u8 op_reg_class, u8 op_channel,
 +                       int cfg_op_channel)
 +{
 +      if (p2p_channel_to_freq(op_reg_class, op_channel) < 0)
 +              return -1;
 +
 +      p2p_dbg(p2p, "Set Operating channel: reg_class %u channel %u",
 +              op_reg_class, op_channel);
 +      p2p->cfg->op_reg_class = op_reg_class;
 +      p2p->cfg->op_channel = op_channel;
 +      p2p->cfg->cfg_op_channel = cfg_op_channel;
 +      return 0;
 +}
 +
 +
 +int p2p_set_pref_chan(struct p2p_data *p2p, unsigned int num_pref_chan,
 +                    const struct p2p_channel *pref_chan)
 +{
 +      struct p2p_channel *n;
 +
 +      if (pref_chan) {
 +              n = os_malloc(num_pref_chan * sizeof(struct p2p_channel));
 +              if (n == NULL)
 +                      return -1;
 +              os_memcpy(n, pref_chan,
 +                        num_pref_chan * sizeof(struct p2p_channel));
 +      } else
 +              n = NULL;
 +
 +      os_free(p2p->cfg->pref_chan);
 +      p2p->cfg->pref_chan = n;
 +      p2p->cfg->num_pref_chan = num_pref_chan;
 +
 +      return 0;
 +}
 +
 +
 +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_interface_addr(struct p2p_data *p2p, const u8 *dev_addr,
 +                         u8 *iface_addr)
 +{
 +      struct p2p_device *dev = p2p_get_device(p2p, dev_addr);
 +      if (dev == NULL || is_zero_ether_addr(dev->interface_addr))
 +              return -1;
 +      os_memcpy(iface_addr, dev->interface_addr, ETH_ALEN);
 +      return 0;
 +}
 +
 +
 +int p2p_get_dev_addr(struct p2p_data *p2p, const u8 *iface_addr,
 +                         u8 *dev_addr)
 +{
 +      struct p2p_device *dev = p2p_get_device_interface(p2p, iface_addr);
 +      if (dev == NULL)
 +              return -1;
 +      os_memcpy(dev_addr, dev->info.p2p_device_addr, ETH_ALEN);
 +      return 0;
 +}
 +
 +
 +void p2p_set_peer_filter(struct p2p_data *p2p, const u8 *addr)
 +{
 +      os_memcpy(p2p->peer_filter, addr, ETH_ALEN);
 +      if (is_zero_ether_addr(p2p->peer_filter))
 +              p2p_dbg(p2p, "Disable peer filter");
 +      else
 +              p2p_dbg(p2p, "Enable peer filter for " MACSTR,
 +                      MAC2STR(p2p->peer_filter));
 +}
 +
 +
 +void p2p_set_cross_connect(struct p2p_data *p2p, int enabled)
 +{
 +      p2p_dbg(p2p, "Cross connection %s", enabled ? "enabled" : "disabled");
 +      if (p2p->cross_connect == enabled)
 +              return;
 +      p2p->cross_connect = enabled;
 +      /* TODO: may need to tear down any action group where we are GO(?) */
 +}
 +
 +
 +int p2p_get_oper_freq(struct p2p_data *p2p, const u8 *iface_addr)
 +{
 +      struct p2p_device *dev = p2p_get_device_interface(p2p, iface_addr);
 +      if (dev == NULL)
 +              return -1;
 +      if (dev->oper_freq <= 0)
 +              return -1;
 +      return dev->oper_freq;
 +}
 +
 +
 +void p2p_set_intra_bss_dist(struct p2p_data *p2p, int enabled)
 +{
 +      p2p_dbg(p2p, "Intra BSS distribution %s",
 +              enabled ? "enabled" : "disabled");
 +      p2p->cfg->p2p_intra_bss = enabled;
 +}
 +
 +
 +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);
 +}
 +
 +
 +int p2p_send_action(struct p2p_data *p2p, unsigned int freq, const u8 *dst,
 +                  const u8 *src, const u8 *bssid, const u8 *buf,
 +                  size_t len, unsigned int wait_time)
 +{
 +      if (p2p->p2p_scan_running) {
 +              p2p_dbg(p2p, "Delay Action frame TX until p2p_scan completes");
 +              if (p2p->after_scan_tx) {
 +                      p2p_dbg(p2p, "Dropped previous pending Action frame TX");
 +                      os_free(p2p->after_scan_tx);
 +              }
 +              p2p->after_scan_tx = os_malloc(sizeof(*p2p->after_scan_tx) +
 +                                             len);
 +              if (p2p->after_scan_tx == NULL)
 +                      return -1;
 +              p2p->after_scan_tx->freq = freq;
 +              os_memcpy(p2p->after_scan_tx->dst, dst, ETH_ALEN);
 +              os_memcpy(p2p->after_scan_tx->src, src, ETH_ALEN);
 +              os_memcpy(p2p->after_scan_tx->bssid, bssid, ETH_ALEN);
 +              p2p->after_scan_tx->len = len;
 +              p2p->after_scan_tx->wait_time = wait_time;
 +              os_memcpy(p2p->after_scan_tx + 1, buf, len);
 +              return 0;
 +      }
 +
 +      return p2p->cfg->send_action(p2p->cfg->cb_ctx, freq, dst, src, bssid,
 +                                   buf, len, wait_time);
 +}
 +
 +
 +void p2p_set_best_channels(struct p2p_data *p2p, int freq_24, int freq_5,
 +                         int freq_overall)
 +{
 +      p2p_dbg(p2p, "Best channel: 2.4 GHz: %d,  5 GHz: %d,  overall: %d",
 +              freq_24, freq_5, freq_overall);
 +      p2p->best_freq_24 = freq_24;
 +      p2p->best_freq_5 = freq_5;
 +      p2p->best_freq_overall = freq_overall;
 +}
 +
 +
 +void p2p_set_own_freq_preference(struct p2p_data *p2p, int freq)
 +{
 +      p2p_dbg(p2p, "Own frequency preference: %d MHz", freq);
 +      p2p->own_freq_preference = freq;
 +}
 +
 +
 +const u8 * p2p_get_go_neg_peer(struct p2p_data *p2p)
 +{
 +      if (p2p == NULL || p2p->go_neg_peer == NULL)
 +              return NULL;
 +      return p2p->go_neg_peer->info.p2p_device_addr;
 +}
 +
 +
 +const struct p2p_peer_info *
 +p2p_get_peer_found(struct p2p_data *p2p, const u8 *addr, int next)
 +{
 +      struct p2p_device *dev;
 +
 +      if (addr) {
 +              dev = p2p_get_device(p2p, addr);
 +              if (!dev)
 +                      return NULL;
 +
 +              if (!next) {
 +                      if (dev->flags & P2P_DEV_PROBE_REQ_ONLY)
 +                              return NULL;
 +
 +                      return &dev->info;
 +              } else {
 +                      do {
 +                              dev = dl_list_first(&dev->list,
 +                                                  struct p2p_device,
 +                                                  list);
 +                              if (!dev || &dev->list == &p2p->devices)
 +                                      return NULL;
 +                      } while (dev->flags & P2P_DEV_PROBE_REQ_ONLY);
 +              }
 +      } else {
 +              dev = dl_list_first(&p2p->devices, struct p2p_device, list);
 +              if (!dev)
 +                      return NULL;
 +              while (dev->flags & P2P_DEV_PROBE_REQ_ONLY) {
 +                      dev = dl_list_first(&dev->list,
 +                                          struct p2p_device,
 +                                          list);
 +                      if (!dev || &dev->list == &p2p->devices)
 +                              return NULL;
 +              }
 +      }
 +
 +      return &dev->info;
 +}
 +
 +
 +int p2p_in_progress(struct p2p_data *p2p)
 +{
 +      if (p2p == NULL)
 +              return 0;
 +      if (p2p->state == P2P_SEARCH)
 +              return 2;
 +      return p2p->state != P2P_IDLE && p2p->state != P2P_PROVISIONING;
 +}
 +
 +
 +void p2p_set_config_timeout(struct p2p_data *p2p, u8 go_timeout,
 +                          u8 client_timeout)
 +{
 +      if (p2p) {
 +              p2p->go_timeout = go_timeout;
 +              p2p->client_timeout = client_timeout;
 +      }
 +}
 +
 +
 +#ifdef CONFIG_WIFI_DISPLAY
 +
 +static void p2p_update_wfd_ie_groups(struct p2p_data *p2p)
 +{
 +      size_t g;
 +      struct p2p_group *group;
 +
 +      for (g = 0; g < p2p->num_groups; g++) {
 +              group = p2p->groups[g];
 +              p2p_group_force_beacon_update_ies(group);
 +      }
 +}
 +
 +
 +int p2p_set_wfd_ie_beacon(struct p2p_data *p2p, struct wpabuf *ie)
 +{
 +      wpabuf_free(p2p->wfd_ie_beacon);
 +      p2p->wfd_ie_beacon = ie;
 +      p2p_update_wfd_ie_groups(p2p);
 +      return 0;
 +}
 +
 +
 +int p2p_set_wfd_ie_probe_req(struct p2p_data *p2p, struct wpabuf *ie)
 +{
 +      wpabuf_free(p2p->wfd_ie_probe_req);
 +      p2p->wfd_ie_probe_req = ie;
 +      return 0;
 +}
 +
 +
 +int p2p_set_wfd_ie_probe_resp(struct p2p_data *p2p, struct wpabuf *ie)
 +{
 +      wpabuf_free(p2p->wfd_ie_probe_resp);
 +      p2p->wfd_ie_probe_resp = ie;
 +      p2p_update_wfd_ie_groups(p2p);
 +      return 0;
 +}
 +
 +
 +int p2p_set_wfd_ie_assoc_req(struct p2p_data *p2p, struct wpabuf *ie)
 +{
 +      wpabuf_free(p2p->wfd_ie_assoc_req);
 +      p2p->wfd_ie_assoc_req = ie;
 +      return 0;
 +}
 +
 +
 +int p2p_set_wfd_ie_invitation(struct p2p_data *p2p, struct wpabuf *ie)
 +{
 +      wpabuf_free(p2p->wfd_ie_invitation);
 +      p2p->wfd_ie_invitation = ie;
 +      return 0;
 +}
 +
 +
 +int p2p_set_wfd_ie_prov_disc_req(struct p2p_data *p2p, struct wpabuf *ie)
 +{
 +      wpabuf_free(p2p->wfd_ie_prov_disc_req);
 +      p2p->wfd_ie_prov_disc_req = ie;
 +      return 0;
 +}
 +
 +
 +int p2p_set_wfd_ie_prov_disc_resp(struct p2p_data *p2p, struct wpabuf *ie)
 +{
 +      wpabuf_free(p2p->wfd_ie_prov_disc_resp);
 +      p2p->wfd_ie_prov_disc_resp = ie;
 +      return 0;
 +}
 +
 +
 +int p2p_set_wfd_ie_go_neg(struct p2p_data *p2p, struct wpabuf *ie)
 +{
 +      wpabuf_free(p2p->wfd_ie_go_neg);
 +      p2p->wfd_ie_go_neg = ie;
 +      return 0;
 +}
 +
 +
 +int p2p_set_wfd_dev_info(struct p2p_data *p2p, const struct wpabuf *elem)
 +{
 +      wpabuf_free(p2p->wfd_dev_info);
 +      if (elem) {
 +              p2p->wfd_dev_info = wpabuf_dup(elem);
 +              if (p2p->wfd_dev_info == NULL)
 +                      return -1;
 +      } else
 +              p2p->wfd_dev_info = NULL;
 +
 +      return 0;
 +}
 +
 +
 +int p2p_set_wfd_assoc_bssid(struct p2p_data *p2p, const struct wpabuf *elem)
 +{
 +      wpabuf_free(p2p->wfd_assoc_bssid);
 +      if (elem) {
 +              p2p->wfd_assoc_bssid = wpabuf_dup(elem);
 +              if (p2p->wfd_assoc_bssid == NULL)
 +                      return -1;
 +      } else
 +              p2p->wfd_assoc_bssid = NULL;
 +
 +      return 0;
 +}
 +
 +
 +int p2p_set_wfd_coupled_sink_info(struct p2p_data *p2p,
 +                                const struct wpabuf *elem)
 +{
 +      wpabuf_free(p2p->wfd_coupled_sink_info);
 +      if (elem) {
 +              p2p->wfd_coupled_sink_info = wpabuf_dup(elem);
 +              if (p2p->wfd_coupled_sink_info == NULL)
 +                      return -1;
 +      } else
 +              p2p->wfd_coupled_sink_info = NULL;
 +
 +      return 0;
 +}
 +
 +#endif /* CONFIG_WIFI_DISPLAY */
 +
 +
 +int p2p_set_disc_int(struct p2p_data *p2p, int min_disc_int, int max_disc_int,
 +                   int max_disc_tu)
 +{
 +      if (min_disc_int > max_disc_int || min_disc_int < 0 || max_disc_int < 0)
 +              return -1;
 +
 +      p2p->min_disc_int = min_disc_int;
 +      p2p->max_disc_int = max_disc_int;
 +      p2p->max_disc_tu = max_disc_tu;
 +      p2p_dbg(p2p, "Set discoverable interval: min=%d max=%d max_tu=%d",
 +              min_disc_int, max_disc_int, max_disc_tu);
 +
 +      return 0;
 +}
 +
 +
 +void p2p_dbg(struct p2p_data *p2p, const char *fmt, ...)
 +{
 +      va_list ap;
 +      char buf[500];
 +
 +      if (!p2p->cfg->debug_print)
 +              return;
 +
 +      va_start(ap, fmt);
 +      vsnprintf(buf, sizeof(buf), fmt, ap);
 +      buf[sizeof(buf) - 1] = '\0';
 +      va_end(ap);
 +      p2p->cfg->debug_print(p2p->cfg->cb_ctx, MSG_DEBUG, buf);
 +}
 +
 +
 +void p2p_info(struct p2p_data *p2p, const char *fmt, ...)
 +{
 +      va_list ap;
 +      char buf[500];
 +
 +      if (!p2p->cfg->debug_print)
 +              return;
 +
 +      va_start(ap, fmt);
 +      vsnprintf(buf, sizeof(buf), fmt, ap);
 +      buf[sizeof(buf) - 1] = '\0';
 +      va_end(ap);
 +      p2p->cfg->debug_print(p2p->cfg->cb_ctx, MSG_INFO, buf);
 +}
 +
 +
 +void p2p_err(struct p2p_data *p2p, const char *fmt, ...)
 +{
 +      va_list ap;
 +      char buf[500];
 +
 +      if (!p2p->cfg->debug_print)
 +              return;
 +
 +      va_start(ap, fmt);
 +      vsnprintf(buf, sizeof(buf), fmt, ap);
 +      buf[sizeof(buf) - 1] = '\0';
 +      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");
++              p2p_parse_free(&msg);
 +              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");
++              p2p_parse_free(&msg);
 +              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");
++                      p2p_parse_free(&msg);
 +                      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);
 +}
++
++
++void p2p_set_own_pref_freq_list(struct p2p_data *p2p,
++                              const unsigned int *pref_freq_list,
++                              unsigned int size)
++{
++      unsigned int i;
++
++      if (size > P2P_MAX_PREF_CHANNELS)
++              size = P2P_MAX_PREF_CHANNELS;
++      p2p->num_pref_freq = size;
++      for (i = 0; i < size; i++) {
++              p2p->pref_freq_list[i] = pref_freq_list[i];
++              p2p_dbg(p2p, "Own preferred frequency list[%u]=%u MHz",
++                      i, p2p->pref_freq_list[i]);
++      }
++}
index 2402db6a7eb943b6e0842286cd1a9923f6a8be7e,0000000000000000000000000000000000000000..b4060be477b6b8625c73431568a7643bf05dc982
mode 100644,000000..100644
--- /dev/null
@@@ -1,2247 -1,0 +1,2343 @@@
- #include "wps/wps_defs.h"
 +/*
 + * Wi-Fi Direct - P2P module
 + * Copyright (c) 2009-2010, Atheros Communications
 + *
 + * This software may be distributed under the terms of the BSD license.
 + * See README for more details.
 + */
 +
 +#ifndef P2P_H
 +#define P2P_H
 +
-       u8 ssid[32];
++#include "common/ieee802_11_defs.h"
++#include "wps/wps.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
++#define P2PS_FEATURE_CAPAB_CPT_MAX 2
++
++/**
++ * P2P_MAX_PREF_CHANNELS - Maximum number of preferred channels
++ */
++#define P2P_MAX_PREF_CHANNELS 100
 +
 +/**
 + * P2P_MAX_REG_CLASSES - Maximum number of regulatory classes
 + */
 +#define P2P_MAX_REG_CLASSES 10
 +
 +/**
 + * P2P_MAX_REG_CLASS_CHANNELS - Maximum number of channels per regulatory class
 + */
 +#define P2P_MAX_REG_CLASS_CHANNELS 20
 +
 +/**
 + * struct p2p_channels - List of supported channels
 + */
 +struct p2p_channels {
 +      /**
 +       * struct p2p_reg_class - Supported regulatory class
 +       */
 +      struct p2p_reg_class {
 +              /**
 +               * reg_class - Regulatory class (IEEE 802.11-2007, Annex J)
 +               */
 +              u8 reg_class;
 +
 +              /**
 +               * channel - Supported channels
 +               */
 +              u8 channel[P2P_MAX_REG_CLASS_CHANNELS];
 +
 +              /**
 +               * channels - Number of channel entries in use
 +               */
 +              size_t channels;
 +      } reg_class[P2P_MAX_REG_CLASSES];
 +
 +      /**
 +       * reg_classes - Number of reg_class entries in use
 +       */
 +      size_t reg_classes;
 +};
 +
 +enum p2p_wps_method {
 +      WPS_NOT_READY, WPS_PIN_DISPLAY, WPS_PIN_KEYPAD, WPS_PBC, WPS_NFC,
 +      WPS_P2PS
 +};
 +
 +/**
 + * struct p2p_go_neg_results - P2P Group Owner Negotiation results
 + */
 +struct p2p_go_neg_results {
 +      /**
 +       * status - Negotiation result (Status Code)
 +       *
 +       * 0 (P2P_SC_SUCCESS) indicates success. Non-zero values indicate
 +       * failed negotiation.
 +       */
 +      int status;
 +
 +      /**
 +       * role_go - Whether local end is Group Owner
 +       */
 +      int role_go;
 +
 +      /**
 +       * freq - Frequency of the group operational channel in MHz
 +       */
 +      int freq;
 +
 +      int ht40;
 +
 +      int vht;
 +
 +      /**
 +       * ssid - SSID of the group
 +       */
-       char device_name[33];
++      u8 ssid[SSID_MAX_LEN];
 +
 +      /**
 +       * ssid_len - Length of SSID in octets
 +       */
 +      size_t ssid_len;
 +
 +      /**
 +       * psk - WPA pre-shared key (256 bits) (GO only)
 +       */
 +      u8 psk[32];
 +
 +      /**
 +       * psk_set - Whether PSK field is configured (GO only)
 +       */
 +      int psk_set;
 +
 +      /**
 +       * passphrase - WPA2-Personal passphrase for the group (GO only)
 +       */
 +      char passphrase[64];
 +
 +      /**
 +       * peer_device_addr - P2P Device Address of the peer
 +       */
 +      u8 peer_device_addr[ETH_ALEN];
 +
 +      /**
 +       * peer_interface_addr - P2P Interface Address of the peer
 +       */
 +      u8 peer_interface_addr[ETH_ALEN];
 +
 +      /**
 +       * wps_method - WPS method to be used during provisioning
 +       */
 +      enum p2p_wps_method wps_method;
 +
 +#define P2P_MAX_CHANNELS 50
 +
 +      /**
 +       * freq_list - Zero-terminated list of possible operational channels
 +       */
 +      int freq_list[P2P_MAX_CHANNELS];
 +
 +      /**
 +       * persistent_group - Whether the group should be made persistent
 +       * 0 = not persistent
 +       * 1 = persistent group without persistent reconnect
 +       * 2 = persistent group with persistent reconnect
 +       */
 +      int persistent_group;
 +
 +      /**
 +       * peer_config_timeout - Peer configuration timeout (in 10 msec units)
 +       */
 +      unsigned int peer_config_timeout;
 +};
 +
 +struct p2ps_provision {
++      /**
++       * pd_seeker - P2PS provision discovery seeker role
++       */
++      unsigned int pd_seeker:1;
++
 +      /**
 +       * 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];
 +
++      /**
++       * cpt_mask - Supported Coordination Protocol Transport mask
++       *
++       * A bitwise mask of supported ASP Coordination Protocol Transports.
++       * This property is set together and corresponds with cpt_priority.
++       */
++      u8 cpt_mask;
++
++      /**
++       * cpt_priority - Coordination Protocol Transport priority list
++       *
++       * Priorities of supported ASP Coordination Protocol Transports.
++       * This property is set together and corresponds with cpt_mask.
++       * The CPT priority list is 0 terminated.
++       */
++      u8 cpt_priority[P2PS_FEATURE_CAPAB_CPT_MAX + 1];
++
 +      /**
 +       * 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];
 +
++      /**
++       * cpt_mask - supported Coordination Protocol Transport mask
++       *
++       * A bitwise mask of supported ASP Coordination Protocol Transports.
++       * This property is set together and corresponds with cpt_priority.
++       */
++      u8 cpt_mask;
++
++      /**
++       * cpt_priority - Coordination Protocol Transport priority list
++       *
++       * Priorities of supported ASP Coordinatin Protocol Transports.
++       * This property is set together and corresponds with cpt_mask.
++       * The CPT priority list is 0 terminated.
++       */
++      u8 cpt_priority[P2PS_FEATURE_CAPAB_CPT_MAX + 1];
++
 +      /**
 +       * 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
 +};
 +
 +#define P2P_MAX_WPS_VENDOR_EXT 10
 +
 +/**
 + * struct p2p_peer_info - P2P peer information
 + */
 +struct p2p_peer_info {
 +      /**
 +       * p2p_device_addr - P2P Device Address of the peer
 +       */
 +      u8 p2p_device_addr[ETH_ALEN];
 +
 +      /**
 +       * pri_dev_type - Primary Device Type
 +       */
 +      u8 pri_dev_type[8];
 +
 +      /**
 +       * device_name - Device Name (0..32 octets encoded in UTF-8)
 +       */
-       char manufacturer[65];
++      char device_name[WPS_DEV_NAME_MAX_LEN + 1];
 +
 +      /**
 +       * manufacturer - Manufacturer (0..64 octets encoded in UTF-8)
 +       */
-       char model_name[33];
++      char manufacturer[WPS_MANUFACTURER_MAX_LEN + 1];
 +
 +      /**
 +       * model_name - Model Name (0..32 octets encoded in UTF-8)
 +       */
-       char model_number[33];
++      char model_name[WPS_MODEL_NAME_MAX_LEN + 1];
 +
 +      /**
 +       * model_number - Model Number (0..32 octets encoded in UTF-8)
 +       */
-       char serial_number[33];
++      char model_number[WPS_MODEL_NUMBER_MAX_LEN + 1];
 +
 +      /**
 +       * serial_number - Serial Number (0..32 octets encoded in UTF-8)
 +       */
-       u8 wps_sec_dev_type_list[128];
++      char serial_number[WPS_SERIAL_NUMBER_MAX_LEN + 1];
 +
 +      /**
 +       * level - Signal level
 +       */
 +      int level;
 +
 +      /**
 +       * config_methods - WPS Configuration Methods
 +       */
 +      u16 config_methods;
 +
 +      /**
 +       * dev_capab - Device Capabilities
 +       */
 +      u8 dev_capab;
 +
 +      /**
 +       * group_capab - Group Capabilities
 +       */
 +      u8 group_capab;
 +
 +      /**
 +       * wps_sec_dev_type_list - WPS secondary device type list
 +       *
 +       * This list includes from 0 to 16 Secondary Device Types as indicated
 +       * by wps_sec_dev_type_list_len (8 * number of types).
 +       */
-       u8 ssid_postfix[32 - 9];
++      u8 wps_sec_dev_type_list[WPS_SEC_DEV_TYPE_MAX_LEN];
 +
 +      /**
 +       * wps_sec_dev_type_list_len - Length of secondary device type list
 +       */
 +      size_t wps_sec_dev_type_list_len;
 +
 +      struct wpabuf *wps_vendor_ext[P2P_MAX_WPS_VENDOR_EXT];
 +
 +      /**
 +       * 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 {
 +      P2P_PROV_DISC_SUCCESS,
 +      P2P_PROV_DISC_TIMEOUT,
 +      P2P_PROV_DISC_REJECTED,
 +      P2P_PROV_DISC_TIMEOUT_JOIN,
 +      P2P_PROV_DISC_INFO_UNAVAILABLE,
 +};
 +
 +struct p2p_channel {
 +      u8 op_class;
 +      u8 chan;
 +};
 +
 +/**
 + * struct p2p_config - P2P configuration
 + *
 + * This configuration is provided to the P2P module during initialization with
 + * p2p_init().
 + */
 +struct p2p_config {
 +      /**
 +       * country - Country code to use in P2P operations
 +       */
 +      char country[3];
 +
 +      /**
 +       * reg_class - Regulatory class for own listen channel
 +       */
 +      u8 reg_class;
 +
 +      /**
 +       * channel - Own listen channel
 +       */
 +      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;
 +
 +      /**
 +       * op_channel - Own operational channel
 +       */
 +      u8 op_channel;
 +
 +      /**
 +       * cfg_op_channel - Whether op_channel is hardcoded in configuration
 +       */
 +      u8 cfg_op_channel;
 +
 +      /**
 +       * channels - Own supported regulatory classes and channels
 +       *
 +       * List of supposerted channels per regulatory class. The regulatory
 +       * classes are defined in IEEE Std 802.11-2007 Annex J and the
 +       * numbering of the clases depends on the configured country code.
 +       */
 +      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;
 +
 +      /**
 +       * pref_chan - Preferred channels for GO Negotiation
 +       */
 +      struct p2p_channel *pref_chan;
 +
 +      /**
 +       * pri_dev_type - Primary Device Type (see WPS)
 +       */
 +      u8 pri_dev_type[8];
 +
 +      /**
 +       * P2P_SEC_DEVICE_TYPES - Maximum number of secondary device types
 +       */
 +#define P2P_SEC_DEVICE_TYPES 5
 +
 +      /**
 +       * sec_dev_type - Optional secondary device types
 +       */
 +      u8 sec_dev_type[P2P_SEC_DEVICE_TYPES][8];
 +
 +      /**
 +       * num_sec_dev_types - Number of sec_dev_type entries
 +       */
 +      size_t num_sec_dev_types;
 +
 +      /**
 +       * dev_addr - P2P Device Address
 +       */
 +      u8 dev_addr[ETH_ALEN];
 +
 +      /**
 +       * dev_name - Device Name
 +       */
 +      char *dev_name;
 +
 +      char *manufacturer;
 +      char *model_name;
 +      char *model_number;
 +      char *serial_number;
 +
 +      u8 uuid[16];
 +      u16 config_methods;
 +
 +      /**
 +       * concurrent_operations - Whether concurrent operations are supported
 +       */
 +      int concurrent_operations;
 +
 +      /**
 +       * max_peers - Maximum number of discovered peers to remember
 +       *
 +       * If more peers are discovered, older entries will be removed to make
 +       * room for the new ones.
 +       */
 +      size_t max_peers;
 +
 +      /**
 +       * p2p_intra_bss - Intra BSS communication is supported
 +       */
 +      int p2p_intra_bss;
 +
 +      /**
 +       * ssid_postfix - Postfix data to add to the SSID
 +       *
 +       * This data will be added to the end of the SSID after the
 +       * DIRECT-<random two octets> prefix.
 +       */
-        * sent on the same channel or to be dropped if the driver is not
-        * anymore listening to Probe Request frames.
++      u8 ssid_postfix[SSID_MAX_LEN - 9];
 +
 +      /**
 +       * ssid_postfix_len - Length of the ssid_postfix data
 +       */
 +      size_t ssid_postfix_len;
 +
 +      /**
 +       * max_listen - Maximum listen duration in ms
 +       */
 +      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;
 +
 +      /**
 +       * debug_print - Debug print
 +       * @ctx: Callback context from cb_ctx
 +       * @level: Debug verbosity level (MSG_*)
 +       * @msg: Debug message
 +       */
 +      void (*debug_print)(void *ctx, int level, const char *msg);
 +
 +
 +      /* Callbacks to request lower layer driver operations */
 +
 +      /**
 +       * p2p_scan - Request a P2P scan/search
 +       * @ctx: Callback context from cb_ctx
 +       * @type: Scan type
 +       * @freq: Specific frequency (MHz) to scan or 0 for no restriction
 +       * @num_req_dev_types: Number of requested device types
 +       * @req_dev_types: Array containing requested device types
 +       * @dev_id: Device ID to search for or %NULL to find all devices
 +       * @pw_id: Device Password ID
 +       * Returns: 0 on success, -1 on failure
 +       *
 +       * This callback function is used to request a P2P scan or search
 +       * 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. @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.
 +       *
 +       * The full scan is used for the initial scan to find group owners from
 +       * all. The other types are used during search phase scan of the social
 +       * channels (with potential variation if the Listen channel of the
 +       * target peer is known or if other channels are scanned in steps).
 +       *
 +       * The scan results are returned after this call by calling
 +       * p2p_scan_res_handler() for each scan result that has a P2P IE and
 +       * then calling p2p_scan_res_handled() to indicate that all scan
 +       * results have been indicated.
 +       */
 +      int (*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);
 +
 +      /**
 +       * send_probe_resp - Transmit a Probe Response frame
 +       * @ctx: Callback context from cb_ctx
 +       * @buf: Probe Response frame (including the header and body)
++       * @freq: Forced frequency (in MHz) to use or 0.
 +       * Returns: 0 on success, -1 on failure
 +       *
 +       * This function is used to reply to Probe Request frames that were
 +       * indicated with a call to p2p_probe_req_rx(). The response is to be
-       int (*send_probe_resp)(void *ctx, const struct wpabuf *buf);
++       * sent on the same channel, unless otherwise specified, or to be
++       * dropped if the driver is not listening to Probe Request frames
++       * anymore.
 +       *
 +       * Alternatively, the responsibility for building the Probe Response
 +       * frames in Listen state may be in another system component in which
 +       * case this function need to be implemented (i.e., the function
 +       * pointer can be %NULL). The WPS and P2P IEs to be added for Probe
 +       * Response frames in such a case are available from the
 +       * start_listen() callback. It should be noted that the received Probe
 +       * Request frames must be indicated by calling p2p_probe_req_rx() even
 +       * if this send_probe_resp() is not used.
 +       */
-       void (*go_neg_req_rx)(void *ctx, const u8 *src, u16 dev_passwd_id);
++      int (*send_probe_resp)(void *ctx, const struct wpabuf *buf,
++                             unsigned int freq);
 +
 +      /**
 +       * send_action - Transmit an Action frame
 +       * @ctx: Callback context from cb_ctx
 +       * @freq: Frequency in MHz for the channel on which to transmit
 +       * @dst: Destination MAC address (Address 1)
 +       * @src: Source MAC address (Address 2)
 +       * @bssid: BSSID (Address 3)
 +       * @buf: Frame body (starting from Category field)
 +       * @len: Length of buf in octets
 +       * @wait_time: How many msec to wait for a response frame
 +       * Returns: 0 on success, -1 on failure
 +       *
 +       * The Action frame may not be transmitted immediately and the status
 +       * of the transmission must be reported by calling
 +       * p2p_send_action_cb() once the frame has either been transmitted or
 +       * it has been dropped due to excessive retries or other failure to
 +       * transmit.
 +       */
 +      int (*send_action)(void *ctx, unsigned int freq, const u8 *dst,
 +                         const u8 *src, const u8 *bssid, const u8 *buf,
 +                         size_t len, unsigned int wait_time);
 +
 +      /**
 +       * send_action_done - Notify that Action frame sequence was completed
 +       * @ctx: Callback context from cb_ctx
 +       *
 +       * This function is called when the Action frame sequence that was
 +       * started with send_action() has been completed, i.e., when there is
 +       * no need to wait for a response from the destination peer anymore.
 +       */
 +      void (*send_action_done)(void *ctx);
 +
 +      /**
 +       * start_listen - Start Listen state
 +       * @ctx: Callback context from cb_ctx
 +       * @freq: Frequency of the listen channel in MHz
 +       * @duration: Duration for the Listen state in milliseconds
 +       * @probe_resp_ie: IE(s) to be added to Probe Response frames
 +       * Returns: 0 on success, -1 on failure
 +       *
 +       * This Listen state may not start immediately since the driver may
 +       * have other pending operations to complete first. Once the Listen
 +       * state has started, p2p_listen_cb() must be called to notify the P2P
 +       * module. Once the Listen state is stopped, p2p_listen_end() must be
 +       * called to notify the P2P module that the driver is not in the Listen
 +       * state anymore.
 +       *
 +       * If the send_probe_resp() is not used for generating the response,
 +       * the IEs from probe_resp_ie need to be added to the end of the Probe
 +       * Response frame body. If send_probe_resp() is used, the probe_resp_ie
 +       * information can be ignored.
 +       */
 +      int (*start_listen)(void *ctx, unsigned int freq,
 +                          unsigned int duration,
 +                          const struct wpabuf *probe_resp_ie);
 +      /**
 +       * stop_listen - Stop Listen state
 +       * @ctx: Callback context from cb_ctx
 +       *
 +       * This callback can be used to stop a Listen state operation that was
 +       * previously requested with start_listen().
 +       */
 +      void (*stop_listen)(void *ctx);
 +
 +      /**
 +       * get_noa - Get current Notice of Absence attribute payload
 +       * @ctx: Callback context from cb_ctx
 +       * @interface_addr: P2P Interface Address of the GO
 +       * @buf: Buffer for returning NoA
 +       * @buf_len: Buffer length in octets
 +       * Returns: Number of octets used in buf, 0 to indicate no NoA is being
 +       * advertized, or -1 on failure
 +       *
 +       * This function is used to fetch the current Notice of Absence
 +       * attribute value from GO.
 +       */
 +      int (*get_noa)(void *ctx, const u8 *interface_addr, u8 *buf,
 +                     size_t buf_len);
 +
 +      /* Callbacks to notify events to upper layer management entity */
 +
 +      /**
 +       * dev_found - Notification of a found P2P Device
 +       * @ctx: Callback context from cb_ctx
 +       * @addr: Source address of the message triggering this notification
 +       * @info: P2P peer information
 +       * @new_device: Inform if the peer is newly found
 +       *
 +       * This callback is used to notify that a new P2P Device has been
 +       * found. This may happen, e.g., during Search state based on scan
 +       * results or during Listen state based on receive Probe Request and
 +       * Group Owner Negotiation Request.
 +       */
 +      void (*dev_found)(void *ctx, const u8 *addr,
 +                        const struct p2p_peer_info *info,
 +                        int new_device);
 +
 +      /**
 +       * dev_lost - Notification of a lost P2P Device
 +       * @ctx: Callback context from cb_ctx
 +       * @dev_addr: P2P Device Address of the lost P2P Device
 +       *
 +       * This callback is used to notify that a P2P Device has been deleted.
 +       */
 +      void (*dev_lost)(void *ctx, const u8 *dev_addr);
 +
 +      /**
 +       * find_stopped - Notification of a p2p_find operation stopping
 +       * @ctx: Callback context from cb_ctx
 +       */
 +      void (*find_stopped)(void *ctx);
 +
 +      /**
 +       * go_neg_req_rx - Notification of a receive GO Negotiation Request
 +       * @ctx: Callback context from cb_ctx
 +       * @src: Source address of the message triggering this notification
 +       * @dev_passwd_id: WPS Device Password ID
++       * @go_intent: Peer's GO Intent
 +       *
 +       * This callback is used to notify that a P2P Device is requesting
 +       * group owner negotiation with us, but we do not have all the
 +       * necessary information to start GO Negotiation. This indicates that
 +       * the local user has not authorized the connection yet by providing a
 +       * PIN or PBC button press. This information can be provided with a
 +       * call to p2p_connect().
 +       */
-        * @go_dev_addr: Buffer for returning intended GO P2P Device Address
++      void (*go_neg_req_rx)(void *ctx, const u8 *src, u16 dev_passwd_id,
++                            u8 go_intent);
 +
 +      /**
 +       * go_neg_completed - Notification of GO Negotiation results
 +       * @ctx: Callback context from cb_ctx
 +       * @res: GO Negotiation results
 +       *
 +       * This callback is used to notify that Group Owner Negotiation has
 +       * been completed. Non-zero struct p2p_go_neg_results::status indicates
 +       * failed negotiation. In case of success, this function is responsible
 +       * for creating a new group interface (or using the existing interface
 +       * depending on driver features), setting up the group interface in
 +       * proper mode based on struct p2p_go_neg_results::role_go and
 +       * initializing WPS provisioning either as a Registrar (if GO) or as an
 +       * Enrollee. Successful WPS provisioning must be indicated by calling
 +       * p2p_wps_success_cb(). The callee is responsible for timing out group
 +       * formation if WPS provisioning cannot be completed successfully
 +       * within 15 seconds.
 +       */
 +      void (*go_neg_completed)(void *ctx, struct p2p_go_neg_results *res);
 +
 +      /**
 +       * sd_request - Callback on Service Discovery Request
 +       * @ctx: Callback context from cb_ctx
 +       * @freq: Frequency (in MHz) of the channel
 +       * @sa: Source address of the request
 +       * @dialog_token: Dialog token
 +       * @update_indic: Service Update Indicator from the source of request
 +       * @tlvs: P2P Service Request TLV(s)
 +       * @tlvs_len: Length of tlvs buffer in octets
 +       *
 +       * This callback is used to indicate reception of a service discovery
 +       * request. Response to the query must be indicated by calling
 +       * p2p_sd_response() with the context information from the arguments to
 +       * this callback function.
 +       *
 +       * This callback handler can be set to %NULL to indicate that service
 +       * discovery is not supported.
 +       */
 +      void (*sd_request)(void *ctx, int freq, const u8 *sa, u8 dialog_token,
 +                         u16 update_indic, const u8 *tlvs, size_t tlvs_len);
 +
 +      /**
 +       * sd_response - Callback on Service Discovery Response
 +       * @ctx: Callback context from cb_ctx
 +       * @sa: Source address of the request
 +       * @update_indic: Service Update Indicator from the source of response
 +       * @tlvs: P2P Service Response TLV(s)
 +       * @tlvs_len: Length of tlvs buffer in octets
 +       *
 +       * This callback is used to indicate reception of a service discovery
 +       * response. This callback handler can be set to %NULL if no service
 +       * discovery requests are used. The information provided with this call
 +       * is replies to the queries scheduled with p2p_sd_request().
 +       */
 +      void (*sd_response)(void *ctx, const u8 *sa, u16 update_indic,
 +                          const u8 *tlvs, size_t tlvs_len);
 +
 +      /**
 +       * prov_disc_req - Callback on Provisiong Discovery Request
 +       * @ctx: Callback context from cb_ctx
 +       * @peer: Source address of the request
 +       * @config_methods: Requested WPS Config Method
 +       * @dev_addr: P2P Device Address of the found P2P Device
 +       * @pri_dev_type: Primary Device Type
 +       * @dev_name: Device Name
 +       * @supp_config_methods: Supported configuration Methods
 +       * @dev_capab: Device Capabilities
 +       * @group_capab: Group Capabilities
 +       * @group_id: P2P Group ID (or %NULL if not included)
 +       * @group_id_len: Length of P2P Group ID
 +       *
 +       * This callback is used to indicate reception of a Provision Discovery
 +       * Request frame that the P2P module accepted.
 +       */
 +      void (*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);
 +
 +      /**
 +       * prov_disc_resp - Callback on Provisiong Discovery Response
 +       * @ctx: Callback context from cb_ctx
 +       * @peer: Source address of the response
 +       * @config_methods: Value from p2p_prov_disc_req() or 0 on failure
 +       *
 +       * This callback is used to indicate reception of a Provision Discovery
 +       * Response frame for a pending request scheduled with
 +       * p2p_prov_disc_req(). This callback handler can be set to %NULL if
 +       * provision discovery is not used.
 +       */
 +      void (*prov_disc_resp)(void *ctx, const u8 *peer, u16 config_methods);
 +
 +      /**
 +       * prov_disc_fail - Callback on Provision Discovery failure
 +       * @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.
 +       *
 +       * This callback handler can be set to %NULL if provision discovery
 +       * 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,
 +                             u32 adv_id, const u8 *adv_mac,
 +                             const char *deferred_session_resp);
 +
 +      /**
 +       * invitation_process - Optional callback for processing Invitations
 +       * @ctx: Callback context from cb_ctx
 +       * @sa: Source address of the Invitation Request
 +       * @bssid: P2P Group BSSID from the request or %NULL if not included
 +       * @go_dev_addr: GO Device Address from P2P Group ID
 +       * @ssid: SSID from P2P Group ID
 +       * @ssid_len: Length of ssid buffer in octets
 +       * @go: Variable for returning whether the local end is GO in the group
 +       * @group_bssid: Buffer for returning P2P Group BSSID (if local end GO)
 +       * @force_freq: Variable for returning forced frequency for the group
 +       * @persistent_group: Whether this is an invitation to reinvoke a
 +       *      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
 +       * by allowing automatic restarting of persistent groups without user
 +       * interaction. If this callback is not implemented (i.e., is %NULL),
 +       * the received Invitation Request frames are replied with
 +       * %P2P_SC_REQ_RECEIVED status and indicated to upper layer with the
 +       * invitation_result() callback.
 +       *
 +       * If the requested parameters are acceptable and the group is known,
 +       * %P2P_SC_SUCCESS may be returned. If the requested group is unknown,
 +       * %P2P_SC_FAIL_UNKNOWN_GROUP should be returned. %P2P_SC_REQ_RECEIVED
 +       * can be returned if there is not enough data to provide immediate
 +       * response, i.e., if some sort of user interaction is needed. The
 +       * invitation_received() callback will be called in that case
 +       * immediately after this call.
 +       */
 +      u8 (*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,
 +                               int dev_pw_id);
 +
 +      /**
 +       * invitation_received - Callback on Invitation Request RX
 +       * @ctx: Callback context from cb_ctx
 +       * @sa: Source address of the Invitation Request
 +       * @bssid: P2P Group BSSID or %NULL if not received
 +       * @ssid: SSID of the group
 +       * @ssid_len: Length of ssid in octets
 +       * @go_dev_addr: GO Device Address
 +       * @status: Response Status
 +       * @op_freq: Operational frequency for the group
 +       *
 +       * This callback is used to indicate sending of an Invitation Response
 +       * for a received Invitation Request. If status == 0 (success), the
 +       * upper layer code is responsible for starting the group. status == 1
 +       * indicates need to get user authorization for the group. Other status
 +       * values indicate that the invitation request was rejected.
 +       */
 +      void (*invitation_received)(void *ctx, const u8 *sa, const u8 *bssid,
 +                                  const u8 *ssid, size_t ssid_len,
 +                                  const u8 *go_dev_addr, u8 status,
 +                                  int op_freq);
 +
 +      /**
 +       * invitation_result - Callback on Invitation result
 +       * @ctx: Callback context from cb_ctx
 +       * @status: Negotiation result (Status Code)
 +       * @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
 +       * the value received from the peer in Invitation Response with 0
 +       * (P2P_SC_SUCCESS) indicating success or -1 to indicate a timeout or a
 +       * local failure in transmitting the Invitation Request.
 +       */
 +      void (*invitation_result)(void *ctx, int status, const u8 *bssid,
 +                                const struct p2p_channels *channels,
 +                                const u8 *addr, int freq, int peer_oper_freq);
 +
 +      /**
 +       * go_connected - Check whether we are connected to a GO
 +       * @ctx: Callback context from cb_ctx
 +       * @dev_addr: P2P Device Address of a GO
 +       * Returns: 1 if we are connected as a P2P client to the specified GO
 +       * 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
++       * and allocate interface for this group if needed
 +       * @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
-                                   u8 *ret_ssid, size_t *ret_ssid_len);
++       * @go_dev_addr: Buffer for returning GO P2P Device Address
 +       * @ret_ssid: Buffer for returning group SSID
 +       * @ret_ssid_len: Buffer for returning length of @ssid
++       * @intended_iface_addr: Buffer for returning intended iface address
 +       * 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,
-                                  int prov_start, const char *session_info);
++                                  u8 *ret_ssid, size_t *ret_ssid_len,
++                                  u8 *intended_iface_addr);
 +
 +      /**
 +       * 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,
-                const u8 *bssid, const u8 *ie, size_t ie_len);
++                                 int prov_start, const char *session_info,
++                                 const u8 *feat_cap, size_t feat_cap_len);
 +
 +      /**
 +       * 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);
++
++      /**
++       * get_pref_freq_list - Get preferred frequency list for an interface
++       * @ctx: Callback context from cb_ctx
++       * @go: Whether the use if for GO role
++       * @len: Length of freq_list in entries (both IN and OUT)
++       * @freq_list: Buffer for returning the preferred frequencies (MHz)
++       * Returns: 0 on success, -1 on failure
++       *
++       * This function can be used to query the preferred frequency list from
++       * the driver specific to a particular interface type.
++       */
++      int (*get_pref_freq_list)(void *ctx, int go,
++                                unsigned int *len, unsigned int *freq_list);
 +};
 +
 +
 +/* P2P module initialization/deinitialization */
 +
 +/**
 + * p2p_init - Initialize P2P module
 + * @cfg: P2P module configuration
 + * Returns: Pointer to private data or %NULL on failure
 + *
 + * This function is used to initialize global P2P module context (one per
 + * device). The P2P module will keep a copy of the configuration data, so the
 + * caller does not need to maintain this structure. However, the callback
 + * functions and the context parameters to them must be kept available until
 + * the P2P module is deinitialized with p2p_deinit().
 + */
 +struct p2p_data * p2p_init(const struct p2p_config *cfg);
 +
 +/**
 + * p2p_deinit - Deinitialize P2P module
 + * @p2p: P2P module context from p2p_init()
 + */
 +void p2p_deinit(struct p2p_data *p2p);
 +
 +/**
 + * p2p_flush - Flush P2P module state
 + * @p2p: P2P module context from p2p_init()
 + *
 + * This command removes the P2P module state like peer device entries.
 + */
 +void p2p_flush(struct p2p_data *p2p);
 +
 +/**
 + * p2p_unauthorize - Unauthorize the specified peer device
 + * @p2p: P2P module context from p2p_init()
 + * @addr: P2P peer entry to be unauthorized
 + * Returns: 0 on success, -1 on failure
 + *
 + * This command removes any connection authorization from the specified P2P
 + * peer device address. This can be used, e.g., to cancel effect of a previous
 + * p2p_authorize() or p2p_connect() call that has not yet resulted in completed
 + * GO Negotiation.
 + */
 +int p2p_unauthorize(struct p2p_data *p2p, const u8 *addr);
 +
 +/**
 + * p2p_set_dev_name - Set device name
 + * @p2p: P2P module context from p2p_init()
 + * Returns: 0 on success, -1 on failure
 + *
 + * This function can be used to update the P2P module configuration with
 + * information that was not available at the time of the p2p_init() call.
 + */
 +int p2p_set_dev_name(struct p2p_data *p2p, const char *dev_name);
 +
 +int p2p_set_manufacturer(struct p2p_data *p2p, const char *manufacturer);
 +int p2p_set_model_name(struct p2p_data *p2p, const char *model_name);
 +int p2p_set_model_number(struct p2p_data *p2p, const char *model_number);
 +int p2p_set_serial_number(struct p2p_data *p2p, const char *serial_number);
 +
 +void p2p_set_config_methods(struct p2p_data *p2p, u16 config_methods);
 +void p2p_set_uuid(struct p2p_data *p2p, const u8 *uuid);
 +
 +/**
 + * p2p_set_pri_dev_type - Set primary device type
 + * @p2p: P2P module context from p2p_init()
 + * Returns: 0 on success, -1 on failure
 + *
 + * This function can be used to update the P2P module configuration with
 + * information that was not available at the time of the p2p_init() call.
 + */
 +int p2p_set_pri_dev_type(struct p2p_data *p2p, const u8 *pri_dev_type);
 +
 +/**
 + * p2p_set_sec_dev_types - Set secondary device types
 + * @p2p: P2P module context from p2p_init()
 + * Returns: 0 on success, -1 on failure
 + *
 + * This function can be used to update the P2P module configuration with
 + * information that was not available at the time of the p2p_init() call.
 + */
 +int p2p_set_sec_dev_types(struct p2p_data *p2p, const u8 dev_types[][8],
 +                        size_t num_dev_types);
 +
 +int p2p_set_country(struct p2p_data *p2p, const char *country);
 +
 +
 +/* Commands from upper layer management entity */
 +
 +enum p2p_discovery_type {
 +      P2P_FIND_START_WITH_FULL,
 +      P2P_FIND_ONLY_SOCIAL,
 +      P2P_FIND_PROGRESSIVE
 +};
 +
 +/**
 + * p2p_find - Start P2P Find (Device Discovery)
 + * @p2p: P2P module context from p2p_init()
 + * @timeout: Timeout for find operation in seconds or 0 for no timeout
 + * @type: Device Discovery type
 + * @num_req_dev_types: Number of requested device types
 + * @req_dev_types: Requested device types array, must be an array
 + *    containing num_req_dev_types * WPS_DEV_TYPE_LEN bytes; %NULL if no
 + *    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,
 +           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)
 + * @p2p: P2P module context from p2p_init()
 + */
 +void p2p_stop_find(struct p2p_data *p2p);
 +
 +/**
 + * p2p_stop_find_for_freq - Stop P2P Find for next oper on specific freq
 + * @p2p: P2P module context from p2p_init()
 + * @freq: Frequency in MHz for next operation
 + *
 + * This is like p2p_stop_find(), but Listen state is not stopped if we are
 + * already on the same frequency.
 + */
 +void p2p_stop_find_for_freq(struct p2p_data *p2p, int freq);
 +
 +/**
 + * p2p_listen - Start P2P Listen state for specified duration
 + * @p2p: P2P module context from p2p_init()
 + * @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.
 + */
 +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
 + * @wps_method: WPS method to be used in provisioning
 + * @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 a persistent group (0 = no, 1 =
 + * persistent group without persistent reconnect, 2 = persistent group with
 + * persistent reconnect)
 + * @force_ssid: Forced SSID for the group if we become GO or %NULL to generate
 + *    a new SSID
 + * @force_ssid_len: Length of $force_ssid buffer
 + * @pd_before_go_neg: Whether to send Provision Discovery prior to GO
 + *    Negotiation as an interoperability workaround when initiating group
 + *    formation
 + * @pref_freq: Preferred operating frequency in MHz or 0 (this is only used if
 + *    force_freq == 0)
 + * Returns: 0 on success, -1 on failure
 + */
 +int p2p_connect(struct p2p_data *p2p, const u8 *peer_addr,
 +              enum p2p_wps_method wps_method,
 +              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, u16 oob_pw_id);
 +
 +/**
 + * p2p_authorize - Authorize P2P group formation (GO negotiation)
 + * @p2p: P2P module context from p2p_init()
 + * @peer_addr: MAC address of the peer P2P client
 + * @wps_method: WPS method to be used in provisioning
 + * @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 a persistent group (0 = no, 1 =
 + * persistent group without persistent reconnect, 2 = persistent group with
 + * persistent reconnect)
 + * @force_ssid: Forced SSID for the group if we become GO or %NULL to generate
 + *    a new SSID
 + * @force_ssid_len: Length of $force_ssid buffer
 + * @pref_freq: Preferred operating frequency in MHz or 0 (this is only used if
 + *    force_freq == 0)
 + * Returns: 0 on success, -1 on failure
 + *
 + * This is like p2p_connect(), but the actual group negotiation is not
 + * initiated automatically, i.e., the other end is expected to do that.
 + */
 +int p2p_authorize(struct p2p_data *p2p, const u8 *peer_addr,
 +                enum p2p_wps_method wps_method,
 +                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, u16 oob_pw_id);
 +
 +/**
 + * p2p_reject - Reject peer device (explicitly block connection attempts)
 + * @p2p: P2P module context from p2p_init()
 + * @peer_addr: MAC address of the peer P2P client
 + * Returns: 0 on success, -1 on failure
 + */
 +int p2p_reject(struct p2p_data *p2p, const u8 *peer_addr);
 +
 +/**
 + * 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)
 + * @user_initiated_pd: Flag to indicate if initiated by user or not
 + * 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 p2p_config::prov_disc_resp() callback.
 + */
 +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);
 +
 +/**
 + * p2p_sd_request - Schedule a service discovery query
 + * @p2p: P2P module context from p2p_init()
 + * @dst: Destination peer or %NULL to apply for all peers
 + * @tlvs: P2P Service Query TLV(s)
 + * Returns: Reference to the query or %NULL on failure
 + *
 + * Response to the query is indicated with the p2p_config::sd_response()
 + * callback.
 + */
 +void * p2p_sd_request(struct p2p_data *p2p, const u8 *dst,
 +                    const struct wpabuf *tlvs);
 +
 +#ifdef CONFIG_WIFI_DISPLAY
 +void * p2p_sd_request_wfd(struct p2p_data *p2p, const u8 *dst,
 +                        const struct wpabuf *tlvs);
 +#endif /* CONFIG_WIFI_DISPLAY */
 +
 +/**
 + * p2p_sd_cancel_request - Cancel a pending service discovery query
 + * @p2p: P2P module context from p2p_init()
 + * @req: Query reference from p2p_sd_request()
 + * Returns: 0 if request for cancelled; -1 if not found
 + */
 +int p2p_sd_cancel_request(struct p2p_data *p2p, void *req);
 +
 +/**
 + * p2p_sd_response - Send response to a service discovery query
 + * @p2p: P2P module context from p2p_init()
 + * @freq: Frequency from p2p_config::sd_request() callback
 + * @dst: Destination address from p2p_config::sd_request() callback
 + * @dialog_token: Dialog token from p2p_config::sd_request() callback
 + * @resp_tlvs: P2P Service Response TLV(s)
 + *
 + * This function is called as a response to the request indicated with
 + * p2p_config::sd_request() callback.
 + */
 +void p2p_sd_response(struct p2p_data *p2p, int freq, const u8 *dst,
 +                   u8 dialog_token, const struct wpabuf *resp_tlvs);
 +
 +/**
 + * p2p_sd_service_update - Indicate a change in local services
 + * @p2p: P2P module context from p2p_init()
 + *
 + * 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.
 + */
 +void p2p_sd_service_update(struct p2p_data *p2p);
 +
 +
 +enum p2p_invite_role {
 +      P2P_INVITE_ROLE_GO,
 +      P2P_INVITE_ROLE_ACTIVE_GO,
 +      P2P_INVITE_ROLE_CLIENT
 +};
 +
 +/**
 + * p2p_invite - Invite a P2P Device into a group
 + * @p2p: P2P module context from p2p_init()
 + * @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
 + * @force_freq: The only allowed channel frequency in MHz or 0
 + * @go_dev_addr: Forced GO Device Address or %NULL if none
 + * @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 dev_pw_id);
 +
 +/**
 + * p2p_presence_req - Request GO presence
 + * @p2p: P2P module context from p2p_init()
 + * @go_interface_addr: GO P2P Interface Address
 + * @own_interface_addr: Own P2P Interface Address for this group
 + * @freq: Group operating frequence (in MHz)
 + * @duration1: Preferred presence duration in microseconds
 + * @interval1: Preferred presence interval in microseconds
 + * @duration2: Acceptable presence duration in microseconds
 + * @interval2: Acceptable presence interval in microseconds
 + * Returns: 0 on success, -1 on failure
 + *
 + * If both duration and interval values are zero, the parameter pair is not
 + * specified (i.e., to remove Presence Request, use duration1 = interval1 = 0).
 + */
 +int p2p_presence_req(struct p2p_data *p2p, const u8 *go_interface_addr,
 +                   const u8 *own_interface_addr, unsigned int freq,
 +                   u32 duration1, u32 interval1, u32 duration2,
 +                   u32 interval2);
 +
 +/**
 + * p2p_ext_listen - Set Extended Listen Timing
 + * @p2p: P2P module context from p2p_init()
 + * @freq: Group operating frequence (in MHz)
 + * @period: Availability period in milliseconds (1-65535; 0 to disable)
 + * @interval: Availability interval in milliseconds (1-65535; 0 to disable)
 + * Returns: 0 on success, -1 on failure
 + *
 + * This function can be used to enable or disable (period = interval = 0)
 + * Extended Listen Timing. When enabled, the P2P Device will become
 + * discoverable (go into Listen State) every @interval milliseconds for at
 + * least @period milliseconds.
 + */
 +int p2p_ext_listen(struct p2p_data *p2p, unsigned int period,
 +                 unsigned int interval);
 +
 +/* Event notifications from upper layer management operations */
 +
 +/**
 + * p2p_wps_success_cb - Report successfully completed WPS provisioning
 + * @p2p: P2P module context from p2p_init()
 + * @mac_addr: Peer address
 + *
 + * This function is used to report successfully completed WPS provisioning
 + * during group formation in both GO/Registrar and client/Enrollee roles.
 + */
 +void p2p_wps_success_cb(struct p2p_data *p2p, const u8 *mac_addr);
 +
 +/**
 + * p2p_group_formation_failed - Report failed WPS provisioning
 + * @p2p: P2P module context from p2p_init()
 + *
 + * 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.
 + */
 +void p2p_group_formation_failed(struct p2p_data *p2p);
 +
 +/**
 + * p2p_get_provisioning_info - Get any stored provisioning info
 + * @p2p: P2P module context from p2p_init()
 + * @addr: Peer P2P Device Address
 + * Returns: WPS provisioning information (WPS config method) or 0 if no
 + * information is available
 + *
 + * This function is used to retrieve stored WPS provisioning info for the given
 + * peer.
 + */
 +u16 p2p_get_provisioning_info(struct p2p_data *p2p, const u8 *addr);
 +
 +/**
 + * p2p_clear_provisioning_info - Clear any stored provisioning info
 + * @p2p: P2P module context from p2p_init()
 + * @iface_addr: Peer P2P Device Address
 + *
 + * This function is used to clear stored WPS provisioning info for the given
 + * peer.
 + */
 +void p2p_clear_provisioning_info(struct p2p_data *p2p, const u8 *addr);
 +
 +
 +/* Event notifications from lower layer driver operations */
 +
 +/**
 + * enum p2p_probe_req_status
 + *
 + * @P2P_PREQ_MALFORMED: frame was not well-formed
 + * @P2P_PREQ_NOT_LISTEN: device isn't in listen state, frame ignored
 + * @P2P_PREQ_NOT_P2P: frame was not a P2P probe request
 + * @P2P_PREQ_P2P_NOT_PROCESSED: frame was P2P but wasn't processed
 + * @P2P_PREQ_P2P_PROCESSED: frame has been processed by P2P
 + */
 +enum p2p_probe_req_status {
 +      P2P_PREQ_MALFORMED,
 +      P2P_PREQ_NOT_LISTEN,
 +      P2P_PREQ_NOT_P2P,
 +      P2P_PREQ_NOT_PROCESSED,
 +      P2P_PREQ_PROCESSED
 +};
 +
 +/**
 + * p2p_probe_req_rx - Report reception of a Probe Request frame
 + * @p2p: P2P module context from p2p_init()
 + * @addr: Source MAC address
 + * @dst: Destination MAC address if available or %NULL
 + * @bssid: BSSID if available or %NULL
 + * @ie: Information elements from the Probe Request frame body
 + * @ie_len: Length of ie buffer in octets
++ * @rx_freq: Probe Request frame RX frequency
 + * Returns: value indicating the type and status of the probe request
 + */
 +enum p2p_probe_req_status
 +p2p_probe_req_rx(struct p2p_data *p2p, const u8 *addr, const u8 *dst,
-       u8 ssid[32];
++               const u8 *bssid, const u8 *ie, size_t ie_len,
++               unsigned int rx_freq);
 +
 +/**
 + * p2p_rx_action - Report received Action frame
 + * @p2p: P2P module context from p2p_init()
 + * @da: Destination address of the received Action frame
 + * @sa: Source address of the received Action frame
 + * @bssid: Address 3 of the received Action frame
 + * @category: Category of the received Action frame
 + * @data: Action frame body after the Category field
 + * @len: Length of the data buffer in octets
 + * @freq: Frequency (in MHz) on which the frame was received
 + */
 +void p2p_rx_action(struct p2p_data *p2p, const u8 *da, const u8 *sa,
 +                 const u8 *bssid, u8 category,
 +                 const u8 *data, size_t len, int freq);
 +
 +/**
 + * p2p_scan_res_handler - Indicate a P2P scan results
 + * @p2p: P2P module context from p2p_init()
 + * @bssid: BSSID of the scan result
 + * @freq: Frequency of the channel on which the device was found in MHz
 + * @rx_time: Time when the result was received
 + * @level: Signal level (signal strength of the received Beacon/Probe Response
 + *    frame)
 + * @ies: Pointer to IEs from the scan result
 + * @ies_len: Length of the ies buffer
 + * Returns: 0 to continue or 1 to stop scan result indication
 + *
 + * This function is called to indicate a scan result entry with P2P IE from a
 + * scan requested with struct p2p_config::p2p_scan(). This can be called during
 + * the actual scan process (i.e., whenever a new device is found) or as a
 + * sequence of calls after the full scan has been completed. The former option
 + * can result in optimized operations, but may not be supported by all
 + * driver/firmware designs. The ies buffer need to include at least the P2P IE,
 + * but it is recommended to include all IEs received from the device. The
 + * caller does not need to check that the IEs contain a P2P IE before calling
 + * this function since frames will be filtered internally if needed.
 + *
 + * This function will return 1 if it wants to stop scan result iteration (and
 + * scan in general if it is still in progress). This is used to allow faster
 + * 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_reltime *rx_time, int level, const u8 *ies,
 +                       size_t ies_len);
 +
 +/**
 + * p2p_scan_res_handled - Indicate end of scan results
 + * @p2p: P2P module context from p2p_init()
 + *
 + * This function is called to indicate that all P2P scan results from a scan
 + * have been reported with zero or more calls to p2p_scan_res_handler(). This
 + * function must be called as a response to successful
 + * struct p2p_config::p2p_scan() call if none of the p2p_scan_res_handler()
 + * calls stopped iteration.
 + */
 +void p2p_scan_res_handled(struct p2p_data *p2p);
 +
 +enum p2p_send_action_result {
 +      P2P_SEND_ACTION_SUCCESS /* Frame was send and acknowledged */,
 +      P2P_SEND_ACTION_NO_ACK /* Frame was sent, but not acknowledged */,
 +      P2P_SEND_ACTION_FAILED /* Frame was not sent due to a failure */
 +};
 +
 +/**
 + * p2p_send_action_cb - Notify TX status of an Action frame
 + * @p2p: P2P module context from p2p_init()
 + * @freq: Channel frequency in MHz
 + * @dst: Destination MAC address (Address 1)
 + * @src: Source MAC address (Address 2)
 + * @bssid: BSSID (Address 3)
 + * @result: Result of the transmission attempt
 + *
 + * This function is used to indicate the result of an Action frame transmission
 + * that was requested with struct p2p_config::send_action() callback.
 + */
 +void p2p_send_action_cb(struct p2p_data *p2p, unsigned int freq, const u8 *dst,
 +                      const u8 *src, const u8 *bssid,
 +                      enum p2p_send_action_result result);
 +
 +/**
 + * p2p_listen_cb - Indicate the start of a requested Listen state
 + * @p2p: P2P module context from p2p_init()
 + * @freq: Listen channel frequency in MHz
 + * @duration: Duration for the Listen state in milliseconds
 + *
 + * This function is used to indicate that a Listen state requested with
 + * struct p2p_config::start_listen() callback has started.
 + */
 +void p2p_listen_cb(struct p2p_data *p2p, unsigned int freq,
 +                 unsigned int duration);
 +
 +/**
 + * p2p_listen_end - Indicate the end of a requested Listen state
 + * @p2p: P2P module context from p2p_init()
 + * @freq: Listen channel frequency in MHz
 + * Returns: 0 if no operations were started, 1 if an operation was started
 + *
 + * This function is used to indicate that a Listen state requested with
 + * struct p2p_config::start_listen() callback has ended.
 + */
 +int p2p_listen_end(struct p2p_data *p2p, unsigned int freq);
 +
 +void p2p_deauth_notif(struct p2p_data *p2p, const u8 *bssid, u16 reason_code,
 +                    const u8 *ie, size_t ie_len);
 +
 +void p2p_disassoc_notif(struct p2p_data *p2p, const u8 *bssid, u16 reason_code,
 +                      const u8 *ie, size_t ie_len);
 +
 +
 +/* Per-group P2P state for GO */
 +
 +struct p2p_group;
 +
 +/**
 + * struct p2p_group_config - P2P group configuration
 + *
 + * This configuration is provided to the P2P module during initialization of
 + * the per-group information with p2p_group_init().
 + */
 +struct p2p_group_config {
 +      /**
 +       * persistent_group - Whether the group is persistent
 +       * 0 = not a persistent group
 +       * 1 = persistent group without persistent reconnect
 +       * 2 = persistent group with persistent reconnect
 +       */
 +      int persistent_group;
 +
 +      /**
 +       * interface_addr - P2P Interface Address of the group
 +       */
 +      u8 interface_addr[ETH_ALEN];
 +
 +      /**
 +       * max_clients - Maximum number of clients in the group
 +       */
 +      unsigned int max_clients;
 +
 +      /**
 +       * ssid - Group SSID
 +       */
-       u8 go_ssid[32];
++      u8 ssid[SSID_MAX_LEN];
 +
 +      /**
 +       * ssid_len - Length of SSID
 +       */
 +      size_t ssid_len;
 +
 +      /**
 +       * freq - Operating channel of the group
 +       */
 +      int freq;
 +
 +      /**
 +       * cb_ctx - Context to use with callback functions
 +       */
 +      void *cb_ctx;
 +
 +      /**
 +       * ie_update - Notification of IE update
 +       * @ctx: Callback context from cb_ctx
 +       * @beacon_ies: P2P IE for Beacon frames or %NULL if no change
 +       * @proberesp_ies: P2P Ie for Probe Response frames
 +       *
 +       * P2P module uses this callback function to notify whenever the P2P IE
 +       * in Beacon or Probe Response frames should be updated based on group
 +       * events.
 +       *
 +       * The callee is responsible for freeing the returned buffer(s) with
 +       * wpabuf_free().
 +       */
 +      void (*ie_update)(void *ctx, struct wpabuf *beacon_ies,
 +                        struct wpabuf *proberesp_ies);
 +
 +      /**
 +       * idle_update - Notification of changes in group idle state
 +       * @ctx: Callback context from cb_ctx
 +       * @idle: Whether the group is idle (no associated stations)
 +       */
 +      void (*idle_update)(void *ctx, int idle);
 +};
 +
 +/**
 + * p2p_group_init - Initialize P2P group
 + * @p2p: P2P module context from p2p_init()
 + * @config: P2P group configuration (will be freed by p2p_group_deinit())
 + * Returns: Pointer to private data or %NULL on failure
 + *
 + * This function is used to initialize per-group P2P module context. Currently,
 + * this is only used to manage GO functionality and P2P clients do not need to
 + * create an instance of this per-group information.
 + */
 +struct p2p_group * p2p_group_init(struct p2p_data *p2p,
 +                                struct p2p_group_config *config);
 +
 +/**
 + * p2p_group_deinit - Deinitialize P2P group
 + * @group: P2P group context from p2p_group_init()
 + */
 +void p2p_group_deinit(struct p2p_group *group);
 +
 +/**
 + * p2p_group_notif_assoc - Notification of P2P client association with GO
 + * @group: P2P group context from p2p_group_init()
 + * @addr: Interface address of the P2P client
 + * @ie: IEs from the (Re)association Request frame
 + * @len: Length of the ie buffer in octets
 + * Returns: 0 on success, -1 on failure
 + */
 +int p2p_group_notif_assoc(struct p2p_group *group, const u8 *addr,
 +                        const u8 *ie, size_t len);
 +
 +/**
 + * p2p_group_assoc_resp_ie - Build P2P IE for (re)association response
 + * @group: P2P group context from p2p_group_init()
 + * @status: Status value (P2P_SC_SUCCESS if association succeeded)
 + * Returns: P2P IE for (Re)association Response or %NULL on failure
 + *
 + * The caller is responsible for freeing the returned buffer with
 + * wpabuf_free().
 + */
 +struct wpabuf * p2p_group_assoc_resp_ie(struct p2p_group *group, u8 status);
 +
 +/**
 + * p2p_group_notif_disassoc - Notification of P2P client disassociation from GO
 + * @group: P2P group context from p2p_group_init()
 + * @addr: Interface address of the P2P client
 + */
 +void p2p_group_notif_disassoc(struct p2p_group *group, const u8 *addr);
 +
 +/**
 + * p2p_group_notif_formation_done - Notification of completed group formation
 + * @group: P2P group context from p2p_group_init()
 + */
 +void p2p_group_notif_formation_done(struct p2p_group *group);
 +
 +/**
 + * p2p_group_notif_noa - Notification of NoA change
 + * @group: P2P group context from p2p_group_init()
 + * @noa: Notice of Absence attribute payload, %NULL if none
 + * @noa_len: Length of noa buffer in octets
 + * Returns: 0 on success, -1 on failure
 + *
 + * Notify the P2P group management about a new NoA contents. This will be
 + * inserted into the P2P IEs in Beacon and Probe Response frames with rest of
 + * the group information.
 + */
 +int p2p_group_notif_noa(struct p2p_group *group, const u8 *noa,
 +                      size_t noa_len);
 +
 +/**
 + * p2p_group_match_dev_type - Match device types in group with requested type
 + * @group: P2P group context from p2p_group_init()
 + * @wps: WPS TLVs from Probe Request frame (concatenated WPS IEs)
 + * Returns: 1 on match, 0 on mismatch
 + *
 + * This function can be used to match the Requested Device Type attribute in
 + * WPS IE with the device types of a group member for deciding whether a GO
 + * should reply to a Probe Request frame. Match will be reported if the WPS IE
 + * is not requested any specific device type.
 + */
 +int p2p_group_match_dev_type(struct p2p_group *group, struct wpabuf *wps);
 +
 +/**
 + * p2p_group_match_dev_id - Match P2P Device Address in group with requested device id
 + */
 +int p2p_group_match_dev_id(struct p2p_group *group, struct wpabuf *p2p);
 +
 +/**
 + * p2p_group_go_discover - Send GO Discoverability Request to a group client
 + * @group: P2P group context from p2p_group_init()
 + * Returns: 0 on success (frame scheduled); -1 if client was not found
 + */
 +int p2p_group_go_discover(struct p2p_group *group, const u8 *dev_id,
 +                        const u8 *searching_dev, int rx_freq);
 +
 +
 +/* Generic helper functions */
 +
 +/**
 + * p2p_ie_text - Build text format description of P2P IE
 + * @p2p_ie: P2P IE
 + * @buf: Buffer for returning text
 + * @end: Pointer to the end of the buf area
 + * Returns: Number of octets written to the buffer or -1 on failure
 + *
 + * This function can be used to parse P2P IE contents into text format
 + * field=value lines.
 + */
 +int p2p_ie_text(struct wpabuf *p2p_ie, char *buf, char *end);
 +
 +/**
 + * p2p_scan_result_text - Build text format description of P2P IE
 + * @ies: Information elements from scan results
 + * @ies_len: ies buffer length in octets
 + * @buf: Buffer for returning text
 + * @end: Pointer to the end of the buf area
 + * Returns: Number of octets written to the buffer or -1 on failure
 + *
 + * This function can be used to parse P2P IE contents into text format
 + * field=value lines.
 + */
 +int p2p_scan_result_text(const u8 *ies, size_t ies_len, char *buf, char *end);
 +
 +/**
 + * p2p_parse_dev_addr_in_p2p_ie - Parse P2P Device Address from a concatenated
 + * P2P IE
 + * @p2p_ie: P2P IE
 + * @dev_addr: Buffer for returning P2P Device Address
 + * Returns: 0 on success or -1 if P2P Device Address could not be parsed
 + */
 +int p2p_parse_dev_addr_in_p2p_ie(struct wpabuf *p2p_ie, u8 *dev_addr);
 +
 +/**
 + * p2p_parse_dev_addr - Parse P2P Device Address from P2P IE(s)
 + * @ies: Information elements from scan results
 + * @ies_len: ies buffer length in octets
 + * @dev_addr: Buffer for returning P2P Device Address
 + * Returns: 0 on success or -1 if P2P Device Address could not be parsed
 + */
 +int p2p_parse_dev_addr(const u8 *ies, size_t ies_len, u8 *dev_addr);
 +
 +/**
 + * p2p_assoc_req_ie - Build P2P IE for (Re)Association Request frame
 + * @p2p: P2P module context from p2p_init()
 + * @bssid: BSSID
 + * @buf: Buffer for writing the P2P IE
 + * @len: Maximum buf length in octets
 + * @p2p_group: Whether this is for association with a P2P GO
 + * @p2p_ie: Reassembled P2P IE data from scan results or %NULL if none
 + * Returns: Number of octets written into buf or -1 on failure
 + */
 +int p2p_assoc_req_ie(struct p2p_data *p2p, const u8 *bssid, u8 *buf,
 +                   size_t len, int p2p_group, struct wpabuf *p2p_ie);
 +
 +/**
 + * p2p_scan_ie - Build P2P IE for Probe Request
 + * @p2p: P2P module context from p2p_init()
 + * @ies: Buffer for writing P2P IE
 + * @dev_id: Device ID to search for or %NULL for any
 + */
 +void p2p_scan_ie(struct p2p_data *p2p, struct wpabuf *ies, const u8 *dev_id);
 +
 +/**
 + * p2p_scan_ie_buf_len - Get maximum buffer length needed for p2p_scan_ie
 + * @p2p: P2P module context from p2p_init()
 + * Returns: Number of octets that p2p_scan_ie() may add to the buffer
 + */
 +size_t p2p_scan_ie_buf_len(struct p2p_data *p2p);
 +
 +/**
 + * p2p_go_params - Generate random P2P group parameters
 + * @p2p: P2P module context from p2p_init()
 + * @params: Buffer for parameters
 + * Returns: 0 on success, -1 on failure
 + */
 +int p2p_go_params(struct p2p_data *p2p, struct p2p_go_neg_results *params);
 +
 +/**
 + * p2p_get_group_capab - Get Group Capability from P2P IE data
 + * @p2p_ie: P2P IE(s) contents
 + * Returns: Group Capability
 + */
 +u8 p2p_get_group_capab(const struct wpabuf *p2p_ie);
 +
 +/**
 + * p2p_get_cross_connect_disallowed - Does WLAN AP disallows cross connection
 + * @p2p_ie: P2P IE(s) contents
 + * Returns: 0 if cross connection is allow, 1 if not
 + */
 +int p2p_get_cross_connect_disallowed(const struct wpabuf *p2p_ie);
 +
 +/**
 + * p2p_get_go_dev_addr - Get P2P Device Address from P2P IE data
 + * @p2p_ie: P2P IE(s) contents
 + * Returns: Pointer to P2P Device Address or %NULL if not included
 + */
 +const u8 * p2p_get_go_dev_addr(const struct wpabuf *p2p_ie);
 +
 +/**
 + * p2p_get_peer_info - Get P2P peer information
 + * @p2p: P2P module context from p2p_init()
 + * @addr: P2P Device Address of the peer or %NULL to indicate the first peer
 + * @next: Whether to select the peer entry following the one indicated by addr
 + * Returns: Pointer to peer info or %NULL if not found
 + */
 +const struct p2p_peer_info * p2p_get_peer_info(struct p2p_data *p2p,
 +                                             const u8 *addr, int next);
 +
 +/**
 + * p2p_get_peer_info_txt - Get internal P2P peer information in text format
 + * @info: Pointer to P2P peer info from p2p_get_peer_info()
 + * @buf: Buffer for returning text
 + * @buflen: Maximum buffer length
 + * Returns: Number of octets written to the buffer or -1 on failure
 + *
 + * Note: This information is internal to the P2P module and subject to change.
 + * As such, this should not really be used by external programs for purposes
 + * other than debugging.
 + */
 +int p2p_get_peer_info_txt(const struct p2p_peer_info *info,
 +                        char *buf, size_t buflen);
 +
 +/**
 + * p2p_peer_known - Check whether P2P peer is known
 + * @p2p: P2P module context from p2p_init()
 + * @addr: P2P Device Address of the peer
 + * Returns: 1 if the specified device is in the P2P peer table or 0 if not
 + */
 +int p2p_peer_known(struct p2p_data *p2p, const u8 *addr);
 +
 +/**
 + * p2p_set_client_discoverability - Set client discoverability capability
 + * @p2p: P2P module context from p2p_init()
 + * @enabled: Whether client discoverability will be enabled
 + *
 + * This function can be used to disable (and re-enable) client discoverability.
 + * This capability is enabled by default and should not be disabled in normal
 + * use cases, i.e., this is mainly for testing purposes.
 + */
 +void p2p_set_client_discoverability(struct p2p_data *p2p, int enabled);
 +
 +/**
 + * p2p_set_managed_oper - Set managed P2P Device operations capability
 + * @p2p: P2P module context from p2p_init()
 + * @enabled: Whether managed P2P Device operations will be enabled
 + */
 +void p2p_set_managed_oper(struct p2p_data *p2p, int enabled);
 +
 +/**
 + * 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_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,
 +                         u8 *dev_addr);
 +
 +void p2p_set_peer_filter(struct p2p_data *p2p, const u8 *addr);
 +
 +/**
 + * p2p_set_cross_connect - Set cross connection capability
 + * @p2p: P2P module context from p2p_init()
 + * @enabled: Whether cross connection will be enabled
 + */
 +void p2p_set_cross_connect(struct p2p_data *p2p, int enabled);
 +
 +int p2p_get_oper_freq(struct p2p_data *p2p, const u8 *iface_addr);
 +
 +/**
 + * p2p_set_intra_bss_dist - Set intra BSS distribution
 + * @p2p: P2P module context from p2p_init()
 + * @enabled: Whether intra BSS distribution will be enabled
 + */
 +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()
 + * @freq: Channel frequency in MHz
 + * Returns: 0 if channel not usable for P2P, 1 if usable for P2P
 + */
 +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
 + * Returns: Preferred channel
 + */
 +unsigned int p2p_get_pref_freq(struct p2p_data *p2p,
 +                             const struct p2p_channels *channels);
 +
 +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
 + * @p2p: P2P module context from p2p_init()
 + * @freq_24: Frequency (MHz) of best channel in 2.4 GHz band
 + * @freq_5: Frequency (MHz) of best channel in 5 GHz band
 + * @freq_overall: Frequency (MHz) of best channel overall
 + */
 +void p2p_set_best_channels(struct p2p_data *p2p, int freq_24, int freq_5,
 +                         int freq_overall);
 +
 +/**
 + * p2p_set_own_freq_preference - Set own preference for channel
 + * @p2p: P2P module context from p2p_init()
 + * @freq: Frequency (MHz) of the preferred channel or 0 if no preference
 + *
 + * This function can be used to set a preference on the operating channel based
 + * on frequencies used on the other virtual interfaces that share the same
 + * radio. If non-zero, this is used to try to avoid multi-channel concurrency.
 + */
 +void p2p_set_own_freq_preference(struct p2p_data *p2p, int freq);
 +
 +const u8 * p2p_get_go_neg_peer(struct p2p_data *p2p);
 +
 +/**
 + * p2p_get_group_num_members - Get number of members in group
 + * @group: P2P group context from p2p_group_init()
 + * Returns: Number of members in the group
 + */
 +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 Device Address for each call and %NULL for no more members
 + */
 +const u8 * p2p_iterate_group_members(struct p2p_group *group, void **next);
 +
 +/**
 + * p2p_group_get_dev_addr - Get a P2P Device Address of a client in a group
 + * @group: P2P group context from p2p_group_init()
 + * @addr: P2P Interface Address of the client
 + * Returns: P2P Device Address of the client if found or %NULL if no match
 + * found
 + */
 +const u8 * p2p_group_get_dev_addr(struct p2p_group *group, const u8 *addr);
 +
 +/**
 + * p2p_group_is_client_connected - Check whether a specific client is connected
 + * @group: P2P group context from p2p_group_init()
 + * @addr: P2P Device Address of the client
 + * Returns: 1 if client is connected or 0 if not
 + */
 +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
 + * @next: Whether to select the peer entry following the one indicated by addr
 + * Returns: The first P2P peer info available or %NULL if no such peer exists
 + */
 +const struct p2p_peer_info *
 +p2p_get_peer_found(struct p2p_data *p2p, const u8 *addr, int next);
 +
 +/**
 + * p2p_remove_wps_vendor_extensions - Remove WPS vendor extensions
 + * @p2p: P2P module context from p2p_init()
 + */
 +void p2p_remove_wps_vendor_extensions(struct p2p_data *p2p);
 +
 +/**
 + * p2p_add_wps_vendor_extension - Add a WPS vendor extension
 + * @p2p: P2P module context from p2p_init()
 + * @vendor_ext: The vendor extensions to add
 + * Returns: 0 on success, -1 on failure
 + *
 + * The wpabuf structures in the array are owned by the P2P
 + * module after this call.
 + */
 +int p2p_add_wps_vendor_extension(struct p2p_data *p2p,
 +                               const struct wpabuf *vendor_ext);
 +
 +/**
 + * p2p_set_oper_channel - Set the P2P operating channel
 + * @p2p: P2P module context from p2p_init()
 + * @op_reg_class: Operating regulatory class to set
 + * @op_channel: operating channel to set
 + * @cfg_op_channel : Whether op_channel is hardcoded in configuration
 + * Returns: 0 on success, -1 on failure
 + */
 +int p2p_set_oper_channel(struct p2p_data *p2p, u8 op_reg_class, u8 op_channel,
 +                       int cfg_op_channel);
 +
 +/**
 + * p2p_set_pref_chan - Set P2P preferred channel list
 + * @p2p: P2P module context from p2p_init()
 + * @num_pref_chan: Number of entries in pref_chan list
 + * @pref_chan: Preferred channels or %NULL to remove preferences
 + * Returns: 0 on success, -1 on failure
 + */
 +int p2p_set_pref_chan(struct p2p_data *p2p, unsigned int num_pref_chan,
 +                    const struct p2p_channel *pref_chan);
 +
 +/**
 + * p2p_set_no_go_freq - Set no GO channel ranges
 + * @p2p: P2P module context from p2p_init()
 + * @list: Channel ranges or %NULL to remove restriction
 + * Returns: 0 on success, -1 on failure
 + */
 +int p2p_set_no_go_freq(struct p2p_data *p2p,
 +                     const struct wpa_freq_range_list *list);
 +
 +/**
 + * p2p_in_progress - Check whether a P2P operation is progress
 + * @p2p: P2P module context from p2p_init()
 + * 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_in_progress(struct p2p_data *p2p);
 +
 +const char * p2p_wps_method_text(enum p2p_wps_method method);
 +
 +/**
 + * p2p_set_config_timeout - Set local config timeouts
 + * @p2p: P2P module context from p2p_init()
 + * @go_timeout: Time in 10 ms units it takes to start the GO mode
 + * @client_timeout: Time in 10 ms units it takes to start the client mode
 + */
 +void p2p_set_config_timeout(struct p2p_data *p2p, u8 go_timeout,
 +                          u8 client_timeout);
 +
 +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);
 +int p2p_set_wfd_ie_assoc_req(struct p2p_data *p2p, struct wpabuf *ie);
 +int p2p_set_wfd_ie_invitation(struct p2p_data *p2p, struct wpabuf *ie);
 +int p2p_set_wfd_ie_prov_disc_req(struct p2p_data *p2p, struct wpabuf *ie);
 +int p2p_set_wfd_ie_prov_disc_resp(struct p2p_data *p2p, struct wpabuf *ie);
 +int p2p_set_wfd_ie_go_neg(struct p2p_data *p2p, struct wpabuf *ie);
 +int p2p_set_wfd_dev_info(struct p2p_data *p2p, const struct wpabuf *elem);
 +int p2p_set_wfd_assoc_bssid(struct p2p_data *p2p, const struct wpabuf *elem);
 +int p2p_set_wfd_coupled_sink_info(struct p2p_data *p2p,
 +                                const struct wpabuf *elem);
 +struct wpabuf * wifi_display_encaps(struct wpabuf *subelems);
 +
 +/**
 + * p2p_set_disc_int - Set min/max discoverable interval for p2p_find
 + * @p2p: P2P module context from p2p_init()
 + * @min_disc_int: minDiscoverableInterval (in units of 100 TU); default 1
 + * @max_disc_int: maxDiscoverableInterval (in units of 100 TU); default 3
 + * @max_disc_tu: Maximum number of TUs (1.024 ms) for discoverable interval; or
 + *    -1 not to limit
 + * Returns: 0 on success, or -1 on failure
 + *
 + * This function can be used to configure minDiscoverableInterval and
 + * maxDiscoverableInterval parameters for the Listen state during device
 + * discovery (p2p_find). A random number of 100 TU units is picked for each
 + * Listen state iteration from [min_disc_int,max_disc_int] range.
 + *
 + * max_disc_tu can be used to futher limit the discoverable duration. However,
 + * it should be noted that use of this parameter is not recommended since it
 + * would not be compliant with the P2P specification.
 + */
 +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];
-                       u16 config_methods, const char *svc_info);
++      u8 go_ssid[SSID_MAX_LEN];
 +      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,
++                      const u8 *cpt_priority);
 +int p2p_service_del_asp(struct p2p_data *p2p, u32 adv_id);
++void p2p_service_flush_asp(struct p2p_data *p2p);
 +struct p2ps_advertisement * p2p_get_p2ps_adv_list(struct p2p_data *p2p);
 +
++/**
++ * p2p_expire_peers - Periodic cleanup function to expire peers
++ * @p2p: P2P module context from p2p_init()
++ *
++ * This is a cleanup function that the entity calling p2p_init() is
++ * expected to call periodically to clean up expired peer entries.
++ */
++void p2p_expire_peers(struct p2p_data *p2p);
++
++void p2p_set_own_pref_freq_list(struct p2p_data *p2p,
++                              const unsigned int *pref_freq_list,
++                              unsigned int size);
++
++/**
++ * p2p_group_get_common_freqs - Get the group common frequencies
++ * @group: P2P group context from p2p_group_init()
++ * @common_freqs: On return will hold the group common frequencies
++ * @num: On return will hold the number of group common frequencies
++ * Returns: 0 on success, -1 otherwise
++ */
++int p2p_group_get_common_freqs(struct p2p_group *group, int *common_freqs,
++                             unsigned int *num);
++
 +#endif /* P2P_H */
index 92c920662edb394348202f73b65eef47a5608549,0000000000000000000000000000000000000000..793d28ba7bdd2a20efbd84f385898fd4c9dc5b98
mode 100644,000000..100644
--- /dev/null
@@@ -1,727 -1,0 +1,835 @@@
-       wpabuf_put_data(buf, p2p->query_hash,
 +/*
 + * P2P - IE builder
 + * 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 "common.h"
 +#include "common/ieee802_11_defs.h"
++#include "common/qca-vendor.h"
 +#include "wps/wps_i.h"
 +#include "p2p_i.h"
 +
 +
 +void p2p_buf_add_action_hdr(struct wpabuf *buf, u8 subtype, u8 dialog_token)
 +{
 +      wpabuf_put_u8(buf, WLAN_ACTION_VENDOR_SPECIFIC);
 +      wpabuf_put_be32(buf, P2P_IE_VENDOR_TYPE);
 +
 +      wpabuf_put_u8(buf, subtype); /* OUI Subtype */
 +      wpabuf_put_u8(buf, dialog_token);
 +      wpa_printf(MSG_DEBUG, "P2P: * Dialog Token: %d", dialog_token);
 +}
 +
 +
 +void p2p_buf_add_public_action_hdr(struct wpabuf *buf, u8 subtype,
 +                                 u8 dialog_token)
 +{
 +      wpabuf_put_u8(buf, WLAN_ACTION_PUBLIC);
 +      wpabuf_put_u8(buf, WLAN_PA_VENDOR_SPECIFIC);
 +      wpabuf_put_be32(buf, P2P_IE_VENDOR_TYPE);
 +
 +      wpabuf_put_u8(buf, subtype); /* OUI Subtype */
 +      wpabuf_put_u8(buf, dialog_token);
 +      wpa_printf(MSG_DEBUG, "P2P: * Dialog Token: %d", dialog_token);
 +}
 +
 +
 +u8 * p2p_buf_add_ie_hdr(struct wpabuf *buf)
 +{
 +      u8 *len;
 +
 +      /* P2P IE header */
 +      wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
 +      len = wpabuf_put(buf, 1); /* IE length to be filled */
 +      wpabuf_put_be32(buf, P2P_IE_VENDOR_TYPE);
 +      wpa_printf(MSG_DEBUG, "P2P: * P2P IE header");
 +      return len;
 +}
 +
 +
 +void p2p_buf_update_ie_hdr(struct wpabuf *buf, u8 *len)
 +{
 +      /* Update P2P IE Length */
 +      *len = (u8 *) wpabuf_put(buf, 0) - len - 1;
 +}
 +
 +
 +void p2p_buf_add_capability(struct wpabuf *buf, u8 dev_capab, u8 group_capab)
 +{
 +      /* P2P Capability */
 +      wpabuf_put_u8(buf, P2P_ATTR_CAPABILITY);
 +      wpabuf_put_le16(buf, 2);
 +      wpabuf_put_u8(buf, dev_capab); /* Device Capabilities */
 +      wpabuf_put_u8(buf, group_capab); /* Group Capabilities */
 +      wpa_printf(MSG_DEBUG, "P2P: * Capability dev=%02x group=%02x",
 +                 dev_capab, group_capab);
 +}
 +
 +
 +void p2p_buf_add_go_intent(struct wpabuf *buf, u8 go_intent)
 +{
 +      /* Group Owner Intent */
 +      wpabuf_put_u8(buf, P2P_ATTR_GROUP_OWNER_INTENT);
 +      wpabuf_put_le16(buf, 1);
 +      wpabuf_put_u8(buf, go_intent);
 +      wpa_printf(MSG_DEBUG, "P2P: * GO Intent: Intent %u Tie breaker %u",
 +                 go_intent >> 1, go_intent & 0x01);
 +}
 +
 +
 +void p2p_buf_add_listen_channel(struct wpabuf *buf, const char *country,
 +                              u8 reg_class, u8 channel)
 +{
 +      /* Listen Channel */
 +      wpabuf_put_u8(buf, P2P_ATTR_LISTEN_CHANNEL);
 +      wpabuf_put_le16(buf, 5);
 +      wpabuf_put_data(buf, country, 3);
 +      wpabuf_put_u8(buf, reg_class); /* Regulatory Class */
 +      wpabuf_put_u8(buf, channel); /* Channel Number */
 +      wpa_printf(MSG_DEBUG, "P2P: * Listen Channel: Regulatory Class %u "
 +                 "Channel %u", reg_class, channel);
 +}
 +
 +
 +void p2p_buf_add_operating_channel(struct wpabuf *buf, const char *country,
 +                                 u8 reg_class, u8 channel)
 +{
 +      /* Operating Channel */
 +      wpabuf_put_u8(buf, P2P_ATTR_OPERATING_CHANNEL);
 +      wpabuf_put_le16(buf, 5);
 +      wpabuf_put_data(buf, country, 3);
 +      wpabuf_put_u8(buf, reg_class); /* Regulatory Class */
 +      wpabuf_put_u8(buf, channel); /* Channel Number */
 +      wpa_printf(MSG_DEBUG, "P2P: * Operating Channel: Regulatory Class %u "
 +                 "Channel %u", reg_class, channel);
 +}
 +
 +
++void p2p_buf_add_pref_channel_list(struct wpabuf *buf,
++                                 const u32 *preferred_freq_list,
++                                 unsigned int size)
++{
++      unsigned int i, count = 0;
++      u8 op_class, op_channel;
++
++      if (!size)
++              return;
++
++      /*
++       * First, determine the number of P2P supported channels in the
++       * pref_freq_list returned from driver. This is needed for calculations
++       * of the vendor IE size.
++       */
++      for (i = 0; i < size; i++) {
++              if (p2p_freq_to_channel(preferred_freq_list[i], &op_class,
++                                      &op_channel) == 0)
++                      count++;
++      }
++
++      wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
++      wpabuf_put_u8(buf, 4 + count * sizeof(u16));
++      wpabuf_put_be24(buf, OUI_QCA);
++      wpabuf_put_u8(buf, QCA_VENDOR_ELEM_P2P_PREF_CHAN_LIST);
++      for (i = 0; i < size; i++) {
++              if (p2p_freq_to_channel(preferred_freq_list[i], &op_class,
++                                      &op_channel) < 0) {
++                      wpa_printf(MSG_DEBUG, "Unsupported frequency %u MHz",
++                                 preferred_freq_list[i]);
++                      continue;
++              }
++              wpabuf_put_u8(buf, op_class);
++              wpabuf_put_u8(buf, op_channel);
++      }
++}
++
++
 +void p2p_buf_add_channel_list(struct wpabuf *buf, const char *country,
 +                            struct p2p_channels *chan)
 +{
 +      u8 *len;
 +      size_t i;
 +
 +      /* Channel List */
 +      wpabuf_put_u8(buf, P2P_ATTR_CHANNEL_LIST);
 +      len = wpabuf_put(buf, 2); /* IE length to be filled */
 +      wpabuf_put_data(buf, country, 3); /* Country String */
 +
 +      for (i = 0; i < chan->reg_classes; i++) {
 +              struct p2p_reg_class *c = &chan->reg_class[i];
 +              wpabuf_put_u8(buf, c->reg_class);
 +              wpabuf_put_u8(buf, c->channels);
 +              wpabuf_put_data(buf, c->channel, c->channels);
 +      }
 +
 +      /* Update attribute length */
 +      WPA_PUT_LE16(len, (u8 *) wpabuf_put(buf, 0) - len - 2);
 +      wpa_hexdump(MSG_DEBUG, "P2P: * Channel List",
 +                  len + 2, (u8 *) wpabuf_put(buf, 0) - len - 2);
 +}
 +
 +
 +void p2p_buf_add_status(struct wpabuf *buf, u8 status)
 +{
 +      /* Status */
 +      wpabuf_put_u8(buf, P2P_ATTR_STATUS);
 +      wpabuf_put_le16(buf, 1);
 +      wpabuf_put_u8(buf, status);
 +      wpa_printf(MSG_DEBUG, "P2P: * Status: %d", status);
 +}
 +
 +
 +void p2p_buf_add_device_info(struct wpabuf *buf, struct p2p_data *p2p,
 +                           struct p2p_device *peer)
 +{
 +      u8 *len;
 +      u16 methods;
 +      size_t nlen, i;
 +
 +      /* P2P Device Info */
 +      wpabuf_put_u8(buf, P2P_ATTR_DEVICE_INFO);
 +      len = wpabuf_put(buf, 2); /* IE length to be filled */
 +
 +      /* P2P Device address */
 +      wpabuf_put_data(buf, p2p->cfg->dev_addr, ETH_ALEN);
 +
 +      /* Config Methods */
 +      methods = 0;
 +      if (peer && peer->wps_method != WPS_NOT_READY) {
 +              if (peer->wps_method == WPS_PBC)
 +                      methods |= WPS_CONFIG_PUSHBUTTON;
 +              else if (peer->wps_method == WPS_PIN_DISPLAY ||
 +                       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_P2PS);
 +      } else {
 +              methods |= WPS_CONFIG_PUSHBUTTON;
 +              methods |= WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD;
 +              methods |= WPS_CONFIG_P2PS;
 +      }
 +      wpabuf_put_be16(buf, methods);
 +
 +      /* Primary Device Type */
 +      wpabuf_put_data(buf, p2p->cfg->pri_dev_type,
 +                      sizeof(p2p->cfg->pri_dev_type));
 +
 +      /* Number of Secondary Device Types */
 +      wpabuf_put_u8(buf, p2p->cfg->num_sec_dev_types);
 +
 +      /* Secondary Device Type List */
 +      for (i = 0; i < p2p->cfg->num_sec_dev_types; i++)
 +              wpabuf_put_data(buf, p2p->cfg->sec_dev_type[i],
 +                              WPS_DEV_TYPE_LEN);
 +
 +      /* Device Name */
 +      nlen = p2p->cfg->dev_name ? os_strlen(p2p->cfg->dev_name) : 0;
 +      wpabuf_put_be16(buf, ATTR_DEV_NAME);
 +      wpabuf_put_be16(buf, nlen);
 +      wpabuf_put_data(buf, p2p->cfg->dev_name, nlen);
 +
 +      /* Update attribute length */
 +      WPA_PUT_LE16(len, (u8 *) wpabuf_put(buf, 0) - len - 2);
 +      wpa_printf(MSG_DEBUG, "P2P: * Device Info");
 +}
 +
 +
 +void p2p_buf_add_device_id(struct wpabuf *buf, const u8 *dev_addr)
 +{
 +      /* P2P Device ID */
 +      wpabuf_put_u8(buf, P2P_ATTR_DEVICE_ID);
 +      wpabuf_put_le16(buf, ETH_ALEN);
 +      wpabuf_put_data(buf, dev_addr, ETH_ALEN);
 +      wpa_printf(MSG_DEBUG, "P2P: * Device ID: " MACSTR, MAC2STR(dev_addr));
 +}
 +
 +
 +void p2p_buf_add_config_timeout(struct wpabuf *buf, u8 go_timeout,
 +                              u8 client_timeout)
 +{
 +      /* Configuration Timeout */
 +      wpabuf_put_u8(buf, P2P_ATTR_CONFIGURATION_TIMEOUT);
 +      wpabuf_put_le16(buf, 2);
 +      wpabuf_put_u8(buf, go_timeout);
 +      wpabuf_put_u8(buf, client_timeout);
 +      wpa_printf(MSG_DEBUG, "P2P: * Configuration Timeout: GO %d (*10ms)  "
 +                 "client %d (*10ms)", go_timeout, client_timeout);
 +}
 +
 +
 +void p2p_buf_add_intended_addr(struct wpabuf *buf, const u8 *interface_addr)
 +{
 +      /* Intended P2P Interface Address */
 +      wpabuf_put_u8(buf, P2P_ATTR_INTENDED_INTERFACE_ADDR);
 +      wpabuf_put_le16(buf, ETH_ALEN);
 +      wpabuf_put_data(buf, interface_addr, ETH_ALEN);
 +      wpa_printf(MSG_DEBUG, "P2P: * Intended P2P Interface Address " MACSTR,
 +                 MAC2STR(interface_addr));
 +}
 +
 +
 +void p2p_buf_add_group_bssid(struct wpabuf *buf, const u8 *bssid)
 +{
 +      /* P2P Group BSSID */
 +      wpabuf_put_u8(buf, P2P_ATTR_GROUP_BSSID);
 +      wpabuf_put_le16(buf, ETH_ALEN);
 +      wpabuf_put_data(buf, bssid, ETH_ALEN);
 +      wpa_printf(MSG_DEBUG, "P2P: * P2P Group BSSID " MACSTR,
 +                 MAC2STR(bssid));
 +}
 +
 +
 +void p2p_buf_add_group_id(struct wpabuf *buf, const u8 *dev_addr,
 +                        const u8 *ssid, size_t ssid_len)
 +{
 +      /* P2P Group ID */
 +      wpabuf_put_u8(buf, P2P_ATTR_GROUP_ID);
 +      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));
 +      wpa_hexdump_ascii(MSG_DEBUG, "P2P: P2P Group ID SSID", ssid, ssid_len);
 +}
 +
 +
 +void p2p_buf_add_invitation_flags(struct wpabuf *buf, u8 flags)
 +{
 +      /* Invitation Flags */
 +      wpabuf_put_u8(buf, P2P_ATTR_INVITATION_FLAGS);
 +      wpabuf_put_le16(buf, 1);
 +      wpabuf_put_u8(buf, flags);
 +      wpa_printf(MSG_DEBUG, "P2P: * Invitation Flags: bitmap 0x%x", flags);
 +}
 +
 +
 +static void p2p_buf_add_noa_desc(struct wpabuf *buf, struct p2p_noa_desc *desc)
 +{
 +      if (desc == NULL)
 +              return;
 +
 +      wpabuf_put_u8(buf, desc->count_type);
 +      wpabuf_put_le32(buf, desc->duration);
 +      wpabuf_put_le32(buf, desc->interval);
 +      wpabuf_put_le32(buf, desc->start_time);
 +}
 +
 +
 +void p2p_buf_add_noa(struct wpabuf *buf, u8 noa_index, u8 opp_ps, u8 ctwindow,
 +                   struct p2p_noa_desc *desc1, struct p2p_noa_desc *desc2)
 +{
 +      /* Notice of Absence */
 +      wpabuf_put_u8(buf, P2P_ATTR_NOTICE_OF_ABSENCE);
 +      wpabuf_put_le16(buf, 2 + (desc1 ? 13 : 0) + (desc2 ? 13 : 0));
 +      wpabuf_put_u8(buf, noa_index);
 +      wpabuf_put_u8(buf, (opp_ps ? 0x80 : 0) | (ctwindow & 0x7f));
 +      p2p_buf_add_noa_desc(buf, desc1);
 +      p2p_buf_add_noa_desc(buf, desc2);
 +      wpa_printf(MSG_DEBUG, "P2P: * Notice of Absence");
 +}
 +
 +
 +void p2p_buf_add_ext_listen_timing(struct wpabuf *buf, u16 period,
 +                                 u16 interval)
 +{
 +      /* Extended Listen Timing */
 +      wpabuf_put_u8(buf, P2P_ATTR_EXT_LISTEN_TIMING);
 +      wpabuf_put_le16(buf, 4);
 +      wpabuf_put_le16(buf, period);
 +      wpabuf_put_le16(buf, interval);
 +      wpa_printf(MSG_DEBUG, "P2P: * Extended Listen Timing (period %u msec  "
 +                 "interval %u msec)", period, interval);
 +}
 +
 +
 +void p2p_buf_add_p2p_interface(struct wpabuf *buf, struct p2p_data *p2p)
 +{
 +      /* P2P Interface */
 +      wpabuf_put_u8(buf, P2P_ATTR_INTERFACE);
 +      wpabuf_put_le16(buf, ETH_ALEN + 1 + ETH_ALEN);
 +      /* P2P Device address */
 +      wpabuf_put_data(buf, p2p->cfg->dev_addr, ETH_ALEN);
 +      /*
 +       * FIX: Fetch interface address list from driver. Do not include
 +       * the P2P Device address if it is never used as interface address.
 +       */
 +      /* P2P Interface Address Count */
 +      wpabuf_put_u8(buf, 1);
 +      wpabuf_put_data(buf, p2p->cfg->dev_addr, ETH_ALEN);
 +}
 +
 +
 +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);
-                   p2p->query_hash, p2p->p2ps_seek_count * P2PS_HASH_LEN);
++      wpabuf_put_data(buf, p2p->p2ps_seek_hash,
 +                      p2p->p2ps_seek_count * P2PS_HASH_LEN);
 +      wpa_hexdump(MSG_DEBUG, "P2P: * Service Hash",
- void p2p_buf_add_service_instance(struct wpabuf *buf, struct p2p_data *p2p,
-                                 u8 hash_count, const u8 *hash,
-                                 struct p2ps_advertisement *adv_list)
++                  p2p->p2ps_seek_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));
 +}
 +
 +
-       struct wpabuf *tmp_buf;
-       u8 *tag_len = NULL, *ie_len = NULL;
-       size_t svc_len = 0, remaining = 0, total_len = 0;
++static int p2ps_wildcard_hash(struct p2p_data *p2p,
++                            const u8 *hash, u8 hash_count)
++{
++      u8 i;
++      const u8 *test = hash;
++
++      for (i = 0; i < hash_count; i++) {
++              if (os_memcmp(test, p2p->wild_card_hash, P2PS_HASH_LEN) == 0)
++                      return 1;
++              test += P2PS_HASH_LEN;
++      }
++
++      return 0;
++}
++
++
++static int p2p_wfa_service_adv(struct p2p_data *p2p)
 +{
 +      struct p2ps_advertisement *adv;
-       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 = p2p->p2ps_adv_list; adv; adv = adv->next) {
++              if (os_strncmp(adv->svc_name, P2PS_WILD_HASH_STR,
++                             os_strlen(P2PS_WILD_HASH_STR)) == 0)
++                      return 1;
++      }
 +
-       for (adv = adv_list; adv && total_len <= MAX_SVC_ADV_LEN;
-            adv = adv->next) {
-               u8 count = hash_count;
-               const u8 *test = hash;
++      return 0;
++}
 +
-               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;
++static int p2p_buf_add_service_info(struct wpabuf *buf, struct p2p_data *p2p,
++                                  u32 adv_id, u16 config_methods,
++                                  const char *svc_name, u8 **ie_len, u8 **pos,
++                                  size_t *total_len, u8 *attr_len)
++{
++      size_t svc_len;
++      size_t remaining;
++      size_t info_len;
++
++      p2p_dbg(p2p, "Add service info for %s (adv_id=%u)", svc_name, adv_id);
++      svc_len = os_strlen(svc_name);
++      info_len = sizeof(adv_id) + sizeof(config_methods) + sizeof(u8) +
++              svc_len;
++
++      if (info_len + *total_len > MAX_SVC_ADV_LEN) {
++              p2p_dbg(p2p,
++                      "Unsufficient buffer, failed to add advertised service info");
++              return -1;
++      }
 +
-                       test += P2PS_HASH_LEN;
-               }
++      if (svc_len > 255) {
++              p2p_dbg(p2p,
++                      "Invalid service name length (%u bytes), failed to add advertised service info",
++                      (unsigned int) svc_len);
++              return -1;
++      }
 +
-               /* 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));
-                       }
++      if (*ie_len) {
++              int ie_data_len = (*pos - *ie_len) - 1;
 +
-               svc_len = os_strlen(adv->svc_name);
++              if (ie_data_len < 0 || ie_data_len > 255) {
++                      p2p_dbg(p2p,
++                              "Invalid IE length, failed to add advertised service info");
++                      return -1;
 +              }
++              remaining = 255 - ie_data_len;
++      } else {
++              /*
++               * Adding new P2P IE header takes 6 extra bytes:
++               * - 2 byte IE header (1 byte IE id and 1 byte length)
++               * - 4 bytes of IE_VENDOR_TYPE are reduced from 255 below
++               */
++              *ie_len = p2p_buf_add_ie_hdr(buf);
++              remaining = 255 - 4;
++      }
 +
-               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(u32) + sizeof(u16) + sizeof(u8)) {
++              /*
++               * Split adv_id, config_methods, and svc_name_len between two
++               * IEs.
++               */
++              size_t front = remaining;
++              size_t back = sizeof(u32) + sizeof(u16) + sizeof(u8) - front;
++              u8 holder[sizeof(u32) + sizeof(u16) + sizeof(u8)];
 +
-               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));
-               }
++              WPA_PUT_LE32(holder, adv_id);
++              WPA_PUT_BE16(&holder[sizeof(u32)], config_methods);
++              holder[sizeof(u32) + sizeof(u16)] = svc_len;
 +
-               /* 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;
-               }
++              if (front)
++                      wpabuf_put_data(buf, holder, front);
 +
-               /*           adv_id      config_methods     svc_string */
-               total_len += sizeof(u32) + sizeof(u16) + sizeof(u8) + svc_len;
++              p2p_buf_update_ie_hdr(buf, *ie_len);
++              *ie_len = p2p_buf_add_ie_hdr(buf);
 +
-       if (tag_len)
-               p2p_buf_update_ie_hdr(tmp_buf, tag_len);
++              wpabuf_put_data(buf, &holder[front], back);
++              remaining = 255 - 4 - (sizeof(u32) + sizeof(u16) + sizeof(u8)) -
++                      back;
++      } else {
++              wpabuf_put_le32(buf, adv_id);
++              wpabuf_put_be16(buf, config_methods);
++              wpabuf_put_u8(buf, svc_len);
++              remaining -= sizeof(adv_id) + sizeof(config_methods) +
++                      sizeof(u8);
 +      }
 +
-       if (ie_len)
-               WPA_PUT_LE16(ie_len, (u16) total_len);
++      if (remaining < svc_len) {
++              /* split svc_name between two or three IEs */
++              size_t front = remaining;
++              size_t back = svc_len - front;
 +
- wild_hash:
-       /* If all fit, return matching instances, otherwise the wildcard */
-       if (total_len <= MAX_SVC_ADV_LEN) {
-               wpabuf_put_buf(buf, tmp_buf);
++              if (front)
++                      wpabuf_put_data(buf, svc_name, front);
 +
-               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);
++              p2p_buf_update_ie_hdr(buf, *ie_len);
++              *ie_len = p2p_buf_add_ie_hdr(buf);
++
++              /* In rare cases, we must split across 3 attributes */
++              if (back > 255 - 4) {
++                      wpabuf_put_data(buf, &svc_name[front], 255 - 4);
++                      back -= 255 - 4;
++                      front += 255 - 4;
++                      p2p_buf_update_ie_hdr(buf, *ie_len);
++                      *ie_len = p2p_buf_add_ie_hdr(buf);
++              }
++
++              wpabuf_put_data(buf, &svc_name[front], back);
++              remaining = 255 - 4 - back;
 +      } else {
++              wpabuf_put_data(buf, svc_name, svc_len);
++              remaining -= svc_len;
++      }
++
++      p2p_buf_update_ie_hdr(buf, *ie_len);
++
++      /* set *ie_len to NULL if a new IE has to be added on the next call */
++      if (!remaining)
++              *ie_len = NULL;
++
++      /* set *pos to point to the next byte to update */
++      *pos = wpabuf_put(buf, 0);
++
++      *total_len += info_len;
++      WPA_PUT_LE16(attr_len, (u16) *total_len);
++      return 0;
++}
++
++
++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;
++      int p2ps_wildcard;
++      size_t total_len;
++      struct wpabuf *tmp_buf = NULL;
++      u8 *pos, *attr_len, *ie_len = NULL;
++
++      if (!adv_list || !hash || !hash_count)
++              return;
++
++      wpa_hexdump(MSG_DEBUG, "P2PS: Probe Request service hash values",
++                  hash, hash_count * P2PS_HASH_LEN);
++      p2ps_wildcard = p2ps_wildcard_hash(p2p, hash, hash_count) &&
++              p2p_wfa_service_adv(p2p);
++
++      /* 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;
++
++      /*
++       * Attribute data can be split into a number of IEs. Start with the
++       * first IE and the attribute headers here.
++       */
++      ie_len = p2p_buf_add_ie_hdr(tmp_buf);
++
++      total_len = 0;
++
++      wpabuf_put_u8(tmp_buf, P2P_ATTR_ADVERTISED_SERVICE);
++      attr_len = wpabuf_put(tmp_buf, sizeof(u16));
++      WPA_PUT_LE16(attr_len, (u16) total_len);
++      p2p_buf_update_ie_hdr(tmp_buf, ie_len);
++      pos = wpabuf_put(tmp_buf, 0);
++
++      if (p2ps_wildcard) {
++              /* org.wi-fi.wfds match found */
++              p2p_buf_add_service_info(tmp_buf, p2p, 0, 0, P2PS_WILD_HASH_STR,
++                                       &ie_len, &pos, &total_len, attr_len);
++      }
++
++      /* add advertised service info of matching services */
++      for (adv = adv_list; adv && total_len <= MAX_SVC_ADV_LEN;
++           adv = adv->next) {
++              const u8 *test = hash;
++              u8 i;
++
++              for (i = 0; i < hash_count; i++) {
++                      /* exact name hash match */
++                      if (os_memcmp(test, adv->hash, P2PS_HASH_LEN) == 0 &&
++                          p2p_buf_add_service_info(tmp_buf, p2p,
++                                                   adv->id,
++                                                   adv->config_methods,
++                                                   adv->svc_name,
++                                                   &ie_len, &pos,
++                                                   &total_len,
++                                                   attr_len))
++                              break;
++
++                      test += P2PS_HASH_LEN;
++              }
 +      }
 +
++      if (total_len)
++              wpabuf_put_buf(buf, tmp_buf);
 +      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;
 +
 +      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) {
 +              /*
 +               * Some deployed WPS implementations fail to parse zeor-length
 +               * 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 0;
 +      }
 +#endif /* CONFIG_WPS_STRICT */
 +      wpabuf_put_be16(buf, len);
 +      if (val)
 +              wpabuf_put_data(buf, val, len);
 +      return 0;
 +}
 +
 +
 +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);
 +
 +      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);
 +              wpa_printf(MSG_DEBUG, "P2P: WPS IE Device Password ID: %d",
 +                         pw_id);
 +              wpabuf_put_be16(buf, 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);
 +
 +              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);
 +
 +              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);
 +      }
 +
 +      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);
 +              wpabuf_put_data(buf, p2p->cfg->sec_dev_type,
 +                              WPS_DEV_TYPE_LEN *
 +                              p2p->cfg->num_sec_dev_types);
 +      }
 +
 +      /* Add the WPS vendor extensions */
 +      for (i = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) {
 +              if (p2p->wps_vendor_ext[i] == NULL)
 +                      break;
 +              if (wpabuf_tailroom(buf) <
 +                  4 + wpabuf_len(p2p->wps_vendor_ext[i]))
 +                      continue;
 +              wpabuf_put_be16(buf, ATTR_VENDOR_EXT);
 +              wpabuf_put_be16(buf, wpabuf_len(p2p->wps_vendor_ext[i]));
 +              wpabuf_put_buf(buf, p2p->wps_vendor_ext[i]);
 +      }
 +
 +      p2p_buf_update_ie_hdr(buf, len);
 +
 +      return 0;
 +}
index 86bae1a2c0db8c1f41ca23e43553abd4181e622d,0000000000000000000000000000000000000000..98805fee2409b42a4f8181df8239fda9c7455222
mode 100644,000000..100644
--- /dev/null
@@@ -1,329 -1,0 +1,329 @@@
-       ies = p2p_build_probe_resp_ies(p2p);
 +/*
 + * Wi-Fi Direct - P2P Device Discoverability procedure
 + * Copyright (c) 2010, Atheros Communications
 + *
 + * 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/ieee802_11_defs.h"
 +#include "p2p_i.h"
 +#include "p2p.h"
 +
 +
 +static struct wpabuf * p2p_build_dev_disc_req(struct p2p_data *p2p,
 +                                            struct p2p_device *go,
 +                                            const u8 *dev_id)
 +{
 +      struct wpabuf *buf;
 +      u8 *len;
 +
 +      buf = wpabuf_alloc(100);
 +      if (buf == NULL)
 +              return NULL;
 +
 +      go->dialog_token++;
 +      if (go->dialog_token == 0)
 +              go->dialog_token = 1;
 +      p2p_buf_add_public_action_hdr(buf, P2P_DEV_DISC_REQ, go->dialog_token);
 +
 +      len = p2p_buf_add_ie_hdr(buf);
 +      p2p_buf_add_device_id(buf, dev_id);
 +      p2p_buf_add_group_id(buf, go->info.p2p_device_addr, go->oper_ssid,
 +                           go->oper_ssid_len);
 +      p2p_buf_update_ie_hdr(buf, len);
 +
 +      return buf;
 +}
 +
 +
 +void p2p_dev_disc_req_cb(struct p2p_data *p2p, int success)
 +{
 +      p2p_dbg(p2p, "Device Discoverability Request TX callback: success=%d",
 +              success);
 +
 +      if (!success) {
 +              /*
 +               * Use P2P find, if needed, to find the other device or to
 +               * retry device discoverability.
 +               */
 +              p2p_set_state(p2p, P2P_CONNECT);
 +              p2p_set_timeout(p2p, 0, 100000);
 +              return;
 +      }
 +
 +      p2p_dbg(p2p, "GO acknowledged Device Discoverability Request - wait for response");
 +      /*
 +       * TODO: is the remain-on-channel from Action frame TX long enough for
 +       * most cases or should we try to increase its duration and/or start
 +       * another remain-on-channel if needed once the previous one expires?
 +       */
 +}
 +
 +
 +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) {
 +              p2p_dbg(p2p, "Could not find peer entry for GO and frequency to send Device Discoverability Request");
 +              return -1;
 +      }
 +
 +      req = p2p_build_dev_disc_req(p2p, go, dev->info.p2p_device_addr);
 +      if (req == NULL)
 +              return -1;
 +
 +      p2p_dbg(p2p, "Sending Device Discoverability Request to GO " MACSTR
 +              " for client " MACSTR,
 +              MAC2STR(go->info.p2p_device_addr),
 +              MAC2STR(dev->info.p2p_device_addr));
 +
 +      p2p->pending_client_disc_go = go;
 +      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), wait_time) < 0) {
 +              p2p_dbg(p2p, "Failed to send Action frame");
 +              wpabuf_free(req);
 +              /* TODO: how to recover from failure? */
 +              return -1;
 +      }
 +
 +      wpabuf_free(req);
 +
 +      return 0;
 +}
 +
 +
 +static struct wpabuf * p2p_build_dev_disc_resp(u8 dialog_token, u8 status)
 +{
 +      struct wpabuf *buf;
 +      u8 *len;
 +
 +      buf = wpabuf_alloc(100);
 +      if (buf == NULL)
 +              return NULL;
 +
 +      p2p_buf_add_public_action_hdr(buf, P2P_DEV_DISC_RESP, dialog_token);
 +
 +      len = p2p_buf_add_ie_hdr(buf);
 +      p2p_buf_add_status(buf, status);
 +      p2p_buf_update_ie_hdr(buf, len);
 +
 +      return buf;
 +}
 +
 +
 +void p2p_dev_disc_resp_cb(struct p2p_data *p2p, int success)
 +{
 +      p2p_dbg(p2p, "Device Discoverability Response TX callback: success=%d",
 +              success);
 +      p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
 +}
 +
 +
 +static void p2p_send_dev_disc_resp(struct p2p_data *p2p, u8 dialog_token,
 +                                 const u8 *addr, int freq, u8 status)
 +{
 +      struct wpabuf *resp;
 +
 +      resp = p2p_build_dev_disc_resp(dialog_token, status);
 +      if (resp == NULL)
 +              return;
 +
 +      p2p_dbg(p2p, "Sending Device Discoverability Response to " MACSTR
 +              " (status %u freq %d)",
 +              MAC2STR(addr), status, freq);
 +
 +      p2p->pending_action_state = P2P_PENDING_DEV_DISC_RESPONSE;
 +      if (p2p_send_action(p2p, freq, addr, p2p->cfg->dev_addr,
 +                          p2p->cfg->dev_addr,
 +                          wpabuf_head(resp), wpabuf_len(resp), 200) < 0) {
 +              p2p_dbg(p2p, "Failed to send Action frame");
 +      }
 +
 +      wpabuf_free(resp);
 +}
 +
 +
 +void p2p_process_dev_disc_req(struct p2p_data *p2p, const u8 *sa,
 +                            const u8 *data, size_t len, int rx_freq)
 +{
 +      struct p2p_message msg;
 +      size_t g;
 +
 +      p2p_dbg(p2p, "Received Device Discoverability Request from " MACSTR
 +              " (freq=%d)", MAC2STR(sa), rx_freq);
 +
 +      if (p2p_parse(data, len, &msg))
 +              return;
 +
 +      if (msg.dialog_token == 0) {
 +              p2p_dbg(p2p, "Invalid Dialog Token 0 (must be nonzero) in Device Discoverability Request");
 +              p2p_send_dev_disc_resp(p2p, msg.dialog_token, sa, rx_freq,
 +                                     P2P_SC_FAIL_INVALID_PARAMS);
 +              p2p_parse_free(&msg);
 +              return;
 +      }
 +
 +      if (msg.device_id == NULL) {
 +              p2p_dbg(p2p, "P2P Device ID attribute missing from Device Discoverability Request");
 +              p2p_send_dev_disc_resp(p2p, msg.dialog_token, sa, rx_freq,
 +                                     P2P_SC_FAIL_INVALID_PARAMS);
 +              p2p_parse_free(&msg);
 +              return;
 +      }
 +
 +      for (g = 0; g < p2p->num_groups; g++) {
 +              if (p2p_group_go_discover(p2p->groups[g], msg.device_id, sa,
 +                                        rx_freq) == 0) {
 +                      p2p_dbg(p2p, "Scheduled GO Discoverability Request for the target device");
 +                      /*
 +                       * P2P group code will use a callback to indicate TX
 +                       * status, so that we can reply to the request once the
 +                       * target client has acknowledged the request or it has
 +                       * timed out.
 +                       */
 +                      p2p->pending_dev_disc_dialog_token = msg.dialog_token;
 +                      os_memcpy(p2p->pending_dev_disc_addr, sa, ETH_ALEN);
 +                      p2p->pending_dev_disc_freq = rx_freq;
 +                      p2p_parse_free(&msg);
 +                      return;
 +              }
 +      }
 +
 +      p2p_dbg(p2p, "Requested client was not found in any group or did not support client discoverability");
 +      p2p_send_dev_disc_resp(p2p, msg.dialog_token, sa, rx_freq,
 +                             P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE);
 +      p2p_parse_free(&msg);
 +}
 +
 +
 +void p2p_process_dev_disc_resp(struct p2p_data *p2p, const u8 *sa,
 +                             const u8 *data, size_t len)
 +{
 +      struct p2p_message msg;
 +      struct p2p_device *go;
 +      u8 status;
 +
 +      p2p_dbg(p2p, "Received Device Discoverability Response from " MACSTR,
 +              MAC2STR(sa));
 +
 +      go = p2p->pending_client_disc_go;
 +      if (go == NULL ||
 +          os_memcmp(sa, go->info.p2p_device_addr, ETH_ALEN) != 0) {
 +              p2p_dbg(p2p, "Ignore unexpected Device Discoverability Response");
 +              return;
 +      }
 +
 +      if (p2p_parse(data, len, &msg))
 +              return;
 +
 +      if (msg.status == NULL) {
 +              p2p_parse_free(&msg);
 +              return;
 +      }
 +
 +      if (msg.dialog_token != go->dialog_token) {
 +              p2p_dbg(p2p, "Ignore Device Discoverability Response with unexpected dialog token %u (expected %u)",
 +                      msg.dialog_token, go->dialog_token);
 +              p2p_parse_free(&msg);
 +              return;
 +      }
 +
 +      status = *msg.status;
 +      p2p_parse_free(&msg);
 +
 +      p2p_dbg(p2p, "Device Discoverability Response status %u", status);
 +
 +      if (p2p->go_neg_peer == NULL ||
 +          os_memcmp(p2p->pending_client_disc_addr,
 +                    p2p->go_neg_peer->info.p2p_device_addr, ETH_ALEN) != 0 ||
 +          os_memcmp(p2p->go_neg_peer->member_in_go_dev,
 +                    go->info.p2p_device_addr, ETH_ALEN) != 0) {
 +              p2p_dbg(p2p, "No pending operation with the client discoverability peer anymore");
 +              return;
 +      }
 +
 +      if (status == 0) {
 +              /*
 +               * Peer is expected to be awake for at least 100 TU; try to
 +               * connect immediately.
 +               */
 +              p2p_dbg(p2p, "Client discoverability request succeeded");
 +              if (p2p->state == P2P_CONNECT) {
 +                      /*
 +                       * Change state to force the timeout to start in
 +                       * P2P_CONNECT again without going through the short
 +                       * Listen state.
 +                       */
 +                      p2p_set_state(p2p, P2P_CONNECT_LISTEN);
 +                      p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
 +              }
 +              p2p_set_timeout(p2p, 0, 0);
 +      } else {
 +              /*
 +               * Client discoverability request failed; try to connect from
 +               * timeout.
 +               */
 +              p2p_dbg(p2p, "Client discoverability request failed");
 +              p2p_set_timeout(p2p, 0, 500000);
 +      }
 +
 +}
 +
 +
 +void p2p_go_disc_req_cb(struct p2p_data *p2p, int success)
 +{
 +      p2p_dbg(p2p, "GO Discoverability Request TX callback: success=%d",
 +              success);
 +      p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
 +
 +      if (p2p->pending_dev_disc_dialog_token == 0) {
 +              p2p_dbg(p2p, "No pending Device Discoverability Request");
 +              return;
 +      }
 +
 +      p2p_send_dev_disc_resp(p2p, p2p->pending_dev_disc_dialog_token,
 +                             p2p->pending_dev_disc_addr,
 +                             p2p->pending_dev_disc_freq,
 +                             success ? P2P_SC_SUCCESS :
 +                             P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE);
 +
 +      p2p->pending_dev_disc_dialog_token = 0;
 +}
 +
 +
 +void p2p_process_go_disc_req(struct p2p_data *p2p, const u8 *da, const u8 *sa,
 +                           const u8 *data, size_t len, int rx_freq)
 +{
 +      unsigned int tu;
 +      struct wpabuf *ies;
 +
 +      p2p_dbg(p2p, "Received GO Discoverability Request - remain awake for 100 TU");
 +
++      ies = p2p_build_probe_resp_ies(p2p, NULL, 0);
 +      if (ies == NULL)
 +              return;
 +
 +      /* Remain awake 100 TU on operating channel */
 +      p2p->pending_client_disc_freq = rx_freq;
 +      tu = 100;
 +      if (p2p->cfg->start_listen(p2p->cfg->cb_ctx, rx_freq, 1024 * tu / 1000,
 +                  ies) < 0) {
 +              p2p_dbg(p2p, "Failed to start listen mode for client discoverability");
 +      }
 +      wpabuf_free(ies);
 +}
index 98abf9d2293e95accd135960008028f366d51748,0000000000000000000000000000000000000000..83b43563d945fbb7f109149b38161ab66230b28c
mode 100644,000000..100644
--- /dev/null
@@@ -1,1305 -1,0 +1,1512 @@@
-       if (peer && peer->go_state == REMOTE_GO) {
 +/*
 + * Wi-Fi Direct - P2P Group Owner Negotiation
 + * 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 "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"
 +
 +
 +static int p2p_go_det(u8 own_intent, u8 peer_value)
 +{
 +      u8 peer_intent = peer_value >> 1;
 +      if (own_intent == peer_intent) {
 +              if (own_intent == P2P_MAX_GO_INTENT)
 +                      return -1; /* both devices want to become GO */
 +
 +              /* Use tie breaker bit to determine GO */
 +              return (peer_value & 0x01) ? 0 : 1;
 +      }
 +
 +      return own_intent > peer_intent;
 +}
 +
 +
 +int p2p_peer_channels_check(struct p2p_data *p2p, struct p2p_channels *own,
 +                          struct p2p_device *dev,
 +                          const u8 *channel_list, size_t channel_list_len)
 +{
 +      const u8 *pos, *end;
 +      struct p2p_channels *ch;
 +      size_t channels;
 +      struct p2p_channels intersection;
 +
 +      ch = &dev->channels;
 +      os_memset(ch, 0, sizeof(*ch));
 +      pos = channel_list;
 +      end = channel_list + channel_list_len;
 +
 +      if (end - pos < 3)
 +              return -1;
 +      os_memcpy(dev->country, pos, 3);
 +      wpa_hexdump_ascii(MSG_DEBUG, "P2P: Peer country", pos, 3);
 +      if (pos[2] != 0x04 && os_memcmp(pos, p2p->cfg->country, 2) != 0) {
 +              p2p_info(p2p, "Mismatching country (ours=%c%c peer's=%c%c)",
 +                      p2p->cfg->country[0], p2p->cfg->country[1],
 +                      pos[0], pos[1]);
 +              return -1;
 +      }
 +      pos += 3;
 +
 +      while (pos + 2 < end) {
 +              struct p2p_reg_class *cl = &ch->reg_class[ch->reg_classes];
 +              cl->reg_class = *pos++;
 +              if (pos + 1 + pos[0] > end) {
 +                      p2p_info(p2p, "Invalid peer Channel List");
 +                      return -1;
 +              }
 +              channels = *pos++;
 +              cl->channels = channels > P2P_MAX_REG_CLASS_CHANNELS ?
 +                      P2P_MAX_REG_CLASS_CHANNELS : channels;
 +              os_memcpy(cl->channel, pos, cl->channels);
 +              pos += channels;
 +              ch->reg_classes++;
 +              if (ch->reg_classes == P2P_MAX_REG_CLASSES)
 +                      break;
 +      }
 +
 +      p2p_channels_intersect(own, &dev->channels, &intersection);
 +      p2p_dbg(p2p, "Own reg_classes %d peer reg_classes %d intersection reg_classes %d",
 +              (int) own->reg_classes,
 +              (int) dev->channels.reg_classes,
 +              (int) intersection.reg_classes);
 +      if (intersection.reg_classes == 0) {
 +              p2p_info(p2p, "No common channels found");
 +              return -1;
 +      }
 +      return 0;
 +}
 +
 +
 +static int p2p_peer_channels(struct p2p_data *p2p, struct p2p_device *dev,
 +                           const u8 *channel_list, size_t channel_list_len)
 +{
 +      return p2p_peer_channels_check(p2p, &p2p->channels, dev,
 +                                     channel_list, channel_list_len);
 +}
 +
 +
 +u16 p2p_wps_method_pw_id(enum p2p_wps_method wps_method)
 +{
 +      switch (wps_method) {
 +      case WPS_PIN_DISPLAY:
 +              return DEV_PW_REGISTRAR_SPECIFIED;
 +      case WPS_PIN_KEYPAD:
 +              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;
 +      }
 +}
 +
 +
 +static const char * p2p_wps_method_str(enum p2p_wps_method wps_method)
 +{
 +      switch (wps_method) {
 +      case WPS_PIN_DISPLAY:
 +              return "Display";
 +      case WPS_PIN_KEYPAD:
 +              return "Keypad";
 +      case WPS_PBC:
 +              return "PBC";
 +      case WPS_NFC:
 +              return "NFC";
 +      case WPS_P2PS:
 +              return "P2PS";
 +      default:
 +              return "??";
 +      }
 +}
 +
 +
 +static struct wpabuf * p2p_build_go_neg_req(struct p2p_data *p2p,
 +                                          struct p2p_device *peer)
 +{
 +      struct wpabuf *buf;
 +      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;
 +
 +      p2p_buf_add_public_action_hdr(buf, P2P_GO_NEG_REQ, peer->dialog_token);
 +
 +      len = p2p_buf_add_ie_hdr(buf);
 +      group_capab = 0;
 +      if (peer->flags & P2P_DEV_PREFER_PERSISTENT_GROUP) {
 +              group_capab |= P2P_GROUP_CAPAB_PERSISTENT_GROUP;
 +              if (peer->flags & P2P_DEV_PREFER_PERSISTENT_RECONN)
 +                      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,
 +                             group_capab);
 +      p2p_buf_add_go_intent(buf, (p2p->go_intent << 1) | peer->tie_breaker);
 +      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);
 +      if (p2p->ext_listen_interval)
 +              p2p_buf_add_ext_listen_timing(buf, p2p->ext_listen_period,
 +                                            p2p->ext_listen_interval);
 +      p2p_buf_add_intended_addr(buf, p2p->intended_addr);
 +      p2p_buf_add_channel_list(buf, p2p->cfg->country, &p2p->channels);
 +      p2p_buf_add_device_info(buf, p2p, peer);
 +      p2p_buf_add_operating_channel(buf, p2p->cfg->country,
 +                                    p2p->op_reg_class, p2p->op_channel);
 +      p2p_buf_update_ie_hdr(buf, len);
 +
++      p2p_buf_add_pref_channel_list(buf, p2p->pref_freq_list,
++                                    p2p->num_pref_freq);
++
 +      /* WPS IE with Device Password ID attribute */
 +      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;
 +}
 +
 +
 +int p2p_connect_send(struct p2p_data *p2p, struct p2p_device *dev)
 +{
 +      struct wpabuf *req;
 +      int freq;
 +
 +      if (dev->flags & P2P_DEV_PD_BEFORE_GO_NEG) {
 +              u16 config_method;
 +              p2p_dbg(p2p, "Use PD-before-GO-Neg workaround for " MACSTR,
 +                      MAC2STR(dev->info.p2p_device_addr));
 +              if (dev->wps_method == WPS_PIN_DISPLAY)
 +                      config_method = WPS_CONFIG_KEYPAD;
 +              else if (dev->wps_method == WPS_PIN_KEYPAD)
 +                      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,
 +                                       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",
 +                      MAC2STR(dev->info.p2p_device_addr));
 +              return -1;
 +      }
 +
 +      req = p2p_build_go_neg_req(p2p, dev);
 +      if (req == NULL)
 +              return -1;
 +      p2p_dbg(p2p, "Sending GO Negotiation Request");
 +      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,
 +                          p2p->cfg->dev_addr, dev->info.p2p_device_addr,
 +                          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->go_neg_req_sent++;
 +
 +      wpabuf_free(req);
 +
 +      return 0;
 +}
 +
 +
 +static struct wpabuf * p2p_build_go_neg_resp(struct p2p_data *p2p,
 +                                           struct p2p_device *peer,
 +                                           u8 dialog_token, u8 status,
 +                                           u8 tie_breaker)
 +{
 +      struct wpabuf *buf;
 +      u8 *len;
 +      u8 group_capab;
 +      size_t extra = 0;
 +      u16 pw_id;
 +
 +      p2p_dbg(p2p, "Building GO Negotiation Response");
 +
 +#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_RESP])
 +              extra += wpabuf_len(p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_RESP]);
 +
 +      buf = wpabuf_alloc(1000 + extra);
 +      if (buf == NULL)
 +              return NULL;
 +
 +      p2p_buf_add_public_action_hdr(buf, P2P_GO_NEG_RESP, dialog_token);
 +
 +      len = p2p_buf_add_ie_hdr(buf);
 +      p2p_buf_add_status(buf, status);
 +      group_capab = 0;
 +      if (peer && peer->go_state == LOCAL_GO) {
 +              if (peer->flags & P2P_DEV_PREFER_PERSISTENT_GROUP) {
 +                      group_capab |= P2P_GROUP_CAPAB_PERSISTENT_GROUP;
 +                      if (peer->flags & P2P_DEV_PREFER_PERSISTENT_RECONN)
 +                              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,
 +                             group_capab);
 +      p2p_buf_add_go_intent(buf, (p2p->go_intent << 1) | tie_breaker);
 +      p2p_buf_add_config_timeout(buf, p2p->go_timeout, p2p->client_timeout);
-       const int op_classes_5ghz[] = { 124, 115, 0 };
++      if (peer && peer->go_state == REMOTE_GO && !p2p->num_pref_freq) {
 +              p2p_dbg(p2p, "Omit Operating Channel attribute");
 +      } else {
 +              p2p_buf_add_operating_channel(buf, p2p->cfg->country,
 +                                            p2p->op_reg_class,
 +                                            p2p->op_channel);
 +      }
 +      p2p_buf_add_intended_addr(buf, p2p->intended_addr);
 +      if (status || peer == NULL) {
 +              p2p_buf_add_channel_list(buf, p2p->cfg->country,
 +                                       &p2p->channels);
 +      } else if (peer->go_state == REMOTE_GO) {
 +              p2p_buf_add_channel_list(buf, p2p->cfg->country,
 +                                       &p2p->channels);
 +      } else {
 +              struct p2p_channels res;
 +              p2p_channels_intersect(&p2p->channels, &peer->channels,
 +                                     &res);
 +              p2p_buf_add_channel_list(buf, p2p->cfg->country, &res);
 +      }
 +      p2p_buf_add_device_info(buf, p2p, peer);
 +      if (peer && peer->go_state == LOCAL_GO) {
 +              p2p_buf_add_group_id(buf, p2p->cfg->dev_addr, p2p->ssid,
 +                                   p2p->ssid_len);
 +      }
 +      p2p_buf_update_ie_hdr(buf, len);
 +
 +      /* WPS IE with Device Password ID attribute */
 +      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;
 +}
 +
 +
 +/**
 + * p2p_reselect_channel - Re-select operating channel based on peer information
 + * @p2p: P2P module context from p2p_init()
 + * @intersection: Support channel list intersection from local and peer
 + *
 + * This function is used to re-select the best channel after having received
 + * information from the peer to allow supported channel lists to be intersected.
 + * This can be used to improve initial channel selection done in
 + * p2p_prepare_channel() prior to the start of GO Negotiation. In addition, this
 + * can be used for Invitation case.
 + */
 +void p2p_reselect_channel(struct p2p_data *p2p,
 +                        struct p2p_channels *intersection)
 +{
 +      struct p2p_reg_class *cl;
 +      int freq;
 +      u8 op_reg_class, op_channel;
 +      unsigned int i;
-                                       msg.dev_password_id);
++      const int op_classes_5ghz[] = { 124, 125, 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,
 +                              &op_reg_class, &op_channel) == 0 &&
 +          p2p_channels_includes(intersection, op_reg_class, op_channel)) {
 +              p2p_dbg(p2p, "Pick own channel preference (reg_class %u channel %u) from intersection",
 +                      op_reg_class, op_channel);
 +              p2p->op_reg_class = op_reg_class;
 +              p2p->op_channel = op_channel;
 +              return;
 +      }
 +
 +      if (p2p->best_freq_overall > 0 &&
 +          p2p_freq_to_channel(p2p->best_freq_overall,
 +                              &op_reg_class, &op_channel) == 0 &&
 +          p2p_channels_includes(intersection, op_reg_class, op_channel)) {
 +              p2p_dbg(p2p, "Pick best overall channel (reg_class %u channel %u) from intersection",
 +                      op_reg_class, op_channel);
 +              p2p->op_reg_class = op_reg_class;
 +              p2p->op_channel = op_channel;
 +              return;
 +      }
 +
 +      /* First, try to pick the best channel from another band */
 +      freq = p2p_channel_to_freq(p2p->op_reg_class, p2p->op_channel);
 +      if (freq >= 2400 && freq < 2500 && p2p->best_freq_5 > 0 &&
 +          !p2p_channels_includes(intersection, p2p->op_reg_class,
 +                                 p2p->op_channel) &&
 +          p2p_freq_to_channel(p2p->best_freq_5,
 +                              &op_reg_class, &op_channel) == 0 &&
 +          p2p_channels_includes(intersection, op_reg_class, op_channel)) {
 +              p2p_dbg(p2p, "Pick best 5 GHz channel (reg_class %u channel %u) from intersection",
 +                      op_reg_class, op_channel);
 +              p2p->op_reg_class = op_reg_class;
 +              p2p->op_channel = op_channel;
 +              return;
 +      }
 +
 +      if (freq >= 4900 && freq < 6000 && p2p->best_freq_24 > 0 &&
 +          !p2p_channels_includes(intersection, p2p->op_reg_class,
 +                                 p2p->op_channel) &&
 +          p2p_freq_to_channel(p2p->best_freq_24,
 +                              &op_reg_class, &op_channel) == 0 &&
 +          p2p_channels_includes(intersection, op_reg_class, op_channel)) {
 +              p2p_dbg(p2p, "Pick best 2.4 GHz channel (reg_class %u channel %u) from intersection",
 +                      op_reg_class, op_channel);
 +              p2p->op_reg_class = op_reg_class;
 +              p2p->op_channel = op_channel;
 +              return;
 +      }
 +
 +      /* Select channel with highest preference if the peer supports it */
 +      for (i = 0; p2p->cfg->pref_chan && i < p2p->cfg->num_pref_chan; i++) {
 +              if (p2p_channels_includes(intersection,
 +                                        p2p->cfg->pref_chan[i].op_class,
 +                                        p2p->cfg->pref_chan[i].chan)) {
 +                      p2p->op_reg_class = p2p->cfg->pref_chan[i].op_class;
 +                      p2p->op_channel = p2p->cfg->pref_chan[i].chan;
 +                      p2p_dbg(p2p, "Pick highest preferred 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 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 */
 +      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;
 +      }
 +
 +      /*
 +       * Try to see if the original channel is in the intersection. If
 +       * so, no need to change anything, as it already contains some
 +       * randomness.
 +       */
 +      if (p2p_channels_includes(intersection, p2p->op_reg_class,
 +                                p2p->op_channel)) {
 +              p2p_dbg(p2p, "Using original operating class and channel (op_class %u channel %u) from intersection",
 +                      p2p->op_reg_class, p2p->op_channel);
 +              return;
 +      }
 +
 +      /*
 +       * Fall back to whatever is included in the channel intersection since
 +       * no better options seems to be available.
 +       */
 +      cl = &intersection->reg_class[0];
 +      p2p_dbg(p2p, "Pick another channel (reg_class %u channel %u) from intersection",
 +              cl->reg_class, cl->channel[0]);
 +      p2p->op_reg_class = cl->reg_class;
 +      p2p->op_channel = cl->channel[0];
 +}
 +
 +
 +int p2p_go_select_channel(struct p2p_data *p2p, struct p2p_device *dev,
 +                        u8 *status)
 +{
 +      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;
 +              p2p_dbg(p2p, "No common channels found");
 +              return -1;
 +      }
 +
 +      if (!p2p_channels_includes(&intersection, p2p->op_reg_class,
 +                                 p2p->op_channel)) {
 +              if (dev->flags & P2P_DEV_FORCE_FREQ) {
 +                      *status = P2P_SC_FAIL_NO_COMMON_CHANNELS;
 +                      p2p_dbg(p2p, "Peer does not support the forced channel");
 +                      return -1;
 +              }
 +
 +              p2p_dbg(p2p, "Selected operating channel (op_class %u channel %u) not acceptable to the peer",
 +                      p2p->op_reg_class, p2p->op_channel);
 +              p2p_reselect_channel(p2p, &intersection);
 +      } else if (!(dev->flags & P2P_DEV_FORCE_FREQ) &&
 +                 !p2p->cfg->cfg_op_channel) {
 +              p2p_dbg(p2p, "Try to optimize channel selection with peer information received; previously selected op_class %u channel %u",
 +                      p2p->op_reg_class, p2p->op_channel);
 +              p2p_reselect_channel(p2p, &intersection);
 +      }
 +
 +      if (!p2p->ssid_set) {
 +              p2p_build_ssid(p2p, p2p->ssid, &p2p->ssid_len);
 +              p2p->ssid_set = 1;
 +      }
 +
 +      return 0;
 +}
 +
 +
++static void p2p_check_pref_chan_no_recv(struct p2p_data *p2p, int go,
++                                      struct p2p_device *dev,
++                                      struct p2p_message *msg,
++                                      unsigned freq_list[], unsigned int size)
++{
++      u8 op_class, op_channel;
++      unsigned int oper_freq = 0, i, j;
++      int found = 0;
++
++      p2p_dbg(p2p,
++              "Peer didn't provide a preferred frequency list, see if any of our preferred channels are supported by peer device");
++
++      /*
++       * Search for a common channel in our preferred frequency list which is
++       * also supported by the peer device.
++       */
++      for (i = 0; i < size && !found; i++) {
++              /*
++               * Make sure that the common frequency is:
++               * 1. Supported by peer
++               * 2. Allowed for P2P use.
++               */
++              oper_freq = freq_list[i];
++              if (p2p_freq_to_channel(oper_freq, &op_class,
++                                      &op_channel) < 0) {
++                      p2p_dbg(p2p, "Unsupported frequency %u MHz", oper_freq);
++                      continue;
++              }
++              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,
++                              "Freq %u MHz (oper_class %u channel %u) not allowed for P2P",
++                              oper_freq, op_class, op_channel);
++                      break;
++              }
++              for (j = 0; j < msg->channel_list_len; j++) {
++
++                      if (op_channel != msg->channel_list[j])
++                              continue;
++
++                      p2p->op_reg_class = op_class;
++                      p2p->op_channel = op_channel;
++                      os_memcpy(&p2p->channels, &p2p->cfg->channels,
++                                sizeof(struct p2p_channels));
++                      found = 1;
++                      break;
++              }
++      }
++
++      if (found) {
++              p2p_dbg(p2p,
++                      "Freq %d MHz is a preferred channel and is also supported by peer, use it as the operating channel",
++                      oper_freq);
++      } else {
++              p2p_dbg(p2p,
++                      "None of our preferred channels are supported by peer!. Use: %d MHz for oper_channel",
++                      dev->oper_freq);
++      }
++}
++
++
++static void p2p_check_pref_chan_recv(struct p2p_data *p2p, int go,
++                                   struct p2p_device *dev,
++                                   struct p2p_message *msg,
++                                   unsigned freq_list[], unsigned int size)
++{
++      u8 op_class, op_channel;
++      unsigned int oper_freq = 0, i, j;
++      int found = 0;
++
++      /*
++       * Peer device supports a Preferred Frequency List.
++       * Search for a common channel in the preferred frequency lists
++       * of both peer and local devices.
++       */
++      for (i = 0; i < size && !found; i++) {
++              for (j = 2; j < (msg->pref_freq_list_len / 2); j++) {
++                      oper_freq = p2p_channel_to_freq(
++                              msg->pref_freq_list[2 * j],
++                              msg->pref_freq_list[2 * j + 1]);
++                      if (freq_list[i] != oper_freq)
++                              continue;
++
++                      /*
++                       * Make sure that the found frequency is:
++                       * 1. Supported
++                       * 2. Allowed for P2P use.
++                       */
++                      if (p2p_freq_to_channel(oper_freq, &op_class,
++                                              &op_channel) < 0) {
++                              p2p_dbg(p2p, "Unsupported frequency %u MHz",
++                                      oper_freq);
++                              continue;
++                      }
++
++                      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,
++                                      "Freq %u MHz (oper_class %u channel %u) not allowed for P2P",
++                                      oper_freq, op_class, op_channel);
++                              break;
++                      }
++                      p2p->op_reg_class = op_class;
++                      p2p->op_channel = op_channel;
++                      os_memcpy(&p2p->channels, &p2p->cfg->channels,
++                                sizeof(struct p2p_channels));
++                      found = 1;
++                      break;
++              }
++      }
++
++      if (found) {
++              p2p_dbg(p2p,
++                      "Freq %d MHz is a common preferred channel for both peer and local, use it as operating channel",
++                      oper_freq);
++      } else {
++              p2p_dbg(p2p,
++                      "No common preferred channels found! Use: %d MHz for oper_channel",
++                      dev->oper_freq);
++      }
++}
++
++
++void p2p_check_pref_chan(struct p2p_data *p2p, int go,
++                       struct p2p_device *dev, struct p2p_message *msg)
++{
++      unsigned int freq_list[P2P_MAX_PREF_CHANNELS], size;
++      unsigned int i;
++      u8 op_class, op_channel;
++
++      /*
++       * Use the preferred channel list from the driver only if there is no
++       * forced_freq, e.g., P2P_CONNECT freq=..., and no preferred operating
++       * channel hardcoded in the configuration file.
++       */
++      if (!p2p->cfg->get_pref_freq_list || p2p->cfg->num_pref_chan ||
++          (dev->flags & P2P_DEV_FORCE_FREQ) || p2p->cfg->cfg_op_channel)
++              return;
++
++      /* Obtain our preferred frequency list from driver based on P2P role. */
++      size = P2P_MAX_PREF_CHANNELS;
++      if (p2p->cfg->get_pref_freq_list(p2p->cfg->cb_ctx, go, &size,
++                                       freq_list))
++              return;
++
++      /*
++       * Check if peer's preference of operating channel is in
++       * our preferred channel list.
++       */
++      for (i = 0; i < size; i++) {
++              if (freq_list[i] == (unsigned int) dev->oper_freq)
++                      break;
++      }
++      if (i != size) {
++              /* Peer operating channel preference matches our preference */
++              if (p2p_freq_to_channel(freq_list[i], &op_class, &op_channel) <
++                  0) {
++                      p2p_dbg(p2p,
++                              "Peer operating channel preference is unsupported frequency %u MHz",
++                              freq_list[i]);
++              } else {
++                      p2p->op_reg_class = op_class;
++                      p2p->op_channel = op_channel;
++                      os_memcpy(&p2p->channels, &p2p->cfg->channels,
++                                sizeof(struct p2p_channels));
++                      return;
++              }
++      }
++
++      p2p_dbg(p2p,
++              "Peer operating channel preference: %d MHz is not in our preferred channel list",
++              dev->oper_freq);
++
++      /*
++        Check if peer's preferred channel list is
++        * _not_ included in the GO Negotiation Request or Invitation Request.
++        */
++      if (msg->pref_freq_list_len == 0)
++              p2p_check_pref_chan_no_recv(p2p, go, dev, msg, freq_list, size);
++      else
++              p2p_check_pref_chan_recv(p2p, go, dev, msg, freq_list, size);
++}
++
++
 +void p2p_process_go_neg_req(struct p2p_data *p2p, const u8 *sa,
 +                          const u8 *data, size_t len, int rx_freq)
 +{
 +      struct p2p_device *dev = NULL;
 +      struct wpabuf *resp;
 +      struct p2p_message msg;
 +      u8 status = P2P_SC_FAIL_INVALID_PARAMS;
 +      int tie_breaker = 0;
 +      int freq;
 +
 +      p2p_dbg(p2p, "Received GO Negotiation Request from " MACSTR "(freq=%d)",
 +              MAC2STR(sa), rx_freq);
 +
 +      if (p2p_parse(data, len, &msg))
 +              return;
 +
 +      if (!msg.capability) {
 +              p2p_dbg(p2p, "Mandatory Capability attribute missing from GO Negotiation Request");
 +#ifdef CONFIG_P2P_STRICT
 +              goto fail;
 +#endif /* CONFIG_P2P_STRICT */
 +      }
 +
 +      if (msg.go_intent)
 +              tie_breaker = *msg.go_intent & 0x01;
 +      else {
 +              p2p_dbg(p2p, "Mandatory GO Intent attribute missing from GO Negotiation Request");
 +#ifdef CONFIG_P2P_STRICT
 +              goto fail;
 +#endif /* CONFIG_P2P_STRICT */
 +      }
 +
 +      if (!msg.config_timeout) {
 +              p2p_dbg(p2p, "Mandatory Configuration Timeout attribute missing from GO Negotiation Request");
 +#ifdef CONFIG_P2P_STRICT
 +              goto fail;
 +#endif /* CONFIG_P2P_STRICT */
 +      }
 +
 +      if (!msg.listen_channel) {
 +              p2p_dbg(p2p, "No Listen Channel attribute received");
 +              goto fail;
 +      }
 +      if (!msg.operating_channel) {
 +              p2p_dbg(p2p, "No Operating Channel attribute received");
 +              goto fail;
 +      }
 +      if (!msg.channel_list) {
 +              p2p_dbg(p2p, "No Channel List attribute received");
 +              goto fail;
 +      }
 +      if (!msg.intended_addr) {
 +              p2p_dbg(p2p, "No Intended P2P Interface Address attribute received");
 +              goto fail;
 +      }
 +      if (!msg.p2p_device_info) {
 +              p2p_dbg(p2p, "No P2P Device Info attribute received");
 +              goto fail;
 +      }
 +
 +      if (os_memcmp(msg.p2p_device_addr, sa, ETH_ALEN) != 0) {
 +              p2p_dbg(p2p, "Unexpected GO Negotiation Request SA=" MACSTR
 +                      " != dev_addr=" MACSTR,
 +                      MAC2STR(sa), MAC2STR(msg.p2p_device_addr));
 +              goto fail;
 +      }
 +
 +      dev = p2p_get_device(p2p, 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) ||
 +                !(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 &&
 +                  (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;
 +              p2p->cfg->go_neg_req_rx(p2p->cfg->cb_ctx, sa,
++                                      msg.dev_password_id,
++                                      msg.go_intent ? (*msg.go_intent >> 1) :
++                                      0);
 +      } else if (p2p->go_neg_peer && p2p->go_neg_peer != dev) {
 +              p2p_dbg(p2p, "Already in Group Formation with another peer");
 +              status = P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE;
 +      } else {
 +              int go;
 +
 +              if (!p2p->go_neg_peer) {
 +                      p2p_dbg(p2p, "Starting GO Negotiation with previously authorized peer");
 +                      if (!(dev->flags & P2P_DEV_FORCE_FREQ)) {
 +                              p2p_dbg(p2p, "Use default channel settings");
 +                              p2p->op_reg_class = p2p->cfg->op_reg_class;
 +                              p2p->op_channel = p2p->cfg->op_channel;
 +                              os_memcpy(&p2p->channels, &p2p->cfg->channels,
 +                                        sizeof(struct p2p_channels));
 +                      } else {
 +                              p2p_dbg(p2p, "Use previously configured forced channel settings");
 +                      }
 +              }
 +
 +              dev->flags &= ~P2P_DEV_NOT_YET_READY;
 +
 +              if (!msg.go_intent) {
 +                      p2p_dbg(p2p, "No GO Intent attribute received");
 +                      goto fail;
 +              }
 +              if ((*msg.go_intent >> 1) > P2P_MAX_GO_INTENT) {
 +                      p2p_dbg(p2p, "Invalid GO Intent value (%u) received",
 +                              *msg.go_intent >> 1);
 +                      goto fail;
 +              }
 +
 +              if (dev->go_neg_req_sent &&
 +                  os_memcmp(sa, p2p->cfg->dev_addr, ETH_ALEN) > 0) {
 +                      p2p_dbg(p2p, "Do not reply since peer has higher address and GO Neg Request already sent");
 +                      p2p_parse_free(&msg);
 +                      return;
 +              }
 +
 +              go = p2p_go_det(p2p->go_intent, *msg.go_intent);
 +              if (go < 0) {
 +                      p2p_dbg(p2p, "Incompatible GO Intent");
 +                      status = P2P_SC_FAIL_BOTH_GO_INTENT_15;
 +                      goto fail;
 +              }
 +
 +              if (p2p_peer_channels(p2p, dev, msg.channel_list,
 +                                    msg.channel_list_len) < 0) {
 +                      p2p_dbg(p2p, "No common channels found");
 +                      status = P2P_SC_FAIL_NO_COMMON_CHANNELS;
 +                      goto fail;
 +              }
 +
 +              switch (msg.dev_password_id) {
 +              case DEV_PW_REGISTRAR_SPECIFIED:
 +                      p2p_dbg(p2p, "PIN from peer Display");
 +                      if (dev->wps_method != WPS_PIN_KEYPAD) {
 +                              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;
 +              case DEV_PW_USER_SPECIFIED:
 +                      p2p_dbg(p2p, "Peer entered PIN on Keypad");
 +                      if (dev->wps_method != WPS_PIN_DISPLAY) {
 +                              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;
 +              case DEV_PW_PUSHBUTTON:
 +                      p2p_dbg(p2p, "Peer using pushbutton");
 +                      if (dev->wps_method != WPS_PBC) {
 +                              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;
 +              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;
 +                      goto fail;
 +              }
 +
 +              if (go && p2p_go_select_channel(p2p, dev, &status) < 0)
 +                      goto fail;
 +
 +              dev->go_state = go ? LOCAL_GO : REMOTE_GO;
 +              dev->oper_freq = p2p_channel_to_freq(msg.operating_channel[3],
 +                                                   msg.operating_channel[4]);
 +              p2p_dbg(p2p, "Peer operating channel preference: %d MHz",
 +                      dev->oper_freq);
 +
++              /*
++               * Use the driver preferred frequency list extension if
++               * supported.
++               */
++              p2p_check_pref_chan(p2p, go, dev, &msg);
++
 +              if (msg.config_timeout) {
 +                      dev->go_timeout = msg.config_timeout[0];
 +                      dev->client_timeout = msg.config_timeout[1];
 +              }
 +
 +              p2p_dbg(p2p, "GO Negotiation with " MACSTR, MAC2STR(sa));
 +              if (p2p->state != P2P_IDLE)
 +                      p2p_stop_find_for_freq(p2p, rx_freq);
 +              p2p_set_state(p2p, P2P_GO_NEG);
 +              p2p_clear_timeout(p2p);
 +              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;
 +      }
 +
 +fail:
 +      if (dev)
 +              dev->status = status;
 +      resp = p2p_build_go_neg_resp(p2p, dev, msg.dialog_token, status,
 +                                   !tie_breaker);
 +      p2p_parse_free(&msg);
 +      if (resp == NULL)
 +              return;
 +      p2p_dbg(p2p, "Sending GO Negotiation Response");
 +      if (rx_freq > 0)
 +              freq = rx_freq;
 +      else
 +              freq = p2p_channel_to_freq(p2p->cfg->reg_class,
 +                                         p2p->cfg->channel);
 +      if (freq < 0) {
 +              p2p_dbg(p2p, "Unknown regulatory class/channel");
 +              wpabuf_free(resp);
 +              return;
 +      }
 +      if (status == P2P_SC_SUCCESS) {
 +              p2p->pending_action_state = P2P_PENDING_GO_NEG_RESPONSE;
 +              dev->flags |= P2P_DEV_WAIT_GO_NEG_CONFIRM;
 +              if (os_memcmp(sa, p2p->cfg->dev_addr, ETH_ALEN) < 0) {
 +                      /*
 +                       * Peer has smaller address, so the GO Negotiation
 +                       * Response from us is expected to complete
 +                       * negotiation. Ignore a GO Negotiation Response from
 +                       * the peer if it happens to be received after this
 +                       * point due to a race condition in GO Negotiation
 +                       * Request transmission and processing.
 +                       */
 +                      dev->flags &= ~P2P_DEV_WAIT_GO_NEG_RESPONSE;
 +              }
 +      } else
 +              p2p->pending_action_state =
 +                      P2P_PENDING_GO_NEG_RESPONSE_FAILURE;
 +      if (p2p_send_action(p2p, freq, sa, p2p->cfg->dev_addr,
 +                          p2p->cfg->dev_addr,
 +                          wpabuf_head(resp), wpabuf_len(resp), 500) < 0) {
 +              p2p_dbg(p2p, "Failed to send Action frame");
 +      }
 +
 +      wpabuf_free(resp);
 +}
 +
 +
 +static struct wpabuf * p2p_build_go_neg_conf(struct p2p_data *p2p,
 +                                           struct p2p_device *peer,
 +                                           u8 dialog_token, u8 status,
 +                                           const u8 *resp_chan, int go)
 +{
 +      struct wpabuf *buf;
 +      u8 *len;
 +      struct p2p_channels res;
 +      u8 group_capab;
 +      size_t extra = 0;
 +
 +      p2p_dbg(p2p, "Building GO Negotiation Confirm");
 +
 +#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_CONF])
 +              extra += wpabuf_len(p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_CONF]);
 +
 +      buf = wpabuf_alloc(1000 + extra);
 +      if (buf == NULL)
 +              return NULL;
 +
 +      p2p_buf_add_public_action_hdr(buf, P2P_GO_NEG_CONF, dialog_token);
 +
 +      len = p2p_buf_add_ie_hdr(buf);
 +      p2p_buf_add_status(buf, status);
 +      group_capab = 0;
 +      if (peer->go_state == LOCAL_GO) {
 +              if (peer->flags & P2P_DEV_PREFER_PERSISTENT_GROUP) {
 +                      group_capab |= P2P_GROUP_CAPAB_PERSISTENT_GROUP;
 +                      if (peer->flags & P2P_DEV_PREFER_PERSISTENT_RECONN)
 +                              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,
 +                             group_capab);
 +      if (go || resp_chan == NULL)
 +              p2p_buf_add_operating_channel(buf, p2p->cfg->country,
 +                                            p2p->op_reg_class,
 +                                            p2p->op_channel);
 +      else
 +              p2p_buf_add_operating_channel(buf, (const char *) resp_chan,
 +                                            resp_chan[3], resp_chan[4]);
 +      p2p_channels_intersect(&p2p->channels, &peer->channels, &res);
 +      p2p_buf_add_channel_list(buf, p2p->cfg->country, &res);
 +      if (go) {
 +              p2p_buf_add_group_id(buf, p2p->cfg->dev_addr, p2p->ssid,
 +                                   p2p->ssid_len);
 +      }
 +      p2p_buf_update_ie_hdr(buf, len);
 +
 +#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_CONF])
 +              wpabuf_put_buf(buf, p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_CONF]);
 +
 +      return buf;
 +}
 +
 +
 +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;
 +      int go = -1;
 +      struct p2p_message msg;
 +      u8 status = P2P_SC_SUCCESS;
 +      int freq;
 +
 +      p2p_dbg(p2p, "Received GO Negotiation Response from " MACSTR
 +              " (freq=%d)", MAC2STR(sa), rx_freq);
 +      dev = p2p_get_device(p2p, sa);
 +      if (dev == NULL || dev->wps_method == WPS_NOT_READY ||
 +          dev != p2p->go_neg_peer) {
 +              p2p_dbg(p2p, "Not ready for GO negotiation with " MACSTR,
 +                      MAC2STR(sa));
 +              return;
 +      }
 +
 +      if (p2p_parse(data, len, &msg))
 +              return;
 +
 +      if (!(dev->flags & P2P_DEV_WAIT_GO_NEG_RESPONSE)) {
 +              p2p_dbg(p2p, "Was not expecting GO Negotiation Response - ignore");
 +              p2p_parse_free(&msg);
 +              return;
 +      }
 +      dev->flags &= ~P2P_DEV_WAIT_GO_NEG_RESPONSE;
 +
 +      if (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_dbg(p2p, "No Status attribute received");
 +              status = P2P_SC_FAIL_INVALID_PARAMS;
 +              goto fail;
 +      }
 +      if (*msg.status) {
 +              p2p_dbg(p2p, "GO Negotiation rejected: status %d", *msg.status);
 +              dev->go_neg_req_sent = 0;
 +              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;
 +                      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, *msg.status);
 +              }
 +              p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
 +              p2p_parse_free(&msg);
 +              return;
 +      }
 +
 +      if (!msg.capability) {
 +              p2p_dbg(p2p, "Mandatory Capability attribute missing from GO Negotiation Response");
 +#ifdef CONFIG_P2P_STRICT
 +              status = P2P_SC_FAIL_INVALID_PARAMS;
 +              goto fail;
 +#endif /* CONFIG_P2P_STRICT */
 +      }
 +
 +      if (!msg.p2p_device_info) {
 +              p2p_dbg(p2p, "Mandatory P2P Device Info attribute missing from GO Negotiation Response");
 +#ifdef CONFIG_P2P_STRICT
 +              status = P2P_SC_FAIL_INVALID_PARAMS;
 +              goto fail;
 +#endif /* CONFIG_P2P_STRICT */
 +      }
 +
 +      if (!msg.intended_addr) {
 +              p2p_dbg(p2p, "No Intended P2P Interface Address attribute received");
 +              status = P2P_SC_FAIL_INVALID_PARAMS;
 +              goto fail;
 +      }
 +
 +      if (!msg.go_intent) {
 +              p2p_dbg(p2p, "No GO Intent attribute received");
 +              status = P2P_SC_FAIL_INVALID_PARAMS;
 +              goto fail;
 +      }
 +      if ((*msg.go_intent >> 1) > P2P_MAX_GO_INTENT) {
 +              p2p_dbg(p2p, "Invalid GO Intent value (%u) received",
 +                      *msg.go_intent >> 1);
 +              status = P2P_SC_FAIL_INVALID_PARAMS;
 +              goto fail;
 +      }
 +
 +      go = p2p_go_det(p2p->go_intent, *msg.go_intent);
 +      if (go < 0) {
 +              p2p_dbg(p2p, "Incompatible GO Intent");
 +              status = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
 +              goto fail;
 +      }
 +
 +      if (!go && msg.group_id) {
 +              /* Store SSID for Provisioning step */
 +              p2p->ssid_len = msg.group_id_len - ETH_ALEN;
 +              os_memcpy(p2p->ssid, msg.group_id + ETH_ALEN, p2p->ssid_len);
 +      } else if (!go) {
 +              p2p_dbg(p2p, "Mandatory P2P Group ID attribute missing from GO Negotiation Response");
 +              p2p->ssid_len = 0;
 +              status = P2P_SC_FAIL_INVALID_PARAMS;
 +              goto fail;
 +      }
 +
 +      if (!msg.config_timeout) {
 +              p2p_dbg(p2p, "Mandatory Configuration Timeout attribute missing from GO Negotiation Response");
 +#ifdef CONFIG_P2P_STRICT
 +              status = P2P_SC_FAIL_INVALID_PARAMS;
 +              goto fail;
 +#endif /* CONFIG_P2P_STRICT */
 +      } else {
 +              dev->go_timeout = msg.config_timeout[0];
 +              dev->client_timeout = msg.config_timeout[1];
 +      }
 +
 +      if (!msg.operating_channel && !go) {
 +              /*
 +               * Note: P2P Client may omit Operating Channel attribute to
 +               * indicate it does not have a preference.
 +               */
 +              p2p_dbg(p2p, "No Operating Channel attribute received");
 +              status = P2P_SC_FAIL_INVALID_PARAMS;
 +              goto fail;
 +      }
 +      if (!msg.channel_list) {
 +              p2p_dbg(p2p, "No Channel List attribute received");
 +              status = P2P_SC_FAIL_INVALID_PARAMS;
 +              goto fail;
 +      }
 +
 +      if (p2p_peer_channels(p2p, dev, msg.channel_list,
 +                            msg.channel_list_len) < 0) {
 +              p2p_dbg(p2p, "No common channels found");
 +              status = P2P_SC_FAIL_NO_COMMON_CHANNELS;
 +              goto fail;
 +      }
 +
 +      if (msg.operating_channel) {
 +              dev->oper_freq = p2p_channel_to_freq(msg.operating_channel[3],
 +                                                   msg.operating_channel[4]);
 +              p2p_dbg(p2p, "Peer operating channel preference: %d MHz",
 +                      dev->oper_freq);
 +      } else
 +              dev->oper_freq = 0;
 +
 +      switch (msg.dev_password_id) {
 +      case DEV_PW_REGISTRAR_SPECIFIED:
 +              p2p_dbg(p2p, "PIN from peer Display");
 +              if (dev->wps_method != WPS_PIN_KEYPAD) {
 +                      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;
 +      case DEV_PW_USER_SPECIFIED:
 +              p2p_dbg(p2p, "Peer entered PIN on Keypad");
 +              if (dev->wps_method != WPS_PIN_DISPLAY) {
 +                      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;
 +      case DEV_PW_PUSHBUTTON:
 +              p2p_dbg(p2p, "Peer using pushbutton");
 +              if (dev->wps_method != WPS_PBC) {
 +                      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;
 +      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;
 +              goto fail;
 +      }
 +
 +      if (go && p2p_go_select_channel(p2p, dev, &status) < 0)
 +              goto fail;
 +
++      /*
++       * Use the driver preferred frequency list extension if local device is
++       * GO.
++       */
++      if (go)
++              p2p_check_pref_chan(p2p, go, dev, &msg);
++
 +      p2p_set_state(p2p, P2P_GO_NEG);
 +      p2p_clear_timeout(p2p);
 +
 +      p2p_dbg(p2p, "GO Negotiation with " MACSTR, MAC2STR(sa));
 +      os_memcpy(dev->intended_addr, msg.intended_addr, ETH_ALEN);
 +
 +fail:
 +      /* 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 (dev->go_neg_conf == NULL)
 +              return;
 +      p2p_dbg(p2p, "Sending GO Negotiation Confirm");
 +      if (status == P2P_SC_SUCCESS) {
 +              p2p->pending_action_state = P2P_PENDING_GO_NEG_CONFIRM;
 +              dev->go_state = go ? LOCAL_GO : REMOTE_GO;
 +      } else
 +              p2p->pending_action_state = P2P_NO_PENDING_ACTION;
 +      if (rx_freq > 0)
 +              freq = rx_freq;
 +      else
 +              freq = dev->listen_freq;
 +
 +      dev->go_neg_conf_freq = freq;
 +      dev->go_neg_conf_sent = 0;
 +
 +      if (p2p_send_action(p2p, freq, sa, p2p->cfg->dev_addr, sa,
 +                          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, -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, status);
 +      }
 +}
 +
 +
 +void p2p_process_go_neg_conf(struct p2p_data *p2p, const u8 *sa,
 +                           const u8 *data, size_t len)
 +{
 +      struct p2p_device *dev;
 +      struct p2p_message msg;
 +
 +      p2p_dbg(p2p, "Received GO Negotiation Confirm from " MACSTR,
 +              MAC2STR(sa));
 +      dev = p2p_get_device(p2p, sa);
 +      if (dev == NULL || dev->wps_method == WPS_NOT_READY ||
 +          dev != p2p->go_neg_peer) {
 +              p2p_dbg(p2p, "Not ready for GO negotiation with " MACSTR,
 +                      MAC2STR(sa));
 +              return;
 +      }
 +
 +      if (p2p->pending_action_state == P2P_PENDING_GO_NEG_RESPONSE) {
 +              p2p_dbg(p2p, "Stopped waiting for TX status on GO Negotiation Response since we already received Confirmation");
 +              p2p->pending_action_state = P2P_NO_PENDING_ACTION;
 +      }
 +
 +      if (p2p_parse(data, len, &msg))
 +              return;
 +
 +      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)",
 +                      msg.dialog_token, dev->dialog_token);
 +              p2p_parse_free(&msg);
 +              return;
 +      }
 +
 +      if (!msg.status) {
 +              p2p_dbg(p2p, "No Status attribute received");
 +              p2p_parse_free(&msg);
 +              return;
 +      }
 +      if (*msg.status) {
 +              p2p_dbg(p2p, "GO Negotiation rejected: status %d", *msg.status);
 +              p2p_go_neg_failed(p2p, *msg.status);
 +              p2p_parse_free(&msg);
 +              return;
 +      }
 +
 +      if (dev->go_state == REMOTE_GO && msg.group_id) {
 +              /* Store SSID for Provisioning step */
 +              p2p->ssid_len = msg.group_id_len - ETH_ALEN;
 +              os_memcpy(p2p->ssid, msg.group_id + ETH_ALEN, p2p->ssid_len);
 +      } 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, P2P_SC_FAIL_INVALID_PARAMS);
 +              p2p_parse_free(&msg);
 +              return;
 +      }
 +
 +      if (!msg.operating_channel) {
 +              p2p_dbg(p2p, "Mandatory Operating Channel attribute missing from GO Negotiation Confirmation");
 +#ifdef CONFIG_P2P_STRICT
 +              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) {
 +              p2p_dbg(p2p, "Mandatory Operating Channel attribute missing from GO Negotiation Confirmation");
 +#ifdef CONFIG_P2P_STRICT
 +              p2p_parse_free(&msg);
 +              return;
 +#endif /* CONFIG_P2P_STRICT */
 +      }
 +
 +      p2p_parse_free(&msg);
 +
 +      if (dev->go_state == UNKNOWN_GO) {
 +              /*
 +               * This should not happen since GO negotiation has already
 +               * been completed.
 +               */
 +              p2p_dbg(p2p, "Unexpected GO Neg state - do not know which end becomes GO");
 +              return;
 +      }
 +
 +      /*
 +       * The peer could have missed our ctrl::ack frame for GO Negotiation
 +       * Confirm and continue retransmitting the frame. To reduce the
 +       * likelihood of the peer not getting successful TX status for the
 +       * GO Negotiation Confirm frame, wait a short time here before starting
 +       * the group so that we will remain on the current channel to
 +       * acknowledge any possible retransmission from the peer.
 +       */
 +      p2p_dbg(p2p, "20 ms wait on current channel before starting group");
 +      os_sleep(0, 20000);
 +
 +      p2p_go_complete(p2p, dev);
 +}
index 41ca99faaf4814b77b41654f24c00614dd2455fd,0000000000000000000000000000000000000000..0d66993465686e9d350a4ee861e75b8d7f188d70
mode 100644,000000..100644
--- /dev/null
@@@ -1,1073 -1,0 +1,1113 @@@
 +/*
 + * Wi-Fi Direct - P2P group operations
 + * 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 "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"
 +#include "p2p.h"
 +
 +
 +struct p2p_group_member {
 +      struct p2p_group_member *next;
 +      u8 addr[ETH_ALEN]; /* P2P Interface Address */
 +      u8 dev_addr[ETH_ALEN]; /* P2P Device Address */
 +      struct wpabuf *p2p_ie;
 +      struct wpabuf *wfd_ie;
 +      struct wpabuf *client_info;
 +      u8 dev_capab;
 +};
 +
 +/**
 + * struct p2p_group - Internal P2P module per-group data
 + */
 +struct p2p_group {
 +      struct p2p_data *p2p;
 +      struct p2p_group_config *cfg;
 +      struct p2p_group_member *members;
 +      unsigned int num_members;
 +      int group_formation;
 +      int beacon_update;
 +      struct wpabuf *noa;
 +      struct wpabuf *wfd_ie;
 +};
 +
 +
 +struct p2p_group * p2p_group_init(struct p2p_data *p2p,
 +                                struct p2p_group_config *config)
 +{
 +      struct p2p_group *group, **groups;
 +
 +      group = os_zalloc(sizeof(*group));
 +      if (group == NULL)
 +              return NULL;
 +
 +      groups = os_realloc_array(p2p->groups, p2p->num_groups + 1,
 +                                sizeof(struct p2p_group *));
 +      if (groups == NULL) {
 +              os_free(group);
 +              return NULL;
 +      }
 +      groups[p2p->num_groups++] = group;
 +      p2p->groups = groups;
 +
 +      group->p2p = p2p;
 +      group->cfg = config;
 +      group->group_formation = 1;
 +      group->beacon_update = 1;
 +      p2p_group_update_ies(group);
 +      group->cfg->idle_update(group->cfg->cb_ctx, 1);
 +
 +      return group;
 +}
 +
 +
 +static void p2p_group_free_member(struct p2p_group_member *m)
 +{
 +      wpabuf_free(m->wfd_ie);
 +      wpabuf_free(m->p2p_ie);
 +      wpabuf_free(m->client_info);
 +      os_free(m);
 +}
 +
 +
 +static void p2p_group_free_members(struct p2p_group *group)
 +{
 +      struct p2p_group_member *m, *prev;
 +      m = group->members;
 +      group->members = NULL;
 +      group->num_members = 0;
 +      while (m) {
 +              prev = m;
 +              m = m->next;
 +              p2p_group_free_member(prev);
 +      }
 +}
 +
 +
 +void p2p_group_deinit(struct p2p_group *group)
 +{
 +      size_t g;
 +      struct p2p_data *p2p;
 +
 +      if (group == NULL)
 +              return;
 +
 +      p2p = group->p2p;
 +
 +      for (g = 0; g < p2p->num_groups; g++) {
 +              if (p2p->groups[g] == group) {
 +                      while (g + 1 < p2p->num_groups) {
 +                              p2p->groups[g] = p2p->groups[g + 1];
 +                              g++;
 +                      }
 +                      p2p->num_groups--;
 +                      break;
 +              }
 +      }
 +
 +      p2p_group_free_members(group);
 +      os_free(group->cfg);
 +      wpabuf_free(group->noa);
 +      wpabuf_free(group->wfd_ie);
 +      os_free(group);
 +}
 +
 +
 +static void p2p_client_info(struct wpabuf *ie, struct p2p_group_member *m)
 +{
 +      if (m->client_info == NULL)
 +              return;
 +      if (wpabuf_tailroom(ie) < wpabuf_len(m->client_info) + 1)
 +              return;
 +      wpabuf_put_buf(ie, m->client_info);
 +}
 +
 +
 +static void p2p_group_add_common_ies(struct p2p_group *group,
 +                                   struct wpabuf *ie)
 +{
 +      u8 dev_capab = group->p2p->dev_capab, group_capab = 0;
 +
 +      /* P2P Capability */
 +      dev_capab &= ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY;
 +      group_capab |= P2P_GROUP_CAPAB_GROUP_OWNER;
 +      if (group->cfg->persistent_group) {
 +              group_capab |= P2P_GROUP_CAPAB_PERSISTENT_GROUP;
 +              if (group->cfg->persistent_group == 2)
 +                      group_capab |= P2P_GROUP_CAPAB_PERSISTENT_RECONN;
 +      }
 +      if (group->p2p->cfg->p2p_intra_bss)
 +              group_capab |= P2P_GROUP_CAPAB_INTRA_BSS_DIST;
 +      if (group->group_formation)
 +              group_capab |= P2P_GROUP_CAPAB_GROUP_FORMATION;
 +      if (group->p2p->cross_connect)
 +              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);
 +}
 +
 +
 +static void p2p_group_add_noa(struct wpabuf *ie, struct wpabuf *noa)
 +{
 +      if (noa == NULL)
 +              return;
 +      /* Notice of Absence */
 +      wpabuf_put_u8(ie, P2P_ATTR_NOTICE_OF_ABSENCE);
 +      wpabuf_put_le16(ie, wpabuf_len(noa));
 +      wpabuf_put_buf(ie, noa);
 +}
 +
 +
 +static struct wpabuf * p2p_group_encaps_probe_resp(struct wpabuf *subelems)
 +{
 +      struct wpabuf *ie;
 +      const u8 *pos, *end;
 +      size_t len;
 +
 +      if (subelems == NULL)
 +              return NULL;
 +
 +      len = wpabuf_len(subelems) + 100;
 +
 +      ie = wpabuf_alloc(len);
 +      if (ie == NULL)
 +              return NULL;
 +
 +      pos = wpabuf_head(subelems);
 +      end = pos + wpabuf_len(subelems);
 +
 +      while (end > pos) {
 +              size_t frag_len = end - pos;
 +              if (frag_len > 251)
 +                      frag_len = 251;
 +              wpabuf_put_u8(ie, WLAN_EID_VENDOR_SPECIFIC);
 +              wpabuf_put_u8(ie, 4 + frag_len);
 +              wpabuf_put_be32(ie, P2P_IE_VENDOR_TYPE);
 +              wpabuf_put_data(ie, pos, frag_len);
 +              pos += frag_len;
 +      }
 +
 +      return ie;
 +}
 +
 +
 +static struct wpabuf * p2p_group_build_beacon_ie(struct p2p_group *group)
 +{
 +      struct wpabuf *ie;
 +      u8 *len;
 +      size_t extra = 0;
 +
 +#ifdef CONFIG_WIFI_DISPLAY
 +      if (group->p2p->wfd_ie_beacon)
 +              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;
 +
 +#ifdef CONFIG_WIFI_DISPLAY
 +      if (group->p2p->wfd_ie_beacon)
 +              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);
 +      p2p_group_add_noa(ie, group->noa);
 +      p2p_buf_update_ie_hdr(ie, len);
 +
 +      return ie;
 +}
 +
 +
 +#ifdef CONFIG_WIFI_DISPLAY
 +
 +struct wpabuf * p2p_group_get_wfd_ie(struct p2p_group *g)
 +{
 +      return g->wfd_ie;
 +}
 +
 +
 +struct wpabuf * wifi_display_encaps(struct wpabuf *subelems)
 +{
 +      struct wpabuf *ie;
 +      const u8 *pos, *end;
 +
 +      if (subelems == NULL)
 +              return NULL;
 +
 +      ie = wpabuf_alloc(wpabuf_len(subelems) + 100);
 +      if (ie == NULL)
 +              return NULL;
 +
 +      pos = wpabuf_head(subelems);
 +      end = pos + wpabuf_len(subelems);
 +
 +      while (end > pos) {
 +              size_t frag_len = end - pos;
 +              if (frag_len > 251)
 +                      frag_len = 251;
 +              wpabuf_put_u8(ie, WLAN_EID_VENDOR_SPECIFIC);
 +              wpabuf_put_u8(ie, 4 + frag_len);
 +              wpabuf_put_be32(ie, WFD_IE_VENDOR_TYPE);
 +              wpabuf_put_data(ie, pos, frag_len);
 +              pos += frag_len;
 +      }
 +
 +      return ie;
 +}
 +
 +
 +static int wifi_display_add_dev_info_descr(struct wpabuf *buf,
 +                                         struct p2p_group_member *m)
 +{
 +      const u8 *pos, *end;
 +      const u8 *dev_info = NULL;
 +      const u8 *assoc_bssid = NULL;
 +      const u8 *coupled_sink = NULL;
 +      u8 zero_addr[ETH_ALEN];
 +
 +      if (m->wfd_ie == NULL)
 +              return 0;
 +
 +      os_memset(zero_addr, 0, ETH_ALEN);
 +      pos = wpabuf_head_u8(m->wfd_ie);
 +      end = pos + wpabuf_len(m->wfd_ie);
 +      while (pos + 1 < end) {
 +              u8 id;
 +              u16 len;
 +
 +              id = *pos++;
 +              len = WPA_GET_BE16(pos);
 +              pos += 2;
 +              if (pos + len > end)
 +                      break;
 +
 +              switch (id) {
 +              case WFD_SUBELEM_DEVICE_INFO:
 +                      if (len < 6)
 +                              break;
 +                      dev_info = pos;
 +                      break;
 +              case WFD_SUBELEM_ASSOCIATED_BSSID:
 +                      if (len < ETH_ALEN)
 +                              break;
 +                      assoc_bssid = pos;
 +                      break;
 +              case WFD_SUBELEM_COUPLED_SINK:
 +                      if (len < 1 + ETH_ALEN)
 +                              break;
 +                      coupled_sink = pos;
 +                      break;
 +              }
 +
 +              pos += len;
 +      }
 +
 +      if (dev_info == NULL)
 +              return 0;
 +
 +      wpabuf_put_u8(buf, 23);
 +      wpabuf_put_data(buf, m->dev_addr, ETH_ALEN);
 +      if (assoc_bssid)
 +              wpabuf_put_data(buf, assoc_bssid, ETH_ALEN);
 +      else
 +              wpabuf_put_data(buf, zero_addr, ETH_ALEN);
 +      wpabuf_put_data(buf, dev_info, 2); /* WFD Device Info */
 +      wpabuf_put_data(buf, dev_info + 4, 2); /* WFD Device Max Throughput */
 +      if (coupled_sink) {
 +              wpabuf_put_data(buf, coupled_sink, 1 + ETH_ALEN);
 +      } else {
 +              wpabuf_put_u8(buf, 0);
 +              wpabuf_put_data(buf, zero_addr, ETH_ALEN);
 +      }
 +
 +      return 1;
 +}
 +
 +
 +static struct wpabuf *
 +wifi_display_build_go_ie(struct p2p_group *group)
 +{
 +      struct wpabuf *wfd_subelems, *wfd_ie;
 +      struct p2p_group_member *m;
 +      u8 *len;
 +      unsigned int count = 0;
 +
 +      if (!group->p2p->wfd_ie_probe_resp)
 +              return NULL;
 +
 +      wfd_subelems = wpabuf_alloc(wpabuf_len(group->p2p->wfd_ie_probe_resp) +
 +                                  group->num_members * 24 + 100);
 +      if (wfd_subelems == NULL)
 +              return NULL;
 +      if (group->p2p->wfd_dev_info)
 +              wpabuf_put_buf(wfd_subelems, group->p2p->wfd_dev_info);
 +      if (group->p2p->wfd_assoc_bssid)
 +              wpabuf_put_buf(wfd_subelems,
 +                             group->p2p->wfd_assoc_bssid);
 +      if (group->p2p->wfd_coupled_sink_info)
 +              wpabuf_put_buf(wfd_subelems,
 +                             group->p2p->wfd_coupled_sink_info);
 +
 +      /* Build WFD Session Info */
 +      wpabuf_put_u8(wfd_subelems, WFD_SUBELEM_SESSION_INFO);
 +      len = wpabuf_put(wfd_subelems, 2);
 +      m = group->members;
 +      while (m) {
 +              if (wifi_display_add_dev_info_descr(wfd_subelems, m))
 +                      count++;
 +              m = m->next;
 +      }
 +
 +      if (count == 0) {
 +              /* No Wi-Fi Display clients - do not include subelement */
 +              wfd_subelems->used -= 3;
 +      } else {
 +              WPA_PUT_BE16(len, (u8 *) wpabuf_put(wfd_subelems, 0) - len -
 +                           2);
 +              p2p_dbg(group->p2p, "WFD: WFD Session Info: %u descriptors",
 +                      count);
 +      }
 +
 +      wfd_ie = wifi_display_encaps(wfd_subelems);
 +      wpabuf_free(wfd_subelems);
 +
 +      return wfd_ie;
 +}
 +
 +static void wifi_display_group_update(struct p2p_group *group)
 +{
 +      wpabuf_free(group->wfd_ie);
 +      group->wfd_ie = wifi_display_build_go_ie(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;
 +
 +      p2p_subelems = wpabuf_alloc(500);
 +      if (p2p_subelems == NULL)
 +              return NULL;
 +
 +      p2p_group_add_common_ies(group, p2p_subelems);
 +      p2p_group_add_noa(p2p_subelems, group->noa);
 +
 +      /* P2P Device Info */
 +      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)
 +              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);
 +              ie = wpabuf_concat(wfd, ie);
 +      }
 +#endif /* CONFIG_WIFI_DISPLAY */
 +
 +      return ie;
 +}
 +
 +
 +void p2p_group_update_ies(struct p2p_group *group)
 +{
 +      struct wpabuf *beacon_ie;
 +      struct wpabuf *probe_resp_ie;
 +
 +#ifdef CONFIG_WIFI_DISPLAY
 +      wifi_display_group_update(group);
 +#endif /* CONFIG_WIFI_DISPLAY */
 +
 +      probe_resp_ie = p2p_group_build_probe_resp_ie(group);
 +      if (probe_resp_ie == NULL)
 +              return;
 +      wpa_hexdump_buf(MSG_MSGDUMP, "P2P: Update GO Probe Response P2P IE",
 +                      probe_resp_ie);
 +
 +      if (group->beacon_update) {
 +              beacon_ie = p2p_group_build_beacon_ie(group);
 +              if (beacon_ie)
 +                      group->beacon_update = 0;
 +              wpa_hexdump_buf(MSG_MSGDUMP, "P2P: Update GO Beacon P2P IE",
 +                              beacon_ie);
 +      } else
 +              beacon_ie = NULL;
 +
 +      group->cfg->ie_update(group->cfg->cb_ctx, beacon_ie, probe_resp_ie);
 +}
 +
 +
 +/**
 + * p2p_build_client_info - Build P2P Client Info Descriptor
 + * @addr: MAC address of the peer device
 + * @p2p_ie: P2P IE from (Re)Association Request
 + * @dev_capab: Buffer for returning Device Capability
 + * @dev_addr: Buffer for returning P2P Device Address
 + * Returns: P2P Client Info Descriptor or %NULL on failure
 + *
 + * This function builds P2P Client Info Descriptor based on the information
 + * available from (Re)Association Request frame. Group owner can use this to
 + * build the P2P Group Info attribute for Probe Response frames.
 + */
 +static struct wpabuf * p2p_build_client_info(const u8 *addr,
 +                                           struct wpabuf *p2p_ie,
 +                                           u8 *dev_capab, u8 *dev_addr)
 +{
 +      const u8 *spos;
 +      struct p2p_message msg;
 +      u8 *len_pos;
 +      struct wpabuf *buf;
 +
 +      if (p2p_ie == NULL)
 +              return NULL;
 +
 +      os_memset(&msg, 0, sizeof(msg));
 +      if (p2p_parse_p2p_ie(p2p_ie, &msg) ||
 +          msg.capability == NULL || msg.p2p_device_info == NULL)
 +              return NULL;
 +
 +      buf = wpabuf_alloc(ETH_ALEN + 1 + 1 + msg.p2p_device_info_len);
 +      if (buf == NULL)
 +              return NULL;
 +
 +      *dev_capab = msg.capability[0];
 +      os_memcpy(dev_addr, msg.p2p_device_addr, ETH_ALEN);
 +
 +      spos = msg.p2p_device_info; /* P2P Device address */
 +
 +      /* P2P Client Info Descriptor */
 +      /* Length to be set */
 +      len_pos = wpabuf_put(buf, 1);
 +      /* P2P Device address */
 +      wpabuf_put_data(buf, spos, ETH_ALEN);
 +      /* P2P Interface address */
 +      wpabuf_put_data(buf, addr, ETH_ALEN);
 +      /* Device Capability Bitmap */
 +      wpabuf_put_u8(buf, msg.capability[0]);
 +      /*
 +       * Config Methods, Primary Device Type, Number of Secondary Device
 +       * Types, Secondary Device Type List, Device Name copied from
 +       * Device Info
 +       */
 +      wpabuf_put_data(buf, spos + ETH_ALEN,
 +                      msg.p2p_device_info_len - ETH_ALEN);
 +
 +      *len_pos = wpabuf_len(buf) - 1;
 +
 +
 +      return buf;
 +}
 +
 +
 +static int p2p_group_remove_member(struct p2p_group *group, const u8 *addr)
 +{
 +      struct p2p_group_member *m, *prev;
 +
 +      if (group == NULL)
 +              return 0;
 +
 +      m = group->members;
 +      prev = NULL;
 +      while (m) {
 +              if (os_memcmp(m->addr, addr, ETH_ALEN) == 0)
 +                      break;
 +              prev = m;
 +              m = m->next;
 +      }
 +
 +      if (m == NULL)
 +              return 0;
 +
 +      if (prev)
 +              prev->next = m->next;
 +      else
 +              group->members = m->next;
 +      p2p_group_free_member(m);
 +      group->num_members--;
 +
 +      return 1;
 +}
 +
 +
 +int p2p_group_notif_assoc(struct p2p_group *group, const u8 *addr,
 +                        const u8 *ie, size_t len)
 +{
 +      struct p2p_group_member *m;
 +
 +      if (group == NULL)
 +              return -1;
 +
 +      p2p_add_device(group->p2p, addr, 0, NULL, 0, ie, len, 0);
 +
 +      m = os_zalloc(sizeof(*m));
 +      if (m == NULL)
 +              return -1;
 +      os_memcpy(m->addr, addr, ETH_ALEN);
 +      m->p2p_ie = ieee802_11_vendor_ie_concat(ie, len, P2P_IE_VENDOR_TYPE);
 +      if (m->p2p_ie) {
 +              m->client_info = p2p_build_client_info(addr, m->p2p_ie,
 +                                                     &m->dev_capab,
 +                                                     m->dev_addr);
 +      }
 +#ifdef CONFIG_WIFI_DISPLAY
 +      m->wfd_ie = ieee802_11_vendor_ie_concat(ie, len, WFD_IE_VENDOR_TYPE);
 +#endif /* CONFIG_WIFI_DISPLAY */
 +
 +      p2p_group_remove_member(group, addr);
 +
 +      m->next = group->members;
 +      group->members = m;
 +      group->num_members++;
 +      p2p_dbg(group->p2p,  "Add client " MACSTR
 +              " to group (p2p=%d wfd=%d client_info=%d); num_members=%u/%u",
 +              MAC2STR(addr), m->p2p_ie ? 1 : 0, m->wfd_ie ? 1 : 0,
 +              m->client_info ? 1 : 0,
 +              group->num_members, group->cfg->max_clients);
 +      if (group->num_members == group->cfg->max_clients)
 +              group->beacon_update = 1;
 +      p2p_group_update_ies(group);
 +      if (group->num_members == 1)
 +              group->cfg->idle_update(group->cfg->cb_ctx, 0);
 +
 +      return 0;
 +}
 +
 +
 +struct wpabuf * p2p_group_assoc_resp_ie(struct p2p_group *group, u8 status)
 +{
 +      struct wpabuf *resp;
 +      u8 *rlen;
 +      size_t extra = 0;
 +
 +#ifdef CONFIG_WIFI_DISPLAY
 +      if (group->wfd_ie)
 +              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
 +       *      denied)
 +       * Extended Listen Timing (may be present)
 +       */
 +      resp = wpabuf_alloc(20 + extra);
 +      if (resp == NULL)
 +              return NULL;
 +
 +#ifdef CONFIG_WIFI_DISPLAY
 +      if (group->wfd_ie)
 +              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);
 +      p2p_buf_update_ie_hdr(resp, rlen);
 +
 +      return resp;
 +}
 +
 +
 +void p2p_group_notif_disassoc(struct p2p_group *group, const u8 *addr)
 +{
 +      if (p2p_group_remove_member(group, addr)) {
 +              p2p_dbg(group->p2p, "Remove client " MACSTR
 +                      " from group; num_members=%u/%u",
 +                      MAC2STR(addr), group->num_members,
 +                      group->cfg->max_clients);
 +              if (group->num_members == group->cfg->max_clients - 1)
 +                      group->beacon_update = 1;
 +              p2p_group_update_ies(group);
 +              if (group->num_members == 0)
 +                      group->cfg->idle_update(group->cfg->cb_ctx, 1);
 +      }
 +}
 +
 +
 +/**
 + * p2p_match_dev_type_member - Match client device type with requested type
 + * @m: Group member
 + * @wps: WPS TLVs from Probe Request frame (concatenated WPS IEs)
 + * Returns: 1 on match, 0 on mismatch
 + *
 + * This function can be used to match the Requested Device Type attribute in
 + * WPS IE with the device types of a group member for deciding whether a GO
 + * should reply to a Probe Request frame.
 + */
 +static int p2p_match_dev_type_member(struct p2p_group_member *m,
 +                                   struct wpabuf *wps)
 +{
 +      const u8 *pos, *end;
 +      struct wps_parse_attr attr;
 +      u8 num_sec;
 +
 +      if (m->client_info == NULL || wps == NULL)
 +              return 0;
 +
 +      pos = wpabuf_head(m->client_info);
 +      end = pos + wpabuf_len(m->client_info);
 +
 +      pos += 1 + 2 * ETH_ALEN + 1 + 2;
 +      if (end - pos < WPS_DEV_TYPE_LEN + 1)
 +              return 0;
 +
 +      if (wps_parse_msg(wps, &attr))
 +              return 1; /* assume no Requested Device Type attributes */
 +
 +      if (attr.num_req_dev_type == 0)
 +              return 1; /* no Requested Device Type attributes -> match */
 +
 +      if (dev_type_list_match(pos, attr.req_dev_type, attr.num_req_dev_type))
 +              return 1; /* Match with client Primary Device Type */
 +
 +      pos += WPS_DEV_TYPE_LEN;
 +      num_sec = *pos++;
 +      if (end - pos < num_sec * WPS_DEV_TYPE_LEN)
 +              return 0;
 +      while (num_sec > 0) {
 +              num_sec--;
 +              if (dev_type_list_match(pos, attr.req_dev_type,
 +                                      attr.num_req_dev_type))
 +                      return 1; /* Match with client Secondary Device Type */
 +              pos += WPS_DEV_TYPE_LEN;
 +      }
 +
 +      /* No matching device type found */
 +      return 0;
 +}
 +
 +
 +int p2p_group_match_dev_type(struct p2p_group *group, struct wpabuf *wps)
 +{
 +      struct p2p_group_member *m;
 +
 +      if (p2p_match_dev_type(group->p2p, wps))
 +              return 1; /* Match with own device type */
 +
 +      for (m = group->members; m; m = m->next) {
 +              if (p2p_match_dev_type_member(m, wps))
 +                      return 1; /* Match with group client device type */
 +      }
 +
 +      /* No match with Requested Device Type */
 +      return 0;
 +}
 +
 +
 +int p2p_group_match_dev_id(struct p2p_group *group, struct wpabuf *p2p)
 +{
 +      struct p2p_group_member *m;
 +      struct p2p_message msg;
 +
 +      os_memset(&msg, 0, sizeof(msg));
 +      if (p2p_parse_p2p_ie(p2p, &msg))
 +              return 1; /* Failed to parse - assume no filter on Device ID */
 +
 +      if (!msg.device_id)
 +              return 1; /* No filter on Device ID */
 +
 +      if (os_memcmp(msg.device_id, group->p2p->cfg->dev_addr, ETH_ALEN) == 0)
 +              return 1; /* Match with our P2P Device Address */
 +
 +      for (m = group->members; m; m = m->next) {
 +              if (os_memcmp(msg.device_id, m->dev_addr, ETH_ALEN) == 0)
 +                      return 1; /* Match with group client P2P Device Address */
 +      }
 +
 +      /* No match with Device ID */
 +      return 0;
 +}
 +
 +
 +void p2p_group_notif_formation_done(struct p2p_group *group)
 +{
 +      if (group == NULL)
 +              return;
 +      group->group_formation = 0;
 +      group->beacon_update = 1;
 +      p2p_group_update_ies(group);
 +}
 +
 +
 +int p2p_group_notif_noa(struct p2p_group *group, const u8 *noa,
 +                      size_t noa_len)
 +{
 +      if (noa == NULL) {
 +              wpabuf_free(group->noa);
 +              group->noa = NULL;
 +      } else {
 +              if (group->noa) {
 +                      if (wpabuf_size(group->noa) >= noa_len) {
 +                              group->noa->used = 0;
 +                              wpabuf_put_data(group->noa, noa, noa_len);
 +                      } else {
 +                              wpabuf_free(group->noa);
 +                              group->noa = NULL;
 +                      }
 +              }
 +
 +              if (!group->noa) {
 +                      group->noa = wpabuf_alloc_copy(noa, noa_len);
 +                      if (group->noa == NULL)
 +                              return -1;
 +              }
 +      }
 +
 +      group->beacon_update = 1;
 +      p2p_group_update_ies(group);
 +      return 0;
 +}
 +
 +
 +static struct p2p_group_member * p2p_group_get_client(struct p2p_group *group,
 +                                                    const u8 *dev_id)
 +{
 +      struct p2p_group_member *m;
 +
 +      for (m = group->members; m; m = m->next) {
 +              if (os_memcmp(dev_id, m->dev_addr, ETH_ALEN) == 0)
 +                      return m;
 +      }
 +
 +      return NULL;
 +}
 +
 +
 +static struct p2p_group_member * p2p_group_get_client_iface(
 +      struct p2p_group *group, const u8 *interface_addr)
 +{
 +      struct p2p_group_member *m;
 +
 +      for (m = group->members; m; m = m->next) {
 +              if (os_memcmp(interface_addr, m->addr, ETH_ALEN) == 0)
 +                      return m;
 +      }
 +
 +      return NULL;
 +}
 +
 +
 +const u8 * p2p_group_get_dev_addr(struct p2p_group *group, const u8 *addr)
 +{
 +      struct p2p_group_member *m;
 +
 +      if (group == NULL)
 +              return NULL;
 +      m = p2p_group_get_client_iface(group, addr);
 +      if (m && !is_zero_ether_addr(m->dev_addr))
 +              return m->dev_addr;
 +      return NULL;
 +}
 +
 +
 +static struct wpabuf * p2p_build_go_disc_req(void)
 +{
 +      struct wpabuf *buf;
 +
 +      buf = wpabuf_alloc(100);
 +      if (buf == NULL)
 +              return NULL;
 +
 +      p2p_buf_add_action_hdr(buf, P2P_GO_DISC_REQ, 0);
 +
 +      return buf;
 +}
 +
 +
 +int p2p_group_go_discover(struct p2p_group *group, const u8 *dev_id,
 +                        const u8 *searching_dev, int rx_freq)
 +{
 +      struct p2p_group_member *m;
 +      struct wpabuf *req;
 +      struct p2p_data *p2p = group->p2p;
 +      int freq;
 +
 +      m = p2p_group_get_client(group, dev_id);
 +      if (m == NULL || m->client_info == NULL) {
 +              p2p_dbg(group->p2p, "Requested client was not in this group "
 +                      MACSTR, MAC2STR(group->cfg->interface_addr));
 +              return -1;
 +      }
 +
 +      if (!(m->dev_capab & P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY)) {
 +              p2p_dbg(group->p2p, "Requested client does not support client discoverability");
 +              return -1;
 +      }
 +
 +      p2p_dbg(group->p2p, "Schedule GO Discoverability Request to be sent to "
 +              MACSTR, MAC2STR(dev_id));
 +
 +      req = p2p_build_go_disc_req();
 +      if (req == NULL)
 +              return -1;
 +
 +      /* TODO: Should really use group operating frequency here */
 +      freq = rx_freq;
 +
 +      p2p->pending_action_state = P2P_PENDING_GO_DISC_REQ;
 +      if (p2p->cfg->send_action(p2p->cfg->cb_ctx, freq, m->addr,
 +                                group->cfg->interface_addr,
 +                                group->cfg->interface_addr,
 +                                wpabuf_head(req), wpabuf_len(req), 200) < 0)
 +      {
 +              p2p_dbg(p2p, "Failed to send Action frame");
 +      }
 +
 +      wpabuf_free(req);
 +
 +      return 0;
 +}
 +
 +
 +const u8 * p2p_group_get_interface_addr(struct p2p_group *group)
 +{
 +      return group->cfg->interface_addr;
 +}
 +
 +
 +u8 p2p_group_presence_req(struct p2p_group *group,
 +                        const u8 *client_interface_addr,
 +                        const u8 *noa, size_t noa_len)
 +{
 +      struct p2p_group_member *m;
 +      u8 curr_noa[50];
 +      int curr_noa_len;
 +
 +      m = p2p_group_get_client_iface(group, client_interface_addr);
 +      if (m == NULL || m->client_info == NULL) {
 +              p2p_dbg(group->p2p, "Client was not in this group");
 +              return P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE;
 +      }
 +
 +      wpa_hexdump(MSG_DEBUG, "P2P: Presence Request NoA", noa, noa_len);
 +
 +      if (group->p2p->cfg->get_noa)
 +              curr_noa_len = group->p2p->cfg->get_noa(
 +                      group->p2p->cfg->cb_ctx, group->cfg->interface_addr,
 +                      curr_noa, sizeof(curr_noa));
 +      else
 +              curr_noa_len = -1;
 +      if (curr_noa_len < 0)
 +              p2p_dbg(group->p2p, "Failed to fetch current NoA");
 +      else if (curr_noa_len == 0)
 +              p2p_dbg(group->p2p, "No NoA being advertized");
 +      else
 +              wpa_hexdump(MSG_DEBUG, "P2P: Current NoA", curr_noa,
 +                          curr_noa_len);
 +
 +      /* TODO: properly process request and store copy */
 +      if (curr_noa_len > 0 || curr_noa_len == -1)
 +              return P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE;
 +
 +      return P2P_SC_SUCCESS;
 +}
 +
 +
 +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;
 +
 +      if (!iter)
 +              iter = group->members;
 +      else
 +              iter = iter->next;
 +
 +      *next = iter;
 +
 +      if (!iter)
 +              return NULL;
 +
 +      return iter->dev_addr;
 +}
 +
 +
 +int p2p_group_is_client_connected(struct p2p_group *group, const u8 *dev_addr)
 +{
 +      struct p2p_group_member *m;
 +
 +      for (m = group->members; m; m = m->next) {
 +              if (os_memcmp(m->dev_addr, dev_addr, ETH_ALEN) == 0)
 +                      return 1;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +int p2p_group_is_group_id_match(struct p2p_group *group, const u8 *group_id,
 +                              size_t group_id_len)
 +{
 +      if (group_id_len != ETH_ALEN + group->cfg->ssid_len)
 +              return 0;
 +      if (os_memcmp(group_id, group->p2p->cfg->dev_addr, ETH_ALEN) != 0)
 +              return 0;
 +      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;
 +      }
 +}
++
++
++int p2p_group_get_common_freqs(struct p2p_group *group, int *common_freqs,
++                             unsigned int *num)
++
++{
++      struct p2p_channels intersect, res;
++      struct p2p_group_member *m;
++
++      if (!group || !common_freqs || !num)
++              return -1;
++
++      os_memset(&intersect, 0, sizeof(intersect));
++      os_memset(&res, 0, sizeof(res));
++
++      p2p_channels_union(&intersect, &group->p2p->cfg->channels,
++                         &intersect);
++
++      p2p_channels_dump(group->p2p,
++                        "Group common freqs before iterating members",
++                        &intersect);
++
++      for (m = group->members; m; m = m->next) {
++              struct p2p_device *dev;
++
++              dev = p2p_get_device(group->p2p, m->dev_addr);
++              if (!dev)
++                      continue;
++
++              p2p_channels_intersect(&intersect, &dev->channels, &res);
++              intersect = res;
++      }
++
++      p2p_channels_dump(group->p2p, "Group common channels", &intersect);
++
++      os_memset(common_freqs, 0, *num * sizeof(int));
++      *num = p2p_channels_to_freqs(&intersect, common_freqs, *num);
++
++      return 0;
++}
index 6af19ceda450ebe09062c203cf880ab14d7f7009,0000000000000000000000000000000000000000..0ce4058fe3e697b7fc87f27bd352fcde6be8f20e
mode 100644,000000..100644
--- /dev/null
@@@ -1,862 -1,0 +1,884 @@@
-       u8 oper_ssid[32];
 +/*
 + * P2P - Internal definitions for P2P module
 + * Copyright (c) 2009-2010, Atheros Communications
 + *
 + * This software may be distributed under the terms of the BSD license.
 + * See README for more details.
 + */
 +
 +#ifndef P2P_I_H
 +#define P2P_I_H
 +
 +#include "utils/list.h"
 +#include "p2p.h"
 +
 +#define P2P_GO_NEG_CNF_MAX_RETRY_COUNT 1
 +
++/*
++ * A threshold (in seconds) to prefer a direct Probe Response frame from a P2P
++ * Device over the P2P Client Info received from a GO.
++ */
++#define P2P_DEV_GROUP_CLIENT_RESP_THRESHOLD 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,
 +      REMOTE_GO
 +};
 +
 +/**
 + * struct p2p_device - P2P Device data (internal to P2P module)
 + */
 +struct p2p_device {
 +      struct dl_list list;
 +      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;
 +
 +      /*
 +       * If the peer was discovered based on an interface address (e.g., GO
 +       * from Beacon/Probe Response), the interface address is stored here.
 +       * p2p_device_addr must still be set in such a case to the unique
 +       * identifier for the P2P Device.
++       *
++       * This field is also used during P2PS PD to store the intended GO
++       * address of the peer.
 +       */
 +      u8 interface_addr[ETH_ALEN];
 +
 +      /*
 +       * P2P Device Address of the GO in whose group this P2P Device is a
 +       * client.
 +       */
 +      u8 member_in_go_dev[ETH_ALEN];
 +
 +      /*
 +       * P2P Interface Address of the GO in whose group this P2P Device is a
 +       * client.
 +       */
 +      u8 member_in_go_iface[ETH_ALEN];
 +
 +      int go_neg_req_sent;
 +      enum p2p_go_state go_state;
 +      u8 dialog_token;
 +      u8 tie_breaker;
 +      u8 intended_addr[ETH_ALEN];
 +
 +      char country[3];
 +      struct p2p_channels channels;
 +      int oper_freq;
-       u8 ssid[32];
++      u8 oper_ssid[SSID_MAX_LEN];
 +      size_t oper_ssid_len;
 +
 +      /**
 +       * req_config_methods - Pending provision discovery methods
 +       */
 +      u16 req_config_methods;
 +
 +      /**
 +       * wps_prov_info - Stored provisioning WPS config method
 +       *
 +       * This is used to store pending WPS config method between Provisioning
 +       * Discovery and connection to a running group.
 +       */
 +      u16 wps_prov_info;
 +
 +#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_PD_PEER_DISPLAY BIT(5)
 +#define P2P_DEV_PD_PEER_KEYPAD BIT(6)
 +#define P2P_DEV_USER_REJECTED BIT(7)
 +#define P2P_DEV_PEER_WAITING_RESPONSE BIT(8)
 +#define P2P_DEV_PREFER_PERSISTENT_GROUP BIT(9)
 +#define P2P_DEV_WAIT_GO_NEG_RESPONSE BIT(10)
 +#define P2P_DEV_WAIT_GO_NEG_CONFIRM BIT(11)
 +#define P2P_DEV_GROUP_CLIENT_ONLY BIT(12)
 +#define P2P_DEV_FORCE_FREQ BIT(13)
 +#define P2P_DEV_PD_FOR_JOIN BIT(14)
 +#define P2P_DEV_REPORTED_ONCE BIT(15)
 +#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)
++#define P2P_DEV_LAST_SEEN_AS_GROUP_CLIENT BIT(22)
++
 +      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 {
 +      struct p2p_sd_query *next;
 +      u8 peer[ETH_ALEN];
 +      int for_all_peers;
 +      int wsd; /* Wi-Fi Display Service Discovery Request */
 +      struct wpabuf *tlvs;
 +};
 +
 +struct p2p_pending_action_tx {
 +      unsigned int freq;
 +      u8 dst[ETH_ALEN];
 +      u8 src[ETH_ALEN];
 +      u8 bssid[ETH_ALEN];
 +      size_t len;
 +      unsigned int wait_time;
 +      /* Followed by len octets of the frame */
 +};
 +
 +/**
 + * struct p2p_data - P2P module data (internal to P2P module)
 + */
 +struct p2p_data {
 +      /**
 +       * cfg - P2P module configuration
 +       *
 +       * This is included in the same memory allocation with the
 +       * struct p2p_data and as such, must not be freed separately.
 +       */
 +      struct p2p_config *cfg;
 +
 +      /**
 +       * state - The current P2P state
 +       */
 +      enum p2p_state {
 +              /**
 +               * P2P_IDLE - Idle
 +               */
 +              P2P_IDLE,
 +
 +              /**
 +               * P2P_SEARCH - Search (Device Discovery)
 +               */
 +              P2P_SEARCH,
 +
 +              /**
 +               * P2P_CONNECT - Trying to start GO Negotiation
 +               */
 +              P2P_CONNECT,
 +
 +              /**
 +               * P2P_CONNECT_LISTEN - Listen during GO Negotiation start
 +               */
 +              P2P_CONNECT_LISTEN,
 +
 +              /**
 +               * P2P_GO_NEG - In GO Negotiation
 +               */
 +              P2P_GO_NEG,
 +
 +              /**
 +               * P2P_LISTEN_ONLY - Listen only
 +               */
 +              P2P_LISTEN_ONLY,
 +
 +              /**
 +               * P2P_WAIT_PEER_CONNECT - Waiting peer in List for GO Neg
 +               */
 +              P2P_WAIT_PEER_CONNECT,
 +
 +              /**
 +               * P2P_WAIT_PEER_IDLE - Waiting peer idle for GO Neg
 +               */
 +              P2P_WAIT_PEER_IDLE,
 +
 +              /**
 +               * P2P_SD_DURING_FIND - Service Discovery during find
 +               */
 +              P2P_SD_DURING_FIND,
 +
 +              /**
 +               * P2P_PROVISIONING - Provisioning (during group formation)
 +               */
 +              P2P_PROVISIONING,
 +
 +              /**
 +               * P2P_PD_DURING_FIND - Provision Discovery during find
 +               */
 +              P2P_PD_DURING_FIND,
 +
 +              /**
 +               * P2P_INVITE - Trying to start Invite
 +               */
 +              P2P_INVITE,
 +
 +              /**
 +               * P2P_INVITE_LISTEN - Listen during Invite
 +               */
 +              P2P_INVITE_LISTEN,
 +      } state;
 +
 +      /**
 +       * min_disc_int - minDiscoverableInterval
 +       */
 +      int min_disc_int;
 +
 +      /**
 +       * max_disc_int - maxDiscoverableInterval
 +       */
 +      int max_disc_int;
 +
 +      /**
 +       * max_disc_tu - Maximum number of TUs for discoverable interval
 +       */
 +      int max_disc_tu;
 +
 +      /**
 +       * devices - List of known P2P Device peers
 +       */
 +      struct dl_list devices;
 +
 +      /**
 +       * go_neg_peer - Pointer to GO Negotiation peer
 +       */
 +      struct p2p_device *go_neg_peer;
 +
 +      /**
 +       * invite_peer - Pointer to Invite peer
 +       */
 +      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
 +       */
 +      struct p2p_device *sd_peer;
 +
 +      /**
 +       * sd_query - Pointer to Service Discovery query
 +       */
 +      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 */
 +
 +      /**
 +       * intended_addr - Local Intended P2P Interface Address
 +       *
 +       * This address is used during group owner negotiation as the Intended
 +       * P2P Interface Address and the group interface will be created with
 +       * address as the local address in case of successfully completed
 +       * negotiation.
 +       */
 +      u8 intended_addr[ETH_ALEN];
 +
 +      /**
 +       * go_intent - Local GO Intent to be used during GO Negotiation
 +       */
 +      u8 go_intent;
 +
 +      /**
 +       * next_tie_breaker - Next tie-breaker value to use in GO Negotiation
 +       */
 +      u8 next_tie_breaker;
 +
 +      /**
 +       * ssid - Selected SSID for GO Negotiation (if local end will be GO)
 +       */
-       u8 inv_ssid[32];
++      u8 ssid[SSID_MAX_LEN];
 +
 +      /**
 +       * ssid_len - ssid length in octets
 +       */
 +      size_t ssid_len;
 +
 +      /**
 +       * ssid_set - Whether SSID is already set for GO Negotiation
 +       */
 +      int ssid_set;
 +
 +      /**
 +       * Regulatory class for own operational channel
 +       */
 +      u8 op_reg_class;
 +
 +      /**
 +       * op_channel - Own operational channel
 +       */
 +      u8 op_channel;
 +
 +      /**
 +       * channels - Own supported regulatory classes and channels
 +       *
 +       * List of supposerted channels per regulatory class. The regulatory
 +       * classes are defined in IEEE Std 802.11-2007 Annex J and the
 +       * numbering of the clases depends on the configured country code.
 +       */
 +      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,
 +              P2P_PENDING_GO_NEG_RESPONSE,
 +              P2P_PENDING_GO_NEG_RESPONSE_FAILURE,
 +              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,
 +              P2P_PENDING_DEV_DISC_RESPONSE,
 +              P2P_PENDING_GO_DISC_REQ
 +      } pending_action_state;
 +
 +      unsigned int pending_listen_freq;
 +      unsigned int pending_listen_sec;
 +      unsigned int pending_listen_usec;
 +
 +      u8 dev_capab;
 +
 +      int in_listen;
 +      int drv_in_listen;
 +
 +      /**
 +       * sd_queries - Pending service discovery queries
 +       */
 +      struct p2p_sd_query *sd_queries;
 +
 +      /**
 +       * srv_update_indic - Service Update Indicator for local services
 +       */
 +      u16 srv_update_indic;
 +
 +      struct wpabuf *sd_resp; /* Fragmented SD response */
 +      u8 sd_resp_addr[ETH_ALEN];
 +      u8 sd_resp_dialog_token;
 +      size_t sd_resp_pos; /* Offset in sd_resp */
 +      u8 sd_frag_id;
 +
 +      struct wpabuf *sd_rx_resp; /* Reassembled SD response */
 +      u16 sd_rx_update_indic;
 +
 +      /* P2P Invitation data */
 +      enum p2p_invite_role inv_role;
 +      u8 inv_bssid[ETH_ALEN];
 +      int inv_bssid_set;
-       u8 query_hash[P2P_MAX_QUERY_HASH * P2PS_HASH_LEN];
-       u8 query_count;
++      u8 inv_ssid[SSID_MAX_LEN];
 +      size_t inv_ssid_len;
 +      u8 inv_sa[ETH_ALEN];
 +      u8 inv_group_bssid[ETH_ALEN];
 +      u8 *inv_group_bssid_ptr;
 +      u8 inv_go_dev_addr[ETH_ALEN];
 +      u8 inv_status;
 +      int inv_op_freq;
 +      int inv_persistent;
 +
 +      enum p2p_discovery_type find_type;
 +      unsigned int last_p2p_find_timeout;
 +      u8 last_prog_scan_class;
 +      u8 last_prog_scan_chan;
 +      int p2p_scan_running;
 +      enum p2p_after_scan {
 +              P2P_AFTER_SCAN_NOTHING,
 +              P2P_AFTER_SCAN_LISTEN,
 +              P2P_AFTER_SCAN_CONNECT
 +      } start_after_scan;
 +      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;
 +      u8 *req_dev_types;
 +      u8 *find_dev_id;
 +      u8 find_dev_id_buf[ETH_ALEN];
 +
 +      struct os_reltime find_start; /* time of last p2p_find start */
 +
 +      struct p2p_group **groups;
 +      size_t num_groups;
 +
 +      struct p2p_device *pending_client_disc_go;
 +      u8 pending_client_disc_addr[ETH_ALEN];
 +      u8 pending_dev_disc_dialog_token;
 +      u8 pending_dev_disc_addr[ETH_ALEN];
 +      int pending_dev_disc_freq;
 +      unsigned int pending_client_disc_freq;
 +
 +      int ext_listen_only;
 +      unsigned int ext_listen_period;
 +      unsigned int ext_listen_interval;
 +      unsigned int ext_listen_interval_sec;
 +      unsigned int ext_listen_interval_usec;
 +
 +      u8 peer_filter[ETH_ALEN];
 +
 +      int cross_connect;
 +
 +      int best_freq_24;
 +      int best_freq_5;
 +      int best_freq_overall;
 +      int own_freq_preference;
 +
 +      /**
 +       * wps_vendor_ext - WPS Vendor Extensions to add
 +       */
 +      struct wpabuf *wps_vendor_ext[P2P_MAX_WPS_VENDOR_EXT];
 +
 +      /*
 +       * user_initiated_pd - Whether a PD request is user initiated or not.
 +       */
 +      u8 user_initiated_pd;
 +
 +      /*
 +       * Keep track of which peer a given PD request was sent to.
 +       * Used to raise a timeout alert in case there is no response.
 +       */
 +      u8 pending_pd_devaddr[ETH_ALEN];
 +
 +      /*
 +       * Retry counter for provision discovery requests when issued
 +       * in IDLE state.
 +       */
 +      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;
 +
 +      /* Extra delay in milliseconds between search iterations */
 +      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 p2ps_svc_found;
 +      u8 p2ps_seek;
++      u8 p2ps_seek_hash[P2P_MAX_QUERY_HASH * P2PS_HASH_LEN];
 +      u8 p2ps_seek_count;
-       char device_name[33];
 +
 +#ifdef CONFIG_WIFI_DISPLAY
 +      struct wpabuf *wfd_ie_beacon;
 +      struct wpabuf *wfd_ie_probe_req;
 +      struct wpabuf *wfd_ie_probe_resp;
 +      struct wpabuf *wfd_ie_assoc_req;
 +      struct wpabuf *wfd_ie_invitation;
 +      struct wpabuf *wfd_ie_prov_disc_req;
 +      struct wpabuf *wfd_ie_prov_disc_resp;
 +      struct wpabuf *wfd_ie_go_neg;
 +      struct wpabuf *wfd_dev_info;
 +      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;
++
++      unsigned int pref_freq_list[P2P_MAX_PREF_CHANNELS];
++      unsigned int num_pref_freq;
 +};
 +
 +/**
 + * struct p2p_message - Parsed P2P message (or P2P IE)
 + */
 +struct p2p_message {
 +      struct wpabuf *p2p_attributes;
 +      struct wpabuf *wps_attributes;
 +      struct wpabuf *wfd_subelems;
 +
 +      u8 dialog_token;
 +
 +      const u8 *capability;
 +      const u8 *go_intent;
 +      const u8 *status;
 +      const u8 *listen_channel;
 +      const u8 *operating_channel;
 +      const u8 *channel_list;
 +      u8 channel_list_len;
 +      const u8 *config_timeout;
 +      const u8 *intended_addr;
 +      const u8 *group_bssid;
 +      const u8 *invitation_flags;
 +
 +      const u8 *group_info;
 +      size_t group_info_len;
 +
 +      const u8 *group_id;
 +      size_t group_id_len;
 +
 +      const u8 *device_id;
 +
 +      const u8 *manageability;
 +
 +      const u8 *noa;
 +      size_t noa_len;
 +
 +      const u8 *ext_listen_timing;
 +
 +      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;
 +      const u8 *p2p_device_addr;
 +      const u8 *pri_dev_type;
 +      u8 num_sec_dev_types;
- struct wpabuf * p2p_build_probe_resp_ies(struct p2p_data *p2p);
++      char device_name[WPS_DEV_NAME_MAX_LEN + 1];
 +      u16 config_methods;
 +
 +      /* 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;
 +      size_t wps_sec_dev_type_list_len;
 +      const u8 *wps_vendor_ext[P2P_MAX_WPS_VENDOR_EXT];
 +      size_t wps_vendor_ext_len[P2P_MAX_WPS_VENDOR_EXT];
 +      const u8 *manufacturer;
 +      size_t manufacturer_len;
 +      const u8 *model_name;
 +      size_t model_name_len;
 +      const u8 *model_number;
 +      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;
++
++      const u8 *pref_freq_list;
++      size_t pref_freq_list_len;
 +};
 +
 +
 +#define P2P_MAX_GROUP_ENTRIES 50
 +
 +struct p2p_group_info {
 +      unsigned int num_clients;
 +      struct p2p_client_info {
 +              const u8 *p2p_device_addr;
 +              const u8 *p2p_interface_addr;
 +              u8 dev_capab;
 +              u16 config_methods;
 +              const u8 *pri_dev_type;
 +              u8 num_sec_dev_types;
 +              const u8 *sec_dev_types;
 +              const char *dev_name;
 +              size_t dev_name_len;
 +      } client[P2P_MAX_GROUP_ENTRIES];
 +};
 +
 +
 +/* p2p_utils.c */
 +int p2p_random(char *buf, size_t len);
 +int p2p_channel_to_freq(int op_class, int channel);
 +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,
 +                       struct p2p_group_info *info);
 +
 +/* p2p_build.c */
 +
 +struct p2p_noa_desc {
 +      u8 count_type;
 +      u32 duration;
 +      u32 interval;
 +      u32 start_time;
 +};
 +
 +/* p2p_group.c */
 +const u8 * p2p_group_get_interface_addr(struct p2p_group *group);
 +u8 p2p_group_presence_req(struct p2p_group *group,
 +                        const u8 *client_interface_addr,
 +                        const u8 *noa, size_t noa_len);
 +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);
 +void p2p_buf_add_public_action_hdr(struct wpabuf *buf, u8 subtype,
 +                                 u8 dialog_token);
 +u8 * p2p_buf_add_ie_hdr(struct wpabuf *buf);
 +void p2p_buf_add_status(struct wpabuf *buf, u8 status);
 +void p2p_buf_add_device_info(struct wpabuf *buf, struct p2p_data *p2p,
 +                           struct p2p_device *peer);
 +void p2p_buf_add_device_id(struct wpabuf *buf, const u8 *dev_addr);
 +void p2p_buf_update_ie_hdr(struct wpabuf *buf, u8 *len);
 +void p2p_buf_add_capability(struct wpabuf *buf, u8 dev_capab, u8 group_capab);
 +void p2p_buf_add_go_intent(struct wpabuf *buf, u8 go_intent);
 +void p2p_buf_add_listen_channel(struct wpabuf *buf, const char *country,
 +                              u8 reg_class, u8 channel);
 +void p2p_buf_add_operating_channel(struct wpabuf *buf, const char *country,
 +                                 u8 reg_class, u8 channel);
 +void p2p_buf_add_channel_list(struct wpabuf *buf, const char *country,
 +                            struct p2p_channels *chan);
 +void p2p_buf_add_config_timeout(struct wpabuf *buf, u8 go_timeout,
 +                              u8 client_timeout);
 +void p2p_buf_add_intended_addr(struct wpabuf *buf, const u8 *interface_addr);
 +void p2p_buf_add_group_bssid(struct wpabuf *buf, const u8 *bssid);
 +void p2p_buf_add_group_id(struct wpabuf *buf, const u8 *dev_addr,
 +                        const u8 *ssid, size_t ssid_len);
 +void p2p_buf_add_invitation_flags(struct wpabuf *buf, u8 flags);
 +void p2p_buf_add_noa(struct wpabuf *buf, u8 noa_index, u8 opp_ps, u8 ctwindow,
 +                   struct p2p_noa_desc *desc1, struct p2p_noa_desc *desc2);
 +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_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);
++void p2p_buf_add_pref_channel_list(struct wpabuf *buf,
++                                 const u32 *preferred_freq_list, u32 size);
 +
 +/* p2p_sd.c */
 +struct p2p_sd_query * p2p_pending_sd_req(struct p2p_data *p2p,
 +                                       struct p2p_device *dev);
 +void p2p_free_sd_queries(struct p2p_data *p2p);
 +void p2p_rx_gas_initial_req(struct p2p_data *p2p, const u8 *sa,
 +                          const u8 *data, size_t len, int rx_freq);
 +void p2p_rx_gas_initial_resp(struct p2p_data *p2p, const u8 *sa,
 +                           const u8 *data, size_t len, int rx_freq);
 +void p2p_rx_gas_comeback_req(struct p2p_data *p2p, const u8 *sa,
 +                           const u8 *data, size_t len, int rx_freq);
 +void p2p_rx_gas_comeback_resp(struct p2p_data *p2p, const u8 *sa,
 +                            const u8 *data, size_t len, int rx_freq);
 +int p2p_start_sd(struct p2p_data *p2p, struct p2p_device *dev);
 +
 +/* p2p_go_neg.c */
 +int p2p_peer_channels_check(struct p2p_data *p2p, struct p2p_channels *own,
 +                          struct p2p_device *dev,
 +                          const u8 *channel_list, size_t channel_list_len);
 +void p2p_process_go_neg_req(struct p2p_data *p2p, const u8 *sa,
 +                          const u8 *data, size_t len, int rx_freq);
 +void p2p_process_go_neg_resp(struct p2p_data *p2p, const u8 *sa,
 +                           const u8 *data, size_t len, int rx_freq);
 +void p2p_process_go_neg_conf(struct p2p_data *p2p, const u8 *sa,
 +                           const u8 *data, size_t len);
 +int p2p_connect_send(struct p2p_data *p2p, struct p2p_device *dev);
 +u16 p2p_wps_method_pw_id(enum p2p_wps_method wps_method);
 +void p2p_reselect_channel(struct p2p_data *p2p,
 +                        struct p2p_channels *intersection);
++void p2p_check_pref_chan(struct p2p_data *p2p, int go,
++                       struct p2p_device *dev, struct p2p_message *msg);
 +
 +/* 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);
 +void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa,
 +                              const u8 *data, size_t len);
 +int p2p_send_prov_disc_req(struct p2p_data *p2p, struct p2p_device *dev,
 +                         int join, int force_freq);
 +void p2p_reset_pending_pd(struct p2p_data *p2p);
++void p2ps_prov_free(struct p2p_data *p2p);
 +
 +/* p2p_invitation.c */
 +void p2p_process_invitation_req(struct p2p_data *p2p, const u8 *sa,
 +                              const u8 *data, size_t len, int rx_freq);
 +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, 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);
 +
 +/* p2p_dev_disc.c */
 +void p2p_process_dev_disc_req(struct p2p_data *p2p, const u8 *sa,
 +                            const u8 *data, size_t len, int rx_freq);
 +void p2p_dev_disc_req_cb(struct p2p_data *p2p, int success);
 +int p2p_send_dev_disc_req(struct p2p_data *p2p, struct p2p_device *dev);
 +void p2p_dev_disc_resp_cb(struct p2p_data *p2p, int success);
 +void p2p_process_dev_disc_resp(struct p2p_data *p2p, const u8 *sa,
 +                             const u8 *data, size_t len);
 +void p2p_go_disc_req_cb(struct p2p_data *p2p, int success);
 +void p2p_process_go_disc_req(struct p2p_data *p2p, const u8 *da, const u8 *sa,
 +                           const u8 *data, size_t len, int rx_freq);
 +
 +/* p2p.c */
 +void p2p_set_state(struct p2p_data *p2p, int new_state);
 +void p2p_set_timeout(struct p2p_data *p2p, unsigned int sec,
 +                   unsigned int usec);
 +void p2p_clear_timeout(struct p2p_data *p2p);
 +void p2p_continue_find(struct p2p_data *p2p);
 +struct p2p_device * p2p_add_dev_from_go_neg_req(struct p2p_data *p2p,
 +                                              const u8 *addr,
 +                                              struct p2p_message *msg);
 +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_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, 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[],
 +                      size_t num_req_dev_type);
++struct wpabuf * p2p_build_probe_resp_ies(struct p2p_data *p2p,
++                                       const u8 *query_hash,
++                                       u8 query_count);
 +void p2p_build_ssid(struct p2p_data *p2p, u8 *ssid, size_t *ssid_len);
 +int p2p_send_action(struct p2p_data *p2p, unsigned int freq, const u8 *dst,
 +                  const u8 *src, const u8 *bssid, const u8 *buf,
 +                  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,
 +                      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, ...)
 +PRINTF_FORMAT(2, 3);
 +void p2p_err(struct p2p_data *p2p, const char *fmt, ...)
 +PRINTF_FORMAT(2, 3);
 +
 +#endif /* P2P_I_H */
index 558c6dd0c58faf4a1319a6e59a0d4eb08f6b2ab5,0000000000000000000000000000000000000000..108e5b7f93e4d0c45d1dac89737b255aa133ed7c
mode 100644,000000..100644
--- /dev/null
@@@ -1,698 -1,0 +1,719 @@@
-               if (msg.group_id_len - ETH_ALEN <= 32) {
 +/*
 + * Wi-Fi Direct - P2P Invitation procedure
 + * Copyright (c) 2010, Atheros Communications
 + *
 + * 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/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,
 +                                              int dev_pw_id)
 +{
 +      struct wpabuf *buf;
 +      u8 *len;
 +      const u8 *dev_addr;
 +      size_t extra = 0;
 +
 +#ifdef CONFIG_WIFI_DISPLAY
 +      struct wpabuf *wfd_ie = p2p->wfd_ie_invitation;
 +      if (wfd_ie && p2p->inv_role == P2P_INVITE_ROLE_ACTIVE_GO) {
 +              size_t i;
 +              for (i = 0; i < p2p->num_groups; i++) {
 +                      struct p2p_group *g = p2p->groups[i];
 +                      struct wpabuf *ie;
 +                      if (os_memcmp(p2p_group_get_interface_addr(g),
 +                                    p2p->inv_bssid, ETH_ALEN) != 0)
 +                              continue;
 +                      ie = p2p_group_get_wfd_ie(g);
 +                      if (ie) {
 +                              wfd_ie = ie;
 +                              break;
 +                      }
 +              }
 +      }
 +      if (wfd_ie)
 +              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;
 +
 +      peer->dialog_token++;
 +      if (peer->dialog_token == 0)
 +              peer->dialog_token = 1;
 +      p2p_buf_add_public_action_hdr(buf, P2P_INVITATION_REQ,
 +                                    peer->dialog_token);
 +
 +      len = p2p_buf_add_ie_hdr(buf);
 +      if (p2p->inv_role == P2P_INVITE_ROLE_ACTIVE_GO || !p2p->inv_persistent)
 +              p2p_buf_add_config_timeout(buf, 0, 0);
 +      else
 +              p2p_buf_add_config_timeout(buf, p2p->go_timeout,
 +                                         p2p->client_timeout);
 +      p2p_buf_add_invitation_flags(buf, p2p->inv_persistent ?
 +                                   P2P_INVITATION_FLAGS_TYPE : 0);
 +      if (p2p->inv_role != P2P_INVITE_ROLE_CLIENT ||
 +          !(peer->flags & P2P_DEV_NO_PREF_CHAN))
 +              p2p_buf_add_operating_channel(buf, p2p->cfg->country,
 +                                            p2p->op_reg_class,
 +                                            p2p->op_channel);
 +      if (p2p->inv_bssid_set)
 +              p2p_buf_add_group_bssid(buf, p2p->inv_bssid);
 +      p2p_buf_add_channel_list(buf, p2p->cfg->country, &p2p->channels);
 +      if (go_dev_addr)
 +              dev_addr = go_dev_addr;
 +      else if (p2p->inv_role == P2P_INVITE_ROLE_CLIENT)
 +              dev_addr = peer->info.p2p_device_addr;
 +      else
 +              dev_addr = p2p->cfg->dev_addr;
 +      p2p_buf_add_group_id(buf, dev_addr, p2p->inv_ssid, p2p->inv_ssid_len);
 +      p2p_buf_add_device_info(buf, p2p, peer);
 +      p2p_buf_update_ie_hdr(buf, len);
 +
++      p2p_buf_add_pref_channel_list(buf, p2p->pref_freq_list,
++                                    p2p->num_pref_freq);
++
 +#ifdef CONFIG_WIFI_DISPLAY
 +      if (wfd_ie)
 +              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;
 +}
 +
 +
 +static struct wpabuf * p2p_build_invitation_resp(struct p2p_data *p2p,
 +                                               struct p2p_device *peer,
 +                                               u8 dialog_token, u8 status,
 +                                               const u8 *group_bssid,
 +                                               u8 reg_class, u8 channel,
 +                                               struct p2p_channels *channels)
 +{
 +      struct wpabuf *buf;
 +      u8 *len;
 +      size_t extra = 0;
 +
 +#ifdef CONFIG_WIFI_DISPLAY
 +      struct wpabuf *wfd_ie = p2p->wfd_ie_invitation;
 +      if (wfd_ie && group_bssid) {
 +              size_t i;
 +              for (i = 0; i < p2p->num_groups; i++) {
 +                      struct p2p_group *g = p2p->groups[i];
 +                      struct wpabuf *ie;
 +                      if (os_memcmp(p2p_group_get_interface_addr(g),
 +                                    group_bssid, ETH_ALEN) != 0)
 +                              continue;
 +                      ie = p2p_group_get_wfd_ie(g);
 +                      if (ie) {
 +                              wfd_ie = ie;
 +                              break;
 +                      }
 +              }
 +      }
 +      if (wfd_ie)
 +              extra = wpabuf_len(wfd_ie);
 +#endif /* CONFIG_WIFI_DISPLAY */
 +
++      if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_INV_RESP])
++              extra += wpabuf_len(p2p->vendor_elem[VENDOR_ELEM_P2P_INV_RESP]);
++
 +      buf = wpabuf_alloc(1000 + extra);
 +      if (buf == NULL)
 +              return NULL;
 +
 +      p2p_buf_add_public_action_hdr(buf, P2P_INVITATION_RESP,
 +                                    dialog_token);
 +
 +      len = p2p_buf_add_ie_hdr(buf);
 +      p2p_buf_add_status(buf, status);
 +      p2p_buf_add_config_timeout(buf, 0, 0); /* FIX */
 +      if (reg_class && channel)
 +              p2p_buf_add_operating_channel(buf, p2p->cfg->country,
 +                                            reg_class, channel);
 +      if (group_bssid)
 +              p2p_buf_add_group_bssid(buf, group_bssid);
 +      if (channels)
 +              p2p_buf_add_channel_list(buf, p2p->cfg->country, channels);
 +      p2p_buf_update_ie_hdr(buf, len);
 +
 +#ifdef CONFIG_WIFI_DISPLAY
 +      if (wfd_ie)
 +              wpabuf_put_buf(buf, wfd_ie);
 +#endif /* CONFIG_WIFI_DISPLAY */
 +
++      if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_INV_RESP])
++              wpabuf_put_buf(buf, p2p->vendor_elem[VENDOR_ELEM_P2P_INV_RESP]);
++
 +      return buf;
 +}
 +
 +
 +void p2p_process_invitation_req(struct p2p_data *p2p, const u8 *sa,
 +                              const u8 *data, size_t len, int rx_freq)
 +{
 +      struct p2p_device *dev;
 +      struct p2p_message msg;
 +      struct wpabuf *resp = NULL;
 +      u8 status = P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE;
 +      int freq;
 +      int go = 0;
 +      u8 group_bssid[ETH_ALEN], *bssid;
 +      int op_freq = 0;
 +      u8 reg_class = 0, channel = 0;
 +      struct p2p_channels all_channels, intersection, *channels = NULL;
 +      int persistent;
 +
 +      os_memset(group_bssid, 0, sizeof(group_bssid));
 +
 +      p2p_dbg(p2p, "Received Invitation Request from " MACSTR " (freq=%d)",
 +              MAC2STR(sa), rx_freq);
 +
 +      if (p2p_parse(data, len, &msg))
 +              return;
 +
 +      dev = p2p_get_device(p2p, sa);
 +      if (dev == NULL || (dev->flags & P2P_DEV_PROBE_REQ_ONLY)) {
 +              p2p_dbg(p2p, "Invitation Request from unknown peer " MACSTR,
 +                      MAC2STR(sa));
 +
 +              if (p2p_add_device(p2p, sa, rx_freq, NULL, 0, data + 1, len - 1,
 +                                 0)) {
 +                      p2p_dbg(p2p, "Invitation Request add device failed "
 +                              MACSTR, MAC2STR(sa));
 +                      status = P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE;
 +                      goto fail;
 +              }
 +
 +              dev = p2p_get_device(p2p, sa);
 +              if (dev == NULL) {
 +                      p2p_dbg(p2p, "Reject Invitation Request from unknown peer "
 +                              MACSTR, MAC2STR(sa));
 +                      status = P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE;
 +                      goto fail;
 +              }
 +      }
 +
 +      if (!msg.group_id || !msg.channel_list) {
 +              p2p_dbg(p2p, "Mandatory attribute missing in Invitation Request from "
 +                      MACSTR, MAC2STR(sa));
 +              status = P2P_SC_FAIL_INVALID_PARAMS;
 +              goto fail;
 +      }
 +
 +      if (msg.invitation_flags)
 +              persistent = *msg.invitation_flags & P2P_INVITATION_FLAGS_TYPE;
 +      else {
 +              /* Invitation Flags is a mandatory attribute starting from P2P
 +               * spec 1.06. As a backwards compatibility mechanism, assume
 +               * the request was for a persistent group if the attribute is
 +               * missing.
 +               */
 +              p2p_dbg(p2p, "Mandatory Invitation Flags attribute missing from Invitation Request");
 +              persistent = 1;
 +      }
 +
 +      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");
 +              status = P2P_SC_FAIL_NO_COMMON_CHANNELS;
 +              goto fail;
 +      }
 +
 +      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,
 +                      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) {
 +              p2p_dbg(p2p, "Invitation processing forced frequency %d MHz",
 +                      op_freq);
 +              if (p2p_freq_to_channel(op_freq, &reg_class, &channel) < 0) {
 +                      p2p_dbg(p2p, "Unknown forced freq %d MHz from invitation_process()",
 +                              op_freq);
 +                      status = P2P_SC_FAIL_NO_COMMON_CHANNELS;
 +                      goto fail;
 +              }
 +
 +              if (!p2p_channels_includes(&intersection, reg_class, channel))
 +              {
 +                      p2p_dbg(p2p, "forced freq %d MHz not in the supported channels interaction",
 +                              op_freq);
 +                      status = P2P_SC_FAIL_NO_COMMON_CHANNELS;
 +                      goto fail;
 +              }
 +
 +              if (status == P2P_SC_SUCCESS)
 +                      channels = &intersection;
 +      } else {
 +              p2p_dbg(p2p, "No forced channel from invitation processing - figure out best one to use");
 +
 +              /* Default to own configuration as a starting point */
 +              p2p->op_reg_class = p2p->cfg->op_reg_class;
 +              p2p->op_channel = p2p->cfg->op_channel;
 +              p2p_dbg(p2p, "Own default op_class %d channel %d",
 +                      p2p->op_reg_class, p2p->op_channel);
 +
 +              /* Use peer preference if specified and compatible */
 +              if (msg.operating_channel) {
 +                      int req_freq;
 +                      req_freq = p2p_channel_to_freq(
 +                              msg.operating_channel[3],
 +                              msg.operating_channel[4]);
 +                      p2p_dbg(p2p, "Peer operating channel preference: %d MHz",
 +                              req_freq);
 +                      if (req_freq > 0 &&
 +                          p2p_channels_includes(&intersection,
 +                                                msg.operating_channel[3],
 +                                                msg.operating_channel[4])) {
 +                              p2p->op_reg_class = msg.operating_channel[3];
 +                              p2p->op_channel = msg.operating_channel[4];
 +                              p2p_dbg(p2p, "Use peer preference op_class %d channel %d",
 +                                      p2p->op_reg_class, p2p->op_channel);
 +                      } else {
 +                              p2p_dbg(p2p, "Cannot use peer channel preference");
 +                      }
 +              }
 +
 +              /* 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);
 +                      p2p_reselect_channel(p2p, &intersection);
 +                      p2p_dbg(p2p, "Re-selection result: op_class %d channel %d",
 +                              p2p->op_reg_class, p2p->op_channel);
 +                      if (!p2p_channels_includes(&intersection,
 +                                                 p2p->op_reg_class,
 +                                                 p2p->op_channel)) {
 +                              p2p_dbg(p2p, "Peer does not support selected operating channel (reg_class=%u channel=%u)",
 +                                      p2p->op_reg_class, p2p->op_channel);
 +                              status = P2P_SC_FAIL_NO_COMMON_CHANNELS;
 +                              goto fail;
 +                      }
 +              } 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);
 +                      p2p_reselect_channel(p2p, &intersection);
 +              }
 +
++              /*
++               * Use the driver preferred frequency list extension if
++               * supported.
++               */
++              p2p_check_pref_chan(p2p, go, dev, &msg);
++
 +              op_freq = p2p_channel_to_freq(p2p->op_reg_class,
 +                                            p2p->op_channel);
 +              if (op_freq < 0) {
 +                      p2p_dbg(p2p, "Unknown operational channel (country=%c%c reg_class=%u channel=%u)",
 +                              p2p->cfg->country[0], p2p->cfg->country[1],
 +                              p2p->op_reg_class, p2p->op_channel);
 +                      status = P2P_SC_FAIL_NO_COMMON_CHANNELS;
 +                      goto fail;
 +              }
 +              p2p_dbg(p2p, "Selected operating channel - %d MHz", op_freq);
 +
 +              if (status == P2P_SC_SUCCESS) {
 +                      reg_class = p2p->op_reg_class;
 +                      channel = p2p->op_channel;
 +                      channels = &intersection;
 +              }
 +      }
 +
 +fail:
 +      if (go && status == P2P_SC_SUCCESS && !is_zero_ether_addr(group_bssid))
 +              bssid = group_bssid;
 +      else
 +              bssid = NULL;
 +      resp = p2p_build_invitation_resp(p2p, dev, msg.dialog_token, status,
 +                                       bssid, reg_class, channel, channels);
 +
 +      if (resp == NULL)
 +              goto out;
 +
 +      if (rx_freq > 0)
 +              freq = rx_freq;
 +      else
 +              freq = p2p_channel_to_freq(p2p->cfg->reg_class,
 +                                         p2p->cfg->channel);
 +      if (freq < 0) {
 +              p2p_dbg(p2p, "Unknown regulatory class/channel");
 +              goto out;
 +      }
 +
 +      /*
 +       * Store copy of invitation data to be used when processing TX status
 +       * callback for the Acton frame.
 +       */
 +      os_memcpy(p2p->inv_sa, sa, ETH_ALEN);
 +      if (msg.group_bssid) {
 +              os_memcpy(p2p->inv_group_bssid, msg.group_bssid, ETH_ALEN);
 +              p2p->inv_group_bssid_ptr = p2p->inv_group_bssid;
 +      } else
 +              p2p->inv_group_bssid_ptr = NULL;
 +      if (msg.group_id) {
++              if (msg.group_id_len - ETH_ALEN <= SSID_MAX_LEN) {
 +                      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);
 +      }
 +      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, p2p->cfg->dev_addr,
 +                          p2p->cfg->dev_addr,
 +                          wpabuf_head(resp), wpabuf_len(resp), 200) < 0) {
 +              p2p_dbg(p2p, "Failed to send Action frame");
 +      }
 +
 +out:
 +      wpabuf_free(resp);
 +      p2p_parse_free(&msg);
 +}
 +
 +
 +void p2p_process_invitation_resp(struct p2p_data *p2p, const u8 *sa,
 +                               const u8 *data, size_t len)
 +{
 +      struct p2p_device *dev;
 +      struct p2p_message msg;
 +      struct p2p_channels intersection, *channels = NULL;
 +
 +      p2p_dbg(p2p, "Received Invitation Response from " MACSTR,
 +              MAC2STR(sa));
 +
 +      dev = p2p_get_device(p2p, 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)) {
 +              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;
 +      }
 +
 +      /*
 +       * 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
 +              p2p_parse_free(&msg);
 +              return;
 +#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) {
 +              p2p_dbg(p2p, "No common channels found");
 +              p2p_parse_free(&msg);
 +              return;
 +      } else {
 +              p2p_channels_intersect(&p2p->channels, &dev->channels,
 +                                     &intersection);
 +              channels = &intersection;
 +      }
 +
 +      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;
 +              }
 +
++              /*
++               * Use the driver preferred frequency list extension if
++               * supported.
++               */
++              p2p_check_pref_chan(p2p, 0, dev, &msg);
++
 +              p2p->cfg->invitation_result(p2p->cfg->cb_ctx, *msg.status,
 +                                          msg.group_bssid, channels, sa,
 +                                          freq, peer_oper_freq);
 +      }
 +
 +      p2p_parse_free(&msg);
 +
 +      p2p_clear_timeout(p2p);
 +      p2p_set_state(p2p, P2P_IDLE);
 +      p2p->invite_peer = NULL;
 +}
 +
 +
 +int p2p_invite_send(struct p2p_data *p2p, struct p2p_device *dev,
 +                  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",
 +                      MAC2STR(dev->info.p2p_device_addr));
 +              return -1;
 +      }
 +
 +      req = p2p_build_invitation_req(p2p, dev, go_dev_addr, dev_pw_id);
 +      if (req == NULL)
 +              return -1;
 +      if (p2p->state != P2P_IDLE)
 +              p2p_stop_listen_for_freq(p2p, freq);
 +      p2p_dbg(p2p, "Sending Invitation Request");
 +      p2p_set_state(p2p, P2P_INVITE);
 +      p2p->pending_action_state = P2P_PENDING_INVITATION_REQUEST;
 +      p2p->invite_peer = dev;
 +      dev->invitation_reqs++;
 +      if (p2p_send_action(p2p, freq, dev->info.p2p_device_addr,
 +                          p2p->cfg->dev_addr, dev->info.p2p_device_addr,
 +                          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);
 +
 +      return 0;
 +}
 +
 +
 +void p2p_invitation_req_cb(struct p2p_data *p2p, int success)
 +{
 +      p2p_dbg(p2p, "Invitation Request TX callback: success=%d", success);
 +
 +      if (p2p->invite_peer == NULL) {
 +              p2p_dbg(p2p, "No pending Invite");
 +              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 ? 500000 : 100000);
 +}
 +
 +
 +void p2p_invitation_resp_cb(struct p2p_data *p2p, int success)
 +{
 +      p2p_dbg(p2p, "Invitation Response TX callback: success=%d", success);
 +      p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
 +
 +      if (!success)
 +              p2p_dbg(p2p, "Assume Invitation Response was actually received by the peer even though Ack was not reported");
 +
 +      if (p2p->cfg->invitation_received) {
 +              p2p->cfg->invitation_received(p2p->cfg->cb_ctx,
 +                                            p2p->inv_sa,
 +                                            p2p->inv_group_bssid_ptr,
 +                                            p2p->inv_ssid, p2p->inv_ssid_len,
 +                                            p2p->inv_go_dev_addr,
 +                                            p2p->inv_status,
 +                                            p2p->inv_op_freq);
 +      }
 +}
 +
 +
 +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 dev_pw_id)
 +{
 +      struct p2p_device *dev;
 +
 +      p2p_dbg(p2p, "Request to invite peer " MACSTR " role=%d persistent=%d "
 +              "force_freq=%u",
 +              MAC2STR(peer), role, persistent_group, force_freq);
 +      if (bssid)
 +              p2p_dbg(p2p, "Invitation for BSSID " MACSTR, MAC2STR(bssid));
 +      if (go_dev_addr) {
 +              p2p_dbg(p2p, "Invitation for GO Device Address " MACSTR,
 +                      MAC2STR(go_dev_addr));
 +              os_memcpy(p2p->invite_go_dev_addr_buf, go_dev_addr, ETH_ALEN);
 +              p2p->invite_go_dev_addr = p2p->invite_go_dev_addr_buf;
 +      } else
 +              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 &&
 +                          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,
 +                              role != P2P_INVITE_ROLE_CLIENT) < 0)
 +              return -1;
 +
 +      if (persistent_group && role == P2P_INVITE_ROLE_CLIENT && !force_freq &&
 +          !pref_freq)
 +              dev->flags |= P2P_DEV_NO_PREF_CHAN;
 +      else
 +              dev->flags &= ~P2P_DEV_NO_PREF_CHAN;
 +
 +      if (dev->flags & P2P_DEV_GROUP_CLIENT_ONLY) {
 +              if (!(dev->info.dev_capab &
 +                    P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY)) {
 +                      p2p_dbg(p2p, "Cannot invite a P2P Device " MACSTR
 +                              " that is in a group and is not discoverable",
 +                              MAC2STR(peer));
 +              }
 +              /* TODO: use device discoverability request through GO */
 +      }
 +
 +      dev->invitation_reqs = 0;
 +
 +      if (p2p->state != P2P_IDLE)
 +              p2p_stop_find(p2p);
 +
 +      p2p->inv_role = role;
 +      p2p->inv_bssid_set = bssid != NULL;
 +      if (bssid)
 +              os_memcpy(p2p->inv_bssid, bssid, ETH_ALEN);
 +      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, dev_pw_id);
 +}
index fd6a4610d839fabd7adda95b3fe0b02dea30ce61,0000000000000000000000000000000000000000..bd1e68bd424171a87423f38340024ab3db884259
mode 100644,000000..100644
--- /dev/null
@@@ -1,877 -1,0 +1,880 @@@
-               if (data + len - pos < (int) nlen || nlen > 32) {
 +/*
 + * P2P - IE parser
 + * 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 "common.h"
 +#include "common/ieee802_11_defs.h"
 +#include "common/ieee802_11_common.h"
 +#include "wps/wps_i.h"
 +#include "p2p_i.h"
 +
 +
 +static int p2p_parse_attribute(u8 id, const u8 *data, u16 len,
 +                             struct p2p_message *msg)
 +{
 +      const u8 *pos;
 +      size_t i, nlen;
 +      char devtype[WPS_DEV_TYPE_BUFSIZE];
 +
 +      switch (id) {
 +      case P2P_ATTR_CAPABILITY:
 +              if (len < 2) {
 +                      wpa_printf(MSG_DEBUG, "P2P: Too short Capability "
 +                                 "attribute (length %d)", len);
 +                      return -1;
 +              }
 +              msg->capability = data;
 +              wpa_printf(MSG_DEBUG, "P2P: * Device Capability %02x "
 +                         "Group Capability %02x",
 +                         data[0], data[1]);
 +              break;
 +      case P2P_ATTR_DEVICE_ID:
 +              if (len < ETH_ALEN) {
 +                      wpa_printf(MSG_DEBUG, "P2P: Too short Device ID "
 +                                 "attribute (length %d)", len);
 +                      return -1;
 +              }
 +              msg->device_id = data;
 +              wpa_printf(MSG_DEBUG, "P2P: * Device ID " MACSTR,
 +                         MAC2STR(msg->device_id));
 +              break;
 +      case P2P_ATTR_GROUP_OWNER_INTENT:
 +              if (len < 1) {
 +                      wpa_printf(MSG_DEBUG, "P2P: Too short GO Intent "
 +                                 "attribute (length %d)", len);
 +                      return -1;
 +              }
 +              msg->go_intent = data;
 +              wpa_printf(MSG_DEBUG, "P2P: * GO Intent: Intent %u "
 +                         "Tie breaker %u", data[0] >> 1, data[0] & 0x01);
 +              break;
 +      case P2P_ATTR_STATUS:
 +              if (len < 1) {
 +                      wpa_printf(MSG_DEBUG, "P2P: Too short Status "
 +                                 "attribute (length %d)", len);
 +                      return -1;
 +              }
 +              msg->status = data;
 +              wpa_printf(MSG_DEBUG, "P2P: * Status: %d", data[0]);
 +              break;
 +      case P2P_ATTR_LISTEN_CHANNEL:
 +              if (len == 0) {
 +                      wpa_printf(MSG_DEBUG, "P2P: * Listen Channel: Ignore "
 +                                 "null channel");
 +                      break;
 +              }
 +              if (len < 5) {
 +                      wpa_printf(MSG_DEBUG, "P2P: Too short Listen Channel "
 +                                 "attribute (length %d)", len);
 +                      return -1;
 +              }
 +              msg->listen_channel = data;
 +              wpa_printf(MSG_DEBUG, "P2P: * Listen Channel: "
 +                         "Country %c%c(0x%02x) Regulatory "
 +                         "Class %d Channel Number %d", data[0], data[1],
 +                         data[2], data[3], data[4]);
 +              break;
 +      case P2P_ATTR_OPERATING_CHANNEL:
 +              if (len == 0) {
 +                      wpa_printf(MSG_DEBUG, "P2P: * Operating Channel: "
 +                                 "Ignore null channel");
 +                      break;
 +              }
 +              if (len < 5) {
 +                      wpa_printf(MSG_DEBUG, "P2P: Too short Operating "
 +                                 "Channel attribute (length %d)", len);
 +                      return -1;
 +              }
 +              msg->operating_channel = data;
 +              wpa_printf(MSG_DEBUG, "P2P: * Operating Channel: "
 +                         "Country %c%c(0x%02x) Regulatory "
 +                         "Class %d Channel Number %d", data[0], data[1],
 +                         data[2], data[3], data[4]);
 +              break;
 +      case P2P_ATTR_CHANNEL_LIST:
 +              if (len < 3) {
 +                      wpa_printf(MSG_DEBUG, "P2P: Too short Channel List "
 +                                 "attribute (length %d)", len);
 +                      return -1;
 +              }
 +              msg->channel_list = data;
 +              msg->channel_list_len = len;
 +              wpa_printf(MSG_DEBUG, "P2P: * Channel List: Country String "
 +                         "'%c%c(0x%02x)'", data[0], data[1], data[2]);
 +              wpa_hexdump(MSG_MSGDUMP, "P2P: Channel List",
 +                          msg->channel_list, msg->channel_list_len);
 +              break;
 +      case P2P_ATTR_GROUP_INFO:
 +              msg->group_info = data;
 +              msg->group_info_len = len;
 +              wpa_printf(MSG_DEBUG, "P2P: * Group Info");
 +              break;
 +      case P2P_ATTR_DEVICE_INFO:
 +              if (len < ETH_ALEN + 2 + 8 + 1) {
 +                      wpa_printf(MSG_DEBUG, "P2P: Too short Device Info "
 +                                 "attribute (length %d)", len);
 +                      return -1;
 +              }
 +              msg->p2p_device_info = data;
 +              msg->p2p_device_info_len = len;
 +              pos = data;
 +              msg->p2p_device_addr = pos;
 +              pos += ETH_ALEN;
 +              msg->config_methods = WPA_GET_BE16(pos);
 +              pos += 2;
 +              msg->pri_dev_type = pos;
 +              pos += 8;
 +              msg->num_sec_dev_types = *pos++;
 +              if (msg->num_sec_dev_types * 8 > data + len - pos) {
 +                      wpa_printf(MSG_DEBUG, "P2P: Device Info underflow");
 +                      return -1;
 +              }
 +              pos += msg->num_sec_dev_types * 8;
 +              if (data + len - pos < 4) {
 +                      wpa_printf(MSG_DEBUG, "P2P: Invalid Device Name "
 +                                 "length %d", (int) (data + len - pos));
 +                      return -1;
 +              }
 +              if (WPA_GET_BE16(pos) != ATTR_DEV_NAME) {
 +                      wpa_hexdump(MSG_DEBUG, "P2P: Unexpected Device Name "
 +                                  "header", pos, 4);
 +                      return -1;
 +              }
 +              pos += 2;
 +              nlen = WPA_GET_BE16(pos);
 +              pos += 2;
-                       if (msg->device_name[i] > 0 &&
-                           msg->device_name[i] < 32)
++              if (data + len - pos < (int) nlen ||
++                  nlen > WPS_DEV_NAME_MAX_LEN) {
 +                      wpa_printf(MSG_DEBUG, "P2P: Invalid Device Name "
 +                                 "length %d (buf len %d)", (int) nlen,
 +                                 (int) (data + len - pos));
 +                      return -1;
 +              }
 +              os_memcpy(msg->device_name, pos, nlen);
 +              msg->device_name[nlen] = '\0';
 +              for (i = 0; i < nlen; i++) {
 +                      if (msg->device_name[i] == '\0')
 +                              break;
-               if (len < ETH_ALEN || len > ETH_ALEN + 32) {
++                      if (is_ctrl_char(msg->device_name[i]))
 +                              msg->device_name[i] = '_';
 +              }
 +              wpa_printf(MSG_DEBUG, "P2P: * Device Info: addr " MACSTR
 +                         " primary device type %s device name '%s' "
 +                         "config methods 0x%x",
 +                         MAC2STR(msg->p2p_device_addr),
 +                         wps_dev_type_bin2str(msg->pri_dev_type, devtype,
 +                                              sizeof(devtype)),
 +                         msg->device_name, msg->config_methods);
 +              break;
 +      case P2P_ATTR_CONFIGURATION_TIMEOUT:
 +              if (len < 2) {
 +                      wpa_printf(MSG_DEBUG, "P2P: Too short Configuration "
 +                                 "Timeout attribute (length %d)", len);
 +                      return -1;
 +              }
 +              msg->config_timeout = data;
 +              wpa_printf(MSG_DEBUG, "P2P: * Configuration Timeout");
 +              break;
 +      case P2P_ATTR_INTENDED_INTERFACE_ADDR:
 +              if (len < ETH_ALEN) {
 +                      wpa_printf(MSG_DEBUG, "P2P: Too short Intended P2P "
 +                                 "Interface Address attribute (length %d)",
 +                                 len);
 +                      return -1;
 +              }
 +              msg->intended_addr = data;
 +              wpa_printf(MSG_DEBUG, "P2P: * Intended P2P Interface Address: "
 +                         MACSTR, MAC2STR(msg->intended_addr));
 +              break;
 +      case P2P_ATTR_GROUP_BSSID:
 +              if (len < ETH_ALEN) {
 +                      wpa_printf(MSG_DEBUG, "P2P: Too short P2P Group BSSID "
 +                                 "attribute (length %d)", len);
 +                      return -1;
 +              }
 +              msg->group_bssid = data;
 +              wpa_printf(MSG_DEBUG, "P2P: * P2P Group BSSID: " MACSTR,
 +                         MAC2STR(msg->group_bssid));
 +              break;
 +      case P2P_ATTR_GROUP_ID:
-               if (len < ETH_ALEN) {
++              if (len < ETH_ALEN || len > ETH_ALEN + SSID_MAX_LEN) {
 +                      wpa_printf(MSG_DEBUG, "P2P: Invalid P2P Group ID "
 +                                 "attribute length %d", len);
 +                      return -1;
 +              }
 +              msg->group_id = data;
 +              msg->group_id_len = len;
 +              wpa_printf(MSG_DEBUG, "P2P: * P2P Group ID: Device Address "
 +                         MACSTR, MAC2STR(msg->group_id));
 +              wpa_hexdump_ascii(MSG_DEBUG, "P2P: * P2P Group ID: SSID",
 +                                msg->group_id + ETH_ALEN,
 +                                msg->group_id_len - ETH_ALEN);
 +              break;
 +      case P2P_ATTR_INVITATION_FLAGS:
 +              if (len < 1) {
 +                      wpa_printf(MSG_DEBUG, "P2P: Too short Invitation "
 +                                 "Flag attribute (length %d)", len);
 +                      return -1;
 +              }
 +              msg->invitation_flags = data;
 +              wpa_printf(MSG_DEBUG, "P2P: * Invitation Flags: bitmap 0x%x",
 +                         data[0]);
 +              break;
 +      case P2P_ATTR_MANAGEABILITY:
 +              if (len < 1) {
 +                      wpa_printf(MSG_DEBUG, "P2P: Too short Manageability "
 +                                 "attribute (length %d)", len);
 +                      return -1;
 +              }
 +              msg->manageability = data;
 +              wpa_printf(MSG_DEBUG, "P2P: * Manageability: bitmap 0x%x",
 +                         data[0]);
 +              break;
 +      case P2P_ATTR_NOTICE_OF_ABSENCE:
 +              if (len < 2) {
 +                      wpa_printf(MSG_DEBUG, "P2P: Too short Notice of "
 +                                 "Absence attribute (length %d)", len);
 +                      return -1;
 +              }
 +              msg->noa = data;
 +              msg->noa_len = len;
 +              wpa_printf(MSG_DEBUG, "P2P: * Notice of Absence");
 +              break;
 +      case P2P_ATTR_EXT_LISTEN_TIMING:
 +              if (len < 4) {
 +                      wpa_printf(MSG_DEBUG, "P2P: Too short Extended Listen "
 +                                 "Timing attribute (length %d)", len);
 +                      return -1;
 +              }
 +              msg->ext_listen_timing = data;
 +              wpa_printf(MSG_DEBUG, "P2P: * Extended Listen Timing "
 +                         "(period %u msec  interval %u msec)",
 +                         WPA_GET_LE16(msg->ext_listen_timing),
 +                         WPA_GET_LE16(msg->ext_listen_timing + 2));
 +              break;
 +      case P2P_ATTR_MINOR_REASON_CODE:
 +              if (len < 1) {
 +                      wpa_printf(MSG_DEBUG, "P2P: Too short Minor Reason "
 +                                 "Code attribute (length %d)", len);
 +                      return -1;
 +              }
 +              msg->minor_reason_code = data;
 +              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:
 +      {
-                                  "P2P: Too short Persistent Group Info (length %u)",
++              if (len < ETH_ALEN || len > ETH_ALEN + SSID_MAX_LEN) {
 +                      wpa_printf(MSG_DEBUG,
-       if (elems.ds_params && elems.ds_params_len >= 1)
++                                 "P2P: Invalid 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);
 +              break;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +/**
 + * p2p_parse_p2p_ie - Parse P2P IE
 + * @buf: Concatenated P2P IE(s) payload
 + * @msg: Buffer for returning parsed attributes
 + * Returns: 0 on success, -1 on failure
 + *
 + * Note: Caller is responsible for clearing the msg data structure before
 + * calling this function.
 + */
 +int p2p_parse_p2p_ie(const struct wpabuf *buf, struct p2p_message *msg)
 +{
 +      const u8 *pos = wpabuf_head_u8(buf);
 +      const u8 *end = pos + wpabuf_len(buf);
 +
 +      wpa_printf(MSG_DEBUG, "P2P: Parsing P2P IE");
 +
 +      while (pos < end) {
 +              u16 attr_len;
 +              u8 id;
 +
 +              if (end - pos < 3) {
 +                      wpa_printf(MSG_DEBUG, "P2P: Invalid P2P attribute");
 +                      return -1;
 +              }
 +              id = *pos++;
 +              attr_len = WPA_GET_LE16(pos);
 +              pos += 2;
 +              wpa_printf(MSG_DEBUG, "P2P: Attribute %d length %u",
 +                         id, attr_len);
 +              if (attr_len > end - pos) {
 +                      wpa_printf(MSG_DEBUG, "P2P: Attribute underflow "
 +                                 "(len=%u left=%d)",
 +                                 attr_len, (int) (end - pos));
 +                      wpa_hexdump(MSG_MSGDUMP, "P2P: Data", pos, end - pos);
 +                      return -1;
 +              }
 +              if (p2p_parse_attribute(id, pos, attr_len, msg))
 +                      return -1;
 +              pos += attr_len;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +static int p2p_parse_wps_ie(const struct wpabuf *buf, struct p2p_message *msg)
 +{
 +      struct wps_parse_attr attr;
 +      int i;
 +
 +      wpa_printf(MSG_DEBUG, "P2P: Parsing WPS IE");
 +      if (wps_parse_msg(buf, &attr))
 +              return -1;
 +      if (attr.dev_name && attr.dev_name_len < sizeof(msg->device_name) &&
 +          !msg->device_name[0])
 +              os_memcpy(msg->device_name, attr.dev_name, attr.dev_name_len);
 +      if (attr.config_methods) {
 +              msg->wps_config_methods =
 +                      WPA_GET_BE16(attr.config_methods);
 +              wpa_printf(MSG_DEBUG, "P2P: Config Methods (WPS): 0x%x",
 +                         msg->wps_config_methods);
 +      }
 +      if (attr.dev_password_id) {
 +              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];
 +              msg->wps_pri_dev_type = attr.primary_dev_type;
 +              wpa_printf(MSG_DEBUG, "P2P: Primary Device Type (WPS): %s",
 +                         wps_dev_type_bin2str(msg->wps_pri_dev_type, devtype,
 +                                              sizeof(devtype)));
 +      }
 +      if (attr.sec_dev_type_list) {
 +              msg->wps_sec_dev_type_list = attr.sec_dev_type_list;
 +              msg->wps_sec_dev_type_list_len = attr.sec_dev_type_list_len;
 +      }
 +
 +      for (i = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) {
 +              msg->wps_vendor_ext[i] = attr.vendor_ext[i];
 +              msg->wps_vendor_ext_len[i] = attr.vendor_ext_len[i];
 +      }
 +
 +      msg->manufacturer = attr.manufacturer;
 +      msg->manufacturer_len = attr.manufacturer_len;
 +      msg->model_name = attr.model_name;
 +      msg->model_name_len = attr.model_name_len;
 +      msg->model_number = attr.model_number;
 +      msg->model_number_len = attr.model_number_len;
 +      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;
 +}
 +
 +
 +/**
 + * p2p_parse_ies - Parse P2P message IEs (both WPS and P2P IE)
 + * @data: IEs from the message
 + * @len: Length of data buffer in octets
 + * @msg: Buffer for returning parsed attributes
 + * Returns: 0 on success, -1 on failure
 + *
 + * Note: Caller is responsible for clearing the msg data structure before
 + * calling this function.
 + *
 + * Note: Caller must free temporary memory allocations by calling
 + * p2p_parse_free() when the parsed data is not needed anymore.
 + */
 +int p2p_parse_ies(const u8 *data, size_t len, struct p2p_message *msg)
 +{
 +      struct ieee802_11_elems elems;
 +
 +      ieee802_11_parse_elems(data, len, &elems, 0);
-               if (count >= 32)
-                       count = 32;
++      if (elems.ds_params)
 +              msg->ds_params = elems.ds_params;
 +      if (elems.ssid)
 +              msg->ssid = elems.ssid - 2;
 +
 +      msg->wps_attributes = ieee802_11_vendor_ie_concat(data, len,
 +                                                        WPS_DEV_OUI_WFA);
 +      if (msg->wps_attributes &&
 +          p2p_parse_wps_ie(msg->wps_attributes, msg)) {
 +              p2p_parse_free(msg);
 +              return -1;
 +      }
 +
 +      msg->p2p_attributes = ieee802_11_vendor_ie_concat(data, len,
 +                                                        P2P_IE_VENDOR_TYPE);
 +      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;
 +      }
 +
 +#ifdef CONFIG_WIFI_DISPLAY
 +      if (elems.wfd) {
 +              msg->wfd_subelems = ieee802_11_vendor_ie_concat(
 +                      data, len, WFD_IE_VENDOR_TYPE);
 +      }
 +#endif /* CONFIG_WIFI_DISPLAY */
 +
++      msg->pref_freq_list = elems.pref_freq_list;
++      msg->pref_freq_list_len = elems.pref_freq_list_len;
++
 +      return 0;
 +}
 +
 +
 +/**
 + * p2p_parse - Parse a P2P Action frame contents
 + * @data: Action frame payload after Category and Code fields
 + * @len: Length of data buffer in octets
 + * @msg: Buffer for returning parsed attributes
 + * Returns: 0 on success, -1 on failure
 + *
 + * Note: Caller must free temporary memory allocations by calling
 + * p2p_parse_free() when the parsed data is not needed anymore.
 + */
 +int p2p_parse(const u8 *data, size_t len, struct p2p_message *msg)
 +{
 +      os_memset(msg, 0, sizeof(*msg));
 +      wpa_printf(MSG_DEBUG, "P2P: Parsing the received message");
 +      if (len < 1) {
 +              wpa_printf(MSG_DEBUG, "P2P: No Dialog Token in the message");
 +              return -1;
 +      }
 +      msg->dialog_token = data[0];
 +      wpa_printf(MSG_DEBUG, "P2P: * Dialog Token: %d", msg->dialog_token);
 +
 +      return p2p_parse_ies(data + 1, len - 1, 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
 + */
 +void p2p_parse_free(struct p2p_message *msg)
 +{
 +      wpabuf_free(msg->p2p_attributes);
 +      msg->p2p_attributes = NULL;
 +      wpabuf_free(msg->wps_attributes);
 +      msg->wps_attributes = NULL;
 +#ifdef CONFIG_WIFI_DISPLAY
 +      wpabuf_free(msg->wfd_subelems);
 +      msg->wfd_subelems = NULL;
 +#endif /* CONFIG_WIFI_DISPLAY */
 +}
 +
 +
 +int p2p_group_info_parse(const u8 *gi, size_t gi_len,
 +                       struct p2p_group_info *info)
 +{
 +      const u8 *g, *gend;
 +
 +      os_memset(info, 0, sizeof(*info));
 +      if (gi == NULL)
 +              return 0;
 +
 +      g = gi;
 +      gend = gi + gi_len;
 +      while (g < gend) {
 +              struct p2p_client_info *cli;
 +              const u8 *t, *cend;
 +              int count;
 +
 +              cli = &info->client[info->num_clients];
 +              cend = g + 1 + g[0];
 +              if (cend > gend)
 +                      return -1; /* invalid data */
 +              /* g at start of P2P Client Info Descriptor */
 +              /* t at Device Capability Bitmap */
 +              t = g + 1 + 2 * ETH_ALEN;
 +              if (t > cend)
 +                      return -1; /* invalid data */
 +              cli->p2p_device_addr = g + 1;
 +              cli->p2p_interface_addr = g + 1 + ETH_ALEN;
 +              cli->dev_capab = t[0];
 +
 +              if (t + 1 + 2 + 8 + 1 > cend)
 +                      return -1; /* invalid data */
 +
 +              cli->config_methods = WPA_GET_BE16(&t[1]);
 +              cli->pri_dev_type = &t[3];
 +
 +              t += 1 + 2 + 8;
 +              /* t at Number of Secondary Device Types */
 +              cli->num_sec_dev_types = *t++;
 +              if (t + 8 * cli->num_sec_dev_types > cend)
 +                      return -1; /* invalid data */
 +              cli->sec_dev_types = t;
 +              t += 8 * cli->num_sec_dev_types;
 +
 +              /* t at Device Name in WPS TLV format */
 +              if (t + 2 + 2 > cend)
 +                      return -1; /* invalid data */
 +              if (WPA_GET_BE16(t) != ATTR_DEV_NAME)
 +                      return -1; /* invalid Device Name TLV */
 +              t += 2;
 +              count = WPA_GET_BE16(t);
 +              t += 2;
 +              if (count > cend - t)
 +                      return -1; /* invalid Device Name TLV */
-               char name[33];
++              if (count >= WPS_DEV_NAME_MAX_LEN)
++                      count = WPS_DEV_NAME_MAX_LEN;
 +              cli->dev_name = (const char *) t;
 +              cli->dev_name_len = count;
 +
 +              g = cend;
 +
 +              info->num_clients++;
 +              if (info->num_clients == P2P_MAX_GROUP_ENTRIES)
 +                      return -1;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +static int p2p_group_info_text(const u8 *gi, size_t gi_len, char *buf,
 +                             char *end)
 +{
 +      char *pos = buf;
 +      int ret;
 +      struct p2p_group_info info;
 +      unsigned int i;
 +
 +      if (p2p_group_info_parse(gi, gi_len, &info) < 0)
 +              return 0;
 +
 +      for (i = 0; i < info.num_clients; i++) {
 +              struct p2p_client_info *cli;
-                       if (name[count] > 0 && name[count] < 32)
++              char name[WPS_DEV_NAME_MAX_LEN + 1];
 +              char devtype[WPS_DEV_TYPE_BUFSIZE];
 +              u8 s;
 +              int count;
 +
 +              cli = &info.client[i];
 +              ret = os_snprintf(pos, end - pos, "p2p_group_client: "
 +                                "dev=" MACSTR " iface=" MACSTR,
 +                                MAC2STR(cli->p2p_device_addr),
 +                                MAC2STR(cli->p2p_interface_addr));
 +              if (os_snprintf_error(end - pos, ret))
 +                      return pos - buf;
 +              pos += ret;
 +
 +              ret = os_snprintf(pos, end - pos,
 +                                " dev_capab=0x%x config_methods=0x%x "
 +                                "dev_type=%s",
 +                                cli->dev_capab, cli->config_methods,
 +                                wps_dev_type_bin2str(cli->pri_dev_type,
 +                                                     devtype,
 +                                                     sizeof(devtype)));
 +              if (os_snprintf_error(end - pos, ret))
 +                      return pos - buf;
 +              pos += ret;
 +
 +              for (s = 0; s < cli->num_sec_dev_types; s++) {
 +                      ret = os_snprintf(pos, end - pos, " dev_type=%s",
 +                                        wps_dev_type_bin2str(
 +                                                &cli->sec_dev_types[s * 8],
 +                                                devtype, sizeof(devtype)));
 +                      if (os_snprintf_error(end - pos, ret))
 +                              return pos - buf;
 +                      pos += ret;
 +              }
 +
 +              os_memcpy(name, cli->dev_name, cli->dev_name_len);
 +              name[cli->dev_name_len] = '\0';
 +              count = (int) cli->dev_name_len - 1;
 +              while (count >= 0) {
++                      if (is_ctrl_char(name[count]))
 +                              name[count] = '_';
 +                      count--;
 +              }
 +
 +              ret = os_snprintf(pos, end - pos, " dev_name='%s'\n", name);
 +              if (os_snprintf_error(end - pos, ret))
 +                      return pos - buf;
 +              pos += ret;
 +      }
 +
 +      return pos - buf;
 +}
 +
 +
 +/**
 + * p2p_attr_text - Build text format description of P2P IE attributes
 + * @data: P2P IE contents
 + * @buf: Buffer for returning text
 + * @end: Pointer to the end of the buf area
 + * Returns: Number of octets written to the buffer or -1 on faikure
 + *
 + * This function can be used to parse P2P IE contents into text format
 + * field=value lines.
 + */
 +int p2p_attr_text(struct wpabuf *data, char *buf, char *end)
 +{
 +      struct p2p_message msg;
 +      char *pos = buf;
 +      int ret;
 +
 +      os_memset(&msg, 0, sizeof(msg));
 +      if (p2p_parse_p2p_ie(data, &msg))
 +              return -1;
 +
 +      if (msg.capability) {
 +              ret = os_snprintf(pos, end - pos,
 +                                "p2p_dev_capab=0x%x\n"
 +                                "p2p_group_capab=0x%x\n",
 +                                msg.capability[0], msg.capability[1]);
 +              if (os_snprintf_error(end - pos, ret))
 +                      return pos - buf;
 +              pos += ret;
 +      }
 +
 +      if (msg.pri_dev_type) {
 +              char devtype[WPS_DEV_TYPE_BUFSIZE];
 +              ret = os_snprintf(pos, end - pos,
 +                                "p2p_primary_device_type=%s\n",
 +                                wps_dev_type_bin2str(msg.pri_dev_type,
 +                                                     devtype,
 +                                                     sizeof(devtype)));
 +              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 (os_snprintf_error(end - pos, ret))
 +              return pos - buf;
 +      pos += ret;
 +
 +      if (msg.p2p_device_addr) {
 +              ret = os_snprintf(pos, end - pos, "p2p_device_addr=" MACSTR
 +                                "\n",
 +                                MAC2STR(msg.p2p_device_addr));
 +              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 (os_snprintf_error(end - pos, ret))
 +              return pos - buf;
 +      pos += ret;
 +
 +      ret = p2p_group_info_text(msg.group_info, msg.group_info_len,
 +                                pos, end);
 +      if (ret < 0)
 +              return pos - buf;
 +      pos += ret;
 +
 +      return pos - buf;
 +}
 +
 +
 +int p2p_get_cross_connect_disallowed(const struct wpabuf *p2p_ie)
 +{
 +      struct p2p_message msg;
 +
 +      os_memset(&msg, 0, sizeof(msg));
 +      if (p2p_parse_p2p_ie(p2p_ie, &msg))
 +              return 0;
 +
 +      if (!msg.manageability)
 +              return 0;
 +
 +      return !(msg.manageability[0] & P2P_MAN_CROSS_CONNECTION_PERMITTED);
 +}
 +
 +
 +u8 p2p_get_group_capab(const struct wpabuf *p2p_ie)
 +{
 +      struct p2p_message msg;
 +
 +      os_memset(&msg, 0, sizeof(msg));
 +      if (p2p_parse_p2p_ie(p2p_ie, &msg))
 +              return 0;
 +
 +      if (!msg.capability)
 +              return 0;
 +
 +      return msg.capability[1];
 +}
 +
 +
 +const u8 * p2p_get_go_dev_addr(const struct wpabuf *p2p_ie)
 +{
 +      struct p2p_message msg;
 +
 +      os_memset(&msg, 0, sizeof(msg));
 +      if (p2p_parse_p2p_ie(p2p_ie, &msg))
 +              return NULL;
 +
 +      if (msg.p2p_device_addr)
 +              return msg.p2p_device_addr;
 +      if (msg.device_id)
 +              return msg.device_id;
 +
 +      return NULL;
 +}
index 328b1e029ce577fcc88974ad250cd349885d487d,0000000000000000000000000000000000000000..890094551821a53ffef394a5be76c6e3aee27622
mode 100644,000000..100644
--- /dev/null
@@@ -1,1178 -1,0 +1,1440 @@@
-       u8 ssid[32];
 +/*
 + * Wi-Fi Direct - P2P provision discovery
 + * 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 "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"
 +
 +
 +/*
 + * Number of retries to attempt for provision discovery requests
 + * in case the peer is not listening.
 + */
 +#define MAX_PROV_DISC_REQ_RETRIES 120
 +
 +
 +static void p2p_build_wps_ie_config_methods(struct wpabuf *buf,
 +                                          u16 config_methods)
 +{
 +      u8 *len;
 +      wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
 +      len = wpabuf_put(buf, 1);
 +      wpabuf_put_be32(buf, WPS_DEV_OUI_WFA);
 +
 +      /* Config Methods */
 +      wpabuf_put_be16(buf, ATTR_CONFIG_METHODS);
 +      wpabuf_put_be16(buf, 2);
 +      wpabuf_put_be16(buf, config_methods);
 +
 +      p2p_buf_update_ie_hdr(buf, len);
 +}
 +
 +
 +static void p2ps_add_new_group_info(struct p2p_data *p2p, struct wpabuf *buf)
 +{
 +      int found;
 +      u8 intended_addr[ETH_ALEN];
-               p2p_buf_add_intended_addr(buf, intended_addr);
++      u8 ssid[SSID_MAX_LEN];
 +      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);
-       u8 feat_cap_mask[] = { 1, 0 };
++
++              if (group_iface)
++                      p2p_buf_add_intended_addr(buf, p2p->intended_addr);
++              else
++                      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 ssid[32];
++      struct p2ps_feature_capab fcap = { prov->cpt_mask, 0 };
 +      int shared_group = 0;
-                       go_dev_addr, ssid, &ssid_len);
++      u8 ssid[SSID_MAX_LEN];
 +      size_t ssid_len;
 +      u8 go_dev_addr[ETH_ALEN];
++      u8 intended_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,
-       p2p_buf_add_feature_capability(buf, sizeof(feat_cap_mask),
-                                      feat_cap_mask);
++                      go_dev_addr, ssid, &ssid_len, intended_addr);
 +      }
 +
 +      /* 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);
 +
-       if (shared_group)
++      p2p_buf_add_feature_capability(buf, sizeof(fcap), (const u8 *) &fcap);
 +
-                                               size_t persist_ssid_len)
++      if (shared_group) {
 +              p2p_buf_add_persistent_group_info(buf, go_dev_addr,
 +                                                ssid, ssid_len);
++              /* Add intended interface address if it is not added yet */
++              if ((prov->conncap == P2PS_SETUP_NONE ||
++                   prov->conncap == P2PS_SETUP_CLIENT) &&
++                  !is_zero_ether_addr(intended_addr))
++                      p2p_buf_add_intended_addr(buf, intended_addr);
++      }
 +}
 +
 +
 +static struct wpabuf * p2p_build_prov_disc_req(struct p2p_data *p2p,
 +                                             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;
 +
 +      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,
 +                             group_capab);
 +      p2p_buf_add_device_info(buf, p2p, NULL);
 +      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);
 +      }
 +      p2p_buf_update_ie_hdr(buf, len);
 +
 +      /* WPS IE with Config Methods attribute */
 +      p2p_build_wps_ie_config_methods(buf, config_methods);
 +
 +#ifdef CONFIG_WIFI_DISPLAY
 +      if (p2p->wfd_ie_prov_disc_req)
 +              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,
 +                                              const u8 *persist_ssid,
-               u8 feat_cap_mask[] = { 1, 0 };
++                                              size_t persist_ssid_len,
++                                              const u8 *fcap,
++                                              u16 fcap_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;
 +      if (wfd_ie && group_id) {
 +              size_t i;
 +              for (i = 0; i < p2p->num_groups; i++) {
 +                      struct p2p_group *g = p2p->groups[i];
 +                      struct wpabuf *ie;
 +                      if (!p2p_group_is_group_id_match(g, group_id,
 +                                                       group_id_len))
 +                              continue;
 +                      ie = p2p_group_get_wfd_ie(g);
 +                      if (ie) {
 +                              wfd_ie = ie;
 +                              break;
 +                      }
 +              }
 +      }
 +      if (wfd_ie)
 +              extra = wpabuf_len(wfd_ie);
 +#endif /* CONFIG_WIFI_DISPLAY */
 +
 +      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 ssid[32];
 +              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)) {
-                               ssid, &ssid_len);
-                       if (persist)
++                      u8 ssid[SSID_MAX_LEN];
 +                      size_t ssid_len;
 +                      u8 go_dev_addr[ETH_ALEN];
++                      u8 intended_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,
-               p2p_buf_add_feature_capability(buf, sizeof(feat_cap_mask),
-                                              feat_cap_mask);
++                              ssid, &ssid_len, intended_addr);
++                      if (persist) {
 +                              p2p_buf_add_persistent_group_info(
 +                                      buf, go_dev_addr, ssid, ssid_len);
++                              if (!is_zero_ether_addr(intended_addr))
++                                      p2p_buf_add_intended_addr(
++                                              buf, intended_addr);
++                      }
 +              }
 +
 +              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);
 +
-       u8 group_mac[ETH_ALEN];
++              p2p_buf_add_feature_capability(buf, fcap_len, fcap);
 +              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);
 +
 +#ifdef CONFIG_WIFI_DISPLAY
 +      if (wfd_ie)
 +              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;
 +}
 +
 +
++static u8 p2ps_own_preferred_cpt(const u8 *cpt_priority, u8 req_cpt_mask)
++{
++      int i;
++
++      for (i = 0; cpt_priority[i]; i++)
++              if (req_cpt_mask & cpt_priority[i])
++                      return cpt_priority[i];
++
++      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;
 +      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];
-       if (!(msg.wps_config_methods &
-             (WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD |
-              WPS_CONFIG_PUSHBUTTON | WPS_CONFIG_P2PS))) {
++      const u8 *group_mac;
 +      int passwd_id = DEV_PW_DEFAULT;
 +      u16 config_methods;
++      u16 allowed_config_methods = WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD;
++      struct p2ps_feature_capab resp_fcap = { 0, 0 };
++      struct p2ps_feature_capab *req_fcap;
 +
 +      if (p2p_parse(data, len, &msg))
 +              return;
 +
 +      p2p_dbg(p2p, "Received Provision Discovery Request from " MACSTR
 +              " with config methods 0x%x (freq=%d)",
 +              MAC2STR(sa), msg.wps_config_methods, rx_freq);
++      group_mac = msg.intended_addr;
 +
 +      dev = p2p_get_device(p2p, sa);
 +      if (dev == NULL || (dev->flags & P2P_DEV_PROBE_REQ_ONLY)) {
 +              p2p_dbg(p2p, "Provision Discovery Request from unknown peer "
 +                      MACSTR, MAC2STR(sa));
 +
 +              if (p2p_add_device(p2p, sa, rx_freq, NULL, 0, data + 1, len - 1,
 +                                 0)) {
 +                      p2p_dbg(p2p, "Provision Discovery Request add device failed "
 +                              MACSTR, MAC2STR(sa));
 +              }
 +      } else if (msg.wfd_subelems) {
 +              wpabuf_free(dev->info.wfd_subelems);
 +              dev->info.wfd_subelems = wpabuf_dup(msg.wfd_subelems);
 +      }
 +
-       os_memset(group_mac, 0, ETH_ALEN);
++      if (msg.adv_id)
++              allowed_config_methods |= WPS_CONFIG_P2PS;
++      else
++              allowed_config_methods |= WPS_CONFIG_PUSHBUTTON;
++
++      if (!(msg.wps_config_methods & allowed_config_methods)) {
 +              p2p_dbg(p2p, "Unsupported Config Methods in Provision Discovery Request");
 +              goto out;
 +      }
 +
 +      /* 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],
 +                                                      msg.group_id,
 +                                                      msg.group_id_len))
 +                              break;
 +              }
 +              if (i == p2p->num_groups) {
 +                      p2p_dbg(p2p, "PD request for unknown P2P Group ID - reject");
 +                      goto out;
 +              }
 +      }
 +
 +      if (dev) {
 +              dev->flags &= ~(P2P_DEV_PD_PEER_DISPLAY |
 +                              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 = P2P_SC_SUCCESS;
 +
 +      os_memset(session_mac, 0, ETH_ALEN);
 +      os_memset(adv_mac, 0, ETH_ALEN);
-               if (msg.intended_addr)
-                       os_memcpy(group_mac, msg.intended_addr, ETH_ALEN);
 +
++      /* Note 1: A feature capability attribute structure can be changed
++       * in the future. The assumption is that such modifications are
++       * backwards compatible, therefore we allow processing of
++       * msg.feature_cap exceeding the size of the p2ps_feature_capab
++       * structure.
++       * Note 2: Vverification of msg.feature_cap_len below has to be changed
++       * to allow 2 byte feature capability processing if struct
++       * p2ps_feature_capab is extended to include additional fields and it
++       * affects the structure size.
++       */
 +      if (msg.adv_id && msg.session_id && msg.session_mac && msg.adv_mac &&
++          msg.feature_cap && msg.feature_cap_len >= sizeof(*req_fcap) &&
 +          (msg.status || msg.conn_cap)) {
 +              u8 remote_conncap;
 +
-                       if (p2ps_adv->config_methods &&
-                           !(msg.wps_config_methods &
-                             p2ps_adv->config_methods)) {
++              req_fcap = (struct p2ps_feature_capab *) msg.feature_cap;
 +
 +              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);
 +
-                               /* Reject this "Deferred Accept* if incompatible
-                                * conncap or method */
++                      resp_fcap.cpt =
++                              p2ps_own_preferred_cpt(p2ps_adv->cpt_priority,
++                                                     req_fcap->cpt);
++
++                      p2p_dbg(p2p,
++                              "cpt: service:0x%x remote:0x%x result:0x%x",
++                              p2ps_adv->cpt_mask, req_fcap->cpt,
++                              resp_fcap.cpt);
++
++                      if (!resp_fcap.cpt) {
++                              p2p_dbg(p2p,
++                                      "Incompatible P2PS feature capability CPT bitmask");
++                              reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
++                      } else 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);
 +
++                              resp_fcap.cpt = p2ps_own_preferred_cpt(
++                                      p2p->p2ps_prov->cpt_priority,
++                                      req_fcap->cpt);
++
++                              p2p_dbg(p2p,
++                                      "cpt: local:0x%x remote:0x%x result:0x%x",
++                                      p2p->p2ps_prov->cpt_mask,
++                                      req_fcap->cpt, resp_fcap.cpt);
++
 +                              /*
 +                               * 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;
 +
-                                   !(msg.wps_config_methods & method))
 +                              if (!conncap ||
-                               else
++                                  !(msg.wps_config_methods & method)) {
++                                      /*
++                                       * Reject this "Deferred Accept*
++                                       * if incompatible conncap or method
++                                       */
++                                      reject =
++                                              P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
++                              } else if (!resp_fcap.cpt) {
++                                      p2p_dbg(p2p,
++                                              "Incompatible P2PS feature capability CPT bitmask");
 +                                      reject =
 +                                              P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
-                                       msg.persistent_ssid_len);
++                              } else {
 +                                      reject = P2P_SC_SUCCESS;
++                              }
 +
 +                              p2p->p2ps_prov->status = reject;
 +                              p2p->p2ps_prov->conncap = conncap;
 +                      }
 +              }
 +      }
 +
 +out:
 +      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,
-                                            0, 0, NULL);
++                                      msg.persistent_ssid_len,
++                                      (const u8 *) &resp_fcap,
++                                      sizeof(resp_fcap));
 +      if (resp == NULL) {
 +              p2p_parse_free(&msg);
 +              return;
 +      }
 +      p2p_dbg(p2p, "Sending Provision Discovery Response");
 +      if (rx_freq > 0)
 +              freq = rx_freq;
 +      else
 +              freq = p2p_channel_to_freq(p2p->cfg->reg_class,
 +                                         p2p->cfg->channel);
 +      if (freq < 0) {
 +              p2p_dbg(p2p, "Unknown regulatory class/channel");
 +              wpabuf_free(resp);
 +              p2p_parse_free(&msg);
 +              return;
 +      }
 +      p2p->pending_action_state = P2P_PENDING_PD_RESPONSE;
 +      if (p2p_send_action(p2p, freq, sa, p2p->cfg->dev_addr,
 +                          p2p->cfg->dev_addr,
 +                          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 (!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, NULL);
++                                           0, 0, NULL, NULL, 0);
 +      } 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);
++                                                   0, NULL, NULL, 0);
 +              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, 0, NULL);
++                                                   0, NULL,
++                                                   (const u8 *) &resp_fcap,
++                                                   sizeof(resp_fcap));
 +      } 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);
++                                           0, 0, NULL,
++                                           (const u8 *) &resp_fcap,
++                                           sizeof(resp_fcap));
 +      } 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, 1, NULL);
++                                           0, 0, NULL,
++                                           (const u8 *) &resp_fcap,
++                                           sizeof(resp_fcap));
 +      } 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, buf);
++                                           0, 1, NULL,
++                                           (const u8 *) &resp_fcap,
++                                           sizeof(resp_fcap));
 +      } 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,
-       if (reject == P2P_SC_SUCCESS && p2p->cfg->prov_disc_req) {
++                              0, 1, buf,
++                              (const u8 *) &resp_fcap, sizeof(resp_fcap));
 +
 +                      os_free(buf);
 +              }
 +      }
 +
-       int success = 0;
++      /*
++       * prov_disc_req callback is used to generate P2P-PROV-DISC-ENTER-PIN,
++       * P2P-PROV-DISC-SHOW-PIN, and P2P-PROV-DISC-PBC-REQ events.
++       * Call it either on legacy P2P PD or on P2PS PD only if we need to
++       * enter/show PIN.
++       *
++       * The callback is called in the following cases:
++       * 1. Legacy P2P PD request, response status SUCCESS
++       * 2. P2PS advertiser, method: DISPLAY, autoaccept: TRUE,
++       *    response status: SUCCESS
++       * 3. P2PS advertiser, method  DISPLAY, autoaccept: FALSE,
++       *    response status: INFO_CURRENTLY_UNAVAILABLE
++       * 4. P2PS advertiser, method: KEYPAD, autoaccept==any,
++       *    response status: INFO_CURRENTLY_UNAVAILABLE
++       * 5. P2PS follow-on with SUCCESS_DEFERRED,
++       *    advertiser role: DISPLAY, autoaccept: FALSE,
++       *    seeker: KEYPAD, response status: SUCCESS
++       */
++      if (p2p->cfg->prov_disc_req &&
++          ((reject == P2P_SC_SUCCESS && !msg.adv_id) ||
++           (!msg.status &&
++           (reject == P2P_SC_SUCCESS ||
++            reject == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) &&
++            passwd_id == DEV_PW_USER_SPECIFIED) ||
++           (!msg.status &&
++            reject == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE &&
++            passwd_id == DEV_PW_REGISTRAR_SPECIFIED) ||
++           (reject == P2P_SC_SUCCESS &&
++            msg.status && *msg.status == P2P_SC_SUCCESS_DEFERRED &&
++             passwd_id == DEV_PW_REGISTRAR_SPECIFIED))) {
 +              const u8 *dev_addr = sa;
++
 +              if (msg.p2p_device_addr)
 +                      dev_addr = msg.p2p_device_addr;
 +              p2p->cfg->prov_disc_req(p2p->cfg->cb_ctx, sa,
 +                                      msg.wps_config_methods,
 +                                      dev_addr, msg.pri_dev_type,
 +                                      msg.device_name, msg.config_methods,
 +                                      msg.capability ? msg.capability[0] : 0,
 +                                      msg.capability ? msg.capability[1] :
 +                                      0,
 +                                      msg.group_id, msg.group_id_len);
 +      }
++
++      if (dev && reject == P2P_SC_SUCCESS) {
++              switch (config_methods) {
++              case WPS_CONFIG_DISPLAY:
++                      dev->wps_prov_info = WPS_CONFIG_KEYPAD;
++                      break;
++              case WPS_CONFIG_KEYPAD:
++                      dev->wps_prov_info = WPS_CONFIG_DISPLAY;
++                      break;
++              case WPS_CONFIG_PUSHBUTTON:
++                      dev->wps_prov_info = WPS_CONFIG_PUSHBUTTON;
++                      break;
++              case WPS_CONFIG_P2PS:
++                      dev->wps_prov_info = WPS_CONFIG_P2PS;
++                      break;
++              default:
++                      dev->wps_prov_info = 0;
++                      break;
++              }
++
++              if (msg.intended_addr)
++                      os_memcpy(dev->interface_addr, msg.intended_addr,
++                                ETH_ALEN);
++      }
 +      p2p_parse_free(&msg);
 +}
 +
 +
++static int p2p_validate_p2ps_pd_resp(struct p2p_data *p2p,
++                                   struct p2p_message *msg)
++{
++      u8 conn_cap_go = 0;
++      u8 conn_cap_cli = 0;
++      u32 session_id;
++      u32 adv_id;
++
++#define P2PS_PD_RESP_CHECK(_val, _attr) \
++      do { \
++              if ((_val) && !msg->_attr) { \
++                      p2p_dbg(p2p, "P2PS PD Response missing " #_attr); \
++                      return -1; \
++              } \
++      } while (0)
++
++      P2PS_PD_RESP_CHECK(1, status);
++      P2PS_PD_RESP_CHECK(1, adv_id);
++      P2PS_PD_RESP_CHECK(1, adv_mac);
++      P2PS_PD_RESP_CHECK(1, capability);
++      P2PS_PD_RESP_CHECK(1, p2p_device_info);
++      P2PS_PD_RESP_CHECK(1, session_id);
++      P2PS_PD_RESP_CHECK(1, session_mac);
++      P2PS_PD_RESP_CHECK(1, feature_cap);
++
++      session_id = WPA_GET_LE32(msg->session_id);
++      adv_id = WPA_GET_LE32(msg->adv_id);
++
++      if (p2p->p2ps_prov->session_id != session_id) {
++              p2p_dbg(p2p,
++                      "Ignore PD Response with unexpected Session ID");
++              return -1;
++      }
++
++      if (os_memcmp(p2p->p2ps_prov->session_mac, msg->session_mac,
++                    ETH_ALEN)) {
++              p2p_dbg(p2p,
++                      "Ignore PD Response with unexpected Session MAC");
++              return -1;
++      }
++
++      if (p2p->p2ps_prov->adv_id != adv_id) {
++              p2p_dbg(p2p,
++                      "Ignore PD Response with unexpected Advertisement ID");
++              return -1;
++      }
++
++      if (os_memcmp(p2p->p2ps_prov->adv_mac, msg->adv_mac, ETH_ALEN) != 0) {
++              p2p_dbg(p2p,
++                      "Ignore PD Response with unexpected Advertisement MAC");
++              return -1;
++      }
++
++      if (msg->listen_channel) {
++              p2p_dbg(p2p,
++                      "Ignore malformed PD Response - unexpected Listen Channel");
++              return -1;
++      }
++
++      if (*msg->status == P2P_SC_SUCCESS &&
++          !(!!msg->conn_cap ^ !!msg->persistent_dev)) {
++              p2p_dbg(p2p,
++                      "Ignore malformed PD Response - either conn_cap or persistent group should be present");
++              return -1;
++      }
++
++      if (msg->persistent_dev && *msg->status != P2P_SC_SUCCESS) {
++              p2p_dbg(p2p,
++                      "Ignore malformed PD Response - persistent group is present, but the status isn't success");
++              return -1;
++      }
++
++      if (msg->conn_cap) {
++              conn_cap_go = *msg->conn_cap == P2PS_SETUP_GROUP_OWNER;
++              conn_cap_cli = *msg->conn_cap == P2PS_SETUP_CLIENT;
++      }
++
++      P2PS_PD_RESP_CHECK(msg->persistent_dev || conn_cap_go || conn_cap_cli,
++                         channel_list);
++      P2PS_PD_RESP_CHECK(msg->persistent_dev || conn_cap_go || conn_cap_cli,
++                         config_timeout);
++
++      P2PS_PD_RESP_CHECK(conn_cap_go, group_id);
++      P2PS_PD_RESP_CHECK(conn_cap_go, intended_addr);
++      P2PS_PD_RESP_CHECK(conn_cap_go, operating_channel);
++      /*
++       * TODO: Also validate that operating channel is present if the device
++       * is a GO in a persistent group. We can't do it here since we don't
++       * know what is the role of the peer. It should be probably done in
++       * p2ps_prov_complete callback, but currently operating channel isn't
++       * passed to it.
++       */
++
++#undef P2PS_PD_RESP_CHECK
++
++      return 0;
++}
++
++
 +void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa,
 +                              const u8 *data, size_t len)
 +{
 +      struct p2p_message msg;
 +      struct p2p_device *dev;
 +      u16 report_config_methods = 0, req_config_methods;
 +      u8 status = P2P_SC_SUCCESS;
-       u8 group_mac[ETH_ALEN];
 +      u32 adv_id = 0;
 +      u8 conncap = P2PS_SETUP_NEW;
 +      u8 adv_mac[ETH_ALEN];
-       if (msg.intended_addr)
-               os_memcpy(group_mac, msg.intended_addr, ETH_ALEN);
-       else
-               os_memset(group_mac, 0, ETH_ALEN);
++      const u8 *group_mac;
 +      int passwd_id = DEV_PW_DEFAULT;
++      int p2ps_seeker;
 +
 +      if (p2p_parse(data, len, &msg))
 +              return;
 +
++      if (p2p->p2ps_prov && p2p_validate_p2ps_pd_resp(p2p, &msg)) {
++              p2p_parse_free(&msg);
++              return;
++      }
++
 +      /* Parse the P2PS members present */
 +      if (msg.status)
 +              status = *msg.status;
 +
-               os_free(p2p->p2ps_prov);
-               p2p->p2ps_prov = NULL;
++      group_mac = msg.intended_addr;
 +
 +      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);
 +
 +      dev = p2p_get_device(p2p, sa);
 +      if (dev == NULL || !dev->req_config_methods) {
 +              p2p_dbg(p2p, "Ignore Provision Discovery Response from " MACSTR
 +                      " with no pending request", MAC2STR(sa));
 +              p2p_parse_free(&msg);
 +              return;
 +      }
 +
 +      if (dev->dialog_token != msg.dialog_token) {
 +              p2p_dbg(p2p, "Ignore Provision Discovery Response with unexpected Dialog Token %u (expected %u)",
 +                      msg.dialog_token, dev->dialog_token);
 +              p2p_parse_free(&msg);
 +              return;
 +      }
 +
 +      if (p2p->pending_action_state == P2P_PENDING_PD) {
 +              os_memset(p2p->pending_pd_devaddr, 0, ETH_ALEN);
 +              p2p->pending_action_state = P2P_NO_PENDING_ACTION;
 +      }
 +
++      p2ps_seeker = p2p->p2ps_prov && p2p->p2ps_prov->pd_seeker;
++
 +      /*
 +       * Use a local copy of the requested config methods since
 +       * p2p_reset_pending_pd() can clear this in the peer entry.
 +       */
 +      req_config_methods = dev->req_config_methods;
 +
 +      /*
 +       * If the response is from the peer to whom a user initiated request
 +       * was sent earlier, we reset that state info here.
 +       */
 +      if (p2p->user_initiated_pd &&
 +          os_memcmp(p2p->pending_pd_devaddr, sa, ETH_ALEN) == 0)
 +              p2p_reset_pending_pd(p2p);
 +
 +      if (msg.wps_config_methods != req_config_methods) {
 +              p2p_dbg(p2p, "Peer rejected our Provision Discovery Request (received config_methods 0x%x expected 0x%x",
 +                      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,
 +                                               adv_id, adv_mac, NULL);
 +              p2p_parse_free(&msg);
-           msg.adv_id &&
++              p2ps_prov_free(p2p);
 +              goto out;
 +      }
 +
 +      report_config_methods = req_config_methods;
 +      dev->flags &= ~(P2P_DEV_PD_PEER_DISPLAY |
 +                      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.persistent_ssid_len, 1, 0, NULL);
 +          (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,
-               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) {
++                              msg.persistent_ssid_len, 1, 0, NULL,
++                              msg.feature_cap, msg.feature_cap_len);
 +              }
-                               0, 0, NULL, 0, 1, 0, NULL);
-               os_free(p2p->p2ps_prov);
-               p2p->p2ps_prov = NULL;
++              p2ps_prov_free(p2p);
++      } else 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,
-                               os_free(p2p->p2ps_prov);
-                               p2p->p2ps_prov = NULL;
++                              0, 0, NULL, 0, 1, 0, NULL, NULL, 0);
++              p2ps_prov_free(p2p);
 +      }
 +
 +      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);
-       } else if (msg.wps_config_methods != dev->req_config_methods ||
-                  status != P2P_SC_SUCCESS) {
++                              p2ps_prov_free(p2p);
 +                              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);
-                                                P2P_PROV_DISC_REJECTED, 0,
-                                                NULL, NULL);
++      } else if (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,
-               os_free(p2p->p2ps_prov);
-               p2p->p2ps_prov = NULL;
++                                               P2P_PROV_DISC_REJECTED,
++                                               adv_id, adv_mac, NULL);
 +              p2p_parse_free(&msg);
-       success = 1;
++              p2ps_prov_free(p2p);
 +              goto out;
 +      }
 +
 +      /* Store the provisioning info */
 +      dev->wps_prov_info = msg.wps_config_methods;
++      if (msg.intended_addr)
++              os_memcpy(dev->interface_addr, msg.intended_addr, ETH_ALEN);
 +
 +      p2p_parse_free(&msg);
-       if (success && p2p->cfg->prov_disc_resp)
 +
 +out:
 +      dev->req_config_methods = 0;
 +      p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
 +      if (dev->flags & P2P_DEV_PD_BEFORE_GO_NEG) {
 +              p2p_dbg(p2p, "Start GO Neg after the PD-before-GO-Neg workaround with "
 +                      MACSTR, MAC2STR(dev->info.p2p_device_addr));
 +              dev->flags &= ~P2P_DEV_PD_BEFORE_GO_NEG;
 +              p2p_connect_send(p2p, dev);
 +              return;
 +      }
-       os_free(p2p->p2ps_prov);
++
++      /*
++       * prov_disc_resp callback is used to generate P2P-PROV-DISC-ENTER-PIN,
++       * P2P-PROV-DISC-SHOW-PIN, and P2P-PROV-DISC-PBC-REQ events.
++       * Call it only for a legacy P2P PD or for P2PS PD scenarios where
++       * show/enter PIN events are needed.
++       *
++       * The callback is called in the following cases:
++       * 1. Legacy P2P PD response with a status SUCCESS
++       * 2. P2PS, advertiser method: DISPLAY, autoaccept: true,
++       *    response status: SUCCESS, local method KEYPAD
++       * 3. P2PS, advertiser method: KEYPAD,Seeker side,
++       *    response status: INFO_CURRENTLY_UNAVAILABLE,
++       *    local method: DISPLAY
++       */
++      if (p2p->cfg->prov_disc_resp &&
++          ((status == P2P_SC_SUCCESS && !adv_id) ||
++           (p2ps_seeker && status == P2P_SC_SUCCESS &&
++            passwd_id == DEV_PW_REGISTRAR_SPECIFIED) ||
++           (p2ps_seeker &&
++            status == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE &&
++            passwd_id == DEV_PW_USER_SPECIFIED)))
 +              p2p->cfg->prov_disc_resp(p2p->cfg->cb_ctx, sa,
 +                                       report_config_methods);
 +
 +      if (p2p->state == P2P_PD_DURING_FIND) {
 +              p2p_clear_timeout(p2p);
 +              p2p_continue_find(p2p);
 +      }
 +}
 +
 +
 +int p2p_send_prov_disc_req(struct p2p_data *p2p, struct p2p_device *dev,
 +                         int join, int force_freq)
 +{
 +      struct wpabuf *req;
 +      int freq;
 +
 +      if (force_freq > 0)
 +              freq = force_freq;
 +      else
 +              freq = dev->listen_freq > 0 ? dev->listen_freq :
 +                      dev->oper_freq;
 +      if (freq <= 0) {
 +              p2p_dbg(p2p, "No Listen/Operating frequency known for the peer "
 +                      MACSTR " to send Provision Discovery Request",
 +                      MAC2STR(dev->info.p2p_device_addr));
 +              return -1;
 +      }
 +
 +      if (dev->flags & P2P_DEV_GROUP_CLIENT_ONLY) {
 +              if (!(dev->info.dev_capab &
 +                    P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY)) {
 +                      p2p_dbg(p2p, "Cannot use PD with P2P Device " MACSTR
 +                              " that is in a group and is not discoverable",
 +                              MAC2STR(dev->info.p2p_device_addr));
 +                      return -1;
 +              }
 +              /* TODO: use device discoverability request through GO */
 +      }
 +
 +      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;
 +
 +      if (p2p->state != P2P_IDLE)
 +              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,
 +                          p2p->cfg->dev_addr, dev->info.p2p_device_addr,
 +                          wpabuf_head(req), wpabuf_len(req), 200) < 0) {
 +              p2p_dbg(p2p, "Failed to send Action frame");
 +              wpabuf_free(req);
 +              return -1;
 +      }
 +
 +      os_memcpy(p2p->pending_pd_devaddr, dev->info.p2p_device_addr, ETH_ALEN);
 +
 +      wpabuf_free(req);
 +      return 0;
 +}
 +
 +
 +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)
 +{
 +      struct p2p_device *dev;
 +
 +      dev = p2p_get_device(p2p, peer_addr);
 +      if (dev == NULL)
 +              dev = p2p_get_device_interface(p2p, 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 && !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;
++      p2ps_prov_free(p2p);
 +      p2p->p2ps_prov = p2ps_prov;
 +
 +      dev->req_config_methods = config_methods;
 +      if (join)
 +              dev->flags |= P2P_DEV_PD_FOR_JOIN;
 +      else
 +              dev->flags &= ~P2P_DEV_PD_FOR_JOIN;
 +
 +      if (p2p->state != P2P_IDLE && p2p->state != P2P_SEARCH &&
 +          p2p->state != P2P_LISTEN_ONLY) {
 +              p2p_dbg(p2p, "Busy with other operations; postpone Provision Discovery Request with "
 +                      MACSTR " (config methods 0x%x)",
 +                      MAC2STR(peer_addr), config_methods);
 +              return 0;
 +      }
 +
 +      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;
 +
 +      /*
 +       * Assign dialog token here to use the same value in each retry within
 +       * the same PD exchange.
 +       */
 +      dev->dialog_token++;
 +      if (dev->dialog_token == 0)
 +              dev->dialog_token = 1;
 +
 +      return p2p_send_prov_disc_req(p2p, dev, join, force_freq);
 +}
 +
 +
 +void p2p_reset_pending_pd(struct p2p_data *p2p)
 +{
 +      struct p2p_device *dev;
 +
 +      dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) {
 +              if (os_memcmp(p2p->pending_pd_devaddr,
 +                            dev->info.p2p_device_addr, ETH_ALEN))
 +                      continue;
 +              if (!dev->req_config_methods)
 +                      continue;
 +              if (dev->flags & P2P_DEV_PD_FOR_JOIN)
 +                      continue;
 +              /* Reset the config methods of the device */
 +              dev->req_config_methods = 0;
 +      }
 +
 +      p2p->user_initiated_pd = 0;
 +      os_memset(p2p->pending_pd_devaddr, 0, ETH_ALEN);
 +      p2p->pd_retries = 0;
 +      p2p->pd_force_freq = 0;
 +}
++
++
++void p2ps_prov_free(struct p2p_data *p2p)
++{
++      os_free(p2p->p2ps_prov);
++      p2p->p2ps_prov = NULL;
++}
index f32751d79ca8b451abc186f31c41df6ce7fc1665,0000000000000000000000000000000000000000..2e2aa8ad06f080e716404e2934962ea78892f0e3
mode 100644,000000..100644
--- /dev/null
@@@ -1,513 -1,0 +1,485 @@@
-       /* 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;
-       }
-       if (freq == 2484) {
-               *op_class = 82; /* channel 14 */
-               *channel = 14;
-               return 0;
-       }
-       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;
-       }
 +/*
 + * P2P - generic helper functions
 + * Copyright (c) 2009, Atheros Communications
 + *
 + * This software may be distributed under the terms of the BSD license.
 + * See README for more details.
 + */
 +
 +#include "includes.h"
 +
 +#include "common.h"
++#include "common/defs.h"
 +#include "common/ieee802_11_common.h"
 +#include "p2p_i.h"
 +
 +
 +/**
 + * p2p_random - Generate random string for SSID and passphrase
 + * @buf: Buffer for returning the result
 + * @len: Number of octets to write to the buffer
 + * Returns: 0 on success, -1 on failure
 + *
 + * This function generates a random string using the following character set:
 + * 'A'-'Z', 'a'-'z', '0'-'9'.
 + */
 +int p2p_random(char *buf, size_t len)
 +{
 +      u8 val;
 +      size_t i;
 +      u8 letters = 'Z' - 'A' + 1;
 +      u8 numbers = 10;
 +
 +      if (os_get_random((unsigned char *) buf, len))
 +              return -1;
 +      /* Character set: 'A'-'Z', 'a'-'z', '0'-'9' */
 +      for (i = 0; i < len; i++) {
 +              val = buf[i];
 +              val %= 2 * letters + numbers;
 +              if (val < letters)
 +                      buf[i] = 'A' + val;
 +              else if (val < 2 * letters)
 +                      buf[i] = 'a' + (val - letters);
 +              else
 +                      buf[i] = '0' + (val - 2 * letters);
 +      }
 +
 +      return 0;
 +}
 +
 +
 +/**
 + * p2p_channel_to_freq - Convert channel info to frequency
 + * @op_class: Operating class
 + * @channel: Channel number
 + * Returns: Frequency in MHz or -1 if the specified channel is unknown
 + */
 +int p2p_channel_to_freq(int op_class, int channel)
 +{
 +      return ieee80211_chan_to_freq(NULL, op_class, channel);
 +}
 +
 +
 +/**
 + * p2p_freq_to_channel - Convert frequency into channel info
 + * @op_class: Buffer for returning operating class
 + * @channel: Buffer for returning channel number
 + * Returns: 0 on success, -1 if the specified frequency is unknown
 + */
 +int p2p_freq_to_channel(unsigned int freq, u8 *op_class, u8 *channel)
 +{
-       return -1;
++      if (ieee80211_freq_to_channel_ext(freq, 0, 0, op_class, channel) ==
++          NUM_HOSTAPD_MODES)
++              return -1;
 +
++      return 0;
 +}
 +
 +
 +static void p2p_reg_class_intersect(const struct p2p_reg_class *a,
 +                                  const struct p2p_reg_class *b,
 +                                  struct p2p_reg_class *res)
 +{
 +      size_t i, j;
 +
 +      res->reg_class = a->reg_class;
 +
 +      for (i = 0; i < a->channels; i++) {
 +              for (j = 0; j < b->channels; j++) {
 +                      if (a->channel[i] != b->channel[j])
 +                              continue;
 +                      res->channel[res->channels] = a->channel[i];
 +                      res->channels++;
 +                      if (res->channels == P2P_MAX_REG_CLASS_CHANNELS)
 +                              return;
 +              }
 +      }
 +}
 +
 +
 +/**
 + * p2p_channels_intersect - Intersection of supported channel lists
 + * @a: First set of supported channels
 + * @b: Second set of supported channels
 + * @res: Data structure for returning the intersection of support channels
 + *
 + * This function can be used to find a common set of supported channels. Both
 + * input channels sets are assumed to use the same country code. If different
 + * country codes are used, the regulatory class numbers may not be matched
 + * correctly and results are undefined.
 + */
 +void p2p_channels_intersect(const struct p2p_channels *a,
 +                          const struct p2p_channels *b,
 +                          struct p2p_channels *res)
 +{
 +      size_t i, j;
 +
 +      os_memset(res, 0, sizeof(*res));
 +
 +      for (i = 0; i < a->reg_classes; i++) {
 +              const struct p2p_reg_class *a_reg = &a->reg_class[i];
 +              for (j = 0; j < b->reg_classes; j++) {
 +                      const struct p2p_reg_class *b_reg = &b->reg_class[j];
 +                      if (a_reg->reg_class != b_reg->reg_class)
 +                              continue;
 +                      p2p_reg_class_intersect(
 +                              a_reg, b_reg,
 +                              &res->reg_class[res->reg_classes]);
 +                      if (res->reg_class[res->reg_classes].channels) {
 +                              res->reg_classes++;
 +                              if (res->reg_classes == P2P_MAX_REG_CLASSES)
 +                                      return;
 +                      }
 +              }
 +      }
 +}
 +
 +
 +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
 + * @reg_class: Regulatory class of the channel to search
 + * @channel: Channel number of the channel to search
 + * Returns: 1 if channel was found or 0 if not
 + */
 +int p2p_channels_includes(const struct p2p_channels *channels, u8 reg_class,
 +                        u8 channel)
 +{
 +      size_t i, j;
 +      for (i = 0; i < channels->reg_classes; i++) {
 +              const struct p2p_reg_class *reg = &channels->reg_class[i];
 +              if (reg->reg_class != reg_class)
 +                      continue;
 +              for (j = 0; j < reg->channels; j++) {
 +                      if (reg->channel[j] == channel)
 +                              return 1;
 +              }
 +      }
 +      return 0;
 +}
 +
 +
 +int p2p_channels_includes_freq(const struct p2p_channels *channels,
 +                             unsigned int freq)
 +{
 +      size_t i, j;
 +      for (i = 0; i < channels->reg_classes; i++) {
 +              const struct p2p_reg_class *reg = &channels->reg_class[i];
 +              for (j = 0; j < reg->channels; j++) {
 +                      if (p2p_channel_to_freq(reg->reg_class,
 +                                              reg->channel[j]) == (int) freq)
 +                              return 1;
 +              }
 +      }
 +      return 0;
 +}
 +
 +
 +int p2p_supported_freq(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);
 +}
 +
 +
 +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 (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(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;
++                      unsigned int k;
++
 +                      if (idx + 1 == max_len)
 +                              break;
 +                      freq = p2p_channel_to_freq(c->reg_class,
 +                                                 c->channel[j]);
 +                      if (freq < 0)
 +                              continue;
++
++                      for (k = 0; k < idx; k++) {
++                              if (freq_list[k] == freq)
++                                      break;
++                      }
++
++                      if (k < idx)
++                              continue;
 +                      freq_list[idx++] = freq;
 +              }
 +      }
 +
 +      freq_list[idx] = 0;
 +
 +      return idx;
 +}
index 8d878a4bd0783c817275290f19fb8b851f4309b3,0000000000000000000000000000000000000000..1ebfd11f3b9a2b7a81d562dd6bf42a3cb3073078
mode 100644,000000..100644
--- /dev/null
@@@ -1,1671 -1,0 +1,1671 @@@
- static struct radius_attr_type radius_attrs[] =
 +/*
 + * RADIUS message processing
 + * 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.
 + */
 +
 +#include "utils/includes.h"
 +
 +#include "utils/common.h"
 +#include "utils/wpabuf.h"
 +#include "crypto/md5.h"
 +#include "crypto/crypto.h"
 +#include "radius.h"
 +
 +
 +/**
 + * struct radius_msg - RADIUS message structure for new and parsed messages
 + */
 +struct radius_msg {
 +      /**
 +       * buf - Allocated buffer for RADIUS message
 +       */
 +      struct wpabuf *buf;
 +
 +      /**
 +       * hdr - Pointer to the RADIUS header in buf
 +       */
 +      struct radius_hdr *hdr;
 +
 +      /**
 +       * attr_pos - Array of indexes to attributes
 +       *
 +       * The values are number of bytes from buf to the beginning of
 +       * struct radius_attr_hdr.
 +       */
 +      size_t *attr_pos;
 +
 +      /**
 +       * attr_size - Total size of the attribute pointer array
 +       */
 +      size_t attr_size;
 +
 +      /**
 +       * attr_used - Total number of attributes in the array
 +       */
 +      size_t attr_used;
 +};
 +
 +
 +struct radius_hdr * radius_msg_get_hdr(struct radius_msg *msg)
 +{
 +      return msg->hdr;
 +}
 +
 +
 +struct wpabuf * radius_msg_get_buf(struct radius_msg *msg)
 +{
 +      return msg->buf;
 +}
 +
 +
 +static struct radius_attr_hdr *
 +radius_get_attr_hdr(struct radius_msg *msg, int idx)
 +{
 +      return (struct radius_attr_hdr *)
 +              (wpabuf_mhead_u8(msg->buf) + msg->attr_pos[idx]);
 +}
 +
 +
 +static void radius_msg_set_hdr(struct radius_msg *msg, u8 code, u8 identifier)
 +{
 +      msg->hdr->code = code;
 +      msg->hdr->identifier = identifier;
 +}
 +
 +
 +static int radius_msg_initialize(struct radius_msg *msg)
 +{
 +      msg->attr_pos = os_calloc(RADIUS_DEFAULT_ATTR_COUNT,
 +                                sizeof(*msg->attr_pos));
 +      if (msg->attr_pos == NULL)
 +              return -1;
 +
 +      msg->attr_size = RADIUS_DEFAULT_ATTR_COUNT;
 +      msg->attr_used = 0;
 +
 +      return 0;
 +}
 +
 +
 +/**
 + * radius_msg_new - Create a new RADIUS message
 + * @code: Code for RADIUS header
 + * @identifier: Identifier for RADIUS header
 + * Returns: Context for RADIUS message or %NULL on failure
 + *
 + * The caller is responsible for freeing the returned data with
 + * radius_msg_free().
 + */
 +struct radius_msg * radius_msg_new(u8 code, u8 identifier)
 +{
 +      struct radius_msg *msg;
 +
 +      msg = os_zalloc(sizeof(*msg));
 +      if (msg == NULL)
 +              return NULL;
 +
 +      msg->buf = wpabuf_alloc(RADIUS_DEFAULT_MSG_SIZE);
 +      if (msg->buf == NULL || radius_msg_initialize(msg)) {
 +              radius_msg_free(msg);
 +              return NULL;
 +      }
 +      msg->hdr = wpabuf_put(msg->buf, sizeof(struct radius_hdr));
 +
 +      radius_msg_set_hdr(msg, code, identifier);
 +
 +      return msg;
 +}
 +
 +
 +/**
 + * radius_msg_free - Free a RADIUS message
 + * @msg: RADIUS message from radius_msg_new() or radius_msg_parse()
 + */
 +void radius_msg_free(struct radius_msg *msg)
 +{
 +      if (msg == NULL)
 +              return;
 +
 +      wpabuf_free(msg->buf);
 +      os_free(msg->attr_pos);
 +      os_free(msg);
 +}
 +
 +
 +static const char *radius_code_string(u8 code)
 +{
 +      switch (code) {
 +      case RADIUS_CODE_ACCESS_REQUEST: return "Access-Request";
 +      case RADIUS_CODE_ACCESS_ACCEPT: return "Access-Accept";
 +      case RADIUS_CODE_ACCESS_REJECT: return "Access-Reject";
 +      case RADIUS_CODE_ACCOUNTING_REQUEST: return "Accounting-Request";
 +      case RADIUS_CODE_ACCOUNTING_RESPONSE: return "Accounting-Response";
 +      case RADIUS_CODE_ACCESS_CHALLENGE: return "Access-Challenge";
 +      case RADIUS_CODE_STATUS_SERVER: return "Status-Server";
 +      case RADIUS_CODE_STATUS_CLIENT: return "Status-Client";
 +      case RADIUS_CODE_RESERVED: return "Reserved";
 +      case RADIUS_CODE_DISCONNECT_REQUEST: return "Disconnect-Request";
 +      case RADIUS_CODE_DISCONNECT_ACK: return "Disconnect-ACK";
 +      case RADIUS_CODE_DISCONNECT_NAK: return "Disconnect-NAK";
 +      case RADIUS_CODE_COA_REQUEST: return "CoA-Request";
 +      case RADIUS_CODE_COA_ACK: return "CoA-ACK";
 +      case RADIUS_CODE_COA_NAK: return "CoA-NAK";
 +      default: return "?Unknown?";
 +      }
 +}
 +
 +
 +struct radius_attr_type {
 +      u8 type;
 +      char *name;
 +      enum {
 +              RADIUS_ATTR_UNDIST, RADIUS_ATTR_TEXT, RADIUS_ATTR_IP,
 +              RADIUS_ATTR_HEXDUMP, RADIUS_ATTR_INT32, RADIUS_ATTR_IPV6
 +      } data_type;
 +};
 +
- static struct radius_attr_type *radius_get_attr_type(u8 type)
++static const struct radius_attr_type radius_attrs[] =
 +{
 +      { RADIUS_ATTR_USER_NAME, "User-Name", RADIUS_ATTR_TEXT },
 +      { RADIUS_ATTR_USER_PASSWORD, "User-Password", RADIUS_ATTR_UNDIST },
 +      { RADIUS_ATTR_NAS_IP_ADDRESS, "NAS-IP-Address", RADIUS_ATTR_IP },
 +      { RADIUS_ATTR_NAS_PORT, "NAS-Port", RADIUS_ATTR_INT32 },
 +      { RADIUS_ATTR_FRAMED_MTU, "Framed-MTU", RADIUS_ATTR_INT32 },
 +      { RADIUS_ATTR_REPLY_MESSAGE, "Reply-Message", RADIUS_ATTR_TEXT },
 +      { RADIUS_ATTR_STATE, "State", RADIUS_ATTR_UNDIST },
 +      { RADIUS_ATTR_CLASS, "Class", RADIUS_ATTR_UNDIST },
 +      { RADIUS_ATTR_VENDOR_SPECIFIC, "Vendor-Specific", RADIUS_ATTR_UNDIST },
 +      { RADIUS_ATTR_SESSION_TIMEOUT, "Session-Timeout", RADIUS_ATTR_INT32 },
 +      { RADIUS_ATTR_IDLE_TIMEOUT, "Idle-Timeout", RADIUS_ATTR_INT32 },
 +      { RADIUS_ATTR_TERMINATION_ACTION, "Termination-Action",
 +        RADIUS_ATTR_INT32 },
 +      { RADIUS_ATTR_CALLED_STATION_ID, "Called-Station-Id",
 +        RADIUS_ATTR_TEXT },
 +      { RADIUS_ATTR_CALLING_STATION_ID, "Calling-Station-Id",
 +        RADIUS_ATTR_TEXT },
 +      { RADIUS_ATTR_NAS_IDENTIFIER, "NAS-Identifier", RADIUS_ATTR_TEXT },
 +      { RADIUS_ATTR_PROXY_STATE, "Proxy-State", RADIUS_ATTR_UNDIST },
 +      { RADIUS_ATTR_ACCT_STATUS_TYPE, "Acct-Status-Type",
 +        RADIUS_ATTR_INT32 },
 +      { RADIUS_ATTR_ACCT_DELAY_TIME, "Acct-Delay-Time", RADIUS_ATTR_INT32 },
 +      { RADIUS_ATTR_ACCT_INPUT_OCTETS, "Acct-Input-Octets",
 +        RADIUS_ATTR_INT32 },
 +      { RADIUS_ATTR_ACCT_OUTPUT_OCTETS, "Acct-Output-Octets",
 +        RADIUS_ATTR_INT32 },
 +      { RADIUS_ATTR_ACCT_SESSION_ID, "Acct-Session-Id", RADIUS_ATTR_TEXT },
 +      { RADIUS_ATTR_ACCT_AUTHENTIC, "Acct-Authentic", RADIUS_ATTR_INT32 },
 +      { RADIUS_ATTR_ACCT_SESSION_TIME, "Acct-Session-Time",
 +        RADIUS_ATTR_INT32 },
 +      { RADIUS_ATTR_ACCT_INPUT_PACKETS, "Acct-Input-Packets",
 +        RADIUS_ATTR_INT32 },
 +      { RADIUS_ATTR_ACCT_OUTPUT_PACKETS, "Acct-Output-Packets",
 +        RADIUS_ATTR_INT32 },
 +      { RADIUS_ATTR_ACCT_TERMINATE_CAUSE, "Acct-Terminate-Cause",
 +        RADIUS_ATTR_INT32 },
 +      { RADIUS_ATTR_ACCT_MULTI_SESSION_ID, "Acct-Multi-Session-Id",
 +        RADIUS_ATTR_TEXT },
 +      { RADIUS_ATTR_ACCT_LINK_COUNT, "Acct-Link-Count", RADIUS_ATTR_INT32 },
 +      { RADIUS_ATTR_ACCT_INPUT_GIGAWORDS, "Acct-Input-Gigawords", 
 +        RADIUS_ATTR_INT32 },
 +      { RADIUS_ATTR_ACCT_OUTPUT_GIGAWORDS, "Acct-Output-Gigawords",
 +        RADIUS_ATTR_INT32 },
 +      { RADIUS_ATTR_EVENT_TIMESTAMP, "Event-Timestamp",
 +        RADIUS_ATTR_INT32 },
 +      { RADIUS_ATTR_NAS_PORT_TYPE, "NAS-Port-Type", RADIUS_ATTR_INT32 },
 +      { RADIUS_ATTR_TUNNEL_TYPE, "Tunnel-Type", RADIUS_ATTR_HEXDUMP },
 +      { RADIUS_ATTR_TUNNEL_MEDIUM_TYPE, "Tunnel-Medium-Type",
 +        RADIUS_ATTR_HEXDUMP },
 +      { RADIUS_ATTR_TUNNEL_PASSWORD, "Tunnel-Password",
 +        RADIUS_ATTR_UNDIST },
 +      { RADIUS_ATTR_CONNECT_INFO, "Connect-Info", RADIUS_ATTR_TEXT },
 +      { RADIUS_ATTR_EAP_MESSAGE, "EAP-Message", RADIUS_ATTR_UNDIST },
 +      { RADIUS_ATTR_MESSAGE_AUTHENTICATOR, "Message-Authenticator",
 +        RADIUS_ATTR_UNDIST },
 +      { RADIUS_ATTR_TUNNEL_PRIVATE_GROUP_ID, "Tunnel-Private-Group-Id",
 +        RADIUS_ATTR_HEXDUMP },
 +      { RADIUS_ATTR_ACCT_INTERIM_INTERVAL, "Acct-Interim-Interval",
 +        RADIUS_ATTR_INT32 },
 +      { 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_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 ARRAY_SIZE(radius_attrs)
 +
 +
-       struct radius_attr_type *attr;
++static const struct radius_attr_type *radius_get_attr_type(u8 type)
 +{
 +      size_t i;
 +
 +      for (i = 0; i < RADIUS_ATTRS; i++) {
 +              if (type == radius_attrs[i].type)
 +                      return &radius_attrs[i];
 +      }
 +
 +      return NULL;
 +}
 +
 +
 +static void radius_msg_dump_attr(struct radius_attr_hdr *hdr)
 +{
-       buf = os_malloc(hlen + send_key_len + 16);
++      const struct radius_attr_type *attr;
 +      int len;
 +      unsigned char *pos;
 +      char buf[1000];
 +
 +      attr = radius_get_attr_type(hdr->type);
 +
 +      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;
 +
 +      len = hdr->length - sizeof(struct radius_attr_hdr);
 +      pos = (unsigned char *) (hdr + 1);
 +
 +      switch (attr->data_type) {
 +      case RADIUS_ATTR_TEXT:
 +              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);
 +                      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) {
 +                      const char *atxt;
 +                      struct in6_addr *addr = (struct in6_addr *) pos;
 +                      atxt = inet_ntop(AF_INET6, addr, buf, sizeof(buf));
 +                      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:
 +              wpa_snprintf_hex(buf, sizeof(buf), pos, len);
 +              wpa_printf(MSG_INFO, "      Value: %s", buf);
 +              break;
 +
 +      case RADIUS_ATTR_INT32:
 +              if (len == 4)
 +                      wpa_printf(MSG_INFO, "      Value: %u",
 +                                 WPA_GET_BE32(pos));
 +              else
 +                      wpa_printf(MSG_INFO, "      Invalid INT32 length %d",
 +                                 len);
 +              break;
 +
 +      default:
 +              break;
 +      }
 +}
 +
 +
 +void radius_msg_dump(struct radius_msg *msg)
 +{
 +      size_t i;
 +
 +      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);
 +              radius_msg_dump_attr(attr);
 +      }
 +}
 +
 +
 +int radius_msg_finish(struct radius_msg *msg, const u8 *secret,
 +                    size_t secret_len)
 +{
 +      if (secret) {
 +              u8 auth[MD5_MAC_LEN];
 +              struct radius_attr_hdr *attr;
 +
 +              os_memset(auth, 0, MD5_MAC_LEN);
 +              attr = radius_msg_add_attr(msg,
 +                                         RADIUS_ATTR_MESSAGE_AUTHENTICATOR,
 +                                         auth, MD5_MAC_LEN);
 +              if (attr == NULL) {
 +                      wpa_printf(MSG_WARNING, "RADIUS: Could not add "
 +                                 "Message-Authenticator");
 +                      return -1;
 +              }
 +              msg->hdr->length = host_to_be16(wpabuf_len(msg->buf));
 +              hmac_md5(secret, secret_len, wpabuf_head(msg->buf),
 +                       wpabuf_len(msg->buf), (u8 *) (attr + 1));
 +      } else
 +              msg->hdr->length = host_to_be16(wpabuf_len(msg->buf));
 +
 +      if (wpabuf_len(msg->buf) > 0xffff) {
 +              wpa_printf(MSG_WARNING, "RADIUS: Too long message (%lu)",
 +                         (unsigned long) wpabuf_len(msg->buf));
 +              return -1;
 +      }
 +      return 0;
 +}
 +
 +
 +int radius_msg_finish_srv(struct radius_msg *msg, const u8 *secret,
 +                        size_t secret_len, const u8 *req_authenticator)
 +{
 +      u8 auth[MD5_MAC_LEN];
 +      struct radius_attr_hdr *attr;
 +      const u8 *addr[4];
 +      size_t len[4];
 +
 +      os_memset(auth, 0, MD5_MAC_LEN);
 +      attr = radius_msg_add_attr(msg, RADIUS_ATTR_MESSAGE_AUTHENTICATOR,
 +                                 auth, MD5_MAC_LEN);
 +      if (attr == NULL) {
 +              wpa_printf(MSG_ERROR, "WARNING: Could not add Message-Authenticator");
 +              return -1;
 +      }
 +      msg->hdr->length = host_to_be16(wpabuf_len(msg->buf));
 +      os_memcpy(msg->hdr->authenticator, req_authenticator,
 +                sizeof(msg->hdr->authenticator));
 +      hmac_md5(secret, secret_len, wpabuf_head(msg->buf),
 +               wpabuf_len(msg->buf), (u8 *) (attr + 1));
 +
 +      /* ResponseAuth = MD5(Code+ID+Length+RequestAuth+Attributes+Secret) */
 +      addr[0] = (u8 *) msg->hdr;
 +      len[0] = 1 + 1 + 2;
 +      addr[1] = req_authenticator;
 +      len[1] = MD5_MAC_LEN;
 +      addr[2] = wpabuf_head_u8(msg->buf) + sizeof(struct radius_hdr);
 +      len[2] = wpabuf_len(msg->buf) - sizeof(struct radius_hdr);
 +      addr[3] = secret;
 +      len[3] = secret_len;
 +      md5_vector(4, addr, len, msg->hdr->authenticator);
 +
 +      if (wpabuf_len(msg->buf) > 0xffff) {
 +              wpa_printf(MSG_WARNING, "RADIUS: Too long message (%lu)",
 +                         (unsigned long) wpabuf_len(msg->buf));
 +              return -1;
 +      }
 +      return 0;
 +}
 +
 +
 +int radius_msg_finish_das_resp(struct radius_msg *msg, const u8 *secret,
 +                             size_t secret_len,
 +                             const struct radius_hdr *req_hdr)
 +{
 +      const u8 *addr[2];
 +      size_t len[2];
 +      u8 auth[MD5_MAC_LEN];
 +      struct radius_attr_hdr *attr;
 +
 +      os_memset(auth, 0, MD5_MAC_LEN);
 +      attr = radius_msg_add_attr(msg, RADIUS_ATTR_MESSAGE_AUTHENTICATOR,
 +                                 auth, MD5_MAC_LEN);
 +      if (attr == NULL) {
 +              wpa_printf(MSG_WARNING, "Could not add Message-Authenticator");
 +              return -1;
 +      }
 +
 +      msg->hdr->length = host_to_be16(wpabuf_len(msg->buf));
 +      os_memcpy(msg->hdr->authenticator, req_hdr->authenticator, 16);
 +      hmac_md5(secret, secret_len, wpabuf_head(msg->buf),
 +               wpabuf_len(msg->buf), (u8 *) (attr + 1));
 +
 +      /* ResponseAuth = MD5(Code+ID+Length+RequestAuth+Attributes+Secret) */
 +      addr[0] = wpabuf_head_u8(msg->buf);
 +      len[0] = wpabuf_len(msg->buf);
 +      addr[1] = secret;
 +      len[1] = secret_len;
 +      if (md5_vector(2, addr, len, msg->hdr->authenticator) < 0)
 +              return -1;
 +
 +      if (wpabuf_len(msg->buf) > 0xffff) {
 +              wpa_printf(MSG_WARNING, "RADIUS: Too long message (%lu)",
 +                         (unsigned long) wpabuf_len(msg->buf));
 +              return -1;
 +      }
 +      return 0;
 +}
 +
 +
 +void radius_msg_finish_acct(struct radius_msg *msg, const u8 *secret,
 +                          size_t secret_len)
 +{
 +      const u8 *addr[2];
 +      size_t len[2];
 +
 +      msg->hdr->length = host_to_be16(wpabuf_len(msg->buf));
 +      os_memset(msg->hdr->authenticator, 0, 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));
 +      }
 +}
 +
 +
 +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)
 +{
 +      const u8 *addr[4];
 +      size_t len[4];
 +      u8 zero[MD5_MAC_LEN];
 +      u8 hash[MD5_MAC_LEN];
 +
 +      os_memset(zero, 0, sizeof(zero));
 +      addr[0] = (u8 *) msg->hdr;
 +      len[0] = sizeof(struct radius_hdr) - MD5_MAC_LEN;
 +      addr[1] = zero;
 +      len[1] = MD5_MAC_LEN;
 +      addr[2] = (u8 *) (msg->hdr + 1);
 +      len[2] = wpabuf_len(msg->buf) - sizeof(struct radius_hdr);
 +      addr[3] = secret;
 +      len[3] = secret_len;
 +      md5_vector(4, addr, len, hash);
 +      return os_memcmp_const(msg->hdr->authenticator, hash, MD5_MAC_LEN) != 0;
 +}
 +
 +
 +int radius_msg_verify_das_req(struct radius_msg *msg, const u8 *secret,
 +                            size_t secret_len)
 +{
 +      const u8 *addr[4];
 +      size_t len[4];
 +      u8 zero[MD5_MAC_LEN];
 +      u8 hash[MD5_MAC_LEN];
 +      u8 auth[MD5_MAC_LEN], orig[MD5_MAC_LEN];
 +      u8 orig_authenticator[16];
 +
 +      struct radius_attr_hdr *attr = NULL, *tmp;
 +      size_t i;
 +
 +      os_memset(zero, 0, sizeof(zero));
 +      addr[0] = (u8 *) msg->hdr;
 +      len[0] = sizeof(struct radius_hdr) - MD5_MAC_LEN;
 +      addr[1] = zero;
 +      len[1] = MD5_MAC_LEN;
 +      addr[2] = (u8 *) (msg->hdr + 1);
 +      len[2] = wpabuf_len(msg->buf) - sizeof(struct radius_hdr);
 +      addr[3] = secret;
 +      len[3] = secret_len;
 +      md5_vector(4, addr, len, hash);
 +      if (os_memcmp_const(msg->hdr->authenticator, hash, MD5_MAC_LEN) != 0)
 +              return 1;
 +
 +      for (i = 0; i < msg->attr_used; i++) {
 +              tmp = radius_get_attr_hdr(msg, i);
 +              if (tmp->type == RADIUS_ATTR_MESSAGE_AUTHENTICATOR) {
 +                      if (attr != NULL) {
 +                              wpa_printf(MSG_WARNING, "Multiple "
 +                                         "Message-Authenticator attributes "
 +                                         "in RADIUS message");
 +                              return 1;
 +                      }
 +                      attr = tmp;
 +              }
 +      }
 +
 +      if (attr == NULL) {
 +              /* Message-Authenticator is MAY; not required */
 +              return 0;
 +      }
 +
 +      os_memcpy(orig, attr + 1, MD5_MAC_LEN);
 +      os_memset(attr + 1, 0, MD5_MAC_LEN);
 +      os_memcpy(orig_authenticator, msg->hdr->authenticator,
 +                sizeof(orig_authenticator));
 +      os_memset(msg->hdr->authenticator, 0,
 +                sizeof(msg->hdr->authenticator));
 +      hmac_md5(secret, secret_len, wpabuf_head(msg->buf),
 +               wpabuf_len(msg->buf), auth);
 +      os_memcpy(attr + 1, orig, MD5_MAC_LEN);
 +      os_memcpy(msg->hdr->authenticator, orig_authenticator,
 +                sizeof(orig_authenticator));
 +
 +      return os_memcmp_const(orig, auth, MD5_MAC_LEN) != 0;
 +}
 +
 +
 +static int radius_msg_add_attr_to_array(struct radius_msg *msg,
 +                                      struct radius_attr_hdr *attr)
 +{
 +      if (msg->attr_used >= msg->attr_size) {
 +              size_t *nattr_pos;
 +              int nlen = msg->attr_size * 2;
 +
 +              nattr_pos = os_realloc_array(msg->attr_pos, nlen,
 +                                           sizeof(*msg->attr_pos));
 +              if (nattr_pos == NULL)
 +                      return -1;
 +
 +              msg->attr_pos = nattr_pos;
 +              msg->attr_size = nlen;
 +      }
 +
 +      msg->attr_pos[msg->attr_used++] =
 +              (unsigned char *) attr - wpabuf_head_u8(msg->buf);
 +
 +      return 0;
 +}
 +
 +
 +struct radius_attr_hdr *radius_msg_add_attr(struct radius_msg *msg, u8 type,
 +                                          const u8 *data, size_t data_len)
 +{
 +      size_t buf_needed;
 +      struct radius_attr_hdr *attr;
 +
 +      if (data_len > RADIUS_MAX_ATTR_LEN) {
 +              wpa_printf(MSG_ERROR, "radius_msg_add_attr: too long attribute (%lu bytes)",
 +                     (unsigned long) data_len);
 +              return NULL;
 +      }
 +
 +      buf_needed = sizeof(*attr) + data_len;
 +
 +      if (wpabuf_tailroom(msg->buf) < buf_needed) {
 +              /* allocate more space for message buffer */
 +              if (wpabuf_resize(&msg->buf, buf_needed) < 0)
 +                      return NULL;
 +              msg->hdr = wpabuf_mhead(msg->buf);
 +      }
 +
 +      attr = wpabuf_put(msg->buf, sizeof(struct radius_attr_hdr));
 +      attr->type = type;
 +      attr->length = sizeof(*attr) + data_len;
 +      wpabuf_put_data(msg->buf, data, data_len);
 +
 +      if (radius_msg_add_attr_to_array(msg, attr))
 +              return NULL;
 +
 +      return attr;
 +}
 +
 +
 +/**
 + * radius_msg_parse - Parse a RADIUS message
 + * @data: RADIUS message to be parsed
 + * @len: Length of data buffer in octets
 + * Returns: Parsed RADIUS message or %NULL on failure
 + *
 + * This parses a RADIUS message and makes a copy of its data. The caller is
 + * responsible for freeing the returned data with radius_msg_free().
 + */
 +struct radius_msg * radius_msg_parse(const u8 *data, size_t len)
 +{
 +      struct radius_msg *msg;
 +      struct radius_hdr *hdr;
 +      struct radius_attr_hdr *attr;
 +      size_t msg_len;
 +      unsigned char *pos, *end;
 +
 +      if (data == NULL || len < sizeof(*hdr))
 +              return NULL;
 +
 +      hdr = (struct radius_hdr *) data;
 +
 +      msg_len = be_to_host16(hdr->length);
 +      if (msg_len < sizeof(*hdr) || msg_len > len) {
 +              wpa_printf(MSG_INFO, "RADIUS: Invalid message length");
 +              return NULL;
 +      }
 +
 +      if (msg_len < len) {
 +              wpa_printf(MSG_DEBUG, "RADIUS: Ignored %lu extra bytes after "
 +                         "RADIUS message", (unsigned long) len - msg_len);
 +      }
 +
 +      msg = os_zalloc(sizeof(*msg));
 +      if (msg == NULL)
 +              return NULL;
 +
 +      msg->buf = wpabuf_alloc_copy(data, msg_len);
 +      if (msg->buf == NULL || radius_msg_initialize(msg)) {
 +              radius_msg_free(msg);
 +              return NULL;
 +      }
 +      msg->hdr = wpabuf_mhead(msg->buf);
 +
 +      /* parse attributes */
 +      pos = wpabuf_mhead_u8(msg->buf) + sizeof(struct radius_hdr);
 +      end = wpabuf_mhead_u8(msg->buf) + wpabuf_len(msg->buf);
 +      while (pos < end) {
 +              if ((size_t) (end - pos) < sizeof(*attr))
 +                      goto fail;
 +
 +              attr = (struct radius_attr_hdr *) pos;
 +
 +              if (pos + attr->length > end || attr->length < sizeof(*attr))
 +                      goto fail;
 +
 +              /* TODO: check that attr->length is suitable for attr->type */
 +
 +              if (radius_msg_add_attr_to_array(msg, attr))
 +                      goto fail;
 +
 +              pos += attr->length;
 +      }
 +
 +      return msg;
 +
 + fail:
 +      radius_msg_free(msg);
 +      return NULL;
 +}
 +
 +
 +int radius_msg_add_eap(struct radius_msg *msg, const u8 *data, size_t data_len)
 +{
 +      const u8 *pos = data;
 +      size_t left = data_len;
 +
 +      while (left > 0) {
 +              int len;
 +              if (left > RADIUS_MAX_ATTR_LEN)
 +                      len = RADIUS_MAX_ATTR_LEN;
 +              else
 +                      len = left;
 +
 +              if (!radius_msg_add_attr(msg, RADIUS_ATTR_EAP_MESSAGE,
 +                                       pos, len))
 +                      return 0;
 +
 +              pos += len;
 +              left -= len;
 +      }
 +
 +      return 1;
 +}
 +
 +
 +struct wpabuf * radius_msg_get_eap(struct radius_msg *msg)
 +{
 +      struct wpabuf *eap;
 +      size_t len, i;
 +      struct radius_attr_hdr *attr;
 +
 +      if (msg == NULL)
 +              return NULL;
 +
 +      len = 0;
 +      for (i = 0; i < msg->attr_used; i++) {
 +              attr = radius_get_attr_hdr(msg, i);
 +              if (attr->type == RADIUS_ATTR_EAP_MESSAGE &&
 +                  attr->length > sizeof(struct radius_attr_hdr))
 +                      len += attr->length - sizeof(struct radius_attr_hdr);
 +      }
 +
 +      if (len == 0)
 +              return NULL;
 +
 +      eap = wpabuf_alloc(len);
 +      if (eap == NULL)
 +              return NULL;
 +
 +      for (i = 0; i < msg->attr_used; i++) {
 +              attr = radius_get_attr_hdr(msg, i);
 +              if (attr->type == RADIUS_ATTR_EAP_MESSAGE &&
 +                  attr->length > sizeof(struct radius_attr_hdr)) {
 +                      int flen = attr->length - sizeof(*attr);
 +                      wpabuf_put_data(eap, attr + 1, flen);
 +              }
 +      }
 +
 +      return eap;
 +}
 +
 +
 +int radius_msg_verify_msg_auth(struct radius_msg *msg, const u8 *secret,
 +                             size_t secret_len, const u8 *req_auth)
 +{
 +      u8 auth[MD5_MAC_LEN], orig[MD5_MAC_LEN];
 +      u8 orig_authenticator[16];
 +      struct radius_attr_hdr *attr = NULL, *tmp;
 +      size_t i;
 +
 +      for (i = 0; i < msg->attr_used; i++) {
 +              tmp = radius_get_attr_hdr(msg, i);
 +              if (tmp->type == RADIUS_ATTR_MESSAGE_AUTHENTICATOR) {
 +                      if (attr != NULL) {
 +                              wpa_printf(MSG_INFO, "Multiple Message-Authenticator attributes in RADIUS message");
 +                              return 1;
 +                      }
 +                      attr = tmp;
 +              }
 +      }
 +
 +      if (attr == NULL) {
 +              wpa_printf(MSG_INFO, "No Message-Authenticator attribute found");
 +              return 1;
 +      }
 +
 +      os_memcpy(orig, attr + 1, MD5_MAC_LEN);
 +      os_memset(attr + 1, 0, MD5_MAC_LEN);
 +      if (req_auth) {
 +              os_memcpy(orig_authenticator, msg->hdr->authenticator,
 +                        sizeof(orig_authenticator));
 +              os_memcpy(msg->hdr->authenticator, req_auth,
 +                        sizeof(msg->hdr->authenticator));
 +      }
 +      hmac_md5(secret, secret_len, wpabuf_head(msg->buf),
 +               wpabuf_len(msg->buf), auth);
 +      os_memcpy(attr + 1, orig, MD5_MAC_LEN);
 +      if (req_auth) {
 +              os_memcpy(msg->hdr->authenticator, orig_authenticator,
 +                        sizeof(orig_authenticator));
 +      }
 +
 +      if (os_memcmp_const(orig, auth, MD5_MAC_LEN) != 0) {
 +              wpa_printf(MSG_INFO, "Invalid Message-Authenticator!");
 +              return 1;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +int radius_msg_verify(struct radius_msg *msg, const u8 *secret,
 +                    size_t secret_len, struct radius_msg *sent_msg, int auth)
 +{
 +      const u8 *addr[4];
 +      size_t len[4];
 +      u8 hash[MD5_MAC_LEN];
 +
 +      if (sent_msg == NULL) {
 +              wpa_printf(MSG_INFO, "No matching Access-Request message found");
 +              return 1;
 +      }
 +
 +      if (auth &&
 +          radius_msg_verify_msg_auth(msg, secret, secret_len,
 +                                     sent_msg->hdr->authenticator)) {
 +              return 1;
 +      }
 +
 +      /* ResponseAuth = MD5(Code+ID+Length+RequestAuth+Attributes+Secret) */
 +      addr[0] = (u8 *) msg->hdr;
 +      len[0] = 1 + 1 + 2;
 +      addr[1] = sent_msg->hdr->authenticator;
 +      len[1] = MD5_MAC_LEN;
 +      addr[2] = wpabuf_head_u8(msg->buf) + sizeof(struct radius_hdr);
 +      len[2] = wpabuf_len(msg->buf) - sizeof(struct radius_hdr);
 +      addr[3] = secret;
 +      len[3] = secret_len;
 +      md5_vector(4, addr, len, hash);
 +      if (os_memcmp_const(hash, msg->hdr->authenticator, MD5_MAC_LEN) != 0) {
 +              wpa_printf(MSG_INFO, "Response Authenticator invalid!");
 +              return 1;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +int radius_msg_copy_attr(struct radius_msg *dst, struct radius_msg *src,
 +                       u8 type)
 +{
 +      struct radius_attr_hdr *attr;
 +      size_t i;
 +      int count = 0;
 +
 +      for (i = 0; i < src->attr_used; i++) {
 +              attr = radius_get_attr_hdr(src, i);
 +              if (attr->type == type && attr->length >= sizeof(*attr)) {
 +                      if (!radius_msg_add_attr(dst, type, (u8 *) (attr + 1),
 +                                               attr->length - sizeof(*attr)))
 +                              return -1;
 +                      count++;
 +              }
 +      }
 +
 +      return count;
 +}
 +
 +
 +/* Create Request Authenticator. The value should be unique over the lifetime
 + * of the shared secret between authenticator and authentication server.
 + * Use one-way MD5 hash calculated from current timestamp and some data given
 + * by the caller. */
 +void radius_msg_make_authenticator(struct radius_msg *msg,
 +                                 const u8 *data, size_t len)
 +{
 +      struct os_time tv;
 +      long int l;
 +      const u8 *addr[3];
 +      size_t elen[3];
 +
 +      os_get_time(&tv);
 +      l = os_random();
 +      addr[0] = (u8 *) &tv;
 +      elen[0] = sizeof(tv);
 +      addr[1] = data;
 +      elen[1] = len;
 +      addr[2] = (u8 *) &l;
 +      elen[2] = sizeof(l);
 +      md5_vector(3, addr, elen, msg->hdr->authenticator);
 +}
 +
 +
 +/* Get Vendor-specific RADIUS Attribute from a parsed RADIUS message.
 + * Returns the Attribute payload and sets alen to indicate the length of the
 + * payload if a vendor attribute with subtype is found, otherwise returns NULL.
 + * The returned payload is allocated with os_malloc() and caller must free it
 + * by calling os_free().
 + */
 +static u8 *radius_msg_get_vendor_attr(struct radius_msg *msg, u32 vendor,
 +                                    u8 subtype, size_t *alen)
 +{
 +      u8 *data, *pos;
 +      size_t i, len;
 +
 +      if (msg == NULL)
 +              return NULL;
 +
 +      for (i = 0; i < msg->attr_used; i++) {
 +              struct radius_attr_hdr *attr = radius_get_attr_hdr(msg, i);
 +              size_t left;
 +              u32 vendor_id;
 +              struct radius_attr_vendor *vhdr;
 +
 +              if (attr->type != RADIUS_ATTR_VENDOR_SPECIFIC ||
 +                  attr->length < sizeof(*attr))
 +                      continue;
 +
 +              left = attr->length - sizeof(*attr);
 +              if (left < 4)
 +                      continue;
 +
 +              pos = (u8 *) (attr + 1);
 +
 +              os_memcpy(&vendor_id, pos, 4);
 +              pos += 4;
 +              left -= 4;
 +
 +              if (ntohl(vendor_id) != vendor)
 +                      continue;
 +
 +              while (left >= sizeof(*vhdr)) {
 +                      vhdr = (struct radius_attr_vendor *) pos;
 +                      if (vhdr->vendor_length > left ||
 +                          vhdr->vendor_length < sizeof(*vhdr)) {
 +                              break;
 +                      }
 +                      if (vhdr->vendor_type != subtype) {
 +                              pos += vhdr->vendor_length;
 +                              left -= vhdr->vendor_length;
 +                              continue;
 +                      }
 +
 +                      len = vhdr->vendor_length - sizeof(*vhdr);
 +                      data = os_malloc(len);
 +                      if (data == NULL)
 +                              return NULL;
 +                      os_memcpy(data, pos + sizeof(*vhdr), len);
 +                      if (alen)
 +                              *alen = len;
 +                      return data;
 +              }
 +      }
 +
 +      return NULL;
 +}
 +
 +
 +static u8 * decrypt_ms_key(const u8 *key, size_t len,
 +                         const u8 *req_authenticator,
 +                         const u8 *secret, size_t secret_len, size_t *reslen)
 +{
 +      u8 *plain, *ppos, *res;
 +      const u8 *pos;
 +      size_t left, plen;
 +      u8 hash[MD5_MAC_LEN];
 +      int i, first = 1;
 +      const u8 *addr[3];
 +      size_t elen[3];
 +
 +      /* key: 16-bit salt followed by encrypted key info */
 +
 +      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) {
 +              wpa_printf(MSG_INFO, "RADIUS: Invalid ms key len %lu",
 +                         (unsigned long) left);
 +              return NULL;
 +      }
 +
 +      plen = left;
 +      ppos = plain = os_malloc(plen);
 +      if (plain == NULL)
 +              return NULL;
 +      plain[0] = 0;
 +
 +      while (left > 0) {
 +              /* b(1) = MD5(Secret + Request-Authenticator + Salt)
 +               * b(i) = MD5(Secret + c(i - 1)) for i > 1 */
 +
 +              addr[0] = secret;
 +              elen[0] = secret_len;
 +              if (first) {
 +                      addr[1] = req_authenticator;
 +                      elen[1] = MD5_MAC_LEN;
 +                      addr[2] = key;
 +                      elen[2] = 2; /* Salt */
 +              } else {
 +                      addr[1] = pos - MD5_MAC_LEN;
 +                      elen[1] = MD5_MAC_LEN;
 +              }
 +              md5_vector(first ? 3 : 2, addr, elen, hash);
 +              first = 0;
 +
 +              for (i = 0; i < MD5_MAC_LEN; i++)
 +                      *ppos++ = *pos++ ^ hash[i];
 +              left -= MD5_MAC_LEN;
 +      }
 +
 +      if (plain[0] == 0 || plain[0] > plen - 1) {
 +              wpa_printf(MSG_INFO, "RADIUS: Failed to decrypt MPPE key");
 +              os_free(plain);
 +              return NULL;
 +      }
 +
 +      res = os_malloc(plain[0]);
 +      if (res == NULL) {
 +              os_free(plain);
 +              return NULL;
 +      }
 +      os_memcpy(res, plain + 1, plain[0]);
 +      if (reslen)
 +              *reslen = plain[0];
 +      os_free(plain);
 +      return res;
 +}
 +
 +
 +static void encrypt_ms_key(const u8 *key, size_t key_len, u16 salt,
 +                         const u8 *req_authenticator,
 +                         const u8 *secret, size_t secret_len,
 +                         u8 *ebuf, size_t *elen)
 +{
 +      int i, len, first = 1;
 +      u8 hash[MD5_MAC_LEN], saltbuf[2], *pos;
 +      const u8 *addr[3];
 +      size_t _len[3];
 +
 +      WPA_PUT_BE16(saltbuf, salt);
 +
 +      len = 1 + key_len;
 +      if (len & 0x0f) {
 +              len = (len & 0xf0) + 16;
 +      }
 +      os_memset(ebuf, 0, len);
 +      ebuf[0] = key_len;
 +      os_memcpy(ebuf + 1, key, key_len);
 +
 +      *elen = len;
 +
 +      pos = ebuf;
 +      while (len > 0) {
 +              /* b(1) = MD5(Secret + Request-Authenticator + Salt)
 +               * b(i) = MD5(Secret + c(i - 1)) for i > 1 */
 +              addr[0] = secret;
 +              _len[0] = secret_len;
 +              if (first) {
 +                      addr[1] = req_authenticator;
 +                      _len[1] = MD5_MAC_LEN;
 +                      addr[2] = saltbuf;
 +                      _len[2] = sizeof(saltbuf);
 +              } else {
 +                      addr[1] = pos - MD5_MAC_LEN;
 +                      _len[1] = MD5_MAC_LEN;
 +              }
 +              md5_vector(first ? 3 : 2, addr, _len, hash);
 +              first = 0;
 +
 +              for (i = 0; i < MD5_MAC_LEN; i++)
 +                      *pos++ ^= hash[i];
 +
 +              len -= MD5_MAC_LEN;
 +      }
 +}
 +
 +
 +struct radius_ms_mppe_keys *
 +radius_msg_get_ms_keys(struct radius_msg *msg, struct radius_msg *sent_msg,
 +                     const u8 *secret, size_t secret_len)
 +{
 +      u8 *key;
 +      size_t keylen;
 +      struct radius_ms_mppe_keys *keys;
 +
 +      if (msg == NULL || sent_msg == NULL)
 +              return NULL;
 +
 +      keys = os_zalloc(sizeof(*keys));
 +      if (keys == NULL)
 +              return NULL;
 +
 +      key = radius_msg_get_vendor_attr(msg, RADIUS_VENDOR_ID_MICROSOFT,
 +                                       RADIUS_VENDOR_ATTR_MS_MPPE_SEND_KEY,
 +                                       &keylen);
 +      if (key) {
 +              keys->send = decrypt_ms_key(key, keylen,
 +                                          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);
 +      }
 +
 +      key = radius_msg_get_vendor_attr(msg, RADIUS_VENDOR_ID_MICROSOFT,
 +                                       RADIUS_VENDOR_ATTR_MS_MPPE_RECV_KEY,
 +                                       &keylen);
 +      if (key) {
 +              keys->recv = decrypt_ms_key(key, keylen,
 +                                          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);
 +      }
 +
 +      return keys;
 +}
 +
 +
 +struct radius_ms_mppe_keys *
 +radius_msg_get_cisco_keys(struct radius_msg *msg, struct radius_msg *sent_msg,
 +                        const u8 *secret, size_t secret_len)
 +{
 +      u8 *key;
 +      size_t keylen;
 +      struct radius_ms_mppe_keys *keys;
 +
 +      if (msg == NULL || sent_msg == NULL)
 +              return NULL;
 +
 +      keys = os_zalloc(sizeof(*keys));
 +      if (keys == NULL)
 +              return NULL;
 +
 +      key = radius_msg_get_vendor_attr(msg, RADIUS_VENDOR_ID_CISCO,
 +                                       RADIUS_CISCO_AV_PAIR, &keylen);
 +      if (key && keylen == 51 &&
 +          os_memcmp(key, "leap:session-key=", 17) == 0) {
 +              keys->recv = decrypt_ms_key(key + 17, keylen - 17,
 +                                          sent_msg->hdr->authenticator,
 +                                          secret, secret_len,
 +                                          &keys->recv_len);
 +      }
 +      os_free(key);
 +
 +      return keys;
 +}
 +
 +
 +int radius_msg_add_mppe_keys(struct radius_msg *msg,
 +                           const u8 *req_authenticator,
 +                           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)
 +{
 +      struct radius_attr_hdr *attr;
 +      u32 vendor_id = htonl(RADIUS_VENDOR_ID_MICROSOFT);
 +      u8 *buf;
 +      struct radius_attr_vendor *vhdr;
 +      u8 *pos;
 +      size_t elen;
 +      int hlen;
 +      u16 salt;
 +
 +      hlen = sizeof(vendor_id) + sizeof(*vhdr) + 2;
 +
 +      /* MS-MPPE-Send-Key */
 +      buf = os_malloc(hlen + send_key_len + 16);
 +      if (buf == NULL) {
 +              return 0;
 +      }
 +      pos = buf;
 +      os_memcpy(pos, &vendor_id, sizeof(vendor_id));
 +      pos += sizeof(vendor_id);
 +      vhdr = (struct radius_attr_vendor *) pos;
 +      vhdr->vendor_type = RADIUS_VENDOR_ATTR_MS_MPPE_SEND_KEY;
 +      pos = (u8 *) (vhdr + 1);
 +      salt = os_random() | 0x8000;
 +      WPA_PUT_BE16(pos, salt);
 +      pos += 2;
 +      encrypt_ms_key(send_key, send_key_len, salt, req_authenticator, secret,
 +                     secret_len, pos, &elen);
 +      vhdr->vendor_length = hlen + elen - sizeof(vendor_id);
 +
 +      attr = radius_msg_add_attr(msg, RADIUS_ATTR_VENDOR_SPECIFIC,
 +                                 buf, hlen + elen);
 +      os_free(buf);
 +      if (attr == NULL) {
 +              return 0;
 +      }
 +
 +      /* MS-MPPE-Recv-Key */
-  * Returns: VLAN ID for the first tunnel configuration of -1 if none is found
++      buf = os_malloc(hlen + recv_key_len + 16);
 +      if (buf == NULL) {
 +              return 0;
 +      }
 +      pos = buf;
 +      os_memcpy(pos, &vendor_id, sizeof(vendor_id));
 +      pos += sizeof(vendor_id);
 +      vhdr = (struct radius_attr_vendor *) pos;
 +      vhdr->vendor_type = RADIUS_VENDOR_ATTR_MS_MPPE_RECV_KEY;
 +      pos = (u8 *) (vhdr + 1);
 +      salt ^= 1;
 +      WPA_PUT_BE16(pos, salt);
 +      pos += 2;
 +      encrypt_ms_key(recv_key, recv_key_len, salt, req_authenticator, secret,
 +                     secret_len, pos, &elen);
 +      vhdr->vendor_length = hlen + elen - sizeof(vendor_id);
 +
 +      attr = radius_msg_add_attr(msg, RADIUS_ATTR_VENDOR_SPECIFIC,
 +                                 buf, hlen + elen);
 +      os_free(buf);
 +      if (attr == NULL) {
 +              return 0;
 +      }
 +
 +      return 1;
 +}
 +
 +
 +int radius_msg_add_wfa(struct radius_msg *msg, u8 subtype, const u8 *data,
 +                     size_t len)
 +{
 +      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 + 16 > buf_len)
 +              return -1;
 +
 +      os_memcpy(buf, data, data_len);
 +
 +      padlen = data_len % 16;
 +      if (padlen && data_len < buf_len) {
 +              padlen = 16 - padlen;
 +              os_memset(buf + data_len, 0, padlen);
 +              buf_len = data_len + padlen;
 +      } else {
 +              buf_len = data_len;
 +      }
 +
 +      addr[0] = secret;
 +      len[0] = secret_len;
 +      addr[1] = msg->hdr->authenticator;
 +      len[1] = 16;
 +      md5_vector(2, addr, len, hash);
 +
 +      for (i = 0; i < 16; i++)
 +              buf[i] ^= hash[i];
 +      pos = 16;
 +
 +      while (pos < buf_len) {
 +              addr[0] = secret;
 +              len[0] = secret_len;
 +              addr[1] = &buf[pos - 16];
 +              len[1] = 16;
 +              md5_vector(2, addr, len, hash);
 +
 +              for (i = 0; i < 16; i++)
 +                      buf[pos + i] ^= hash[i];
 +
 +              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, res);
 +}
 +
 +
 +int radius_msg_get_attr(struct radius_msg *msg, u8 type, u8 *buf, size_t len)
 +{
 +      struct radius_attr_hdr *attr = NULL, *tmp;
 +      size_t i, dlen;
 +
 +      for (i = 0; i < msg->attr_used; i++) {
 +              tmp = radius_get_attr_hdr(msg, i);
 +              if (tmp->type == type) {
 +                      attr = tmp;
 +                      break;
 +              }
 +      }
 +
 +      if (!attr || attr->length < sizeof(*attr))
 +              return -1;
 +
 +      dlen = attr->length - sizeof(*attr);
 +      if (buf)
 +              os_memcpy(buf, (attr + 1), dlen > len ? len : dlen);
 +      return dlen;
 +}
 +
 +
 +int radius_msg_get_attr_ptr(struct radius_msg *msg, u8 type, u8 **buf,
 +                          size_t *len, const u8 *start)
 +{
 +      size_t i;
 +      struct radius_attr_hdr *attr = NULL, *tmp;
 +
 +      for (i = 0; i < msg->attr_used; i++) {
 +              tmp = radius_get_attr_hdr(msg, i);
 +              if (tmp->type == type &&
 +                  (start == NULL || (u8 *) tmp > start)) {
 +                      attr = tmp;
 +                      break;
 +              }
 +      }
 +
 +      if (!attr || attr->length < sizeof(*attr))
 +              return -1;
 +
 +      *buf = (u8 *) (attr + 1);
 +      *len = attr->length - sizeof(*attr);
 +      return 0;
 +}
 +
 +
 +int radius_msg_count_attr(struct radius_msg *msg, u8 type, int min_len)
 +{
 +      size_t i;
 +      int count;
 +
 +      for (count = 0, i = 0; i < msg->attr_used; i++) {
 +              struct radius_attr_hdr *attr = radius_get_attr_hdr(msg, i);
 +              if (attr->type == type &&
 +                  attr->length >= sizeof(struct radius_attr_hdr) + min_len)
 +                      count++;
 +      }
 +
 +      return count;
 +}
 +
 +
 +struct radius_tunnel_attrs {
 +      int tag_used;
 +      int type; /* Tunnel-Type */
 +      int medium_type; /* Tunnel-Medium-Type */
 +      int vlanid;
 +};
 +
 +
 +/**
 + * radius_msg_get_vlanid - Parse RADIUS attributes for VLAN tunnel information
 + * @msg: RADIUS message
-       return -1;
++ * Returns: VLAN ID for the first tunnel configuration or 0 if none is found
 + */
 +int radius_msg_get_vlanid(struct radius_msg *msg)
 +{
 +      struct radius_tunnel_attrs tunnel[RADIUS_TUNNEL_TAGS], *tun;
 +      size_t i;
 +      struct radius_attr_hdr *attr = NULL;
 +      const u8 *data;
 +      char buf[10];
 +      size_t dlen;
 +
 +      os_memset(&tunnel, 0, sizeof(tunnel));
 +
 +      for (i = 0; i < msg->attr_used; i++) {
 +              attr = radius_get_attr_hdr(msg, i);
 +              if (attr->length < sizeof(*attr))
 +                      return -1;
 +              data = (const u8 *) (attr + 1);
 +              dlen = attr->length - sizeof(*attr);
 +              if (attr->length < 3)
 +                      continue;
 +              if (data[0] >= RADIUS_TUNNEL_TAGS)
 +                      tun = &tunnel[0];
 +              else
 +                      tun = &tunnel[data[0]];
 +
 +              switch (attr->type) {
 +              case RADIUS_ATTR_TUNNEL_TYPE:
 +                      if (attr->length != 6)
 +                              break;
 +                      tun->tag_used++;
 +                      tun->type = WPA_GET_BE24(data + 1);
 +                      break;
 +              case RADIUS_ATTR_TUNNEL_MEDIUM_TYPE:
 +                      if (attr->length != 6)
 +                              break;
 +                      tun->tag_used++;
 +                      tun->medium_type = WPA_GET_BE24(data + 1);
 +                      break;
 +              case RADIUS_ATTR_TUNNEL_PRIVATE_GROUP_ID:
 +                      if (data[0] < RADIUS_TUNNEL_TAGS) {
 +                              data++;
 +                              dlen--;
 +                      }
 +                      if (dlen >= sizeof(buf))
 +                              break;
 +                      os_memcpy(buf, data, dlen);
 +                      buf[dlen] = '\0';
 +                      tun->tag_used++;
 +                      tun->vlanid = atoi(buf);
 +                      break;
 +              }
 +      }
 +
 +      for (i = 0; i < RADIUS_TUNNEL_TAGS; i++) {
 +              tun = &tunnel[i];
 +              if (tun->tag_used &&
 +                  tun->type == RADIUS_TUNNEL_TYPE_VLAN &&
 +                  tun->medium_type == RADIUS_TUNNEL_MEDIUM_TYPE_802 &&
 +                  tun->vlanid > 0)
 +                      return tun->vlanid;
 +      }
 +
++      return 0;
 +}
 +
 +
 +/**
 + * radius_msg_get_tunnel_password - Parse RADIUS attribute Tunnel-Password
 + * @msg: Received RADIUS message
 + * @keylen: Length of returned password
 + * @secret: RADIUS shared secret
 + * @secret_len: Length of secret
 + * @sent_msg: Sent RADIUS message
 + * @n: Number of password attribute to return (starting with 0)
 + * Returns: Pointer to n-th password (free with os_free) or %NULL
 + */
 +char * radius_msg_get_tunnel_password(struct radius_msg *msg, int *keylen,
 +                                    const u8 *secret, size_t secret_len,
 +                                    struct radius_msg *sent_msg, size_t n)
 +{
 +      u8 *buf = NULL;
 +      size_t buflen;
 +      const u8 *salt;
 +      u8 *str;
 +      const u8 *addr[3];
 +      size_t len[3];
 +      u8 hash[16];
 +      u8 *pos;
 +      size_t i, j = 0;
 +      struct radius_attr_hdr *attr;
 +      const u8 *data;
 +      size_t dlen;
 +      const u8 *fdata = NULL; /* points to found item */
 +      size_t fdlen = -1;
 +      char *ret = NULL;
 +
 +      /* find n-th valid Tunnel-Password attribute */
 +      for (i = 0; i < msg->attr_used; i++) {
 +              attr = radius_get_attr_hdr(msg, i);
 +              if (attr == NULL ||
 +                  attr->type != RADIUS_ATTR_TUNNEL_PASSWORD) {
 +                      continue;
 +              }
 +              if (attr->length <= 5)
 +                      continue;
 +              data = (const u8 *) (attr + 1);
 +              dlen = attr->length - sizeof(*attr);
 +              if (dlen <= 3 || dlen % 16 != 3)
 +                      continue;
 +              j++;
 +              if (j <= n)
 +                      continue;
 +
 +              fdata = data;
 +              fdlen = dlen;
 +              break;
 +      }
 +      if (fdata == NULL)
 +              goto out;
 +
 +      /* alloc writable memory for decryption */
 +      buf = os_malloc(fdlen);
 +      if (buf == NULL)
 +              goto out;
 +      os_memcpy(buf, fdata, fdlen);
 +      buflen = fdlen;
 +
 +      /* init pointers */
 +      salt = buf + 1;
 +      str = buf + 3;
 +
 +      /* decrypt blocks */
 +      pos = buf + buflen - 16; /* last block */
 +      while (pos >= str + 16) { /* all but the first block */
 +              addr[0] = secret;
 +              len[0] = secret_len;
 +              addr[1] = pos - 16;
 +              len[1] = 16;
 +              md5_vector(2, addr, len, hash);
 +
 +              for (i = 0; i < 16; i++)
 +                      pos[i] ^= hash[i];
 +
 +              pos -= 16;
 +      }
 +
 +      /* decrypt first block */
 +      if (str != pos)
 +              goto out;
 +      addr[0] = secret;
 +      len[0] = secret_len;
 +      addr[1] = sent_msg->hdr->authenticator;
 +      len[1] = 16;
 +      addr[2] = salt;
 +      len[2] = 2;
 +      md5_vector(3, addr, len, hash);
 +
 +      for (i = 0; i < 16; i++)
 +              pos[i] ^= hash[i];
 +
 +      /* derive plaintext length from first subfield */
 +      *keylen = (unsigned char) str[0];
 +      if ((u8 *) (str + *keylen) >= (u8 *) (buf + buflen)) {
 +              /* decryption error - invalid key length */
 +              goto out;
 +      }
 +      if (*keylen == 0) {
 +              /* empty password */
 +              goto out;
 +      }
 +
 +      /* copy passphrase into new buffer */
 +      ret = os_malloc(*keylen);
 +      if (ret)
 +              os_memcpy(ret, str + 1, *keylen);
 +
 +out:
 +      /* return new buffer */
 +      os_free(buf);
 +      return ret;
 +}
 +
 +
 +void radius_free_class(struct radius_class_data *c)
 +{
 +      size_t i;
 +      if (c == NULL)
 +              return;
 +      for (i = 0; i < c->count; i++)
 +              os_free(c->attr[i].data);
 +      os_free(c->attr);
 +      c->attr = NULL;
 +      c->count = 0;
 +}
 +
 +
 +int radius_copy_class(struct radius_class_data *dst,
 +                    const struct radius_class_data *src)
 +{
 +      size_t i;
 +
 +      if (src->attr == NULL)
 +              return 0;
 +
 +      dst->attr = os_calloc(src->count, sizeof(struct radius_attr_data));
 +      if (dst->attr == NULL)
 +              return -1;
 +
 +      dst->count = 0;
 +
 +      for (i = 0; i < src->count; i++) {
 +              dst->attr[i].data = os_malloc(src->attr[i].len);
 +              if (dst->attr[i].data == NULL)
 +                      break;
 +              dst->count++;
 +              os_memcpy(dst->attr[i].data, src->attr[i].data,
 +                        src->attr[i].len);
 +              dst->attr[i].len = src->attr[i].len;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +u8 radius_msg_find_unlisted_attr(struct radius_msg *msg, u8 *attrs)
 +{
 +      size_t i, j;
 +      struct radius_attr_hdr *attr;
 +
 +      for (i = 0; i < msg->attr_used; i++) {
 +              attr = radius_get_attr_hdr(msg, i);
 +
 +              for (j = 0; attrs[j]; j++) {
 +                      if (attr->type == attrs[j])
 +                              break;
 +              }
 +
 +              if (attrs[j] == 0)
 +                      return attr->type; /* unlisted attr */
 +      }
 +
 +      return 0;
 +}
index 39ceea879cafeeffd2059c51dbaac5f9f9c63975,0000000000000000000000000000000000000000..b7d991bbd097956ccb78bb1987297bf0104c22eb
mode 100644,000000..100644
--- /dev/null
@@@ -1,410 -1,0 +1,410 @@@
-               if ((unsigned int) abs(now.sec - timestamp) >
 +/*
 + * RADIUS Dynamic Authorization Server (DAS) (RFC 5176)
 + * 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.
 + */
 +
 +#include "includes.h"
 +#include <net/if.h>
 +
 +#include "utils/common.h"
 +#include "utils/eloop.h"
 +#include "utils/ip_addr.h"
 +#include "radius.h"
 +#include "radius_das.h"
 +
 +
 +struct radius_das_data {
 +      int sock;
 +      u8 *shared_secret;
 +      size_t shared_secret_len;
 +      struct hostapd_ip_addr client_addr;
 +      unsigned int time_window;
 +      int require_event_timestamp;
 +      void *ctx;
 +      enum radius_das_res (*disconnect)(void *ctx,
 +                                        struct radius_das_attrs *attr);
 +};
 +
 +
 +static struct radius_msg * radius_das_disconnect(struct radius_das_data *das,
 +                                               struct radius_msg *msg,
 +                                               const char *abuf,
 +                                               int from_port)
 +{
 +      struct radius_hdr *hdr;
 +      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;
 +      u8 attr;
 +      enum radius_das_res res;
 +      struct radius_das_attrs attrs;
 +      u8 *buf;
 +      size_t len;
 +      char tmp[100];
 +      u8 sta_addr[ETH_ALEN];
 +
 +      hdr = radius_msg_get_hdr(msg);
 +
 +      attr = radius_msg_find_unlisted_attr(msg, allowed);
 +      if (attr) {
 +              wpa_printf(MSG_INFO, "DAS: Unsupported attribute %u in "
 +                         "Disconnect-Request from %s:%d", attr,
 +                         abuf, from_port);
 +              error = 401;
 +              goto fail;
 +      }
 +
 +      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))
 +                      len = sizeof(tmp) - 1;
 +              os_memcpy(tmp, buf, len);
 +              tmp[len] = '\0';
 +              if (hwaddr_aton2(tmp, sta_addr) < 0) {
 +                      wpa_printf(MSG_INFO, "DAS: Invalid Calling-Station-Id "
 +                                 "'%s' from %s:%d", tmp, abuf, from_port);
 +                      error = 407;
 +                      goto fail;
 +              }
 +              attrs.sta_addr = sta_addr;
 +      }
 +
 +      if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_USER_NAME,
 +                                  &buf, &len, NULL) == 0) {
 +              attrs.user_name = buf;
 +              attrs.user_name_len = len;
 +      }
 +
 +      if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_ACCT_SESSION_ID,
 +                                  &buf, &len, NULL) == 0) {
 +              attrs.acct_session_id = buf;
 +              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;
 +              attrs.cui_len = len;
 +      }
 +
 +      res = das->disconnect(das->ctx, &attrs);
 +      switch (res) {
 +      case RADIUS_DAS_NAS_MISMATCH:
 +              wpa_printf(MSG_INFO, "DAS: NAS mismatch from %s:%d",
 +                         abuf, from_port);
 +              error = 403;
 +              break;
 +      case RADIUS_DAS_SESSION_NOT_FOUND:
 +              wpa_printf(MSG_INFO, "DAS: Session not found for request from "
 +                         "%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;
 +      }
 +
 +fail:
 +      reply = radius_msg_new(error ? RADIUS_CODE_DISCONNECT_NAK :
 +                             RADIUS_CODE_DISCONNECT_ACK, hdr->identifier);
 +      if (reply == NULL)
 +              return NULL;
 +
 +      if (error) {
 +              if (!radius_msg_add_attr_int32(reply, RADIUS_ATTR_ERROR_CAUSE,
 +                                             error)) {
 +                      radius_msg_free(reply);
 +                      return NULL;
 +              }
 +      }
 +
 +      return reply;
 +}
 +
 +
 +static void radius_das_receive(int sock, void *eloop_ctx, void *sock_ctx)
 +{
 +      struct radius_das_data *das = eloop_ctx;
 +      u8 buf[1500];
 +      union {
 +              struct sockaddr_storage ss;
 +              struct sockaddr_in sin;
 +#ifdef CONFIG_IPV6
 +              struct sockaddr_in6 sin6;
 +#endif /* CONFIG_IPV6 */
 +      } from;
 +      char abuf[50];
 +      int from_port = 0;
 +      socklen_t fromlen;
 +      int len;
 +      struct radius_msg *msg, *reply = NULL;
 +      struct radius_hdr *hdr;
 +      struct wpabuf *rbuf;
 +      u32 val;
 +      int res;
 +      struct os_time now;
 +
 +      fromlen = sizeof(from);
 +      len = recvfrom(sock, buf, sizeof(buf), 0,
 +                     (struct sockaddr *) &from.ss, &fromlen);
 +      if (len < 0) {
 +              wpa_printf(MSG_ERROR, "DAS: recvfrom: %s", strerror(errno));
 +              return;
 +      }
 +
 +      os_strlcpy(abuf, inet_ntoa(from.sin.sin_addr), sizeof(abuf));
 +      from_port = ntohs(from.sin.sin_port);
 +
 +      wpa_printf(MSG_DEBUG, "DAS: Received %d bytes from %s:%d",
 +                 len, abuf, from_port);
 +      if (das->client_addr.u.v4.s_addr != from.sin.sin_addr.s_addr) {
 +              wpa_printf(MSG_DEBUG, "DAS: Drop message from unknown client");
 +              return;
 +      }
 +
 +      msg = radius_msg_parse(buf, len);
 +      if (msg == NULL) {
 +              wpa_printf(MSG_DEBUG, "DAS: Parsing incoming RADIUS packet "
 +                         "from %s:%d failed", abuf, from_port);
 +              return;
 +      }
 +
 +      if (wpa_debug_level <= MSG_MSGDUMP)
 +              radius_msg_dump(msg);
 +
 +      if (radius_msg_verify_das_req(msg, das->shared_secret,
 +                                     das->shared_secret_len)) {
 +              wpa_printf(MSG_DEBUG, "DAS: Invalid authenticator in packet "
 +                         "from %s:%d - drop", abuf, from_port);
 +              goto fail;
 +      }
 +
 +      os_get_time(&now);
 +      res = radius_msg_get_attr(msg, RADIUS_ATTR_EVENT_TIMESTAMP,
 +                                (u8 *) &val, 4);
 +      if (res == 4) {
 +              u32 timestamp = ntohl(val);
++              if ((unsigned int) abs((int) (now.sec - timestamp)) >
 +                  das->time_window) {
 +                      wpa_printf(MSG_DEBUG, "DAS: Unacceptable "
 +                                 "Event-Timestamp (%u; local time %u) in "
 +                                 "packet from %s:%d - drop",
 +                                 timestamp, (unsigned int) now.sec,
 +                                 abuf, from_port);
 +                      goto fail;
 +              }
 +      } else if (das->require_event_timestamp) {
 +              wpa_printf(MSG_DEBUG, "DAS: Missing Event-Timestamp in packet "
 +                         "from %s:%d - drop", abuf, from_port);
 +              goto fail;
 +      }
 +
 +      hdr = radius_msg_get_hdr(msg);
 +
 +      switch (hdr->code) {
 +      case RADIUS_CODE_DISCONNECT_REQUEST:
 +              reply = radius_das_disconnect(das, msg, abuf, from_port);
 +              break;
 +      case RADIUS_CODE_COA_REQUEST:
 +              /* TODO */
 +              reply = radius_msg_new(RADIUS_CODE_COA_NAK,
 +                                     hdr->identifier);
 +              if (reply == NULL)
 +                      break;
 +
 +              /* Unsupported Service */
 +              if (!radius_msg_add_attr_int32(reply, RADIUS_ATTR_ERROR_CAUSE,
 +                                             405)) {
 +                      radius_msg_free(reply);
 +                      reply = NULL;
 +                      break;
 +              }
 +              break;
 +      default:
 +              wpa_printf(MSG_DEBUG, "DAS: Unexpected RADIUS code %u in "
 +                         "packet from %s:%d",
 +                         hdr->code, abuf, from_port);
 +      }
 +
 +      if (reply) {
 +              wpa_printf(MSG_DEBUG, "DAS: Reply to %s:%d", abuf, from_port);
 +
 +              if (!radius_msg_add_attr_int32(reply,
 +                                             RADIUS_ATTR_EVENT_TIMESTAMP,
 +                                             now.sec)) {
 +                      wpa_printf(MSG_DEBUG, "DAS: Failed to add "
 +                                 "Event-Timestamp attribute");
 +              }
 +
 +              if (radius_msg_finish_das_resp(reply, das->shared_secret,
 +                                             das->shared_secret_len, hdr) <
 +                  0) {
 +                      wpa_printf(MSG_DEBUG, "DAS: Failed to add "
 +                                 "Message-Authenticator attribute");
 +              }
 +
 +              if (wpa_debug_level <= MSG_MSGDUMP)
 +                      radius_msg_dump(reply);
 +
 +              rbuf = radius_msg_get_buf(reply);
 +              res = sendto(das->sock, wpabuf_head(rbuf),
 +                           wpabuf_len(rbuf), 0,
 +                           (struct sockaddr *) &from.ss, fromlen);
 +              if (res < 0) {
 +                      wpa_printf(MSG_ERROR, "DAS: sendto(to %s:%d): %s",
 +                                 abuf, from_port, strerror(errno));
 +              }
 +      }
 +
 +fail:
 +      radius_msg_free(msg);
 +      radius_msg_free(reply);
 +}
 +
 +
 +static int radius_das_open_socket(int port)
 +{
 +      int s;
 +      struct sockaddr_in addr;
 +
 +      s = socket(PF_INET, SOCK_DGRAM, 0);
 +      if (s < 0) {
 +              wpa_printf(MSG_INFO, "RADIUS DAS: socket: %s", strerror(errno));
 +              return -1;
 +      }
 +
 +      os_memset(&addr, 0, sizeof(addr));
 +      addr.sin_family = AF_INET;
 +      addr.sin_port = htons(port);
 +      if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
 +              wpa_printf(MSG_INFO, "RADIUS DAS: bind: %s", strerror(errno));
 +              close(s);
 +              return -1;
 +      }
 +
 +      return s;
 +}
 +
 +
 +struct radius_das_data *
 +radius_das_init(struct radius_das_conf *conf)
 +{
 +      struct radius_das_data *das;
 +
 +      if (conf->port == 0 || conf->shared_secret == NULL ||
 +          conf->client_addr == NULL)
 +              return NULL;
 +
 +      das = os_zalloc(sizeof(*das));
 +      if (das == NULL)
 +              return NULL;
 +
 +      das->time_window = conf->time_window;
 +      das->require_event_timestamp = conf->require_event_timestamp;
 +      das->ctx = conf->ctx;
 +      das->disconnect = conf->disconnect;
 +
 +      os_memcpy(&das->client_addr, conf->client_addr,
 +                sizeof(das->client_addr));
 +
 +      das->shared_secret = os_malloc(conf->shared_secret_len);
 +      if (das->shared_secret == NULL) {
 +              radius_das_deinit(das);
 +              return NULL;
 +      }
 +      os_memcpy(das->shared_secret, conf->shared_secret,
 +                conf->shared_secret_len);
 +      das->shared_secret_len = conf->shared_secret_len;
 +
 +      das->sock = radius_das_open_socket(conf->port);
 +      if (das->sock < 0) {
 +              wpa_printf(MSG_ERROR, "Failed to open UDP socket for RADIUS "
 +                         "DAS");
 +              radius_das_deinit(das);
 +              return NULL;
 +      }
 +
 +      if (eloop_register_read_sock(das->sock, radius_das_receive, das, NULL))
 +      {
 +              radius_das_deinit(das);
 +              return NULL;
 +      }
 +
 +      return das;
 +}
 +
 +
 +void radius_das_deinit(struct radius_das_data *das)
 +{
 +      if (das == NULL)
 +              return;
 +
 +      if (das->sock >= 0) {
 +              eloop_unregister_read_sock(das->sock);
 +              close(das->sock);
 +      }
 +
 +      os_free(das->shared_secret);
 +      os_free(das);
 +}
index 85a485e91d93223fe567a39c84ebb7c0fecaf986,0000000000000000000000000000000000000000..744283c7dc9d739535e7f1c948205ec6adfee225
mode 100644,000000..100644
--- /dev/null
@@@ -1,2156 -1,0 +1,2168 @@@
- static struct eapol_callbacks radius_server_eapol_cb;
 +/*
 + * RADIUS authentication server
 + * 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.
 + */
 +
 +#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"
 +
 +/**
 + * RADIUS_SESSION_TIMEOUT - Session timeout in seconds
 + */
 +#define RADIUS_SESSION_TIMEOUT 60
 +
 +/**
 + * RADIUS_MAX_SESSION - Maximum number of active sessions
 + */
 +#define RADIUS_MAX_SESSION 100
 +
 +/**
 + * RADIUS_MAX_MSG_LEN - Maximum message length for incoming RADIUS messages
 + */
 +#define RADIUS_MAX_MSG_LEN 3000
 +
-               os_memcpy(data->pac_opaque_encr_key, conf->pac_opaque_encr_key,
-                         16);
++static const struct eapol_callbacks radius_server_eapol_cb;
 +
 +struct radius_client;
 +struct radius_server_data;
 +
 +/**
 + * struct radius_server_counters - RADIUS server statistics counters
 + */
 +struct radius_server_counters {
 +      u32 access_requests;
 +      u32 invalid_requests;
 +      u32 dup_access_requests;
 +      u32 access_accepts;
 +      u32 access_rejects;
 +      u32 access_challenges;
 +      u32 malformed_access_requests;
 +      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;
 +};
 +
 +/**
 + * struct radius_session - Internal RADIUS server data for a session
 + */
 +struct radius_session {
 +      struct radius_session *next;
 +      struct radius_client *client;
 +      struct radius_server_data *server;
 +      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;
 +      int last_from_port;
 +      struct sockaddr_storage last_from;
 +      socklen_t last_fromlen;
 +      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;
 +};
 +
 +/**
 + * struct radius_client - Internal RADIUS server data for a client
 + */
 +struct radius_client {
 +      struct radius_client *next;
 +      struct in_addr addr;
 +      struct in_addr mask;
 +#ifdef CONFIG_IPV6
 +      struct in6_addr addr6;
 +      struct in6_addr mask6;
 +#endif /* CONFIG_IPV6 */
 +      char *shared_secret;
 +      int shared_secret_len;
 +      struct radius_session *sessions;
 +      struct radius_server_counters counters;
 +};
 +
 +/**
 + * struct radius_server_data - Internal RADIUS server data
 + */
 +struct radius_server_data {
 +      /**
 +       * auth_sock - Socket for RADIUS authentication messages
 +       */
 +      int auth_sock;
 +
 +      /**
 +       * acct_sock - Socket for RADIUS accounting messages
 +       */
 +      int acct_sock;
 +
 +      /**
 +       * clients - List of authorized RADIUS clients
 +       */
 +      struct radius_client *clients;
 +
 +      /**
 +       * next_sess_id - Next session identifier
 +       */
 +      unsigned int next_sess_id;
 +
 +      /**
 +       * conf_ctx - Context pointer for callbacks
 +       *
 +       * This is used as the ctx argument in get_eap_user() calls.
 +       */
 +      void *conf_ctx;
 +
 +      /**
 +       * num_sess - Number of active sessions
 +       */
 +      int num_sess;
 +
 +      /**
 +       * eap_sim_db_priv - EAP-SIM/AKA database context
 +       *
 +       * This is passed to the EAP-SIM/AKA server implementation as a
 +       * callback context.
 +       */
 +      void *eap_sim_db_priv;
 +
 +      /**
 +       * ssl_ctx - TLS context
 +       *
 +       * This is passed to the EAP server implementation as a callback
 +       * context for TLS operations.
 +       */
 +      void *ssl_ctx;
 +
 +      /**
 +       * pac_opaque_encr_key - PAC-Opaque encryption key for EAP-FAST
 +       *
 +       * This parameter is used to set a key for EAP-FAST to encrypt the
 +       * PAC-Opaque data. It can be set to %NULL if EAP-FAST is not used. If
 +       * set, must point to a 16-octet key.
 +       */
 +      u8 *pac_opaque_encr_key;
 +
 +      /**
 +       * eap_fast_a_id - EAP-FAST authority identity (A-ID)
 +       *
 +       * If EAP-FAST is not used, this can be set to %NULL. In theory, this
 +       * is a variable length field, but due to some existing implementations
 +       * requiring A-ID to be 16 octets in length, it is recommended to use
 +       * that length for the field to provide interoperability with deployed
 +       * peer implementations.
 +       */
 +      u8 *eap_fast_a_id;
 +
 +      /**
 +       * eap_fast_a_id_len - Length of eap_fast_a_id buffer in octets
 +       */
 +      size_t eap_fast_a_id_len;
 +
 +      /**
 +       * eap_fast_a_id_info - EAP-FAST authority identifier information
 +       *
 +       * This A-ID-Info contains a user-friendly name for the A-ID. For
 +       * example, this could be the enterprise and server names in
 +       * human-readable format. This field is encoded as UTF-8. If EAP-FAST
 +       * is not used, this can be set to %NULL.
 +       */
 +      char *eap_fast_a_id_info;
 +
 +      /**
 +       * eap_fast_prov - EAP-FAST provisioning modes
 +       *
 +       * 0 = provisioning disabled, 1 = only anonymous provisioning allowed,
 +       * 2 = only authenticated provisioning allowed, 3 = both provisioning
 +       * modes allowed.
 +       */
 +      int eap_fast_prov;
 +
 +      /**
 +       * pac_key_lifetime - EAP-FAST PAC-Key lifetime in seconds
 +       *
 +       * This is the hard limit on how long a provisioned PAC-Key can be
 +       * used.
 +       */
 +      int pac_key_lifetime;
 +
 +      /**
 +       * pac_key_refresh_time - EAP-FAST PAC-Key refresh time in seconds
 +       *
 +       * This is a soft limit on the PAC-Key. The server will automatically
 +       * generate a new PAC-Key when this number of seconds (or fewer) of the
 +       * lifetime remains.
 +       */
 +      int pac_key_refresh_time;
 +
 +      /**
 +       * eap_sim_aka_result_ind - EAP-SIM/AKA protected success indication
 +       *
 +       * This controls whether the protected success/failure indication
 +       * (AT_RESULT_IND) is used with EAP-SIM and EAP-AKA.
 +       */
 +      int eap_sim_aka_result_ind;
 +
 +      /**
 +       * tnc - Trusted Network Connect (TNC)
 +       *
 +       * This controls whether TNC is enabled and will be required before the
 +       * peer is allowed to connect. Note: This is only used with EAP-TTLS
 +       * and EAP-FAST. If any other EAP method is enabled, the peer will be
 +       * allowed to connect without TNC.
 +       */
 +      int tnc;
 +
 +      /**
 +       * pwd_group - The D-H group assigned for EAP-pwd
 +       *
 +       * If EAP-pwd is not used it can be set to zero.
 +       */
 +      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 */
 +
++      unsigned int tls_session_lifetime;
++
 +      /**
 +       * wps - Wi-Fi Protected Setup context
 +       *
 +       * If WPS is used with an external RADIUS server (which is quite
 +       * unlikely configuration), this is used to provide a pointer to WPS
 +       * context data. Normally, this can be set to %NULL.
 +       */
 +      struct wps_context *wps;
 +
 +      /**
 +       * ipv6 - Whether to enable IPv6 support in the RADIUS server
 +       */
 +      int ipv6;
 +
 +      /**
 +       * start_time - Timestamp of server start
 +       */
 +      struct os_reltime start_time;
 +
 +      /**
 +       * counters - Statistics counters for server operations
 +       *
 +       * These counters are the sum over all clients.
 +       */
 +      struct radius_server_counters counters;
 +
 +      /**
 +       * get_eap_user - Callback for fetching EAP user information
 +       * @ctx: Context data from conf_ctx
 +       * @identity: User identity
 +       * @identity_len: identity buffer length in octets
 +       * @phase2: Whether this is for Phase 2 identity
 +       * @user: Data structure for filling in the user information
 +       * Returns: 0 on success, -1 on failure
 +       *
 +       * This is used to fetch information from user database. The callback
 +       * will fill in information about allowed EAP methods and the user
 +       * password. The password field will be an allocated copy of the
 +       * password data and RADIUS server will free it after use.
 +       */
 +      int (*get_eap_user)(void *ctx, const u8 *identity, size_t identity_len,
 +                          int phase2, struct eap_user *user);
 +
 +      /**
 +       * eap_req_id_text - Optional data for EAP-Request/Identity
 +       *
 +       * This can be used to configure an optional, displayable message that
 +       * will be sent in EAP-Request/Identity. This string can contain an
 +       * ASCII-0 character (nul) to separate network infromation per RFC
 +       * 4284. The actual string length is explicit provided in
 +       * eap_req_id_text_len since nul character will not be used as a string
 +       * terminator.
 +       */
 +      char *eap_req_id_text;
 +
 +      /**
 +       * eap_req_id_text_len - Length of eap_req_id_text buffer in octets
 +       */
 +      size_t eap_req_id_text_len;
 +
 +      /*
 +       * msg_ctx - Context data for wpa_msg() calls
 +       */
 +      void *msg_ctx;
 +
 +#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 */
 +};
 +
 +
 +#define RADIUS_DEBUG(args...) \
 +wpa_printf(MSG_DEBUG, "RADIUS SRV: " args)
 +#define RADIUS_ERROR(args...) \
 +wpa_printf(MSG_ERROR, "RADIUS SRV: " args)
 +#define RADIUS_DUMP(args...) \
 +wpa_hexdump(MSG_MSGDUMP, "RADIUS SRV: " args)
 +#define RADIUS_DUMP_ASCII(args...) \
 +wpa_hexdump_ascii(MSG_MSGDUMP, "RADIUS SRV: " args)
 +
 +
 +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,
 +                       int ipv6)
 +{
 +      struct radius_client *client = data->clients;
 +
 +      while (client) {
 +#ifdef CONFIG_IPV6
 +              if (ipv6) {
 +                      struct in6_addr *addr6;
 +                      int i;
 +
 +                      addr6 = (struct in6_addr *) addr;
 +                      for (i = 0; i < 16; i++) {
 +                              if ((addr6->s6_addr[i] &
 +                                   client->mask6.s6_addr[i]) !=
 +                                  (client->addr6.s6_addr[i] &
 +                                   client->mask6.s6_addr[i])) {
 +                                      i = 17;
 +                                      break;
 +                              }
 +                      }
 +                      if (i == 16) {
 +                              break;
 +                      }
 +              }
 +#endif /* CONFIG_IPV6 */
 +              if (!ipv6 && (client->addr.s_addr & client->mask.s_addr) ==
 +                  (addr->s_addr & client->mask.s_addr)) {
 +                      break;
 +              }
 +
 +              client = client->next;
 +      }
 +
 +      return client;
 +}
 +
 +
 +static struct radius_session *
 +radius_server_get_session(struct radius_client *client, unsigned int sess_id)
 +{
 +      struct radius_session *sess = client->sessions;
 +
 +      while (sess) {
 +              if (sess->sess_id == sess_id) {
 +                      break;
 +              }
 +              sess = sess->next;
 +      }
 +
 +      return sess;
 +}
 +
 +
 +static void radius_server_session_free(struct radius_server_data *data,
 +                                     struct radius_session *sess)
 +{
 +      eloop_cancel_timeout(radius_server_session_timeout, data, sess);
 +      eloop_cancel_timeout(radius_server_session_remove_timeout, data, sess);
 +      eap_server_sm_deinit(sess->eap);
 +      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--;
 +}
 +
 +
 +static void radius_server_session_remove(struct radius_server_data *data,
 +                                       struct radius_session *sess)
 +{
 +      struct radius_client *client = sess->client;
 +      struct radius_session *session, *prev;
 +
 +      eloop_cancel_timeout(radius_server_session_remove_timeout, data, sess);
 +
 +      prev = NULL;
 +      session = client->sessions;
 +      while (session) {
 +              if (session == sess) {
 +                      if (prev == NULL) {
 +                              client->sessions = sess->next;
 +                      } else {
 +                              prev->next = sess->next;
 +                      }
 +                      radius_server_session_free(data, sess);
 +                      break;
 +              }
 +              prev = session;
 +              session = session->next;
 +      }
 +}
 +
 +
 +static void radius_server_session_remove_timeout(void *eloop_ctx,
 +                                               void *timeout_ctx)
 +{
 +      struct radius_server_data *data = eloop_ctx;
 +      struct radius_session *sess = timeout_ctx;
 +      RADIUS_DEBUG("Removing completed session 0x%x", sess->sess_id);
 +      radius_server_session_remove(data, sess);
 +}
 +
 +
 +static void radius_server_session_timeout(void *eloop_ctx, void *timeout_ctx)
 +{
 +      struct radius_server_data *data = eloop_ctx;
 +      struct radius_session *sess = timeout_ctx;
 +
 +      RADIUS_DEBUG("Timing out authentication session 0x%x", sess->sess_id);
 +      radius_server_session_remove(data, sess);
 +}
 +
 +
 +static struct radius_session *
 +radius_server_new_session(struct radius_server_data *data,
 +                        struct radius_client *client)
 +{
 +      struct radius_session *sess;
 +
 +      if (data->num_sess >= RADIUS_MAX_SESSION) {
 +              RADIUS_DEBUG("Maximum number of existing session - no room "
 +                           "for a new session");
 +              return NULL;
 +      }
 +
 +      sess = os_zalloc(sizeof(*sess));
 +      if (sess == NULL)
 +              return NULL;
 +
 +      sess->server = data;
 +      sess->client = client;
 +      sess->sess_id = data->next_sess_id++;
 +      sess->next = client->sessions;
 +      client->sessions = sess;
 +      eloop_register_timeout(RADIUS_SESSION_TIMEOUT, 0,
 +                             radius_server_session_timeout, data, sess);
 +      data->num_sess++;
 +      return sess;
 +}
 +
 +
 +#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, 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");
 +
 +      if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_USER_NAME, &user,
 +                                  &user_len, NULL) < 0) {
 +              RADIUS_DEBUG("Could not get User-Name");
 +              return NULL;
 +      }
 +      RADIUS_DUMP_ASCII("User-Name", user, user_len);
 +
 +      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("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;
 +      eap_conf.eap_sim_db_priv = data->eap_sim_db_priv;
 +      eap_conf.backend_auth = TRUE;
 +      eap_conf.eap_server = 1;
 +      eap_conf.pac_opaque_encr_key = data->pac_opaque_encr_key;
 +      eap_conf.eap_fast_a_id = data->eap_fast_a_id;
 +      eap_conf.eap_fast_a_id_len = data->eap_fast_a_id_len;
 +      eap_conf.eap_fast_a_id_info = data->eap_fast_a_id_info;
 +      eap_conf.eap_fast_prov = data->eap_fast_prov;
 +      eap_conf.pac_key_lifetime = data->pac_key_lifetime;
 +      eap_conf.pac_key_refresh_time = data->pac_key_refresh_time;
 +      eap_conf.eap_sim_aka_result_ind = data->eap_sim_aka_result_ind;
 +      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;
++      eap_conf.tls_session_lifetime = data->tls_session_lifetime;
 +      radius_server_testing_options(sess, &eap_conf);
 +      sess->eap = eap_server_sm_init(sess, &radius_server_eapol_cb,
 +                                     &eap_conf);
 +      if (sess->eap == NULL) {
 +              RADIUS_DEBUG("Failed to initialize EAP state machine for the "
 +                           "new session");
 +              radius_server_session_free(data, sess);
 +              return NULL;
 +      }
 +      sess->eap_if = eap_get_interface(sess->eap);
 +      sess->eap_if->eapRestart = TRUE;
 +      sess->eap_if->portEnabled = TRUE;
 +
 +      RADIUS_DEBUG("New session 0x%x initialized", sess->sess_id);
 +
 +      return sess;
 +}
 +
 +
 +static struct radius_msg *
 +radius_server_encapsulate_eap(struct radius_server_data *data,
 +                            struct radius_client *client,
 +                            struct radius_session *sess,
 +                            struct radius_msg *request)
 +{
 +      struct radius_msg *msg;
 +      int code;
 +      unsigned int sess_id;
 +      struct radius_hdr *hdr = radius_msg_get_hdr(request);
 +
 +      if (sess->eap_if->eapFail) {
 +              sess->eap_if->eapFail = FALSE;
 +              code = RADIUS_CODE_ACCESS_REJECT;
 +      } else if (sess->eap_if->eapSuccess) {
 +              sess->eap_if->eapSuccess = FALSE;
 +              code = RADIUS_CODE_ACCESS_ACCEPT;
 +      } else {
 +              sess->eap_if->eapReq = FALSE;
 +              code = RADIUS_CODE_ACCESS_CHALLENGE;
 +      }
 +
 +      msg = radius_msg_new(code, hdr->identifier);
 +      if (msg == NULL) {
 +              RADIUS_DEBUG("Failed to allocate reply message");
 +              return NULL;
 +      }
 +
 +      sess_id = htonl(sess->sess_id);
 +      if (code == RADIUS_CODE_ACCESS_CHALLENGE &&
 +          !radius_msg_add_attr(msg, RADIUS_ATTR_STATE,
 +                               (u8 *) &sess_id, sizeof(sess_id))) {
 +              RADIUS_DEBUG("Failed to add State attribute");
 +      }
 +
 +      if (sess->eap_if->eapReqData &&
 +          !radius_msg_add_eap(msg, wpabuf_head(sess->eap_if->eapReqData),
 +                              wpabuf_len(sess->eap_if->eapReqData))) {
 +              RADIUS_DEBUG("Failed to add EAP-Message attribute");
 +      }
 +
 +      if (code == RADIUS_CODE_ACCESS_ACCEPT && sess->eap_if->eapKeyData) {
 +              int len;
 +#ifdef CONFIG_RADIUS_TEST
 +              if (data->dump_msk_file) {
 +                      FILE *f;
 +                      char buf[2 * 64 + 1];
 +                      f = fopen(data->dump_msk_file, "a");
 +                      if (f) {
 +                              len = sess->eap_if->eapKeyDataLen;
 +                              if (len > 64)
 +                                      len = 64;
 +                              len = wpa_snprintf_hex(
 +                                      buf, sizeof(buf),
 +                                      sess->eap_if->eapKeyData, len);
 +                              buf[len] = '\0';
 +                              fprintf(f, "%s\n", buf);
 +                              fclose(f);
 +                      }
 +              }
 +#endif /* CONFIG_RADIUS_TEST */
 +              if (sess->eap_if->eapKeyDataLen > 64) {
 +                      len = 32;
 +              } else {
 +                      len = sess->eap_if->eapKeyDataLen / 2;
 +              }
 +              if (!radius_msg_add_mppe_keys(msg, hdr->authenticator,
 +                                            (u8 *) client->shared_secret,
 +                                            client->shared_secret_len,
 +                                            sess->eap_if->eapKeyData + len,
 +                                            len, sess->eap_if->eapKeyData,
 +                                            len)) {
 +                      RADIUS_DEBUG("Failed to add MPPE key attributes");
 +              }
 +      }
 +
 +#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) {
 +              RADIUS_DEBUG("Failed to add Message-Authenticator attribute");
 +      }
 +
 +      return msg;
 +}
 +
 +
 +static int radius_server_reject(struct radius_server_data *data,
 +                              struct radius_client *client,
 +                              struct radius_msg *request,
 +                              struct sockaddr *from, socklen_t fromlen,
 +                              const char *from_addr, int from_port)
 +{
 +      struct radius_msg *msg;
 +      int ret = 0;
 +      struct eap_hdr eapfail;
 +      struct wpabuf *buf;
 +      struct radius_hdr *hdr = radius_msg_get_hdr(request);
 +
 +      RADIUS_DEBUG("Reject invalid request from %s:%d",
 +                   from_addr, from_port);
 +
 +      msg = radius_msg_new(RADIUS_CODE_ACCESS_REJECT, hdr->identifier);
 +      if (msg == NULL) {
 +              return -1;
 +      }
 +
 +      os_memset(&eapfail, 0, sizeof(eapfail));
 +      eapfail.code = EAP_CODE_FAILURE;
 +      eapfail.identifier = 0;
 +      eapfail.length = host_to_be16(sizeof(eapfail));
 +
 +      if (!radius_msg_add_eap(msg, (u8 *) &eapfail, sizeof(eapfail))) {
 +              RADIUS_DEBUG("Failed to add EAP-Message attribute");
 +      }
 +
 +      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 -1;
 +      }
 +
 +      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");
 +      }
 +
 +      if (wpa_debug_level <= MSG_MSGDUMP) {
 +              radius_msg_dump(msg);
 +      }
 +
 +      data->counters.access_rejects++;
 +      client->counters.access_rejects++;
 +      buf = radius_msg_get_buf(msg);
 +      if (sendto(data->auth_sock, wpabuf_head(buf), wpabuf_len(buf), 0,
 +                 (struct sockaddr *) from, sizeof(*from)) < 0) {
 +              wpa_printf(MSG_INFO, "sendto[RADIUS SRV]: %s", strerror(errno));
 +              ret = -1;
 +      }
 +
 +      radius_msg_free(msg);
 +
 +      return ret;
 +}
 +
 +
 +static int radius_server_request(struct radius_server_data *data,
 +                               struct radius_msg *msg,
 +                               struct sockaddr *from, socklen_t fromlen,
 +                               struct radius_client *client,
 +                               const char *from_addr, int from_port,
 +                               struct radius_session *force_sess)
 +{
 +      struct wpabuf *eap = NULL;
 +      int res, state_included = 0;
 +      u8 statebuf[4];
 +      unsigned int state;
 +      struct radius_session *sess;
 +      struct radius_msg *reply;
 +      int is_complete = 0;
 +
 +      if (force_sess)
 +              sess = force_sess;
 +      else {
 +              res = radius_msg_get_attr(msg, RADIUS_ATTR_STATE, statebuf,
 +                                        sizeof(statebuf));
 +              state_included = res >= 0;
 +              if (res == sizeof(statebuf)) {
 +                      state = WPA_GET_BE32(statebuf);
 +                      sess = radius_server_get_session(client, state);
 +              } else {
 +                      sess = NULL;
 +              }
 +      }
 +
 +      if (sess) {
 +              RADIUS_DEBUG("Request for session 0x%x", sess->sess_id);
 +      } else if (state_included) {
 +              RADIUS_DEBUG("State attribute included but no session found");
 +              radius_server_reject(data, client, msg, from, fromlen,
 +                                   from_addr, from_port);
 +              return -1;
 +      } else {
 +              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,
 +                                           from_addr, from_port);
 +                      return -1;
 +              }
 +      }
 +
 +      if (sess->last_from_port == from_port &&
 +          sess->last_identifier == radius_msg_get_hdr(msg)->identifier &&
 +          os_memcmp(sess->last_authenticator,
 +                    radius_msg_get_hdr(msg)->authenticator, 16) == 0) {
 +              RADIUS_DEBUG("Duplicate message from %s", from_addr);
 +              data->counters.dup_access_requests++;
 +              client->counters.dup_access_requests++;
 +
 +              if (sess->last_reply) {
 +                      struct wpabuf *buf;
 +                      buf = radius_msg_get_buf(sess->last_reply);
 +                      res = sendto(data->auth_sock, wpabuf_head(buf),
 +                                   wpabuf_len(buf), 0,
 +                                   (struct sockaddr *) from, fromlen);
 +                      if (res < 0) {
 +                              wpa_printf(MSG_INFO, "sendto[RADIUS SRV]: %s",
 +                                         strerror(errno));
 +                      }
 +                      return 0;
 +              }
 +
 +              RADIUS_DEBUG("No previous reply available for duplicate "
 +                           "message");
 +              return -1;
 +      }
 +                    
 +      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);
 +              data->counters.packets_dropped++;
 +              client->counters.packets_dropped++;
 +              return -1;
 +      }
 +
 +      RADIUS_DUMP("Received EAP data", wpabuf_head(eap), wpabuf_len(eap));
 +
 +      /* FIX: if Code is Request, Success, or Failure, send Access-Reject;
 +       * RFC3579 Sect. 2.6.2.
 +       * Include EAP-Response/Nak with no preferred method if
 +       * code == request.
 +       * If code is not 1-4, discard the packet silently.
 +       * Or is this already done by the EAP state machine? */
 +
 +      wpabuf_free(sess->eap_if->eapRespData);
 +      sess->eap_if->eapRespData = eap;
 +      sess->eap_if->eapResp = TRUE;
 +      eap_server_sm_step(sess->eap);
 +
 +      if ((sess->eap_if->eapReq || sess->eap_if->eapSuccess ||
 +           sess->eap_if->eapFail) && sess->eap_if->eapReqData) {
 +              RADIUS_DUMP("EAP data from the state machine",
 +                          wpabuf_head(sess->eap_if->eapReqData),
 +                          wpabuf_len(sess->eap_if->eapReqData));
 +      } else if (sess->eap_if->eapFail) {
 +              RADIUS_DEBUG("No EAP data from the state machine, but eapFail "
 +                           "set");
 +      } else if (eap_sm_method_pending(sess->eap)) {
 +              radius_msg_free(sess->last_msg);
 +              sess->last_msg = msg;
 +              sess->last_from_port = from_port;
 +              os_free(sess->last_from_addr);
 +              sess->last_from_addr = os_strdup(from_addr);
 +              sess->last_fromlen = fromlen;
 +              os_memcpy(&sess->last_from, from, fromlen);
 +              return -2;
 +      } else {
 +              RADIUS_DEBUG("No EAP data from the state machine - ignore this"
 +                           " Access-Request silently (assuming it was a "
 +                           "duplicate)");
 +              data->counters.packets_dropped++;
 +              client->counters.packets_dropped++;
 +              return -1;
 +      }
 +
 +      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;
 +
 +              RADIUS_DEBUG("Reply to %s:%d", from_addr, from_port);
 +              if (wpa_debug_level <= MSG_MSGDUMP) {
 +                      radius_msg_dump(reply);
 +              }
 +
 +              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;
 +              case RADIUS_CODE_ACCESS_CHALLENGE:
 +                      data->counters.access_challenges++;
 +                      client->counters.access_challenges++;
 +                      break;
 +              }
 +              buf = radius_msg_get_buf(reply);
 +              res = sendto(data->auth_sock, wpabuf_head(buf),
 +                           wpabuf_len(buf), 0,
 +                           (struct sockaddr *) from, fromlen);
 +              if (res < 0) {
 +                      wpa_printf(MSG_INFO, "sendto[RADIUS SRV]: %s",
 +                                 strerror(errno));
 +              }
 +              radius_msg_free(sess->last_reply);
 +              sess->last_reply = reply;
 +              sess->last_from_port = from_port;
 +              hdr = radius_msg_get_hdr(msg);
 +              sess->last_identifier = hdr->identifier;
 +              os_memcpy(sess->last_authenticator, hdr->authenticator, 16);
 +      } else {
 +              data->counters.packets_dropped++;
 +              client->counters.packets_dropped++;
 +      }
 +
 +      if (is_complete) {
 +              RADIUS_DEBUG("Removing completed session 0x%x after timeout",
 +                           sess->sess_id);
 +              eloop_cancel_timeout(radius_server_session_remove_timeout,
 +                                   data, sess);
 +              eloop_register_timeout(10, 0,
 +                                     radius_server_session_remove_timeout,
 +                                     data, sess);
 +      }
 +
 +      return 0;
 +}
 +
 +
 +static void radius_server_receive_auth(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;
 +      struct radius_client *client = NULL;
 +      struct radius_msg *msg = NULL;
 +      char abuf[50];
 +      int from_port = 0;
 +
 +      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_requests++;
 +              goto fail;
 +      }
 +
 +      msg = radius_msg_parse(buf, len);
 +      if (msg == NULL) {
 +              RADIUS_DEBUG("Parsing incoming RADIUS frame failed");
 +              data->counters.malformed_access_requests++;
 +              client->counters.malformed_access_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_ACCESS_REQUEST) {
 +              RADIUS_DEBUG("Unexpected RADIUS code %d",
 +                           radius_msg_get_hdr(msg)->code);
 +              data->counters.unknown_types++;
 +              client->counters.unknown_types++;
 +              goto fail;
 +      }
 +
 +      data->counters.access_requests++;
 +      client->counters.access_requests++;
 +
 +      if (radius_msg_verify_msg_auth(msg, (u8 *) client->shared_secret,
 +                                     client->shared_secret_len, NULL)) {
 +              RADIUS_DEBUG("Invalid Message-Authenticator from %s", abuf);
 +              data->counters.bad_authenticators++;
 +              client->counters.bad_authenticators++;
 +              goto fail;
 +      }
 +
 +      if (radius_server_request(data, msg, (struct sockaddr *) &from,
 +                                fromlen, client, abuf, from_port, NULL) ==
 +          -2)
 +              return; /* msg was stored with the session */
 +
 +fail:
 +      radius_msg_free(msg);
 +      os_free(buf);
 +}
 +
 +
 +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;
 +#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
 +      /* Turn off Path MTU discovery on IPv4/UDP sockets. */
 +      int action = IP_PMTUDISC_DONT;
 +      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));
 +#endif
 +      return r;
 +}
 +
 +
 +static int radius_server_open_socket(int port)
 +{
 +      int s;
 +      struct sockaddr_in addr;
 +
 +      s = socket(PF_INET, SOCK_DGRAM, 0);
 +      if (s < 0) {
 +              wpa_printf(MSG_INFO, "RADIUS: socket: %s", strerror(errno));
 +              return -1;
 +      }
 +
 +      radius_server_disable_pmtu_discovery(s);
 +
 +      os_memset(&addr, 0, sizeof(addr));
 +      addr.sin_family = AF_INET;
 +      addr.sin_port = htons(port);
 +      if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
 +              wpa_printf(MSG_INFO, "RADIUS: bind: %s", strerror(errno));
 +              close(s);
 +              return -1;
 +      }
 +
 +      return s;
 +}
 +
 +
 +#ifdef CONFIG_IPV6
 +static int radius_server_open_socket6(int port)
 +{
 +      int s;
 +      struct sockaddr_in6 addr;
 +
 +      s = socket(PF_INET6, SOCK_DGRAM, 0);
 +      if (s < 0) {
 +              wpa_printf(MSG_INFO, "RADIUS: socket[IPv6]: %s",
 +                         strerror(errno));
 +              return -1;
 +      }
 +
 +      os_memset(&addr, 0, sizeof(addr));
 +      addr.sin6_family = AF_INET6;
 +      os_memcpy(&addr.sin6_addr, &in6addr_any, sizeof(in6addr_any));
 +      addr.sin6_port = htons(port);
 +      if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
 +              wpa_printf(MSG_INFO, "RADIUS: bind: %s", strerror(errno));
 +              close(s);
 +              return -1;
 +      }
 +
 +      return s;
 +}
 +#endif /* CONFIG_IPV6 */
 +
 +
 +static void radius_server_free_sessions(struct radius_server_data *data,
 +                                      struct radius_session *sessions)
 +{
 +      struct radius_session *session, *prev;
 +
 +      session = sessions;
 +      while (session) {
 +              prev = session;
 +              session = session->next;
 +              radius_server_session_free(data, prev);
 +      }
 +}
 +
 +
 +static void radius_server_free_clients(struct radius_server_data *data,
 +                                     struct radius_client *clients)
 +{
 +      struct radius_client *client, *prev;
 +
 +      client = clients;
 +      while (client) {
 +              prev = client;
 +              client = client->next;
 +
 +              radius_server_free_sessions(data, prev->sessions);
 +              os_free(prev->shared_secret);
 +              os_free(prev);
 +      }
 +}
 +
 +
 +static struct radius_client *
 +radius_server_read_clients(const char *client_file, int ipv6)
 +{
 +      FILE *f;
 +      const int buf_size = 1024;
 +      char *buf, *pos;
 +      struct radius_client *clients, *tail, *entry;
 +      int line = 0, mask, failed = 0, i;
 +      struct in_addr addr;
 +#ifdef CONFIG_IPV6
 +      struct in6_addr addr6;
 +#endif /* CONFIG_IPV6 */
 +      unsigned int val;
 +
 +      f = fopen(client_file, "r");
 +      if (f == NULL) {
 +              RADIUS_ERROR("Could not open client file '%s'", client_file);
 +              return NULL;
 +      }
 +
 +      buf = os_malloc(buf_size);
 +      if (buf == NULL) {
 +              fclose(f);
 +              return NULL;
 +      }
 +
 +      clients = tail = NULL;
 +      while (fgets(buf, buf_size, f)) {
 +              /* Configuration file format:
 +               * 192.168.1.0/24 secret
 +               * 192.168.1.2 secret
 +               * fe80::211:22ff:fe33:4455/64 secretipv6
 +               */
 +              line++;
 +              buf[buf_size - 1] = '\0';
 +              pos = buf;
 +              while (*pos != '\0' && *pos != '\n')
 +                      pos++;
 +              if (*pos == '\n')
 +                      *pos = '\0';
 +              if (*buf == '\0' || *buf == '#')
 +                      continue;
 +
 +              pos = buf;
 +              while ((*pos >= '0' && *pos <= '9') || *pos == '.' ||
 +                     (*pos >= 'a' && *pos <= 'f') || *pos == ':' ||
 +                     (*pos >= 'A' && *pos <= 'F')) {
 +                      pos++;
 +              }
 +
 +              if (*pos == '\0') {
 +                      failed = 1;
 +                      break;
 +              }
 +
 +              if (*pos == '/') {
 +                      char *end;
 +                      *pos++ = '\0';
 +                      mask = strtol(pos, &end, 10);
 +                      if ((pos == end) ||
 +                          (mask < 0 || mask > (ipv6 ? 128 : 32))) {
 +                              failed = 1;
 +                              break;
 +                      }
 +                      pos = end;
 +              } else {
 +                      mask = ipv6 ? 128 : 32;
 +                      *pos++ = '\0';
 +              }
 +
 +              if (!ipv6 && inet_aton(buf, &addr) == 0) {
 +                      failed = 1;
 +                      break;
 +              }
 +#ifdef CONFIG_IPV6
 +              if (ipv6 && inet_pton(AF_INET6, buf, &addr6) <= 0) {
 +                      if (inet_pton(AF_INET, buf, &addr) <= 0) {
 +                              failed = 1;
 +                              break;
 +                      }
 +                      /* Convert IPv4 address to IPv6 */
 +                      if (mask <= 32)
 +                              mask += (128 - 32);
 +                      os_memset(addr6.s6_addr, 0, 10);
 +                      addr6.s6_addr[10] = 0xff;
 +                      addr6.s6_addr[11] = 0xff;
 +                      os_memcpy(addr6.s6_addr + 12, (char *) &addr.s_addr,
 +                                4);
 +              }
 +#endif /* CONFIG_IPV6 */
 +
 +              while (*pos == ' ' || *pos == '\t') {
 +                      pos++;
 +              }
 +
 +              if (*pos == '\0') {
 +                      failed = 1;
 +                      break;
 +              }
 +
 +              entry = os_zalloc(sizeof(*entry));
 +              if (entry == NULL) {
 +                      failed = 1;
 +                      break;
 +              }
 +              entry->shared_secret = os_strdup(pos);
 +              if (entry->shared_secret == NULL) {
 +                      failed = 1;
 +                      os_free(entry);
 +                      break;
 +              }
 +              entry->shared_secret_len = os_strlen(entry->shared_secret);
 +              if (!ipv6) {
 +                      entry->addr.s_addr = addr.s_addr;
 +                      val = 0;
 +                      for (i = 0; i < mask; i++)
 +                              val |= 1 << (31 - i);
 +                      entry->mask.s_addr = htonl(val);
 +              }
 +#ifdef CONFIG_IPV6
 +              if (ipv6) {
 +                      int offset = mask / 8;
 +
 +                      os_memcpy(entry->addr6.s6_addr, addr6.s6_addr, 16);
 +                      os_memset(entry->mask6.s6_addr, 0xff, offset);
 +                      val = 0;
 +                      for (i = 0; i < (mask % 8); i++)
 +                              val |= 1 << (7 - i);
 +                      if (offset < 16)
 +                              entry->mask6.s6_addr[offset] = val;
 +              }
 +#endif /* CONFIG_IPV6 */
 +
 +              if (tail == NULL) {
 +                      clients = tail = entry;
 +              } else {
 +                      tail->next = entry;
 +                      tail = entry;
 +              }
 +      }
 +
 +      if (failed) {
 +              RADIUS_ERROR("Invalid line %d in '%s'", line, client_file);
 +              radius_server_free_clients(NULL, clients);
 +              clients = NULL;
 +      }
 +
 +      os_free(buf);
 +      fclose(f);
 +
 +      return clients;
 +}
 +
 +
 +/**
 + * radius_server_init - Initialize RADIUS server
 + * @conf: Configuration for the RADIUS server
 + * Returns: Pointer to private RADIUS server context or %NULL on failure
 + *
 + * This initializes a RADIUS server instance and returns a context pointer that
 + * will be used in other calls to the RADIUS server module. The server can be
 + * deinitialize by calling radius_server_deinit().
 + */
 +struct radius_server_data *
 +radius_server_init(struct radius_server_conf *conf)
 +{
 +      struct radius_server_data *data;
 +
 +#ifndef CONFIG_IPV6
 +      if (conf->ipv6) {
 +              wpa_printf(MSG_ERROR, "RADIUS server compiled without IPv6 support");
 +              return NULL;
 +      }
 +#endif /* CONFIG_IPV6 */
 +
 +      data = os_zalloc(sizeof(*data));
 +      if (data == NULL)
 +              return NULL;
 +
 +      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;
 +      data->msg_ctx = conf->msg_ctx;
 +      data->ipv6 = conf->ipv6;
 +      if (conf->pac_opaque_encr_key) {
 +              data->pac_opaque_encr_key = os_malloc(16);
- static struct eapol_callbacks radius_server_eapol_cb =
++              if (data->pac_opaque_encr_key) {
++                      os_memcpy(data->pac_opaque_encr_key,
++                                conf->pac_opaque_encr_key, 16);
++              }
 +      }
 +      if (conf->eap_fast_a_id) {
 +              data->eap_fast_a_id = os_malloc(conf->eap_fast_a_id_len);
 +              if (data->eap_fast_a_id) {
 +                      os_memcpy(data->eap_fast_a_id, conf->eap_fast_a_id,
 +                                conf->eap_fast_a_id_len);
 +                      data->eap_fast_a_id_len = conf->eap_fast_a_id_len;
 +              }
 +      }
 +      if (conf->eap_fast_a_id_info)
 +              data->eap_fast_a_id_info = os_strdup(conf->eap_fast_a_id_info);
 +      data->eap_fast_prov = conf->eap_fast_prov;
 +      data->pac_key_lifetime = conf->pac_key_lifetime;
 +      data->pac_key_refresh_time = conf->pac_key_refresh_time;
 +      data->get_eap_user = conf->get_eap_user;
 +      data->eap_sim_aka_result_ind = conf->eap_sim_aka_result_ind;
 +      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) {
 +                      os_memcpy(data->eap_req_id_text, conf->eap_req_id_text,
 +                                conf->eap_req_id_text_len);
 +                      data->eap_req_id_text_len = conf->eap_req_id_text_len;
 +              }
 +      }
 +      data->erp = conf->erp;
 +      data->erp_domain = conf->erp_domain;
++      data->tls_session_lifetime = conf->tls_session_lifetime;
 +
 +      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)
 +              data->dump_msk_file = os_strdup(conf->dump_msk_file);
 +#endif /* CONFIG_RADIUS_TEST */
 +
 +      data->clients = radius_server_read_clients(conf->client_file,
 +                                                 conf->ipv6);
 +      if (data->clients == NULL) {
 +              wpa_printf(MSG_ERROR, "No RADIUS clients configured");
 +              radius_server_deinit(data);
 +              return NULL;
 +      }
 +
 +#ifdef CONFIG_IPV6
 +      if (conf->ipv6)
 +              data->auth_sock = radius_server_open_socket6(conf->auth_port);
 +      else
 +#endif /* CONFIG_IPV6 */
 +      data->auth_sock = radius_server_open_socket(conf->auth_port);
 +      if (data->auth_sock < 0) {
 +              wpa_printf(MSG_ERROR, "Failed to open UDP socket for RADIUS authentication server");
 +              radius_server_deinit(data);
 +              return NULL;
 +      }
 +      if (eloop_register_read_sock(data->auth_sock,
 +                                   radius_server_receive_auth,
 +                                   data, NULL)) {
 +              radius_server_deinit(data);
 +              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()
 + */
 +void radius_server_deinit(struct radius_server_data *data)
 +{
 +      if (data == NULL)
 +              return;
 +
 +      if (data->auth_sock >= 0) {
 +              eloop_unregister_read_sock(data->auth_sock);
 +              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);
 +      os_free(data->eap_fast_a_id);
 +      os_free(data->eap_fast_a_id_info);
 +      os_free(data->eap_req_id_text);
 +#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);
 +}
 +
 +
 +/**
 + * radius_server_get_mib - Get RADIUS server MIB information
 + * @data: RADIUS server context from radius_server_init()
 + * @buf: Buffer for returning the MIB data in text format
 + * @buflen: buf length in octets
 + * Returns: Number of octets written into buf
 + */
 +int radius_server_get_mib(struct radius_server_data *data, char *buf,
 +                        size_t buflen)
 +{
 +      int ret, uptime;
 +      unsigned int idx;
 +      char *end, *pos;
 +      struct os_reltime now;
 +      struct radius_client *cli;
 +
 +      /* RFC 2619 - RADIUS Authentication Server MIB */
 +
 +      if (data == NULL || buflen == 0)
 +              return 0;
 +
 +      pos = buf;
 +      end = buf + buflen;
 +
 +      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,
 +                        "RADIUS-AUTH-SERVER-MIB\n"
 +                        "radiusAuthServIdent=hostapd\n"
 +                        "radiusAuthServUpTime=%d\n"
 +                        "radiusAuthServResetTime=0\n"
 +                        "radiusAuthServConfigReset=4\n",
 +                        uptime);
 +      if (os_snprintf_error(end - pos, ret)) {
 +              *pos = '\0';
 +              return pos - buf;
 +      }
 +      pos += ret;
 +
 +      ret = os_snprintf(pos, end - pos,
 +                        "radiusAuthServTotalAccessRequests=%u\n"
 +                        "radiusAuthServTotalInvalidRequests=%u\n"
 +                        "radiusAuthServTotalDupAccessRequests=%u\n"
 +                        "radiusAuthServTotalAccessAccepts=%u\n"
 +                        "radiusAuthServTotalAccessRejects=%u\n"
 +                        "radiusAuthServTotalAccessChallenges=%u\n"
 +                        "radiusAuthServTotalMalformedAccessRequests=%u\n"
 +                        "radiusAuthServTotalBadAuthenticators=%u\n"
 +                        "radiusAuthServTotalPacketsDropped=%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,
 +                        data->counters.access_accepts,
 +                        data->counters.access_rejects,
 +                        data->counters.access_challenges,
 +                        data->counters.malformed_access_requests,
 +                        data->counters.bad_authenticators,
 +                        data->counters.packets_dropped,
 +                        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;
 +      }
 +      pos += ret;
 +
 +      for (cli = data->clients, idx = 0; cli; cli = cli->next, idx++) {
 +              char abuf[50], mbuf[50];
 +#ifdef CONFIG_IPV6
 +              if (data->ipv6) {
 +                      if (inet_ntop(AF_INET6, &cli->addr6, abuf,
 +                                    sizeof(abuf)) == NULL)
 +                              abuf[0] = '\0';
 +                      if (inet_ntop(AF_INET6, &cli->mask6, mbuf,
 +                                    sizeof(mbuf)) == NULL)
 +                              mbuf[0] = '\0';
 +              }
 +#endif /* CONFIG_IPV6 */
 +              if (!data->ipv6) {
 +                      os_strlcpy(abuf, inet_ntoa(cli->addr), sizeof(abuf));
 +                      os_strlcpy(mbuf, inet_ntoa(cli->mask), sizeof(mbuf));
 +              }
 +
 +              ret = os_snprintf(pos, end - pos,
 +                                "radiusAuthClientIndex=%u\n"
 +                                "radiusAuthClientAddress=%s/%s\n"
 +                                "radiusAuthServAccessRequests=%u\n"
 +                                "radiusAuthServDupAccessRequests=%u\n"
 +                                "radiusAuthServAccessAccepts=%u\n"
 +                                "radiusAuthServAccessRejects=%u\n"
 +                                "radiusAuthServAccessChallenges=%u\n"
 +                                "radiusAuthServMalformedAccessRequests=%u\n"
 +                                "radiusAuthServBadAuthenticators=%u\n"
 +                                "radiusAuthServPacketsDropped=%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,
 +                                cli->counters.dup_access_requests,
 +                                cli->counters.access_accepts,
 +                                cli->counters.access_rejects,
 +                                cli->counters.access_challenges,
 +                                cli->counters.malformed_access_requests,
 +                                cli->counters.bad_authenticators,
 +                                cli->counters.packets_dropped,
 +                                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;
 +              }
 +              pos += ret;
 +      }
 +
 +      return pos - buf;
 +}
 +
 +
 +static int radius_server_get_eap_user(void *ctx, const u8 *identity,
 +                                    size_t identity_len, int phase2,
 +                                    struct eap_user *user)
 +{
 +      struct radius_session *sess = ctx;
 +      struct radius_server_data *data = sess->server;
 +      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;
 +      }
++
++      if (ret) {
++              RADIUS_DEBUG("%s: User-Name not found from user database",
++                           __func__);
++      }
++
 +      return ret;
 +}
 +
 +
 +static const char * radius_server_get_eap_req_id_text(void *ctx, size_t *len)
 +{
 +      struct radius_session *sess = ctx;
 +      struct radius_server_data *data = sess->server;
 +      *len = data->eap_req_id_text_len;
 +      return data->eap_req_id_text;
 +}
 +
 +
 +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 const 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 */
 +};
 +
 +
 +/**
 + * radius_server_eap_pending_cb - Pending EAP data notification
 + * @data: RADIUS server context from radius_server_init()
 + * @ctx: Pending EAP context pointer
 + *
 + * This function is used to notify EAP server module that a pending operation
 + * has been completed and processing of the EAP session can proceed.
 + */
 +void radius_server_eap_pending_cb(struct radius_server_data *data, void *ctx)
 +{
 +      struct radius_client *cli;
 +      struct radius_session *s, *sess = NULL;
 +      struct radius_msg *msg;
 +
 +      if (data == NULL)
 +              return;
 +
 +      for (cli = data->clients; cli; cli = cli->next) {
 +              for (s = cli->sessions; s; s = s->next) {
 +                      if (s->eap == ctx && s->last_msg) {
 +                              sess = s;
 +                              break;
 +                      }
 +              }
 +              if (sess)
 +                      break;
 +      }
 +
 +      if (sess == NULL) {
 +              RADIUS_DEBUG("No session matched callback ctx");
 +              return;
 +      }
 +
 +      msg = sess->last_msg;
 +      sess->last_msg = NULL;
 +      eap_sm_pending_cb(sess->eap);
 +      if (radius_server_request(data, msg,
 +                                (struct sockaddr *) &sess->last_from,
 +                                sess->last_fromlen, cli,
 +                                sess->last_from_addr,
 +                                sess->last_from_port, sess) == -2)
 +              return; /* msg was stored with the session */
 +
 +      radius_msg_free(msg);
 +}
index ca4e38c12e99502622caacbb597fd2b0feaaa079,0000000000000000000000000000000000000000..7a25802c81527f492e1dfc4312b286bdf099c1d7
mode 100644,000000..100644
--- /dev/null
@@@ -1,246 -1,0 +1,248 @@@
 +/*
 + * RADIUS authentication server
 + * Copyright (c) 2005-2009, 2011, Jouni Malinen <j@w1.fi>
 + *
 + * This software may be distributed under the terms of the BSD license.
 + * See README for more details.
 + */
 +
 +#ifndef RADIUS_SERVER_H
 +#define RADIUS_SERVER_H
 +
 +struct radius_server_data;
 +struct eap_user;
 +
 +/**
 + * struct radius_server_conf - RADIUS server configuration
 + */
 +struct radius_server_conf {
 +      /**
 +       * auth_port - UDP port to listen to as an authentication server
 +       */
 +      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
 +       * used with them in a format where each client is on its own line. The
 +       * first item on the line is the IPv4 or IPv6 address of the client
 +       * with an optional address mask to allow full network to be specified
 +       * (e.g., 192.168.1.2 or 192.168.1.0/24). This is followed by white
 +       * space (space or tabulator) and the shared secret. Lines starting
 +       * with '#' are skipped and can be used as comments.
 +       */
 +      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.
 +       */
 +      void *conf_ctx;
 +
 +      /**
 +       * eap_sim_db_priv - EAP-SIM/AKA database context
 +       *
 +       * This is passed to the EAP-SIM/AKA server implementation as a
 +       * callback context.
 +       */
 +      void *eap_sim_db_priv;
 +
 +      /**
 +       * ssl_ctx - TLS context
 +       *
 +       * This is passed to the EAP server implementation as a callback
 +       * context for TLS operations.
 +       */
 +      void *ssl_ctx;
 +
 +      /**
 +       * pac_opaque_encr_key - PAC-Opaque encryption key for EAP-FAST
 +       *
 +       * This parameter is used to set a key for EAP-FAST to encrypt the
 +       * PAC-Opaque data. It can be set to %NULL if EAP-FAST is not used. If
 +       * set, must point to a 16-octet key.
 +       */
 +      u8 *pac_opaque_encr_key;
 +
 +      /**
 +       * eap_fast_a_id - EAP-FAST authority identity (A-ID)
 +       *
 +       * If EAP-FAST is not used, this can be set to %NULL. In theory, this
 +       * is a variable length field, but due to some existing implementations
 +       * requiring A-ID to be 16 octets in length, it is recommended to use
 +       * that length for the field to provide interoperability with deployed
 +       * peer implementations.
 +       */
 +      u8 *eap_fast_a_id;
 +
 +      /**
 +       * eap_fast_a_id_len - Length of eap_fast_a_id buffer in octets
 +       */
 +      size_t eap_fast_a_id_len;
 +
 +      /**
 +       * eap_fast_a_id_info - EAP-FAST authority identifier information
 +       *
 +       * This A-ID-Info contains a user-friendly name for the A-ID. For
 +       * example, this could be the enterprise and server names in
 +       * human-readable format. This field is encoded as UTF-8. If EAP-FAST
 +       * is not used, this can be set to %NULL.
 +       */
 +      char *eap_fast_a_id_info;
 +
 +      /**
 +       * eap_fast_prov - EAP-FAST provisioning modes
 +       *
 +       * 0 = provisioning disabled, 1 = only anonymous provisioning allowed,
 +       * 2 = only authenticated provisioning allowed, 3 = both provisioning
 +       * modes allowed.
 +       */
 +      int eap_fast_prov;
 +
 +      /**
 +       * pac_key_lifetime - EAP-FAST PAC-Key lifetime in seconds
 +       *
 +       * This is the hard limit on how long a provisioned PAC-Key can be
 +       * used.
 +       */
 +      int pac_key_lifetime;
 +
 +      /**
 +       * pac_key_refresh_time - EAP-FAST PAC-Key refresh time in seconds
 +       *
 +       * This is a soft limit on the PAC-Key. The server will automatically
 +       * generate a new PAC-Key when this number of seconds (or fewer) of the
 +       * lifetime remains.
 +       */
 +      int pac_key_refresh_time;
 +
 +      /**
 +       * eap_sim_aka_result_ind - EAP-SIM/AKA protected success indication
 +       *
 +       * This controls whether the protected success/failure indication
 +       * (AT_RESULT_IND) is used with EAP-SIM and EAP-AKA.
 +       */
 +      int eap_sim_aka_result_ind;
 +
 +      /**
 +       * tnc - Trusted Network Connect (TNC)
 +       *
 +       * This controls whether TNC is enabled and will be required before the
 +       * peer is allowed to connect. Note: This is only used with EAP-TTLS
 +       * and EAP-FAST. If any other EAP method is enabled, the peer will be
 +       * allowed to connect without TNC.
 +       */
 +      int tnc;
 +
 +      /**
 +       * pwd_group - EAP-pwd D-H group
 +       *
 +       * This is used to select which D-H group to use with EAP-pwd.
 +       */
 +      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;
 +
++      unsigned int tls_session_lifetime;
++
 +      /**
 +       * wps - Wi-Fi Protected Setup context
 +       *
 +       * If WPS is used with an external RADIUS server (which is quite
 +       * unlikely configuration), this is used to provide a pointer to WPS
 +       * context data. Normally, this can be set to %NULL.
 +       */
 +      struct wps_context *wps;
 +
 +      /**
 +       * ipv6 - Whether to enable IPv6 support in the RADIUS server
 +       */
 +      int ipv6;
 +
 +      /**
 +       * get_eap_user - Callback for fetching EAP user information
 +       * @ctx: Context data from conf_ctx
 +       * @identity: User identity
 +       * @identity_len: identity buffer length in octets
 +       * @phase2: Whether this is for Phase 2 identity
 +       * @user: Data structure for filling in the user information
 +       * Returns: 0 on success, -1 on failure
 +       *
 +       * This is used to fetch information from user database. The callback
 +       * will fill in information about allowed EAP methods and the user
 +       * password. The password field will be an allocated copy of the
 +       * password data and RADIUS server will free it after use.
 +       */
 +      int (*get_eap_user)(void *ctx, const u8 *identity, size_t identity_len,
 +                          int phase2, struct eap_user *user);
 +
 +      /**
 +       * eap_req_id_text - Optional data for EAP-Request/Identity
 +       *
 +       * This can be used to configure an optional, displayable message that
 +       * will be sent in EAP-Request/Identity. This string can contain an
 +       * ASCII-0 character (nul) to separate network infromation per RFC
 +       * 4284. The actual string length is explicit provided in
 +       * eap_req_id_text_len since nul character will not be used as a string
 +       * terminator.
 +       */
 +      const char *eap_req_id_text;
 +
 +      /**
 +       * eap_req_id_text_len - Length of eap_req_id_text buffer in octets
 +       */
 +      size_t eap_req_id_text_len;
 +
 +      /*
 +       * msg_ctx - Context data for wpa_msg() calls
 +       */
 +      void *msg_ctx;
 +
 +#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,
 +                        size_t buflen);
 +
 +void radius_server_eap_pending_cb(struct radius_server_data *data, void *ctx);
 +
 +#endif /* RADIUS_SERVER_H */
index c1d7749191d751ba0544872021217b18510e99b6,0000000000000000000000000000000000000000..722c20a706f9d30dac16089ef48f44e7d3e2fcf4
mode 100644,000000..100644
--- /dev/null
@@@ -1,3009 -1,0 +1,3008 @@@
-       if (!kde->ht_capabilities ||
-           kde->ht_capabilities_len <
-           sizeof(struct ieee80211_ht_capabilities) ) {
 +/*
 + * wpa_supplicant - TDLS
 + * Copyright (c) 2010-2011, Atheros Communications
 + *
 + * 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/os.h"
 +#include "common/ieee802_11_defs.h"
++#include "common/ieee802_11_common.h"
 +#include "crypto/sha256.h"
 +#include "crypto/crypto.h"
 +#include "crypto/aes_wrap.h"
 +#include "rsn_supp/wpa.h"
 +#include "rsn_supp/wpa_ie.h"
 +#include "rsn_supp/wpa_i.h"
 +#include "drivers/driver.h"
 +#include "l2_packet/l2_packet.h"
 +
 +#ifdef CONFIG_TDLS_TESTING
 +#define TDLS_TESTING_LONG_FRAME BIT(0)
 +#define TDLS_TESTING_ALT_RSN_IE BIT(1)
 +#define TDLS_TESTING_DIFF_BSSID BIT(2)
 +#define TDLS_TESTING_SHORT_LIFETIME BIT(3)
 +#define TDLS_TESTING_WRONG_LIFETIME_RESP BIT(4)
 +#define TDLS_TESTING_WRONG_LIFETIME_CONF BIT(5)
 +#define TDLS_TESTING_LONG_LIFETIME BIT(6)
 +#define TDLS_TESTING_CONCURRENT_INIT BIT(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 */
 +
 +#define TPK_LIFETIME 43200 /* 12 hours */
 +#define TPK_M1_RETRY_COUNT 3
 +#define TPK_M1_TIMEOUT 5000 /* in milliseconds */
 +#define TPK_M2_RETRY_COUNT 10
 +#define TPK_M2_TIMEOUT 500 /* in milliseconds */
 +
 +#define TDLS_MIC_LEN          16
 +
 +#define TDLS_TIMEOUT_LEN      4
 +
 +struct wpa_tdls_ftie {
 +      u8 ie_type; /* FTIE */
 +      u8 ie_len;
 +      u8 mic_ctrl[2];
 +      u8 mic[TDLS_MIC_LEN];
 +      u8 Anonce[WPA_NONCE_LEN]; /* Responder Nonce in TDLS */
 +      u8 Snonce[WPA_NONCE_LEN]; /* Initiator Nonce in TDLS */
 +      /* followed by optional elements */
 +} STRUCT_PACKED;
 +
 +struct wpa_tdls_timeoutie {
 +      u8 ie_type; /* Timeout IE */
 +      u8 ie_len;
 +      u8 interval_type;
 +      u8 value[TDLS_TIMEOUT_LEN];
 +} STRUCT_PACKED;
 +
 +struct wpa_tdls_lnkid {
 +      u8 ie_type; /* Link Identifier IE */
 +      u8 ie_len;
 +      u8 bssid[ETH_ALEN];
 +      u8 init_sta[ETH_ALEN];
 +      u8 resp_sta[ETH_ALEN];
 +} STRUCT_PACKED;
 +
 +/* TDLS frame headers as per IEEE Std 802.11z-2010 */
 +struct wpa_tdls_frame {
 +      u8 payloadtype; /* IEEE80211_TDLS_RFTYPE */
 +      u8 category; /* Category */
 +      u8 action; /* Action (enum tdls_frame_type) */
 +} STRUCT_PACKED;
 +
 +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
 +#define IEEE80211_MAX_SUPP_RATES 32
 +
 +struct wpa_tdls_peer {
 +      struct wpa_tdls_peer *next;
 +      unsigned int reconfig_key:1;
 +      int initiator; /* whether this end was initiator for TDLS setup */
 +      u8 addr[ETH_ALEN]; /* other end MAC address */
 +      u8 inonce[WPA_NONCE_LEN]; /* Initiator Nonce */
 +      u8 rnonce[WPA_NONCE_LEN]; /* Responder Nonce */
 +      u8 rsnie_i[TDLS_MAX_IE_LEN]; /* Initiator RSN IE */
 +      size_t rsnie_i_len;
 +      u8 rsnie_p[TDLS_MAX_IE_LEN]; /* Peer RSN IE */
 +      size_t rsnie_p_len;
 +      u32 lifetime;
 +      int cipher; /* Selected cipher (WPA_CIPHER_*) */
 +      u8 dtoken;
 +
 +      struct tpk {
 +              u8 kck[16]; /* TPK-KCK */
 +              u8 tk[16]; /* TPK-TK; assuming only CCMP will be used */
 +      } tpk;
 +      int tpk_set;
 +      int tpk_success;
 +      int tpk_in_progress;
 +
 +      struct tpk_timer {
 +              u8 dest[ETH_ALEN];
 +              int count;      /* Retry Count */
 +              int timer;      /* Timeout in milliseconds */
 +              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;
 +
 +      u16 capability;
 +
 +      u8 supp_rates[IEEE80211_MAX_SUPP_RATES];
 +      size_t supp_rates_len;
 +
 +      struct ieee80211_ht_capabilities *ht_capabilities;
 +      struct ieee80211_vht_capabilities *vht_capabilities;
 +
 +      u8 qos_info;
 +
 +      u16 aid;
 +
 +      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;
 +};
 +
 +
 +static int wpa_tdls_get_privacy(struct wpa_sm *sm)
 +{
 +      /*
 +       * Get info needed from supplicant to check if the current BSS supports
 +       * security. Other than OPEN mode, rest are considered secured
 +       * WEP/WPA/WPA2 hence TDLS frames are processed for TPK handshake.
 +       */
 +      return sm->pairwise_cipher != WPA_CIPHER_NONE;
 +}
 +
 +
 +static u8 * wpa_add_ie(u8 *pos, const u8 *ie, size_t ie_len)
 +{
 +      os_memcpy(pos, ie, ie_len);
 +      return pos + ie_len;
 +}
 +
 +
 +static int wpa_tdls_del_key(struct wpa_sm *sm, struct wpa_tdls_peer *peer)
 +{
 +      if (wpa_sm_set_key(sm, WPA_ALG_NONE, peer->addr,
 +                         0, 0, NULL, 0, NULL, 0) < 0) {
 +              wpa_printf(MSG_WARNING, "TDLS: Failed to delete TPK-TK from "
 +                         "the driver");
 +              return -1;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +static int wpa_tdls_set_key(struct wpa_sm *sm, struct wpa_tdls_peer *peer)
 +{
 +      u8 key_len;
 +      u8 rsc[6];
 +      enum wpa_alg alg;
 +
 +      os_memset(rsc, 0, 6);
 +
 +      switch (peer->cipher) {
 +      case WPA_CIPHER_CCMP:
 +              alg = WPA_ALG_CCMP;
 +              key_len = 16;
 +              break;
 +      case WPA_CIPHER_NONE:
 +              wpa_printf(MSG_DEBUG, "TDLS: Pairwise Cipher Suite: "
 +                         "NONE - do not use pairwise keys");
 +              return -1;
 +      default:
 +              wpa_printf(MSG_WARNING, "TDLS: Unsupported pairwise cipher %d",
 +                         sm->pairwise_cipher);
 +              return -1;
 +      }
 +
 +      if (wpa_sm_set_key(sm, alg, peer->addr, -1, 1,
 +                         rsc, sizeof(rsc), peer->tpk.tk, key_len) < 0) {
 +              wpa_printf(MSG_WARNING, "TDLS: Failed to set TPK to the "
 +                         "driver");
 +              return -1;
 +      }
 +      return 0;
 +}
 +
 +
 +static int wpa_tdls_send_tpk_msg(struct wpa_sm *sm, const u8 *dst,
 +                               u8 action_code, u8 dialog_token,
 +                               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, 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, 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 peer_capab=%u initiator=%d "
 +                 "msg_len=%u",
 +                 MAC2STR(dest), action_code, dialog_token, status_code,
 +                 peer_capab, initiator, (unsigned int) msg_len);
 +
 +      if (wpa_tdls_send_tpk_msg(sm, dest, action_code, dialog_token,
 +                                status_code, peer_capab, initiator, msg,
 +                                msg_len)) {
 +              wpa_printf(MSG_INFO, "TDLS: Failed to send message "
 +                         "(action_code=%u)", action_code);
 +              return -1;
 +      }
 +
 +      if (action_code == WLAN_TDLS_SETUP_CONFIRM ||
 +          action_code == WLAN_TDLS_TEARDOWN ||
 +          action_code == WLAN_TDLS_DISCOVERY_REQUEST ||
 +          action_code == WLAN_TDLS_DISCOVERY_RESPONSE)
 +              return 0; /* No retries */
 +
 +      for (peer = sm->tdls; peer; peer = peer->next) {
 +              if (os_memcmp(peer->addr, dest, ETH_ALEN) == 0)
 +                      break;
 +      }
 +
 +      if (peer == NULL) {
 +              wpa_printf(MSG_INFO, "TDLS: No matching entry found for "
 +                         "retry " MACSTR, MAC2STR(dest));
 +              return 0;
 +      }
 +
 +      eloop_cancel_timeout(wpa_tdls_tpk_retry_timeout, sm, peer);
 +
 +      if (action_code == WLAN_TDLS_SETUP_RESPONSE) {
 +              peer->sm_tmr.count = TPK_M2_RETRY_COUNT;
 +              peer->sm_tmr.timer = TPK_M2_TIMEOUT;
 +      } else {
 +              peer->sm_tmr.count = TPK_M1_RETRY_COUNT;
 +              peer->sm_tmr.timer = TPK_M1_TIMEOUT;
 +      }
 +
 +      /* Copy message to resend on timeout */
 +      os_memcpy(peer->sm_tmr.dest, dest, ETH_ALEN);
 +      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);
 +      if (peer->sm_tmr.buf == NULL)
 +              return -1;
 +      os_memcpy(peer->sm_tmr.buf, msg, msg_len);
 +
 +      wpa_printf(MSG_DEBUG, "TDLS: Retry timeout registered "
 +                 "(action_code=%u)", action_code);
 +      eloop_register_timeout(peer->sm_tmr.timer / 1000,
 +                             (peer->sm_tmr.timer % 1000) * 1000,
 +                             wpa_tdls_tpk_retry_timeout, sm, peer);
 +      return 0;
 +}
 +
 +
 +static int wpa_tdls_do_teardown(struct wpa_sm *sm, struct wpa_tdls_peer *peer,
 +                              u16 reason_code)
 +{
 +      int ret;
 +
 +      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;
 +}
 +
 +
 +static void wpa_tdls_tpk_retry_timeout(void *eloop_ctx, void *timeout_ctx)
 +{
 +
 +      struct wpa_sm *sm = eloop_ctx;
 +      struct wpa_tdls_peer *peer = timeout_ctx;
 +
 +      if (peer->sm_tmr.count) {
 +              peer->sm_tmr.count--;
 +
 +              wpa_printf(MSG_INFO, "TDLS: Retrying sending of message "
 +                         "(action_code=%u)",
 +                         peer->sm_tmr.action_code);
 +
 +              if (peer->sm_tmr.buf == NULL) {
 +                      wpa_printf(MSG_INFO, "TDLS: No retry buffer available "
 +                                 "for action_code=%u",
 +                                 peer->sm_tmr.action_code);
 +                      eloop_cancel_timeout(wpa_tdls_tpk_retry_timeout, sm,
 +                                           peer);
 +                      return;
 +              }
 +
 +              /* resend TPK Handshake Message to Peer */
 +              if (wpa_tdls_send_tpk_msg(sm, peer->sm_tmr.dest,
 +                                        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 "
 +                                 "transmission");
 +              }
 +
 +              eloop_cancel_timeout(wpa_tdls_tpk_retry_timeout, sm, peer);
 +              eloop_register_timeout(peer->sm_tmr.timer / 1000,
 +                                     (peer->sm_tmr.timer % 1000) * 1000,
 +                                     wpa_tdls_tpk_retry_timeout, sm, peer);
 +      } else {
 +              eloop_cancel_timeout(wpa_tdls_tpk_retry_timeout, sm, peer);
 +
 +              wpa_printf(MSG_DEBUG, "TDLS: Sending Teardown Request");
 +              wpa_tdls_do_teardown(sm, peer,
 +                                   WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED);
 +      }
 +}
 +
 +
 +static void wpa_tdls_tpk_retry_timeout_cancel(struct wpa_sm *sm,
 +                                            struct wpa_tdls_peer *peer,
 +                                            u8 action_code)
 +{
 +      if (action_code == peer->sm_tmr.action_code) {
 +              wpa_printf(MSG_DEBUG, "TDLS: Retry timeout cancelled for "
 +                         "action_code=%u", action_code);
 +
 +              /* Cancel Timeout registered */
 +              eloop_cancel_timeout(wpa_tdls_tpk_retry_timeout, sm, peer);
 +
 +              /* free all resources meant for retry */
 +              os_free(peer->sm_tmr.buf);
 +              peer->sm_tmr.buf = NULL;
 +
 +              peer->sm_tmr.count = 0;
 +              peer->sm_tmr.timer = 0;
 +              peer->sm_tmr.buf_len = 0;
 +              peer->sm_tmr.action_code = 0xff;
 +      } else {
 +              wpa_printf(MSG_INFO, "TDLS: Error in cancelling retry timeout "
 +                         "(Unknown action_code=%u)", action_code);
 +      }
 +}
 +
 +
 +static void wpa_tdls_generate_tpk(struct wpa_tdls_peer *peer,
 +                                const u8 *own_addr, const u8 *bssid)
 +{
 +      u8 key_input[SHA256_MAC_LEN];
 +      const u8 *nonce[2];
 +      size_t len[2];
 +      u8 data[3 * ETH_ALEN];
 +
 +      /* IEEE Std 802.11z-2010 8.5.9.1:
 +       * TPK-Key-Input = SHA-256(min(SNonce, ANonce) || max(SNonce, ANonce))
 +       */
 +      len[0] = WPA_NONCE_LEN;
 +      len[1] = WPA_NONCE_LEN;
 +      if (os_memcmp(peer->inonce, peer->rnonce, WPA_NONCE_LEN) < 0) {
 +              nonce[0] = peer->inonce;
 +              nonce[1] = peer->rnonce;
 +      } else {
 +              nonce[0] = peer->rnonce;
 +              nonce[1] = peer->inonce;
 +      }
 +      wpa_hexdump(MSG_DEBUG, "TDLS: min(Nonce)", nonce[0], WPA_NONCE_LEN);
 +      wpa_hexdump(MSG_DEBUG, "TDLS: max(Nonce)", nonce[1], WPA_NONCE_LEN);
 +      sha256_vector(2, nonce, len, key_input);
 +      wpa_hexdump_key(MSG_DEBUG, "TDLS: TPK-Key-Input",
 +                      key_input, SHA256_MAC_LEN);
 +
 +      /*
 +       * TPK-Key-Data = KDF-N_KEY(TPK-Key-Input, "TDLS PMK",
 +       *      min(MAC_I, MAC_R) || max(MAC_I, MAC_R) || BSSID || N_KEY)
 +       * TODO: is N_KEY really included in KDF Context and if so, in which
 +       * presentation format (little endian 16-bit?) is it used? It gets
 +       * added by the KDF anyway..
 +       */
 +
 +      if (os_memcmp(own_addr, peer->addr, ETH_ALEN) < 0) {
 +              os_memcpy(data, own_addr, ETH_ALEN);
 +              os_memcpy(data + ETH_ALEN, peer->addr, ETH_ALEN);
 +      } else {
 +              os_memcpy(data, peer->addr, ETH_ALEN);
 +              os_memcpy(data + ETH_ALEN, own_addr, ETH_ALEN);
 +      }
 +      os_memcpy(data + 2 * ETH_ALEN, bssid, ETH_ALEN);
 +      wpa_hexdump(MSG_DEBUG, "TDLS: KDF Context", data, sizeof(data));
 +
 +      sha256_prf(key_input, SHA256_MAC_LEN, "TDLS PMK", data, sizeof(data),
 +                 (u8 *) &peer->tpk, sizeof(peer->tpk));
 +      wpa_hexdump_key(MSG_DEBUG, "TDLS: TPK-KCK",
 +                      peer->tpk.kck, sizeof(peer->tpk.kck));
 +      wpa_hexdump_key(MSG_DEBUG, "TDLS: TPK-TK",
 +                      peer->tpk.tk, sizeof(peer->tpk.tk));
 +      peer->tpk_set = 1;
 +}
 +
 +
 +/**
 + * wpa_tdls_ftie_mic - Calculate TDLS FTIE MIC
 + * @kck: TPK-KCK
 + * @lnkid: Pointer to the beginning of Link Identifier IE
 + * @rsnie: Pointer to the beginning of RSN IE used for handshake
 + * @timeoutie: Pointer to the beginning of Timeout IE used for handshake
 + * @ftie: Pointer to the beginning of FT IE
 + * @mic: Pointer for writing MIC
 + *
 + * Calculate MIC for TDLS frame.
 + */
 +static int wpa_tdls_ftie_mic(const u8 *kck, u8 trans_seq, const u8 *lnkid,
 +                           const u8 *rsnie, const u8 *timeoutie,
 +                           const u8 *ftie, u8 *mic)
 +{
 +      u8 *buf, *pos;
 +      struct wpa_tdls_ftie *_ftie;
 +      const struct wpa_tdls_lnkid *_lnkid;
 +      int ret;
 +      int len = 2 * ETH_ALEN + 1 + 2 + lnkid[1] + 2 + rsnie[1] +
 +              2 + timeoutie[1] + 2 + ftie[1];
 +      buf = os_zalloc(len);
 +      if (!buf) {
 +              wpa_printf(MSG_WARNING, "TDLS: No memory for MIC calculation");
 +              return -1;
 +      }
 +
 +      pos = buf;
 +      _lnkid = (const struct wpa_tdls_lnkid *) lnkid;
 +      /* 1) TDLS initiator STA MAC address */
 +      os_memcpy(pos, _lnkid->init_sta, ETH_ALEN);
 +      pos += ETH_ALEN;
 +      /* 2) TDLS responder STA MAC address */
 +      os_memcpy(pos, _lnkid->resp_sta, ETH_ALEN);
 +      pos += ETH_ALEN;
 +      /* 3) Transaction Sequence number */
 +      *pos++ = trans_seq;
 +      /* 4) Link Identifier IE */
 +      os_memcpy(pos, lnkid, 2 + lnkid[1]);
 +      pos += 2 + lnkid[1];
 +      /* 5) RSN IE */
 +      os_memcpy(pos, rsnie, 2 + rsnie[1]);
 +      pos += 2 + rsnie[1];
 +      /* 6) Timeout Interval IE */
 +      os_memcpy(pos, timeoutie, 2 + timeoutie[1]);
 +      pos += 2 + timeoutie[1];
 +      /* 7) FTIE, with the MIC field of the FTIE set to 0 */
 +      os_memcpy(pos, ftie, 2 + ftie[1]);
 +      _ftie = (struct wpa_tdls_ftie *) pos;
 +      os_memset(_ftie->mic, 0, TDLS_MIC_LEN);
 +      pos += 2 + ftie[1];
 +
 +      wpa_hexdump(MSG_DEBUG, "TDLS: Data for FTIE MIC", buf, pos - buf);
 +      wpa_hexdump_key(MSG_DEBUG, "TDLS: KCK", kck, 16);
 +      ret = omac1_aes_128(kck, buf, pos - buf, mic);
 +      os_free(buf);
 +      wpa_hexdump(MSG_DEBUG, "TDLS: FTIE MIC", mic, 16);
 +      return ret;
 +}
 +
 +
 +/**
 + * wpa_tdls_key_mic_teardown - Calculate TDLS FTIE MIC for Teardown frame
 + * @kck: TPK-KCK
 + * @trans_seq: Transaction Sequence Number (4 - Teardown)
 + * @rcode: Reason code for Teardown
 + * @dtoken: Dialog Token used for that particular link
 + * @lnkid: Pointer to the beginning of Link Identifier IE
 + * @ftie: Pointer to the beginning of FT IE
 + * @mic: Pointer for writing MIC
 + *
 + * Calculate MIC for TDLS frame.
 + */
 +static int wpa_tdls_key_mic_teardown(const u8 *kck, u8 trans_seq, u16 rcode,
 +                                   u8 dtoken, const u8 *lnkid,
 +                                   const u8 *ftie, u8 *mic)
 +{
 +      u8 *buf, *pos;
 +      struct wpa_tdls_ftie *_ftie;
 +      int ret;
 +      int len;
 +
 +      if (lnkid == NULL)
 +              return -1;
 +
 +      len = 2 + lnkid[1] + sizeof(rcode) + sizeof(dtoken) +
 +              sizeof(trans_seq) + 2 + ftie[1];
 +
 +      buf = os_zalloc(len);
 +      if (!buf) {
 +              wpa_printf(MSG_WARNING, "TDLS: No memory for MIC calculation");
 +              return -1;
 +      }
 +
 +      pos = buf;
 +      /* 1) Link Identifier IE */
 +      os_memcpy(pos, lnkid, 2 + lnkid[1]);
 +      pos += 2 + lnkid[1];
 +      /* 2) Reason Code */
 +      WPA_PUT_LE16(pos, rcode);
 +      pos += sizeof(rcode);
 +      /* 3) Dialog token */
 +      *pos++ = dtoken;
 +      /* 4) Transaction Sequence number */
 +      *pos++ = trans_seq;
 +      /* 7) FTIE, with the MIC field of the FTIE set to 0 */
 +      os_memcpy(pos, ftie, 2 + ftie[1]);
 +      _ftie = (struct wpa_tdls_ftie *) pos;
 +      os_memset(_ftie->mic, 0, TDLS_MIC_LEN);
 +      pos += 2 + ftie[1];
 +
 +      wpa_hexdump(MSG_DEBUG, "TDLS: Data for FTIE MIC", buf, pos - buf);
 +      wpa_hexdump_key(MSG_DEBUG, "TDLS: KCK", kck, 16);
 +      ret = omac1_aes_128(kck, buf, pos - buf, mic);
 +      os_free(buf);
 +      wpa_hexdump(MSG_DEBUG, "TDLS: FTIE MIC", mic, 16);
 +      return ret;
 +}
 +
 +
 +static int wpa_supplicant_verify_tdls_mic(u8 trans_seq,
 +                                        struct wpa_tdls_peer *peer,
 +                                        const u8 *lnkid, const u8 *timeoutie,
 +                                        const struct wpa_tdls_ftie *ftie)
 +{
 +      u8 mic[16];
 +
 +      if (peer->tpk_set) {
 +              wpa_tdls_ftie_mic(peer->tpk.kck, trans_seq, lnkid,
 +                                peer->rsnie_p, timeoutie, (u8 *) ftie,
 +                                mic);
 +              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",
 +                                  ftie->mic, 16);
 +                      wpa_hexdump(MSG_DEBUG, "TDLS: Calculated MIC",
 +                                  mic, 16);
 +                      return -1;
 +              }
 +      } else {
 +              wpa_printf(MSG_WARNING, "TDLS: Could not verify TDLS MIC, "
 +                         "TPK not set - dropping packet");
 +              return -1;
 +      }
 +      return 0;
 +}
 +
 +
 +static int wpa_supplicant_verify_tdls_mic_teardown(
 +      u8 trans_seq, u16 rcode, u8 dtoken, struct wpa_tdls_peer *peer,
 +      const u8 *lnkid, const struct wpa_tdls_ftie *ftie)
 +{
 +      u8 mic[16];
 +
 +      if (peer->tpk_set) {
 +              wpa_tdls_key_mic_teardown(peer->tpk.kck, trans_seq, rcode,
 +                                        dtoken, lnkid, (u8 *) ftie, mic);
 +              if (os_memcmp_const(mic, ftie->mic, 16) != 0) {
 +                      wpa_printf(MSG_INFO, "TDLS: Invalid MIC in Teardown - "
 +                                 "dropping packet");
 +                      return -1;
 +              }
 +      } else {
 +              wpa_printf(MSG_INFO, "TDLS: Could not verify TDLS Teardown "
 +                         "MIC, TPK not set - dropping packet");
 +              return -1;
 +      }
 +      return 0;
 +}
 +
 +
 +static void wpa_tdls_tpk_timeout(void *eloop_ctx, void *timeout_ctx)
 +{
 +      struct wpa_sm *sm = eloop_ctx;
 +      struct wpa_tdls_peer *peer = timeout_ctx;
 +
 +      /*
 +       * On TPK lifetime expiration, we have an option of either tearing down
 +       * the direct link or trying to re-initiate it. The selection of what
 +       * to do is not strictly speaking controlled by our role in the expired
 +       * link, but for now, use that to select whether to renew or tear down
 +       * the link.
 +       */
 +
 +      if (peer->initiator) {
 +              wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime expired for " MACSTR
 +                         " - try to renew", MAC2STR(peer->addr));
 +              wpa_tdls_start(sm, peer->addr);
 +      } else {
 +              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);
 +      }
 +}
 +
 +
 +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));
 +      eloop_cancel_timeout(wpa_tdls_tpk_timeout, sm, 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);
 +      peer->ht_capabilities = NULL;
 +      os_free(peer->vht_capabilities);
 +      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)
 +{
 +      lnkid->ie_type = WLAN_EID_LINK_ID;
 +      lnkid->ie_len = 3 * ETH_ALEN;
 +      os_memcpy(lnkid->bssid, sm->bssid, ETH_ALEN);
 +      if (peer->initiator) {
 +              os_memcpy(lnkid->init_sta, sm->own_addr, ETH_ALEN);
 +              os_memcpy(lnkid->resp_sta, peer->addr, ETH_ALEN);
 +      } else {
 +              os_memcpy(lnkid->init_sta, peer->addr, ETH_ALEN);
 +              os_memcpy(lnkid->resp_sta, sm->own_addr, ETH_ALEN);
 +      }
 +}
 +
 +
 +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;
 +      struct wpa_tdls_lnkid lnkid;
 +      u8 dialog_token;
 +      u8 *rbuf, *pos;
 +      int ielen;
 +
 +      if (sm->tdls_disabled || !sm->tdls_supported)
 +              return -1;
 +
 +      /* Find the node and free from the list */
 +      for (peer = sm->tdls; peer; peer = peer->next) {
 +              if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0)
 +                      break;
 +      }
 +
 +      if (peer == NULL) {
 +              wpa_printf(MSG_INFO, "TDLS: No matching entry found for "
 +                         "Teardown " MACSTR, MAC2STR(addr));
 +              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,
 +                 MAC2STR(addr));
 +
 +      ielen = 0;
 +      if (wpa_tdls_get_privacy(sm) && peer->tpk_set && peer->tpk_success) {
 +              /* To add FTIE for Teardown request and compute MIC */
 +              ielen += sizeof(*ftie);
 +#ifdef CONFIG_TDLS_TESTING
 +              if (tdls_testing & TDLS_TESTING_LONG_FRAME)
 +                      ielen += 170;
 +#endif /* CONFIG_TDLS_TESTING */
 +      }
 +
 +      rbuf = os_zalloc(ielen + 1);
 +      if (rbuf == NULL)
 +              return -1;
 +      pos = rbuf;
 +
 +      if (!wpa_tdls_get_privacy(sm) || !peer->tpk_set || !peer->tpk_success)
 +              goto skip_ies;
 +
 +      ftie = (struct wpa_tdls_ftie *) pos;
 +      ftie->ie_type = WLAN_EID_FAST_BSS_TRANSITION;
 +      /* Using the recent nonce which should be for CONFIRM frame */
 +      os_memcpy(ftie->Anonce, peer->rnonce, WPA_NONCE_LEN);
 +      os_memcpy(ftie->Snonce, peer->inonce, WPA_NONCE_LEN);
 +      ftie->ie_len = sizeof(struct wpa_tdls_ftie) - 2;
 +      pos = (u8 *) (ftie + 1);
 +#ifdef CONFIG_TDLS_TESTING
 +      if (tdls_testing & TDLS_TESTING_LONG_FRAME) {
 +              wpa_printf(MSG_DEBUG, "TDLS: Testing - add extra subelem to "
 +                         "FTIE");
 +              ftie->ie_len += 170;
 +              *pos++ = 255; /* FTIE subelem */
 +              *pos++ = 168; /* FTIE subelem length */
 +              pos += 168;
 +      }
 +#endif /* CONFIG_TDLS_TESTING */
 +      wpa_hexdump(MSG_DEBUG, "TDLS: FTIE for TDLS Teardown handshake",
 +                  (u8 *) ftie, pos - (u8 *) ftie);
 +
 +      /* compute MIC before sending */
 +      wpa_tdls_linkid(sm, peer, &lnkid);
 +      wpa_tdls_key_mic_teardown(peer->tpk.kck, 4, reason_code,
 +                                dialog_token, (u8 *) &lnkid, (u8 *) ftie,
 +                                ftie->mic);
 +
 +skip_ies:
 +      /* TODO: register for a Timeout handler, if Teardown is not received at
 +       * the other end, then try again another time */
 +
 +      /* request driver to send Teardown using this FTIE */
 +      wpa_tdls_tpk_send(sm, addr, WLAN_TDLS_TEARDOWN, 0,
 +                        reason_code, 0, peer->initiator, rbuf, pos - rbuf);
 +      os_free(rbuf);
 +
 +      return 0;
 +}
 +
 +
 +int wpa_tdls_teardown_link(struct wpa_sm *sm, const u8 *addr, u16 reason_code)
 +{
 +      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 == NULL) {
 +              wpa_printf(MSG_DEBUG, "TDLS: Could not find peer " MACSTR
 +                 " for link Teardown", MAC2STR(addr));
 +              return -1;
 +      }
 +
 +      if (!peer->tpk_success) {
 +              wpa_printf(MSG_DEBUG, "TDLS: Peer " MACSTR
 +                 " not connected - cannot Teardown link", MAC2STR(addr));
 +              return -1;
 +      }
 +
 +      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_unreachable_link(struct wpa_sm *sm, const u8 *addr)
 +{
 +      struct wpa_tdls_peer *peer;
 +
 +      for (peer = sm->tdls; peer; peer = peer->next) {
 +              if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0)
 +                      break;
 +      }
 +
 +      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";
 +}
 +
 +
 +static int wpa_tdls_recv_teardown(struct wpa_sm *sm, const u8 *src_addr,
 +                                const u8 *buf, size_t len)
 +{
 +      struct wpa_tdls_peer *peer = NULL;
 +      struct wpa_tdls_ftie *ftie;
 +      struct wpa_tdls_lnkid *lnkid;
 +      struct wpa_eapol_ie_parse kde;
 +      u16 reason_code;
 +      const u8 *pos;
 +      int ielen;
 +
 +      /* Find the node and free from the list */
 +      for (peer = sm->tdls; peer; peer = peer->next) {
 +              if (os_memcmp(peer->addr, src_addr, ETH_ALEN) == 0)
 +                      break;
 +      }
 +
 +      if (peer == NULL) {
 +              wpa_printf(MSG_INFO, "TDLS: No matching entry found for "
 +                         "Teardown " MACSTR, MAC2STR(src_addr));
 +              return 0;
 +      }
 +
 +      pos = buf;
 +      pos += 1 /* pkt_type */ + 1 /* Category */ + 1 /* Action */;
 +
 +      reason_code = WPA_GET_LE16(pos);
 +      pos += 2;
 +
 +      wpa_printf(MSG_DEBUG, "TDLS: TDLS Teardown Request from " MACSTR
 +                 " (reason code %u)", MAC2STR(src_addr), reason_code);
 +
 +      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 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 "
 +                         "Teardown");
 +              return -1;
 +      }
 +      lnkid = (struct wpa_tdls_lnkid *) kde.lnkid;
 +
 +      if (!wpa_tdls_get_privacy(sm) || !peer->tpk_set || !peer->tpk_success)
 +              goto skip_ftie;
 +
 +      if (kde.ftie == NULL || kde.ftie_len < sizeof(*ftie)) {
 +              wpa_printf(MSG_INFO, "TDLS: No FTIE in TDLS Teardown");
 +              return -1;
 +      }
 +
 +      ftie = (struct wpa_tdls_ftie *) kde.ftie;
 +
 +      /* Process MIC check to see if TDLS Teardown is right */
 +      if (wpa_supplicant_verify_tdls_mic_teardown(4, reason_code,
 +                                                  peer->dtoken, peer,
 +                                                  (u8 *) lnkid, ftie) < 0) {
 +              wpa_printf(MSG_DEBUG, "TDLS: MIC failure for TDLS "
 +                         "Teardown Request from " MACSTR, MAC2STR(src_addr));
 +              return -1;
 +      }
 +
 +skip_ftie:
 +      /*
 +       * Request the driver to disable the direct link and clear associated
 +       * keys.
 +       */
 +      wpa_tdls_disable_peer_link(sm, peer);
 +      return 0;
 +}
 +
 +
 +/**
 + * wpa_tdls_send_error - To send suitable TDLS status response with
 + *    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, 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,
 +                               0, initiator, NULL, 0);
 +}
 +
 +
 +static struct wpa_tdls_peer *
 +wpa_tdls_add_peer(struct wpa_sm *sm, const u8 *addr, int *existing)
 +{
 +      struct wpa_tdls_peer *peer;
 +
 +      if (existing)
 +              *existing = 0;
 +      for (peer = sm->tdls; peer; peer = peer->next) {
 +              if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0) {
 +                      if (existing)
 +                              *existing = 1;
 +                      return peer; /* re-use existing entry */
 +              }
 +      }
 +
 +      wpa_printf(MSG_INFO, "TDLS: Creating peer entry for " MACSTR,
 +                 MAC2STR(addr));
 +
 +      peer = os_zalloc(sizeof(*peer));
 +      if (peer == NULL)
 +              return NULL;
 +
 +      os_memcpy(peer->addr, addr, ETH_ALEN);
 +      peer->next = sm->tdls;
 +      sm->tdls = peer;
 +
 +      return peer;
 +}
 +
 +
 +static int wpa_tdls_send_tpk_m1(struct wpa_sm *sm,
 +                              struct wpa_tdls_peer *peer)
 +{
 +      size_t buf_len;
 +      struct wpa_tdls_timeoutie timeoutie;
 +      u16 rsn_capab;
 +      struct wpa_tdls_ftie *ftie;
 +      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");
 +              peer->rsnie_i_len = 0;
 +              goto skip_rsnie;
 +      }
 +
 +      /*
 +       * TPK Handshake Message 1:
 +       * FTIE: ANonce=0, SNonce=initiator nonce MIC=0, DataKDs=(RSNIE_I,
 +       * Timeout Interval IE))
 +       */
 +
 +      /* 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
 +       * 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;
 +#ifdef CONFIG_TDLS_TESTING
 +      if (tdls_testing & TDLS_TESTING_ALT_RSN_IE) {
 +              wpa_printf(MSG_DEBUG, "TDLS: Use alternative RSN IE for "
 +                         "testing");
 +              rsn_capab = WPA_CAPABILITY_PEERKEY_ENABLED;
 +      }
 +#endif /* CONFIG_TDLS_TESTING */
 +      WPA_PUT_LE16(pos, rsn_capab);
 +      pos += 2;
 +#ifdef CONFIG_TDLS_TESTING
 +      if (tdls_testing & TDLS_TESTING_ALT_RSN_IE) {
 +              /* Number of PMKIDs */
 +              *pos++ = 0x00;
 +              *pos++ = 0x00;
 +      }
 +#endif /* CONFIG_TDLS_TESTING */
 +
 +      hdr->len = (pos - peer->rsnie_i) - 2;
 +      peer->rsnie_i_len = pos - peer->rsnie_i;
 +      wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE for TPK handshake",
 +                  peer->rsnie_i, peer->rsnie_i_len);
 +
 +skip_rsnie:
 +      buf_len = 0;
 +      if (wpa_tdls_get_privacy(sm))
 +              buf_len += peer->rsnie_i_len + sizeof(struct wpa_tdls_ftie) +
 +                      sizeof(struct wpa_tdls_timeoutie);
 +#ifdef CONFIG_TDLS_TESTING
 +      if (wpa_tdls_get_privacy(sm) &&
 +          (tdls_testing & TDLS_TESTING_LONG_FRAME))
 +              buf_len += 170;
 +      if (tdls_testing & TDLS_TESTING_DIFF_BSSID)
 +              buf_len += sizeof(struct wpa_tdls_lnkid);
 +#endif /* CONFIG_TDLS_TESTING */
 +      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);
 +
 +      ftie = (struct wpa_tdls_ftie *) pos;
 +      ftie->ie_type = WLAN_EID_FAST_BSS_TRANSITION;
 +      ftie->ie_len = sizeof(struct wpa_tdls_ftie) - 2;
 +
 +      if (os_get_random(peer->inonce, WPA_NONCE_LEN)) {
 +              wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
 +                      "TDLS: Failed to get random data for initiator Nonce");
 +              os_free(rbuf);
 +              wpa_tdls_peer_free(sm, peer);
 +              return -1;
 +      }
 +      wpa_hexdump(MSG_DEBUG, "TDLS: Initiator Nonce for TPK handshake",
 +                  peer->inonce, WPA_NONCE_LEN);
 +      os_memcpy(ftie->Snonce, peer->inonce, WPA_NONCE_LEN);
 +
 +      wpa_hexdump(MSG_DEBUG, "TDLS: FTIE for TPK Handshake M1",
 +                  (u8 *) ftie, sizeof(struct wpa_tdls_ftie));
 +
 +      pos = (u8 *) (ftie + 1);
 +
 +#ifdef CONFIG_TDLS_TESTING
 +      if (tdls_testing & TDLS_TESTING_LONG_FRAME) {
 +              wpa_printf(MSG_DEBUG, "TDLS: Testing - add extra subelem to "
 +                         "FTIE");
 +              ftie->ie_len += 170;
 +              *pos++ = 255; /* FTIE subelem */
 +              *pos++ = 168; /* FTIE subelem length */
 +              pos += 168;
 +      }
 +#endif /* CONFIG_TDLS_TESTING */
 +
 +      /* Lifetime */
 +      peer->lifetime = TPK_LIFETIME;
 +#ifdef CONFIG_TDLS_TESTING
 +      if (tdls_testing & TDLS_TESTING_SHORT_LIFETIME) {
 +              wpa_printf(MSG_DEBUG, "TDLS: Testing - use short TPK "
 +                         "lifetime");
 +              peer->lifetime = 301;
 +      }
 +      if (tdls_testing & TDLS_TESTING_LONG_LIFETIME) {
 +              wpa_printf(MSG_DEBUG, "TDLS: Testing - use long TPK "
 +                         "lifetime");
 +              peer->lifetime = 0xffffffff;
 +      }
 +#endif /* CONFIG_TDLS_TESTING */
 +      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:
 +
 +#ifdef CONFIG_TDLS_TESTING
 +      if (tdls_testing & TDLS_TESTING_DIFF_BSSID) {
 +              wpa_printf(MSG_DEBUG, "TDLS: Testing - use incorrect BSSID in "
 +                         "Link Identifier");
 +              struct wpa_tdls_lnkid *l = (struct wpa_tdls_lnkid *) pos;
 +              wpa_tdls_linkid(sm, peer, l);
 +              l->bssid[5] ^= 0x01;
 +              pos += sizeof(*l);
 +      }
 +#endif /* CONFIG_TDLS_TESTING */
 +
 +      wpa_printf(MSG_DEBUG, "TDLS: Sending TDLS Setup Request / TPK "
 +                 "Handshake Message 1 (peer " MACSTR ")",
 +                 MAC2STR(peer->addr));
 +
 +      status = wpa_tdls_tpk_send(sm, peer->addr, WLAN_TDLS_SETUP_REQUEST,
 +                                 1, 0, 0, peer->initiator, rbuf, pos - rbuf);
 +      os_free(rbuf);
 +
 +      return status;
 +}
 +
 +
 +static int wpa_tdls_send_tpk_m2(struct wpa_sm *sm,
 +                              const unsigned char *src_addr, u8 dtoken,
 +                              struct wpa_tdls_lnkid *lnkid,
 +                              const struct wpa_tdls_peer *peer)
 +{
 +      u8 *rbuf, *pos;
 +      size_t buf_len;
 +      u32 lifetime;
 +      struct wpa_tdls_timeoutie timeoutie;
 +      struct wpa_tdls_ftie *ftie;
 +      int status;
 +
 +      buf_len = 0;
 +      if (wpa_tdls_get_privacy(sm)) {
 +              /* Peer RSN IE, FTIE(Initiator Nonce, Responder Nonce),
 +               * Lifetime */
 +              buf_len += peer->rsnie_i_len + sizeof(struct wpa_tdls_ftie) +
 +                      sizeof(struct wpa_tdls_timeoutie);
 +#ifdef CONFIG_TDLS_TESTING
 +              if (tdls_testing & TDLS_TESTING_LONG_FRAME)
 +                      buf_len += 170;
 +#endif /* CONFIG_TDLS_TESTING */
 +      }
 +
 +      rbuf = os_zalloc(buf_len + 1);
 +      if (rbuf == NULL)
 +              return -1;
 +      pos = rbuf;
 +
 +      if (!wpa_tdls_get_privacy(sm))
 +              goto skip_ies;
 +
 +      /* Peer RSN IE */
 +      pos = wpa_add_ie(pos, peer->rsnie_p, peer->rsnie_p_len);
 +
 +      ftie = (struct wpa_tdls_ftie *) pos;
 +      ftie->ie_type = WLAN_EID_FAST_BSS_TRANSITION;
 +      /* TODO: ftie->mic_control to set 2-RESPONSE */
 +      os_memcpy(ftie->Anonce, peer->rnonce, WPA_NONCE_LEN);
 +      os_memcpy(ftie->Snonce, peer->inonce, WPA_NONCE_LEN);
 +      ftie->ie_len = sizeof(struct wpa_tdls_ftie) - 2;
 +      wpa_hexdump(MSG_DEBUG, "TDLS: FTIE for TPK M2",
 +                  (u8 *) ftie, sizeof(*ftie));
 +
 +      pos = (u8 *) (ftie + 1);
 +
 +#ifdef CONFIG_TDLS_TESTING
 +      if (tdls_testing & TDLS_TESTING_LONG_FRAME) {
 +              wpa_printf(MSG_DEBUG, "TDLS: Testing - add extra subelem to "
 +                         "FTIE");
 +              ftie->ie_len += 170;
 +              *pos++ = 255; /* FTIE subelem */
 +              *pos++ = 168; /* FTIE subelem length */
 +              pos += 168;
 +      }
 +#endif /* CONFIG_TDLS_TESTING */
 +
 +      /* Lifetime */
 +      lifetime = peer->lifetime;
 +#ifdef CONFIG_TDLS_TESTING
 +      if (tdls_testing & TDLS_TESTING_WRONG_LIFETIME_RESP) {
 +              wpa_printf(MSG_DEBUG, "TDLS: Testing - use wrong TPK "
 +                         "lifetime in response");
 +              lifetime++;
 +      }
 +#endif /* CONFIG_TDLS_TESTING */
 +      pos = wpa_add_tdls_timeoutie(pos, (u8 *) &timeoutie,
 +                                   sizeof(timeoutie), lifetime);
 +      wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime %u seconds from initiator",
 +                 lifetime);
 +
 +      /* 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:
 +      status = wpa_tdls_tpk_send(sm, src_addr, WLAN_TDLS_SETUP_RESPONSE,
 +                                 dtoken, 0, 0, peer->initiator, rbuf,
 +                                 pos - rbuf);
 +      os_free(rbuf);
 +
 +      return status;
 +}
 +
 +
 +static int wpa_tdls_send_tpk_m3(struct wpa_sm *sm,
 +                              const unsigned char *src_addr, u8 dtoken,
 +                              struct wpa_tdls_lnkid *lnkid,
 +                              const struct wpa_tdls_peer *peer)
 +{
 +      u8 *rbuf, *pos;
 +      size_t buf_len;
 +      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)) {
 +              /* Peer RSN IE, FTIE(Initiator Nonce, Responder Nonce),
 +               * Lifetime */
 +              buf_len += peer->rsnie_i_len + sizeof(struct wpa_tdls_ftie) +
 +                      sizeof(struct wpa_tdls_timeoutie);
 +#ifdef CONFIG_TDLS_TESTING
 +              if (tdls_testing & TDLS_TESTING_LONG_FRAME)
 +                      buf_len += 170;
 +#endif /* CONFIG_TDLS_TESTING */
 +      }
 +
 +      rbuf = os_zalloc(buf_len + 1);
 +      if (rbuf == NULL)
 +              return -1;
 +      pos = rbuf;
 +
 +      if (!wpa_tdls_get_privacy(sm))
 +              goto skip_ies;
 +
 +      /* Peer RSN IE */
 +      pos = wpa_add_ie(pos, peer->rsnie_p, peer->rsnie_p_len);
 +
 +      ftie = (struct wpa_tdls_ftie *) pos;
 +      ftie->ie_type = WLAN_EID_FAST_BSS_TRANSITION;
 +      /*TODO: ftie->mic_control to set 3-CONFIRM */
 +      os_memcpy(ftie->Anonce, peer->rnonce, WPA_NONCE_LEN);
 +      os_memcpy(ftie->Snonce, peer->inonce, WPA_NONCE_LEN);
 +      ftie->ie_len = sizeof(struct wpa_tdls_ftie) - 2;
 +
 +      pos = (u8 *) (ftie + 1);
 +
 +#ifdef CONFIG_TDLS_TESTING
 +      if (tdls_testing & TDLS_TESTING_LONG_FRAME) {
 +              wpa_printf(MSG_DEBUG, "TDLS: Testing - add extra subelem to "
 +                         "FTIE");
 +              ftie->ie_len += 170;
 +              *pos++ = 255; /* FTIE subelem */
 +              *pos++ = 168; /* FTIE subelem length */
 +              pos += 168;
 +      }
 +#endif /* CONFIG_TDLS_TESTING */
 +
 +      /* Lifetime */
 +      lifetime = peer->lifetime;
 +#ifdef CONFIG_TDLS_TESTING
 +      if (tdls_testing & TDLS_TESTING_WRONG_LIFETIME_CONF) {
 +              wpa_printf(MSG_DEBUG, "TDLS: Testing - use wrong TPK "
 +                         "lifetime in confirm");
 +              lifetime++;
 +      }
 +#endif /* CONFIG_TDLS_TESTING */
 +      pos = wpa_add_tdls_timeoutie(pos, (u8 *) &timeoutie,
 +                                   sizeof(timeoutie), lifetime);
 +      wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime %u seconds",
 +                 lifetime);
 +
 +      /* 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:
 +
 +      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 status;
 +}
 +
 +
 +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;
 +
 +      /* 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;
 +}
 +
 +
 +static int
 +wpa_tdls_process_discovery_request(struct wpa_sm *sm, const u8 *addr,
 +                                 const u8 *buf, size_t len)
 +{
 +      struct wpa_eapol_ie_parse kde;
 +      const struct wpa_tdls_lnkid *lnkid;
 +      struct wpa_tdls_peer *peer;
 +      size_t min_req_len = sizeof(struct wpa_tdls_frame) +
 +              1 /* dialog token */ + sizeof(struct wpa_tdls_lnkid);
 +      u8 dialog_token;
 +
 +      wpa_printf(MSG_DEBUG, "TDLS: Discovery Request from " MACSTR,
 +                 MAC2STR(addr));
 +
 +      if (len < min_req_len) {
 +              wpa_printf(MSG_DEBUG, "TDLS Discovery Request is too short: "
 +                         "%d", (int) len);
 +              return -1;
 +      }
 +
 +      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) {
 +              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 "
 +                         "Request");
 +              return -1;
 +      }
 +
 +      lnkid = (const struct wpa_tdls_lnkid *) kde.lnkid;
 +
 +      if (os_memcmp(sm->bssid, lnkid->bssid, ETH_ALEN) != 0) {
 +              wpa_printf(MSG_DEBUG, "TDLS: Discovery Request from different "
 +                         " BSS " MACSTR, MAC2STR(lnkid->bssid));
 +              return -1;
 +      }
 +
 +      peer = wpa_tdls_add_peer(sm, addr, NULL);
 +      if (peer == NULL)
 +              return -1;
 +
 +      return wpa_tdls_send_discovery_response(sm, peer, dialog_token);
 +}
 +
 +
 +int wpa_tdls_send_discovery_request(struct wpa_sm *sm, const u8 *addr)
 +{
 +      if (sm->tdls_disabled || !sm->tdls_supported)
 +              return -1;
 +
 +      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, 0, 1, NULL, 0);
 +}
 +
 +
 +static int copy_supp_rates(const struct wpa_eapol_ie_parse *kde,
 +                         struct wpa_tdls_peer *peer)
 +{
 +      if (!kde->supp_rates) {
 +              wpa_printf(MSG_DEBUG, "TDLS: No supported rates received");
 +              return -1;
 +      }
 +      peer->supp_rates_len = merge_byte_arrays(
 +              peer->supp_rates, sizeof(peer->supp_rates),
 +              kde->supp_rates + 2, kde->supp_rates_len - 2,
 +              kde->ext_supp_rates ? kde->ext_supp_rates + 2 : NULL,
 +              kde->ext_supp_rates_len - 2);
 +      return 0;
 +}
 +
 +
 +static int copy_peer_ht_capab(const struct wpa_eapol_ie_parse *kde,
 +                            struct wpa_tdls_peer *peer)
 +{
-       if (!kde->vht_capabilities ||
-           kde->vht_capabilities_len <
-           sizeof(struct ieee80211_vht_capabilities) ) {
++      if (!kde->ht_capabilities) {
 +              wpa_printf(MSG_DEBUG, "TDLS: No supported ht capabilities "
 +                         "received");
 +              return 0;
 +      }
 +
 +      if (!peer->ht_capabilities) {
 +              peer->ht_capabilities =
 +                        os_zalloc(sizeof(struct ieee80211_ht_capabilities));
 +              if (peer->ht_capabilities == NULL)
 +                        return -1;
 +      }
 +
 +      os_memcpy(peer->ht_capabilities, kde->ht_capabilities,
 +                  sizeof(struct ieee80211_ht_capabilities));
 +      wpa_hexdump(MSG_DEBUG, "TDLS: Peer HT capabilities",
 +                  (u8 *) peer->ht_capabilities,
 +                  sizeof(struct ieee80211_ht_capabilities));
 +
 +      return 0;
 +}
 +
 +
 +static int copy_peer_vht_capab(const struct wpa_eapol_ie_parse *kde,
 +                            struct wpa_tdls_peer *peer)
 +{
- static int wpa_tdls_prohibited(struct wpa_eapol_ie_parse *elems)
++      if (!kde->vht_capabilities) {
 +              wpa_printf(MSG_DEBUG, "TDLS: No supported vht capabilities "
 +                         "received");
 +              return 0;
 +      }
 +
 +      if (!peer->vht_capabilities) {
 +              peer->vht_capabilities =
 +                        os_zalloc(sizeof(struct ieee80211_vht_capabilities));
 +              if (peer->vht_capabilities == NULL)
 +                        return -1;
 +      }
 +
 +      os_memcpy(peer->vht_capabilities, kde->vht_capabilities,
 +                  sizeof(struct ieee80211_vht_capabilities));
 +      wpa_hexdump(MSG_DEBUG, "TDLS: Peer VHT capabilities",
 +                  (u8 *) peer->vht_capabilities,
 +                  sizeof(struct ieee80211_vht_capabilities));
 +
 +      return 0;
 +}
 +
 +
 +static int copy_peer_ext_capab(const struct wpa_eapol_ie_parse *kde,
 +                             struct wpa_tdls_peer *peer)
 +{
 +      if (!kde->ext_capab) {
 +              wpa_printf(MSG_DEBUG, "TDLS: No extended capabilities "
 +                         "received");
 +              return 0;
 +      }
 +
 +      if (!peer->ext_capab || peer->ext_capab_len < kde->ext_capab_len - 2) {
 +              /* Need to allocate buffer to fit the new information */
 +              os_free(peer->ext_capab);
 +              peer->ext_capab = os_zalloc(kde->ext_capab_len - 2);
 +              if (peer->ext_capab == NULL)
 +                      return -1;
 +      }
 +
 +      peer->ext_capab_len = kde->ext_capab_len - 2;
 +      os_memcpy(peer->ext_capab, kde->ext_capab + 2, peer->ext_capab_len);
 +
 +      return 0;
 +}
 +
 +
 +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)
 +{
 +      struct wpa_tdls_peer *peer;
 +      struct wpa_eapol_ie_parse kde;
 +      struct wpa_ie_data ie;
 +      int cipher;
 +      const u8 *cpos;
 +      struct wpa_tdls_ftie *ftie = NULL;
 +      struct wpa_tdls_timeoutie *timeoutie;
 +      struct wpa_tdls_lnkid *lnkid;
 +      u32 lifetime = 0;
 +#if 0
 +      struct rsn_ie_hdr *hdr;
 +      u8 *pos;
 +      u16 rsn_capab;
 +      u16 rsn_ver;
 +#endif
 +      u8 dtoken;
 +      u16 ielen;
 +      u16 status = WLAN_STATUS_UNSPECIFIED_FAILURE;
 +      int tdls_prohibited = sm->tdls_prohibited;
 +      int existing_peer = 0;
 +
 +      if (len < 3 + 3)
 +              return -1;
 +
 +      cpos = buf;
 +      cpos += 1 /* pkt_type */ + 1 /* Category */ + 1 /* Action */;
 +
 +      /* driver had already verified the frame format */
 +      dtoken = *cpos++; /* dialog token */
 +
 +      wpa_printf(MSG_INFO, "TDLS: Dialog Token in TPK M1 %d", dtoken);
 +
 +      peer = wpa_tdls_add_peer(sm, src_addr, &existing_peer);
 +      if (peer == NULL)
 +              goto error;
 +
 +      /* If found, use existing entry instead of adding a new one;
 +       * how to handle the case where both ends initiate at the
 +       * same time? */
 +      if (existing_peer) {
 +              if (peer->tpk_success) {
 +                      wpa_printf(MSG_DEBUG, "TDLS: TDLS Setup Request while "
 +                                 "direct link is enabled - tear down the "
 +                                 "old link first");
 +                      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 "
 +                                         MACSTR, MAC2STR(src_addr));
 +                              return -1;
 +                      } else {
 +                              wpa_printf(MSG_DEBUG, "TDLS: Accept request "
 +                                         "from peer with lower address "
 +                                         MACSTR " (terminate previously "
 +                                         "initiated negotiation",
 +                                         MAC2STR(src_addr));
 +                              wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK,
 +                                               peer->addr);
 +                              wpa_tdls_peer_clear(sm, peer);
 +                      }
 +              }
 +      }
 +
 +      /* capability information */
 +      peer->capability = WPA_GET_LE16(cpos);
 +      cpos += 2;
 +
 +      ielen = len - (cpos - 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 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 "
 +                         "TPK M1");
 +              goto error;
 +      }
 +      wpa_hexdump(MSG_DEBUG, "TDLS: Link ID Received from TPK M1",
 +                  kde.lnkid, kde.lnkid_len);
 +      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_REQUEST_DECLINED;
 +              goto error;
 +      }
 +
 +      wpa_printf(MSG_DEBUG, "TDLS: TPK M1 - TPK initiator " MACSTR,
 +                 MAC2STR(src_addr));
 +
 +      if (copy_supp_rates(&kde, peer) < 0)
 +              goto error;
 +
 +      if (copy_peer_ht_capab(&kde, peer) < 0)
 +              goto error;
 +
 +      if (copy_peer_vht_capab(&kde, peer) < 0)
 +              goto error;
 +
 +      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
 +      if (tdls_testing & TDLS_TESTING_CONCURRENT_INIT) {
 +              peer = wpa_tdls_add_peer(sm, src_addr, NULL);
 +              if (peer == NULL)
 +                      goto error;
 +              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);
 +      }
 +
 +      if ((tdls_testing & TDLS_TESTING_IGNORE_AP_PROHIBIT) &&
 +          tdls_prohibited) {
 +              wpa_printf(MSG_DEBUG, "TDLS: Testing - ignore AP prohibition "
 +                         "on TDLS");
 +              tdls_prohibited = 0;
 +      }
 +#endif /* CONFIG_TDLS_TESTING */
 +
 +      if (tdls_prohibited) {
 +              wpa_printf(MSG_INFO, "TDLS: TDLS prohibited in this BSS");
 +              status = WLAN_STATUS_REQUEST_DECLINED;
 +              goto error;
 +      }
 +
 +      if (!wpa_tdls_get_privacy(sm)) {
 +              if (kde.rsn_ie) {
 +                      wpa_printf(MSG_INFO, "TDLS: RSN IE in TPK M1 while "
 +                                 "security is disabled");
 +                      status = WLAN_STATUS_SECURITY_DISABLED;
 +                      goto error;
 +              }
 +              goto skip_rsn;
 +      }
 +
 +      if (kde.ftie == NULL || kde.ftie_len < sizeof(*ftie) ||
 +          kde.rsn_ie == NULL) {
 +              wpa_printf(MSG_INFO, "TDLS: No FTIE or RSN IE in TPK M1");
 +              status = WLAN_STATUS_INVALID_PARAMETERS;
 +              goto error;
 +      }
 +
 +      if (kde.rsn_ie_len > TDLS_MAX_IE_LEN) {
 +              wpa_printf(MSG_INFO, "TDLS: Too long Initiator RSN IE in "
 +                         "TPK M1");
 +              status = WLAN_STATUS_INVALID_RSNIE;
 +              goto error;
 +      }
 +
 +      if (wpa_parse_wpa_ie_rsn(kde.rsn_ie, kde.rsn_ie_len, &ie) < 0) {
 +              wpa_printf(MSG_INFO, "TDLS: Failed to parse RSN IE in TPK M1");
 +              status = WLAN_STATUS_INVALID_RSNIE;
 +              goto error;
 +      }
 +
 +      cipher = ie.pairwise_cipher;
 +      if (cipher & WPA_CIPHER_CCMP) {
 +              wpa_printf(MSG_DEBUG, "TDLS: Using CCMP for direct link");
 +              cipher = WPA_CIPHER_CCMP;
 +      } else {
 +              wpa_printf(MSG_INFO, "TDLS: No acceptable cipher in TPK M1");
 +              status = WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID;
 +              goto error;
 +      }
 +
 +      if ((ie.capabilities &
 +           (WPA_CAPABILITY_NO_PAIRWISE | WPA_CAPABILITY_PEERKEY_ENABLED)) !=
 +          WPA_CAPABILITY_PEERKEY_ENABLED) {
 +              wpa_printf(MSG_INFO, "TDLS: Invalid RSN Capabilities in "
 +                         "TPK M1");
 +              status = WLAN_STATUS_INVALID_RSN_IE_CAPAB;
 +              goto error;
 +      }
 +
 +      /* Lifetime */
 +      if (kde.key_lifetime == NULL) {
 +              wpa_printf(MSG_INFO, "TDLS: No Key Lifetime IE in TPK M1");
 +              status = WLAN_STATUS_UNACCEPTABLE_LIFETIME;
 +              goto error;
 +      }
 +      timeoutie = (struct wpa_tdls_timeoutie *) kde.key_lifetime;
 +      lifetime = WPA_GET_LE32(timeoutie->value);
 +      wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime %u seconds", lifetime);
 +      if (lifetime < 300) {
 +              wpa_printf(MSG_INFO, "TDLS: Too short TPK lifetime");
 +              status = WLAN_STATUS_UNACCEPTABLE_LIFETIME;
 +              goto error;
 +      }
 +
 +skip_rsn:
 +#ifdef CONFIG_TDLS_TESTING
 +      if (tdls_testing & TDLS_TESTING_CONCURRENT_INIT) {
 +              if (os_memcmp(sm->own_addr, peer->addr, ETH_ALEN) < 0) {
 +                      /*
 +                       * The request frame from us is going to win, so do not
 +                       * replace information based on this request frame from
 +                       * the peer.
 +                       */
 +                      goto skip_rsn_check;
 +              }
 +      }
 +#endif /* CONFIG_TDLS_TESTING */
 +
 +      peer->initiator = 0; /* Need to check */
 +      peer->dtoken = dtoken;
 +
 +      if (!wpa_tdls_get_privacy(sm)) {
 +              peer->rsnie_i_len = 0;
 +              peer->rsnie_p_len = 0;
 +              peer->cipher = WPA_CIPHER_NONE;
 +              goto skip_rsn_check;
 +      }
 +
 +      ftie = (struct wpa_tdls_ftie *) kde.ftie;
 +      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_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
 +      /* get version info from RSNIE received from Peer */
 +      hdr = (struct rsn_ie_hdr *) kde.rsn_ie;
 +      rsn_ver = WPA_GET_LE16(hdr->version);
 +
 +      /* use min(peer's version, out version) */
 +      if (rsn_ver > RSN_VERSION)
 +              rsn_ver = RSN_VERSION;
 +
 +      hdr = (struct rsn_ie_hdr *) peer->rsnie_p;
 +
 +      hdr->elem_id = WLAN_EID_RSN;
 +      WPA_PUT_LE16(hdr->version, rsn_ver);
 +      pos = (u8 *) (hdr + 1);
 +
 +      RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED);
 +      pos += RSN_SELECTOR_LEN;
 +      /* Include only the selected cipher in pairwise cipher suite */
 +      WPA_PUT_LE16(pos, 1);
 +      pos += 2;
 +      if (cipher == WPA_CIPHER_CCMP)
 +              RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP);
 +      pos += RSN_SELECTOR_LEN;
 +
 +      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 - peer->rsnie_p) - 2;
 +      peer->rsnie_p_len = pos - peer->rsnie_p;
 +#endif
 +
 +      /* temp fix: validation of RSNIE later */
 +      os_memcpy(peer->rsnie_p, peer->rsnie_i, peer->rsnie_i_len);
 +      peer->rsnie_p_len = peer->rsnie_i_len;
 +
 +      wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE for TPK handshake",
 +                  peer->rsnie_p, peer->rsnie_p_len);
 +
 +      peer->lifetime = lifetime;
 +
 +      wpa_tdls_generate_tpk(peer, sm->own_addr, sm->bssid);
 +
 +skip_rsn_check:
 +#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_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, 0,
 +                          status);
 +      if (peer)
 +              wpa_tdls_peer_free(sm, peer);
 +      return -1;
 +}
 +
 +
 +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;
 +              /*
 +               * Start the initiator process a bit earlier to avoid race
 +               * condition with the responder sending teardown request.
 +               */
 +              if (lifetime > 3 && peer->initiator)
 +                      lifetime -= 3;
 +              eloop_register_timeout(lifetime, 0, wpa_tdls_tpk_timeout,
 +                                     sm, peer);
 +#ifdef CONFIG_TDLS_TESTING
 +      if (tdls_testing & TDLS_TESTING_NO_TPK_EXPIRATION) {
 +              wpa_printf(MSG_DEBUG, "TDLS: Testing - disable TPK "
 +                         "expiration");
 +              eloop_cancel_timeout(wpa_tdls_tpk_timeout, sm, peer);
 +      }
 +#endif /* CONFIG_TDLS_TESTING */
 +      }
 +
 +      if (peer->reconfig_key && wpa_tdls_set_key(sm, peer) < 0) {
 +              wpa_printf(MSG_INFO, "TDLS: Could not configure key to the "
 +                         "driver");
 +              return -1;
 +      }
 +      peer->reconfig_key = 0;
 +
 +      return wpa_sm_tdls_oper(sm, TDLS_ENABLE_LINK, peer->addr);
 +}
 +
 +
 +static int wpa_tdls_process_tpk_m2(struct wpa_sm *sm, const u8 *src_addr,
 +                                 const u8 *buf, size_t len)
 +{
 +      struct wpa_tdls_peer *peer;
 +      struct wpa_eapol_ie_parse kde;
 +      struct wpa_ie_data ie;
 +      int cipher;
 +      struct wpa_tdls_ftie *ftie;
 +      struct wpa_tdls_timeoutie *timeoutie;
 +      struct wpa_tdls_lnkid *lnkid;
 +      u32 lifetime;
 +      u8 dtoken;
 +      int ielen;
 +      u16 status;
 +      const u8 *pos;
 +      int ret = 0;
 +
 +      wpa_printf(MSG_DEBUG, "TDLS: Received TDLS Setup Response / TPK M2 "
 +                 "(Peer " MACSTR ")", MAC2STR(src_addr));
 +      for (peer = sm->tdls; peer; peer = peer->next) {
 +              if (os_memcmp(peer->addr, src_addr, ETH_ALEN) == 0)
 +                      break;
 +      }
 +      if (peer == NULL) {
 +              wpa_printf(MSG_INFO, "TDLS: No matching peer found for "
 +                         "TPK M2: " MACSTR, MAC2STR(src_addr));
 +              return -1;
 +      }
 +      if (!peer->initiator) {
 +              /*
 +               * This may happen if both devices try to initiate TDLS at the
 +               * same time and we accept the TPK M1 from the peer in
 +               * wpa_tdls_process_tpk_m1() and clear our previous state.
 +               */
 +              wpa_printf(MSG_INFO, "TDLS: We were not the initiator, so "
 +                         "ignore TPK M2 from " MACSTR, MAC2STR(src_addr));
 +              return -1;
 +      }
 +      wpa_tdls_tpk_retry_timeout_cancel(sm, peer, WLAN_TDLS_SETUP_REQUEST);
 +
 +      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);
 +      pos += 2 /* status code */;
 +
 +      if (status != WLAN_STATUS_SUCCESS) {
 +              wpa_printf(MSG_INFO, "TDLS: Status code in TPK M2: %u",
 +                         status);
 +              wpa_tdls_disable_peer_link(sm, peer);
 +              return -1;
 +      }
 +
 +      status = WLAN_STATUS_UNSPECIFIED_FAILURE;
 +
 +      /* TODO: need to verify dialog token matches here or in kernel */
 +      dtoken = *pos++; /* dialog token */
 +
 +      wpa_printf(MSG_DEBUG, "TDLS: Dialog Token in TPK M2 %d", dtoken);
 +
 +      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 */
 +
 +      /*
 +       * 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) {
 +              wpa_printf(MSG_DEBUG, "TDLS: Testing - decline response");
 +              status = WLAN_STATUS_REQUEST_DECLINED;
 +              goto error;
 +      }
 +#endif /* CONFIG_TDLS_TESTING */
 +
 +      if (kde.lnkid == NULL || kde.lnkid_len < 3 * ETH_ALEN) {
 +              wpa_printf(MSG_INFO, "TDLS: No valid Link Identifier IE in "
 +                         "TPK M2");
 +              goto error;
 +      }
 +      wpa_hexdump(MSG_DEBUG, "TDLS: Link ID Received from TPK M2",
 +                  kde.lnkid, kde.lnkid_len);
 +      lnkid = (struct wpa_tdls_lnkid *) kde.lnkid;
 +
 +      if (os_memcmp(sm->bssid, lnkid->bssid, ETH_ALEN) != 0) {
 +              wpa_printf(MSG_INFO, "TDLS: TPK M2 from different BSS");
 +              status = WLAN_STATUS_NOT_IN_SAME_BSS;
 +              goto error;
 +      }
 +
 +      if (copy_supp_rates(&kde, peer) < 0)
 +              goto error;
 +
 +      if (copy_peer_ht_capab(&kde, peer) < 0)
 +              goto error;
 +
 +      if (copy_peer_vht_capab(&kde, peer) < 0)
 +              goto error;
 +
 +      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)) {
 +              peer->rsnie_p_len = 0;
 +              peer->cipher = WPA_CIPHER_NONE;
 +              goto skip_rsn;
 +      }
 +
 +      if (kde.ftie == NULL || kde.ftie_len < sizeof(*ftie) ||
 +          kde.rsn_ie == NULL) {
 +              wpa_printf(MSG_INFO, "TDLS: No FTIE or RSN IE in TPK M2");
 +              status = WLAN_STATUS_INVALID_PARAMETERS;
 +              goto error;
 +      }
 +      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
 +       * match. Since we list only a single pairwise cipher in TPK M1, the
 +       * memcmp is likely to work in most cases, though.
 +       */
 +      if (kde.rsn_ie_len != peer->rsnie_i_len ||
 +          os_memcmp(peer->rsnie_i, kde.rsn_ie, peer->rsnie_i_len) != 0) {
 +              wpa_printf(MSG_INFO, "TDLS: RSN IE in TPK M2 does "
 +                         "not match with RSN IE used in TPK M1");
 +              wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE Sent in TPK M1",
 +                          peer->rsnie_i, peer->rsnie_i_len);
 +              wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE Received from TPK M2",
 +                          kde.rsn_ie, kde.rsn_ie_len);
 +              status = WLAN_STATUS_INVALID_RSNIE;
 +              goto error;
 +      }
 +
 +      if (wpa_parse_wpa_ie_rsn(kde.rsn_ie, kde.rsn_ie_len, &ie) < 0) {
 +              wpa_printf(MSG_INFO, "TDLS: Failed to parse RSN IE in TPK M2");
 +              status = WLAN_STATUS_INVALID_RSNIE;
 +              goto error;
 +      }
 +
 +      cipher = ie.pairwise_cipher;
 +      if (cipher == WPA_CIPHER_CCMP) {
 +              wpa_printf(MSG_DEBUG, "TDLS: Using CCMP for direct link");
 +              cipher = WPA_CIPHER_CCMP;
 +      } else {
 +              wpa_printf(MSG_INFO, "TDLS: No acceptable cipher in TPK M2");
 +              status = WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID;
 +              goto error;
 +      }
 +
 +      wpa_hexdump(MSG_DEBUG, "TDLS: FTIE Received from TPK M2",
 +                  kde.ftie, sizeof(*ftie));
 +      ftie = (struct wpa_tdls_ftie *) kde.ftie;
 +
 +      if (!os_memcmp(peer->inonce, ftie->Snonce, WPA_NONCE_LEN) == 0) {
 +              wpa_printf(MSG_INFO, "TDLS: FTIE SNonce in TPK M2 does "
 +                         "not match with FTIE SNonce used in TPK M1");
 +              /* Silently discard the frame */
 +              return -1;
 +      }
 +
 +      /* Responder Nonce and RSN IE */
 +      os_memcpy(peer->rnonce, ftie->Anonce, WPA_NONCE_LEN);
 +      os_memcpy(peer->rsnie_p, kde.rsn_ie, kde.rsn_ie_len);
 +      peer->rsnie_p_len = kde.rsn_ie_len;
 +      peer->cipher = cipher;
 +
 +      /* Lifetime */
 +      if (kde.key_lifetime == NULL) {
 +              wpa_printf(MSG_INFO, "TDLS: No Key Lifetime IE in TPK M2");
 +              status = WLAN_STATUS_UNACCEPTABLE_LIFETIME;
 +              goto error;
 +      }
 +      timeoutie = (struct wpa_tdls_timeoutie *) kde.key_lifetime;
 +      lifetime = WPA_GET_LE32(timeoutie->value);
 +      wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime %u seconds in TPK M2",
 +                 lifetime);
 +      if (lifetime != peer->lifetime) {
 +              wpa_printf(MSG_INFO, "TDLS: Unexpected TPK lifetime %u in "
 +                         "TPK M2 (expected %u)", lifetime, peer->lifetime);
 +              status = WLAN_STATUS_UNACCEPTABLE_LIFETIME;
 +              goto error;
 +      }
 +
 +      wpa_tdls_generate_tpk(peer, sm->own_addr, sm->bssid);
 +
 +      /* Process MIC check to see if TPK M2 is right */
 +      if (wpa_supplicant_verify_tdls_mic(2, peer, (u8 *) lnkid,
 +                                         (u8 *) timeoutie, ftie) < 0) {
 +              /* Discard the frame */
 +              wpa_tdls_del_key(sm, peer);
 +              wpa_tdls_disable_peer_link(sm, peer);
 +              return -1;
 +      }
 +
 +      if (wpa_tdls_set_key(sm, peer) < 0) {
 +              /*
 +               * Some drivers may not be able to config the key prior to full
 +               * STA entry having been configured.
 +               */
 +              wpa_printf(MSG_DEBUG, "TDLS: Try to configure TPK again after "
 +                         "STA entry is complete");
 +              peer->reconfig_key = 1;
 +      }
 +
 +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");
 +      if (wpa_tdls_send_tpk_m3(sm, src_addr, dtoken, lnkid, peer) < 0)
 +              goto error;
 +
 +      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, 1,
 +                          status);
 +      wpa_tdls_disable_peer_link(sm, peer);
 +      return -1;
 +}
 +
 +
 +static int wpa_tdls_process_tpk_m3(struct wpa_sm *sm, const u8 *src_addr,
 +                                 const u8 *buf, size_t len)
 +{
 +      struct wpa_tdls_peer *peer;
 +      struct wpa_eapol_ie_parse kde;
 +      struct wpa_tdls_ftie *ftie;
 +      struct wpa_tdls_timeoutie *timeoutie;
 +      struct wpa_tdls_lnkid *lnkid;
 +      int ielen;
 +      u16 status;
 +      const u8 *pos;
 +      u32 lifetime;
 +      int ret = 0;
 +
 +      wpa_printf(MSG_DEBUG, "TDLS: Received TDLS Setup Confirm / TPK M3 "
 +                 "(Peer " MACSTR ")", MAC2STR(src_addr));
 +      for (peer = sm->tdls; peer; peer = peer->next) {
 +              if (os_memcmp(peer->addr, src_addr, ETH_ALEN) == 0)
 +                      break;
 +      }
 +      if (peer == NULL) {
 +              wpa_printf(MSG_INFO, "TDLS: No matching peer found for "
 +                         "TPK M3: " MACSTR, MAC2STR(src_addr));
 +              return -1;
 +      }
 +      wpa_tdls_tpk_retry_timeout_cancel(sm, peer, WLAN_TDLS_SETUP_RESPONSE);
 +
 +      if (len < 3 + 3)
 +              goto error;
 +      pos = buf;
 +      pos += 1 /* pkt_type */ + 1 /* Category */ + 1 /* Action */;
 +
 +      status = WPA_GET_LE16(pos);
 +
 +      if (status != 0) {
 +              wpa_printf(MSG_INFO, "TDLS: Status code in TPK M3: %u",
 +                         status);
 +              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_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");
 +              goto error;
 +      }
 +      wpa_hexdump(MSG_DEBUG, "TDLS: Link ID Received from TPK M3",
 +                  (u8 *) kde.lnkid, kde.lnkid_len);
 +      lnkid = (struct wpa_tdls_lnkid *) kde.lnkid;
 +
 +      if (os_memcmp(sm->bssid, lnkid->bssid, ETH_ALEN) != 0) {
 +              wpa_printf(MSG_INFO, "TDLS: TPK M3 from diff BSS");
 +              goto error;
 +      }
 +
 +      if (!wpa_tdls_get_privacy(sm))
 +              goto skip_rsn;
 +
 +      if (kde.ftie == NULL || kde.ftie_len < sizeof(*ftie)) {
 +              wpa_printf(MSG_INFO, "TDLS: No FTIE in TPK M3");
 +              goto error;
 +      }
 +      wpa_hexdump(MSG_DEBUG, "TDLS: FTIE Received from TPK M3",
 +                  kde.ftie, sizeof(*ftie));
 +      ftie = (struct wpa_tdls_ftie *) kde.ftie;
 +
 +      if (kde.rsn_ie == NULL) {
 +              wpa_printf(MSG_INFO, "TDLS: No RSN IE in TPK M3");
 +              goto error;
 +      }
 +      wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE Received from TPK M3",
 +                  kde.rsn_ie, kde.rsn_ie_len);
 +      if (kde.rsn_ie_len != peer->rsnie_p_len ||
 +          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");
 +              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");
 +              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");
 +              goto error;
 +      }
 +
 +      if (kde.key_lifetime == NULL) {
 +              wpa_printf(MSG_INFO, "TDLS: No Key Lifetime IE in TPK M3");
 +              goto error;
 +      }
 +      timeoutie = (struct wpa_tdls_timeoutie *) kde.key_lifetime;
 +      wpa_hexdump(MSG_DEBUG, "TDLS: Timeout IE Received from TPK M3",
 +                  (u8 *) timeoutie, sizeof(*timeoutie));
 +      lifetime = WPA_GET_LE32(timeoutie->value);
 +      wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime %u seconds in TPK M3",
 +                 lifetime);
 +      if (lifetime != peer->lifetime) {
 +              wpa_printf(MSG_INFO, "TDLS: Unexpected TPK lifetime %u in "
 +                         "TPK M3 (expected %u)", lifetime, peer->lifetime);
 +              goto error;
 +      }
 +
 +      if (wpa_supplicant_verify_tdls_mic(3, peer, (u8 *) lnkid,
 +                                         (u8 *) timeoutie, ftie) < 0) {
 +              wpa_tdls_del_key(sm, peer);
 +              goto error;
 +      }
 +
 +      if (wpa_tdls_set_key(sm, peer) < 0) {
 +              /*
 +               * Some drivers may not be able to config the key prior to full
 +               * STA entry having been configured.
 +               */
 +              wpa_printf(MSG_DEBUG, "TDLS: Try to configure TPK again after "
 +                         "STA entry is complete");
 +              peer->reconfig_key = 1;
 +      }
 +
 +skip_rsn:
 +      /* 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;
 +}
 +
 +
 +static u8 * wpa_add_tdls_timeoutie(u8 *pos, u8 *ie, size_t ie_len, u32 tsecs)
 +{
 +      struct wpa_tdls_timeoutie *lifetime = (struct wpa_tdls_timeoutie *) ie;
 +
 +      os_memset(lifetime, 0, ie_len);
 +      lifetime->ie_type = WLAN_EID_TIMEOUT_INTERVAL;
 +      lifetime->ie_len = sizeof(struct wpa_tdls_timeoutie) - 2;
 +      lifetime->interval_type = WLAN_TIMEOUT_KEY_LIFETIME;
 +      WPA_PUT_LE32(lifetime->value, tsecs);
 +      os_memcpy(pos, ie, ie_len);
 +      return pos + ie_len;
 +}
 +
 +
 +/**
 + * wpa_tdls_start - Initiate TDLS handshake (send TPK Handshake Message 1)
 + * @sm: Pointer to WPA state machine data from wpa_sm_init()
 + * @peer: MAC address of the peer STA
 + * Returns: 0 on success, or -1 on failure
 + *
 + * Send TPK Handshake Message 1 info to driver to start TDLS
 + * handshake with the peer.
 + */
 +int wpa_tdls_start(struct wpa_sm *sm, const u8 *addr)
 +{
 +      struct wpa_tdls_peer *peer;
 +      int tdls_prohibited = sm->tdls_prohibited;
 +
 +      if (sm->tdls_disabled || !sm->tdls_supported)
 +              return -1;
 +
 +#ifdef CONFIG_TDLS_TESTING
 +      if ((tdls_testing & TDLS_TESTING_IGNORE_AP_PROHIBIT) &&
 +          tdls_prohibited) {
 +              wpa_printf(MSG_DEBUG, "TDLS: Testing - ignore AP prohibition "
 +                         "on TDLS");
 +              tdls_prohibited = 0;
 +      }
 +#endif /* CONFIG_TDLS_TESTING */
 +
 +      if (tdls_prohibited) {
 +              wpa_printf(MSG_DEBUG, "TDLS: TDLS is prohibited in this BSS - "
 +                         "reject request to start setup");
 +              return -1;
 +      }
 +
 +      peer = wpa_tdls_add_peer(sm, addr, NULL);
 +      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 */
 +      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_peer_link(sm, peer);
 +              return -1;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +void wpa_tdls_remove(struct wpa_sm *sm, const u8 *addr)
 +{
 +      struct wpa_tdls_peer *peer;
 +
 +      if (sm->tdls_disabled || !sm->tdls_supported)
 +              return;
 +
 +      for (peer = sm->tdls; peer; peer = peer->next) {
 +              if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0)
 +                      break;
 +      }
 +
 +      if (peer == NULL || !peer->tpk_success)
 +              return;
 +
 +      if (sm->tdls_external_setup) {
 +              /*
 +               * Disable previous link to allow renegotiation to be completed
 +               * on AP path.
 +               */
 +              wpa_tdls_do_teardown(sm, peer,
 +                                   WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED);
 +      }
 +}
 +
 +
 +/**
 + * wpa_supplicant_rx_tdls - Receive TDLS data frame
 + *
 + * This function is called to receive TDLS (ethertype = 0x890d) data frames.
 + */
 +static void wpa_supplicant_rx_tdls(void *ctx, const u8 *src_addr,
 +                                 const u8 *buf, size_t len)
 +{
 +      struct wpa_sm *sm = ctx;
 +      struct wpa_tdls_frame *tf;
 +
 +      wpa_hexdump(MSG_DEBUG, "TDLS: Received Data frame encapsulation",
 +                  buf, len);
 +
 +      if (sm->tdls_disabled || !sm->tdls_supported) {
 +              wpa_printf(MSG_DEBUG, "TDLS: Discard message - TDLS disabled "
 +                         "or unsupported by driver");
 +              return;
 +      }
 +
 +      if (os_memcmp(src_addr, sm->own_addr, ETH_ALEN) == 0) {
 +              wpa_printf(MSG_DEBUG, "TDLS: Discard copy of own message");
 +              return;
 +      }
 +
 +      if (len < sizeof(*tf)) {
 +              wpa_printf(MSG_INFO, "TDLS: Drop too short frame");
 +              return;
 +      }
 +
 +      /* Check to make sure its a valid encapsulated TDLS frame */
 +      tf = (struct wpa_tdls_frame *) buf;
 +      if (tf->payloadtype != 2 /* TDLS_RFTYPE */ ||
 +          tf->category != WLAN_ACTION_TDLS) {
 +              wpa_printf(MSG_INFO, "TDLS: Invalid frame - payloadtype=%u "
 +                         "category=%u action=%u",
 +                         tf->payloadtype, tf->category, tf->action);
 +              return;
 +      }
 +
 +      switch (tf->action) {
 +      case WLAN_TDLS_SETUP_REQUEST:
 +              wpa_tdls_process_tpk_m1(sm, src_addr, buf, len);
 +              break;
 +      case WLAN_TDLS_SETUP_RESPONSE:
 +              wpa_tdls_process_tpk_m2(sm, src_addr, buf, len);
 +              break;
 +      case WLAN_TDLS_SETUP_CONFIRM:
 +              wpa_tdls_process_tpk_m3(sm, src_addr, buf, len);
 +              break;
 +      case WLAN_TDLS_TEARDOWN:
 +              wpa_tdls_recv_teardown(sm, src_addr, buf, len);
 +              break;
 +      case WLAN_TDLS_DISCOVERY_REQUEST:
 +              wpa_tdls_process_discovery_request(sm, src_addr, buf, len);
 +              break;
 +      default:
 +              /* Kernel code will process remaining frames */
 +              wpa_printf(MSG_DEBUG, "TDLS: Ignore TDLS frame action code %u",
 +                         tf->action);
 +              break;
 +      }
 +}
 +
 +
 +/**
 + * wpa_tdls_init - Initialize driver interface parameters for TDLS
 + * @wpa_s: Pointer to wpa_supplicant data
 + * Returns: 0 on success, -1 on failure
 + *
 + * This function is called to initialize driver interface parameters for TDLS.
 + * wpa_drv_init() must have been called before this function to initialize the
 + * driver interface.
 + */
 +int wpa_tdls_init(struct wpa_sm *sm)
 +{
 +      if (sm == NULL)
 +              return -1;
 +
 +      sm->l2_tdls = l2_packet_init(sm->bridge_ifname ? sm->bridge_ifname :
 +                                   sm->ifname,
 +                                   sm->own_addr,
 +                                   ETH_P_80211_ENCAP, wpa_supplicant_rx_tdls,
 +                                   sm, 0);
 +      if (sm->l2_tdls == NULL) {
 +              wpa_printf(MSG_ERROR, "TDLS: Failed to open l2_packet "
 +                         "connection");
 +              return -1;
 +      }
 +
 +      /*
 +       * Drivers that support TDLS but don't implement the get_capa callback
 +       * are assumed to perform everything internally
 +       */
 +      if (wpa_sm_tdls_get_capa(sm, &sm->tdls_supported,
 +                               &sm->tdls_external_setup,
 +                               &sm->tdls_chan_switch) < 0) {
 +              sm->tdls_supported = 1;
 +              sm->tdls_external_setup = 0;
 +      }
 +
 +      wpa_printf(MSG_DEBUG, "TDLS: TDLS operation%s supported by "
 +                 "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;
 +}
 +
 +
 +void wpa_tdls_teardown_peers(struct wpa_sm *sm)
 +{
 +      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_do_teardown(sm, peer,
 +                                           WLAN_REASON_DEAUTH_LEAVING);
 +              else
 +                      wpa_sm_tdls_oper(sm, TDLS_TEARDOWN, peer->addr);
 +
 +              peer = tmp;
 +      }
 +}
 +
 +
 +static void wpa_tdls_remove_peers(struct wpa_sm *sm)
 +{
 +      struct wpa_tdls_peer *peer, *tmp;
 +
 +      peer = sm->tdls;
 +
 +      while (peer) {
 +              int res;
 +              tmp = peer->next;
 +              res = wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, peer->addr);
 +              wpa_printf(MSG_DEBUG, "TDLS: Remove peer " MACSTR " (res=%d)",
 +                         MAC2STR(peer->addr), res);
 +              wpa_tdls_peer_free(sm, peer);
 +              peer = tmp;
 +      }
 +}
 +
 +
 +/**
 + * wpa_tdls_deinit - Deinitialize driver interface parameters for TDLS
 + *
 + * This function is called to recover driver interface parameters for TDLS
 + * and frees resources allocated for it.
 + */
 +void wpa_tdls_deinit(struct wpa_sm *sm)
 +{
 +      if (sm == NULL)
 +              return;
 +
 +      if (sm->l2_tdls)
 +              l2_packet_deinit(sm->l2_tdls);
 +      sm->l2_tdls = NULL;
 +
 +      wpa_tdls_remove_peers(sm);
 +}
 +
 +
 +void wpa_tdls_assoc(struct wpa_sm *sm)
 +{
 +      wpa_printf(MSG_DEBUG, "TDLS: Remove peers on association");
 +      wpa_tdls_remove_peers(sm);
 +}
 +
 +
 +void wpa_tdls_disassoc(struct wpa_sm *sm)
 +{
 +      wpa_printf(MSG_DEBUG, "TDLS: Remove peers on disassociation");
 +      wpa_tdls_remove_peers(sm);
 +}
 +
 +
- static int wpa_tdls_chan_switch_prohibited(struct wpa_eapol_ie_parse *elems)
++static int wpa_tdls_prohibited(struct ieee802_11_elems *elems)
 +{
 +      /* bit 38 - TDLS Prohibited */
 +      return !!(elems->ext_capab[2 + 4] & 0x40);
 +}
 +
 +
-       struct wpa_eapol_ie_parse elems;
++static int wpa_tdls_chan_switch_prohibited(struct ieee802_11_elems *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)
 +{
-       if (ies == NULL || wpa_supplicant_parse_ies(ies, len, &elems) < 0 ||
++      struct ieee802_11_elems elems;
 +
 +      sm->tdls_prohibited = 0;
 +      sm->tdls_chan_switch_prohibited = 0;
 +
-       struct wpa_eapol_ie_parse elems;
++      if (ies == NULL ||
++          ieee802_11_parse_elems(ies, len, &elems, 0) == ParseFailed ||
 +          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 (ies == NULL || wpa_supplicant_parse_ies(ies, len, &elems) < 0 ||
++      struct ieee802_11_elems elems;
 +
++      if (ies == NULL ||
++          ieee802_11_parse_elems(ies, len, &elems, 0) == ParseFailed ||
 +          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;
 +      }
 +}
 +
 +
 +void wpa_tdls_enable(struct wpa_sm *sm, int enabled)
 +{
 +      wpa_printf(MSG_DEBUG, "TDLS: %s", enabled ? "enabled" : "disabled");
 +      sm->tdls_disabled = !enabled;
 +}
 +
 +
 +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;
 +}
index 127e246ee7a0504d8708f1acb080198daa796e1e,0000000000000000000000000000000000000000..d397ff1605c28d0feed4660a7ed61270c0d11651
mode 100644,000000..100644
--- /dev/null
@@@ -1,2954 -1,0 +1,2977 @@@
-       wpa_hexdump(MSG_DEBUG, "RSN: received GTK in group key handshake",
-                   ie.gtk, ie.gtk_len);
 +/*
 + * WPA Supplicant - WPA state machine and EAPOL-Key processing
 + * 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 "includes.h"
 +
 +#include "common.h"
 +#include "crypto/aes_wrap.h"
 +#include "crypto/crypto.h"
 +#include "crypto/random.h"
 +#include "common/ieee802_11_defs.h"
 +#include "eapol_supp/eapol_supp_sm.h"
 +#include "wpa.h"
 +#include "eloop.h"
 +#include "preauth.h"
 +#include "pmksa_cache.h"
 +#include "wpa_i.h"
 +#include "wpa_ie.h"
 +#include "peerkey.h"
 +
 +
 +/**
 + * 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: EAPOL-Key message
 + * @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, 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
 +               * BSSID from the driver.
 +               */
 +              if (wpa_sm_get_bssid(sm, sm->bssid) < 0) {
 +                      wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
 +                              "WPA: Failed to read BSSID for "
 +                              "EAPOL-Key destination address");
 +              } else {
 +                      dest = sm->bssid;
 +                      wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
 +                              "WPA: Use BSSID (" MACSTR
 +                              ") as the destination for EAPOL-Key",
 +                              MAC2STR(dest));
 +              }
 +      }
 +      if (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 key_mgmt 0x%x MIC",
 +                      ver, sm->key_mgmt);
 +              goto out;
 +      }
 +      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);
 +out:
 +      os_free(msg);
 +}
 +
 +
 +/**
 + * wpa_sm_key_request - Send EAPOL-Key Request
 + * @sm: Pointer to WPA state machine data from wpa_sm_init()
 + * @error: Indicate whether this is an Michael MIC error report
 + * @pairwise: 1 = error report for pairwise packet, 0 = for group packet
 + *
 + * Send an EAPOL-Key Request to the current authenticator. This function is
 + * used to request rekeying and it is usually called when a local Michael MIC
 + * failure is detected.
 + */
 +void wpa_sm_key_request(struct wpa_sm *sm, int error, int pairwise)
 +{
 +      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, *key_mic;
 +
 +      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;
 +      else
 +              ver = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4;
 +
 +      if (wpa_sm_get_bssid(sm, bssid) < 0) {
 +              wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
 +                      "Failed to read BSSID for EAPOL-Key request");
 +              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,
 +                                hdrlen, &rlen, (void *) &reply);
 +      if (rbuf == NULL)
 +              return;
 +      reply192 = (struct wpa_eapol_key_192 *) reply;
 +
 +      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)
 +              key_info |= WPA_KEY_INFO_MIC;
 +      if (error)
 +              key_info |= WPA_KEY_INFO_ERROR;
 +      if (pairwise)
 +              key_info |= WPA_KEY_INFO_KEY_TYPE;
 +      WPA_PUT_BE16(reply->key_info, key_info);
 +      WPA_PUT_BE16(reply->key_length, 0);
 +      os_memcpy(reply->replay_counter, sm->request_counter,
 +                WPA_REPLAY_COUNTER_LEN);
 +      inc_byte_array(sm->request_counter, WPA_REPLAY_COUNTER_LEN);
 +
 +      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, 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 */
 +}
 +
 +
 +static int wpa_supplicant_get_pmk(struct wpa_sm *sm,
 +                                const unsigned char *src_addr,
 +                                const u8 *pmkid)
 +{
 +      int abort_cached = 0;
 +
 +      if (pmkid && !sm->cur_pmksa) {
 +              /* When using drivers that generate RSN IE, wpa_supplicant may
 +               * not have enough time to get the association information
 +               * event before receiving this 1/4 message, so try to find a
 +               * matching PMKSA cache entry here. */
 +              sm->cur_pmksa = pmksa_cache_get(sm->pmksa, src_addr, pmkid,
 +                                              NULL);
 +              if (sm->cur_pmksa) {
 +                      wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
 +                              "RSN: found matching PMKID from PMKSA cache");
 +              } else {
 +                      wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
 +                              "RSN: no matching PMKID found");
 +                      abort_cached = 1;
 +              }
 +      }
 +
 +      if (pmkid && sm->cur_pmksa &&
 +          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",
 +                              sm->pmk, sm->pmk_len);
 +              eapol_sm_notify_cached(sm->eapol);
 +#ifdef CONFIG_IEEE80211R
 +              sm->xxkey_len = 0;
 +#endif /* CONFIG_IEEE80211R */
 +      } else if (wpa_key_mgmt_wpa_ieee8021x(sm->key_mgmt) && sm->eapol) {
 +              int res, pmk_len;
 +              pmk_len = PMK_LEN;
 +              res = eapol_sm_get_key(sm->eapol, sm->pmk, PMK_LEN);
 +              if (res) {
 +                      /*
 +                       * EAP-LEAP is an exception from other EAP methods: it
 +                       * uses only 16-byte PMK.
 +                       */
 +                      res = eapol_sm_get_key(sm->eapol, sm->pmk, 16);
 +                      pmk_len = 16;
 +              } else {
 +#ifdef CONFIG_IEEE80211R
 +                      u8 buf[2 * PMK_LEN];
 +                      if (eapol_sm_get_key(sm->eapol, buf, 2 * PMK_LEN) == 0)
 +                      {
 +                              os_memcpy(sm->xxkey, buf + PMK_LEN, PMK_LEN);
 +                              sm->xxkey_len = PMK_LEN;
 +                              os_memset(buf, 0, sizeof(buf));
 +                      }
 +#endif /* CONFIG_IEEE80211R */
 +              }
 +              if (res == 0) {
 +                      struct rsn_pmksa_cache_entry *sa = NULL;
 +                      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);
 +                      }
 +                      if (!sm->cur_pmksa && pmkid &&
 +                          pmksa_cache_get(sm->pmksa, src_addr, pmkid, NULL))
 +                      {
 +                              wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
 +                                      "RSN: the new PMK matches with the "
 +                                      "PMKID");
 +                              abort_cached = 0;
++                      } else if (sa && !sm->cur_pmksa && pmkid) {
++                              /*
++                               * It looks like the authentication server
++                               * derived mismatching MSK. This should not
++                               * really happen, but bugs happen.. There is not
++                               * much we can do here without knowing what
++                               * exactly caused the server to misbehave.
++                               */
++                              wpa_dbg(sm->ctx->msg_ctx, MSG_INFO,
++                                      "RSN: PMKID mismatch - authentication server may have derived different MSK?!");
++                              return -1;
 +                      }
 +
 +                      if (!sm->cur_pmksa)
 +                              sm->cur_pmksa = sa;
 +              } else {
 +                      wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
 +                              "WPA: Failed to get master session key from "
 +                              "EAPOL state machines - key handshake "
 +                              "aborted");
 +                      if (sm->cur_pmksa) {
 +                              wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
 +                                      "RSN: Cancelled PMKSA caching "
 +                                      "attempt");
 +                              sm->cur_pmksa = NULL;
 +                              abort_cached = 1;
 +                      } else if (!abort_cached) {
 +                              return -1;
 +                      }
 +              }
 +      }
 +
 +      if (abort_cached && wpa_key_mgmt_wpa_ieee8021x(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;
 +
 +              wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
 +                      "RSN: no PMKSA entry found - trigger "
 +                      "full EAP authentication");
 +              buf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_START,
 +                                       NULL, 0, &buflen, NULL);
 +              if (buf) {
 +                      wpa_sm_ether_send(sm, sm->bssid, ETH_P_EAPOL,
 +                                        buf, buflen);
 +                      os_free(buf);
 +                      return -2;
 +              }
 +
 +              return -1;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +/**
 + * wpa_supplicant_send_2_of_4 - Send message 2 of WPA/RSN 4-Way Handshake
 + * @sm: Pointer to WPA state machine data from wpa_sm_init()
 + * @dst: Destination address for the frame
 + * @key: Pointer to the EAPOL-Key frame header
 + * @ver: Version bits from EAPOL-Key Key Info
 + * @nonce: Nonce value for the EAPOL-Key frame
 + * @wpa_ie: WPA/RSN IE
 + * @wpa_ie_len: Length of the WPA/RSN IE
 + * @ptk: PTK to use for keyed hash and encryption
 + * Returns: 0 on success, -1 on failure
 + */
 +int wpa_supplicant_send_2_of_4(struct wpa_sm *sm, const unsigned char *dst,
 +                             const struct wpa_eapol_key *key,
 +                             int ver, const u8 *nonce,
 +                             const u8 *wpa_ie, size_t wpa_ie_len,
 +                             struct wpa_ptk *ptk)
 +{
 +      size_t mic_len, hdrlen, rlen;
 +      struct wpa_eapol_key *reply;
 +      struct wpa_eapol_key_192 *reply192;
 +      u8 *rbuf, *key_mic;
 +      u8 *rsn_ie_buf = NULL;
 +
 +      if (wpa_ie == NULL) {
 +              wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: No wpa_ie set - "
 +                      "cannot generate msg 2/4");
 +              return -1;
 +      }
 +
 +#ifdef CONFIG_IEEE80211R
 +      if (wpa_key_mgmt_ft(sm->key_mgmt)) {
 +              int res;
 +
 +              /*
 +               * Add PMKR1Name into RSN IE (PMKID-List) and add MDIE and
 +               * FTIE from (Re)Association Response.
 +               */
 +              rsn_ie_buf = os_malloc(wpa_ie_len + 2 + 2 + PMKID_LEN +
 +                                     sm->assoc_resp_ies_len);
 +              if (rsn_ie_buf == NULL)
 +                      return -1;
 +              os_memcpy(rsn_ie_buf, wpa_ie, wpa_ie_len);
 +              res = wpa_insert_pmkid(rsn_ie_buf, wpa_ie_len,
 +                                     sm->pmk_r1_name);
 +              if (res < 0) {
 +                      os_free(rsn_ie_buf);
 +                      return -1;
 +              }
 +              wpa_ie_len += res;
 +
 +              if (sm->assoc_resp_ies) {
 +                      os_memcpy(rsn_ie_buf + wpa_ie_len, sm->assoc_resp_ies,
 +                                sm->assoc_resp_ies_len);
 +                      wpa_ie_len += sm->assoc_resp_ies_len;
 +              }
 +
 +              wpa_ie = rsn_ie_buf;
 +      }
 +#endif /* CONFIG_IEEE80211R */
 +
 +      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, 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 ||
 +                     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 || 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_hexdump(MSG_DEBUG, "WPA: Replay Counter", reply->replay_counter,
 +                  WPA_REPLAY_COUNTER_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, 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)
 +{
 +#ifdef CONFIG_IEEE80211R
 +      if (wpa_key_mgmt_ft(sm->key_mgmt))
 +              return wpa_derive_ptk_ft(sm, src_addr, key, ptk);
 +#endif /* CONFIG_IEEE80211R */
 +
 +      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, const u8 *key_data,
 +                                        size_t key_data_len)
 +{
 +      struct wpa_eapol_ie_parse ie;
 +      struct wpa_ptk *ptk;
 +      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 "
 +                      "found (msg 1 of 4)");
 +              return;
 +      }
 +
 +      wpa_sm_set_state(sm, WPA_4WAY_HANDSHAKE);
 +      wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: RX message 1 of 4-Way "
 +              "Handshake from " MACSTR " (ver=%d)", MAC2STR(src_addr), ver);
 +
 +      os_memset(&ie, 0, sizeof(ie));
 +
 +      if (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN) {
 +              /* RSN: msg 1/4 should contain PMKID for the selected PMK */
 +              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 "
 +                                  "Authenticator", ie.pmkid, PMKID_LEN);
 +              }
 +      }
 +
 +      res = wpa_supplicant_get_pmk(sm, src_addr, ie.pmkid);
 +      if (res == -2) {
 +              wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: Do not reply to "
 +                      "msg 1/4 - requesting full EAP authentication");
 +              return;
 +      }
 +      if (res)
 +              goto failed;
 +
 +      if (sm->renew_snonce) {
 +              if (random_get_bytes(sm->snonce, WPA_NONCE_LEN)) {
 +                      wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
 +                              "WPA: Failed to get random data for SNonce");
 +                      goto failed;
 +              }
 +              sm->renew_snonce = 0;
 +              wpa_hexdump(MSG_DEBUG, "WPA: Renewed SNonce",
 +                          sm->snonce, WPA_NONCE_LEN);
 +      }
 +
 +      /* Calculate PTK which will be stored as a temporary PTK until it has
 +       * been verified when processing message 3/4. */
 +      ptk = &sm->tptk;
 +      wpa_derive_ptk(sm, src_addr, key, ptk);
 +      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,
 +                                     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);
 +}
 +
 +
 +static void wpa_sm_start_preauth(void *eloop_ctx, void *timeout_ctx)
 +{
 +      struct wpa_sm *sm = eloop_ctx;
 +      rsn_preauth_candidate_process(sm);
 +}
 +
 +
 +static void wpa_supplicant_key_neg_complete(struct wpa_sm *sm,
 +                                          const u8 *addr, int secure)
 +{
 +      wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
 +              "WPA: Key negotiation completed with "
 +              MACSTR " [PTK=%s GTK=%s]", MAC2STR(addr),
 +              wpa_cipher_txt(sm->pairwise_cipher),
 +              wpa_cipher_txt(sm->group_cipher));
 +      wpa_sm_cancel_auth_timeout(sm);
 +      wpa_sm_set_state(sm, WPA_COMPLETED);
 +
 +      if (secure) {
 +              wpa_sm_mlme_setprotection(
 +                      sm, addr, MLME_SETPROTECTION_PROTECT_TYPE_RX_TX,
 +                      MLME_SETPROTECTION_KEY_TYPE_PAIRWISE);
 +              eapol_sm_notify_portValid(sm->eapol, TRUE);
 +              if (wpa_key_mgmt_wpa_psk(sm->key_mgmt))
 +                      eapol_sm_notify_eap_success(sm->eapol, TRUE);
 +              /*
 +               * Start preauthentication after a short wait to avoid a
 +               * possible race condition between the data receive and key
 +               * configuration after the 4-Way Handshake. This increases the
 +               * likelihood of the first preauth EAPOL-Start frame getting to
 +               * the target AP.
 +               */
 +              eloop_register_timeout(1, 0, wpa_sm_start_preauth, sm, NULL);
 +      }
 +
 +      if (sm->cur_pmksa && sm->cur_pmksa->opportunistic) {
 +              wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
 +                      "RSN: Authenticator accepted "
 +                      "opportunistic PMKSA entry - marking it valid");
 +              sm->cur_pmksa->opportunistic = 0;
 +      }
 +
 +#ifdef CONFIG_IEEE80211R
 +      if (wpa_key_mgmt_ft(sm->key_mgmt)) {
 +              /* Prepare for the next transition */
 +              wpa_ft_prepare_auth_request(sm, NULL);
 +      }
 +#endif /* CONFIG_IEEE80211R */
 +}
 +
 +
 +static void wpa_sm_rekey_ptk(void *eloop_ctx, void *timeout_ctx)
 +{
 +      struct wpa_sm *sm = eloop_ctx;
 +      wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Request PTK rekeying");
 +      wpa_sm_key_request(sm, 0, 1);
 +}
 +
 +
 +static int wpa_supplicant_install_ptk(struct wpa_sm *sm,
 +                                    const struct wpa_eapol_key *key)
 +{
 +      int keylen, rsclen;
 +      enum wpa_alg alg;
 +      const u8 *key_rsc;
 +      u8 null_rsc[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
 +
 +      wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
 +              "WPA: Installing PTK to the driver");
 +
 +      if (sm->pairwise_cipher == WPA_CIPHER_NONE) {
 +              wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Pairwise Cipher "
 +                      "Suite: NONE - do not use pairwise keys");
 +              return 0;
 +      }
 +
 +      if (!wpa_cipher_valid_pairwise(sm->pairwise_cipher)) {
 +              wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
 +                      "WPA: Unsupported pairwise cipher %d",
 +                      sm->pairwise_cipher);
 +              return -1;
 +      }
 +
 +      alg = wpa_cipher_to_alg(sm->pairwise_cipher);
 +      keylen = wpa_cipher_key_len(sm->pairwise_cipher);
 +      rsclen = wpa_cipher_rsc_len(sm->pairwise_cipher);
 +
 +      if (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN) {
 +              key_rsc = null_rsc;
 +      } else {
 +              key_rsc = key->key_rsc;
 +              wpa_hexdump(MSG_DEBUG, "WPA: RSC", key_rsc, rsclen);
 +      }
 +
 +      if (wpa_sm_set_key(sm, alg, sm->bssid, 0, 1, key_rsc, rsclen,
 +                         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 ")",
 +                      alg, keylen, MAC2STR(sm->bssid));
 +              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,
 +                                     sm, NULL);
 +      }
 +
 +      return 0;
 +}
 +
 +
 +static int wpa_supplicant_check_group_cipher(struct wpa_sm *sm,
 +                                           int group_cipher,
 +                                           int keylen, int maxkeylen,
 +                                           int *key_rsc_len,
 +                                           enum wpa_alg *alg)
 +{
 +      int klen;
 +
 +      *alg = wpa_cipher_to_alg(group_cipher);
 +      if (*alg == WPA_ALG_NONE) {
 +              wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
 +                      "WPA: Unsupported Group Cipher %d",
 +                      group_cipher);
 +              return -1;
 +      }
 +      *key_rsc_len = wpa_cipher_rsc_len(group_cipher);
 +
 +      klen = wpa_cipher_key_len(group_cipher);
 +      if (keylen != klen || maxkeylen < klen) {
 +              wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
 +                      "WPA: Unsupported %s Group Cipher key length %d (%d)",
 +                      wpa_cipher_txt(group_cipher), keylen, maxkeylen);
 +              return -1;
 +      }
 +      return 0;
 +}
 +
 +
 +struct wpa_gtk_data {
 +      enum wpa_alg alg;
 +      int tx, key_rsc_len, keyidx;
 +      u8 gtk[32];
 +      int gtk_len;
 +};
 +
 +
 +static int wpa_supplicant_install_gtk(struct wpa_sm *sm,
 +                                    const struct wpa_gtk_data *gd,
 +                                    const u8 *key_rsc)
 +{
 +      const u8 *_gtk = gd->gtk;
 +      u8 gtk_buf[32];
 +
 +      wpa_hexdump_key(MSG_DEBUG, "WPA: Group Key", gd->gtk, gd->gtk_len);
 +      wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
 +              "WPA: Installing GTK to the driver (keyidx=%d tx=%d len=%d)",
 +              gd->keyidx, gd->tx, gd->gtk_len);
 +      wpa_hexdump(MSG_DEBUG, "WPA: RSC", key_rsc, gd->key_rsc_len);
 +      if (sm->group_cipher == WPA_CIPHER_TKIP) {
 +              /* Swap Tx/Rx keys for Michael MIC */
 +              os_memcpy(gtk_buf, gd->gtk, 16);
 +              os_memcpy(gtk_buf + 16, gd->gtk + 24, 8);
 +              os_memcpy(gtk_buf + 24, gd->gtk + 16, 8);
 +              _gtk = gtk_buf;
 +      }
 +      if (sm->pairwise_cipher == WPA_CIPHER_NONE) {
 +              if (wpa_sm_set_key(sm, gd->alg, NULL,
 +                                 gd->keyidx, 1, key_rsc, gd->key_rsc_len,
 +                                 _gtk, gd->gtk_len) < 0) {
 +                      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,
 +                                gd->keyidx, gd->tx, key_rsc, gd->key_rsc_len,
 +                                _gtk, gd->gtk_len) < 0) {
 +              wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
 +                      "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;
 +}
 +
 +
 +static int wpa_supplicant_gtk_tx_bit_workaround(const struct wpa_sm *sm,
 +                                              int tx)
 +{
 +      if (tx && sm->pairwise_cipher != WPA_CIPHER_NONE) {
 +              /* Ignore Tx bit for GTK if a pairwise key is used. One AP
 +               * seemed to set this bit (incorrectly, since Tx is only when
 +               * doing Group Key only APs) and without this workaround, the
 +               * data connection does not work because wpa_supplicant
 +               * configured non-zero keyidx to be used for unicast. */
 +              wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
 +                      "WPA: Tx bit set for GTK, but pairwise "
 +                      "keys are used - ignore Tx bit");
 +              return 0;
 +      }
 +      return tx;
 +}
 +
 +
 +static int wpa_supplicant_pairwise_gtk(struct wpa_sm *sm,
 +                                     const struct wpa_eapol_key *key,
 +                                     const u8 *gtk, size_t gtk_len,
 +                                     int key_info)
 +{
 +      struct wpa_gtk_data gd;
 +
 +      /*
 +       * IEEE Std 802.11i-2004 - 8.5.2 EAPOL-Key frames - Figure 43x
 +       * GTK KDE format:
 +       * KeyID[bits 0-1], Tx [bit 2], Reserved [bits 3-7]
 +       * Reserved [bits 0-7]
 +       * GTK
 +       */
 +
 +      os_memset(&gd, 0, sizeof(gd));
 +      wpa_hexdump_key(MSG_DEBUG, "RSN: received GTK in pairwise handshake",
 +                      gtk, gtk_len);
 +
 +      if (gtk_len < 2 || gtk_len - 2 > sizeof(gd.gtk))
 +              return -1;
 +
 +      gd.keyidx = gtk[0] & 0x3;
 +      gd.tx = wpa_supplicant_gtk_tx_bit_workaround(sm,
 +                                                   !!(gtk[0] & BIT(2)));
 +      gtk += 2;
 +      gtk_len -= 2;
 +
 +      os_memcpy(gd.gtk, gtk, gtk_len);
 +      gd.gtk_len = gtk_len;
 +
 +      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);
 +      return 0;
 +}
 +
 +
 +static int ieee80211w_set_keys(struct wpa_sm *sm,
 +                             struct wpa_eapol_ie_parse *ie)
 +{
 +#ifdef CONFIG_IEEE80211W
 +      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;
 +              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);
 +              wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: IGTK keyid %d "
 +                      "pn %02x%02x%02x%02x%02x%02x",
 +                      keyidx, MAC2STR(igtk->pn));
 +              wpa_hexdump_key(MSG_DEBUG, "WPA: IGTK",
 +                              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_cipher_to_alg(sm->mgmt_group_cipher),
 +                                 broadcast_ether_addr,
 +                                 keyidx, 0, igtk->pn, sizeof(igtk->pn),
 +                                 igtk->igtk, len) < 0) {
 +                      wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
 +                              "WPA: Failed to configure IGTK to the driver");
 +                      return -1;
 +              }
 +      }
 +
 +      return 0;
 +#else /* CONFIG_IEEE80211W */
 +      return 0;
 +#endif /* CONFIG_IEEE80211W */
 +}
 +
 +
 +static void wpa_report_ie_mismatch(struct wpa_sm *sm,
 +                                 const char *reason, const u8 *src_addr,
 +                                 const u8 *wpa_ie, size_t wpa_ie_len,
 +                                 const u8 *rsn_ie, size_t rsn_ie_len)
 +{
 +      wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: %s (src=" MACSTR ")",
 +              reason, MAC2STR(src_addr));
 +
 +      if (sm->ap_wpa_ie) {
 +              wpa_hexdump(MSG_INFO, "WPA: WPA IE in Beacon/ProbeResp",
 +                          sm->ap_wpa_ie, sm->ap_wpa_ie_len);
 +      }
 +      if (wpa_ie) {
 +              if (!sm->ap_wpa_ie) {
 +                      wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
 +                              "WPA: No WPA IE in Beacon/ProbeResp");
 +              }
 +              wpa_hexdump(MSG_INFO, "WPA: WPA IE in 3/4 msg",
 +                          wpa_ie, wpa_ie_len);
 +      }
 +
 +      if (sm->ap_rsn_ie) {
 +              wpa_hexdump(MSG_INFO, "WPA: RSN IE in Beacon/ProbeResp",
 +                          sm->ap_rsn_ie, sm->ap_rsn_ie_len);
 +      }
 +      if (rsn_ie) {
 +              if (!sm->ap_rsn_ie) {
 +                      wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
 +                              "WPA: No RSN IE in Beacon/ProbeResp");
 +              }
 +              wpa_hexdump(MSG_INFO, "WPA: RSN IE in 3/4 msg",
 +                          rsn_ie, rsn_ie_len);
 +      }
 +
 +      wpa_sm_deauthenticate(sm, WLAN_REASON_IE_IN_4WAY_DIFFERS);
 +}
 +
 +
 +#ifdef CONFIG_IEEE80211R
 +
 +static int ft_validate_mdie(struct wpa_sm *sm,
 +                          const unsigned char *src_addr,
 +                          struct wpa_eapol_ie_parse *ie,
 +                          const u8 *assoc_resp_mdie)
 +{
 +      struct rsn_mdie *mdie;
 +
 +      mdie = (struct rsn_mdie *) (ie->mdie + 2);
 +      if (ie->mdie == NULL || ie->mdie_len < 2 + sizeof(*mdie) ||
 +          os_memcmp(mdie->mobility_domain, sm->mobility_domain,
 +                    MOBILITY_DOMAIN_ID_LEN) != 0) {
 +              wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "FT: MDIE in msg 3/4 did "
 +                      "not match with the current mobility domain");
 +              return -1;
 +      }
 +
 +      if (assoc_resp_mdie &&
 +          (assoc_resp_mdie[1] != ie->mdie[1] ||
 +           os_memcmp(assoc_resp_mdie, ie->mdie, 2 + ie->mdie[1]) != 0)) {
 +              wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "FT: MDIE mismatch");
 +              wpa_hexdump(MSG_DEBUG, "FT: MDIE in EAPOL-Key msg 3/4",
 +                          ie->mdie, 2 + ie->mdie[1]);
 +              wpa_hexdump(MSG_DEBUG, "FT: MDIE in (Re)Association Response",
 +                          assoc_resp_mdie, 2 + assoc_resp_mdie[1]);
 +              return -1;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +static int ft_validate_ftie(struct wpa_sm *sm,
 +                          const unsigned char *src_addr,
 +                          struct wpa_eapol_ie_parse *ie,
 +                          const u8 *assoc_resp_ftie)
 +{
 +      if (ie->ftie == NULL) {
 +              wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
 +                      "FT: No FTIE in EAPOL-Key msg 3/4");
 +              return -1;
 +      }
 +
 +      if (assoc_resp_ftie == NULL)
 +              return 0;
 +
 +      if (assoc_resp_ftie[1] != ie->ftie[1] ||
 +          os_memcmp(assoc_resp_ftie, ie->ftie, 2 + ie->ftie[1]) != 0) {
 +              wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "FT: FTIE mismatch");
 +              wpa_hexdump(MSG_DEBUG, "FT: FTIE in EAPOL-Key msg 3/4",
 +                          ie->ftie, 2 + ie->ftie[1]);
 +              wpa_hexdump(MSG_DEBUG, "FT: FTIE in (Re)Association Response",
 +                          assoc_resp_ftie, 2 + assoc_resp_ftie[1]);
 +              return -1;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +static int ft_validate_rsnie(struct wpa_sm *sm,
 +                           const unsigned char *src_addr,
 +                           struct wpa_eapol_ie_parse *ie)
 +{
 +      struct wpa_ie_data rsn;
 +
 +      if (!ie->rsn_ie)
 +              return 0;
 +
 +      /*
 +       * Verify that PMKR1Name from EAPOL-Key message 3/4
 +       * matches with the value we derived.
 +       */
 +      if (wpa_parse_wpa_ie_rsn(ie->rsn_ie, ie->rsn_ie_len, &rsn) < 0 ||
 +          rsn.num_pmkid != 1 || rsn.pmkid == NULL) {
 +              wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "FT: No PMKR1Name in "
 +                      "FT 4-way handshake message 3/4");
 +              return -1;
 +      }
 +
 +      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");
 +              wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name from Authenticator",
 +                          rsn.pmkid, WPA_PMK_NAME_LEN);
 +              wpa_hexdump(MSG_DEBUG, "FT: Derived PMKR1Name",
 +                          sm->pmk_r1_name, WPA_PMK_NAME_LEN);
 +              return -1;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +static int wpa_supplicant_validate_ie_ft(struct wpa_sm *sm,
 +                                       const unsigned char *src_addr,
 +                                       struct wpa_eapol_ie_parse *ie)
 +{
 +      const u8 *pos, *end, *mdie = NULL, *ftie = NULL;
 +
 +      if (sm->assoc_resp_ies) {
 +              pos = sm->assoc_resp_ies;
 +              end = pos + sm->assoc_resp_ies_len;
 +              while (pos + 2 < end) {
 +                      if (pos + 2 + pos[1] > end)
 +                              break;
 +                      switch (*pos) {
 +                      case WLAN_EID_MOBILITY_DOMAIN:
 +                              mdie = pos;
 +                              break;
 +                      case WLAN_EID_FAST_BSS_TRANSITION:
 +                              ftie = pos;
 +                              break;
 +                      }
 +                      pos += 2 + pos[1];
 +              }
 +      }
 +
 +      if (ft_validate_mdie(sm, src_addr, ie, mdie) < 0 ||
 +          ft_validate_ftie(sm, src_addr, ie, ftie) < 0 ||
 +          ft_validate_rsnie(sm, src_addr, ie) < 0)
 +              return -1;
 +
 +      return 0;
 +}
 +
 +#endif /* CONFIG_IEEE80211R */
 +
 +
 +static int wpa_supplicant_validate_ie(struct wpa_sm *sm,
 +                                    const unsigned char *src_addr,
 +                                    struct wpa_eapol_ie_parse *ie)
 +{
 +      if (sm->ap_wpa_ie == NULL && sm->ap_rsn_ie == NULL) {
 +              wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
 +                      "WPA: No WPA/RSN IE for this AP known. "
 +                      "Trying to get from scan results");
 +              if (wpa_sm_get_beacon_ie(sm) < 0) {
 +                      wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
 +                              "WPA: Could not find AP from "
 +                              "the scan results");
 +              } else {
 +                      wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG,
 +                              "WPA: Found the current AP from "
 +                              "updated scan results");
 +              }
 +      }
 +
 +      if (ie->wpa_ie == NULL && ie->rsn_ie == NULL &&
 +          (sm->ap_wpa_ie || sm->ap_rsn_ie)) {
 +              wpa_report_ie_mismatch(sm, "IE in 3/4 msg does not match "
 +                                     "with IE in Beacon/ProbeResp (no IE?)",
 +                                     src_addr, ie->wpa_ie, ie->wpa_ie_len,
 +                                     ie->rsn_ie, ie->rsn_ie_len);
 +              return -1;
 +      }
 +
 +      if ((ie->wpa_ie && sm->ap_wpa_ie &&
 +           (ie->wpa_ie_len != sm->ap_wpa_ie_len ||
 +            os_memcmp(ie->wpa_ie, sm->ap_wpa_ie, ie->wpa_ie_len) != 0)) ||
 +          (ie->rsn_ie && sm->ap_rsn_ie &&
 +           wpa_compare_rsn_ie(wpa_key_mgmt_ft(sm->key_mgmt),
 +                              sm->ap_rsn_ie, sm->ap_rsn_ie_len,
 +                              ie->rsn_ie, ie->rsn_ie_len))) {
 +              wpa_report_ie_mismatch(sm, "IE in 3/4 msg does not match "
 +                                     "with IE in Beacon/ProbeResp",
 +                                     src_addr, ie->wpa_ie, ie->wpa_ie_len,
 +                                     ie->rsn_ie, ie->rsn_ie_len);
 +              return -1;
 +      }
 +
 +      if (sm->proto == WPA_PROTO_WPA &&
 +          ie->rsn_ie && sm->ap_rsn_ie == NULL && sm->rsn_enabled) {
 +              wpa_report_ie_mismatch(sm, "Possible downgrade attack "
 +                                     "detected - RSN was enabled and RSN IE "
 +                                     "was in msg 3/4, but not in "
 +                                     "Beacon/ProbeResp",
 +                                     src_addr, ie->wpa_ie, ie->wpa_ie_len,
 +                                     ie->rsn_ie, ie->rsn_ie_len);
 +              return -1;
 +      }
 +
 +#ifdef CONFIG_IEEE80211R
 +      if (wpa_key_mgmt_ft(sm->key_mgmt) &&
 +          wpa_supplicant_validate_ie_ft(sm, src_addr, ie) < 0)
 +              return -1;
 +#endif /* CONFIG_IEEE80211R */
 +
 +      return 0;
 +}
 +
 +
 +/**
 + * wpa_supplicant_send_4_of_4 - Send message 4 of WPA/RSN 4-Way Handshake
 + * @sm: Pointer to WPA state machine data from wpa_sm_init()
 + * @dst: Destination address for the frame
 + * @key: Pointer to the EAPOL-Key frame header
 + * @ver: Version bits from EAPOL-Key Key Info
 + * @key_info: Key Info
 + * @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,
 +                             struct wpa_ptk *ptk)
 +{
 +      size_t mic_len, hdrlen, rlen;
 +      struct wpa_eapol_key *reply;
 +      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,
 +                                hdrlen, &rlen, (void *) &reply);
 +      if (rbuf == NULL)
 +              return -1;
 +      reply192 = (struct wpa_eapol_key_192 *) reply;
 +
 +      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 || 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);
 +
 +      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, ptk->kck_len, ver, dst, ETH_P_EAPOL,
 +                         rbuf, rlen, key_mic);
 +
 +      return 0;
 +}
 +
 +
 +static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm,
 +                                        const struct wpa_eapol_key *key,
 +                                        u16 ver, const u8 *key_data,
 +                                        size_t key_data_len)
 +{
 +      u16 key_info, keylen;
 +      struct wpa_eapol_ie_parse ie;
 +
 +      wpa_sm_set_state(sm, WPA_4WAY_HANDSHAKE);
 +      wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: RX message 3 of 4-Way "
 +              "Handshake from " MACSTR " (ver=%d)", MAC2STR(sm->bssid), ver);
 +
 +      key_info = WPA_GET_BE16(key->key_info);
 +
 +      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,
 +                      "WPA: GTK IE in unencrypted key data");
 +              goto failed;
 +      }
 +#ifdef CONFIG_IEEE80211W
 +      if (ie.igtk && !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
 +              wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
 +                      "WPA: IGTK KDE in unencrypted key data");
 +              goto failed;
 +      }
 +
 +      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);
 +              goto failed;
 +      }
 +#endif /* CONFIG_IEEE80211W */
 +
 +      if (wpa_supplicant_validate_ie(sm, sm->bssid, &ie) < 0)
 +              goto failed;
 +
 +      if (os_memcmp(sm->anonce, key->key_nonce, WPA_NONCE_LEN) != 0) {
 +              wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
 +                      "WPA: ANonce from message 1 of 4-Way Handshake "
 +                      "differs from 3 of 4-Way Handshake - drop packet (src="
 +                      MACSTR ")", MAC2STR(sm->bssid));
 +              goto failed;
 +      }
 +
 +      keylen = WPA_GET_BE16(key->key_length);
 +      if (keylen != wpa_cipher_key_len(sm->pairwise_cipher)) {
 +              wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
 +                      "WPA: Invalid %s key length %d (src=" MACSTR
 +                      ")", wpa_cipher_txt(sm->pairwise_cipher), keylen,
 +                      MAC2STR(sm->bssid));
 +              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,
 +                                     &sm->ptk)) {
 +              goto failed;
 +      }
 +
 +      /* SNonce was successfully used in msg 3/4, so mark it to be renewed
 +       * for the next 4-Way Handshake. If msg 3 is received again, the old
 +       * SNonce will still be used to avoid changing PTK. */
 +      sm->renew_snonce = 1;
 +
 +      if (key_info & WPA_KEY_INFO_INSTALL) {
 +              if (wpa_supplicant_install_ptk(sm, key))
 +                      goto failed;
 +      }
 +
 +      if (key_info & WPA_KEY_INFO_SECURE) {
 +              wpa_sm_mlme_setprotection(
 +                      sm, sm->bssid, MLME_SETPROTECTION_PROTECT_TYPE_RX,
 +                      MLME_SETPROTECTION_KEY_TYPE_PAIRWISE);
 +              eapol_sm_notify_portValid(sm->eapol, TRUE);
 +      }
 +      wpa_sm_set_state(sm, WPA_GROUP_HANDSHAKE);
 +
 +      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,
 +                      "RSN: Failed to configure GTK");
 +              goto failed;
 +      }
 +
 +      if (ieee80211w_set_keys(sm, &ie) < 0) {
 +              wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
 +                      "RSN: Failed to configure IGTK");
 +              goto failed;
 +      }
 +
 +      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:
 +      wpa_sm_deauthenticate(sm, WLAN_REASON_UNSPECIFIED);
 +}
 +
 +
 +static int wpa_supplicant_process_1_of_2_rsn(struct wpa_sm *sm,
 +                                           const u8 *keydata,
 +                                           size_t keydatalen,
 +                                           u16 key_info,
 +                                           struct wpa_gtk_data *gd)
 +{
 +      int maxkeylen;
 +      struct wpa_eapol_ie_parse ie;
 +
 +      wpa_hexdump(MSG_DEBUG, "RSN: msg 1/2 key data", keydata, keydatalen);
 +      if (wpa_supplicant_parse_ies(keydata, keydatalen, &ie) < 0)
 +              return -1;
 +      if (ie.gtk && !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
 +              wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
 +                      "WPA: GTK IE in unencrypted key data");
 +              return -1;
 +      }
 +      if (ie.gtk == NULL) {
 +              wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
 +                      "WPA: No GTK IE in Group Key msg 1/2");
 +              return -1;
 +      }
 +      maxkeylen = gd->gtk_len = ie.gtk_len - 2;
 +
 +      if (wpa_supplicant_check_group_cipher(sm, sm->group_cipher,
 +                                            gd->gtk_len, maxkeylen,
 +                                            &gd->key_rsc_len, &gd->alg))
 +              return -1;
 +
++      wpa_hexdump_key(MSG_DEBUG, "RSN: received GTK in group key handshake",
++                      ie.gtk, ie.gtk_len);
 +      gd->keyidx = ie.gtk[0] & 0x3;
 +      gd->tx = wpa_supplicant_gtk_tx_bit_workaround(sm,
 +                                                    !!(ie.gtk[0] & BIT(2)));
 +      if (ie.gtk_len - 2 > sizeof(gd->gtk)) {
 +              wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
 +                      "RSN: Too long GTK in GTK IE (len=%lu)",
 +                      (unsigned long) ie.gtk_len - 2);
 +              return -1;
 +      }
 +      os_memcpy(gd->gtk, ie.gtk + 2, ie.gtk_len - 2);
 +
 +      if (ieee80211w_set_keys(sm, &ie) < 0)
 +              wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
 +                      "RSN: Failed to configure IGTK");
 +
 +      return 0;
 +}
 +
 +
 +static int wpa_supplicant_process_1_of_2_wpa(struct wpa_sm *sm,
 +                                           const struct wpa_eapol_key *key,
 +                                           const u8 *key_data,
 +                                           size_t key_data_len, u16 key_info,
 +                                           u16 ver, struct wpa_gtk_data *gd)
 +{
 +      size_t maxkeylen;
 +      u16 gtk_len;
 +
 +      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,
 +                              "WPA: Too short maxkeylen (%lu)",
 +                              (unsigned long) maxkeylen);
 +                      return -1;
 +              }
 +              maxkeylen -= 8;
 +      }
 +
 +      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 && sm->ptk.kek_len == 16) {
++#ifdef CONFIG_NO_RC4
++              wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
++                      "WPA: RC4 not supported in the build");
++              return -1;
++#else /* CONFIG_NO_RC4 */
 +              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) key_data_len);
 +                      return -1;
 +              }
 +              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));
++#endif /* CONFIG_NO_RC4 */
 +      } else if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
 +              if (maxkeylen % 8) {
 +                      wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
 +                              "WPA: Unsupported AES-WRAP len %lu",
 +                              (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) key_data_len,
 +                              (unsigned long) maxkeylen);
 +                      return -1;
 +              }
 +              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");
 +                      return -1;
 +              }
 +      } else {
 +              wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
 +                      "WPA: Unsupported key_info type %d", ver);
 +              return -1;
 +      }
 +      gd->tx = wpa_supplicant_gtk_tx_bit_workaround(
 +              sm, !!(key_info & WPA_KEY_INFO_TXRX));
 +      return 0;
 +}
 +
 +
 +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 mic_len, hdrlen, rlen;
 +      struct wpa_eapol_key *reply;
 +      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,
 +                                hdrlen, &rlen, (void *) &reply);
 +      if (rbuf == NULL)
 +              return -1;
 +      reply192 = (struct wpa_eapol_key_192 *) reply;
 +
 +      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 || 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);
 +
 +      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, sm->ptk.kck_len, ver, sm->bssid,
 +                         ETH_P_EAPOL, rbuf, rlen, key_mic);
 +
 +      return 0;
 +}
 +
 +
 +static void wpa_supplicant_process_1_of_2(struct wpa_sm *sm,
 +                                        const unsigned char *src_addr,
 +                                        const struct wpa_eapol_key *key,
 +                                        const u8 *key_data,
 +                                        size_t key_data_len, u16 ver)
 +{
 +      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;
 +      wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: RX message 1 of Group Key "
 +              "Handshake from " MACSTR " (ver=%d)", MAC2STR(src_addr), ver);
 +
 +      key_info = WPA_GET_BE16(key->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, key_data,
 +                                                      key_data_len,
 +                                                      key_info, ver, &gd);
 +      }
 +
 +      wpa_sm_set_state(sm, WPA_GROUP_HANDSHAKE);
 +
 +      if (ret)
 +              goto failed;
 +
 +      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_DEBUG, "WPA: Group rekeying "
 +                      "completed with " MACSTR " [GTK=%s]",
 +                      MAC2STR(sm->bssid), wpa_cipher_txt(sm->group_cipher));
 +              wpa_sm_cancel_auth_timeout(sm);
 +              wpa_sm_set_state(sm, WPA_COMPLETED);
 +      } 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_192 *key,
 +                                             u16 ver,
 +                                             const u8 *buf, size_t len)
 +{
 +      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, mic_len);
 +      if (sm->tptk_set) {
 +              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");
 +              } else {
 +                      ok = 1;
 +                      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, 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");
 +                      return -1;
 +              }
 +              ok = 1;
 +      }
 +
 +      if (!ok) {
 +              wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
 +                      "WPA: Could not verify EAPOL-Key MIC - "
 +                      "dropping packet");
 +              return -1;
 +      }
 +
 +      os_memcpy(sm->rx_replay_counter, key->replay_counter,
 +                WPA_REPLAY_COUNTER_LEN);
 +      sm->rx_replay_counter_set = 1;
 +      return 0;
 +}
 +
 +
 +/* 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,
 +                                         u8 *key_data, size_t *key_data_len)
 +{
 +      wpa_hexdump(MSG_DEBUG, "RSN: encrypted key data",
 +                  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 "
 +                      "Data");
 +              return -1;
 +      }
 +
 +      /* 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 && sm->ptk.kek_len == 16) {
++#ifdef CONFIG_NO_RC4
++              wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
++                      "WPA: RC4 not supported in the build");
++              return -1;
++#else /* CONFIG_NO_RC4 */
 +              u8 ek[32];
 +              os_memcpy(ek, key->key_iv, 16);
 +              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));
++#endif /* CONFIG_NO_RC4 */
 +      } else if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES ||
 +                 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 (*key_data_len < 8 || *key_data_len % 8) {
 +                      wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
 +                              "WPA: Unsupported AES-WRAP len %u",
 +                              (unsigned int) *key_data_len);
 +                      return -1;
 +              }
 +              *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, 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_data, buf, *key_data_len);
 +              os_free(buf);
 +              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",
 +                      key_data, *key_data_len);
 +      return 0;
 +}
 +
 +
 +/**
 + * wpa_sm_aborted_cached - Notify WPA that PMKSA caching was aborted
 + * @sm: Pointer to WPA state machine data from wpa_sm_init()
 + */
 +void wpa_sm_aborted_cached(struct wpa_sm *sm)
 +{
 +      if (sm && sm->cur_pmksa) {
 +              wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
 +                      "RSN: Cancelling PMKSA caching attempt");
 +              sm->cur_pmksa = NULL;
 +      }
 +}
 +
 +
 +static void wpa_eapol_key_dump(struct wpa_sm *sm,
 +                             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);
 +
 +      wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "  EAPOL-Key type=%d", key->type);
 +      wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
 +              "  key_info 0x%x (ver=%d keyidx=%d rsvd=%d %s%s%s%s%s%s%s%s)",
 +              key_info, key_info & WPA_KEY_INFO_TYPE_MASK,
 +              (key_info & WPA_KEY_INFO_KEY_INDEX_MASK) >>
 +              WPA_KEY_INFO_KEY_INDEX_SHIFT,
 +              (key_info & (BIT(13) | BIT(14) | BIT(15))) >> 13,
 +              key_info & WPA_KEY_INFO_KEY_TYPE ? "Pairwise" : "Group",
 +              key_info & WPA_KEY_INFO_INSTALL ? " Install" : "",
 +              key_info & WPA_KEY_INFO_ACK ? " Ack" : "",
 +              key_info & WPA_KEY_INFO_MIC ? " MIC" : "",
 +              key_info & WPA_KEY_INFO_SECURE ? " Secure" : "",
 +              key_info & WPA_KEY_INFO_ERROR ? " Error" : "",
 +              key_info & WPA_KEY_INFO_REQUEST ? " Request" : "",
 +              key_info & WPA_KEY_INFO_ENCR_KEY_DATA ? " Encr" : "");
 +      wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
 +              "  key_length=%u key_data_length=%u",
 +              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", mic, mic_len);
 +#endif /* CONFIG_NO_STDOUT_DEBUG */
 +}
 +
 +
 +/**
 + * wpa_sm_rx_eapol - Process received WPA EAPOL frames
 + * @sm: Pointer to WPA state machine data from wpa_sm_init()
 + * @src_addr: Source MAC address of the EAPOL packet
 + * @buf: Pointer to the beginning of the EAPOL data (EAPOL header)
 + * @len: Length of the EAPOL frame
 + * Returns: 1 = WPA EAPOL-Key processed, 0 = not a WPA EAPOL-Key, -1 failure
 + *
 + * This function is called for each received EAPOL frame. Other than EAPOL-Key
 + * frames can be skipped if filtering is done elsewhere. wpa_sm_rx_eapol() is
 + * only processing WPA and WPA2 EAPOL-Key frames.
 + *
 + * The received EAPOL-Key packets are validated and valid packets are replied
 + * to. In addition, key material (PTK, GTK) is configured at the end of a
 + * successful key handshake.
 + */
 +int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr,
 +                  const u8 *buf, size_t len)
 +{
 +      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 = 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 */
 +
 +      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) + keyhdrlen);
 +              return 0;
 +      }
 +
 +      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,
 +              "IEEE 802.1X RX: version=%d type=%d length=%lu",
 +              hdr->version, hdr->type, (unsigned long) plen);
 +
 +      if (hdr->version < EAPOL_VERSION) {
 +              /* TODO: backwards compatibility */
 +      }
 +      if (hdr->type != IEEE802_1X_TYPE_EAPOL_KEY) {
 +              wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
 +                      "WPA: EAPOL frame (type %u) discarded, "
 +                      "not a Key frame", hdr->type);
 +              ret = 0;
 +              goto out;
 +      }
 +      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)",
 +                      (unsigned long) plen, (unsigned long) len);
 +              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)
 +      {
 +              wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
 +                      "WPA: EAPOL-Key type (%d) unknown, discarded",
 +                      key->type);
 +              ret = 0;
 +              goto out;
 +      }
 +
 +      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 &&
 +          !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). */
 +              if (ver != WPA_KEY_INFO_TYPE_AES_128_CMAC) {
 +                      wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
 +                              "FT: AP did not use AES-128-CMAC");
 +                      goto out;
 +              }
 +      } else
 +#endif /* CONFIG_IEEE80211R */
 +#ifdef CONFIG_IEEE80211W
 +      if (wpa_key_mgmt_sha256(sm->key_mgmt)) {
 +              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");
 +                      goto out;
 +              }
 +      } 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 "
 +                      "descriptor version (%d) is not 2", ver);
 +              if (sm->group_cipher != WPA_CIPHER_CCMP &&
 +                  !(key_info & WPA_KEY_INFO_KEY_TYPE)) {
 +                      /* Earlier versions of IEEE 802.11i did not explicitly
 +                       * require version 2 descriptor for all EAPOL-Key
 +                       * packets, so allow group keys to use version 1 if
 +                       * CCMP is not used for them. */
 +                      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;
 +      } 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);
 +              goto out;
 +      }
 +
 +#ifdef CONFIG_PEERKEY
 +      for (peerkey = sm->peerkey; peerkey; peerkey = peerkey->next) {
 +              if (os_memcmp(peerkey->addr, src_addr, ETH_ALEN) == 0)
 +                      break;
 +      }
 +
 +      if (!(key_info & WPA_KEY_INFO_SMK_MESSAGE) && peerkey) {
 +              if (!peerkey->initiator && peerkey->replay_counter_set &&
 +                  os_memcmp(key->replay_counter, peerkey->replay_counter,
 +                            WPA_REPLAY_COUNTER_LEN) <= 0) {
 +                      wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
 +                              "RSN: EAPOL-Key Replay Counter did not "
 +                              "increase (STK) - dropping packet");
 +                      goto out;
 +              } else if (peerkey->initiator) {
 +                      u8 _tmp[WPA_REPLAY_COUNTER_LEN];
 +                      os_memcpy(_tmp, key->replay_counter,
 +                                WPA_REPLAY_COUNTER_LEN);
 +                      inc_byte_array(_tmp, WPA_REPLAY_COUNTER_LEN);
 +                      if (os_memcmp(_tmp, peerkey->replay_counter,
 +                                    WPA_REPLAY_COUNTER_LEN) != 0) {
 +                              wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
 +                                      "RSN: EAPOL-Key Replay "
 +                                      "Counter did not match (STK) - "
 +                                      "dropping packet");
 +                              goto out;
 +                      }
 +              }
 +      }
 +
 +      if (peerkey && peerkey->initiator && (key_info & WPA_KEY_INFO_ACK)) {
 +              wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
 +                      "RSN: Ack bit in key_info from STK peer");
 +              goto out;
 +      }
 +#endif /* CONFIG_PEERKEY */
 +
 +      if (!peerkey && sm->rx_replay_counter_set &&
 +          os_memcmp(key->replay_counter, sm->rx_replay_counter,
 +                    WPA_REPLAY_COUNTER_LEN) <= 0) {
 +              wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
 +                      "WPA: EAPOL-Key Replay Counter did not increase - "
 +                      "dropping packet");
 +              goto out;
 +      }
 +
 +      if (!(key_info & (WPA_KEY_INFO_ACK | WPA_KEY_INFO_SMK_MESSAGE))
 +#ifdef CONFIG_PEERKEY
 +          && (peerkey == NULL || !peerkey->initiator)
 +#endif /* CONFIG_PEERKEY */
 +              ) {
 +              wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
 +                      "WPA: No Ack bit in key_info");
 +              goto out;
 +      }
 +
 +      if (key_info & WPA_KEY_INFO_REQUEST) {
 +              wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
 +                      "WPA: EAPOL-Key with Request bit - dropped");
 +              goto out;
 +      }
 +
 +      if ((key_info & WPA_KEY_INFO_MIC) && !peerkey &&
 +          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, key192, ver, tmp,
 +                                       data_len))
 +              goto out;
 +#endif /* CONFIG_PEERKEY */
 +
 +      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, key_data,
 +                                                  &key_data_len))
 +                      goto out;
 +      }
 +
 +      if (key_info & WPA_KEY_INFO_KEY_TYPE) {
 +              if (key_info & WPA_KEY_INFO_KEY_INDEX_MASK) {
 +                      wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
 +                              "WPA: Ignored EAPOL-Key (Pairwise) with "
 +                              "non-zero key index");
 +                      goto out;
 +              }
 +              if (peerkey) {
 +                      /* PeerKey 4-Way Handshake */
 +                      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, key_data,
 +                                                    key_data_len);
 +              } else {
 +                      /* 1/4 4-Way Handshake */
 +                      wpa_supplicant_process_1_of_4(sm, src_addr, key,
 +                                                    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, 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,
 +                                                    key_data, key_data_len,
 +                                                    ver);
 +              } else {
 +                      wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
 +                              "WPA: EAPOL-Key (Group) without Mic bit - "
 +                              "dropped");
 +              }
 +      }
 +
 +      ret = 1;
 +
 +out:
 +      bin_clear_free(tmp, data_len);
 +      return ret;
 +}
 +
 +
 +#ifdef CONFIG_CTRL_IFACE
 +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 ||
 +                       sm->proto == WPA_PROTO_OSEN) ?
 +                      RSN_AUTH_KEY_MGMT_UNSPEC_802_1X :
 +                      WPA_AUTH_KEY_MGMT_UNSPEC_802_1X);
 +      case WPA_KEY_MGMT_PSK:
 +              return (sm->proto == WPA_PROTO_RSN ?
 +                      RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X :
 +                      WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X);
 +#ifdef CONFIG_IEEE80211R
 +      case WPA_KEY_MGMT_FT_IEEE8021X:
 +              return RSN_AUTH_KEY_MGMT_FT_802_1X;
 +      case WPA_KEY_MGMT_FT_PSK:
 +              return RSN_AUTH_KEY_MGMT_FT_PSK;
 +#endif /* CONFIG_IEEE80211R */
 +#ifdef CONFIG_IEEE80211W
 +      case WPA_KEY_MGMT_IEEE8021X_SHA256:
 +              return RSN_AUTH_KEY_MGMT_802_1X_SHA256;
 +      case WPA_KEY_MGMT_PSK_SHA256:
 +              return RSN_AUTH_KEY_MGMT_PSK_SHA256;
 +#endif /* CONFIG_IEEE80211W */
 +      case WPA_KEY_MGMT_CCKM:
 +              return (sm->proto == WPA_PROTO_RSN ?
 +                      RSN_AUTH_KEY_MGMT_CCKM:
 +                      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;
 +      }
 +}
 +
 +
 +#define RSN_SUITE "%02x-%02x-%02x-%d"
 +#define RSN_SUITE_ARG(s) \
 +((s) >> 24) & 0xff, ((s) >> 16) & 0xff, ((s) >> 8) & 0xff, (s) & 0xff
 +
 +/**
 + * wpa_sm_get_mib - Dump text list of MIB entries
 + * @sm: Pointer to WPA state machine data from wpa_sm_init()
 + * @buf: Buffer for the list
 + * @buflen: Length of the buffer
 + * Returns: Number of bytes written to buffer
 + *
 + * This function is used fetch dot11 MIB variables.
 + */
 +int wpa_sm_get_mib(struct wpa_sm *sm, char *buf, size_t buflen)
 +{
 +      char pmkid_txt[PMKID_LEN * 2 + 1];
 +      int rsna, ret;
 +      size_t len;
 +
 +      if (sm->cur_pmksa) {
 +              wpa_snprintf_hex(pmkid_txt, sizeof(pmkid_txt),
 +                               sm->cur_pmksa->pmkid, PMKID_LEN);
 +      } else
 +              pmkid_txt[0] = '\0';
 +
 +      if ((wpa_key_mgmt_wpa_psk(sm->key_mgmt) ||
 +           wpa_key_mgmt_wpa_ieee8021x(sm->key_mgmt)) &&
 +          sm->proto == WPA_PROTO_RSN)
 +              rsna = 1;
 +      else
 +              rsna = 0;
 +
 +      ret = os_snprintf(buf, buflen,
 +                        "dot11RSNAOptionImplemented=TRUE\n"
 +                        "dot11RSNAPreauthenticationImplemented=TRUE\n"
 +                        "dot11RSNAEnabled=%s\n"
 +                        "dot11RSNAPreauthenticationEnabled=%s\n"
 +                        "dot11RSNAConfigVersion=%d\n"
 +                        "dot11RSNAConfigPairwiseKeysSupported=5\n"
 +                        "dot11RSNAConfigGroupCipherSize=%d\n"
 +                        "dot11RSNAConfigPMKLifetime=%d\n"
 +                        "dot11RSNAConfigPMKReauthThreshold=%d\n"
 +                        "dot11RSNAConfigNumberOfPTKSAReplayCounters=1\n"
 +                        "dot11RSNAConfigSATimeout=%d\n",
 +                        rsna ? "TRUE" : "FALSE",
 +                        rsna ? "TRUE" : "FALSE",
 +                        RSN_VERSION,
 +                        wpa_cipher_key_len(sm->group_cipher) * 8,
 +                        sm->dot11RSNAConfigPMKLifetime,
 +                        sm->dot11RSNAConfigPMKReauthThreshold,
 +                        sm->dot11RSNAConfigSATimeout);
 +      if (os_snprintf_error(buflen, ret))
 +              return 0;
 +      len = ret;
 +
 +      ret = os_snprintf(
 +              buf + len, buflen - len,
 +              "dot11RSNAAuthenticationSuiteSelected=" RSN_SUITE "\n"
 +              "dot11RSNAPairwiseCipherSelected=" RSN_SUITE "\n"
 +              "dot11RSNAGroupCipherSelected=" RSN_SUITE "\n"
 +              "dot11RSNAPMKIDUsed=%s\n"
 +              "dot11RSNAAuthenticationSuiteRequested=" RSN_SUITE "\n"
 +              "dot11RSNAPairwiseCipherRequested=" RSN_SUITE "\n"
 +              "dot11RSNAGroupCipherRequested=" RSN_SUITE "\n"
 +              "dot11RSNAConfigNumberOfGTKSAReplayCounters=0\n"
 +              "dot11RSNA4WayHandshakeFailures=%u\n",
 +              RSN_SUITE_ARG(wpa_key_mgmt_suite(sm)),
 +              RSN_SUITE_ARG(wpa_cipher_to_suite(sm->proto,
 +                                                sm->pairwise_cipher)),
 +              RSN_SUITE_ARG(wpa_cipher_to_suite(sm->proto,
 +                                                sm->group_cipher)),
 +              pmkid_txt,
 +              RSN_SUITE_ARG(wpa_key_mgmt_suite(sm)),
 +              RSN_SUITE_ARG(wpa_cipher_to_suite(sm->proto,
 +                                                sm->pairwise_cipher)),
 +              RSN_SUITE_ARG(wpa_cipher_to_suite(sm->proto,
 +                                                sm->group_cipher)),
 +              sm->dot11RSNA4WayHandshakeFailures);
 +      if (!os_snprintf_error(buflen - len, ret))
 +              len += ret;
 +
 +      return (int) len;
 +}
 +#endif /* CONFIG_CTRL_IFACE */
 +
 +
 +static void wpa_sm_pmksa_free_cb(struct rsn_pmksa_cache_entry *entry,
 +                               void *ctx, enum pmksa_free_reason reason)
 +{
 +      struct wpa_sm *sm = ctx;
 +      int deauth = 0;
 +
 +      wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: PMKSA cache entry free_cb: "
 +              MACSTR " reason=%d", MAC2STR(entry->aa), reason);
 +
 +      if (sm->cur_pmksa == entry) {
 +              wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
 +                      "RSN: %s current PMKSA entry",
 +                      reason == PMKSA_REPLACE ? "replaced" : "removed");
 +              pmksa_cache_clear_current(sm);
 +
 +              /*
 +               * If an entry is simply being replaced, there's no need to
 +               * deauthenticate because it will be immediately re-added.
 +               * This happens when EAP authentication is completed again
 +               * (reauth or failed PMKSA caching attempt).
 +               */
 +              if (reason != PMKSA_REPLACE)
 +                      deauth = 1;
 +      }
 +
 +      if (reason == PMKSA_EXPIRE &&
 +          (sm->pmk_len == entry->pmk_len &&
 +           os_memcmp(sm->pmk, entry->pmk, sm->pmk_len) == 0)) {
 +              wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
 +                      "RSN: deauthenticating due to expired PMK");
 +              pmksa_cache_clear_current(sm);
 +              deauth = 1;
 +      }
 +
 +      if (deauth) {
 +              os_memset(sm->pmk, 0, sizeof(sm->pmk));
 +              wpa_sm_deauthenticate(sm, WLAN_REASON_UNSPECIFIED);
 +      }
 +}
 +
 +
 +/**
 + * wpa_sm_init - Initialize WPA state machine
 + * @ctx: Context pointer for callbacks; this needs to be an allocated buffer
 + * Returns: Pointer to the allocated WPA state machine data
 + *
 + * This function is used to allocate a new WPA state machine and the returned
 + * value is passed to all WPA state machine calls.
 + */
 +struct wpa_sm * wpa_sm_init(struct wpa_sm_ctx *ctx)
 +{
 +      struct wpa_sm *sm;
 +
 +      sm = os_zalloc(sizeof(*sm));
 +      if (sm == NULL)
 +              return NULL;
 +      dl_list_init(&sm->pmksa_candidates);
 +      sm->renew_snonce = 1;
 +      sm->ctx = ctx;
 +
 +      sm->dot11RSNAConfigPMKLifetime = 43200;
 +      sm->dot11RSNAConfigPMKReauthThreshold = 70;
 +      sm->dot11RSNAConfigSATimeout = 60;
 +
 +      sm->pmksa = pmksa_cache_init(wpa_sm_pmksa_free_cb, sm, sm);
 +      if (sm->pmksa == NULL) {
 +              wpa_msg(sm->ctx->msg_ctx, MSG_ERROR,
 +                      "RSN: PMKSA cache initialization failed");
 +              os_free(sm);
 +              return NULL;
 +      }
 +
 +      return sm;
 +}
 +
 +
 +/**
 + * wpa_sm_deinit - Deinitialize WPA state machine
 + * @sm: Pointer to WPA state machine data from wpa_sm_init()
 + */
 +void wpa_sm_deinit(struct wpa_sm *sm)
 +{
 +      if (sm == NULL)
 +              return;
 +      pmksa_cache_deinit(sm->pmksa);
 +      eloop_cancel_timeout(wpa_sm_start_preauth, sm, NULL);
 +      eloop_cancel_timeout(wpa_sm_rekey_ptk, sm, NULL);
 +      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
 +      os_free(sm->assoc_resp_ies);
 +#endif /* CONFIG_IEEE80211R */
 +      os_free(sm);
 +}
 +
 +
 +/**
 + * wpa_sm_notify_assoc - Notify WPA state machine about association
 + * @sm: Pointer to WPA state machine data from wpa_sm_init()
 + * @bssid: The BSSID of the new association
 + *
 + * This function is called to let WPA state machine know that the connection
 + * was established.
 + */
 +void wpa_sm_notify_assoc(struct wpa_sm *sm, const u8 *bssid)
 +{
 +      int clear_ptk = 1;
 +
 +      if (sm == NULL)
 +              return;
 +
 +      wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
 +              "WPA: Association event - clear replay counter");
 +      os_memcpy(sm->bssid, bssid, ETH_ALEN);
 +      os_memset(sm->rx_replay_counter, 0, WPA_REPLAY_COUNTER_LEN);
 +      sm->rx_replay_counter_set = 0;
 +      sm->renew_snonce = 1;
 +      if (os_memcmp(sm->preauth_bssid, bssid, ETH_ALEN) == 0)
 +              rsn_preauth_deinit(sm);
 +
 +#ifdef CONFIG_IEEE80211R
 +      if (wpa_ft_is_completed(sm)) {
 +              /*
 +               * Clear portValid to kick EAPOL state machine to re-enter
 +               * AUTHENTICATED state to get the EAPOL port Authorized.
 +               */
 +              eapol_sm_notify_portValid(sm->eapol, FALSE);
 +              wpa_supplicant_key_neg_complete(sm, sm->bssid, 1);
 +
 +              /* Prepare for the next transition */
 +              wpa_ft_prepare_auth_request(sm, NULL);
 +
 +              clear_ptk = 0;
 +      }
 +#endif /* CONFIG_IEEE80211R */
 +
 +      if (clear_ptk) {
 +              /*
 +               * IEEE 802.11, 8.4.10: Delete PTK SA on (re)association if
 +               * this is not part of a Fast BSS Transition.
 +               */
 +              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 */
 +}
 +
 +
 +/**
 + * wpa_sm_notify_disassoc - Notify WPA state machine about disassociation
 + * @sm: Pointer to WPA state machine data from wpa_sm_init()
 + *
 + * This function is called to let WPA state machine know that the connection
 + * was lost. This will abort any existing pre-authentication session.
 + */
 +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)
 +              sm->dot11RSNA4WayHandshakeFailures++;
 +#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;
 +}
 +
 +
 +/**
 + * wpa_sm_set_pmk - Set PMK
 + * @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,
 +                  const u8 *bssid)
 +{
 +      if (sm == NULL)
 +              return;
 +
 +      sm->pmk_len = pmk_len;
 +      os_memcpy(sm->pmk, pmk, pmk_len);
 +
 +#ifdef CONFIG_IEEE80211R
 +      /* Set XXKey to be PSK for FT key derivation */
 +      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);
 +      }
 +}
 +
 +
 +/**
 + * wpa_sm_set_pmk_from_pmksa - Set PMK based on the current PMKSA
 + * @sm: Pointer to WPA state machine data from wpa_sm_init()
 + *
 + * Take the PMK from the current PMKSA into use. If no PMKSA is active, the PMK
 + * will be cleared.
 + */
 +void wpa_sm_set_pmk_from_pmksa(struct wpa_sm *sm)
 +{
 +      if (sm == NULL)
 +              return;
 +
 +      if (sm->cur_pmksa) {
 +              sm->pmk_len = sm->cur_pmksa->pmk_len;
 +              os_memcpy(sm->pmk, sm->cur_pmksa->pmk, sm->pmk_len);
 +      } else {
 +              sm->pmk_len = PMK_LEN;
 +              os_memset(sm->pmk, 0, PMK_LEN);
 +      }
 +}
 +
 +
 +/**
 + * wpa_sm_set_fast_reauth - Set fast reauthentication (EAP) enabled/disabled
 + * @sm: Pointer to WPA state machine data from wpa_sm_init()
 + * @fast_reauth: Whether fast reauthentication (EAP) is allowed
 + */
 +void wpa_sm_set_fast_reauth(struct wpa_sm *sm, int fast_reauth)
 +{
 +      if (sm)
 +              sm->fast_reauth = fast_reauth;
 +}
 +
 +
 +/**
 + * wpa_sm_set_scard_ctx - Set context pointer for smartcard callbacks
 + * @sm: Pointer to WPA state machine data from wpa_sm_init()
 + * @scard_ctx: Context pointer for smartcard related callback functions
 + */
 +void wpa_sm_set_scard_ctx(struct wpa_sm *sm, void *scard_ctx)
 +{
 +      if (sm == NULL)
 +              return;
 +      sm->scard_ctx = scard_ctx;
 +      if (sm->preauth_eapol)
 +              eapol_sm_register_scard_ctx(sm->preauth_eapol, scard_ctx);
 +}
 +
 +
 +/**
 + * wpa_sm_set_config - Notification of current configration change
 + * @sm: Pointer to WPA state machine data from wpa_sm_init()
 + * @config: Pointer to current network configuration
 + *
 + * Notify WPA state machine that configuration has changed. config will be
 + * stored as a backpointer to network configuration. This can be %NULL to clear
 + * the stored pointed.
 + */
 +void wpa_sm_set_config(struct wpa_sm *sm, struct rsn_supp_config *config)
 +{
 +      if (!sm)
 +              return;
 +
 +      if (config) {
 +              sm->network_ctx = config->network_ctx;
 +              sm->peerkey_enabled = config->peerkey_enabled;
 +              sm->allowed_pairwise_cipher = config->allowed_pairwise_cipher;
 +              sm->proactive_key_caching = config->proactive_key_caching;
 +              sm->eap_workaround = config->eap_workaround;
 +              sm->eap_conf_ctx = config->eap_conf_ctx;
 +              if (config->ssid) {
 +                      os_memcpy(sm->ssid, config->ssid, config->ssid_len);
 +                      sm->ssid_len = config->ssid_len;
 +              } 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;
 +              sm->allowed_pairwise_cipher = 0;
 +              sm->proactive_key_caching = 0;
 +              sm->eap_workaround = 0;
 +              sm->eap_conf_ctx = NULL;
 +              sm->ssid_len = 0;
 +              sm->wpa_ptk_rekey = 0;
 +              sm->p2p = 0;
 +      }
 +}
 +
 +
 +/**
 + * wpa_sm_set_own_addr - Set own MAC address
 + * @sm: Pointer to WPA state machine data from wpa_sm_init()
 + * @addr: Own MAC address
 + */
 +void wpa_sm_set_own_addr(struct wpa_sm *sm, const u8 *addr)
 +{
 +      if (sm)
 +              os_memcpy(sm->own_addr, addr, ETH_ALEN);
 +}
 +
 +
 +/**
 + * wpa_sm_set_ifname - Set network interface name
 + * @sm: Pointer to WPA state machine data from wpa_sm_init()
 + * @ifname: Interface name
 + * @bridge_ifname: Optional bridge interface name (for pre-auth)
 + */
 +void wpa_sm_set_ifname(struct wpa_sm *sm, const char *ifname,
 +                     const char *bridge_ifname)
 +{
 +      if (sm) {
 +              sm->ifname = ifname;
 +              sm->bridge_ifname = bridge_ifname;
 +      }
 +}
 +
 +
 +/**
 + * wpa_sm_set_eapol - Set EAPOL state machine pointer
 + * @sm: Pointer to WPA state machine data from wpa_sm_init()
 + * @eapol: Pointer to EAPOL state machine allocated with eapol_sm_init()
 + */
 +void wpa_sm_set_eapol(struct wpa_sm *sm, struct eapol_sm *eapol)
 +{
 +      if (sm)
 +              sm->eapol = eapol;
 +}
 +
 +
 +/**
 + * wpa_sm_set_param - Set WPA state machine parameters
 + * @sm: Pointer to WPA state machine data from wpa_sm_init()
 + * @param: Parameter field
 + * @value: Parameter value
 + * Returns: 0 on success, -1 on failure
 + */
 +int wpa_sm_set_param(struct wpa_sm *sm, enum wpa_sm_conf_params param,
 +                   unsigned int value)
 +{
 +      int ret = 0;
 +
 +      if (sm == NULL)
 +              return -1;
 +
 +      switch (param) {
 +      case RSNA_PMK_LIFETIME:
 +              if (value > 0)
 +                      sm->dot11RSNAConfigPMKLifetime = value;
 +              else
 +                      ret = -1;
 +              break;
 +      case RSNA_PMK_REAUTH_THRESHOLD:
 +              if (value > 0 && value <= 100)
 +                      sm->dot11RSNAConfigPMKReauthThreshold = value;
 +              else
 +                      ret = -1;
 +              break;
 +      case RSNA_SA_TIMEOUT:
 +              if (value > 0)
 +                      sm->dot11RSNAConfigSATimeout = value;
 +              else
 +                      ret = -1;
 +              break;
 +      case WPA_PARAM_PROTO:
 +              sm->proto = value;
 +              break;
 +      case WPA_PARAM_PAIRWISE:
 +              sm->pairwise_cipher = value;
 +              break;
 +      case WPA_PARAM_GROUP:
 +              sm->group_cipher = value;
 +              break;
 +      case WPA_PARAM_KEY_MGMT:
 +              sm->key_mgmt = value;
 +              break;
 +#ifdef CONFIG_IEEE80211W
 +      case WPA_PARAM_MGMT_GROUP:
 +              sm->mgmt_group_cipher = value;
 +              break;
 +#endif /* CONFIG_IEEE80211W */
 +      case WPA_PARAM_RSN_ENABLED:
 +              sm->rsn_enabled = value;
 +              break;
 +      case WPA_PARAM_MFP:
 +              sm->mfp = value;
 +              break;
 +      default:
 +              break;
 +      }
 +
 +      return ret;
 +}
 +
 +
 +/**
 + * wpa_sm_get_status - Get WPA state machine
 + * @sm: Pointer to WPA state machine data from wpa_sm_init()
 + * @buf: Buffer for status information
 + * @buflen: Maximum buffer length
 + * @verbose: Whether to include verbose status information
 + * Returns: Number of bytes written to buf.
 + *
 + * Query WPA state machine for status information. This function fills in
 + * a text area with current status information. If the buffer (buf) is not
 + * large enough, status information will be truncated to fit the buffer.
 + */
 +int wpa_sm_get_status(struct wpa_sm *sm, char *buf, size_t buflen,
 +                    int verbose)
 +{
 +      char *pos = buf, *end = buf + buflen;
 +      int ret;
 +
 +      ret = os_snprintf(pos, end - pos,
 +                        "pairwise_cipher=%s\n"
 +                        "group_cipher=%s\n"
 +                        "key_mgmt=%s\n",
 +                        wpa_cipher_txt(sm->pairwise_cipher),
 +                        wpa_cipher_txt(sm->group_cipher),
 +                        wpa_key_mgmt_txt(sm->key_mgmt, sm->proto));
 +      if (os_snprintf_error(end - pos, ret))
 +              return pos - buf;
 +      pos += ret;
 +
 +      if (sm->mfp != NO_MGMT_FRAME_PROTECTION && sm->ap_rsn_ie) {
 +              struct wpa_ie_data rsn;
 +              if (wpa_parse_wpa_ie_rsn(sm->ap_rsn_ie, sm->ap_rsn_ie_len, &rsn)
 +                  >= 0 &&
 +                  rsn.capabilities & (WPA_CAPABILITY_MFPR |
 +                                      WPA_CAPABILITY_MFPC)) {
 +                      ret = os_snprintf(pos, end - pos, "pmf=%d\n",
 +                                        (rsn.capabilities &
 +                                         WPA_CAPABILITY_MFPR) ? 2 : 1);
 +                      if (os_snprintf_error(end - pos, ret))
 +                              return pos - buf;
 +                      pos += ret;
 +              }
 +      }
 +
 +      return pos - buf;
 +}
 +
 +
 +int wpa_sm_pmf_enabled(struct wpa_sm *sm)
 +{
 +      struct wpa_ie_data rsn;
 +
 +      if (sm->mfp == NO_MGMT_FRAME_PROTECTION || !sm->ap_rsn_ie)
 +              return 0;
 +
 +      if (wpa_parse_wpa_ie_rsn(sm->ap_rsn_ie, sm->ap_rsn_ie_len, &rsn) >= 0 &&
 +          rsn.capabilities & (WPA_CAPABILITY_MFPR | WPA_CAPABILITY_MFPC))
 +              return 1;
 +
 +      return 0;
 +}
 +
 +
 +/**
 + * wpa_sm_set_assoc_wpa_ie_default - Generate own WPA/RSN IE from configuration
 + * @sm: Pointer to WPA state machine data from wpa_sm_init()
 + * @wpa_ie: Pointer to buffer for WPA/RSN IE
 + * @wpa_ie_len: Pointer to the length of the wpa_ie buffer
 + * Returns: 0 on success, -1 on failure
 + */
 +int wpa_sm_set_assoc_wpa_ie_default(struct wpa_sm *sm, u8 *wpa_ie,
 +                                  size_t *wpa_ie_len)
 +{
 +      int res;
 +
 +      if (sm == NULL)
 +              return -1;
 +
 +      res = wpa_gen_wpa_ie(sm, wpa_ie, *wpa_ie_len);
 +      if (res < 0)
 +              return -1;
 +      *wpa_ie_len = res;
 +
 +      wpa_hexdump(MSG_DEBUG, "WPA: Set own WPA IE default",
 +                  wpa_ie, *wpa_ie_len);
 +
 +      if (sm->assoc_wpa_ie == NULL) {
 +              /*
 +               * Make a copy of the WPA/RSN IE so that 4-Way Handshake gets
 +               * the correct version of the IE even if PMKSA caching is
 +               * aborted (which would remove PMKID from IE generation).
 +               */
 +              sm->assoc_wpa_ie = os_malloc(*wpa_ie_len);
 +              if (sm->assoc_wpa_ie == NULL)
 +                      return -1;
 +
 +              os_memcpy(sm->assoc_wpa_ie, wpa_ie, *wpa_ie_len);
 +              sm->assoc_wpa_ie_len = *wpa_ie_len;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +/**
 + * wpa_sm_set_assoc_wpa_ie - Set own WPA/RSN IE from (Re)AssocReq
 + * @sm: Pointer to WPA state machine data from wpa_sm_init()
 + * @ie: Pointer to IE data (starting from id)
 + * @len: IE length
 + * Returns: 0 on success, -1 on failure
 + *
 + * Inform WPA state machine about the WPA/RSN IE used in (Re)Association
 + * Request frame. The IE will be used to override the default value generated
 + * with wpa_sm_set_assoc_wpa_ie_default().
 + */
 +int wpa_sm_set_assoc_wpa_ie(struct wpa_sm *sm, const u8 *ie, size_t len)
 +{
 +      if (sm == NULL)
 +              return -1;
 +
 +      os_free(sm->assoc_wpa_ie);
 +      if (ie == NULL || len == 0) {
 +              wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
 +                      "WPA: clearing own WPA/RSN IE");
 +              sm->assoc_wpa_ie = NULL;
 +              sm->assoc_wpa_ie_len = 0;
 +      } else {
 +              wpa_hexdump(MSG_DEBUG, "WPA: set own WPA/RSN IE", ie, len);
 +              sm->assoc_wpa_ie = os_malloc(len);
 +              if (sm->assoc_wpa_ie == NULL)
 +                      return -1;
 +
 +              os_memcpy(sm->assoc_wpa_ie, ie, len);
 +              sm->assoc_wpa_ie_len = len;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +/**
 + * wpa_sm_set_ap_wpa_ie - Set AP WPA IE from Beacon/ProbeResp
 + * @sm: Pointer to WPA state machine data from wpa_sm_init()
 + * @ie: Pointer to IE data (starting from id)
 + * @len: IE length
 + * Returns: 0 on success, -1 on failure
 + *
 + * Inform WPA state machine about the WPA IE used in Beacon / Probe Response
 + * frame.
 + */
 +int wpa_sm_set_ap_wpa_ie(struct wpa_sm *sm, const u8 *ie, size_t len)
 +{
 +      if (sm == NULL)
 +              return -1;
 +
 +      os_free(sm->ap_wpa_ie);
 +      if (ie == NULL || len == 0) {
 +              wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
 +                      "WPA: clearing AP WPA IE");
 +              sm->ap_wpa_ie = NULL;
 +              sm->ap_wpa_ie_len = 0;
 +      } else {
 +              wpa_hexdump(MSG_DEBUG, "WPA: set AP WPA IE", ie, len);
 +              sm->ap_wpa_ie = os_malloc(len);
 +              if (sm->ap_wpa_ie == NULL)
 +                      return -1;
 +
 +              os_memcpy(sm->ap_wpa_ie, ie, len);
 +              sm->ap_wpa_ie_len = len;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +/**
 + * wpa_sm_set_ap_rsn_ie - Set AP RSN IE from Beacon/ProbeResp
 + * @sm: Pointer to WPA state machine data from wpa_sm_init()
 + * @ie: Pointer to IE data (starting from id)
 + * @len: IE length
 + * Returns: 0 on success, -1 on failure
 + *
 + * Inform WPA state machine about the RSN IE used in Beacon / Probe Response
 + * frame.
 + */
 +int wpa_sm_set_ap_rsn_ie(struct wpa_sm *sm, const u8 *ie, size_t len)
 +{
 +      if (sm == NULL)
 +              return -1;
 +
 +      os_free(sm->ap_rsn_ie);
 +      if (ie == NULL || len == 0) {
 +              wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
 +                      "WPA: clearing AP RSN IE");
 +              sm->ap_rsn_ie = NULL;
 +              sm->ap_rsn_ie_len = 0;
 +      } else {
 +              wpa_hexdump(MSG_DEBUG, "WPA: set AP RSN IE", ie, len);
 +              sm->ap_rsn_ie = os_malloc(len);
 +              if (sm->ap_rsn_ie == NULL)
 +                      return -1;
 +
 +              os_memcpy(sm->ap_rsn_ie, ie, len);
 +              sm->ap_rsn_ie_len = len;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +/**
 + * wpa_sm_parse_own_wpa_ie - Parse own WPA/RSN IE
 + * @sm: Pointer to WPA state machine data from wpa_sm_init()
 + * @data: Pointer to data area for parsing results
 + * Returns: 0 on success, -1 if IE is not known, or -2 on parsing failure
 + *
 + * Parse the contents of the own WPA or RSN IE from (Re)AssocReq and write the
 + * parsed data into data.
 + */
 +int wpa_sm_parse_own_wpa_ie(struct wpa_sm *sm, struct wpa_ie_data *data)
 +{
 +      if (sm == NULL)
 +              return -1;
 +
 +      if (sm->assoc_wpa_ie == NULL) {
 +              wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
 +                      "WPA: No WPA/RSN IE available from association info");
 +              return -1;
 +      }
 +      if (wpa_parse_wpa_ie(sm->assoc_wpa_ie, sm->assoc_wpa_ie_len, data))
 +              return -2;
 +      return 0;
 +}
 +
 +
 +int wpa_sm_pmksa_cache_list(struct wpa_sm *sm, char *buf, size_t len)
 +{
 +      return pmksa_cache_list(sm->pmksa, buf, len);
 +}
 +
 +
 +void wpa_sm_drop_sa(struct wpa_sm *sm)
 +{
 +      wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Clear old PMK and PTK");
 +      sm->ptk_set = 0;
 +      sm->tptk_set = 0;
 +      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 */
 +}
 +
 +
 +int wpa_sm_has_ptk(struct wpa_sm *sm)
 +{
 +      if (sm == NULL)
 +              return 0;
 +      return sm->ptk_set;
 +}
 +
 +
 +void wpa_sm_update_replay_ctr(struct wpa_sm *sm, const u8 *replay_ctr)
 +{
 +      os_memcpy(sm->rx_replay_counter, replay_ctr, WPA_REPLAY_COUNTER_LEN);
 +}
 +
 +
 +void wpa_sm_pmksa_cache_flush(struct wpa_sm *sm, void *network_ctx)
 +{
 +      pmksa_cache_flush(sm->pmksa, network_ctx, NULL, 0);
 +}
 +
 +
 +#ifdef CONFIG_WNM
 +int wpa_wnmsleep_install_key(struct wpa_sm *sm, u8 subelem_id, u8 *buf)
 +{
 +      u16 keyinfo;
 +      u8 keylen;  /* plaintext key len */
 +      u8 *key_rsc;
 +
 +      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;
 +              if (gd.gtk_len != buf[4]) {
 +                      wpa_printf(MSG_DEBUG, "GTK len mismatch len %d vs %d",
 +                                 gd.gtk_len, buf[4]);
 +                      return -1;
 +              }
 +              gd.keyidx = keyinfo & 0x03; /* B0 - B1 */
 +              gd.tx = wpa_supplicant_gtk_tx_bit_workaround(
 +                       sm, !!(keyinfo & WPA_KEY_INFO_TXRX));
 +
 +              os_memcpy(gd.gtk, buf + 13, gd.gtk_len);
 +
 +              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, keylen);
 +
 +              wpa_hexdump_key(MSG_DEBUG, "Install IGTK (WNM SLEEP)",
 +                              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, 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");
 +              return -1;
 +      }
 +
 +      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;
 +}
index 06dea0550f1bcf4e4a2f25f426e06391c0ed13de,0000000000000000000000000000000000000000..205793e7f43abd5b7f44e6201b5f17f067cd2e9a
mode 100644,000000..100644
--- /dev/null
@@@ -1,849 -1,0 +1,847 @@@
-       if (sm->group_cipher != WPA_CIPHER_CCMP &&
-           sm->group_cipher != WPA_CIPHER_GCMP &&
-           sm->group_cipher != WPA_CIPHER_TKIP) {
 +/*
 + * WPA Supplicant - IEEE 802.11r - Fast BSS Transition
 + * 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.
 + */
 +
 +#include "includes.h"
 +
 +#include "common.h"
 +#include "crypto/aes_wrap.h"
 +#include "crypto/random.h"
 +#include "common/ieee802_11_defs.h"
 +#include "common/ieee802_11_common.h"
 +#include "wpa.h"
 +#include "wpa_i.h"
 +
 +#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)
 +{
 +      u8 ptk_name[WPA_PMK_NAME_LEN];
 +      const u8 *anonce = key->key_nonce;
 +
 +      if (sm->xxkey_len == 0) {
 +              wpa_printf(MSG_DEBUG, "FT: XXKey not available for key "
 +                         "derivation");
 +              return -1;
 +      }
 +
 +      wpa_derive_pmk_r0(sm->xxkey, sm->xxkey_len, sm->ssid,
 +                        sm->ssid_len, sm->mobility_domain,
 +                        sm->r0kh_id, sm->r0kh_id_len, sm->own_addr,
 +                        sm->pmk_r0, sm->pmk_r0_name);
 +      wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R0", sm->pmk_r0, PMK_LEN);
 +      wpa_hexdump(MSG_DEBUG, "FT: PMKR0Name",
 +                  sm->pmk_r0_name, WPA_PMK_NAME_LEN);
 +      wpa_derive_pmk_r1(sm->pmk_r0, sm->pmk_r0_name, sm->r1kh_id,
 +                        sm->own_addr, sm->pmk_r1, sm->pmk_r1_name);
 +      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);
 +      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);
 +}
 +
 +
 +/**
 + * wpa_sm_set_ft_params - Set FT (IEEE 802.11r) parameters
 + * @sm: Pointer to WPA state machine data from wpa_sm_init()
 + * @ies: Association Response IEs or %NULL to clear FT parameters
 + * @ies_len: Length of ies buffer in octets
 + * Returns: 0 on success, -1 on failure
 + */
 +int wpa_sm_set_ft_params(struct wpa_sm *sm, const u8 *ies, size_t ies_len)
 +{
 +      struct wpa_ft_ies ft;
 +
 +      if (sm == NULL)
 +              return 0;
 +
 +      if (wpa_ft_parse_ies(ies, ies_len, &ft) < 0)
 +              return -1;
 +
 +      if (ft.mdie && ft.mdie_len < MOBILITY_DOMAIN_ID_LEN + 1)
 +              return -1;
 +
 +      if (ft.mdie) {
 +              wpa_hexdump(MSG_DEBUG, "FT: Mobility domain",
 +                          ft.mdie, MOBILITY_DOMAIN_ID_LEN);
 +              os_memcpy(sm->mobility_domain, ft.mdie,
 +                        MOBILITY_DOMAIN_ID_LEN);
 +              sm->mdie_ft_capab = ft.mdie[MOBILITY_DOMAIN_ID_LEN];
 +              wpa_printf(MSG_DEBUG, "FT: Capability and Policy: 0x%02x",
 +                         sm->mdie_ft_capab);
 +      } else
 +              os_memset(sm->mobility_domain, 0, MOBILITY_DOMAIN_ID_LEN);
 +
 +      if (ft.r0kh_id) {
 +              wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID",
 +                          ft.r0kh_id, ft.r0kh_id_len);
 +              os_memcpy(sm->r0kh_id, ft.r0kh_id, ft.r0kh_id_len);
 +              sm->r0kh_id_len = ft.r0kh_id_len;
 +      } else {
 +              /* FIX: When should R0KH-ID be cleared? We need to keep the
 +               * old R0KH-ID in order to be able to use this during FT. */
 +              /*
 +               * os_memset(sm->r0kh_id, 0, FT_R0KH_ID_LEN);
 +               * sm->r0kh_id_len = 0;
 +               */
 +      }
 +
 +      if (ft.r1kh_id) {
 +              wpa_hexdump(MSG_DEBUG, "FT: R1KH-ID",
 +                          ft.r1kh_id, FT_R1KH_ID_LEN);
 +              os_memcpy(sm->r1kh_id, ft.r1kh_id, FT_R1KH_ID_LEN);
 +      } else
 +              os_memset(sm->r1kh_id, 0, FT_R1KH_ID_LEN);
 +
 +      os_free(sm->assoc_resp_ies);
 +      sm->assoc_resp_ies = os_malloc(ft.mdie_len + 2 + ft.ftie_len + 2);
 +      if (sm->assoc_resp_ies) {
 +              u8 *pos = sm->assoc_resp_ies;
 +              if (ft.mdie) {
 +                      os_memcpy(pos, ft.mdie - 2, ft.mdie_len + 2);
 +                      pos += ft.mdie_len + 2;
 +              }
 +              if (ft.ftie) {
 +                      os_memcpy(pos, ft.ftie - 2, ft.ftie_len + 2);
 +                      pos += ft.ftie_len + 2;
 +              }
 +              sm->assoc_resp_ies_len = pos - sm->assoc_resp_ies;
 +              wpa_hexdump(MSG_DEBUG, "FT: Stored MDIE and FTIE from "
 +                          "(Re)Association Response",
 +                          sm->assoc_resp_ies, sm->assoc_resp_ies_len);
 +      }
 +
 +      return 0;
 +}
 +
 +
 +/**
 + * wpa_ft_gen_req_ies - Generate FT (IEEE 802.11r) IEs for Auth/ReAssoc Request
 + * @sm: Pointer to WPA state machine data from wpa_sm_init()
 + * @len: Buffer for returning the length of the IEs
 + * @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
 + * @ap_mdie: Mobility Domain IE from the target AP
 + * Returns: Pointer to buffer with IEs or %NULL on failure
 + *
 + * Caller is responsible for freeing the returned buffer with os_free();
 + */
 +static u8 * wpa_ft_gen_req_ies(struct wpa_sm *sm, size_t *len,
 +                             const u8 *anonce, const u8 *pmk_name,
 +                             const u8 *kck, size_t kck_len,
 +                             const u8 *target_ap,
 +                             const u8 *ric_ies, size_t ric_ies_len,
 +                             const u8 *ap_mdie)
 +{
 +      size_t buf_len;
 +      u8 *buf, *pos, *ftie_len, *ftie_pos;
 +      struct rsn_mdie *mdie;
 +      struct rsn_ftie *ftie;
 +      struct rsn_ie_hdr *rsnie;
 +      u16 capab;
 +
 +      sm->ft_completed = 0;
 +
 +      buf_len = 2 + sizeof(struct rsn_mdie) + 2 + sizeof(struct rsn_ftie) +
 +              2 + sm->r0kh_id_len + ric_ies_len + 100;
 +      buf = os_zalloc(buf_len);
 +      if (buf == NULL)
 +              return NULL;
 +      pos = buf;
 +
 +      /* RSNIE[PMKR0Name/PMKR1Name] */
 +      rsnie = (struct rsn_ie_hdr *) pos;
 +      rsnie->elem_id = WLAN_EID_RSN;
 +      WPA_PUT_LE16(rsnie->version, RSN_VERSION);
 +      pos = (u8 *) (rsnie + 1);
 +
 +      /* Group Suite Selector */
++      if (!wpa_cipher_valid_group(sm->group_cipher)) {
 +              wpa_printf(MSG_WARNING, "FT: Invalid group cipher (%d)",
 +                         sm->group_cipher);
 +              os_free(buf);
 +              return NULL;
 +      }
 +      RSN_SELECTOR_PUT(pos, wpa_cipher_to_suite(WPA_PROTO_RSN,
 +                                                sm->group_cipher));
 +      pos += RSN_SELECTOR_LEN;
 +
 +      /* Pairwise Suite Count */
 +      WPA_PUT_LE16(pos, 1);
 +      pos += 2;
 +
 +      /* Pairwise Suite List */
 +      if (!wpa_cipher_valid_pairwise(sm->pairwise_cipher)) {
 +              wpa_printf(MSG_WARNING, "FT: Invalid pairwise cipher (%d)",
 +                         sm->pairwise_cipher);
 +              os_free(buf);
 +              return NULL;
 +      }
 +      RSN_SELECTOR_PUT(pos, wpa_cipher_to_suite(WPA_PROTO_RSN,
 +                                                sm->pairwise_cipher));
 +      pos += RSN_SELECTOR_LEN;
 +
 +      /* Authenticated Key Management Suite Count */
 +      WPA_PUT_LE16(pos, 1);
 +      pos += 2;
 +
 +      /* Authenticated Key Management Suite List */
 +      if (sm->key_mgmt == WPA_KEY_MGMT_FT_IEEE8021X)
 +              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);
 +              os_free(buf);
 +              return NULL;
 +      }
 +      pos += RSN_SELECTOR_LEN;
 +
 +      /* RSN Capabilities */
 +      capab = 0;
 +#ifdef CONFIG_IEEE80211W
 +      if (sm->mgmt_group_cipher == WPA_CIPHER_AES_128_CMAC)
 +              capab |= WPA_CAPABILITY_MFPC;
 +#endif /* CONFIG_IEEE80211W */
 +      WPA_PUT_LE16(pos, capab);
 +      pos += 2;
 +
 +      /* PMKID Count */
 +      WPA_PUT_LE16(pos, 1);
 +      pos += 2;
 +
 +      /* PMKID List [PMKR0Name/PMKR1Name] */
 +      os_memcpy(pos, pmk_name, WPA_PMK_NAME_LEN);
 +      pos += WPA_PMK_NAME_LEN;
 +
 +#ifdef CONFIG_IEEE80211W
 +      if (sm->mgmt_group_cipher == WPA_CIPHER_AES_128_CMAC) {
 +              /* Management Group Cipher Suite */
 +              RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_AES_128_CMAC);
 +              pos += RSN_SELECTOR_LEN;
 +      }
 +#endif /* CONFIG_IEEE80211W */
 +
 +      rsnie->len = (pos - (u8 *) rsnie) - 2;
 +
 +      /* MDIE */
 +      *pos++ = WLAN_EID_MOBILITY_DOMAIN;
 +      *pos++ = sizeof(*mdie);
 +      mdie = (struct rsn_mdie *) pos;
 +      pos += sizeof(*mdie);
 +      os_memcpy(mdie->mobility_domain, sm->mobility_domain,
 +                MOBILITY_DOMAIN_ID_LEN);
 +      mdie->ft_capab = ap_mdie && ap_mdie[1] >= 3 ? ap_mdie[4] :
 +              sm->mdie_ft_capab;
 +
 +      /* FTIE[SNonce, [R1KH-ID,] R0KH-ID ] */
 +      ftie_pos = pos;
 +      *pos++ = WLAN_EID_FAST_BSS_TRANSITION;
 +      ftie_len = pos++;
 +      ftie = (struct rsn_ftie *) pos;
 +      pos += sizeof(*ftie);
 +      os_memcpy(ftie->snonce, sm->snonce, WPA_NONCE_LEN);
 +      if (anonce)
 +              os_memcpy(ftie->anonce, anonce, WPA_NONCE_LEN);
 +      if (kck) {
 +              /* R1KH-ID sub-element in third FT message */
 +              *pos++ = FTIE_SUBELEM_R1KH_ID;
 +              *pos++ = FT_R1KH_ID_LEN;
 +              os_memcpy(pos, sm->r1kh_id, FT_R1KH_ID_LEN);
 +              pos += FT_R1KH_ID_LEN;
 +      }
 +      /* R0KH-ID sub-element */
 +      *pos++ = FTIE_SUBELEM_R0KH_ID;
 +      *pos++ = sm->r0kh_id_len;
 +      os_memcpy(pos, sm->r0kh_id, sm->r0kh_id_len);
 +      pos += sm->r0kh_id_len;
 +      *ftie_len = pos - ftie_len - 1;
 +
 +      if (ric_ies) {
 +              /* RIC Request */
 +              os_memcpy(pos, ric_ies, ric_ies_len);
 +              pos += ric_ies_len;
 +      }
 +
 +      if (kck) {
 +              /*
 +               * IEEE Std 802.11r-2008, 11A.8.4
 +               * MIC shall be calculated over:
 +               * non-AP STA MAC address
 +               * Target AP MAC address
 +               * Transaction seq number (5 for ReassocReq, 3 otherwise)
 +               * RSN IE
 +               * MDIE
 +               * FTIE (with MIC field set to 0)
 +               * RIC-Request (if present)
 +               */
 +              /* Information element count */
 +              ftie->mic_control[1] = 3 + ieee802_11_ie_count(ric_ies,
 +                                                             ric_ies_len);
 +              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,
 +                             ric_ies_len, ftie->mic) < 0) {
 +                      wpa_printf(MSG_INFO, "FT: Failed to calculate MIC");
 +                      os_free(buf);
 +                      return NULL;
 +              }
 +      }
 +
 +      *len = pos - buf;
 +
 +      return buf;
 +}
 +
 +
 +static int wpa_ft_install_ptk(struct wpa_sm *sm, const u8 *bssid)
 +{
 +      int keylen;
 +      enum wpa_alg alg;
 +      u8 null_rsc[6] = { 0, 0, 0, 0, 0, 0 };
 +
 +      wpa_printf(MSG_DEBUG, "FT: Installing PTK to the driver.");
 +
 +      if (!wpa_cipher_valid_pairwise(sm->pairwise_cipher)) {
 +              wpa_printf(MSG_WARNING, "FT: Unsupported pairwise cipher %d",
 +                         sm->pairwise_cipher);
 +              return -1;
 +      }
 +
 +      alg = wpa_cipher_to_alg(sm->pairwise_cipher);
 +      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.tk, keylen) < 0) {
 +              wpa_printf(MSG_WARNING, "FT: Failed to set PTK to the driver");
 +              return -1;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +/**
 + * wpa_ft_prepare_auth_request - Generate over-the-air auth request
 + * @sm: Pointer to WPA state machine data from wpa_sm_init()
 + * @mdie: Target AP MDIE
 + * Returns: 0 on success, -1 on failure
 + */
 +int wpa_ft_prepare_auth_request(struct wpa_sm *sm, const u8 *mdie)
 +{
 +      u8 *ft_ies;
 +      size_t ft_ies_len;
 +
 +      /* Generate a new SNonce */
 +      if (random_get_bytes(sm->snonce, WPA_NONCE_LEN)) {
 +              wpa_printf(MSG_INFO, "FT: Failed to generate a new SNonce");
 +              return -1;
 +      }
 +
 +      ft_ies = wpa_ft_gen_req_ies(sm, &ft_ies_len, NULL, sm->pmk_r0_name,
 +                                  NULL, 0, sm->bssid, NULL, 0, mdie);
 +      if (ft_ies) {
 +              wpa_sm_update_ft_ies(sm, sm->mobility_domain,
 +                                   ft_ies, ft_ies_len);
 +              os_free(ft_ies);
 +      }
 +
 +      return 0;
 +}
 +
 +
 +int wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len,
 +                          int ft_action, const u8 *target_ap,
 +                          const u8 *ric_ies, size_t ric_ies_len)
 +{
 +      u8 *ft_ies;
 +      size_t ft_ies_len;
 +      struct wpa_ft_ies parse;
 +      struct rsn_mdie *mdie;
 +      struct rsn_ftie *ftie;
 +      u8 ptk_name[WPA_PMK_NAME_LEN];
 +      int ret;
 +      const u8 *bssid;
 +
 +      wpa_hexdump(MSG_DEBUG, "FT: Response IEs", ies, ies_len);
 +      wpa_hexdump(MSG_DEBUG, "FT: RIC IEs", ric_ies, ric_ies_len);
 +
 +      if (ft_action) {
 +              if (!sm->over_the_ds_in_progress) {
 +                      wpa_printf(MSG_DEBUG, "FT: No over-the-DS in progress "
 +                                 "- drop FT Action Response");
 +                      return -1;
 +              }
 +
 +              if (os_memcmp(target_ap, sm->target_ap, ETH_ALEN) != 0) {
 +                      wpa_printf(MSG_DEBUG, "FT: No over-the-DS in progress "
 +                                 "with this Target AP - drop FT Action "
 +                                 "Response");
 +                      return -1;
 +              }
 +      }
 +
 +      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;
 +      }
 +
 +      if (wpa_ft_parse_ies(ies, ies_len, &parse) < 0) {
 +              wpa_printf(MSG_DEBUG, "FT: Failed to parse IEs");
 +              return -1;
 +      }
 +
 +      mdie = (struct rsn_mdie *) parse.mdie;
 +      if (mdie == NULL || parse.mdie_len < sizeof(*mdie) ||
 +          os_memcmp(mdie->mobility_domain, sm->mobility_domain,
 +                    MOBILITY_DOMAIN_ID_LEN) != 0) {
 +              wpa_printf(MSG_DEBUG, "FT: Invalid MDIE");
 +              return -1;
 +      }
 +
 +      ftie = (struct rsn_ftie *) parse.ftie;
 +      if (ftie == NULL || parse.ftie_len < sizeof(*ftie)) {
 +              wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
 +              return -1;
 +      }
 +
 +      if (os_memcmp(ftie->snonce, sm->snonce, WPA_NONCE_LEN) != 0) {
 +              wpa_printf(MSG_DEBUG, "FT: SNonce mismatch in FTIE");
 +              wpa_hexdump(MSG_DEBUG, "FT: Received SNonce",
 +                          ftie->snonce, WPA_NONCE_LEN);
 +              wpa_hexdump(MSG_DEBUG, "FT: Expected SNonce",
 +                          sm->snonce, WPA_NONCE_LEN);
 +              return -1;
 +      }
 +
 +      if (parse.r0kh_id == NULL) {
 +              wpa_printf(MSG_DEBUG, "FT: No R0KH-ID subelem in FTIE");
 +              return -1;
 +      }
 +
 +      if (parse.r0kh_id_len != sm->r0kh_id_len ||
 +          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",
 +                          parse.r0kh_id, parse.r0kh_id_len);
 +              wpa_hexdump(MSG_DEBUG, "FT: The current R0KH-ID",
 +                          sm->r0kh_id, sm->r0kh_id_len);
 +              return -1;
 +      }
 +
 +      if (parse.r1kh_id == NULL) {
 +              wpa_printf(MSG_DEBUG, "FT: No R1KH-ID subelem in FTIE");
 +              return -1;
 +      }
 +
 +      if (parse.rsn_pmkid == NULL ||
 +          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;
 +      }
 +
 +      os_memcpy(sm->r1kh_id, parse.r1kh_id, FT_R1KH_ID_LEN);
 +      wpa_hexdump(MSG_DEBUG, "FT: R1KH-ID", sm->r1kh_id, FT_R1KH_ID_LEN);
 +      wpa_hexdump(MSG_DEBUG, "FT: SNonce", sm->snonce, WPA_NONCE_LEN);
 +      wpa_hexdump(MSG_DEBUG, "FT: ANonce", ftie->anonce, WPA_NONCE_LEN);
 +      os_memcpy(sm->anonce, ftie->anonce, WPA_NONCE_LEN);
 +      wpa_derive_pmk_r1(sm->pmk_r0, sm->pmk_r0_name, sm->r1kh_id,
 +                        sm->own_addr, sm->pmk_r1, sm->pmk_r1_name);
 +      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);
 +
 +      bssid = target_ap;
 +      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, sm->ptk.kck_len, bssid,
 +                                  ric_ies, ric_ies_len,
 +                                  parse.mdie ? parse.mdie - 2 : NULL);
 +      if (ft_ies) {
 +              wpa_sm_update_ft_ies(sm, sm->mobility_domain,
 +                                   ft_ies, ft_ies_len);
 +              os_free(ft_ies);
 +      }
 +
 +      wpa_sm_mark_authenticated(sm, bssid);
 +      ret = wpa_ft_install_ptk(sm, bssid);
 +      if (ret) {
 +              /*
 +               * Some drivers do not support key configuration when we are
 +               * not associated with the target AP. Work around this by
 +               * trying again after the following reassociation gets
 +               * completed.
 +               */
 +              wpa_printf(MSG_DEBUG, "FT: Failed to set PTK prior to "
 +                         "association - try again after reassociation");
 +              sm->set_ptk_after_assoc = 1;
 +      } else
 +              sm->set_ptk_after_assoc = 0;
 +
 +      sm->ft_completed = 1;
 +      if (ft_action) {
 +              /*
 +               * The caller is expected trigger re-association with the
 +               * Target AP.
 +               */
 +              os_memcpy(sm->bssid, target_ap, ETH_ALEN);
 +      }
 +
 +      return 0;
 +}
 +
 +
 +int wpa_ft_is_completed(struct wpa_sm *sm)
 +{
 +      if (sm == NULL)
 +              return 0;
 +
 +      if (!wpa_key_mgmt_ft(sm->key_mgmt))
 +              return 0;
 +
 +      return sm->ft_completed;
 +}
 +
 +
 +void wpa_reset_ft_completed(struct wpa_sm *sm)
 +{
 +      if (sm != NULL)
 +              sm->ft_completed = 0;
 +}
 +
 +
 +static int wpa_ft_process_gtk_subelem(struct wpa_sm *sm, const u8 *gtk_elem,
 +                                    size_t gtk_elem_len)
 +{
 +      u8 gtk[32];
 +      int keyidx;
 +      enum wpa_alg alg;
 +      size_t gtk_len, keylen, rsc_len;
 +
 +      if (gtk_elem == NULL) {
 +              wpa_printf(MSG_DEBUG, "FT: No GTK included in FTIE");
 +              return 0;
 +      }
 +
 +      wpa_hexdump_key(MSG_DEBUG, "FT: Received GTK in Reassoc Resp",
 +                      gtk_elem, gtk_elem_len);
 +
 +      if (gtk_elem_len < 11 + 24 || (gtk_elem_len - 11) % 8 ||
 +          gtk_elem_len - 19 > sizeof(gtk)) {
 +              wpa_printf(MSG_DEBUG, "FT: Invalid GTK sub-elem "
 +                         "length %lu", (unsigned long) gtk_elem_len);
 +              return -1;
 +      }
 +      gtk_len = gtk_elem_len - 19;
 +      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;
 +      }
 +
 +      keylen = wpa_cipher_key_len(sm->group_cipher);
 +      rsc_len = wpa_cipher_rsc_len(sm->group_cipher);
 +      alg = wpa_cipher_to_alg(sm->group_cipher);
 +      if (alg == WPA_ALG_NONE) {
 +              wpa_printf(MSG_WARNING, "WPA: Unsupported Group Cipher %d",
 +                         sm->group_cipher);
 +              return -1;
 +      }
 +
 +      if (gtk_len < keylen) {
 +              wpa_printf(MSG_DEBUG, "FT: Too short GTK in FTIE");
 +              return -1;
 +      }
 +
 +      /* Key Info[2] | Key Length[1] | RSC[8] | Key[5..32]. */
 +
 +      keyidx = WPA_GET_LE16(gtk_elem) & 0x03;
 +
 +      if (gtk_elem[2] != keylen) {
 +              wpa_printf(MSG_DEBUG, "FT: GTK length mismatch: received %d "
 +                         "negotiated %lu",
 +                         gtk_elem[2], (unsigned long) keylen);
 +              return -1;
 +      }
 +
 +      wpa_hexdump_key(MSG_DEBUG, "FT: GTK from Reassoc Resp", gtk, keylen);
 +      if (sm->group_cipher == WPA_CIPHER_TKIP) {
 +              /* Swap Tx/Rx keys for Michael MIC */
 +              u8 tmp[8];
 +              os_memcpy(tmp, gtk + 16, 8);
 +              os_memcpy(gtk + 16, gtk + 24, 8);
 +              os_memcpy(gtk + 24, tmp, 8);
 +      }
 +      if (wpa_sm_set_key(sm, alg, broadcast_ether_addr, keyidx, 0,
 +                         gtk_elem + 3, rsc_len, gtk, keylen) < 0) {
 +              wpa_printf(MSG_WARNING, "WPA: Failed to set GTK to the "
 +                         "driver.");
 +              return -1;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +#ifdef CONFIG_IEEE80211W
 +static int wpa_ft_process_igtk_subelem(struct wpa_sm *sm, const u8 *igtk_elem,
 +                                     size_t igtk_elem_len)
 +{
 +      u8 igtk[WPA_IGTK_LEN];
 +      u16 keyidx;
 +
 +      if (sm->mgmt_group_cipher != WPA_CIPHER_AES_128_CMAC)
 +              return 0;
 +
 +      if (igtk_elem == NULL) {
 +              wpa_printf(MSG_DEBUG, "FT: No IGTK included in FTIE");
 +              return 0;
 +      }
 +
 +      wpa_hexdump_key(MSG_DEBUG, "FT: Received IGTK in Reassoc Resp",
 +                      igtk_elem, igtk_elem_len);
 +
 +      if (igtk_elem_len != 2 + 6 + 1 + WPA_IGTK_LEN + 8) {
 +              wpa_printf(MSG_DEBUG, "FT: Invalid IGTK sub-elem "
 +                         "length %lu", (unsigned long) igtk_elem_len);
 +              return -1;
 +      }
 +      if (igtk_elem[8] != WPA_IGTK_LEN) {
 +              wpa_printf(MSG_DEBUG, "FT: Invalid IGTK sub-elem Key Length "
 +                         "%d", igtk_elem[8]);
 +              return -1;
 +      }
 +
 +      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;
 +      }
 +
 +      /* KeyID[2] | IPN[6] | Key Length[1] | Key[16+8] */
 +
 +      keyidx = WPA_GET_LE16(igtk_elem);
 +
 +      wpa_hexdump_key(MSG_DEBUG, "FT: IGTK from Reassoc Resp", igtk,
 +                      WPA_IGTK_LEN);
 +      if (wpa_sm_set_key(sm, WPA_ALG_IGTK, broadcast_ether_addr, keyidx, 0,
 +                         igtk_elem + 2, 6, igtk, WPA_IGTK_LEN) < 0) {
 +              wpa_printf(MSG_WARNING, "WPA: Failed to set IGTK to the "
 +                         "driver.");
 +              return -1;
 +      }
 +
 +      return 0;
 +}
 +#endif /* CONFIG_IEEE80211W */
 +
 +
 +int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies,
 +                               size_t ies_len, const u8 *src_addr)
 +{
 +      struct wpa_ft_ies parse;
 +      struct rsn_mdie *mdie;
 +      struct rsn_ftie *ftie;
 +      unsigned int count;
 +      u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN];
 +
 +      wpa_hexdump(MSG_DEBUG, "FT: Response IEs", ies, ies_len);
 +
 +      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;
 +      }
 +
 +      if (wpa_ft_parse_ies(ies, ies_len, &parse) < 0) {
 +              wpa_printf(MSG_DEBUG, "FT: Failed to parse IEs");
 +              return -1;
 +      }
 +
 +      mdie = (struct rsn_mdie *) parse.mdie;
 +      if (mdie == NULL || parse.mdie_len < sizeof(*mdie) ||
 +          os_memcmp(mdie->mobility_domain, sm->mobility_domain,
 +                    MOBILITY_DOMAIN_ID_LEN) != 0) {
 +              wpa_printf(MSG_DEBUG, "FT: Invalid MDIE");
 +              return -1;
 +      }
 +
 +      ftie = (struct rsn_ftie *) parse.ftie;
 +      if (ftie == NULL || parse.ftie_len < sizeof(*ftie)) {
 +              wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
 +              return -1;
 +      }
 +
 +      if (os_memcmp(ftie->snonce, sm->snonce, WPA_NONCE_LEN) != 0) {
 +              wpa_printf(MSG_DEBUG, "FT: SNonce mismatch in FTIE");
 +              wpa_hexdump(MSG_DEBUG, "FT: Received SNonce",
 +                          ftie->snonce, WPA_NONCE_LEN);
 +              wpa_hexdump(MSG_DEBUG, "FT: Expected SNonce",
 +                          sm->snonce, WPA_NONCE_LEN);
 +              return -1;
 +      }
 +
 +      if (os_memcmp(ftie->anonce, sm->anonce, WPA_NONCE_LEN) != 0) {
 +              wpa_printf(MSG_DEBUG, "FT: ANonce mismatch in FTIE");
 +              wpa_hexdump(MSG_DEBUG, "FT: Received ANonce",
 +                          ftie->anonce, WPA_NONCE_LEN);
 +              wpa_hexdump(MSG_DEBUG, "FT: Expected ANonce",
 +                          sm->anonce, WPA_NONCE_LEN);
 +              return -1;
 +      }
 +
 +      if (parse.r0kh_id == NULL) {
 +              wpa_printf(MSG_DEBUG, "FT: No R0KH-ID subelem in FTIE");
 +              return -1;
 +      }
 +
 +      if (parse.r0kh_id_len != sm->r0kh_id_len ||
 +          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",
 +                          parse.r0kh_id, parse.r0kh_id_len);
 +              wpa_hexdump(MSG_DEBUG, "FT: The current R0KH-ID",
 +                          sm->r0kh_id, sm->r0kh_id_len);
 +              return -1;
 +      }
 +
 +      if (parse.r1kh_id == NULL) {
 +              wpa_printf(MSG_DEBUG, "FT: No R1KH-ID subelem in FTIE");
 +              return -1;
 +      }
 +
 +      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_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;
 +      }
 +
 +      count = 3;
 +      if (parse.ric)
 +              count += ieee802_11_ie_count(parse.ric, parse.ric_len);
 +      if (ftie->mic_control[1] != count) {
 +              wpa_printf(MSG_DEBUG, "FT: Unexpected IE count in MIC "
 +                         "Control: received %u expected %u",
 +                         ftie->mic_control[1], count);
 +              return -1;
 +      }
 +
 +      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,
 +                     parse.ric, parse.ric_len,
 +                     mic) < 0) {
 +              wpa_printf(MSG_DEBUG, "FT: Failed to calculate MIC");
 +              return -1;
 +      }
 +
 +      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);
 +              return -1;
 +      }
 +
 +      if (wpa_ft_process_gtk_subelem(sm, parse.gtk, parse.gtk_len) < 0)
 +              return -1;
 +
 +#ifdef CONFIG_IEEE80211W
 +      if (wpa_ft_process_igtk_subelem(sm, parse.igtk, parse.igtk_len) < 0)
 +              return -1;
 +#endif /* CONFIG_IEEE80211W */
 +
 +      if (sm->set_ptk_after_assoc) {
 +              wpa_printf(MSG_DEBUG, "FT: Try to set PTK again now that we "
 +                         "are associated");
 +              if (wpa_ft_install_ptk(sm, src_addr) < 0)
 +                      return -1;
 +              sm->set_ptk_after_assoc = 0;
 +      }
 +
 +      if (parse.ric) {
 +              wpa_hexdump(MSG_MSGDUMP, "FT: RIC Response",
 +                          parse.ric, parse.ric_len);
 +              /* TODO: parse response and inform driver about results when
 +               * using wpa_supplicant SME */
 +      }
 +
 +      wpa_printf(MSG_DEBUG, "FT: Completed successfully");
 +
 +      return 0;
 +}
 +
 +
 +/**
 + * wpa_ft_start_over_ds - Generate over-the-DS auth request
 + * @sm: Pointer to WPA state machine data from wpa_sm_init()
 + * @target_ap: Target AP Address
 + * @mdie: Mobility Domain IE from the target AP
 + * Returns: 0 on success, -1 on failure
 + */
 +int wpa_ft_start_over_ds(struct wpa_sm *sm, const u8 *target_ap,
 +                       const u8 *mdie)
 +{
 +      u8 *ft_ies;
 +      size_t ft_ies_len;
 +
 +      wpa_printf(MSG_DEBUG, "FT: Request over-the-DS with " MACSTR,
 +                 MAC2STR(target_ap));
 +
 +      /* Generate a new SNonce */
 +      if (random_get_bytes(sm->snonce, WPA_NONCE_LEN)) {
 +              wpa_printf(MSG_INFO, "FT: Failed to generate a new SNonce");
 +              return -1;
 +      }
 +
 +      ft_ies = wpa_ft_gen_req_ies(sm, &ft_ies_len, NULL, sm->pmk_r0_name,
 +                                  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);
 +              wpa_sm_send_ft_action(sm, 1, target_ap, ft_ies, ft_ies_len);
 +              os_free(ft_ies);
 +      }
 +
 +      return 0;
 +}
 +
 +#endif /* CONFIG_IEEE80211R */
index cb334df675bebe98ea70d10c4d13725ccc1e8cf0,0000000000000000000000000000000000000000..0c37b35c1ee1df83693db4247485629f08f5cef9
mode 100644,000000..100644
--- /dev/null
@@@ -1,599 -1,0 +1,605 @@@
-               } else if (*pos == WLAN_EID_MOBILITY_DOMAIN) {
 +/*
 + * wpa_supplicant - WPA/RSN IE and KDE processing
 + * 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 "includes.h"
 +
 +#include "common.h"
 +#include "wpa.h"
 +#include "pmksa_cache.h"
 +#include "common/ieee802_11_defs.h"
 +#include "wpa_i.h"
 +#include "wpa_ie.h"
 +
 +
 +/**
 + * wpa_parse_wpa_ie - Parse WPA/RSN IE
 + * @wpa_ie: Pointer to WPA or RSN IE
 + * @wpa_ie_len: Length of the WPA/RSN IE
 + * @data: Pointer to data area for parsing results
 + * Returns: 0 on success, -1 on failure
 + *
 + * Parse the contents of WPA or RSN IE and write the parsed data into data.
 + */
 +int wpa_parse_wpa_ie(const u8 *wpa_ie, size_t wpa_ie_len,
 +                   struct wpa_ie_data *data)
 +{
 +      if (wpa_ie_len >= 1 && wpa_ie[0] == WLAN_EID_RSN)
 +              return wpa_parse_wpa_ie_rsn(wpa_ie, wpa_ie_len, data);
++      if (wpa_ie_len >= 6 && wpa_ie[0] == WLAN_EID_VENDOR_SPECIFIC &&
++          wpa_ie[1] >= 4 && WPA_GET_BE32(&wpa_ie[2]) == OSEN_IE_VENDOR_TYPE)
++              return wpa_parse_wpa_ie_rsn(wpa_ie, wpa_ie_len, data);
 +      else
 +              return wpa_parse_wpa_ie_wpa(wpa_ie, wpa_ie_len, data);
 +}
 +
 +
 +static int wpa_gen_wpa_ie_wpa(u8 *wpa_ie, size_t wpa_ie_len,
 +                            int pairwise_cipher, int group_cipher,
 +                            int key_mgmt)
 +{
 +      u8 *pos;
 +      struct wpa_ie_hdr *hdr;
 +      u32 suite;
 +
 +      if (wpa_ie_len < sizeof(*hdr) + WPA_SELECTOR_LEN +
 +          2 + WPA_SELECTOR_LEN + 2 + WPA_SELECTOR_LEN)
 +              return -1;
 +
 +      hdr = (struct wpa_ie_hdr *) wpa_ie;
 +      hdr->elem_id = WLAN_EID_VENDOR_SPECIFIC;
 +      RSN_SELECTOR_PUT(hdr->oui, WPA_OUI_TYPE);
 +      WPA_PUT_LE16(hdr->version, WPA_VERSION);
 +      pos = (u8 *) (hdr + 1);
 +
 +      suite = wpa_cipher_to_suite(WPA_PROTO_WPA, group_cipher);
 +      if (suite == 0) {
 +              wpa_printf(MSG_WARNING, "Invalid group cipher (%d).",
 +                         group_cipher);
 +              return -1;
 +      }
 +      RSN_SELECTOR_PUT(pos, suite);
 +      pos += WPA_SELECTOR_LEN;
 +
 +      *pos++ = 1;
 +      *pos++ = 0;
 +      suite = wpa_cipher_to_suite(WPA_PROTO_WPA, 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 += WPA_SELECTOR_LEN;
 +
 +      *pos++ = 1;
 +      *pos++ = 0;
 +      if (key_mgmt == WPA_KEY_MGMT_IEEE8021X) {
 +              RSN_SELECTOR_PUT(pos, WPA_AUTH_KEY_MGMT_UNSPEC_802_1X);
 +      } else if (key_mgmt == WPA_KEY_MGMT_PSK) {
 +              RSN_SELECTOR_PUT(pos, WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X);
 +      } else if (key_mgmt == WPA_KEY_MGMT_WPA_NONE) {
 +              RSN_SELECTOR_PUT(pos, WPA_AUTH_KEY_MGMT_NONE);
 +      } else if (key_mgmt == WPA_KEY_MGMT_CCKM) {
 +              RSN_SELECTOR_PUT(pos, WPA_AUTH_KEY_MGMT_CCKM);
 +      } else {
 +              wpa_printf(MSG_WARNING, "Invalid key management type (%d).",
 +                         key_mgmt);
 +              return -1;
 +      }
 +      pos += WPA_SELECTOR_LEN;
 +
 +      /* WPA Capabilities; use defaults, so no need to include it */
 +
 +      hdr->len = (pos - wpa_ie) - 2;
 +
 +      WPA_ASSERT((size_t) (pos - wpa_ie) <= wpa_ie_len);
 +
 +      return pos - wpa_ie;
 +}
 +
 +
 +static int wpa_gen_wpa_ie_rsn(u8 *rsn_ie, size_t rsn_ie_len,
 +                            int pairwise_cipher, int group_cipher,
 +                            int key_mgmt, int mgmt_group_cipher,
 +                            struct wpa_sm *sm)
 +{
 +      u8 *pos;
 +      struct rsn_ie_hdr *hdr;
 +      u16 capab;
 +      u32 suite;
 +
 +      if (rsn_ie_len < sizeof(*hdr) + RSN_SELECTOR_LEN +
 +          2 + RSN_SELECTOR_LEN + 2 + RSN_SELECTOR_LEN + 2 +
 +          (sm->cur_pmksa ? 2 + PMKID_LEN : 0)) {
 +              wpa_printf(MSG_DEBUG, "RSN: Too short IE buffer (%lu bytes)",
 +                         (unsigned long) rsn_ie_len);
 +              return -1;
 +      }
 +
 +      hdr = (struct rsn_ie_hdr *) rsn_ie;
 +      hdr->elem_id = WLAN_EID_RSN;
 +      WPA_PUT_LE16(hdr->version, RSN_VERSION);
 +      pos = (u8 *) (hdr + 1);
 +
 +      suite = wpa_cipher_to_suite(WPA_PROTO_RSN, 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;
 +
 +      *pos++ = 1;
 +      *pos++ = 0;
 +      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;
 +
 +      *pos++ = 1;
 +      *pos++ = 0;
 +      if (key_mgmt == WPA_KEY_MGMT_IEEE8021X) {
 +              RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_UNSPEC_802_1X);
 +      } else if (key_mgmt == WPA_KEY_MGMT_PSK) {
 +              RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X);
 +      } else if (key_mgmt == WPA_KEY_MGMT_CCKM) {
 +              RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_CCKM);
 +#ifdef CONFIG_IEEE80211R
 +      } else if (key_mgmt == WPA_KEY_MGMT_FT_IEEE8021X) {
 +              RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_802_1X);
 +      } else if (key_mgmt == WPA_KEY_MGMT_FT_PSK) {
 +              RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_PSK);
 +#endif /* CONFIG_IEEE80211R */
 +#ifdef CONFIG_IEEE80211W
 +      } else if (key_mgmt == WPA_KEY_MGMT_IEEE8021X_SHA256) {
 +              RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SHA256);
 +      } else if (key_mgmt == WPA_KEY_MGMT_PSK_SHA256) {
 +              RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_PSK_SHA256);
 +#endif /* CONFIG_IEEE80211W */
 +#ifdef CONFIG_SAE
 +      } else if (key_mgmt == WPA_KEY_MGMT_SAE) {
 +              RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_SAE);
 +      } 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);
 +              return -1;
 +      }
 +      pos += RSN_SELECTOR_LEN;
 +
 +      /* RSN Capabilities */
 +      capab = 0;
 +#ifdef CONFIG_IEEE80211W
 +      if (sm->mfp)
 +              capab |= WPA_CAPABILITY_MFPC;
 +      if (sm->mfp == 2)
 +              capab |= WPA_CAPABILITY_MFPR;
 +#endif /* CONFIG_IEEE80211W */
 +      WPA_PUT_LE16(pos, capab);
 +      pos += 2;
 +
 +      if (sm->cur_pmksa) {
 +              /* PMKID Count (2 octets, little endian) */
 +              *pos++ = 1;
 +              *pos++ = 0;
 +              /* PMKID */
 +              os_memcpy(pos, sm->cur_pmksa->pmkid, PMKID_LEN);
 +              pos += PMKID_LEN;
 +      }
 +
 +#ifdef CONFIG_IEEE80211W
 +      if (wpa_cipher_valid_mgmt_group(mgmt_group_cipher)) {
 +              if (!sm->cur_pmksa) {
 +                      /* PMKID Count */
 +                      WPA_PUT_LE16(pos, 0);
 +                      pos += 2;
 +              }
 +
 +              /* Management Group Cipher Suite */
 +              RSN_SELECTOR_PUT(pos, wpa_cipher_to_suite(WPA_PROTO_RSN,
 +                                                        mgmt_group_cipher));
 +              pos += RSN_SELECTOR_LEN;
 +      }
 +#endif /* CONFIG_IEEE80211W */
 +
 +      hdr->len = (pos - rsn_ie) - 2;
 +
 +      WPA_ASSERT((size_t) (pos - rsn_ie) <= rsn_ie_len);
 +
 +      return pos - rsn_ie;
 +}
 +
 +
 +#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()
 + * @wpa_ie: Pointer to memory area for the generated WPA/RSN IE
 + * @wpa_ie_len: Maximum length of the generated WPA/RSN IE
 + * Returns: Length of the generated WPA/RSN IE or -1 on failure
 + */
 +int wpa_gen_wpa_ie(struct wpa_sm *sm, u8 *wpa_ie, size_t wpa_ie_len)
 +{
 +      if (sm->proto == WPA_PROTO_RSN)
 +              return wpa_gen_wpa_ie_rsn(wpa_ie, wpa_ie_len,
 +                                        sm->pairwise_cipher,
 +                                        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,
 +                                        sm->group_cipher,
 +                                        sm->key_mgmt);
 +}
 +
 +
 +/**
 + * 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
 + * @ie: Pointer to parsed IE data
 + * Returns: 0 on success, 1 if end mark is found, -1 on failure
 + */
 +static int wpa_parse_generic(const u8 *pos, const u8 *end,
 +                           struct wpa_eapol_ie_parse *ie)
 +{
 +      if (pos[1] == 0)
 +              return 1;
 +
 +      if (pos[1] >= 6 &&
 +          RSN_SELECTOR_GET(pos + 2) == WPA_OUI_TYPE &&
 +          pos[2 + WPA_SELECTOR_LEN] == 1 &&
 +          pos[2 + WPA_SELECTOR_LEN + 1] == 0) {
 +              ie->wpa_ie = pos;
 +              ie->wpa_ie_len = pos[1] + 2;
 +              wpa_hexdump(MSG_DEBUG, "WPA: WPA IE in EAPOL-Key",
 +                          ie->wpa_ie, ie->wpa_ie_len);
 +              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) {
 +              ie->pmkid = pos + 2 + RSN_SELECTOR_LEN;
 +              wpa_hexdump(MSG_DEBUG, "WPA: PMKID in EAPOL-Key",
 +                          pos, pos[1] + 2);
 +              return 0;
 +      }
 +
 +      if (pos[1] > RSN_SELECTOR_LEN + 2 &&
 +          RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_GROUPKEY) {
 +              ie->gtk = pos + 2 + RSN_SELECTOR_LEN;
 +              ie->gtk_len = pos[1] - RSN_SELECTOR_LEN;
 +              wpa_hexdump_key(MSG_DEBUG, "WPA: GTK in EAPOL-Key",
 +                              pos, pos[1] + 2);
 +              return 0;
 +      }
 +
 +      if (pos[1] > RSN_SELECTOR_LEN + 2 &&
 +          RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_MAC_ADDR) {
 +              ie->mac_addr = pos + 2 + RSN_SELECTOR_LEN;
 +              ie->mac_addr_len = pos[1] - RSN_SELECTOR_LEN;
 +              wpa_hexdump(MSG_DEBUG, "WPA: MAC Address in EAPOL-Key",
 +                          pos, pos[1] + 2);
 +              return 0;
 +      }
 +
 +#ifdef CONFIG_PEERKEY
 +      if (pos[1] > RSN_SELECTOR_LEN + 2 &&
 +          RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_SMK) {
 +              ie->smk = pos + 2 + RSN_SELECTOR_LEN;
 +              ie->smk_len = pos[1] - RSN_SELECTOR_LEN;
 +              wpa_hexdump_key(MSG_DEBUG, "WPA: SMK in EAPOL-Key",
 +                              pos, pos[1] + 2);
 +              return 0;
 +      }
 +
 +      if (pos[1] > RSN_SELECTOR_LEN + 2 &&
 +          RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_NONCE) {
 +              ie->nonce = pos + 2 + RSN_SELECTOR_LEN;
 +              ie->nonce_len = pos[1] - RSN_SELECTOR_LEN;
 +              wpa_hexdump(MSG_DEBUG, "WPA: Nonce in EAPOL-Key",
 +                          pos, pos[1] + 2);
 +              return 0;
 +      }
 +
 +      if (pos[1] > RSN_SELECTOR_LEN + 2 &&
 +          RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_LIFETIME) {
 +              ie->lifetime = pos + 2 + RSN_SELECTOR_LEN;
 +              ie->lifetime_len = pos[1] - RSN_SELECTOR_LEN;
 +              wpa_hexdump(MSG_DEBUG, "WPA: Lifetime in EAPOL-Key",
 +                          pos, pos[1] + 2);
 +              return 0;
 +      }
 +
 +      if (pos[1] > RSN_SELECTOR_LEN + 2 &&
 +          RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_ERROR) {
 +              ie->error = pos + 2 + RSN_SELECTOR_LEN;
 +              ie->error_len = pos[1] - RSN_SELECTOR_LEN;
 +              wpa_hexdump(MSG_DEBUG, "WPA: Error in EAPOL-Key",
 +                          pos, pos[1] + 2);
 +              return 0;
 +      }
 +#endif /* CONFIG_PEERKEY */
 +
 +#ifdef CONFIG_IEEE80211W
 +      if (pos[1] > RSN_SELECTOR_LEN + 2 &&
 +          RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_IGTK) {
 +              ie->igtk = pos + 2 + RSN_SELECTOR_LEN;
 +              ie->igtk_len = pos[1] - RSN_SELECTOR_LEN;
 +              wpa_hexdump_key(MSG_DEBUG, "WPA: IGTK in EAPOL-Key",
 +                              pos, pos[1] + 2);
 +              return 0;
 +      }
 +#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;
 +}
 +
 +
 +/**
 + * wpa_supplicant_parse_ies - Parse EAPOL-Key Key Data IEs
 + * @buf: Pointer to the Key Data buffer
 + * @len: Key Data Length
 + * @ie: Pointer to parsed IE data
 + * Returns: 0 on success, -1 on failure
 + */
 +int wpa_supplicant_parse_ies(const u8 *buf, size_t len,
 +                           struct wpa_eapol_ie_parse *ie)
 +{
 +      const u8 *pos, *end;
 +      int ret = 0;
 +
 +      os_memset(ie, 0, sizeof(*ie));
 +      for (pos = buf, end = pos + len; pos + 1 < end; pos += 2 + pos[1]) {
 +              if (pos[0] == 0xdd &&
 +                  ((pos == buf + len - 1) || pos[1] == 0)) {
 +                      /* Ignore padding */
 +                      break;
 +              }
 +              if (pos + 2 + pos[1] > end) {
 +                      wpa_printf(MSG_DEBUG, "WPA: EAPOL-Key Key Data "
 +                                 "underflow (ie=%d len=%d pos=%d)",
 +                                 pos[0], pos[1], (int) (pos - buf));
 +                      wpa_hexdump_key(MSG_DEBUG, "WPA: Key Data",
 +                                      buf, len);
 +                      ret = -1;
 +                      break;
 +              }
 +              if (*pos == WLAN_EID_RSN) {
 +                      ie->rsn_ie = pos;
 +                      ie->rsn_ie_len = pos[1] + 2;
 +                      wpa_hexdump(MSG_DEBUG, "WPA: RSN IE in EAPOL-Key",
 +                                  ie->rsn_ie, ie->rsn_ie_len);
-               } else if (*pos == WLAN_EID_FAST_BSS_TRANSITION) {
++              } else if (*pos == WLAN_EID_MOBILITY_DOMAIN &&
++                         pos[1] >= sizeof(struct rsn_mdie)) {
 +                      ie->mdie = pos;
 +                      ie->mdie_len = pos[1] + 2;
 +                      wpa_hexdump(MSG_DEBUG, "WPA: MDIE in EAPOL-Key",
 +                                  ie->mdie, ie->mdie_len);
-               } else if (*pos == WLAN_EID_HT_CAP) {
++              } else if (*pos == WLAN_EID_FAST_BSS_TRANSITION &&
++                         pos[1] >= sizeof(struct rsn_ftie)) {
 +                      ie->ftie = pos;
 +                      ie->ftie_len = pos[1] + 2;
 +                      wpa_hexdump(MSG_DEBUG, "WPA: FTIE in EAPOL-Key",
 +                                  ie->ftie, ie->ftie_len);
 +              } else if (*pos == WLAN_EID_TIMEOUT_INTERVAL && pos[1] >= 5) {
 +                      if (pos[2] == WLAN_TIMEOUT_REASSOC_DEADLINE) {
 +                              ie->reassoc_deadline = pos;
 +                              wpa_hexdump(MSG_DEBUG, "WPA: Reassoc Deadline "
 +                                          "in EAPOL-Key",
 +                                          ie->reassoc_deadline, pos[1] + 2);
 +                      } else if (pos[2] == WLAN_TIMEOUT_KEY_LIFETIME) {
 +                              ie->key_lifetime = pos;
 +                              wpa_hexdump(MSG_DEBUG, "WPA: KeyLifetime "
 +                                          "in EAPOL-Key",
 +                                          ie->key_lifetime, pos[1] + 2);
 +                      } else {
 +                              wpa_hexdump(MSG_DEBUG, "WPA: Unrecognized "
 +                                          "EAPOL-Key Key Data IE",
 +                                          pos, 2 + pos[1]);
 +                      }
 +              } else if (*pos == WLAN_EID_LINK_ID) {
 +                      if (pos[1] >= 18) {
 +                              ie->lnkid = pos;
 +                              ie->lnkid_len = pos[1] + 2;
 +                      }
 +              } else if (*pos == WLAN_EID_EXT_CAPAB) {
 +                      ie->ext_capab = pos;
 +                      ie->ext_capab_len = pos[1] + 2;
 +              } else if (*pos == WLAN_EID_SUPP_RATES) {
 +                      ie->supp_rates = pos;
 +                      ie->supp_rates_len = pos[1] + 2;
 +              } else if (*pos == WLAN_EID_EXT_SUPP_RATES) {
 +                      ie->ext_supp_rates = pos;
 +                      ie->ext_supp_rates_len = pos[1] + 2;
-                       ie->ht_capabilities_len = pos[1];
++              } else if (*pos == WLAN_EID_HT_CAP &&
++                         pos[1] >= sizeof(struct ieee80211_ht_capabilities)) {
 +                      ie->ht_capabilities = pos + 2;
-               } else if (*pos == WLAN_EID_VHT_CAP) {
 +              } else if (*pos == WLAN_EID_VHT_AID) {
 +                      if (pos[1] >= 2)
 +                              ie->aid = WPA_GET_LE16(pos + 2) & 0x3fff;
-                       ie->vht_capabilities_len = pos[1];
++              } else if (*pos == WLAN_EID_VHT_CAP &&
++                         pos[1] >= sizeof(struct ieee80211_vht_capabilities))
++              {
 +                      ie->vht_capabilities = pos + 2;
 +              } 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)
 +                              break;
 +                      if (ret > 0) {
 +                              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]);
 +              }
 +      }
 +
 +      return ret;
 +}
index 0fc42cc492ac53115bca7d7716ea8b1a76ba4035,0000000000000000000000000000000000000000..fe95af0abc5194c706ef1f113c9b782ae45a1a7b
mode 100644,000000..100644
--- /dev/null
@@@ -1,74 -1,0 +1,72 @@@
-       size_t ht_capabilities_len;
 +/*
 + * wpa_supplicant - WPA/RSN IE and KDE definitions
 + * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
 + *
 + * This software may be distributed under the terms of the BSD license.
 + * See README for more details.
 + */
 +
 +#ifndef WPA_IE_H
 +#define WPA_IE_H
 +
 +struct wpa_sm;
 +
 +struct wpa_eapol_ie_parse {
 +      const u8 *wpa_ie;
 +      size_t wpa_ie_len;
 +      const u8 *rsn_ie;
 +      size_t rsn_ie_len;
 +      const u8 *pmkid;
 +      const u8 *gtk;
 +      size_t gtk_len;
 +      const u8 *mac_addr;
 +      size_t mac_addr_len;
 +#ifdef CONFIG_PEERKEY
 +      const u8 *smk;
 +      size_t smk_len;
 +      const u8 *nonce;
 +      size_t nonce_len;
 +      const u8 *lifetime;
 +      size_t lifetime_len;
 +      const u8 *error;
 +      size_t error_len;
 +#endif /* CONFIG_PEERKEY */
 +#ifdef CONFIG_IEEE80211W
 +      const u8 *igtk;
 +      size_t igtk_len;
 +#endif /* CONFIG_IEEE80211W */
 +      const u8 *mdie;
 +      size_t mdie_len;
 +      const u8 *ftie;
 +      size_t ftie_len;
 +      const u8 *reassoc_deadline;
 +      const u8 *key_lifetime;
 +      const u8 *lnkid;
 +      size_t lnkid_len;
 +      const u8 *ext_capab;
 +      size_t ext_capab_len;
 +      const u8 *supp_rates;
 +      size_t supp_rates_len;
 +      const u8 *ext_supp_rates;
 +      size_t ext_supp_rates_len;
 +      const u8 *ht_capabilities;
-       size_t vht_capabilities_len;
 +      const u8 *vht_capabilities;
 +      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,
 +                           struct wpa_eapol_ie_parse *ie);
 +int wpa_gen_wpa_ie(struct wpa_sm *sm, u8 *wpa_ie, size_t wpa_ie_len);
 +
 +#endif /* WPA_IE_H */
index 3fb8fbed25e78b780ebd2f6a9469b8d486ac627e,0000000000000000000000000000000000000000..8bc824f20dcdc4a7932686e39e8de63fb77f2eba
mode 100644,000000..100644
--- /dev/null
@@@ -1,3401 -1,0 +1,3400 @@@
-             res = MP_MEM;
-             break;
 +/*
 + * Minimal code for RSA support from LibTomMath 0.41
 + * http://libtom.org/
 + * http://libtom.org/files/ltm-0.41.tar.bz2
 + * This library was released in public domain by Tom St Denis.
 + *
 + * The combination in this file may not use all of the optimized algorithms
 + * from LibTomMath and may be considerable slower than the LibTomMath with its
 + * default settings. The main purpose of having this version here is to make it
 + * easier to build bignum.c wrapper without having to install and build an
 + * external library.
 + *
 + * If CONFIG_INTERNAL_LIBTOMMATH is defined, bignum.c includes this
 + * libtommath.c file instead of using the external LibTomMath library.
 + */
 +
 +#ifndef CHAR_BIT
 +#define CHAR_BIT 8
 +#endif
 +
 +#define BN_MP_INVMOD_C
 +#define BN_S_MP_EXPTMOD_C /* Note: #undef in tommath_superclass.h; this would
 +                         * require BN_MP_EXPTMOD_FAST_C instead */
 +#define BN_S_MP_MUL_DIGS_C
 +#define BN_MP_INVMOD_SLOW_C
 +#define BN_S_MP_SQR_C
 +#define BN_S_MP_MUL_HIGH_DIGS_C /* Note: #undef in tommath_superclass.h; this
 +                               * would require other than mp_reduce */
 +
 +#ifdef LTM_FAST
 +
 +/* Use faster div at the cost of about 1 kB */
 +#define BN_MP_MUL_D_C
 +
 +/* Include faster exptmod (Montgomery) at the cost of about 2.5 kB in code */
 +#define BN_MP_EXPTMOD_FAST_C
 +#define BN_MP_MONTGOMERY_SETUP_C
 +#define BN_FAST_MP_MONTGOMERY_REDUCE_C
 +#define BN_MP_MONTGOMERY_CALC_NORMALIZATION_C
 +#define BN_MP_MUL_2_C
 +
 +/* Include faster sqr at the cost of about 0.5 kB in code */
 +#define BN_FAST_S_MP_SQR_C
 +
 +/* About 0.25 kB of code, but ~1.7kB of stack space! */
 +#define BN_FAST_S_MP_MUL_DIGS_C
 +
 +#else /* LTM_FAST */
 +
 +#define BN_MP_DIV_SMALL
 +#define BN_MP_INIT_MULTI_C
 +#define BN_MP_CLEAR_MULTI_C
 +#define BN_MP_ABS_C
 +#endif /* LTM_FAST */
 +
 +/* Current uses do not require support for negative exponent in exptmod, so we
 + * can save about 1.5 kB in leaving out invmod. */
 +#define LTM_NO_NEG_EXP
 +
 +/* from tommath.h */
 +
 +#ifndef MIN
 +   #define MIN(x,y) ((x)<(y)?(x):(y))
 +#endif
 +
 +#ifndef MAX
 +   #define MAX(x,y) ((x)>(y)?(x):(y))
 +#endif
 +
 +#define  OPT_CAST(x)
 +
 +#ifdef __x86_64__
 +typedef unsigned long mp_digit;
 +typedef unsigned long mp_word __attribute__((mode(TI)));
 +
 +#define DIGIT_BIT 60
 +#define MP_64BIT
 +#else
 +typedef unsigned long mp_digit;
 +typedef u64 mp_word;
 +
 +#define DIGIT_BIT          28
 +#define MP_28BIT
 +#endif
 +
 +
 +#define XMALLOC  os_malloc
 +#define XFREE    os_free
 +#define XREALLOC os_realloc
 +
 +
 +#define MP_MASK          ((((mp_digit)1)<<((mp_digit)DIGIT_BIT))-((mp_digit)1))
 +
 +#define MP_LT        -1   /* less than */
 +#define MP_EQ         0   /* equal to */
 +#define MP_GT         1   /* greater than */
 +
 +#define MP_ZPOS       0   /* positive integer */
 +#define MP_NEG        1   /* negative */
 +
 +#define MP_OKAY       0   /* ok result */
 +#define MP_MEM        -2  /* out of mem */
 +#define MP_VAL        -3  /* invalid input */
 +
 +#define MP_YES        1   /* yes response */
 +#define MP_NO         0   /* no response */
 +
 +typedef int           mp_err;
 +
 +/* define this to use lower memory usage routines (exptmods mostly) */
 +#define MP_LOW_MEM
 +
 +/* default precision */
 +#ifndef MP_PREC
 +   #ifndef MP_LOW_MEM
 +      #define MP_PREC                 32     /* default digits of precision */
 +   #else
 +      #define MP_PREC                 8      /* default digits of precision */
 +   #endif   
 +#endif
 +
 +/* size of comba arrays, should be at least 2 * 2**(BITS_PER_WORD - BITS_PER_DIGIT*2) */
 +#define MP_WARRAY               (1 << (sizeof(mp_word) * CHAR_BIT - 2 * DIGIT_BIT + 1))
 +
 +/* the infamous mp_int structure */
 +typedef struct  {
 +    int used, alloc, sign;
 +    mp_digit *dp;
 +} mp_int;
 +
 +
 +/* ---> Basic Manipulations <--- */
 +#define mp_iszero(a) (((a)->used == 0) ? MP_YES : MP_NO)
 +#define mp_iseven(a) (((a)->used > 0 && (((a)->dp[0] & 1) == 0)) ? MP_YES : MP_NO)
 +#define mp_isodd(a)  (((a)->used > 0 && (((a)->dp[0] & 1) == 1)) ? MP_YES : MP_NO)
 +
 +
 +/* prototypes for copied functions */
 +#define s_mp_mul(a, b, c) s_mp_mul_digs(a, b, c, (a)->used + (b)->used + 1)
 +static int s_mp_exptmod(mp_int * G, mp_int * X, mp_int * P, mp_int * Y, int redmode);
 +static int s_mp_mul_digs (mp_int * a, mp_int * b, mp_int * c, int digs);
 +static int s_mp_sqr(mp_int * a, mp_int * b);
 +static int s_mp_mul_high_digs(mp_int * a, mp_int * b, mp_int * c, int digs);
 +
 +#ifdef BN_FAST_S_MP_MUL_DIGS_C
 +static int fast_s_mp_mul_digs (mp_int * a, mp_int * b, mp_int * c, int digs);
 +#endif
 +
 +#ifdef BN_MP_INIT_MULTI_C
 +static int mp_init_multi(mp_int *mp, ...);
 +#endif
 +#ifdef BN_MP_CLEAR_MULTI_C
 +static void mp_clear_multi(mp_int *mp, ...);
 +#endif
 +static int mp_lshd(mp_int * a, int b);
 +static void mp_set(mp_int * a, mp_digit b);
 +static void mp_clamp(mp_int * a);
 +static void mp_exch(mp_int * a, mp_int * b);
 +static void mp_rshd(mp_int * a, int b);
 +static void mp_zero(mp_int * a);
 +static int mp_mod_2d(mp_int * a, int b, mp_int * c);
 +static int mp_div_2d(mp_int * a, int b, mp_int * c, mp_int * d);
 +static int mp_init_copy(mp_int * a, mp_int * b);
 +static int mp_mul_2d(mp_int * a, int b, mp_int * c);
 +#ifndef LTM_NO_NEG_EXP
 +static int mp_div_2(mp_int * a, mp_int * b);
 +static int mp_invmod(mp_int * a, mp_int * b, mp_int * c);
 +static int mp_invmod_slow(mp_int * a, mp_int * b, mp_int * c);
 +#endif /* LTM_NO_NEG_EXP */
 +static int mp_copy(mp_int * a, mp_int * b);
 +static int mp_count_bits(mp_int * a);
 +static int mp_div(mp_int * a, mp_int * b, mp_int * c, mp_int * d);
 +static int mp_mod(mp_int * a, mp_int * b, mp_int * c);
 +static int mp_grow(mp_int * a, int size);
 +static int mp_cmp_mag(mp_int * a, mp_int * b);
 +#ifdef BN_MP_ABS_C
 +static int mp_abs(mp_int * a, mp_int * b);
 +#endif
 +static int mp_sqr(mp_int * a, mp_int * b);
 +static int mp_reduce_2k_l(mp_int *a, mp_int *n, mp_int *d);
 +static int mp_reduce_2k_setup_l(mp_int *a, mp_int *d);
 +static int mp_2expt(mp_int * a, int b);
 +static int mp_reduce_setup(mp_int * a, mp_int * b);
 +static int mp_reduce(mp_int * x, mp_int * m, mp_int * mu);
 +static int mp_init_size(mp_int * a, int size);
 +#ifdef BN_MP_EXPTMOD_FAST_C
 +static int mp_exptmod_fast (mp_int * G, mp_int * X, mp_int * P, mp_int * Y, int redmode);
 +#endif /* BN_MP_EXPTMOD_FAST_C */
 +#ifdef BN_FAST_S_MP_SQR_C
 +static int fast_s_mp_sqr (mp_int * a, mp_int * b);
 +#endif /* BN_FAST_S_MP_SQR_C */
 +#ifdef BN_MP_MUL_D_C
 +static int mp_mul_d (mp_int * a, mp_digit b, mp_int * c);
 +#endif /* BN_MP_MUL_D_C */
 +
 +
 +
 +/* functions from bn_<func name>.c */
 +
 +
 +/* reverse an array, used for radix code */
 +static void bn_reverse (unsigned char *s, int len)
 +{
 +  int     ix, iy;
 +  unsigned char t;
 +
 +  ix = 0;
 +  iy = len - 1;
 +  while (ix < iy) {
 +    t     = s[ix];
 +    s[ix] = s[iy];
 +    s[iy] = t;
 +    ++ix;
 +    --iy;
 +  }
 +}
 +
 +
 +/* low level addition, based on HAC pp.594, Algorithm 14.7 */
 +static int s_mp_add (mp_int * a, mp_int * b, mp_int * c)
 +{
 +  mp_int *x;
 +  int     olduse, res, min, max;
 +
 +  /* find sizes, we let |a| <= |b| which means we have to sort
 +   * them.  "x" will point to the input with the most digits
 +   */
 +  if (a->used > b->used) {
 +    min = b->used;
 +    max = a->used;
 +    x = a;
 +  } else {
 +    min = a->used;
 +    max = b->used;
 +    x = b;
 +  }
 +
 +  /* init result */
 +  if (c->alloc < max + 1) {
 +    if ((res = mp_grow (c, max + 1)) != MP_OKAY) {
 +      return res;
 +    }
 +  }
 +
 +  /* get old used digit count and set new one */
 +  olduse = c->used;
 +  c->used = max + 1;
 +
 +  {
 +    register mp_digit u, *tmpa, *tmpb, *tmpc;
 +    register int i;
 +
 +    /* alias for digit pointers */
 +
 +    /* first input */
 +    tmpa = a->dp;
 +
 +    /* second input */
 +    tmpb = b->dp;
 +
 +    /* destination */
 +    tmpc = c->dp;
 +
 +    /* zero the carry */
 +    u = 0;
 +    for (i = 0; i < min; i++) {
 +      /* Compute the sum at one digit, T[i] = A[i] + B[i] + U */
 +      *tmpc = *tmpa++ + *tmpb++ + u;
 +
 +      /* U = carry bit of T[i] */
 +      u = *tmpc >> ((mp_digit)DIGIT_BIT);
 +
 +      /* take away carry bit from T[i] */
 +      *tmpc++ &= MP_MASK;
 +    }
 +
 +    /* now copy higher words if any, that is in A+B 
 +     * if A or B has more digits add those in 
 +     */
 +    if (min != max) {
 +      for (; i < max; i++) {
 +        /* T[i] = X[i] + U */
 +        *tmpc = x->dp[i] + u;
 +
 +        /* U = carry bit of T[i] */
 +        u = *tmpc >> ((mp_digit)DIGIT_BIT);
 +
 +        /* take away carry bit from T[i] */
 +        *tmpc++ &= MP_MASK;
 +      }
 +    }
 +
 +    /* add carry */
 +    *tmpc++ = u;
 +
 +    /* clear digits above oldused */
 +    for (i = c->used; i < olduse; i++) {
 +      *tmpc++ = 0;
 +    }
 +  }
 +
 +  mp_clamp (c);
 +  return MP_OKAY;
 +}
 +
 +
 +/* low level subtraction (assumes |a| > |b|), HAC pp.595 Algorithm 14.9 */
 +static int s_mp_sub (mp_int * a, mp_int * b, mp_int * c)
 +{
 +  int     olduse, res, min, max;
 +
 +  /* find sizes */
 +  min = b->used;
 +  max = a->used;
 +
 +  /* init result */
 +  if (c->alloc < max) {
 +    if ((res = mp_grow (c, max)) != MP_OKAY) {
 +      return res;
 +    }
 +  }
 +  olduse = c->used;
 +  c->used = max;
 +
 +  {
 +    register mp_digit u, *tmpa, *tmpb, *tmpc;
 +    register int i;
 +
 +    /* alias for digit pointers */
 +    tmpa = a->dp;
 +    tmpb = b->dp;
 +    tmpc = c->dp;
 +
 +    /* set carry to zero */
 +    u = 0;
 +    for (i = 0; i < min; i++) {
 +      /* T[i] = A[i] - B[i] - U */
 +      *tmpc = *tmpa++ - *tmpb++ - u;
 +
 +      /* U = carry bit of T[i]
 +       * Note this saves performing an AND operation since
 +       * if a carry does occur it will propagate all the way to the
 +       * MSB.  As a result a single shift is enough to get the carry
 +       */
 +      u = *tmpc >> ((mp_digit)(CHAR_BIT * sizeof (mp_digit) - 1));
 +
 +      /* Clear carry from T[i] */
 +      *tmpc++ &= MP_MASK;
 +    }
 +
 +    /* now copy higher words if any, e.g. if A has more digits than B  */
 +    for (; i < max; i++) {
 +      /* T[i] = A[i] - U */
 +      *tmpc = *tmpa++ - u;
 +
 +      /* U = carry bit of T[i] */
 +      u = *tmpc >> ((mp_digit)(CHAR_BIT * sizeof (mp_digit) - 1));
 +
 +      /* Clear carry from T[i] */
 +      *tmpc++ &= MP_MASK;
 +    }
 +
 +    /* clear digits above used (since we may not have grown result above) */
 +    for (i = c->used; i < olduse; i++) {
 +      *tmpc++ = 0;
 +    }
 +  }
 +
 +  mp_clamp (c);
 +  return MP_OKAY;
 +}
 +
 +
 +/* init a new mp_int */
 +static int mp_init (mp_int * a)
 +{
 +  int i;
 +
 +  /* allocate memory required and clear it */
 +  a->dp = OPT_CAST(mp_digit) XMALLOC (sizeof (mp_digit) * MP_PREC);
 +  if (a->dp == NULL) {
 +    return MP_MEM;
 +  }
 +
 +  /* set the digits to zero */
 +  for (i = 0; i < MP_PREC; i++) {
 +      a->dp[i] = 0;
 +  }
 +
 +  /* set the used to zero, allocated digits to the default precision
 +   * and sign to positive */
 +  a->used  = 0;
 +  a->alloc = MP_PREC;
 +  a->sign  = MP_ZPOS;
 +
 +  return MP_OKAY;
 +}
 +
 +
 +/* clear one (frees)  */
 +static void mp_clear (mp_int * a)
 +{
 +  int i;
 +
 +  /* only do anything if a hasn't been freed previously */
 +  if (a->dp != NULL) {
 +    /* first zero the digits */
 +    for (i = 0; i < a->used; i++) {
 +        a->dp[i] = 0;
 +    }
 +
 +    /* free ram */
 +    XFREE(a->dp);
 +
 +    /* reset members to make debugging easier */
 +    a->dp    = NULL;
 +    a->alloc = a->used = 0;
 +    a->sign  = MP_ZPOS;
 +  }
 +}
 +
 +
 +/* high level addition (handles signs) */
 +static int mp_add (mp_int * a, mp_int * b, mp_int * c)
 +{
 +  int     sa, sb, res;
 +
 +  /* get sign of both inputs */
 +  sa = a->sign;
 +  sb = b->sign;
 +
 +  /* handle two cases, not four */
 +  if (sa == sb) {
 +    /* both positive or both negative */
 +    /* add their magnitudes, copy the sign */
 +    c->sign = sa;
 +    res = s_mp_add (a, b, c);
 +  } else {
 +    /* one positive, the other negative */
 +    /* subtract the one with the greater magnitude from */
 +    /* the one of the lesser magnitude.  The result gets */
 +    /* the sign of the one with the greater magnitude. */
 +    if (mp_cmp_mag (a, b) == MP_LT) {
 +      c->sign = sb;
 +      res = s_mp_sub (b, a, c);
 +    } else {
 +      c->sign = sa;
 +      res = s_mp_sub (a, b, c);
 +    }
 +  }
 +  return res;
 +}
 +
 +
 +/* high level subtraction (handles signs) */
 +static int mp_sub (mp_int * a, mp_int * b, mp_int * c)
 +{
 +  int     sa, sb, res;
 +
 +  sa = a->sign;
 +  sb = b->sign;
 +
 +  if (sa != sb) {
 +    /* subtract a negative from a positive, OR */
 +    /* subtract a positive from a negative. */
 +    /* In either case, ADD their magnitudes, */
 +    /* and use the sign of the first number. */
 +    c->sign = sa;
 +    res = s_mp_add (a, b, c);
 +  } else {
 +    /* subtract a positive from a positive, OR */
 +    /* subtract a negative from a negative. */
 +    /* First, take the difference between their */
 +    /* magnitudes, then... */
 +    if (mp_cmp_mag (a, b) != MP_LT) {
 +      /* Copy the sign from the first */
 +      c->sign = sa;
 +      /* The first has a larger or equal magnitude */
 +      res = s_mp_sub (a, b, c);
 +    } else {
 +      /* The result has the *opposite* sign from */
 +      /* the first number. */
 +      c->sign = (sa == MP_ZPOS) ? MP_NEG : MP_ZPOS;
 +      /* The second has a larger magnitude */
 +      res = s_mp_sub (b, a, c);
 +    }
 +  }
 +  return res;
 +}
 +
 +
 +/* high level multiplication (handles sign) */
 +static int mp_mul (mp_int * a, mp_int * b, mp_int * c)
 +{
 +  int     res, neg;
 +  neg = (a->sign == b->sign) ? MP_ZPOS : MP_NEG;
 +
 +  /* use Toom-Cook? */
 +#ifdef BN_MP_TOOM_MUL_C
 +  if (MIN (a->used, b->used) >= TOOM_MUL_CUTOFF) {
 +    res = mp_toom_mul(a, b, c);
 +  } else 
 +#endif
 +#ifdef BN_MP_KARATSUBA_MUL_C
 +  /* use Karatsuba? */
 +  if (MIN (a->used, b->used) >= KARATSUBA_MUL_CUTOFF) {
 +    res = mp_karatsuba_mul (a, b, c);
 +  } else 
 +#endif
 +  {
 +    /* can we use the fast multiplier?
 +     *
 +     * The fast multiplier can be used if the output will 
 +     * have less than MP_WARRAY digits and the number of 
 +     * digits won't affect carry propagation
 +     */
 +#ifdef BN_FAST_S_MP_MUL_DIGS_C
 +    int     digs = a->used + b->used + 1;
 +
 +    if ((digs < MP_WARRAY) &&
 +        MIN(a->used, b->used) <= 
 +        (1 << ((CHAR_BIT * sizeof (mp_word)) - (2 * DIGIT_BIT)))) {
 +      res = fast_s_mp_mul_digs (a, b, c, digs);
 +    } else 
 +#endif
 +#ifdef BN_S_MP_MUL_DIGS_C
 +      res = s_mp_mul (a, b, c); /* uses s_mp_mul_digs */
 +#else
 +#error mp_mul could fail
 +      res = MP_VAL;
 +#endif
 +
 +  }
 +  c->sign = (c->used > 0) ? neg : MP_ZPOS;
 +  return res;
 +}
 +
 +
 +/* d = a * b (mod c) */
 +static int mp_mulmod (mp_int * a, mp_int * b, mp_int * c, mp_int * d)
 +{
 +  int     res;
 +  mp_int  t;
 +
 +  if ((res = mp_init (&t)) != MP_OKAY) {
 +    return res;
 +  }
 +
 +  if ((res = mp_mul (a, b, &t)) != MP_OKAY) {
 +    mp_clear (&t);
 +    return res;
 +  }
 +  res = mp_mod (&t, c, d);
 +  mp_clear (&t);
 +  return res;
 +}
 +
 +
 +/* c = a mod b, 0 <= c < b */
 +static int mp_mod (mp_int * a, mp_int * b, mp_int * c)
 +{
 +  mp_int  t;
 +  int     res;
 +
 +  if ((res = mp_init (&t)) != MP_OKAY) {
 +    return res;
 +  }
 +
 +  if ((res = mp_div (a, b, NULL, &t)) != MP_OKAY) {
 +    mp_clear (&t);
 +    return res;
 +  }
 +
 +  if (t.sign != b->sign) {
 +    res = mp_add (b, &t, c);
 +  } else {
 +    res = MP_OKAY;
 +    mp_exch (&t, c);
 +  }
 +
 +  mp_clear (&t);
 +  return res;
 +}
 +
 +
 +/* this is a shell function that calls either the normal or Montgomery
 + * exptmod functions.  Originally the call to the montgomery code was
 + * embedded in the normal function but that wasted a lot of stack space
 + * for nothing (since 99% of the time the Montgomery code would be called)
 + */
 +static int mp_exptmod (mp_int * G, mp_int * X, mp_int * P, mp_int * Y)
 +{
 +  int dr;
 +
 +  /* modulus P must be positive */
 +  if (P->sign == MP_NEG) {
 +     return MP_VAL;
 +  }
 +
 +  /* if exponent X is negative we have to recurse */
 +  if (X->sign == MP_NEG) {
 +#ifdef LTM_NO_NEG_EXP
 +        return MP_VAL;
 +#else /* LTM_NO_NEG_EXP */
 +#ifdef BN_MP_INVMOD_C
 +     mp_int tmpG, tmpX;
 +     int err;
 +
 +     /* first compute 1/G mod P */
 +     if ((err = mp_init(&tmpG)) != MP_OKAY) {
 +        return err;
 +     }
 +     if ((err = mp_invmod(G, P, &tmpG)) != MP_OKAY) {
 +        mp_clear(&tmpG);
 +        return err;
 +     }
 +
 +     /* now get |X| */
 +     if ((err = mp_init(&tmpX)) != MP_OKAY) {
 +        mp_clear(&tmpG);
 +        return err;
 +     }
 +     if ((err = mp_abs(X, &tmpX)) != MP_OKAY) {
 +        mp_clear_multi(&tmpG, &tmpX, NULL);
 +        return err;
 +     }
 +
 +     /* and now compute (1/G)**|X| instead of G**X [X < 0] */
 +     err = mp_exptmod(&tmpG, &tmpX, P, Y);
 +     mp_clear_multi(&tmpG, &tmpX, NULL);
 +     return err;
 +#else 
 +#error mp_exptmod would always fail
 +     /* no invmod */
 +     return MP_VAL;
 +#endif
 +#endif /* LTM_NO_NEG_EXP */
 +  }
 +
 +/* modified diminished radix reduction */
 +#if defined(BN_MP_REDUCE_IS_2K_L_C) && defined(BN_MP_REDUCE_2K_L_C) && defined(BN_S_MP_EXPTMOD_C)
 +  if (mp_reduce_is_2k_l(P) == MP_YES) {
 +     return s_mp_exptmod(G, X, P, Y, 1);
 +  }
 +#endif
 +
 +#ifdef BN_MP_DR_IS_MODULUS_C
 +  /* is it a DR modulus? */
 +  dr = mp_dr_is_modulus(P);
 +#else
 +  /* default to no */
 +  dr = 0;
 +#endif
 +
 +#ifdef BN_MP_REDUCE_IS_2K_C
 +  /* if not, is it a unrestricted DR modulus? */
 +  if (dr == 0) {
 +     dr = mp_reduce_is_2k(P) << 1;
 +  }
 +#endif
 +    
 +  /* if the modulus is odd or dr != 0 use the montgomery method */
 +#ifdef BN_MP_EXPTMOD_FAST_C
 +  if (mp_isodd (P) == 1 || dr !=  0) {
 +    return mp_exptmod_fast (G, X, P, Y, dr);
 +  } else {
 +#endif
 +#ifdef BN_S_MP_EXPTMOD_C
 +    /* otherwise use the generic Barrett reduction technique */
 +    return s_mp_exptmod (G, X, P, Y, 0);
 +#else
 +#error mp_exptmod could fail
 +    /* no exptmod for evens */
 +    return MP_VAL;
 +#endif
 +#ifdef BN_MP_EXPTMOD_FAST_C
 +  }
 +#endif
 +  if (dr == 0) {
 +    /* avoid compiler warnings about possibly unused variable */
 +  }
 +}
 +
 +
 +/* compare two ints (signed)*/
 +static int mp_cmp (mp_int * a, mp_int * b)
 +{
 +  /* compare based on sign */
 +  if (a->sign != b->sign) {
 +     if (a->sign == MP_NEG) {
 +        return MP_LT;
 +     } else {
 +        return MP_GT;
 +     }
 +  }
 +  
 +  /* compare digits */
 +  if (a->sign == MP_NEG) {
 +     /* if negative compare opposite direction */
 +     return mp_cmp_mag(b, a);
 +  } else {
 +     return mp_cmp_mag(a, b);
 +  }
 +}
 +
 +
 +/* compare a digit */
 +static int mp_cmp_d(mp_int * a, mp_digit b)
 +{
 +  /* compare based on sign */
 +  if (a->sign == MP_NEG) {
 +    return MP_LT;
 +  }
 +
 +  /* compare based on magnitude */
 +  if (a->used > 1) {
 +    return MP_GT;
 +  }
 +
 +  /* compare the only digit of a to b */
 +  if (a->dp[0] > b) {
 +    return MP_GT;
 +  } else if (a->dp[0] < b) {
 +    return MP_LT;
 +  } else {
 +    return MP_EQ;
 +  }
 +}
 +
 +
 +#ifndef LTM_NO_NEG_EXP
 +/* hac 14.61, pp608 */
 +static int mp_invmod (mp_int * a, mp_int * b, mp_int * c)
 +{
 +  /* b cannot be negative */
 +  if (b->sign == MP_NEG || mp_iszero(b) == 1) {
 +    return MP_VAL;
 +  }
 +
 +#ifdef BN_FAST_MP_INVMOD_C
 +  /* if the modulus is odd we can use a faster routine instead */
 +  if (mp_isodd (b) == 1) {
 +    return fast_mp_invmod (a, b, c);
 +  }
 +#endif
 +
 +#ifdef BN_MP_INVMOD_SLOW_C
 +  return mp_invmod_slow(a, b, c);
 +#endif
 +
 +#ifndef BN_FAST_MP_INVMOD_C
 +#ifndef BN_MP_INVMOD_SLOW_C
 +#error mp_invmod would always fail
 +#endif
 +#endif
 +  return MP_VAL;
 +}
 +#endif /* LTM_NO_NEG_EXP */
 +
 +
 +/* get the size for an unsigned equivalent */
 +static int mp_unsigned_bin_size (mp_int * a)
 +{
 +  int     size = mp_count_bits (a);
 +  return (size / 8 + ((size & 7) != 0 ? 1 : 0));
 +}
 +
 +
 +#ifndef LTM_NO_NEG_EXP
 +/* hac 14.61, pp608 */
 +static int mp_invmod_slow (mp_int * a, mp_int * b, mp_int * c)
 +{
 +  mp_int  x, y, u, v, A, B, C, D;
 +  int     res;
 +
 +  /* b cannot be negative */
 +  if (b->sign == MP_NEG || mp_iszero(b) == 1) {
 +    return MP_VAL;
 +  }
 +
 +  /* init temps */
 +  if ((res = mp_init_multi(&x, &y, &u, &v, 
 +                           &A, &B, &C, &D, NULL)) != MP_OKAY) {
 +     return res;
 +  }
 +
 +  /* x = a, y = b */
 +  if ((res = mp_mod(a, b, &x)) != MP_OKAY) {
 +      goto LBL_ERR;
 +  }
 +  if ((res = mp_copy (b, &y)) != MP_OKAY) {
 +    goto LBL_ERR;
 +  }
 +
 +  /* 2. [modified] if x,y are both even then return an error! */
 +  if (mp_iseven (&x) == 1 && mp_iseven (&y) == 1) {
 +    res = MP_VAL;
 +    goto LBL_ERR;
 +  }
 +
 +  /* 3. u=x, v=y, A=1, B=0, C=0,D=1 */
 +  if ((res = mp_copy (&x, &u)) != MP_OKAY) {
 +    goto LBL_ERR;
 +  }
 +  if ((res = mp_copy (&y, &v)) != MP_OKAY) {
 +    goto LBL_ERR;
 +  }
 +  mp_set (&A, 1);
 +  mp_set (&D, 1);
 +
 +top:
 +  /* 4.  while u is even do */
 +  while (mp_iseven (&u) == 1) {
 +    /* 4.1 u = u/2 */
 +    if ((res = mp_div_2 (&u, &u)) != MP_OKAY) {
 +      goto LBL_ERR;
 +    }
 +    /* 4.2 if A or B is odd then */
 +    if (mp_isodd (&A) == 1 || mp_isodd (&B) == 1) {
 +      /* A = (A+y)/2, B = (B-x)/2 */
 +      if ((res = mp_add (&A, &y, &A)) != MP_OKAY) {
 +         goto LBL_ERR;
 +      }
 +      if ((res = mp_sub (&B, &x, &B)) != MP_OKAY) {
 +         goto LBL_ERR;
 +      }
 +    }
 +    /* A = A/2, B = B/2 */
 +    if ((res = mp_div_2 (&A, &A)) != MP_OKAY) {
 +      goto LBL_ERR;
 +    }
 +    if ((res = mp_div_2 (&B, &B)) != MP_OKAY) {
 +      goto LBL_ERR;
 +    }
 +  }
 +
 +  /* 5.  while v is even do */
 +  while (mp_iseven (&v) == 1) {
 +    /* 5.1 v = v/2 */
 +    if ((res = mp_div_2 (&v, &v)) != MP_OKAY) {
 +      goto LBL_ERR;
 +    }
 +    /* 5.2 if C or D is odd then */
 +    if (mp_isodd (&C) == 1 || mp_isodd (&D) == 1) {
 +      /* C = (C+y)/2, D = (D-x)/2 */
 +      if ((res = mp_add (&C, &y, &C)) != MP_OKAY) {
 +         goto LBL_ERR;
 +      }
 +      if ((res = mp_sub (&D, &x, &D)) != MP_OKAY) {
 +         goto LBL_ERR;
 +      }
 +    }
 +    /* C = C/2, D = D/2 */
 +    if ((res = mp_div_2 (&C, &C)) != MP_OKAY) {
 +      goto LBL_ERR;
 +    }
 +    if ((res = mp_div_2 (&D, &D)) != MP_OKAY) {
 +      goto LBL_ERR;
 +    }
 +  }
 +
 +  /* 6.  if u >= v then */
 +  if (mp_cmp (&u, &v) != MP_LT) {
 +    /* u = u - v, A = A - C, B = B - D */
 +    if ((res = mp_sub (&u, &v, &u)) != MP_OKAY) {
 +      goto LBL_ERR;
 +    }
 +
 +    if ((res = mp_sub (&A, &C, &A)) != MP_OKAY) {
 +      goto LBL_ERR;
 +    }
 +
 +    if ((res = mp_sub (&B, &D, &B)) != MP_OKAY) {
 +      goto LBL_ERR;
 +    }
 +  } else {
 +    /* v - v - u, C = C - A, D = D - B */
 +    if ((res = mp_sub (&v, &u, &v)) != MP_OKAY) {
 +      goto LBL_ERR;
 +    }
 +
 +    if ((res = mp_sub (&C, &A, &C)) != MP_OKAY) {
 +      goto LBL_ERR;
 +    }
 +
 +    if ((res = mp_sub (&D, &B, &D)) != MP_OKAY) {
 +      goto LBL_ERR;
 +    }
 +  }
 +
 +  /* if not zero goto step 4 */
 +  if (mp_iszero (&u) == 0)
 +    goto top;
 +
 +  /* now a = C, b = D, gcd == g*v */
 +
 +  /* if v != 1 then there is no inverse */
 +  if (mp_cmp_d (&v, 1) != MP_EQ) {
 +    res = MP_VAL;
 +    goto LBL_ERR;
 +  }
 +
 +  /* if its too low */
 +  while (mp_cmp_d(&C, 0) == MP_LT) {
 +      if ((res = mp_add(&C, b, &C)) != MP_OKAY) {
 +         goto LBL_ERR;
 +      }
 +  }
 +  
 +  /* too big */
 +  while (mp_cmp_mag(&C, b) != MP_LT) {
 +      if ((res = mp_sub(&C, b, &C)) != MP_OKAY) {
 +         goto LBL_ERR;
 +      }
 +  }
 +  
 +  /* C is now the inverse */
 +  mp_exch (&C, c);
 +  res = MP_OKAY;
 +LBL_ERR:mp_clear_multi (&x, &y, &u, &v, &A, &B, &C, &D, NULL);
 +  return res;
 +}
 +#endif /* LTM_NO_NEG_EXP */
 +
 +
 +/* compare maginitude of two ints (unsigned) */
 +static int mp_cmp_mag (mp_int * a, mp_int * b)
 +{
 +  int     n;
 +  mp_digit *tmpa, *tmpb;
 +
 +  /* compare based on # of non-zero digits */
 +  if (a->used > b->used) {
 +    return MP_GT;
 +  }
 +  
 +  if (a->used < b->used) {
 +    return MP_LT;
 +  }
 +
 +  /* alias for a */
 +  tmpa = a->dp + (a->used - 1);
 +
 +  /* alias for b */
 +  tmpb = b->dp + (a->used - 1);
 +
 +  /* compare based on digits  */
 +  for (n = 0; n < a->used; ++n, --tmpa, --tmpb) {
 +    if (*tmpa > *tmpb) {
 +      return MP_GT;
 +    }
 +
 +    if (*tmpa < *tmpb) {
 +      return MP_LT;
 +    }
 +  }
 +  return MP_EQ;
 +}
 +
 +
 +/* reads a unsigned char array, assumes the msb is stored first [big endian] */
 +static int mp_read_unsigned_bin (mp_int * a, const unsigned char *b, int c)
 +{
 +  int     res;
 +
 +  /* make sure there are at least two digits */
 +  if (a->alloc < 2) {
 +     if ((res = mp_grow(a, 2)) != MP_OKAY) {
 +        return res;
 +     }
 +  }
 +
 +  /* zero the int */
 +  mp_zero (a);
 +
 +  /* read the bytes in */
 +  while (c-- > 0) {
 +    if ((res = mp_mul_2d (a, 8, a)) != MP_OKAY) {
 +      return res;
 +    }
 +
 +#ifndef MP_8BIT
 +      a->dp[0] |= *b++;
 +      a->used += 1;
 +#else
 +      a->dp[0] = (*b & MP_MASK);
 +      a->dp[1] |= ((*b++ >> 7U) & 1);
 +      a->used += 2;
 +#endif
 +  }
 +  mp_clamp (a);
 +  return MP_OKAY;
 +}
 +
 +
 +/* store in unsigned [big endian] format */
 +static int mp_to_unsigned_bin (mp_int * a, unsigned char *b)
 +{
 +  int     x, res;
 +  mp_int  t;
 +
 +  if ((res = mp_init_copy (&t, a)) != MP_OKAY) {
 +    return res;
 +  }
 +
 +  x = 0;
 +  while (mp_iszero (&t) == 0) {
 +#ifndef MP_8BIT
 +      b[x++] = (unsigned char) (t.dp[0] & 255);
 +#else
 +      b[x++] = (unsigned char) (t.dp[0] | ((t.dp[1] & 0x01) << 7));
 +#endif
 +    if ((res = mp_div_2d (&t, 8, &t, NULL)) != MP_OKAY) {
 +      mp_clear (&t);
 +      return res;
 +    }
 +  }
 +  bn_reverse (b, x);
 +  mp_clear (&t);
 +  return MP_OKAY;
 +}
 +
 +
 +/* shift right by a certain bit count (store quotient in c, optional remainder in d) */
 +static int mp_div_2d (mp_int * a, int b, mp_int * c, mp_int * d)
 +{
 +  mp_digit D, r, rr;
 +  int     x, res;
 +  mp_int  t;
 +
 +
 +  /* if the shift count is <= 0 then we do no work */
 +  if (b <= 0) {
 +    res = mp_copy (a, c);
 +    if (d != NULL) {
 +      mp_zero (d);
 +    }
 +    return res;
 +  }
 +
 +  if ((res = mp_init (&t)) != MP_OKAY) {
 +    return res;
 +  }
 +
 +  /* get the remainder */
 +  if (d != NULL) {
 +    if ((res = mp_mod_2d (a, b, &t)) != MP_OKAY) {
 +      mp_clear (&t);
 +      return res;
 +    }
 +  }
 +
 +  /* copy */
 +  if ((res = mp_copy (a, c)) != MP_OKAY) {
 +    mp_clear (&t);
 +    return res;
 +  }
 +
 +  /* shift by as many digits in the bit count */
 +  if (b >= (int)DIGIT_BIT) {
 +    mp_rshd (c, b / DIGIT_BIT);
 +  }
 +
 +  /* shift any bit count < DIGIT_BIT */
 +  D = (mp_digit) (b % DIGIT_BIT);
 +  if (D != 0) {
 +    register mp_digit *tmpc, mask, shift;
 +
 +    /* mask */
 +    mask = (((mp_digit)1) << D) - 1;
 +
 +    /* shift for lsb */
 +    shift = DIGIT_BIT - D;
 +
 +    /* alias */
 +    tmpc = c->dp + (c->used - 1);
 +
 +    /* carry */
 +    r = 0;
 +    for (x = c->used - 1; x >= 0; x--) {
 +      /* get the lower  bits of this word in a temp */
 +      rr = *tmpc & mask;
 +
 +      /* shift the current word and mix in the carry bits from the previous word */
 +      *tmpc = (*tmpc >> D) | (r << shift);
 +      --tmpc;
 +
 +      /* set the carry to the carry bits of the current word found above */
 +      r = rr;
 +    }
 +  }
 +  mp_clamp (c);
 +  if (d != NULL) {
 +    mp_exch (&t, d);
 +  }
 +  mp_clear (&t);
 +  return MP_OKAY;
 +}
 +
 +
 +static int mp_init_copy (mp_int * a, mp_int * b)
 +{
 +  int     res;
 +
 +  if ((res = mp_init (a)) != MP_OKAY) {
 +    return res;
 +  }
 +  return mp_copy (b, a);
 +}
 +
 +
 +/* set to zero */
 +static void mp_zero (mp_int * a)
 +{
 +  int       n;
 +  mp_digit *tmp;
 +
 +  a->sign = MP_ZPOS;
 +  a->used = 0;
 +
 +  tmp = a->dp;
 +  for (n = 0; n < a->alloc; n++) {
 +     *tmp++ = 0;
 +  }
 +}
 +
 +
 +/* copy, b = a */
 +static int mp_copy (mp_int * a, mp_int * b)
 +{
 +  int     res, n;
 +
 +  /* if dst == src do nothing */
 +  if (a == b) {
 +    return MP_OKAY;
 +  }
 +
 +  /* grow dest */
 +  if (b->alloc < a->used) {
 +     if ((res = mp_grow (b, a->used)) != MP_OKAY) {
 +        return res;
 +     }
 +  }
 +
 +  /* zero b and copy the parameters over */
 +  {
 +    register mp_digit *tmpa, *tmpb;
 +
 +    /* pointer aliases */
 +
 +    /* source */
 +    tmpa = a->dp;
 +
 +    /* destination */
 +    tmpb = b->dp;
 +
 +    /* copy all the digits */
 +    for (n = 0; n < a->used; n++) {
 +      *tmpb++ = *tmpa++;
 +    }
 +
 +    /* clear high digits */
 +    for (; n < b->used; n++) {
 +      *tmpb++ = 0;
 +    }
 +  }
 +
 +  /* copy used count and sign */
 +  b->used = a->used;
 +  b->sign = a->sign;
 +  return MP_OKAY;
 +}
 +
 +
 +/* shift right a certain amount of digits */
 +static void mp_rshd (mp_int * a, int b)
 +{
 +  int     x;
 +
 +  /* if b <= 0 then ignore it */
 +  if (b <= 0) {
 +    return;
 +  }
 +
 +  /* if b > used then simply zero it and return */
 +  if (a->used <= b) {
 +    mp_zero (a);
 +    return;
 +  }
 +
 +  {
 +    register mp_digit *bottom, *top;
 +
 +    /* shift the digits down */
 +
 +    /* bottom */
 +    bottom = a->dp;
 +
 +    /* top [offset into digits] */
 +    top = a->dp + b;
 +
 +    /* this is implemented as a sliding window where 
 +     * the window is b-digits long and digits from 
 +     * the top of the window are copied to the bottom
 +     *
 +     * e.g.
 +
 +     b-2 | b-1 | b0 | b1 | b2 | ... | bb |   ---->
 +                 /\                   |      ---->
 +                  \-------------------/      ---->
 +     */
 +    for (x = 0; x < (a->used - b); x++) {
 +      *bottom++ = *top++;
 +    }
 +
 +    /* zero the top digits */
 +    for (; x < a->used; x++) {
 +      *bottom++ = 0;
 +    }
 +  }
 +  
 +  /* remove excess digits */
 +  a->used -= b;
 +}
 +
 +
 +/* swap the elements of two integers, for cases where you can't simply swap the 
 + * mp_int pointers around
 + */
 +static void mp_exch (mp_int * a, mp_int * b)
 +{
 +  mp_int  t;
 +
 +  t  = *a;
 +  *a = *b;
 +  *b = t;
 +}
 +
 +
 +/* trim unused digits 
 + *
 + * This is used to ensure that leading zero digits are
 + * trimed and the leading "used" digit will be non-zero
 + * Typically very fast.  Also fixes the sign if there
 + * are no more leading digits
 + */
 +static void mp_clamp (mp_int * a)
 +{
 +  /* decrease used while the most significant digit is
 +   * zero.
 +   */
 +  while (a->used > 0 && a->dp[a->used - 1] == 0) {
 +    --(a->used);
 +  }
 +
 +  /* reset the sign flag if used == 0 */
 +  if (a->used == 0) {
 +    a->sign = MP_ZPOS;
 +  }
 +}
 +
 +
 +/* grow as required */
 +static int mp_grow (mp_int * a, int size)
 +{
 +  int     i;
 +  mp_digit *tmp;
 +
 +  /* if the alloc size is smaller alloc more ram */
 +  if (a->alloc < size) {
 +    /* ensure there are always at least MP_PREC digits extra on top */
 +    size += (MP_PREC * 2) - (size % MP_PREC);
 +
 +    /* reallocate the array a->dp
 +     *
 +     * We store the return in a temporary variable
 +     * in case the operation failed we don't want
 +     * to overwrite the dp member of a.
 +     */
 +    tmp = OPT_CAST(mp_digit) XREALLOC (a->dp, sizeof (mp_digit) * size);
 +    if (tmp == NULL) {
 +      /* reallocation failed but "a" is still valid [can be freed] */
 +      return MP_MEM;
 +    }
 +
 +    /* reallocation succeeded so set a->dp */
 +    a->dp = tmp;
 +
 +    /* zero excess digits */
 +    i        = a->alloc;
 +    a->alloc = size;
 +    for (; i < a->alloc; i++) {
 +      a->dp[i] = 0;
 +    }
 +  }
 +  return MP_OKAY;
 +}
 +
 +
 +#ifdef BN_MP_ABS_C
 +/* b = |a| 
 + *
 + * Simple function copies the input and fixes the sign to positive
 + */
 +static int mp_abs (mp_int * a, mp_int * b)
 +{
 +  int     res;
 +
 +  /* copy a to b */
 +  if (a != b) {
 +     if ((res = mp_copy (a, b)) != MP_OKAY) {
 +       return res;
 +     }
 +  }
 +
 +  /* force the sign of b to positive */
 +  b->sign = MP_ZPOS;
 +
 +  return MP_OKAY;
 +}
 +#endif
 +
 +
 +/* set to a digit */
 +static void mp_set (mp_int * a, mp_digit b)
 +{
 +  mp_zero (a);
 +  a->dp[0] = b & MP_MASK;
 +  a->used  = (a->dp[0] != 0) ? 1 : 0;
 +}
 +
 +
 +#ifndef LTM_NO_NEG_EXP
 +/* b = a/2 */
 +static int mp_div_2(mp_int * a, mp_int * b)
 +{
 +  int     x, res, oldused;
 +
 +  /* copy */
 +  if (b->alloc < a->used) {
 +    if ((res = mp_grow (b, a->used)) != MP_OKAY) {
 +      return res;
 +    }
 +  }
 +
 +  oldused = b->used;
 +  b->used = a->used;
 +  {
 +    register mp_digit r, rr, *tmpa, *tmpb;
 +
 +    /* source alias */
 +    tmpa = a->dp + b->used - 1;
 +
 +    /* dest alias */
 +    tmpb = b->dp + b->used - 1;
 +
 +    /* carry */
 +    r = 0;
 +    for (x = b->used - 1; x >= 0; x--) {
 +      /* get the carry for the next iteration */
 +      rr = *tmpa & 1;
 +
 +      /* shift the current digit, add in carry and store */
 +      *tmpb-- = (*tmpa-- >> 1) | (r << (DIGIT_BIT - 1));
 +
 +      /* forward carry to next iteration */
 +      r = rr;
 +    }
 +
 +    /* zero excess digits */
 +    tmpb = b->dp + b->used;
 +    for (x = b->used; x < oldused; x++) {
 +      *tmpb++ = 0;
 +    }
 +  }
 +  b->sign = a->sign;
 +  mp_clamp (b);
 +  return MP_OKAY;
 +}
 +#endif /* LTM_NO_NEG_EXP */
 +
 +
 +/* shift left by a certain bit count */
 +static int mp_mul_2d (mp_int * a, int b, mp_int * c)
 +{
 +  mp_digit d;
 +  int      res;
 +
 +  /* copy */
 +  if (a != c) {
 +     if ((res = mp_copy (a, c)) != MP_OKAY) {
 +       return res;
 +     }
 +  }
 +
 +  if (c->alloc < (int)(c->used + b/DIGIT_BIT + 1)) {
 +     if ((res = mp_grow (c, c->used + b / DIGIT_BIT + 1)) != MP_OKAY) {
 +       return res;
 +     }
 +  }
 +
 +  /* shift by as many digits in the bit count */
 +  if (b >= (int)DIGIT_BIT) {
 +    if ((res = mp_lshd (c, b / DIGIT_BIT)) != MP_OKAY) {
 +      return res;
 +    }
 +  }
 +
 +  /* shift any bit count < DIGIT_BIT */
 +  d = (mp_digit) (b % DIGIT_BIT);
 +  if (d != 0) {
 +    register mp_digit *tmpc, shift, mask, r, rr;
 +    register int x;
 +
 +    /* bitmask for carries */
 +    mask = (((mp_digit)1) << d) - 1;
 +
 +    /* shift for msbs */
 +    shift = DIGIT_BIT - d;
 +
 +    /* alias */
 +    tmpc = c->dp;
 +
 +    /* carry */
 +    r    = 0;
 +    for (x = 0; x < c->used; x++) {
 +      /* get the higher bits of the current word */
 +      rr = (*tmpc >> shift) & mask;
 +
 +      /* shift the current word and OR in the carry */
 +      *tmpc = ((*tmpc << d) | r) & MP_MASK;
 +      ++tmpc;
 +
 +      /* set the carry to the carry bits of the current word */
 +      r = rr;
 +    }
 +    
 +    /* set final carry */
 +    if (r != 0) {
 +       c->dp[(c->used)++] = r;
 +    }
 +  }
 +  mp_clamp (c);
 +  return MP_OKAY;
 +}
 +
 +
 +#ifdef BN_MP_INIT_MULTI_C
 +static int mp_init_multi(mp_int *mp, ...) 
 +{
 +    mp_err res = MP_OKAY;      /* Assume ok until proven otherwise */
 +    int n = 0;                 /* Number of ok inits */
 +    mp_int* cur_arg = mp;
 +    va_list args;
 +
 +    va_start(args, mp);        /* init args to next argument from caller */
 +    while (cur_arg != NULL) {
 +        if (mp_init(cur_arg) != MP_OKAY) {
 +            /* Oops - error! Back-track and mp_clear what we already
 +               succeeded in init-ing, then return error.
 +            */
 +            va_list clean_args;
 +            
 +            /* end the current list */
 +            va_end(args);
 +            
 +            /* now start cleaning up */            
 +            cur_arg = mp;
 +            va_start(clean_args, mp);
 +            while (n--) {
 +                mp_clear(cur_arg);
 +                cur_arg = va_arg(clean_args, mp_int*);
 +            }
 +            va_end(clean_args);
-   if ((res = mp_init_multi(&ta, &tb, &tq, &q, NULL) != MP_OKAY)) {
++            return MP_MEM;
 +        }
 +        n++;
 +        cur_arg = va_arg(args, mp_int*);
 +    }
 +    va_end(args);
 +    return res;                /* Assumed ok, if error flagged above. */
 +}
 +#endif
 +
 +
 +#ifdef BN_MP_CLEAR_MULTI_C
 +static void mp_clear_multi(mp_int *mp, ...) 
 +{
 +    mp_int* next_mp = mp;
 +    va_list args;
 +    va_start(args, mp);
 +    while (next_mp != NULL) {
 +        mp_clear(next_mp);
 +        next_mp = va_arg(args, mp_int*);
 +    }
 +    va_end(args);
 +}
 +#endif
 +
 +
 +/* shift left a certain amount of digits */
 +static int mp_lshd (mp_int * a, int b)
 +{
 +  int     x, res;
 +
 +  /* if its less than zero return */
 +  if (b <= 0) {
 +    return MP_OKAY;
 +  }
 +
 +  /* grow to fit the new digits */
 +  if (a->alloc < a->used + b) {
 +     if ((res = mp_grow (a, a->used + b)) != MP_OKAY) {
 +       return res;
 +     }
 +  }
 +
 +  {
 +    register mp_digit *top, *bottom;
 +
 +    /* increment the used by the shift amount then copy upwards */
 +    a->used += b;
 +
 +    /* top */
 +    top = a->dp + a->used - 1;
 +
 +    /* base */
 +    bottom = a->dp + a->used - 1 - b;
 +
 +    /* much like mp_rshd this is implemented using a sliding window
 +     * except the window goes the otherway around.  Copying from
 +     * the bottom to the top.  see bn_mp_rshd.c for more info.
 +     */
 +    for (x = a->used - 1; x >= b; x--) {
 +      *top-- = *bottom--;
 +    }
 +
 +    /* zero the lower digits */
 +    top = a->dp;
 +    for (x = 0; x < b; x++) {
 +      *top++ = 0;
 +    }
 +  }
 +  return MP_OKAY;
 +}
 +
 +
 +/* returns the number of bits in an int */
 +static int mp_count_bits (mp_int * a)
 +{
 +  int     r;
 +  mp_digit q;
 +
 +  /* shortcut */
 +  if (a->used == 0) {
 +    return 0;
 +  }
 +
 +  /* get number of digits and add that */
 +  r = (a->used - 1) * DIGIT_BIT;
 +  
 +  /* take the last digit and count the bits in it */
 +  q = a->dp[a->used - 1];
 +  while (q > ((mp_digit) 0)) {
 +    ++r;
 +    q >>= ((mp_digit) 1);
 +  }
 +  return r;
 +}
 +
 +
 +/* calc a value mod 2**b */
 +static int mp_mod_2d (mp_int * a, int b, mp_int * c)
 +{
 +  int     x, res;
 +
 +  /* if b is <= 0 then zero the int */
 +  if (b <= 0) {
 +    mp_zero (c);
 +    return MP_OKAY;
 +  }
 +
 +  /* if the modulus is larger than the value than return */
 +  if (b >= (int) (a->used * DIGIT_BIT)) {
 +    res = mp_copy (a, c);
 +    return res;
 +  }
 +
 +  /* copy */
 +  if ((res = mp_copy (a, c)) != MP_OKAY) {
 +    return res;
 +  }
 +
 +  /* zero digits above the last digit of the modulus */
 +  for (x = (b / DIGIT_BIT) + ((b % DIGIT_BIT) == 0 ? 0 : 1); x < c->used; x++) {
 +    c->dp[x] = 0;
 +  }
 +  /* clear the digit that is not completely outside/inside the modulus */
 +  c->dp[b / DIGIT_BIT] &=
 +    (mp_digit) ((((mp_digit) 1) << (((mp_digit) b) % DIGIT_BIT)) - ((mp_digit) 1));
 +  mp_clamp (c);
 +  return MP_OKAY;
 +}
 +
 +
 +#ifdef BN_MP_DIV_SMALL
 +
 +/* slower bit-bang division... also smaller */
 +static int mp_div(mp_int * a, mp_int * b, mp_int * c, mp_int * d)
 +{
 +   mp_int ta, tb, tq, q;
 +   int    res, n, n2;
 +
 +  /* is divisor zero ? */
 +  if (mp_iszero (b) == 1) {
 +    return MP_VAL;
 +  }
 +
 +  /* if a < b then q=0, r = a */
 +  if (mp_cmp_mag (a, b) == MP_LT) {
 +    if (d != NULL) {
 +      res = mp_copy (a, d);
 +    } else {
 +      res = MP_OKAY;
 +    }
 +    if (c != NULL) {
 +      mp_zero (c);
 +    }
 +    return res;
 +  }
 +      
 +  /* init our temps */
++  if ((res = mp_init_multi(&ta, &tb, &tq, &q, NULL)) != MP_OKAY) {
 +     return res;
 +  }
 +
 +
 +  mp_set(&tq, 1);
 +  n = mp_count_bits(a) - mp_count_bits(b);
 +  if (((res = mp_abs(a, &ta)) != MP_OKAY) ||
 +      ((res = mp_abs(b, &tb)) != MP_OKAY) || 
 +      ((res = mp_mul_2d(&tb, n, &tb)) != MP_OKAY) ||
 +      ((res = mp_mul_2d(&tq, n, &tq)) != MP_OKAY)) {
 +      goto LBL_ERR;
 +  }
 +
 +  while (n-- >= 0) {
 +     if (mp_cmp(&tb, &ta) != MP_GT) {
 +        if (((res = mp_sub(&ta, &tb, &ta)) != MP_OKAY) ||
 +            ((res = mp_add(&q, &tq, &q)) != MP_OKAY)) {
 +           goto LBL_ERR;
 +        }
 +     }
 +     if (((res = mp_div_2d(&tb, 1, &tb, NULL)) != MP_OKAY) ||
 +         ((res = mp_div_2d(&tq, 1, &tq, NULL)) != MP_OKAY)) {
 +           goto LBL_ERR;
 +     }
 +  }
 +
 +  /* now q == quotient and ta == remainder */
 +  n  = a->sign;
 +  n2 = (a->sign == b->sign ? MP_ZPOS : MP_NEG);
 +  if (c != NULL) {
 +     mp_exch(c, &q);
 +     c->sign  = (mp_iszero(c) == MP_YES) ? MP_ZPOS : n2;
 +  }
 +  if (d != NULL) {
 +     mp_exch(d, &ta);
 +     d->sign = (mp_iszero(d) == MP_YES) ? MP_ZPOS : n;
 +  }
 +LBL_ERR:
 +   mp_clear_multi(&ta, &tb, &tq, &q, NULL);
 +   return res;
 +}
 +
 +#else
 +
 +/* integer signed division. 
 + * c*b + d == a [e.g. a/b, c=quotient, d=remainder]
 + * HAC pp.598 Algorithm 14.20
 + *
 + * Note that the description in HAC is horribly 
 + * incomplete.  For example, it doesn't consider 
 + * the case where digits are removed from 'x' in 
 + * the inner loop.  It also doesn't consider the 
 + * case that y has fewer than three digits, etc..
 + *
 + * The overall algorithm is as described as 
 + * 14.20 from HAC but fixed to treat these cases.
 +*/
 +static int mp_div (mp_int * a, mp_int * b, mp_int * c, mp_int * d)
 +{
 +  mp_int  q, x, y, t1, t2;
 +  int     res, n, t, i, norm, neg;
 +
 +  /* is divisor zero ? */
 +  if (mp_iszero (b) == 1) {
 +    return MP_VAL;
 +  }
 +
 +  /* if a < b then q=0, r = a */
 +  if (mp_cmp_mag (a, b) == MP_LT) {
 +    if (d != NULL) {
 +      res = mp_copy (a, d);
 +    } else {
 +      res = MP_OKAY;
 +    }
 +    if (c != NULL) {
 +      mp_zero (c);
 +    }
 +    return res;
 +  }
 +
 +  if ((res = mp_init_size (&q, a->used + 2)) != MP_OKAY) {
 +    return res;
 +  }
 +  q.used = a->used + 2;
 +
 +  if ((res = mp_init (&t1)) != MP_OKAY) {
 +    goto LBL_Q;
 +  }
 +
 +  if ((res = mp_init (&t2)) != MP_OKAY) {
 +    goto LBL_T1;
 +  }
 +
 +  if ((res = mp_init_copy (&x, a)) != MP_OKAY) {
 +    goto LBL_T2;
 +  }
 +
 +  if ((res = mp_init_copy (&y, b)) != MP_OKAY) {
 +    goto LBL_X;
 +  }
 +
 +  /* fix the sign */
 +  neg = (a->sign == b->sign) ? MP_ZPOS : MP_NEG;
 +  x.sign = y.sign = MP_ZPOS;
 +
 +  /* normalize both x and y, ensure that y >= b/2, [b == 2**DIGIT_BIT] */
 +  norm = mp_count_bits(&y) % DIGIT_BIT;
 +  if (norm < (int)(DIGIT_BIT-1)) {
 +     norm = (DIGIT_BIT-1) - norm;
 +     if ((res = mp_mul_2d (&x, norm, &x)) != MP_OKAY) {
 +       goto LBL_Y;
 +     }
 +     if ((res = mp_mul_2d (&y, norm, &y)) != MP_OKAY) {
 +       goto LBL_Y;
 +     }
 +  } else {
 +     norm = 0;
 +  }
 +
 +  /* note hac does 0 based, so if used==5 then its 0,1,2,3,4, e.g. use 4 */
 +  n = x.used - 1;
 +  t = y.used - 1;
 +
 +  /* while (x >= y*b**n-t) do { q[n-t] += 1; x -= y*b**{n-t} } */
 +  if ((res = mp_lshd (&y, n - t)) != MP_OKAY) { /* y = y*b**{n-t} */
 +    goto LBL_Y;
 +  }
 +
 +  while (mp_cmp (&x, &y) != MP_LT) {
 +    ++(q.dp[n - t]);
 +    if ((res = mp_sub (&x, &y, &x)) != MP_OKAY) {
 +      goto LBL_Y;
 +    }
 +  }
 +
 +  /* reset y by shifting it back down */
 +  mp_rshd (&y, n - t);
 +
 +  /* step 3. for i from n down to (t + 1) */
 +  for (i = n; i >= (t + 1); i--) {
 +    if (i > x.used) {
 +      continue;
 +    }
 +
 +    /* step 3.1 if xi == yt then set q{i-t-1} to b-1, 
 +     * otherwise set q{i-t-1} to (xi*b + x{i-1})/yt */
 +    if (x.dp[i] == y.dp[t]) {
 +      q.dp[i - t - 1] = ((((mp_digit)1) << DIGIT_BIT) - 1);
 +    } else {
 +      mp_word tmp;
 +      tmp = ((mp_word) x.dp[i]) << ((mp_word) DIGIT_BIT);
 +      tmp |= ((mp_word) x.dp[i - 1]);
 +      tmp /= ((mp_word) y.dp[t]);
 +      if (tmp > (mp_word) MP_MASK)
 +        tmp = MP_MASK;
 +      q.dp[i - t - 1] = (mp_digit) (tmp & (mp_word) (MP_MASK));
 +    }
 +
 +    /* while (q{i-t-1} * (yt * b + y{t-1})) > 
 +             xi * b**2 + xi-1 * b + xi-2 
 +     
 +       do q{i-t-1} -= 1; 
 +    */
 +    q.dp[i - t - 1] = (q.dp[i - t - 1] + 1) & MP_MASK;
 +    do {
 +      q.dp[i - t - 1] = (q.dp[i - t - 1] - 1) & MP_MASK;
 +
 +      /* find left hand */
 +      mp_zero (&t1);
 +      t1.dp[0] = (t - 1 < 0) ? 0 : y.dp[t - 1];
 +      t1.dp[1] = y.dp[t];
 +      t1.used = 2;
 +      if ((res = mp_mul_d (&t1, q.dp[i - t - 1], &t1)) != MP_OKAY) {
 +        goto LBL_Y;
 +      }
 +
 +      /* find right hand */
 +      t2.dp[0] = (i - 2 < 0) ? 0 : x.dp[i - 2];
 +      t2.dp[1] = (i - 1 < 0) ? 0 : x.dp[i - 1];
 +      t2.dp[2] = x.dp[i];
 +      t2.used = 3;
 +    } while (mp_cmp_mag(&t1, &t2) == MP_GT);
 +
 +    /* step 3.3 x = x - q{i-t-1} * y * b**{i-t-1} */
 +    if ((res = mp_mul_d (&y, q.dp[i - t - 1], &t1)) != MP_OKAY) {
 +      goto LBL_Y;
 +    }
 +
 +    if ((res = mp_lshd (&t1, i - t - 1)) != MP_OKAY) {
 +      goto LBL_Y;
 +    }
 +
 +    if ((res = mp_sub (&x, &t1, &x)) != MP_OKAY) {
 +      goto LBL_Y;
 +    }
 +
 +    /* if x < 0 then { x = x + y*b**{i-t-1}; q{i-t-1} -= 1; } */
 +    if (x.sign == MP_NEG) {
 +      if ((res = mp_copy (&y, &t1)) != MP_OKAY) {
 +        goto LBL_Y;
 +      }
 +      if ((res = mp_lshd (&t1, i - t - 1)) != MP_OKAY) {
 +        goto LBL_Y;
 +      }
 +      if ((res = mp_add (&x, &t1, &x)) != MP_OKAY) {
 +        goto LBL_Y;
 +      }
 +
 +      q.dp[i - t - 1] = (q.dp[i - t - 1] - 1UL) & MP_MASK;
 +    }
 +  }
 +
 +  /* now q is the quotient and x is the remainder 
 +   * [which we have to normalize] 
 +   */
 +  
 +  /* get sign before writing to c */
 +  x.sign = x.used == 0 ? MP_ZPOS : a->sign;
 +
 +  if (c != NULL) {
 +    mp_clamp (&q);
 +    mp_exch (&q, c);
 +    c->sign = neg;
 +  }
 +
 +  if (d != NULL) {
 +    mp_div_2d (&x, norm, &x, NULL);
 +    mp_exch (&x, d);
 +  }
 +
 +  res = MP_OKAY;
 +
 +LBL_Y:mp_clear (&y);
 +LBL_X:mp_clear (&x);
 +LBL_T2:mp_clear (&t2);
 +LBL_T1:mp_clear (&t1);
 +LBL_Q:mp_clear (&q);
 +  return res;
 +}
 +
 +#endif
 +
 +
 +#ifdef MP_LOW_MEM
 +   #define TAB_SIZE 32
 +#else
 +   #define TAB_SIZE 256
 +#endif
 +
 +static int s_mp_exptmod (mp_int * G, mp_int * X, mp_int * P, mp_int * Y, int redmode)
 +{
 +  mp_int  M[TAB_SIZE], res, mu;
 +  mp_digit buf;
 +  int     err, bitbuf, bitcpy, bitcnt, mode, digidx, x, y, winsize;
 +  int (*redux)(mp_int*,mp_int*,mp_int*);
 +
 +  /* find window size */
 +  x = mp_count_bits (X);
 +  if (x <= 7) {
 +    winsize = 2;
 +  } else if (x <= 36) {
 +    winsize = 3;
 +  } else if (x <= 140) {
 +    winsize = 4;
 +  } else if (x <= 450) {
 +    winsize = 5;
 +  } else if (x <= 1303) {
 +    winsize = 6;
 +  } else if (x <= 3529) {
 +    winsize = 7;
 +  } else {
 +    winsize = 8;
 +  }
 +
 +#ifdef MP_LOW_MEM
 +    if (winsize > 5) {
 +       winsize = 5;
 +    }
 +#endif
 +
 +  /* init M array */
 +  /* init first cell */
 +  if ((err = mp_init(&M[1])) != MP_OKAY) {
 +     return err; 
 +  }
 +
 +  /* now init the second half of the array */
 +  for (x = 1<<(winsize-1); x < (1 << winsize); x++) {
 +    if ((err = mp_init(&M[x])) != MP_OKAY) {
 +      for (y = 1<<(winsize-1); y < x; y++) {
 +        mp_clear (&M[y]);
 +      }
 +      mp_clear(&M[1]);
 +      return err;
 +    }
 +  }
 +
 +  /* create mu, used for Barrett reduction */
 +  if ((err = mp_init (&mu)) != MP_OKAY) {
 +    goto LBL_M;
 +  }
 +  
 +  if (redmode == 0) {
 +     if ((err = mp_reduce_setup (&mu, P)) != MP_OKAY) {
 +        goto LBL_MU;
 +     }
 +     redux = mp_reduce;
 +  } else {
 +     if ((err = mp_reduce_2k_setup_l (P, &mu)) != MP_OKAY) {
 +        goto LBL_MU;
 +     }
 +     redux = mp_reduce_2k_l;
 +  }    
 +
 +  /* create M table
 +   *
 +   * The M table contains powers of the base, 
 +   * e.g. M[x] = G**x mod P
 +   *
 +   * The first half of the table is not 
 +   * computed though accept for M[0] and M[1]
 +   */
 +  if ((err = mp_mod (G, P, &M[1])) != MP_OKAY) {
 +    goto LBL_MU;
 +  }
 +
 +  /* compute the value at M[1<<(winsize-1)] by squaring 
 +   * M[1] (winsize-1) times 
 +   */
 +  if ((err = mp_copy (&M[1], &M[1 << (winsize - 1)])) != MP_OKAY) {
 +    goto LBL_MU;
 +  }
 +
 +  for (x = 0; x < (winsize - 1); x++) {
 +    /* square it */
 +    if ((err = mp_sqr (&M[1 << (winsize - 1)], 
 +                       &M[1 << (winsize - 1)])) != MP_OKAY) {
 +      goto LBL_MU;
 +    }
 +
 +    /* reduce modulo P */
 +    if ((err = redux (&M[1 << (winsize - 1)], P, &mu)) != MP_OKAY) {
 +      goto LBL_MU;
 +    }
 +  }
 +
 +  /* create upper table, that is M[x] = M[x-1] * M[1] (mod P)
 +   * for x = (2**(winsize - 1) + 1) to (2**winsize - 1)
 +   */
 +  for (x = (1 << (winsize - 1)) + 1; x < (1 << winsize); x++) {
 +    if ((err = mp_mul (&M[x - 1], &M[1], &M[x])) != MP_OKAY) {
 +      goto LBL_MU;
 +    }
 +    if ((err = redux (&M[x], P, &mu)) != MP_OKAY) {
 +      goto LBL_MU;
 +    }
 +  }
 +
 +  /* setup result */
 +  if ((err = mp_init (&res)) != MP_OKAY) {
 +    goto LBL_MU;
 +  }
 +  mp_set (&res, 1);
 +
 +  /* set initial mode and bit cnt */
 +  mode   = 0;
 +  bitcnt = 1;
 +  buf    = 0;
 +  digidx = X->used - 1;
 +  bitcpy = 0;
 +  bitbuf = 0;
 +
 +  for (;;) {
 +    /* grab next digit as required */
 +    if (--bitcnt == 0) {
 +      /* if digidx == -1 we are out of digits */
 +      if (digidx == -1) {
 +        break;
 +      }
 +      /* read next digit and reset the bitcnt */
 +      buf    = X->dp[digidx--];
 +      bitcnt = (int) DIGIT_BIT;
 +    }
 +
 +    /* grab the next msb from the exponent */
 +    y     = (buf >> (mp_digit)(DIGIT_BIT - 1)) & 1;
 +    buf <<= (mp_digit)1;
 +
 +    /* if the bit is zero and mode == 0 then we ignore it
 +     * These represent the leading zero bits before the first 1 bit
 +     * in the exponent.  Technically this opt is not required but it
 +     * does lower the # of trivial squaring/reductions used
 +     */
 +    if (mode == 0 && y == 0) {
 +      continue;
 +    }
 +
 +    /* if the bit is zero and mode == 1 then we square */
 +    if (mode == 1 && y == 0) {
 +      if ((err = mp_sqr (&res, &res)) != MP_OKAY) {
 +        goto LBL_RES;
 +      }
 +      if ((err = redux (&res, P, &mu)) != MP_OKAY) {
 +        goto LBL_RES;
 +      }
 +      continue;
 +    }
 +
 +    /* else we add it to the window */
 +    bitbuf |= (y << (winsize - ++bitcpy));
 +    mode    = 2;
 +
 +    if (bitcpy == winsize) {
 +      /* ok window is filled so square as required and multiply  */
 +      /* square first */
 +      for (x = 0; x < winsize; x++) {
 +        if ((err = mp_sqr (&res, &res)) != MP_OKAY) {
 +          goto LBL_RES;
 +        }
 +        if ((err = redux (&res, P, &mu)) != MP_OKAY) {
 +          goto LBL_RES;
 +        }
 +      }
 +
 +      /* then multiply */
 +      if ((err = mp_mul (&res, &M[bitbuf], &res)) != MP_OKAY) {
 +        goto LBL_RES;
 +      }
 +      if ((err = redux (&res, P, &mu)) != MP_OKAY) {
 +        goto LBL_RES;
 +      }
 +
 +      /* empty window and reset */
 +      bitcpy = 0;
 +      bitbuf = 0;
 +      mode   = 1;
 +    }
 +  }
 +
 +  /* if bits remain then square/multiply */
 +  if (mode == 2 && bitcpy > 0) {
 +    /* square then multiply if the bit is set */
 +    for (x = 0; x < bitcpy; x++) {
 +      if ((err = mp_sqr (&res, &res)) != MP_OKAY) {
 +        goto LBL_RES;
 +      }
 +      if ((err = redux (&res, P, &mu)) != MP_OKAY) {
 +        goto LBL_RES;
 +      }
 +
 +      bitbuf <<= 1;
 +      if ((bitbuf & (1 << winsize)) != 0) {
 +        /* then multiply */
 +        if ((err = mp_mul (&res, &M[1], &res)) != MP_OKAY) {
 +          goto LBL_RES;
 +        }
 +        if ((err = redux (&res, P, &mu)) != MP_OKAY) {
 +          goto LBL_RES;
 +        }
 +      }
 +    }
 +  }
 +
 +  mp_exch (&res, Y);
 +  err = MP_OKAY;
 +LBL_RES:mp_clear (&res);
 +LBL_MU:mp_clear (&mu);
 +LBL_M:
 +  mp_clear(&M[1]);
 +  for (x = 1<<(winsize-1); x < (1 << winsize); x++) {
 +    mp_clear (&M[x]);
 +  }
 +  return err;
 +}
 +
 +
 +/* computes b = a*a */
 +static int mp_sqr (mp_int * a, mp_int * b)
 +{
 +  int     res;
 +
 +#ifdef BN_MP_TOOM_SQR_C
 +  /* use Toom-Cook? */
 +  if (a->used >= TOOM_SQR_CUTOFF) {
 +    res = mp_toom_sqr(a, b);
 +  /* Karatsuba? */
 +  } else 
 +#endif
 +#ifdef BN_MP_KARATSUBA_SQR_C
 +if (a->used >= KARATSUBA_SQR_CUTOFF) {
 +    res = mp_karatsuba_sqr (a, b);
 +  } else 
 +#endif
 +  {
 +#ifdef BN_FAST_S_MP_SQR_C
 +    /* can we use the fast comba multiplier? */
 +    if ((a->used * 2 + 1) < MP_WARRAY && 
 +         a->used < 
 +         (1 << (sizeof(mp_word) * CHAR_BIT - 2*DIGIT_BIT - 1))) {
 +      res = fast_s_mp_sqr (a, b);
 +    } else
 +#endif
 +#ifdef BN_S_MP_SQR_C
 +      res = s_mp_sqr (a, b);
 +#else
 +#error mp_sqr could fail
 +      res = MP_VAL;
 +#endif
 +  }
 +  b->sign = MP_ZPOS;
 +  return res;
 +}
 +
 +
 +/* reduces a modulo n where n is of the form 2**p - d 
 +   This differs from reduce_2k since "d" can be larger
 +   than a single digit.
 +*/
 +static int mp_reduce_2k_l(mp_int *a, mp_int *n, mp_int *d)
 +{
 +   mp_int q;
 +   int    p, res;
 +   
 +   if ((res = mp_init(&q)) != MP_OKAY) {
 +      return res;
 +   }
 +   
 +   p = mp_count_bits(n);    
 +top:
 +   /* q = a/2**p, a = a mod 2**p */
 +   if ((res = mp_div_2d(a, p, &q, a)) != MP_OKAY) {
 +      goto ERR;
 +   }
 +   
 +   /* q = q * d */
 +   if ((res = mp_mul(&q, d, &q)) != MP_OKAY) { 
 +      goto ERR;
 +   }
 +   
 +   /* a = a + q */
 +   if ((res = s_mp_add(a, &q, a)) != MP_OKAY) {
 +      goto ERR;
 +   }
 +   
 +   if (mp_cmp_mag(a, n) != MP_LT) {
 +      s_mp_sub(a, n, a);
 +      goto top;
 +   }
 +   
 +ERR:
 +   mp_clear(&q);
 +   return res;
 +}
 +
 +
 +/* determines the setup value */
 +static int mp_reduce_2k_setup_l(mp_int *a, mp_int *d)
 +{
 +   int    res;
 +   mp_int tmp;
 +   
 +   if ((res = mp_init(&tmp)) != MP_OKAY) {
 +      return res;
 +   }
 +   
 +   if ((res = mp_2expt(&tmp, mp_count_bits(a))) != MP_OKAY) {
 +      goto ERR;
 +   }
 +   
 +   if ((res = s_mp_sub(&tmp, a, d)) != MP_OKAY) {
 +      goto ERR;
 +   }
 +   
 +ERR:
 +   mp_clear(&tmp);
 +   return res;
 +}
 +
 +
 +/* computes a = 2**b 
 + *
 + * Simple algorithm which zeroes the int, grows it then just sets one bit
 + * as required.
 + */
 +static int mp_2expt (mp_int * a, int b)
 +{
 +  int     res;
 +
 +  /* zero a as per default */
 +  mp_zero (a);
 +
 +  /* grow a to accommodate the single bit */
 +  if ((res = mp_grow (a, b / DIGIT_BIT + 1)) != MP_OKAY) {
 +    return res;
 +  }
 +
 +  /* set the used count of where the bit will go */
 +  a->used = b / DIGIT_BIT + 1;
 +
 +  /* put the single bit in its place */
 +  a->dp[b / DIGIT_BIT] = ((mp_digit)1) << (b % DIGIT_BIT);
 +
 +  return MP_OKAY;
 +}
 +
 +
 +/* pre-calculate the value required for Barrett reduction
 + * For a given modulus "b" it calulates the value required in "a"
 + */
 +static int mp_reduce_setup (mp_int * a, mp_int * b)
 +{
 +  int     res;
 +  
 +  if ((res = mp_2expt (a, b->used * 2 * DIGIT_BIT)) != MP_OKAY) {
 +    return res;
 +  }
 +  return mp_div (a, b, a, NULL);
 +}
 +
 +
 +/* reduces x mod m, assumes 0 < x < m**2, mu is 
 + * precomputed via mp_reduce_setup.
 + * From HAC pp.604 Algorithm 14.42
 + */
 +static int mp_reduce (mp_int * x, mp_int * m, mp_int * mu)
 +{
 +  mp_int  q;
 +  int     res, um = m->used;
 +
 +  /* q = x */
 +  if ((res = mp_init_copy (&q, x)) != MP_OKAY) {
 +    return res;
 +  }
 +
 +  /* q1 = x / b**(k-1)  */
 +  mp_rshd (&q, um - 1);         
 +
 +  /* according to HAC this optimization is ok */
 +  if (((unsigned long) um) > (((mp_digit)1) << (DIGIT_BIT - 1))) {
 +    if ((res = mp_mul (&q, mu, &q)) != MP_OKAY) {
 +      goto CLEANUP;
 +    }
 +  } else {
 +#ifdef BN_S_MP_MUL_HIGH_DIGS_C
 +    if ((res = s_mp_mul_high_digs (&q, mu, &q, um)) != MP_OKAY) {
 +      goto CLEANUP;
 +    }
 +#elif defined(BN_FAST_S_MP_MUL_HIGH_DIGS_C)
 +    if ((res = fast_s_mp_mul_high_digs (&q, mu, &q, um)) != MP_OKAY) {
 +      goto CLEANUP;
 +    }
 +#else 
 +    { 
 +#error mp_reduce would always fail
 +      res = MP_VAL;
 +      goto CLEANUP;
 +    }
 +#endif
 +  }
 +
 +  /* q3 = q2 / b**(k+1) */
 +  mp_rshd (&q, um + 1);         
 +
 +  /* x = x mod b**(k+1), quick (no division) */
 +  if ((res = mp_mod_2d (x, DIGIT_BIT * (um + 1), x)) != MP_OKAY) {
 +    goto CLEANUP;
 +  }
 +
 +  /* q = q * m mod b**(k+1), quick (no division) */
 +  if ((res = s_mp_mul_digs (&q, m, &q, um + 1)) != MP_OKAY) {
 +    goto CLEANUP;
 +  }
 +
 +  /* x = x - q */
 +  if ((res = mp_sub (x, &q, x)) != MP_OKAY) {
 +    goto CLEANUP;
 +  }
 +
 +  /* If x < 0, add b**(k+1) to it */
 +  if (mp_cmp_d (x, 0) == MP_LT) {
 +    mp_set (&q, 1);
 +    if ((res = mp_lshd (&q, um + 1)) != MP_OKAY) {
 +      goto CLEANUP;
 +    }
 +    if ((res = mp_add (x, &q, x)) != MP_OKAY) {
 +      goto CLEANUP;
 +    }
 +  }
 +
 +  /* Back off if it's too big */
 +  while (mp_cmp (x, m) != MP_LT) {
 +    if ((res = s_mp_sub (x, m, x)) != MP_OKAY) {
 +      goto CLEANUP;
 +    }
 +  }
 +  
 +CLEANUP:
 +  mp_clear (&q);
 +
 +  return res;
 +}
 +
 +
 +/* multiplies |a| * |b| and only computes up to digs digits of result
 + * HAC pp. 595, Algorithm 14.12  Modified so you can control how 
 + * many digits of output are created.
 + */
 +static int s_mp_mul_digs (mp_int * a, mp_int * b, mp_int * c, int digs)
 +{
 +  mp_int  t;
 +  int     res, pa, pb, ix, iy;
 +  mp_digit u;
 +  mp_word r;
 +  mp_digit tmpx, *tmpt, *tmpy;
 +
 +#ifdef BN_FAST_S_MP_MUL_DIGS_C
 +  /* can we use the fast multiplier? */
 +  if (((digs) < MP_WARRAY) &&
 +      MIN (a->used, b->used) < 
 +          (1 << ((CHAR_BIT * sizeof (mp_word)) - (2 * DIGIT_BIT)))) {
 +    return fast_s_mp_mul_digs (a, b, c, digs);
 +  }
 +#endif
 +
 +  if ((res = mp_init_size (&t, digs)) != MP_OKAY) {
 +    return res;
 +  }
 +  t.used = digs;
 +
 +  /* compute the digits of the product directly */
 +  pa = a->used;
 +  for (ix = 0; ix < pa; ix++) {
 +    /* set the carry to zero */
 +    u = 0;
 +
 +    /* limit ourselves to making digs digits of output */
 +    pb = MIN (b->used, digs - ix);
 +
 +    /* setup some aliases */
 +    /* copy of the digit from a used within the nested loop */
 +    tmpx = a->dp[ix];
 +    
 +    /* an alias for the destination shifted ix places */
 +    tmpt = t.dp + ix;
 +    
 +    /* an alias for the digits of b */
 +    tmpy = b->dp;
 +
 +    /* compute the columns of the output and propagate the carry */
 +    for (iy = 0; iy < pb; iy++) {
 +      /* compute the column as a mp_word */
 +      r       = ((mp_word)*tmpt) +
 +                ((mp_word)tmpx) * ((mp_word)*tmpy++) +
 +                ((mp_word) u);
 +
 +      /* the new column is the lower part of the result */
 +      *tmpt++ = (mp_digit) (r & ((mp_word) MP_MASK));
 +
 +      /* get the carry word from the result */
 +      u       = (mp_digit) (r >> ((mp_word) DIGIT_BIT));
 +    }
 +    /* set carry if it is placed below digs */
 +    if (ix + iy < digs) {
 +      *tmpt = u;
 +    }
 +  }
 +
 +  mp_clamp (&t);
 +  mp_exch (&t, c);
 +
 +  mp_clear (&t);
 +  return MP_OKAY;
 +}
 +
 +
 +#ifdef BN_FAST_S_MP_MUL_DIGS_C
 +/* Fast (comba) multiplier
 + *
 + * This is the fast column-array [comba] multiplier.  It is 
 + * designed to compute the columns of the product first 
 + * then handle the carries afterwards.  This has the effect 
 + * of making the nested loops that compute the columns very
 + * simple and schedulable on super-scalar processors.
 + *
 + * This has been modified to produce a variable number of 
 + * digits of output so if say only a half-product is required 
 + * you don't have to compute the upper half (a feature 
 + * required for fast Barrett reduction).
 + *
 + * Based on Algorithm 14.12 on pp.595 of HAC.
 + *
 + */
 +static int fast_s_mp_mul_digs (mp_int * a, mp_int * b, mp_int * c, int digs)
 +{
 +  int     olduse, res, pa, ix, iz;
 +  mp_digit W[MP_WARRAY];
 +  register mp_word  _W;
 +
 +  /* grow the destination as required */
 +  if (c->alloc < digs) {
 +    if ((res = mp_grow (c, digs)) != MP_OKAY) {
 +      return res;
 +    }
 +  }
 +
 +  /* number of output digits to produce */
 +  pa = MIN(digs, a->used + b->used);
 +
 +  /* clear the carry */
 +  _W = 0;
 +  for (ix = 0; ix < pa; ix++) { 
 +      int      tx, ty;
 +      int      iy;
 +      mp_digit *tmpx, *tmpy;
 +
 +      /* get offsets into the two bignums */
 +      ty = MIN(b->used-1, ix);
 +      tx = ix - ty;
 +
 +      /* setup temp aliases */
 +      tmpx = a->dp + tx;
 +      tmpy = b->dp + ty;
 +
 +      /* this is the number of times the loop will iterrate, essentially 
 +         while (tx++ < a->used && ty-- >= 0) { ... }
 +       */
 +      iy = MIN(a->used-tx, ty+1);
 +
 +      /* execute loop */
 +      for (iz = 0; iz < iy; ++iz) {
 +         _W += ((mp_word)*tmpx++)*((mp_word)*tmpy--);
 +
 +      }
 +
 +      /* store term */
 +      W[ix] = ((mp_digit)_W) & MP_MASK;
 +
 +      /* make next carry */
 +      _W = _W >> ((mp_word)DIGIT_BIT);
 + }
 +
 +  /* setup dest */
 +  olduse  = c->used;
 +  c->used = pa;
 +
 +  {
 +    register mp_digit *tmpc;
 +    tmpc = c->dp;
 +    for (ix = 0; ix < pa+1; ix++) {
 +      /* now extract the previous digit [below the carry] */
 +      *tmpc++ = W[ix];
 +    }
 +
 +    /* clear unused digits [that existed in the old copy of c] */
 +    for (; ix < olduse; ix++) {
 +      *tmpc++ = 0;
 +    }
 +  }
 +  mp_clamp (c);
 +  return MP_OKAY;
 +}
 +#endif /* BN_FAST_S_MP_MUL_DIGS_C */
 +
 +
 +/* init an mp_init for a given size */
 +static int mp_init_size (mp_int * a, int size)
 +{
 +  int x;
 +
 +  /* pad size so there are always extra digits */
 +  size += (MP_PREC * 2) - (size % MP_PREC);   
 +  
 +  /* alloc mem */
 +  a->dp = OPT_CAST(mp_digit) XMALLOC (sizeof (mp_digit) * size);
 +  if (a->dp == NULL) {
 +    return MP_MEM;
 +  }
 +
 +  /* set the members */
 +  a->used  = 0;
 +  a->alloc = size;
 +  a->sign  = MP_ZPOS;
 +
 +  /* zero the digits */
 +  for (x = 0; x < size; x++) {
 +      a->dp[x] = 0;
 +  }
 +
 +  return MP_OKAY;
 +}
 +
 +
 +/* low level squaring, b = a*a, HAC pp.596-597, Algorithm 14.16 */
 +static int s_mp_sqr (mp_int * a, mp_int * b)
 +{
 +  mp_int  t;
 +  int     res, ix, iy, pa;
 +  mp_word r;
 +  mp_digit u, tmpx, *tmpt;
 +
 +  pa = a->used;
 +  if ((res = mp_init_size (&t, 2*pa + 1)) != MP_OKAY) {
 +    return res;
 +  }
 +
 +  /* default used is maximum possible size */
 +  t.used = 2*pa + 1;
 +
 +  for (ix = 0; ix < pa; ix++) {
 +    /* first calculate the digit at 2*ix */
 +    /* calculate double precision result */
 +    r = ((mp_word) t.dp[2*ix]) +
 +        ((mp_word)a->dp[ix])*((mp_word)a->dp[ix]);
 +
 +    /* store lower part in result */
 +    t.dp[ix+ix] = (mp_digit) (r & ((mp_word) MP_MASK));
 +
 +    /* get the carry */
 +    u           = (mp_digit)(r >> ((mp_word) DIGIT_BIT));
 +
 +    /* left hand side of A[ix] * A[iy] */
 +    tmpx        = a->dp[ix];
 +
 +    /* alias for where to store the results */
 +    tmpt        = t.dp + (2*ix + 1);
 +    
 +    for (iy = ix + 1; iy < pa; iy++) {
 +      /* first calculate the product */
 +      r       = ((mp_word)tmpx) * ((mp_word)a->dp[iy]);
 +
 +      /* now calculate the double precision result, note we use
 +       * addition instead of *2 since it's easier to optimize
 +       */
 +      r       = ((mp_word) *tmpt) + r + r + ((mp_word) u);
 +
 +      /* store lower part */
 +      *tmpt++ = (mp_digit) (r & ((mp_word) MP_MASK));
 +
 +      /* get carry */
 +      u       = (mp_digit)(r >> ((mp_word) DIGIT_BIT));
 +    }
 +    /* propagate upwards */
 +    while (u != ((mp_digit) 0)) {
 +      r       = ((mp_word) *tmpt) + ((mp_word) u);
 +      *tmpt++ = (mp_digit) (r & ((mp_word) MP_MASK));
 +      u       = (mp_digit)(r >> ((mp_word) DIGIT_BIT));
 +    }
 +  }
 +
 +  mp_clamp (&t);
 +  mp_exch (&t, b);
 +  mp_clear (&t);
 +  return MP_OKAY;
 +}
 +
 +
 +/* multiplies |a| * |b| and does not compute the lower digs digits
 + * [meant to get the higher part of the product]
 + */
 +static int s_mp_mul_high_digs (mp_int * a, mp_int * b, mp_int * c, int digs)
 +{
 +  mp_int  t;
 +  int     res, pa, pb, ix, iy;
 +  mp_digit u;
 +  mp_word r;
 +  mp_digit tmpx, *tmpt, *tmpy;
 +
 +  /* can we use the fast multiplier? */
 +#ifdef BN_FAST_S_MP_MUL_HIGH_DIGS_C
 +  if (((a->used + b->used + 1) < MP_WARRAY)
 +      && MIN (a->used, b->used) < (1 << ((CHAR_BIT * sizeof (mp_word)) - (2 * DIGIT_BIT)))) {
 +    return fast_s_mp_mul_high_digs (a, b, c, digs);
 +  }
 +#endif
 +
 +  if ((res = mp_init_size (&t, a->used + b->used + 1)) != MP_OKAY) {
 +    return res;
 +  }
 +  t.used = a->used + b->used + 1;
 +
 +  pa = a->used;
 +  pb = b->used;
 +  for (ix = 0; ix < pa; ix++) {
 +    /* clear the carry */
 +    u = 0;
 +
 +    /* left hand side of A[ix] * B[iy] */
 +    tmpx = a->dp[ix];
 +
 +    /* alias to the address of where the digits will be stored */
 +    tmpt = &(t.dp[digs]);
 +
 +    /* alias for where to read the right hand side from */
 +    tmpy = b->dp + (digs - ix);
 +
 +    for (iy = digs - ix; iy < pb; iy++) {
 +      /* calculate the double precision result */
 +      r       = ((mp_word)*tmpt) +
 +                ((mp_word)tmpx) * ((mp_word)*tmpy++) +
 +                ((mp_word) u);
 +
 +      /* get the lower part */
 +      *tmpt++ = (mp_digit) (r & ((mp_word) MP_MASK));
 +
 +      /* carry the carry */
 +      u       = (mp_digit) (r >> ((mp_word) DIGIT_BIT));
 +    }
 +    *tmpt = u;
 +  }
 +  mp_clamp (&t);
 +  mp_exch (&t, c);
 +  mp_clear (&t);
 +  return MP_OKAY;
 +}
 +
 +
 +#ifdef BN_MP_MONTGOMERY_SETUP_C
 +/* setups the montgomery reduction stuff */
 +static int
 +mp_montgomery_setup (mp_int * n, mp_digit * rho)
 +{
 +  mp_digit x, b;
 +
 +/* fast inversion mod 2**k
 + *
 + * Based on the fact that
 + *
 + * XA = 1 (mod 2**n)  =>  (X(2-XA)) A = 1 (mod 2**2n)
 + *                    =>  2*X*A - X*X*A*A = 1
 + *                    =>  2*(1) - (1)     = 1
 + */
 +  b = n->dp[0];
 +
 +  if ((b & 1) == 0) {
 +    return MP_VAL;
 +  }
 +
 +  x = (((b + 2) & 4) << 1) + b; /* here x*a==1 mod 2**4 */
 +  x *= 2 - b * x;               /* here x*a==1 mod 2**8 */
 +#if !defined(MP_8BIT)
 +  x *= 2 - b * x;               /* here x*a==1 mod 2**16 */
 +#endif
 +#if defined(MP_64BIT) || !(defined(MP_8BIT) || defined(MP_16BIT))
 +  x *= 2 - b * x;               /* here x*a==1 mod 2**32 */
 +#endif
 +#ifdef MP_64BIT
 +  x *= 2 - b * x;               /* here x*a==1 mod 2**64 */
 +#endif
 +
 +  /* rho = -1/m mod b */
 +  *rho = (unsigned long)(((mp_word)1 << ((mp_word) DIGIT_BIT)) - x) & MP_MASK;
 +
 +  return MP_OKAY;
 +}
 +#endif
 +
 +
 +#ifdef BN_FAST_MP_MONTGOMERY_REDUCE_C
 +/* computes xR**-1 == x (mod N) via Montgomery Reduction
 + *
 + * This is an optimized implementation of montgomery_reduce
 + * which uses the comba method to quickly calculate the columns of the
 + * reduction.
 + *
 + * Based on Algorithm 14.32 on pp.601 of HAC.
 +*/
 +static int fast_mp_montgomery_reduce (mp_int * x, mp_int * n, mp_digit rho)
 +{
 +  int     ix, res, olduse;
 +  mp_word W[MP_WARRAY];
 +
 +  /* get old used count */
 +  olduse = x->used;
 +
 +  /* grow a as required */
 +  if (x->alloc < n->used + 1) {
 +    if ((res = mp_grow (x, n->used + 1)) != MP_OKAY) {
 +      return res;
 +    }
 +  }
 +
 +  /* first we have to get the digits of the input into
 +   * an array of double precision words W[...]
 +   */
 +  {
 +    register mp_word *_W;
 +    register mp_digit *tmpx;
 +
 +    /* alias for the W[] array */
 +    _W   = W;
 +
 +    /* alias for the digits of  x*/
 +    tmpx = x->dp;
 +
 +    /* copy the digits of a into W[0..a->used-1] */
 +    for (ix = 0; ix < x->used; ix++) {
 +      *_W++ = *tmpx++;
 +    }
 +
 +    /* zero the high words of W[a->used..m->used*2] */
 +    for (; ix < n->used * 2 + 1; ix++) {
 +      *_W++ = 0;
 +    }
 +  }
 +
 +  /* now we proceed to zero successive digits
 +   * from the least significant upwards
 +   */
 +  for (ix = 0; ix < n->used; ix++) {
 +    /* mu = ai * m' mod b
 +     *
 +     * We avoid a double precision multiplication (which isn't required)
 +     * by casting the value down to a mp_digit.  Note this requires
 +     * that W[ix-1] have  the carry cleared (see after the inner loop)
 +     */
 +    register mp_digit mu;
 +    mu = (mp_digit) (((W[ix] & MP_MASK) * rho) & MP_MASK);
 +
 +    /* a = a + mu * m * b**i
 +     *
 +     * This is computed in place and on the fly.  The multiplication
 +     * by b**i is handled by offseting which columns the results
 +     * are added to.
 +     *
 +     * Note the comba method normally doesn't handle carries in the
 +     * inner loop In this case we fix the carry from the previous
 +     * column since the Montgomery reduction requires digits of the
 +     * result (so far) [see above] to work.  This is
 +     * handled by fixing up one carry after the inner loop.  The
 +     * carry fixups are done in order so after these loops the
 +     * first m->used words of W[] have the carries fixed
 +     */
 +    {
 +      register int iy;
 +      register mp_digit *tmpn;
 +      register mp_word *_W;
 +
 +      /* alias for the digits of the modulus */
 +      tmpn = n->dp;
 +
 +      /* Alias for the columns set by an offset of ix */
 +      _W = W + ix;
 +
 +      /* inner loop */
 +      for (iy = 0; iy < n->used; iy++) {
 +          *_W++ += ((mp_word)mu) * ((mp_word)*tmpn++);
 +      }
 +    }
 +
 +    /* now fix carry for next digit, W[ix+1] */
 +    W[ix + 1] += W[ix] >> ((mp_word) DIGIT_BIT);
 +  }
 +
 +  /* now we have to propagate the carries and
 +   * shift the words downward [all those least
 +   * significant digits we zeroed].
 +   */
 +  {
 +    register mp_digit *tmpx;
 +    register mp_word *_W, *_W1;
 +
 +    /* nox fix rest of carries */
 +
 +    /* alias for current word */
 +    _W1 = W + ix;
 +
 +    /* alias for next word, where the carry goes */
 +    _W = W + ++ix;
 +
 +    for (; ix <= n->used * 2 + 1; ix++) {
 +      *_W++ += *_W1++ >> ((mp_word) DIGIT_BIT);
 +    }
 +
 +    /* copy out, A = A/b**n
 +     *
 +     * The result is A/b**n but instead of converting from an
 +     * array of mp_word to mp_digit than calling mp_rshd
 +     * we just copy them in the right order
 +     */
 +
 +    /* alias for destination word */
 +    tmpx = x->dp;
 +
 +    /* alias for shifted double precision result */
 +    _W = W + n->used;
 +
 +    for (ix = 0; ix < n->used + 1; ix++) {
 +      *tmpx++ = (mp_digit)(*_W++ & ((mp_word) MP_MASK));
 +    }
 +
 +    /* zero oldused digits, if the input a was larger than
 +     * m->used+1 we'll have to clear the digits
 +     */
 +    for (; ix < olduse; ix++) {
 +      *tmpx++ = 0;
 +    }
 +  }
 +
 +  /* set the max used and clamp */
 +  x->used = n->used + 1;
 +  mp_clamp (x);
 +
 +  /* if A >= m then A = A - m */
 +  if (mp_cmp_mag (x, n) != MP_LT) {
 +    return s_mp_sub (x, n, x);
 +  }
 +  return MP_OKAY;
 +}
 +#endif
 +
 +
 +#ifdef BN_MP_MUL_2_C
 +/* b = a*2 */
 +static int mp_mul_2(mp_int * a, mp_int * b)
 +{
 +  int     x, res, oldused;
 +
 +  /* grow to accommodate result */
 +  if (b->alloc < a->used + 1) {
 +    if ((res = mp_grow (b, a->used + 1)) != MP_OKAY) {
 +      return res;
 +    }
 +  }
 +
 +  oldused = b->used;
 +  b->used = a->used;
 +
 +  {
 +    register mp_digit r, rr, *tmpa, *tmpb;
 +
 +    /* alias for source */
 +    tmpa = a->dp;
 +    
 +    /* alias for dest */
 +    tmpb = b->dp;
 +
 +    /* carry */
 +    r = 0;
 +    for (x = 0; x < a->used; x++) {
 +    
 +      /* get what will be the *next* carry bit from the 
 +       * MSB of the current digit 
 +       */
 +      rr = *tmpa >> ((mp_digit)(DIGIT_BIT - 1));
 +      
 +      /* now shift up this digit, add in the carry [from the previous] */
 +      *tmpb++ = ((*tmpa++ << ((mp_digit)1)) | r) & MP_MASK;
 +      
 +      /* copy the carry that would be from the source 
 +       * digit into the next iteration 
 +       */
 +      r = rr;
 +    }
 +
 +    /* new leading digit? */
 +    if (r != 0) {
 +      /* add a MSB which is always 1 at this point */
 +      *tmpb = 1;
 +      ++(b->used);
 +    }
 +
 +    /* now zero any excess digits on the destination 
 +     * that we didn't write to 
 +     */
 +    tmpb = b->dp + b->used;
 +    for (x = b->used; x < oldused; x++) {
 +      *tmpb++ = 0;
 +    }
 +  }
 +  b->sign = a->sign;
 +  return MP_OKAY;
 +}
 +#endif
 +
 +
 +#ifdef BN_MP_MONTGOMERY_CALC_NORMALIZATION_C
 +/*
 + * shifts with subtractions when the result is greater than b.
 + *
 + * The method is slightly modified to shift B unconditionally up to just under
 + * the leading bit of b.  This saves a lot of multiple precision shifting.
 + */
 +static int mp_montgomery_calc_normalization (mp_int * a, mp_int * b)
 +{
 +  int     x, bits, res;
 +
 +  /* how many bits of last digit does b use */
 +  bits = mp_count_bits (b) % DIGIT_BIT;
 +
 +  if (b->used > 1) {
 +     if ((res = mp_2expt (a, (b->used - 1) * DIGIT_BIT + bits - 1)) != MP_OKAY) {
 +        return res;
 +     }
 +  } else {
 +     mp_set(a, 1);
 +     bits = 1;
 +  }
 +
 +
 +  /* now compute C = A * B mod b */
 +  for (x = bits - 1; x < (int)DIGIT_BIT; x++) {
 +    if ((res = mp_mul_2 (a, a)) != MP_OKAY) {
 +      return res;
 +    }
 +    if (mp_cmp_mag (a, b) != MP_LT) {
 +      if ((res = s_mp_sub (a, b, a)) != MP_OKAY) {
 +        return res;
 +      }
 +    }
 +  }
 +
 +  return MP_OKAY;
 +}
 +#endif
 +
 +
 +#ifdef BN_MP_EXPTMOD_FAST_C
 +/* computes Y == G**X mod P, HAC pp.616, Algorithm 14.85
 + *
 + * Uses a left-to-right k-ary sliding window to compute the modular exponentiation.
 + * The value of k changes based on the size of the exponent.
 + *
 + * Uses Montgomery or Diminished Radix reduction [whichever appropriate]
 + */
 +
 +static int mp_exptmod_fast (mp_int * G, mp_int * X, mp_int * P, mp_int * Y, int redmode)
 +{
 +  mp_int  M[TAB_SIZE], res;
 +  mp_digit buf, mp;
 +  int     err, bitbuf, bitcpy, bitcnt, mode, digidx, x, y, winsize;
 +
 +  /* use a pointer to the reduction algorithm.  This allows us to use
 +   * one of many reduction algorithms without modding the guts of
 +   * the code with if statements everywhere.
 +   */
 +  int     (*redux)(mp_int*,mp_int*,mp_digit);
 +
 +  /* find window size */
 +  x = mp_count_bits (X);
 +  if (x <= 7) {
 +    winsize = 2;
 +  } else if (x <= 36) {
 +    winsize = 3;
 +  } else if (x <= 140) {
 +    winsize = 4;
 +  } else if (x <= 450) {
 +    winsize = 5;
 +  } else if (x <= 1303) {
 +    winsize = 6;
 +  } else if (x <= 3529) {
 +    winsize = 7;
 +  } else {
 +    winsize = 8;
 +  }
 +
 +#ifdef MP_LOW_MEM
 +  if (winsize > 5) {
 +     winsize = 5;
 +  }
 +#endif
 +
 +  /* init M array */
 +  /* init first cell */
 +  if ((err = mp_init(&M[1])) != MP_OKAY) {
 +     return err;
 +  }
 +
 +  /* now init the second half of the array */
 +  for (x = 1<<(winsize-1); x < (1 << winsize); x++) {
 +    if ((err = mp_init(&M[x])) != MP_OKAY) {
 +      for (y = 1<<(winsize-1); y < x; y++) {
 +        mp_clear (&M[y]);
 +      }
 +      mp_clear(&M[1]);
 +      return err;
 +    }
 +  }
 +
 +  /* determine and setup reduction code */
 +  if (redmode == 0) {
 +#ifdef BN_MP_MONTGOMERY_SETUP_C     
 +     /* now setup montgomery  */
 +     if ((err = mp_montgomery_setup (P, &mp)) != MP_OKAY) {
 +        goto LBL_M;
 +     }
 +#else
 +     err = MP_VAL;
 +     goto LBL_M;
 +#endif
 +
 +     /* automatically pick the comba one if available (saves quite a few calls/ifs) */
 +#ifdef BN_FAST_MP_MONTGOMERY_REDUCE_C
 +     if (((P->used * 2 + 1) < MP_WARRAY) &&
 +          P->used < (1 << ((CHAR_BIT * sizeof (mp_word)) - (2 * DIGIT_BIT)))) {
 +        redux = fast_mp_montgomery_reduce;
 +     } else 
 +#endif
 +     {
 +#ifdef BN_MP_MONTGOMERY_REDUCE_C
 +        /* use slower baseline Montgomery method */
 +        redux = mp_montgomery_reduce;
 +#else
 +        err = MP_VAL;
 +        goto LBL_M;
 +#endif
 +     }
 +  } else if (redmode == 1) {
 +#if defined(BN_MP_DR_SETUP_C) && defined(BN_MP_DR_REDUCE_C)
 +     /* setup DR reduction for moduli of the form B**k - b */
 +     mp_dr_setup(P, &mp);
 +     redux = mp_dr_reduce;
 +#else
 +     err = MP_VAL;
 +     goto LBL_M;
 +#endif
 +  } else {
 +#if defined(BN_MP_REDUCE_2K_SETUP_C) && defined(BN_MP_REDUCE_2K_C)
 +     /* setup DR reduction for moduli of the form 2**k - b */
 +     if ((err = mp_reduce_2k_setup(P, &mp)) != MP_OKAY) {
 +        goto LBL_M;
 +     }
 +     redux = mp_reduce_2k;
 +#else
 +     err = MP_VAL;
 +     goto LBL_M;
 +#endif
 +  }
 +
 +  /* setup result */
 +  if ((err = mp_init (&res)) != MP_OKAY) {
 +    goto LBL_M;
 +  }
 +
 +  /* create M table
 +   *
 +
 +   *
 +   * The first half of the table is not computed though accept for M[0] and M[1]
 +   */
 +
 +  if (redmode == 0) {
 +#ifdef BN_MP_MONTGOMERY_CALC_NORMALIZATION_C
 +     /* now we need R mod m */
 +     if ((err = mp_montgomery_calc_normalization (&res, P)) != MP_OKAY) {
 +       goto LBL_RES;
 +     }
 +#else 
 +     err = MP_VAL;
 +     goto LBL_RES;
 +#endif
 +
 +     /* now set M[1] to G * R mod m */
 +     if ((err = mp_mulmod (G, &res, P, &M[1])) != MP_OKAY) {
 +       goto LBL_RES;
 +     }
 +  } else {
 +     mp_set(&res, 1);
 +     if ((err = mp_mod(G, P, &M[1])) != MP_OKAY) {
 +        goto LBL_RES;
 +     }
 +  }
 +
 +  /* compute the value at M[1<<(winsize-1)] by squaring M[1] (winsize-1) times */
 +  if ((err = mp_copy (&M[1], &M[1 << (winsize - 1)])) != MP_OKAY) {
 +    goto LBL_RES;
 +  }
 +
 +  for (x = 0; x < (winsize - 1); x++) {
 +    if ((err = mp_sqr (&M[1 << (winsize - 1)], &M[1 << (winsize - 1)])) != MP_OKAY) {
 +      goto LBL_RES;
 +    }
 +    if ((err = redux (&M[1 << (winsize - 1)], P, mp)) != MP_OKAY) {
 +      goto LBL_RES;
 +    }
 +  }
 +
 +  /* create upper table */
 +  for (x = (1 << (winsize - 1)) + 1; x < (1 << winsize); x++) {
 +    if ((err = mp_mul (&M[x - 1], &M[1], &M[x])) != MP_OKAY) {
 +      goto LBL_RES;
 +    }
 +    if ((err = redux (&M[x], P, mp)) != MP_OKAY) {
 +      goto LBL_RES;
 +    }
 +  }
 +
 +  /* set initial mode and bit cnt */
 +  mode   = 0;
 +  bitcnt = 1;
 +  buf    = 0;
 +  digidx = X->used - 1;
 +  bitcpy = 0;
 +  bitbuf = 0;
 +
 +  for (;;) {
 +    /* grab next digit as required */
 +    if (--bitcnt == 0) {
 +      /* if digidx == -1 we are out of digits so break */
 +      if (digidx == -1) {
 +        break;
 +      }
 +      /* read next digit and reset bitcnt */
 +      buf    = X->dp[digidx--];
 +      bitcnt = (int)DIGIT_BIT;
 +    }
 +
 +    /* grab the next msb from the exponent */
 +    y     = (mp_digit)(buf >> (DIGIT_BIT - 1)) & 1;
 +    buf <<= (mp_digit)1;
 +
 +    /* if the bit is zero and mode == 0 then we ignore it
 +     * These represent the leading zero bits before the first 1 bit
 +     * in the exponent.  Technically this opt is not required but it
 +     * does lower the # of trivial squaring/reductions used
 +     */
 +    if (mode == 0 && y == 0) {
 +      continue;
 +    }
 +
 +    /* if the bit is zero and mode == 1 then we square */
 +    if (mode == 1 && y == 0) {
 +      if ((err = mp_sqr (&res, &res)) != MP_OKAY) {
 +        goto LBL_RES;
 +      }
 +      if ((err = redux (&res, P, mp)) != MP_OKAY) {
 +        goto LBL_RES;
 +      }
 +      continue;
 +    }
 +
 +    /* else we add it to the window */
 +    bitbuf |= (y << (winsize - ++bitcpy));
 +    mode    = 2;
 +
 +    if (bitcpy == winsize) {
 +      /* ok window is filled so square as required and multiply  */
 +      /* square first */
 +      for (x = 0; x < winsize; x++) {
 +        if ((err = mp_sqr (&res, &res)) != MP_OKAY) {
 +          goto LBL_RES;
 +        }
 +        if ((err = redux (&res, P, mp)) != MP_OKAY) {
 +          goto LBL_RES;
 +        }
 +      }
 +
 +      /* then multiply */
 +      if ((err = mp_mul (&res, &M[bitbuf], &res)) != MP_OKAY) {
 +        goto LBL_RES;
 +      }
 +      if ((err = redux (&res, P, mp)) != MP_OKAY) {
 +        goto LBL_RES;
 +      }
 +
 +      /* empty window and reset */
 +      bitcpy = 0;
 +      bitbuf = 0;
 +      mode   = 1;
 +    }
 +  }
 +
 +  /* if bits remain then square/multiply */
 +  if (mode == 2 && bitcpy > 0) {
 +    /* square then multiply if the bit is set */
 +    for (x = 0; x < bitcpy; x++) {
 +      if ((err = mp_sqr (&res, &res)) != MP_OKAY) {
 +        goto LBL_RES;
 +      }
 +      if ((err = redux (&res, P, mp)) != MP_OKAY) {
 +        goto LBL_RES;
 +      }
 +
 +      /* get next bit of the window */
 +      bitbuf <<= 1;
 +      if ((bitbuf & (1 << winsize)) != 0) {
 +        /* then multiply */
 +        if ((err = mp_mul (&res, &M[1], &res)) != MP_OKAY) {
 +          goto LBL_RES;
 +        }
 +        if ((err = redux (&res, P, mp)) != MP_OKAY) {
 +          goto LBL_RES;
 +        }
 +      }
 +    }
 +  }
 +
 +  if (redmode == 0) {
 +     /* fixup result if Montgomery reduction is used
 +      * recall that any value in a Montgomery system is
 +      * actually multiplied by R mod n.  So we have
 +      * to reduce one more time to cancel out the factor
 +      * of R.
 +      */
 +     if ((err = redux(&res, P, mp)) != MP_OKAY) {
 +       goto LBL_RES;
 +     }
 +  }
 +
 +  /* swap res with Y */
 +  mp_exch (&res, Y);
 +  err = MP_OKAY;
 +LBL_RES:mp_clear (&res);
 +LBL_M:
 +  mp_clear(&M[1]);
 +  for (x = 1<<(winsize-1); x < (1 << winsize); x++) {
 +    mp_clear (&M[x]);
 +  }
 +  return err;
 +}
 +#endif
 +
 +
 +#ifdef BN_FAST_S_MP_SQR_C
 +/* the jist of squaring...
 + * you do like mult except the offset of the tmpx [one that 
 + * starts closer to zero] can't equal the offset of tmpy.  
 + * So basically you set up iy like before then you min it with
 + * (ty-tx) so that it never happens.  You double all those 
 + * you add in the inner loop
 +
 +After that loop you do the squares and add them in.
 +*/
 +
 +static int fast_s_mp_sqr (mp_int * a, mp_int * b)
 +{
 +  int       olduse, res, pa, ix, iz;
 +  mp_digit   W[MP_WARRAY], *tmpx;
 +  mp_word   W1;
 +
 +  /* grow the destination as required */
 +  pa = a->used + a->used;
 +  if (b->alloc < pa) {
 +    if ((res = mp_grow (b, pa)) != MP_OKAY) {
 +      return res;
 +    }
 +  }
 +
 +  /* number of output digits to produce */
 +  W1 = 0;
 +  for (ix = 0; ix < pa; ix++) { 
 +      int      tx, ty, iy;
 +      mp_word  _W;
 +      mp_digit *tmpy;
 +
 +      /* clear counter */
 +      _W = 0;
 +
 +      /* get offsets into the two bignums */
 +      ty = MIN(a->used-1, ix);
 +      tx = ix - ty;
 +
 +      /* setup temp aliases */
 +      tmpx = a->dp + tx;
 +      tmpy = a->dp + ty;
 +
 +      /* this is the number of times the loop will iterrate, essentially
 +         while (tx++ < a->used && ty-- >= 0) { ... }
 +       */
 +      iy = MIN(a->used-tx, ty+1);
 +
 +      /* now for squaring tx can never equal ty 
 +       * we halve the distance since they approach at a rate of 2x
 +       * and we have to round because odd cases need to be executed
 +       */
 +      iy = MIN(iy, (ty-tx+1)>>1);
 +
 +      /* execute loop */
 +      for (iz = 0; iz < iy; iz++) {
 +         _W += ((mp_word)*tmpx++)*((mp_word)*tmpy--);
 +      }
 +
 +      /* double the inner product and add carry */
 +      _W = _W + _W + W1;
 +
 +      /* even columns have the square term in them */
 +      if ((ix&1) == 0) {
 +         _W += ((mp_word)a->dp[ix>>1])*((mp_word)a->dp[ix>>1]);
 +      }
 +
 +      /* store it */
 +      W[ix] = (mp_digit)(_W & MP_MASK);
 +
 +      /* make next carry */
 +      W1 = _W >> ((mp_word)DIGIT_BIT);
 +  }
 +
 +  /* setup dest */
 +  olduse  = b->used;
 +  b->used = a->used+a->used;
 +
 +  {
 +    mp_digit *tmpb;
 +    tmpb = b->dp;
 +    for (ix = 0; ix < pa; ix++) {
 +      *tmpb++ = W[ix] & MP_MASK;
 +    }
 +
 +    /* clear unused digits [that existed in the old copy of c] */
 +    for (; ix < olduse; ix++) {
 +      *tmpb++ = 0;
 +    }
 +  }
 +  mp_clamp (b);
 +  return MP_OKAY;
 +}
 +#endif
 +
 +
 +#ifdef BN_MP_MUL_D_C
 +/* multiply by a digit */
 +static int
 +mp_mul_d (mp_int * a, mp_digit b, mp_int * c)
 +{
 +  mp_digit u, *tmpa, *tmpc;
 +  mp_word  r;
 +  int      ix, res, olduse;
 +
 +  /* make sure c is big enough to hold a*b */
 +  if (c->alloc < a->used + 1) {
 +    if ((res = mp_grow (c, a->used + 1)) != MP_OKAY) {
 +      return res;
 +    }
 +  }
 +
 +  /* get the original destinations used count */
 +  olduse = c->used;
 +
 +  /* set the sign */
 +  c->sign = a->sign;
 +
 +  /* alias for a->dp [source] */
 +  tmpa = a->dp;
 +
 +  /* alias for c->dp [dest] */
 +  tmpc = c->dp;
 +
 +  /* zero carry */
 +  u = 0;
 +
 +  /* compute columns */
 +  for (ix = 0; ix < a->used; ix++) {
 +    /* compute product and carry sum for this term */
 +    r       = ((mp_word) u) + ((mp_word)*tmpa++) * ((mp_word)b);
 +
 +    /* mask off higher bits to get a single digit */
 +    *tmpc++ = (mp_digit) (r & ((mp_word) MP_MASK));
 +
 +    /* send carry into next iteration */
 +    u       = (mp_digit) (r >> ((mp_word) DIGIT_BIT));
 +  }
 +
 +  /* store final carry [if any] and increment ix offset  */
 +  *tmpc++ = u;
 +  ++ix;
 +
 +  /* now zero digits above the top */
 +  while (ix++ < olduse) {
 +     *tmpc++ = 0;
 +  }
 +
 +  /* set used count */
 +  c->used = a->used + 1;
 +  mp_clamp(c);
 +
 +  return MP_OKAY;
 +}
 +#endif
index facdd659173db9d87cc865b5fa1b15ed4a9d689f,0000000000000000000000000000000000000000..a6f0587e34c57854693d3bf093dcea518bc48837
mode 100644,000000..100644
--- /dev/null
@@@ -1,832 -1,0 +1,830 @@@
-  * tlsv1_client_get_keys - Get master key and random data from TLS connection
 +/*
 + * TLS v1.0/v1.1/v1.2 client (RFC 2246, RFC 4346, RFC 5246)
 + * 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 "includes.h"
 +
 +#include "common.h"
 +#include "crypto/sha1.h"
 +#include "crypto/tls.h"
 +#include "tlsv1_common.h"
 +#include "tlsv1_record.h"
 +#include "tlsv1_client.h"
 +#include "tlsv1_client_i.h"
 +
 +/* TODO:
 + * Support for a message fragmented across several records (RFC 2246, 6.2.1)
 + */
 +
 +
 +void tls_alert(struct tlsv1_client *conn, u8 level, u8 description)
 +{
 +      conn->alert_level = level;
 +      conn->alert_description = description;
 +}
 +
 +
 +void tlsv1_client_free_dh(struct tlsv1_client *conn)
 +{
 +      os_free(conn->dh_p);
 +      os_free(conn->dh_g);
 +      os_free(conn->dh_ys);
 +      conn->dh_p = conn->dh_g = conn->dh_ys = NULL;
 +}
 +
 +
 +int tls_derive_pre_master_secret(u8 *pre_master_secret)
 +{
 +      WPA_PUT_BE16(pre_master_secret, TLS_VERSION);
 +      if (os_get_random(pre_master_secret + 2,
 +                        TLS_PRE_MASTER_SECRET_LEN - 2))
 +              return -1;
 +      return 0;
 +}
 +
 +
 +int tls_derive_keys(struct tlsv1_client *conn,
 +                  const u8 *pre_master_secret, size_t pre_master_secret_len)
 +{
 +      u8 seed[2 * TLS_RANDOM_LEN];
 +      u8 key_block[TLS_MAX_KEY_BLOCK_LEN];
 +      u8 *pos;
 +      size_t key_block_len;
 +
 +      if (pre_master_secret) {
 +              wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: pre_master_secret",
 +                              pre_master_secret, pre_master_secret_len);
 +              os_memcpy(seed, conn->client_random, TLS_RANDOM_LEN);
 +              os_memcpy(seed + TLS_RANDOM_LEN, conn->server_random,
 +                        TLS_RANDOM_LEN);
 +              if (tls_prf(conn->rl.tls_version,
 +                          pre_master_secret, pre_master_secret_len,
 +                          "master secret", seed, 2 * TLS_RANDOM_LEN,
 +                          conn->master_secret, TLS_MASTER_SECRET_LEN)) {
 +                      wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive "
 +                                 "master_secret");
 +                      return -1;
 +              }
 +              wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: master_secret",
 +                              conn->master_secret, TLS_MASTER_SECRET_LEN);
 +      }
 +
 +      os_memcpy(seed, conn->server_random, TLS_RANDOM_LEN);
 +      os_memcpy(seed + TLS_RANDOM_LEN, conn->client_random, TLS_RANDOM_LEN);
 +      key_block_len = 2 * (conn->rl.hash_size + conn->rl.key_material_len);
 +      if (conn->rl.tls_version == TLS_VERSION_1)
 +              key_block_len += 2 * conn->rl.iv_size;
 +      if (tls_prf(conn->rl.tls_version,
 +                  conn->master_secret, TLS_MASTER_SECRET_LEN,
 +                  "key expansion", seed, 2 * TLS_RANDOM_LEN,
 +                  key_block, key_block_len)) {
 +              wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive key_block");
 +              return -1;
 +      }
 +      wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: key_block",
 +                      key_block, key_block_len);
 +
 +      pos = key_block;
 +
 +      /* client_write_MAC_secret */
 +      os_memcpy(conn->rl.write_mac_secret, pos, conn->rl.hash_size);
 +      pos += conn->rl.hash_size;
 +      /* server_write_MAC_secret */
 +      os_memcpy(conn->rl.read_mac_secret, pos, conn->rl.hash_size);
 +      pos += conn->rl.hash_size;
 +
 +      /* client_write_key */
 +      os_memcpy(conn->rl.write_key, pos, conn->rl.key_material_len);
 +      pos += conn->rl.key_material_len;
 +      /* server_write_key */
 +      os_memcpy(conn->rl.read_key, pos, conn->rl.key_material_len);
 +      pos += conn->rl.key_material_len;
 +
 +      if (conn->rl.tls_version == TLS_VERSION_1) {
 +              /* client_write_IV */
 +              os_memcpy(conn->rl.write_iv, pos, conn->rl.iv_size);
 +              pos += conn->rl.iv_size;
 +              /* server_write_IV */
 +              os_memcpy(conn->rl.read_iv, pos, conn->rl.iv_size);
 +              pos += conn->rl.iv_size;
 +      } else {
 +              /*
 +               * Use IV field to set the mask value for TLS v1.1. A fixed
 +               * mask of zero is used per the RFC 4346, 6.2.3.2 CBC Block
 +               * Cipher option 2a.
 +               */
 +              os_memset(conn->rl.write_iv, 0, conn->rl.iv_size);
 +      }
 +
 +      return 0;
 +}
 +
 +
 +/**
 + * tlsv1_client_handshake - Process TLS handshake
 + * @conn: TLSv1 client connection data from tlsv1_client_init()
 + * @in_data: Input data from TLS peer
 + * @in_len: Input data length
 + * @out_len: Length of the output buffer.
 + * @appl_data: Pointer to application data pointer, or %NULL if dropped
 + * @appl_data_len: Pointer to variable that is set to appl_data length
 + * @need_more_data: Set to 1 if more data would be needed to complete
 + *    processing
 + * Returns: Pointer to output data, %NULL on failure
 + */
 +u8 * tlsv1_client_handshake(struct tlsv1_client *conn,
 +                          const u8 *in_data, size_t in_len,
 +                          size_t *out_len, u8 **appl_data,
 +                          size_t *appl_data_len, int *need_more_data)
 +{
 +      const u8 *pos, *end;
 +      u8 *msg = NULL, *in_msg = NULL, *in_pos, *in_end, alert, ct;
 +      size_t in_msg_len;
 +      int no_appl_data;
 +      int used;
 +
 +      if (need_more_data)
 +              *need_more_data = 0;
 +
 +      if (conn->state == CLIENT_HELLO) {
 +              if (in_len)
 +                      return NULL;
 +              return tls_send_client_hello(conn, out_len);
 +      }
 +
 +      if (conn->partial_input) {
 +              if (wpabuf_resize(&conn->partial_input, in_len) < 0) {
 +                      wpa_printf(MSG_DEBUG, "TLSv1: Failed to allocate "
 +                                 "memory for pending record");
 +                      tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
 +                                TLS_ALERT_INTERNAL_ERROR);
 +                      goto failed;
 +              }
 +              wpabuf_put_data(conn->partial_input, in_data, in_len);
 +              in_data = wpabuf_head(conn->partial_input);
 +              in_len = wpabuf_len(conn->partial_input);
 +      }
 +
 +      if (in_data == NULL || in_len == 0)
 +              return NULL;
 +
 +      pos = in_data;
 +      end = in_data + in_len;
 +      in_msg = os_malloc(in_len);
 +      if (in_msg == NULL)
 +              return NULL;
 +
 +      /* Each received packet may include multiple records */
 +      while (pos < end) {
 +              in_msg_len = in_len;
 +              used = tlsv1_record_receive(&conn->rl, pos, end - pos,
 +                                          in_msg, &in_msg_len, &alert);
 +              if (used < 0) {
 +                      wpa_printf(MSG_DEBUG, "TLSv1: Processing received "
 +                                 "record failed");
 +                      tls_alert(conn, TLS_ALERT_LEVEL_FATAL, alert);
 +                      goto failed;
 +              }
 +              if (used == 0) {
 +                      struct wpabuf *partial;
 +                      wpa_printf(MSG_DEBUG, "TLSv1: Need more data");
 +                      partial = wpabuf_alloc_copy(pos, end - pos);
 +                      wpabuf_free(conn->partial_input);
 +                      conn->partial_input = partial;
 +                      if (conn->partial_input == NULL) {
 +                              wpa_printf(MSG_DEBUG, "TLSv1: Failed to "
 +                                         "allocate memory for pending "
 +                                         "record");
 +                              tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
 +                                        TLS_ALERT_INTERNAL_ERROR);
 +                              goto failed;
 +                      }
 +                      os_free(in_msg);
 +                      if (need_more_data)
 +                              *need_more_data = 1;
 +                      return NULL;
 +              }
 +              ct = pos[0];
 +
 +              in_pos = in_msg;
 +              in_end = in_msg + in_msg_len;
 +
 +              /* Each received record may include multiple messages of the
 +               * same ContentType. */
 +              while (in_pos < in_end) {
 +                      in_msg_len = in_end - in_pos;
 +                      if (tlsv1_client_process_handshake(conn, ct, in_pos,
 +                                                         &in_msg_len,
 +                                                         appl_data,
 +                                                         appl_data_len) < 0)
 +                              goto failed;
 +                      in_pos += in_msg_len;
 +              }
 +
 +              pos += used;
 +      }
 +
 +      os_free(in_msg);
 +      in_msg = NULL;
 +
 +      no_appl_data = appl_data == NULL || *appl_data == NULL;
 +      msg = tlsv1_client_handshake_write(conn, out_len, no_appl_data);
 +
 +failed:
 +      os_free(in_msg);
 +      if (conn->alert_level) {
 +              wpabuf_free(conn->partial_input);
 +              conn->partial_input = NULL;
 +              conn->state = FAILED;
 +              os_free(msg);
 +              msg = tlsv1_client_send_alert(conn, conn->alert_level,
 +                                            conn->alert_description,
 +                                            out_len);
 +      } else if (msg == NULL) {
 +              msg = os_zalloc(1);
 +              *out_len = 0;
 +      }
 +
 +      if (need_more_data == NULL || !(*need_more_data)) {
 +              wpabuf_free(conn->partial_input);
 +              conn->partial_input = NULL;
 +      }
 +
 +      return msg;
 +}
 +
 +
 +/**
 + * tlsv1_client_encrypt - Encrypt data into TLS tunnel
 + * @conn: TLSv1 client connection data from tlsv1_client_init()
 + * @in_data: Pointer to plaintext data to be encrypted
 + * @in_len: Input buffer length
 + * @out_data: Pointer to output buffer (encrypted TLS data)
 + * @out_len: Maximum out_data length 
 + * Returns: Number of bytes written to out_data, -1 on failure
 + *
 + * This function is used after TLS handshake has been completed successfully to
 + * send data in the encrypted tunnel.
 + */
 +int tlsv1_client_encrypt(struct tlsv1_client *conn,
 +                       const u8 *in_data, size_t in_len,
 +                       u8 *out_data, size_t out_len)
 +{
 +      size_t rlen;
 +
 +      wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Plaintext AppData",
 +                      in_data, in_len);
 +
 +      if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_APPLICATION_DATA,
 +                            out_data, out_len, in_data, in_len, &rlen) < 0) {
 +              wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record");
 +              tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
 +                        TLS_ALERT_INTERNAL_ERROR);
 +              return -1;
 +      }
 +
 +      return rlen;
 +}
 +
 +
 +/**
 + * tlsv1_client_decrypt - Decrypt data from TLS tunnel
 + * @conn: TLSv1 client connection data from tlsv1_client_init()
 + * @in_data: Pointer to input buffer (encrypted TLS data)
 + * @in_len: Input buffer length
 + * @need_more_data: Set to 1 if more data would be needed to complete
 + *    processing
 + * Returns: Decrypted data or %NULL on failure
 + *
 + * This function is used after TLS handshake has been completed successfully to
 + * receive data from the encrypted tunnel.
 + */
 +struct wpabuf * tlsv1_client_decrypt(struct tlsv1_client *conn,
 +                                   const u8 *in_data, size_t in_len,
 +                                   int *need_more_data)
 +{
 +      const u8 *in_end, *pos;
 +      int used;
 +      u8 alert, *out_pos, ct;
 +      size_t olen;
 +      struct wpabuf *buf = NULL;
 +
 +      if (need_more_data)
 +              *need_more_data = 0;
 +
 +      if (conn->partial_input) {
 +              if (wpabuf_resize(&conn->partial_input, in_len) < 0) {
 +                      wpa_printf(MSG_DEBUG, "TLSv1: Failed to allocate "
 +                                 "memory for pending record");
 +                      alert = TLS_ALERT_INTERNAL_ERROR;
 +                      goto fail;
 +              }
 +              wpabuf_put_data(conn->partial_input, in_data, in_len);
 +              in_data = wpabuf_head(conn->partial_input);
 +              in_len = wpabuf_len(conn->partial_input);
 +      }
 +
 +      pos = in_data;
 +      in_end = in_data + in_len;
 +
 +      while (pos < in_end) {
 +              ct = pos[0];
 +              if (wpabuf_resize(&buf, in_end - pos) < 0) {
 +                      alert = TLS_ALERT_INTERNAL_ERROR;
 +                      goto fail;
 +              }
 +              out_pos = wpabuf_put(buf, 0);
 +              olen = wpabuf_tailroom(buf);
 +              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");
 +                      goto fail;
 +              }
 +              if (used == 0) {
 +                      struct wpabuf *partial;
 +                      wpa_printf(MSG_DEBUG, "TLSv1: Need more data");
 +                      partial = wpabuf_alloc_copy(pos, in_end - pos);
 +                      wpabuf_free(conn->partial_input);
 +                      conn->partial_input = partial;
 +                      if (conn->partial_input == NULL) {
 +                              wpa_printf(MSG_DEBUG, "TLSv1: Failed to "
 +                                         "allocate memory for pending "
 +                                         "record");
 +                              alert = TLS_ALERT_INTERNAL_ERROR;
 +                              goto fail;
 +                      }
 +                      if (need_more_data)
 +                              *need_more_data = 1;
 +                      return buf;
 +              }
 +
 +              if (ct == TLS_CONTENT_TYPE_ALERT) {
 +                      if (olen < 2) {
 +                              wpa_printf(MSG_DEBUG, "TLSv1: Alert "
 +                                         "underflow");
 +                              alert = TLS_ALERT_DECODE_ERROR;
 +                              goto fail;
 +                      }
 +                      wpa_printf(MSG_DEBUG, "TLSv1: Received alert %d:%d",
 +                                 out_pos[0], out_pos[1]);
 +                      if (out_pos[0] == TLS_ALERT_LEVEL_WARNING) {
 +                              /* Continue processing */
 +                              pos += used;
 +                              continue;
 +                      }
 +
 +                      alert = out_pos[1];
 +                      goto fail;
 +              }
 +
 +              if (ct != TLS_CONTENT_TYPE_APPLICATION_DATA) {
 +                      wpa_printf(MSG_DEBUG, "TLSv1: Unexpected content type "
 +                                 "0x%x when decrypting application data",
 +                                 pos[0]);
 +                      alert = TLS_ALERT_UNEXPECTED_MESSAGE;
 +                      goto fail;
 +              }
 +
 +              wpabuf_put(buf, olen);
 +
 +              pos += used;
 +      }
 +
 +      wpabuf_free(conn->partial_input);
 +      conn->partial_input = NULL;
 +      return buf;
 +
 +fail:
 +      wpabuf_free(buf);
 +      wpabuf_free(conn->partial_input);
 +      conn->partial_input = NULL;
 +      tls_alert(conn, TLS_ALERT_LEVEL_FATAL, alert);
 +      return NULL;
 +}
 +
 +
 +/**
 + * tlsv1_client_global_init - Initialize TLSv1 client
 + * Returns: 0 on success, -1 on failure
 + *
 + * This function must be called before using any other TLSv1 client functions.
 + */
 +int tlsv1_client_global_init(void)
 +{
 +      return crypto_global_init();
 +}
 +
 +
 +/**
 + * tlsv1_client_global_deinit - Deinitialize TLSv1 client
 + *
 + * This function can be used to deinitialize the TLSv1 client that was
 + * initialized by calling tlsv1_client_global_init(). No TLSv1 client functions
 + * can be called after this before calling tlsv1_client_global_init() again.
 + */
 +void tlsv1_client_global_deinit(void)
 +{
 +      crypto_global_deinit();
 +}
 +
 +
 +/**
 + * tlsv1_client_init - Initialize TLSv1 client connection
 + * Returns: Pointer to TLSv1 client connection data or %NULL on failure
 + */
 +struct tlsv1_client * tlsv1_client_init(void)
 +{
 +      struct tlsv1_client *conn;
 +      size_t count;
 +      u16 *suites;
 +
 +      conn = os_zalloc(sizeof(*conn));
 +      if (conn == NULL)
 +              return NULL;
 +
 +      conn->state = CLIENT_HELLO;
 +
 +      if (tls_verify_hash_init(&conn->verify) < 0) {
 +              wpa_printf(MSG_DEBUG, "TLSv1: Failed to initialize verify "
 +                         "hash");
 +              os_free(conn);
 +              return NULL;
 +      }
 +
 +      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;
 +      conn->num_cipher_suites = count;
 +
 +      conn->rl.tls_version = TLS_VERSION;
 +
 +      return conn;
 +}
 +
 +
 +/**
 + * tlsv1_client_deinit - Deinitialize TLSv1 client connection
 + * @conn: TLSv1 client connection data from tlsv1_client_init()
 + */
 +void tlsv1_client_deinit(struct tlsv1_client *conn)
 +{
 +      crypto_public_key_free(conn->server_rsa_key);
 +      tlsv1_record_set_cipher_suite(&conn->rl, TLS_NULL_WITH_NULL_NULL);
 +      tlsv1_record_change_write_cipher(&conn->rl);
 +      tlsv1_record_change_read_cipher(&conn->rl);
 +      tls_verify_hash_free(&conn->verify);
 +      os_free(conn->client_hello_ext);
 +      tlsv1_client_free_dh(conn);
 +      tlsv1_cred_free(conn->cred);
 +      wpabuf_free(conn->partial_input);
 +      os_free(conn);
 +}
 +
 +
 +/**
 + * tlsv1_client_established - Check whether connection has been established
 + * @conn: TLSv1 client connection data from tlsv1_client_init()
 + * Returns: 1 if connection is established, 0 if not
 + */
 +int tlsv1_client_established(struct tlsv1_client *conn)
 +{
 +      return conn->state == ESTABLISHED;
 +}
 +
 +
 +/**
 + * tlsv1_client_prf - Use TLS-PRF to derive keying material
 + * @conn: TLSv1 client connection data from tlsv1_client_init()
 + * @label: Label (e.g., description of the key) for PRF
 + * @server_random_first: seed is 0 = client_random|server_random,
 + * 1 = server_random|client_random
 + * @out: Buffer for output data from TLS-PRF
 + * @out_len: Length of the output buffer
 + * Returns: 0 on success, -1 on failure
 + */
 +int tlsv1_client_prf(struct tlsv1_client *conn, const char *label,
 +                   int server_random_first, u8 *out, size_t out_len)
 +{
 +      u8 seed[2 * TLS_RANDOM_LEN];
 +
 +      if (conn->state != ESTABLISHED)
 +              return -1;
 +
 +      if (server_random_first) {
 +              os_memcpy(seed, conn->server_random, TLS_RANDOM_LEN);
 +              os_memcpy(seed + TLS_RANDOM_LEN, conn->client_random,
 +                        TLS_RANDOM_LEN);
 +      } else {
 +              os_memcpy(seed, conn->client_random, TLS_RANDOM_LEN);
 +              os_memcpy(seed + TLS_RANDOM_LEN, conn->server_random,
 +                        TLS_RANDOM_LEN);
 +      }
 +
 +      return tls_prf(conn->rl.tls_version,
 +                     conn->master_secret, TLS_MASTER_SECRET_LEN,
 +                     label, seed, 2 * TLS_RANDOM_LEN, out, out_len);
 +}
 +
 +
 +/**
 + * tlsv1_client_get_cipher - Get current cipher name
 + * @conn: TLSv1 client connection data from tlsv1_client_init()
 + * @buf: Buffer for the cipher name
 + * @buflen: buf size
 + * Returns: 0 on success, -1 on failure
 + *
 + * Get the name of the currently used cipher.
 + */
 +int tlsv1_client_get_cipher(struct tlsv1_client *conn, char *buf,
 +                          size_t buflen)
 +{
 +      char *cipher;
 +
 +      switch (conn->rl.cipher_suite) {
 +      case TLS_RSA_WITH_RC4_128_MD5:
 +              cipher = "RC4-MD5";
 +              break;
 +      case TLS_RSA_WITH_RC4_128_SHA:
 +              cipher = "RC4-SHA";
 +              break;
 +      case TLS_RSA_WITH_DES_CBC_SHA:
 +              cipher = "DES-CBC-SHA";
 +              break;
 +      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_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;
 +      }
 +
 +      if (os_strlcpy(buf, cipher, buflen) >= buflen)
 +              return -1;
 +      return 0;
 +}
 +
 +
 +/**
 + * tlsv1_client_shutdown - Shutdown TLS connection
 + * @conn: TLSv1 client connection data from tlsv1_client_init()
 + * Returns: 0 on success, -1 on failure
 + */
 +int tlsv1_client_shutdown(struct tlsv1_client *conn)
 +{
 +      conn->state = CLIENT_HELLO;
 +
 +      if (tls_verify_hash_init(&conn->verify) < 0) {
 +              wpa_printf(MSG_DEBUG, "TLSv1: Failed to re-initialize verify "
 +                         "hash");
 +              return -1;
 +      }
 +
 +      tlsv1_record_set_cipher_suite(&conn->rl, TLS_NULL_WITH_NULL_NULL);
 +      tlsv1_record_change_write_cipher(&conn->rl);
 +      tlsv1_record_change_read_cipher(&conn->rl);
 +
 +      conn->certificate_requested = 0;
 +      crypto_public_key_free(conn->server_rsa_key);
 +      conn->server_rsa_key = NULL;
 +      conn->session_resumed = 0;
 +
 +      return 0;
 +}
 +
 +
 +/**
 + * tlsv1_client_resumed - Was session resumption used
 + * @conn: TLSv1 client connection data from tlsv1_client_init()
 + * Returns: 1 if current session used session resumption, 0 if not
 + */
 +int tlsv1_client_resumed(struct tlsv1_client *conn)
 +{
 +      return !!conn->session_resumed;
 +}
 +
 +
 +/**
 + * tlsv1_client_hello_ext - Set TLS extension for ClientHello
 + * @conn: TLSv1 client connection data from tlsv1_client_init()
 + * @ext_type: Extension type
 + * @data: Extension payload (%NULL to remove extension)
 + * @data_len: Extension payload length
 + * Returns: 0 on success, -1 on failure
 + */
 +int tlsv1_client_hello_ext(struct tlsv1_client *conn, int ext_type,
 +                         const u8 *data, size_t data_len)
 +{
 +      u8 *pos;
 +
 +      conn->session_ticket_included = 0;
 +      os_free(conn->client_hello_ext);
 +      conn->client_hello_ext = NULL;
 +      conn->client_hello_ext_len = 0;
 +
 +      if (data == NULL || data_len == 0)
 +              return 0;
 +
 +      pos = conn->client_hello_ext = os_malloc(6 + data_len);
 +      if (pos == NULL)
 +              return -1;
 +
 +      WPA_PUT_BE16(pos, 4 + data_len);
 +      pos += 2;
 +      WPA_PUT_BE16(pos, ext_type);
 +      pos += 2;
 +      WPA_PUT_BE16(pos, data_len);
 +      pos += 2;
 +      os_memcpy(pos, data, data_len);
 +      conn->client_hello_ext_len = 6 + data_len;
 +
 +      if (ext_type == TLS_EXT_PAC_OPAQUE) {
 +              conn->session_ticket_included = 1;
 +              wpa_printf(MSG_DEBUG, "TLSv1: Using session ticket");
 +      }
 +
 +      return 0;
 +}
 +
 +
 +/**
-  * @keys: Structure of key/random data (filled on success)
++ * tlsv1_client_get_random - Get random data from TLS connection
 + * @conn: TLSv1 client connection data from tlsv1_client_init()
- int tlsv1_client_get_keys(struct tlsv1_client *conn, struct tls_keys *keys)
++ * @keys: Structure of random data (filled on success)
 + * Returns: 0 on success, -1 on failure
 + */
-               keys->master_key = conn->master_secret;
-               keys->master_key_len = TLS_MASTER_SECRET_LEN;
++int tlsv1_client_get_random(struct tlsv1_client *conn, struct tls_random *keys)
 +{
 +      os_memset(keys, 0, sizeof(*keys));
 +      if (conn->state == CLIENT_HELLO)
 +              return -1;
 +
 +      keys->client_random = conn->client_random;
 +      keys->client_random_len = TLS_RANDOM_LEN;
 +
 +      if (conn->state != SERVER_HELLO) {
 +              keys->server_random = conn->server_random;
 +              keys->server_random_len = TLS_RANDOM_LEN;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +/**
 + * tlsv1_client_get_keyblock_size - Get TLS key_block size
 + * @conn: TLSv1 client connection data from tlsv1_client_init()
 + * Returns: Size of the key_block for the negotiated cipher suite or -1 on
 + * failure
 + */
 +int tlsv1_client_get_keyblock_size(struct tlsv1_client *conn)
 +{
 +      if (conn->state == CLIENT_HELLO || conn->state == SERVER_HELLO)
 +              return -1;
 +
 +      return 2 * (conn->rl.hash_size + conn->rl.key_material_len +
 +                  conn->rl.iv_size);
 +}
 +
 +
 +/**
 + * tlsv1_client_set_cipher_list - Configure acceptable cipher suites
 + * @conn: TLSv1 client connection data from tlsv1_client_init()
 + * @ciphers: Zero (TLS_CIPHER_NONE) terminated list of allowed ciphers
 + * (TLS_CIPHER_*).
 + * Returns: 0 on success, -1 on failure
 + */
 +int tlsv1_client_set_cipher_list(struct tlsv1_client *conn, u8 *ciphers)
 +{
 +      size_t count;
 +      u16 *suites;
 +
 +      /* TODO: implement proper configuration of cipher suites */
 +      if (ciphers[0] == TLS_CIPHER_ANON_DH_AES128_SHA) {
 +              count = 0;
 +              suites = conn->cipher_suites;
 +              suites[count++] = TLS_DH_anon_WITH_AES_256_CBC_SHA256;
 +              suites[count++] = TLS_DH_anon_WITH_AES_256_CBC_SHA;
 +              suites[count++] = TLS_DH_anon_WITH_AES_128_CBC_SHA256;
 +              suites[count++] = TLS_DH_anon_WITH_AES_128_CBC_SHA;
 +              suites[count++] = TLS_DH_anon_WITH_3DES_EDE_CBC_SHA;
 +              suites[count++] = TLS_DH_anon_WITH_RC4_128_MD5;
 +              suites[count++] = TLS_DH_anon_WITH_DES_CBC_SHA;
 +
 +              /*
 +               * Cisco AP (at least 350 and 1200 series) local authentication
 +               * server does not know how to search cipher suites from the
 +               * list and seem to require that the last entry in the list is
 +               * the one that it wants to use. However, TLS specification
 +               * requires the list to be in the client preference order. As a
 +               * workaround, add anon-DH AES-128-SHA1 again at the end of the
 +               * list to allow the Cisco code to find it.
 +               */
 +              suites[count++] = TLS_DH_anon_WITH_AES_128_CBC_SHA;
 +              conn->num_cipher_suites = count;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +/**
 + * tlsv1_client_set_cred - Set client credentials
 + * @conn: TLSv1 client connection data from tlsv1_client_init()
 + * @cred: Credentials from tlsv1_cred_alloc()
 + * Returns: 0 on success, -1 on failure
 + *
 + * On success, the client takes ownership of the credentials block and caller
 + * must not free it. On failure, caller is responsible for freeing the
 + * credential block.
 + */
 +int tlsv1_client_set_cred(struct tlsv1_client *conn,
 +                        struct tlsv1_credentials *cred)
 +{
 +      tlsv1_cred_free(conn->cred);
 +      conn->cred = cred;
 +      return 0;
 +}
 +
 +
 +void tlsv1_client_set_time_checks(struct tlsv1_client *conn, int enabled)
 +{
 +      conn->disable_time_checks = !enabled;
 +}
 +
 +
 +void tlsv1_client_set_session_ticket_cb(struct tlsv1_client *conn,
 +                                      tlsv1_client_session_ticket_cb cb,
 +                                      void *ctx)
 +{
 +      wpa_printf(MSG_DEBUG, "TLSv1: SessionTicket callback set %p (ctx %p)",
 +                 cb, ctx);
 +      conn->session_ticket_cb = cb;
 +      conn->session_ticket_cb_ctx = ctx;
 +}
index 8ec85f1a9193601cdd1a8b3d0fbc6bac5f7879d0,0000000000000000000000000000000000000000..a4e25e969937c6e3c2c578099ea2d2c98254ad48
mode 100644,000000..100644
--- /dev/null
@@@ -1,54 -1,0 +1,54 @@@
- int tlsv1_client_get_keys(struct tlsv1_client *conn, struct tls_keys *keys);
 +/*
 + * TLS v1.0/v1.1/v1.2 client (RFC 2246, RFC 4346, RFC 5246)
 + * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
 + *
 + * This software may be distributed under the terms of the BSD license.
 + * See README for more details.
 + */
 +
 +#ifndef TLSV1_CLIENT_H
 +#define TLSV1_CLIENT_H
 +
 +#include "tlsv1_cred.h"
 +
 +struct tlsv1_client;
 +
 +int tlsv1_client_global_init(void);
 +void tlsv1_client_global_deinit(void);
 +struct tlsv1_client * tlsv1_client_init(void);
 +void tlsv1_client_deinit(struct tlsv1_client *conn);
 +int tlsv1_client_established(struct tlsv1_client *conn);
 +int tlsv1_client_prf(struct tlsv1_client *conn, const char *label,
 +                   int server_random_first, u8 *out, size_t out_len);
 +u8 * tlsv1_client_handshake(struct tlsv1_client *conn,
 +                          const u8 *in_data, size_t in_len,
 +                          size_t *out_len, u8 **appl_data,
 +                          size_t *appl_data_len, int *need_more_data);
 +int tlsv1_client_encrypt(struct tlsv1_client *conn,
 +                       const u8 *in_data, size_t in_len,
 +                       u8 *out_data, size_t out_len);
 +struct wpabuf * tlsv1_client_decrypt(struct tlsv1_client *conn,
 +                                   const u8 *in_data, size_t in_len,
 +                                   int *need_more_data);
 +int tlsv1_client_get_cipher(struct tlsv1_client *conn, char *buf,
 +                          size_t buflen);
 +int tlsv1_client_shutdown(struct tlsv1_client *conn);
 +int tlsv1_client_resumed(struct tlsv1_client *conn);
 +int tlsv1_client_hello_ext(struct tlsv1_client *conn, int ext_type,
 +                         const u8 *data, size_t data_len);
++int tlsv1_client_get_random(struct tlsv1_client *conn, struct tls_random *data);
 +int tlsv1_client_get_keyblock_size(struct tlsv1_client *conn);
 +int tlsv1_client_set_cipher_list(struct tlsv1_client *conn, u8 *ciphers);
 +int tlsv1_client_set_cred(struct tlsv1_client *conn,
 +                        struct tlsv1_credentials *cred);
 +void tlsv1_client_set_time_checks(struct tlsv1_client *conn, int enabled);
 +
 +typedef int (*tlsv1_client_session_ticket_cb)
 +(void *ctx, const u8 *ticket, size_t len, const u8 *client_random,
 + const u8 *server_random, u8 *master_secret);
 +
 +void tlsv1_client_set_session_ticket_cb(struct tlsv1_client *conn,
 +                                      tlsv1_client_session_ticket_cb cb,
 +                                      void *ctx);
 +
 +#endif /* TLSV1_CLIENT_H */
index 93ae4888d89804da357dc4ab332662bd73e08759,0000000000000000000000000000000000000000..ba47337bcbb1187f6e44791609e39fb5c4811106
mode 100644,000000..100644
--- /dev/null
@@@ -1,824 -1,0 +1,822 @@@
-  * tlsv1_server_get_keys - Get master key and random data from TLS connection
 +/*
 + * TLS v1.0/v1.1/v1.2 server (RFC 2246, RFC 4346, RFC 5246)
 + * 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 "includes.h"
 +
 +#include "common.h"
 +#include "crypto/sha1.h"
 +#include "crypto/tls.h"
 +#include "tlsv1_common.h"
 +#include "tlsv1_record.h"
 +#include "tlsv1_server.h"
 +#include "tlsv1_server_i.h"
 +
 +/* TODO:
 + * Support for a message fragmented across several records (RFC 2246, 6.2.1)
 + */
 +
 +
 +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;
 +      conn->alert_description = description;
 +}
 +
 +
 +int tlsv1_server_derive_keys(struct tlsv1_server *conn,
 +                           const u8 *pre_master_secret,
 +                           size_t pre_master_secret_len)
 +{
 +      u8 seed[2 * TLS_RANDOM_LEN];
 +      u8 key_block[TLS_MAX_KEY_BLOCK_LEN];
 +      u8 *pos;
 +      size_t key_block_len;
 +
 +      if (pre_master_secret) {
 +              wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: pre_master_secret",
 +                              pre_master_secret, pre_master_secret_len);
 +              os_memcpy(seed, conn->client_random, TLS_RANDOM_LEN);
 +              os_memcpy(seed + TLS_RANDOM_LEN, conn->server_random,
 +                        TLS_RANDOM_LEN);
 +              if (tls_prf(conn->rl.tls_version,
 +                          pre_master_secret, pre_master_secret_len,
 +                          "master secret", seed, 2 * TLS_RANDOM_LEN,
 +                          conn->master_secret, TLS_MASTER_SECRET_LEN)) {
 +                      wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive "
 +                                 "master_secret");
 +                      return -1;
 +              }
 +              wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: master_secret",
 +                              conn->master_secret, TLS_MASTER_SECRET_LEN);
 +      }
 +
 +      os_memcpy(seed, conn->server_random, TLS_RANDOM_LEN);
 +      os_memcpy(seed + TLS_RANDOM_LEN, conn->client_random, TLS_RANDOM_LEN);
 +      key_block_len = 2 * (conn->rl.hash_size + conn->rl.key_material_len +
 +                           conn->rl.iv_size);
 +      if (tls_prf(conn->rl.tls_version,
 +                  conn->master_secret, TLS_MASTER_SECRET_LEN,
 +                  "key expansion", seed, 2 * TLS_RANDOM_LEN,
 +                  key_block, key_block_len)) {
 +              wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive key_block");
 +              return -1;
 +      }
 +      wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: key_block",
 +                      key_block, key_block_len);
 +
 +      pos = key_block;
 +
 +      /* client_write_MAC_secret */
 +      os_memcpy(conn->rl.read_mac_secret, pos, conn->rl.hash_size);
 +      pos += conn->rl.hash_size;
 +      /* server_write_MAC_secret */
 +      os_memcpy(conn->rl.write_mac_secret, pos, conn->rl.hash_size);
 +      pos += conn->rl.hash_size;
 +
 +      /* client_write_key */
 +      os_memcpy(conn->rl.read_key, pos, conn->rl.key_material_len);
 +      pos += conn->rl.key_material_len;
 +      /* server_write_key */
 +      os_memcpy(conn->rl.write_key, pos, conn->rl.key_material_len);
 +      pos += conn->rl.key_material_len;
 +
 +      /* client_write_IV */
 +      os_memcpy(conn->rl.read_iv, pos, conn->rl.iv_size);
 +      pos += conn->rl.iv_size;
 +      /* server_write_IV */
 +      os_memcpy(conn->rl.write_iv, pos, conn->rl.iv_size);
 +      pos += conn->rl.iv_size;
 +
 +      return 0;
 +}
 +
 +
 +/**
 + * tlsv1_server_handshake - Process TLS handshake
 + * @conn: TLSv1 server connection data from tlsv1_server_init()
 + * @in_data: Input data from TLS peer
 + * @in_len: Input data length
 + * @out_len: Length of the output buffer.
 + * Returns: Pointer to output data, %NULL on failure
 + */
 +u8 * tlsv1_server_handshake(struct tlsv1_server *conn,
 +                          const u8 *in_data, size_t in_len,
 +                          size_t *out_len)
 +{
 +      const u8 *pos, *end;
 +      u8 *msg = NULL, *in_msg, *in_pos, *in_end, alert, ct;
 +      size_t in_msg_len;
 +      int used;
 +
 +      if (in_data == NULL || in_len == 0) {
 +              wpa_printf(MSG_DEBUG, "TLSv1: No input data to server");
 +              return NULL;
 +      }
 +
 +      pos = in_data;
 +      end = in_data + in_len;
 +      in_msg = os_malloc(in_len);
 +      if (in_msg == NULL)
 +              return NULL;
 +
 +      /* Each received packet may include multiple records */
 +      while (pos < end) {
 +              in_msg_len = in_len;
 +              used = tlsv1_record_receive(&conn->rl, pos, end - pos,
 +                                          in_msg, &in_msg_len, &alert);
 +              if (used < 0) {
 +                      wpa_printf(MSG_DEBUG, "TLSv1: Processing received "
 +                                 "record failed");
 +                      tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, alert);
 +                      goto failed;
 +              }
 +              if (used == 0) {
 +                      /* need more data */
 +                      wpa_printf(MSG_DEBUG, "TLSv1: Partial processing not "
 +                                 "yet supported");
 +                      tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, alert);
 +                      goto failed;
 +              }
 +              ct = pos[0];
 +
 +              in_pos = in_msg;
 +              in_end = in_msg + in_msg_len;
 +
 +              /* Each received record may include multiple messages of the
 +               * same ContentType. */
 +              while (in_pos < in_end) {
 +                      in_msg_len = in_end - in_pos;
 +                      if (tlsv1_server_process_handshake(conn, ct, in_pos,
 +                                                         &in_msg_len) < 0)
 +                              goto failed;
 +                      in_pos += in_msg_len;
 +              }
 +
 +              pos += used;
 +      }
 +
 +      os_free(in_msg);
 +      in_msg = NULL;
 +
 +      msg = tlsv1_server_handshake_write(conn, out_len);
 +
 +failed:
 +      os_free(in_msg);
 +      if (conn->alert_level) {
 +              if (conn->state == FAILED) {
 +                      /* Avoid alert loops */
 +                      wpa_printf(MSG_DEBUG, "TLSv1: Drop alert loop");
 +                      os_free(msg);
 +                      return NULL;
 +              }
 +              conn->state = FAILED;
 +              os_free(msg);
 +              msg = tlsv1_server_send_alert(conn, conn->alert_level,
 +                                            conn->alert_description,
 +                                            out_len);
 +      }
 +
 +      return msg;
 +}
 +
 +
 +/**
 + * tlsv1_server_encrypt - Encrypt data into TLS tunnel
 + * @conn: TLSv1 server connection data from tlsv1_server_init()
 + * @in_data: Pointer to plaintext data to be encrypted
 + * @in_len: Input buffer length
 + * @out_data: Pointer to output buffer (encrypted TLS data)
 + * @out_len: Maximum out_data length 
 + * Returns: Number of bytes written to out_data, -1 on failure
 + *
 + * This function is used after TLS handshake has been completed successfully to
 + * send data in the encrypted tunnel.
 + */
 +int tlsv1_server_encrypt(struct tlsv1_server *conn,
 +                       const u8 *in_data, size_t in_len,
 +                       u8 *out_data, size_t out_len)
 +{
 +      size_t rlen;
 +
 +      wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Plaintext AppData",
 +                      in_data, in_len);
 +
 +      if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_APPLICATION_DATA,
 +                            out_data, out_len, in_data, in_len, &rlen) < 0) {
 +              wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record");
 +              tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
 +                                 TLS_ALERT_INTERNAL_ERROR);
 +              return -1;
 +      }
 +
 +      return rlen;
 +}
 +
 +
 +/**
 + * tlsv1_server_decrypt - Decrypt data from TLS tunnel
 + * @conn: TLSv1 server connection data from tlsv1_server_init()
 + * @in_data: Pointer to input buffer (encrypted TLS data)
 + * @in_len: Input buffer length
 + * @out_data: Pointer to output buffer (decrypted data from TLS tunnel)
 + * @out_len: Maximum out_data length
 + * Returns: Number of bytes written to out_data, -1 on failure
 + *
 + * This function is used after TLS handshake has been completed successfully to
 + * receive data from the encrypted tunnel.
 + */
 +int tlsv1_server_decrypt(struct tlsv1_server *conn,
 +                       const u8 *in_data, size_t in_len,
 +                       u8 *out_data, size_t out_len)
 +{
 +      const u8 *in_end, *pos;
 +      int used;
 +      u8 alert, *out_end, *out_pos, ct;
 +      size_t olen;
 +
 +      pos = in_data;
 +      in_end = in_data + in_len;
 +      out_pos = out_data;
 +      out_end = out_data + out_len;
 +
 +      while (pos < in_end) {
 +              ct = pos[0];
 +              olen = out_end - out_pos;
 +              used = tlsv1_record_receive(&conn->rl, pos, in_end - pos,
 +                                          out_pos, &olen, &alert);
 +              if (used < 0) {
 +                      tlsv1_server_log(conn, "Record layer processing failed");
 +                      tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, alert);
 +                      return -1;
 +              }
 +              if (used == 0) {
 +                      /* need more data */
 +                      wpa_printf(MSG_DEBUG, "TLSv1: Partial processing not "
 +                                 "yet supported");
 +                      tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, alert);
 +                      return -1;
 +              }
 +
 +              if (ct == TLS_CONTENT_TYPE_ALERT) {
 +                      if (olen < 2) {
 +                              tlsv1_server_log(conn, "Alert underflow");
 +                              tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
 +                                                 TLS_ALERT_DECODE_ERROR);
 +                              return -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;
 +                              continue;
 +                      }
 +
 +                      tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
 +                                         out_pos[1]);
 +                      return -1;
 +              }
 +
 +              if (ct != TLS_CONTENT_TYPE_APPLICATION_DATA) {
 +                      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 "
 +                                 "for processing the received record");
 +                      tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
 +                                         TLS_ALERT_INTERNAL_ERROR);
 +                      return -1;
 +              }
 +
 +              pos += used;
 +      }
 +
 +      return out_pos - out_data;
 +}
 +
 +
 +/**
 + * tlsv1_server_global_init - Initialize TLSv1 server
 + * Returns: 0 on success, -1 on failure
 + *
 + * This function must be called before using any other TLSv1 server functions.
 + */
 +int tlsv1_server_global_init(void)
 +{
 +      return crypto_global_init();
 +}
 +
 +
 +/**
 + * tlsv1_server_global_deinit - Deinitialize TLSv1 server
 + *
 + * This function can be used to deinitialize the TLSv1 server that was
 + * initialized by calling tlsv1_server_global_init(). No TLSv1 server functions
 + * can be called after this before calling tlsv1_server_global_init() again.
 + */
 +void tlsv1_server_global_deinit(void)
 +{
 +      crypto_global_deinit();
 +}
 +
 +
 +/**
 + * tlsv1_server_init - Initialize TLSv1 server connection
 + * @cred: Pointer to server credentials from tlsv1_server_cred_alloc()
 + * Returns: Pointer to TLSv1 server connection data or %NULL on failure
 + */
 +struct tlsv1_server * tlsv1_server_init(struct tlsv1_credentials *cred)
 +{
 +      struct tlsv1_server *conn;
 +      size_t count;
 +      u16 *suites;
 +
 +      conn = os_zalloc(sizeof(*conn));
 +      if (conn == NULL)
 +              return NULL;
 +
 +      conn->cred = cred;
 +
 +      conn->state = CLIENT_HELLO;
 +
 +      if (tls_verify_hash_init(&conn->verify) < 0) {
 +              wpa_printf(MSG_DEBUG, "TLSv1: Failed to initialize verify "
 +                         "hash");
 +              os_free(conn);
 +              return NULL;
 +      }
 +
 +      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;
 +      conn->num_cipher_suites = count;
 +
 +      return conn;
 +}
 +
 +
 +static void tlsv1_server_clear_data(struct tlsv1_server *conn)
 +{
 +      tlsv1_record_set_cipher_suite(&conn->rl, TLS_NULL_WITH_NULL_NULL);
 +      tlsv1_record_change_write_cipher(&conn->rl);
 +      tlsv1_record_change_read_cipher(&conn->rl);
 +      tls_verify_hash_free(&conn->verify);
 +
 +      crypto_public_key_free(conn->client_rsa_key);
 +      conn->client_rsa_key = NULL;
 +
 +      os_free(conn->session_ticket);
 +      conn->session_ticket = NULL;
 +      conn->session_ticket_len = 0;
 +      conn->use_session_ticket = 0;
 +
 +      os_free(conn->dh_secret);
 +      conn->dh_secret = NULL;
 +      conn->dh_secret_len = 0;
 +}
 +
 +
 +/**
 + * tlsv1_server_deinit - Deinitialize TLSv1 server connection
 + * @conn: TLSv1 server connection data from tlsv1_server_init()
 + */
 +void tlsv1_server_deinit(struct tlsv1_server *conn)
 +{
 +      tlsv1_server_clear_data(conn);
 +      os_free(conn);
 +}
 +
 +
 +/**
 + * tlsv1_server_established - Check whether connection has been established
 + * @conn: TLSv1 server connection data from tlsv1_server_init()
 + * Returns: 1 if connection is established, 0 if not
 + */
 +int tlsv1_server_established(struct tlsv1_server *conn)
 +{
 +      return conn->state == ESTABLISHED;
 +}
 +
 +
 +/**
 + * tlsv1_server_prf - Use TLS-PRF to derive keying material
 + * @conn: TLSv1 server connection data from tlsv1_server_init()
 + * @label: Label (e.g., description of the key) for PRF
 + * @server_random_first: seed is 0 = client_random|server_random,
 + * 1 = server_random|client_random
 + * @out: Buffer for output data from TLS-PRF
 + * @out_len: Length of the output buffer
 + * Returns: 0 on success, -1 on failure
 + */
 +int tlsv1_server_prf(struct tlsv1_server *conn, const char *label,
 +                   int server_random_first, u8 *out, size_t out_len)
 +{
 +      u8 seed[2 * TLS_RANDOM_LEN];
 +
 +      if (conn->state != ESTABLISHED)
 +              return -1;
 +
 +      if (server_random_first) {
 +              os_memcpy(seed, conn->server_random, TLS_RANDOM_LEN);
 +              os_memcpy(seed + TLS_RANDOM_LEN, conn->client_random,
 +                        TLS_RANDOM_LEN);
 +      } else {
 +              os_memcpy(seed, conn->client_random, TLS_RANDOM_LEN);
 +              os_memcpy(seed + TLS_RANDOM_LEN, conn->server_random,
 +                        TLS_RANDOM_LEN);
 +      }
 +
 +      return tls_prf(conn->rl.tls_version,
 +                     conn->master_secret, TLS_MASTER_SECRET_LEN,
 +                     label, seed, 2 * TLS_RANDOM_LEN, out, out_len);
 +}
 +
 +
 +/**
 + * tlsv1_server_get_cipher - Get current cipher name
 + * @conn: TLSv1 server connection data from tlsv1_server_init()
 + * @buf: Buffer for the cipher name
 + * @buflen: buf size
 + * Returns: 0 on success, -1 on failure
 + *
 + * Get the name of the currently used cipher.
 + */
 +int tlsv1_server_get_cipher(struct tlsv1_server *conn, char *buf,
 +                          size_t buflen)
 +{
 +      char *cipher;
 +
 +      switch (conn->rl.cipher_suite) {
 +      case TLS_RSA_WITH_RC4_128_MD5:
 +              cipher = "RC4-MD5";
 +              break;
 +      case TLS_RSA_WITH_RC4_128_SHA:
 +              cipher = "RC4-SHA";
 +              break;
 +      case TLS_RSA_WITH_DES_CBC_SHA:
 +              cipher = "DES-CBC-SHA";
 +              break;
 +      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_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;
 +      }
 +
 +      if (os_strlcpy(buf, cipher, buflen) >= buflen)
 +              return -1;
 +      return 0;
 +}
 +
 +
 +/**
 + * tlsv1_server_shutdown - Shutdown TLS connection
 + * @conn: TLSv1 server connection data from tlsv1_server_init()
 + * Returns: 0 on success, -1 on failure
 + */
 +int tlsv1_server_shutdown(struct tlsv1_server *conn)
 +{
 +      conn->state = CLIENT_HELLO;
 +
 +      if (tls_verify_hash_init(&conn->verify) < 0) {
 +              wpa_printf(MSG_DEBUG, "TLSv1: Failed to re-initialize verify "
 +                         "hash");
 +              return -1;
 +      }
 +
 +      tlsv1_server_clear_data(conn);
 +
 +      return 0;
 +}
 +
 +
 +/**
 + * tlsv1_server_resumed - Was session resumption used
 + * @conn: TLSv1 server connection data from tlsv1_server_init()
 + * Returns: 1 if current session used session resumption, 0 if not
 + */
 +int tlsv1_server_resumed(struct tlsv1_server *conn)
 +{
 +      return 0;
 +}
 +
 +
 +/**
-  * @keys: Structure of key/random data (filled on success)
++ * tlsv1_server_get_random - Get random data from TLS connection
 + * @conn: TLSv1 server connection data from tlsv1_server_init()
- int tlsv1_server_get_keys(struct tlsv1_server *conn, struct tls_keys *keys)
++ * @keys: Structure of random data (filled on success)
 + * Returns: 0 on success, -1 on failure
 + */
-               keys->master_key = conn->master_secret;
-               keys->master_key_len = TLS_MASTER_SECRET_LEN;
++int tlsv1_server_get_random(struct tlsv1_server *conn, struct tls_random *keys)
 +{
 +      os_memset(keys, 0, sizeof(*keys));
 +      if (conn->state == CLIENT_HELLO)
 +              return -1;
 +
 +      keys->client_random = conn->client_random;
 +      keys->client_random_len = TLS_RANDOM_LEN;
 +
 +      if (conn->state != SERVER_HELLO) {
 +              keys->server_random = conn->server_random;
 +              keys->server_random_len = TLS_RANDOM_LEN;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +/**
 + * tlsv1_server_get_keyblock_size - Get TLS key_block size
 + * @conn: TLSv1 server connection data from tlsv1_server_init()
 + * Returns: Size of the key_block for the negotiated cipher suite or -1 on
 + * failure
 + */
 +int tlsv1_server_get_keyblock_size(struct tlsv1_server *conn)
 +{
 +      if (conn->state == CLIENT_HELLO || conn->state == SERVER_HELLO)
 +              return -1;
 +
 +      return 2 * (conn->rl.hash_size + conn->rl.key_material_len +
 +                  conn->rl.iv_size);
 +}
 +
 +
 +/**
 + * tlsv1_server_set_cipher_list - Configure acceptable cipher suites
 + * @conn: TLSv1 server connection data from tlsv1_server_init()
 + * @ciphers: Zero (TLS_CIPHER_NONE) terminated list of allowed ciphers
 + * (TLS_CIPHER_*).
 + * Returns: 0 on success, -1 on failure
 + */
 +int tlsv1_server_set_cipher_list(struct tlsv1_server *conn, u8 *ciphers)
 +{
 +      size_t count;
 +      u16 *suites;
 +
 +      /* TODO: implement proper configuration of cipher suites */
 +      if (ciphers[0] == TLS_CIPHER_ANON_DH_AES128_SHA) {
 +              count = 0;
 +              suites = conn->cipher_suites;
 +              suites[count++] = TLS_RSA_WITH_AES_256_CBC_SHA;
 +              suites[count++] = TLS_RSA_WITH_AES_128_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;
 +              suites[count++] = TLS_DH_anon_WITH_AES_256_CBC_SHA;
 +              suites[count++] = TLS_DH_anon_WITH_AES_128_CBC_SHA;
 +              suites[count++] = TLS_DH_anon_WITH_3DES_EDE_CBC_SHA;
 +              suites[count++] = TLS_DH_anon_WITH_RC4_128_MD5;
 +              suites[count++] = TLS_DH_anon_WITH_DES_CBC_SHA;
 +              conn->num_cipher_suites = count;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +int tlsv1_server_set_verify(struct tlsv1_server *conn, int verify_peer)
 +{
 +      conn->verify_peer = verify_peer;
 +      return 0;
 +}
 +
 +
 +void tlsv1_server_set_session_ticket_cb(struct tlsv1_server *conn,
 +                                      tlsv1_server_session_ticket_cb cb,
 +                                      void *ctx)
 +{
 +      wpa_printf(MSG_DEBUG, "TLSv1: SessionTicket callback set %p (ctx %p)",
 +                 cb, ctx);
 +      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 b2b28d1e1215a00b434a5f75ad8d9a819ad7acf3,0000000000000000000000000000000000000000..10e7699312b06fe80dbcc3b16a87a130af8e1c8a
mode 100644,000000..100644
--- /dev/null
@@@ -1,53 -1,0 +1,53 @@@
- int tlsv1_server_get_keys(struct tlsv1_server *conn, struct tls_keys *keys);
 +/*
 + * TLS v1.0/v1.1/v1.2 server (RFC 2246, RFC 4346, RFC 5246)
 + * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
 + *
 + * This software may be distributed under the terms of the BSD license.
 + * See README for more details.
 + */
 +
 +#ifndef TLSV1_SERVER_H
 +#define TLSV1_SERVER_H
 +
 +#include "tlsv1_cred.h"
 +
 +struct tlsv1_server;
 +
 +int tlsv1_server_global_init(void);
 +void tlsv1_server_global_deinit(void);
 +struct tlsv1_server * tlsv1_server_init(struct tlsv1_credentials *cred);
 +void tlsv1_server_deinit(struct tlsv1_server *conn);
 +int tlsv1_server_established(struct tlsv1_server *conn);
 +int tlsv1_server_prf(struct tlsv1_server *conn, const char *label,
 +                   int server_random_first, u8 *out, size_t out_len);
 +u8 * tlsv1_server_handshake(struct tlsv1_server *conn,
 +                          const u8 *in_data, size_t in_len, size_t *out_len);
 +int tlsv1_server_encrypt(struct tlsv1_server *conn,
 +                       const u8 *in_data, size_t in_len,
 +                       u8 *out_data, size_t out_len);
 +int tlsv1_server_decrypt(struct tlsv1_server *conn,
 +                       const u8 *in_data, size_t in_len,
 +                       u8 *out_data, size_t out_len);
 +int tlsv1_server_get_cipher(struct tlsv1_server *conn, char *buf,
 +                          size_t buflen);
 +int tlsv1_server_shutdown(struct tlsv1_server *conn);
 +int tlsv1_server_resumed(struct tlsv1_server *conn);
++int tlsv1_server_get_random(struct tlsv1_server *conn, struct tls_random *data);
 +int tlsv1_server_get_keyblock_size(struct tlsv1_server *conn);
 +int tlsv1_server_set_cipher_list(struct tlsv1_server *conn, u8 *ciphers);
 +int tlsv1_server_set_verify(struct tlsv1_server *conn, int verify_peer);
 +
 +typedef int (*tlsv1_server_session_ticket_cb)
 +(void *ctx, const u8 *ticket, size_t len, const u8 *client_random,
 + const u8 *server_random, u8 *master_secret);
 +
 +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 742af328cf8cc019f557ab280a424e7ef9715c6c,0000000000000000000000000000000000000000..b51dfcd44732027ba7f6eb8f9a980e208a8f1c43
mode 100644,000000..100644
--- /dev/null
@@@ -1,1990 -1,0 +1,1990 @@@
-                           pos + hdr.length, end - pos + hdr.length);
 +/*
 + * X.509v3 certificate parsing and processing (RFC 3280 profile)
 + * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
 + *
 + * This software may be distributed under the terms of the BSD license.
 + * See README for more details.
 + */
 +
 +#include "includes.h"
 +
 +#include "common.h"
 +#include "crypto/crypto.h"
 +#include "asn1.h"
 +#include "x509v3.h"
 +
 +
 +static void x509_free_name(struct x509_name *name)
 +{
 +      size_t i;
 +
 +      for (i = 0; i < name->num_attr; i++) {
 +              os_free(name->attr[i].value);
 +              name->attr[i].value = NULL;
 +              name->attr[i].type = X509_NAME_ATTR_NOT_USED;
 +      }
 +      name->num_attr = 0;
 +      os_free(name->email);
 +      name->email = NULL;
 +
 +      os_free(name->alt_email);
 +      os_free(name->dns);
 +      os_free(name->uri);
 +      os_free(name->ip);
 +      name->alt_email = name->dns = name->uri = NULL;
 +      name->ip = NULL;
 +      name->ip_len = 0;
 +      os_memset(&name->rid, 0, sizeof(name->rid));
 +}
 +
 +
 +/**
 + * x509_certificate_free - Free an X.509 certificate
 + * @cert: Certificate to be freed
 + */
 +void x509_certificate_free(struct x509_certificate *cert)
 +{
 +      if (cert == NULL)
 +              return;
 +      if (cert->next) {
 +              wpa_printf(MSG_DEBUG, "X509: x509_certificate_free: cer=%p "
 +                         "was still on a list (next=%p)\n",
 +                         cert, cert->next);
 +      }
 +      x509_free_name(&cert->issuer);
 +      x509_free_name(&cert->subject);
 +      os_free(cert->public_key);
 +      os_free(cert->sign_value);
 +      os_free(cert);
 +}
 +
 +
 +/**
 + * x509_certificate_free - Free an X.509 certificate chain
 + * @cert: Pointer to the first certificate in the chain
 + */
 +void x509_certificate_chain_free(struct x509_certificate *cert)
 +{
 +      struct x509_certificate *next;
 +
 +      while (cert) {
 +              next = cert->next;
 +              cert->next = NULL;
 +              x509_certificate_free(cert);
 +              cert = next;
 +      }
 +}
 +
 +
 +static int x509_whitespace(char c)
 +{
 +      return c == ' ' || c == '\t';
 +}
 +
 +
 +static void x509_str_strip_whitespace(char *a)
 +{
 +      char *ipos, *opos;
 +      int remove_whitespace = 1;
 +
 +      ipos = opos = a;
 +
 +      while (*ipos) {
 +              if (remove_whitespace && x509_whitespace(*ipos))
 +                      ipos++;
 +              else {
 +                      remove_whitespace = x509_whitespace(*ipos);
 +                      *opos++ = *ipos++;
 +              }
 +      }
 +
 +      *opos-- = '\0';
 +      if (opos > a && x509_whitespace(*opos))
 +              *opos = '\0';
 +}
 +
 +
 +static int x509_str_compare(const char *a, const char *b)
 +{
 +      char *aa, *bb;
 +      int ret;
 +
 +      if (!a && b)
 +              return -1;
 +      if (a && !b)
 +              return 1;
 +      if (!a && !b)
 +              return 0;
 +
 +      aa = os_strdup(a);
 +      bb = os_strdup(b);
 +
 +      if (aa == NULL || bb == NULL) {
 +              os_free(aa);
 +              os_free(bb);
 +              return os_strcasecmp(a, b);
 +      }
 +
 +      x509_str_strip_whitespace(aa);
 +      x509_str_strip_whitespace(bb);
 +
 +      ret = os_strcasecmp(aa, bb);
 +
 +      os_free(aa);
 +      os_free(bb);
 +
 +      return ret;
 +}
 +
 +
 +/**
 + * x509_name_compare - Compare X.509 certificate names
 + * @a: Certificate name
 + * @b: Certificate name
 + * Returns: <0, 0, or >0 based on whether a is less than, equal to, or
 + * greater than b
 + */
 +int x509_name_compare(struct x509_name *a, struct x509_name *b)
 +{
 +      int res;
 +      size_t i;
 +
 +      if (!a && b)
 +              return -1;
 +      if (a && !b)
 +              return 1;
 +      if (!a && !b)
 +              return 0;
 +      if (a->num_attr < b->num_attr)
 +              return -1;
 +      if (a->num_attr > b->num_attr)
 +              return 1;
 +
 +      for (i = 0; i < a->num_attr; i++) {
 +              if (a->attr[i].type < b->attr[i].type)
 +                      return -1;
 +              if (a->attr[i].type > b->attr[i].type)
 +                      return -1;
 +              res = x509_str_compare(a->attr[i].value, b->attr[i].value);
 +              if (res)
 +                      return res;
 +      }
 +      res = x509_str_compare(a->email, b->email);
 +      if (res)
 +              return res;
 +
 +      return 0;
 +}
 +
 +
 +static int x509_parse_algorithm_identifier(
 +      const u8 *buf, size_t len,
 +      struct x509_algorithm_identifier *id, const u8 **next)
 +{
 +      struct asn1_hdr hdr;
 +      const u8 *pos, *end;
 +
 +      /*
 +       * AlgorithmIdentifier ::= SEQUENCE {
 +       *     algorithm            OBJECT IDENTIFIER,
 +       *     parameters           ANY DEFINED BY algorithm OPTIONAL
 +       * }
 +       */
 +
 +      if (asn1_get_next(buf, len, &hdr) < 0 ||
 +          hdr.class != ASN1_CLASS_UNIVERSAL ||
 +          hdr.tag != ASN1_TAG_SEQUENCE) {
 +              wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE "
 +                         "(AlgorithmIdentifier) - found class %d tag 0x%x",
 +                         hdr.class, hdr.tag);
 +              return -1;
 +      }
 +      pos = hdr.payload;
 +      end = pos + hdr.length;
 +
 +      if (end > buf + len)
 +              return -1;
 +
 +      *next = end;
 +
 +      if (asn1_get_oid(pos, end - pos, &id->oid, &pos))
 +              return -1;
 +
 +      /* TODO: optional parameters */
 +
 +      return 0;
 +}
 +
 +
 +static int x509_parse_public_key(const u8 *buf, size_t len,
 +                               struct x509_certificate *cert,
 +                               const u8 **next)
 +{
 +      struct asn1_hdr hdr;
 +      const u8 *pos, *end;
 +
 +      /*
 +       * SubjectPublicKeyInfo ::= SEQUENCE {
 +       *     algorithm            AlgorithmIdentifier,
 +       *     subjectPublicKey     BIT STRING
 +       * }
 +       */
 +
 +      pos = buf;
 +      end = buf + len;
 +
 +      if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
 +          hdr.class != ASN1_CLASS_UNIVERSAL ||
 +          hdr.tag != ASN1_TAG_SEQUENCE) {
 +              wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE "
 +                         "(SubjectPublicKeyInfo) - found class %d tag 0x%x",
 +                         hdr.class, hdr.tag);
 +              return -1;
 +      }
 +      pos = hdr.payload;
 +
 +      if (pos + hdr.length > end)
 +              return -1;
 +      end = pos + hdr.length;
 +      *next = end;
 +
 +      if (x509_parse_algorithm_identifier(pos, end - pos,
 +                                          &cert->public_key_alg, &pos))
 +              return -1;
 +
 +      if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
 +          hdr.class != ASN1_CLASS_UNIVERSAL ||
 +          hdr.tag != ASN1_TAG_BITSTRING) {
 +              wpa_printf(MSG_DEBUG, "X509: Expected BITSTRING "
 +                         "(subjectPublicKey) - found class %d tag 0x%x",
 +                         hdr.class, hdr.tag);
 +              return -1;
 +      }
 +      if (hdr.length < 1)
 +              return -1;
 +      pos = hdr.payload;
 +      if (*pos) {
 +              wpa_printf(MSG_DEBUG, "X509: BITSTRING - %d unused bits",
 +                         *pos);
 +              /*
 +               * TODO: should this be rejected? X.509 certificates are
 +               * unlikely to use such a construction. Now we would end up
 +               * including the extra bits in the buffer which may also be
 +               * ok.
 +               */
 +      }
 +      os_free(cert->public_key);
 +      cert->public_key = os_malloc(hdr.length - 1);
 +      if (cert->public_key == NULL) {
 +              wpa_printf(MSG_DEBUG, "X509: Failed to allocate memory for "
 +                         "public key");
 +              return -1;
 +      }
 +      os_memcpy(cert->public_key, pos + 1, hdr.length - 1);
 +      cert->public_key_len = hdr.length - 1;
 +      wpa_hexdump(MSG_MSGDUMP, "X509: subjectPublicKey",
 +                  cert->public_key, cert->public_key_len);
 +
 +      return 0;
 +}
 +
 +
 +static int x509_parse_name(const u8 *buf, size_t len, struct x509_name *name,
 +                         const u8 **next)
 +{
 +      struct asn1_hdr hdr;
 +      const u8 *pos, *end, *set_pos, *set_end, *seq_pos, *seq_end;
 +      struct asn1_oid oid;
 +      char *val;
 +
 +      /*
 +       * Name ::= CHOICE { RDNSequence }
 +       * RDNSequence ::= SEQUENCE OF RelativeDistinguishedName
 +       * RelativeDistinguishedName ::= SET OF AttributeTypeAndValue
 +       * AttributeTypeAndValue ::= SEQUENCE {
 +       *     type     AttributeType,
 +       *     value    AttributeValue
 +       * }
 +       * AttributeType ::= OBJECT IDENTIFIER
 +       * AttributeValue ::= ANY DEFINED BY AttributeType
 +       */
 +
 +      if (asn1_get_next(buf, len, &hdr) < 0 ||
 +          hdr.class != ASN1_CLASS_UNIVERSAL ||
 +          hdr.tag != ASN1_TAG_SEQUENCE) {
 +              wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE "
 +                         "(Name / RDNSequencer) - found class %d tag 0x%x",
 +                         hdr.class, hdr.tag);
 +              return -1;
 +      }
 +      pos = hdr.payload;
 +
 +      if (pos + hdr.length > buf + len)
 +              return -1;
 +
 +      end = *next = pos + hdr.length;
 +
 +      while (pos < end) {
 +              enum x509_name_attr_type type;
 +
 +              if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
 +                  hdr.class != ASN1_CLASS_UNIVERSAL ||
 +                  hdr.tag != ASN1_TAG_SET) {
 +                      wpa_printf(MSG_DEBUG, "X509: Expected SET "
 +                                 "(RelativeDistinguishedName) - found class "
 +                                 "%d tag 0x%x", hdr.class, hdr.tag);
 +                      x509_free_name(name);
 +                      return -1;
 +              }
 +
 +              set_pos = hdr.payload;
 +              pos = set_end = hdr.payload + hdr.length;
 +
 +              if (asn1_get_next(set_pos, set_end - set_pos, &hdr) < 0 ||
 +                  hdr.class != ASN1_CLASS_UNIVERSAL ||
 +                  hdr.tag != ASN1_TAG_SEQUENCE) {
 +                      wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE "
 +                                 "(AttributeTypeAndValue) - found class %d "
 +                                 "tag 0x%x", hdr.class, hdr.tag);
 +                      x509_free_name(name);
 +                      return -1;
 +              }
 +
 +              seq_pos = hdr.payload;
 +              seq_end = hdr.payload + hdr.length;
 +
 +              if (asn1_get_oid(seq_pos, seq_end - seq_pos, &oid, &seq_pos)) {
 +                      x509_free_name(name);
 +                      return -1;
 +              }
 +
 +              if (asn1_get_next(seq_pos, seq_end - seq_pos, &hdr) < 0 ||
 +                  hdr.class != ASN1_CLASS_UNIVERSAL) {
 +                      wpa_printf(MSG_DEBUG, "X509: Failed to parse "
 +                                 "AttributeValue");
 +                      x509_free_name(name);
 +                      return -1;
 +              }
 +
 +              /* RFC 3280:
 +               * MUST: country, organization, organizational-unit,
 +               * distinguished name qualifier, state or province name,
 +               * common name, serial number.
 +               * SHOULD: locality, title, surname, given name, initials,
 +               * pseudonym, generation qualifier.
 +               * MUST: domainComponent (RFC 2247).
 +               */
 +              type = X509_NAME_ATTR_NOT_USED;
 +              if (oid.len == 4 &&
 +                  oid.oid[0] == 2 && oid.oid[1] == 5 && oid.oid[2] == 4) {
 +                      /* id-at ::= 2.5.4 */
 +                      switch (oid.oid[3]) {
 +                      case 3:
 +                              /* commonName */
 +                              type = X509_NAME_ATTR_CN;
 +                              break;
 +                      case 6:
 +                              /*  countryName */
 +                              type = X509_NAME_ATTR_C;
 +                              break;
 +                      case 7:
 +                              /* localityName */
 +                              type = X509_NAME_ATTR_L;
 +                              break;
 +                      case 8:
 +                              /* stateOrProvinceName */
 +                              type = X509_NAME_ATTR_ST;
 +                              break;
 +                      case 10:
 +                              /* organizationName */
 +                              type = X509_NAME_ATTR_O;
 +                              break;
 +                      case 11:
 +                              /* organizationalUnitName */
 +                              type = X509_NAME_ATTR_OU;
 +                              break;
 +                      }
 +              } else if (oid.len == 7 &&
 +                         oid.oid[0] == 1 && oid.oid[1] == 2 &&
 +                         oid.oid[2] == 840 && oid.oid[3] == 113549 &&
 +                         oid.oid[4] == 1 && oid.oid[5] == 9 &&
 +                         oid.oid[6] == 1) {
 +                      /* 1.2.840.113549.1.9.1 - e-mailAddress */
 +                      os_free(name->email);
 +                      name->email = os_malloc(hdr.length + 1);
 +                      if (name->email == NULL) {
 +                              x509_free_name(name);
 +                              return -1;
 +                      }
 +                      os_memcpy(name->email, hdr.payload, hdr.length);
 +                      name->email[hdr.length] = '\0';
 +                      continue;
 +              } else if (oid.len == 7 &&
 +                         oid.oid[0] == 0 && oid.oid[1] == 9 &&
 +                         oid.oid[2] == 2342 && oid.oid[3] == 19200300 &&
 +                         oid.oid[4] == 100 && oid.oid[5] == 1 &&
 +                         oid.oid[6] == 25) {
 +                      /* 0.9.2342.19200300.100.1.25 - domainComponent */
 +                      type = X509_NAME_ATTR_DC;
 +              }
 +
 +              if (type == X509_NAME_ATTR_NOT_USED) {
 +                      wpa_hexdump(MSG_DEBUG, "X509: Unrecognized OID",
 +                                  (u8 *) oid.oid,
 +                                  oid.len * sizeof(oid.oid[0]));
 +                      wpa_hexdump_ascii(MSG_MSGDUMP, "X509: Attribute Data",
 +                                        hdr.payload, hdr.length);
 +                      continue;
 +              }
 +
 +              if (name->num_attr == X509_MAX_NAME_ATTRIBUTES) {
 +                      wpa_printf(MSG_INFO, "X509: Too many Name attributes");
 +                      x509_free_name(name);
 +                      return -1;
 +              }
 +
 +              val = dup_binstr(hdr.payload, hdr.length);
 +              if (val == NULL) {
 +                      x509_free_name(name);
 +                      return -1;
 +              }
 +              if (os_strlen(val) != hdr.length) {
 +                      wpa_printf(MSG_INFO, "X509: Reject certificate with "
 +                                 "embedded NUL byte in a string (%s[NUL])",
 +                                 val);
 +                      os_free(val);
 +                      x509_free_name(name);
 +                      return -1;
 +              }
 +
 +              name->attr[name->num_attr].type = type;
 +              name->attr[name->num_attr].value = val;
 +              name->num_attr++;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +static char * x509_name_attr_str(enum x509_name_attr_type type)
 +{
 +      switch (type) {
 +      case X509_NAME_ATTR_NOT_USED:
 +              return "[N/A]";
 +      case X509_NAME_ATTR_DC:
 +              return "DC";
 +      case X509_NAME_ATTR_CN:
 +              return "CN";
 +      case X509_NAME_ATTR_C:
 +              return "C";
 +      case X509_NAME_ATTR_L:
 +              return "L";
 +      case X509_NAME_ATTR_ST:
 +              return "ST";
 +      case X509_NAME_ATTR_O:
 +              return "O";
 +      case X509_NAME_ATTR_OU:
 +              return "OU";
 +      }
 +      return "?";
 +}
 +
 +
 +/**
 + * x509_name_string - Convert an X.509 certificate name into a string
 + * @name: Name to convert
 + * @buf: Buffer for the string
 + * @len: Maximum buffer length
 + */
 +void x509_name_string(struct x509_name *name, char *buf, size_t len)
 +{
 +      char *pos, *end;
 +      int ret;
 +      size_t i;
 +
 +      if (len == 0)
 +              return;
 +
 +      pos = buf;
 +      end = buf + len;
 +
 +      for (i = 0; i < name->num_attr; i++) {
 +              ret = os_snprintf(pos, end - pos, "%s=%s, ",
 +                                x509_name_attr_str(name->attr[i].type),
 +                                name->attr[i].value);
 +              if (os_snprintf_error(end - pos, ret))
 +                      goto done;
 +              pos += ret;
 +      }
 +
 +      if (pos > buf + 1 && pos[-1] == ' ' && pos[-2] == ',') {
 +              pos--;
 +              *pos = '\0';
 +              pos--;
 +              *pos = '\0';
 +      }
 +
 +      if (name->email) {
 +              ret = os_snprintf(pos, end - pos, "/emailAddress=%s",
 +                                name->email);
 +              if (os_snprintf_error(end - pos, ret))
 +                      goto done;
 +              pos += ret;
 +      }
 +
 +done:
 +      end[-1] = '\0';
 +}
 +
 +
 +static int x509_parse_time(const u8 *buf, size_t len, u8 asn1_tag,
 +                         os_time_t *val)
 +{
 +      const char *pos;
 +      int year, month, day, hour, min, sec;
 +
 +      /*
 +       * Time ::= CHOICE {
 +       *     utcTime        UTCTime,
 +       *     generalTime    GeneralizedTime
 +       * }
 +       *
 +       * UTCTime: YYMMDDHHMMSSZ
 +       * GeneralizedTime: YYYYMMDDHHMMSSZ
 +       */
 +
 +      pos = (const char *) buf;
 +
 +      switch (asn1_tag) {
 +      case ASN1_TAG_UTCTIME:
 +              if (len != 13 || buf[12] != 'Z') {
 +                      wpa_hexdump_ascii(MSG_DEBUG, "X509: Unrecognized "
 +                                        "UTCTime format", buf, len);
 +                      return -1;
 +              }
 +              if (sscanf(pos, "%02d", &year) != 1) {
 +                      wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse "
 +                                        "UTCTime year", buf, len);
 +                      return -1;
 +              }
 +              if (year < 50)
 +                      year += 2000;
 +              else
 +                      year += 1900;
 +              pos += 2;
 +              break;
 +      case ASN1_TAG_GENERALIZEDTIME:
 +              if (len != 15 || buf[14] != 'Z') {
 +                      wpa_hexdump_ascii(MSG_DEBUG, "X509: Unrecognized "
 +                                        "GeneralizedTime format", buf, len);
 +                      return -1;
 +              }
 +              if (sscanf(pos, "%04d", &year) != 1) {
 +                      wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse "
 +                                        "GeneralizedTime year", buf, len);
 +                      return -1;
 +              }
 +              pos += 4;
 +              break;
 +      default:
 +              wpa_printf(MSG_DEBUG, "X509: Expected UTCTime or "
 +                         "GeneralizedTime - found tag 0x%x", asn1_tag);
 +              return -1;
 +      }
 +
 +      if (sscanf(pos, "%02d", &month) != 1) {
 +              wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse Time "
 +                                "(month)", buf, len);
 +              return -1;
 +      }
 +      pos += 2;
 +
 +      if (sscanf(pos, "%02d", &day) != 1) {
 +              wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse Time "
 +                                "(day)", buf, len);
 +              return -1;
 +      }
 +      pos += 2;
 +
 +      if (sscanf(pos, "%02d", &hour) != 1) {
 +              wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse Time "
 +                                "(hour)", buf, len);
 +              return -1;
 +      }
 +      pos += 2;
 +
 +      if (sscanf(pos, "%02d", &min) != 1) {
 +              wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse Time "
 +                                "(min)", buf, len);
 +              return -1;
 +      }
 +      pos += 2;
 +
 +      if (sscanf(pos, "%02d", &sec) != 1) {
 +              wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse Time "
 +                                "(sec)", buf, len);
 +              return -1;
 +      }
 +
 +      if (os_mktime(year, month, day, hour, min, sec, val) < 0) {
 +              wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to convert Time",
 +                                buf, len);
 +              if (year < 1970) {
 +                      /*
 +                       * At least some test certificates have been configured
 +                       * to use dates prior to 1970. Set the date to
 +                       * beginning of 1970 to handle these case.
 +                       */
 +                      wpa_printf(MSG_DEBUG, "X509: Year=%d before epoch - "
 +                                 "assume epoch as the time", year);
 +                      *val = 0;
 +                      return 0;
 +              }
 +              return -1;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +static int x509_parse_validity(const u8 *buf, size_t len,
 +                             struct x509_certificate *cert, const u8 **next)
 +{
 +      struct asn1_hdr hdr;
 +      const u8 *pos;
 +      size_t plen;
 +
 +      /*
 +       * Validity ::= SEQUENCE {
 +       *     notBefore      Time,
 +       *     notAfter       Time
 +       * }
 +       *
 +       * RFC 3280, 4.1.2.5:
 +       * CAs conforming to this profile MUST always encode certificate
 +       * validity dates through the year 2049 as UTCTime; certificate
 +       * validity dates in 2050 or later MUST be encoded as GeneralizedTime.
 +       */
 +
 +      if (asn1_get_next(buf, len, &hdr) < 0 ||
 +          hdr.class != ASN1_CLASS_UNIVERSAL ||
 +          hdr.tag != ASN1_TAG_SEQUENCE) {
 +              wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE "
 +                         "(Validity) - found class %d tag 0x%x",
 +                         hdr.class, hdr.tag);
 +              return -1;
 +      }
 +      pos = hdr.payload;
 +      plen = hdr.length;
 +
 +      if (pos + plen > buf + len)
 +              return -1;
 +
 +      *next = pos + plen;
 +
 +      if (asn1_get_next(pos, plen, &hdr) < 0 ||
 +          hdr.class != ASN1_CLASS_UNIVERSAL ||
 +          x509_parse_time(hdr.payload, hdr.length, hdr.tag,
 +                          &cert->not_before) < 0) {
 +              wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse notBefore "
 +                                "Time", hdr.payload, hdr.length);
 +              return -1;
 +      }
 +
 +      pos = hdr.payload + hdr.length;
 +      plen = *next - pos;
 +
 +      if (asn1_get_next(pos, plen, &hdr) < 0 ||
 +          hdr.class != ASN1_CLASS_UNIVERSAL ||
 +          x509_parse_time(hdr.payload, hdr.length, hdr.tag,
 +                          &cert->not_after) < 0) {
 +              wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse notAfter "
 +                                "Time", hdr.payload, hdr.length);
 +              return -1;
 +      }
 +
 +      wpa_printf(MSG_MSGDUMP, "X509: Validity: notBefore: %lu notAfter: %lu",
 +                 (unsigned long) cert->not_before,
 +                 (unsigned long) cert->not_after);
 +
 +      return 0;
 +}
 +
 +
 +static int x509_id_ce_oid(struct asn1_oid *oid)
 +{
 +      /* id-ce arc from X.509 for standard X.509v3 extensions */
 +      return oid->len >= 4 &&
 +              oid->oid[0] == 2 /* joint-iso-ccitt */ &&
 +              oid->oid[1] == 5 /* ds */ &&
 +              oid->oid[2] == 29 /* id-ce */;
 +}
 +
 +
 +static int x509_parse_ext_key_usage(struct x509_certificate *cert,
 +                                  const u8 *pos, size_t len)
 +{
 +      struct asn1_hdr hdr;
 +
 +      /*
 +       * KeyUsage ::= BIT STRING {
 +       *     digitalSignature        (0),
 +       *     nonRepudiation          (1),
 +       *     keyEncipherment         (2),
 +       *     dataEncipherment        (3),
 +       *     keyAgreement            (4),
 +       *     keyCertSign             (5),
 +       *     cRLSign                 (6),
 +       *     encipherOnly            (7),
 +       *     decipherOnly            (8) }
 +       */
 +
 +      if (asn1_get_next(pos, len, &hdr) < 0 ||
 +          hdr.class != ASN1_CLASS_UNIVERSAL ||
 +          hdr.tag != ASN1_TAG_BITSTRING ||
 +          hdr.length < 1) {
 +              wpa_printf(MSG_DEBUG, "X509: Expected BIT STRING in "
 +                         "KeyUsage; found %d tag 0x%x len %d",
 +                         hdr.class, hdr.tag, hdr.length);
 +              return -1;
 +      }
 +
 +      cert->extensions_present |= X509_EXT_KEY_USAGE;
 +      cert->key_usage = asn1_bit_string_to_long(hdr.payload, hdr.length);
 +
 +      wpa_printf(MSG_DEBUG, "X509: KeyUsage 0x%lx", cert->key_usage);
 +
 +      return 0;
 +}
 +
 +
 +static int x509_parse_ext_basic_constraints(struct x509_certificate *cert,
 +                                          const u8 *pos, size_t len)
 +{
 +      struct asn1_hdr hdr;
 +      unsigned long value;
 +      size_t left;
 +
 +      /*
 +       * BasicConstraints ::= SEQUENCE {
 +       * cA                      BOOLEAN DEFAULT FALSE,
 +       * pathLenConstraint       INTEGER (0..MAX) OPTIONAL }
 +       */
 +
 +      if (asn1_get_next(pos, len, &hdr) < 0 ||
 +          hdr.class != ASN1_CLASS_UNIVERSAL ||
 +          hdr.tag != ASN1_TAG_SEQUENCE) {
 +              wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE in "
 +                         "BasicConstraints; found %d tag 0x%x",
 +                         hdr.class, hdr.tag);
 +              return -1;
 +      }
 +
 +      cert->extensions_present |= X509_EXT_BASIC_CONSTRAINTS;
 +
 +      if (hdr.length == 0)
 +              return 0;
 +
 +      if (asn1_get_next(hdr.payload, hdr.length, &hdr) < 0 ||
 +          hdr.class != ASN1_CLASS_UNIVERSAL) {
 +              wpa_printf(MSG_DEBUG, "X509: Failed to parse "
 +                         "BasicConstraints");
 +              return -1;
 +      }
 +
 +      if (hdr.tag == ASN1_TAG_BOOLEAN) {
 +              if (hdr.length != 1) {
 +                      wpa_printf(MSG_DEBUG, "X509: Unexpected "
 +                                 "Boolean length (%u) in BasicConstraints",
 +                                 hdr.length);
 +                      return -1;
 +              }
 +              cert->ca = hdr.payload[0];
 +
 +              if (hdr.payload + hdr.length == pos + len) {
 +                      wpa_printf(MSG_DEBUG, "X509: BasicConstraints - cA=%d",
 +                                 cert->ca);
 +                      return 0;
 +              }
 +
 +              if (asn1_get_next(hdr.payload + hdr.length, len - hdr.length,
 +                                &hdr) < 0 ||
 +                  hdr.class != ASN1_CLASS_UNIVERSAL) {
 +                      wpa_printf(MSG_DEBUG, "X509: Failed to parse "
 +                                 "BasicConstraints");
 +                      return -1;
 +              }
 +      }
 +
 +      if (hdr.tag != ASN1_TAG_INTEGER) {
 +              wpa_printf(MSG_DEBUG, "X509: Expected INTEGER in "
 +                         "BasicConstraints; found class %d tag 0x%x",
 +                         hdr.class, hdr.tag);
 +              return -1;
 +      }
 +
 +      pos = hdr.payload;
 +      left = hdr.length;
 +      value = 0;
 +      while (left) {
 +              value <<= 8;
 +              value |= *pos++;
 +              left--;
 +      }
 +
 +      cert->path_len_constraint = value;
 +      cert->extensions_present |= X509_EXT_PATH_LEN_CONSTRAINT;
 +
 +      wpa_printf(MSG_DEBUG, "X509: BasicConstraints - cA=%d "
 +                 "pathLenConstraint=%lu",
 +                 cert->ca, cert->path_len_constraint);
 +
 +      return 0;
 +}
 +
 +
 +static int x509_parse_alt_name_rfc8222(struct x509_name *name,
 +                                     const u8 *pos, size_t len)
 +{
 +      /* rfc822Name IA5String */
 +      wpa_hexdump_ascii(MSG_MSGDUMP, "X509: altName - rfc822Name", pos, len);
 +      os_free(name->alt_email);
 +      name->alt_email = os_zalloc(len + 1);
 +      if (name->alt_email == NULL)
 +              return -1;
 +      os_memcpy(name->alt_email, pos, len);
 +      if (os_strlen(name->alt_email) != len) {
 +              wpa_printf(MSG_INFO, "X509: Reject certificate with "
 +                         "embedded NUL byte in rfc822Name (%s[NUL])",
 +                         name->alt_email);
 +              os_free(name->alt_email);
 +              name->alt_email = NULL;
 +              return -1;
 +      }
 +      return 0;
 +}
 +
 +
 +static int x509_parse_alt_name_dns(struct x509_name *name,
 +                                 const u8 *pos, size_t len)
 +{
 +      /* dNSName IA5String */
 +      wpa_hexdump_ascii(MSG_MSGDUMP, "X509: altName - dNSName", pos, len);
 +      os_free(name->dns);
 +      name->dns = os_zalloc(len + 1);
 +      if (name->dns == NULL)
 +              return -1;
 +      os_memcpy(name->dns, pos, len);
 +      if (os_strlen(name->dns) != len) {
 +              wpa_printf(MSG_INFO, "X509: Reject certificate with "
 +                         "embedded NUL byte in dNSName (%s[NUL])",
 +                         name->dns);
 +              os_free(name->dns);
 +              name->dns = NULL;
 +              return -1;
 +      }
 +      return 0;
 +}
 +
 +
 +static int x509_parse_alt_name_uri(struct x509_name *name,
 +                                 const u8 *pos, size_t len)
 +{
 +      /* uniformResourceIdentifier IA5String */
 +      wpa_hexdump_ascii(MSG_MSGDUMP,
 +                        "X509: altName - uniformResourceIdentifier",
 +                        pos, len);
 +      os_free(name->uri);
 +      name->uri = os_zalloc(len + 1);
 +      if (name->uri == NULL)
 +              return -1;
 +      os_memcpy(name->uri, pos, len);
 +      if (os_strlen(name->uri) != len) {
 +              wpa_printf(MSG_INFO, "X509: Reject certificate with "
 +                         "embedded NUL byte in uniformResourceIdentifier "
 +                         "(%s[NUL])", name->uri);
 +              os_free(name->uri);
 +              name->uri = NULL;
 +              return -1;
 +      }
 +      return 0;
 +}
 +
 +
 +static int x509_parse_alt_name_ip(struct x509_name *name,
 +                                     const u8 *pos, size_t len)
 +{
 +      /* iPAddress OCTET STRING */
 +      wpa_hexdump(MSG_MSGDUMP, "X509: altName - iPAddress", pos, len);
 +      os_free(name->ip);
 +      name->ip = os_malloc(len);
 +      if (name->ip == NULL)
 +              return -1;
 +      os_memcpy(name->ip, pos, len);
 +      name->ip_len = len;
 +      return 0;
 +}
 +
 +
 +static int x509_parse_alt_name_rid(struct x509_name *name,
 +                                 const u8 *pos, size_t len)
 +{
 +      char buf[80];
 +
 +      /* registeredID OBJECT IDENTIFIER */
 +      if (asn1_parse_oid(pos, len, &name->rid) < 0)
 +              return -1;
 +
 +      asn1_oid_to_str(&name->rid, buf, sizeof(buf));
 +      wpa_printf(MSG_MSGDUMP, "X509: altName - registeredID: %s", buf);
 +
 +      return 0;
 +}
 +
 +
 +static int x509_parse_ext_alt_name(struct x509_name *name,
 +                                 const u8 *pos, size_t len)
 +{
 +      struct asn1_hdr hdr;
 +      const u8 *p, *end;
 +
 +      /*
 +       * GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
 +       *
 +       * GeneralName ::= CHOICE {
 +       *     otherName                       [0]     OtherName,
 +       *     rfc822Name                      [1]     IA5String,
 +       *     dNSName                         [2]     IA5String,
 +       *     x400Address                     [3]     ORAddress,
 +       *     directoryName                   [4]     Name,
 +       *     ediPartyName                    [5]     EDIPartyName,
 +       *     uniformResourceIdentifier       [6]     IA5String,
 +       *     iPAddress                       [7]     OCTET STRING,
 +       *     registeredID                    [8]     OBJECT IDENTIFIER }
 +       *
 +       * OtherName ::= SEQUENCE {
 +       *     type-id    OBJECT IDENTIFIER,
 +       *     value      [0] EXPLICIT ANY DEFINED BY type-id }
 +       *
 +       * EDIPartyName ::= SEQUENCE {
 +       *     nameAssigner            [0]     DirectoryString OPTIONAL,
 +       *     partyName               [1]     DirectoryString }
 +       */
 +
 +      for (p = pos, end = pos + len; p < end; p = hdr.payload + hdr.length) {
 +              int res;
 +
 +              if (asn1_get_next(p, end - p, &hdr) < 0) {
 +                      wpa_printf(MSG_DEBUG, "X509: Failed to parse "
 +                                 "SubjectAltName item");
 +                      return -1;
 +              }
 +
 +              if (hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC)
 +                      continue;
 +
 +              switch (hdr.tag) {
 +              case 1:
 +                      res = x509_parse_alt_name_rfc8222(name, hdr.payload,
 +                                                        hdr.length);
 +                      break;
 +              case 2:
 +                      res = x509_parse_alt_name_dns(name, hdr.payload,
 +                                                    hdr.length);
 +                      break;
 +              case 6:
 +                      res = x509_parse_alt_name_uri(name, hdr.payload,
 +                                                    hdr.length);
 +                      break;
 +              case 7:
 +                      res = x509_parse_alt_name_ip(name, hdr.payload,
 +                                                   hdr.length);
 +                      break;
 +              case 8:
 +                      res = x509_parse_alt_name_rid(name, hdr.payload,
 +                                                    hdr.length);
 +                      break;
 +              case 0: /* TODO: otherName */
 +              case 3: /* TODO: x500Address */
 +              case 4: /* TODO: directoryName */
 +              case 5: /* TODO: ediPartyName */
 +              default:
 +                      res = 0;
 +                      break;
 +              }
 +              if (res < 0)
 +                      return res;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +static int x509_parse_ext_subject_alt_name(struct x509_certificate *cert,
 +                                         const u8 *pos, size_t len)
 +{
 +      struct asn1_hdr hdr;
 +
 +      /* SubjectAltName ::= GeneralNames */
 +
 +      if (asn1_get_next(pos, len, &hdr) < 0 ||
 +          hdr.class != ASN1_CLASS_UNIVERSAL ||
 +          hdr.tag != ASN1_TAG_SEQUENCE) {
 +              wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE in "
 +                         "SubjectAltName; found %d tag 0x%x",
 +                         hdr.class, hdr.tag);
 +              return -1;
 +      }
 +
 +      wpa_printf(MSG_DEBUG, "X509: SubjectAltName");
 +      cert->extensions_present |= X509_EXT_SUBJECT_ALT_NAME;
 +
 +      if (hdr.length == 0)
 +              return 0;
 +
 +      return x509_parse_ext_alt_name(&cert->subject, hdr.payload,
 +                                     hdr.length);
 +}
 +
 +
 +static int x509_parse_ext_issuer_alt_name(struct x509_certificate *cert,
 +                                        const u8 *pos, size_t len)
 +{
 +      struct asn1_hdr hdr;
 +
 +      /* IssuerAltName ::= GeneralNames */
 +
 +      if (asn1_get_next(pos, len, &hdr) < 0 ||
 +          hdr.class != ASN1_CLASS_UNIVERSAL ||
 +          hdr.tag != ASN1_TAG_SEQUENCE) {
 +              wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE in "
 +                         "IssuerAltName; found %d tag 0x%x",
 +                         hdr.class, hdr.tag);
 +              return -1;
 +      }
 +
 +      wpa_printf(MSG_DEBUG, "X509: IssuerAltName");
 +      cert->extensions_present |= X509_EXT_ISSUER_ALT_NAME;
 +
 +      if (hdr.length == 0)
 +              return 0;
 +
 +      return x509_parse_ext_alt_name(&cert->issuer, hdr.payload,
 +                                     hdr.length);
 +}
 +
 +
 +static int x509_parse_extension_data(struct x509_certificate *cert,
 +                                   struct asn1_oid *oid,
 +                                   const u8 *pos, size_t len)
 +{
 +      if (!x509_id_ce_oid(oid))
 +              return 1;
 +
 +      /* TODO: add other extensions required by RFC 3280, Ch 4.2:
 +       * certificate policies (section 4.2.1.5)
 +       * name constraints (section 4.2.1.11)
 +       * policy constraints (section 4.2.1.12)
 +       * extended key usage (section 4.2.1.13)
 +       * inhibit any-policy (section 4.2.1.15)
 +       */
 +      switch (oid->oid[3]) {
 +      case 15: /* id-ce-keyUsage */
 +              return x509_parse_ext_key_usage(cert, pos, len);
 +      case 17: /* id-ce-subjectAltName */
 +              return x509_parse_ext_subject_alt_name(cert, pos, len);
 +      case 18: /* id-ce-issuerAltName */
 +              return x509_parse_ext_issuer_alt_name(cert, pos, len);
 +      case 19: /* id-ce-basicConstraints */
 +              return x509_parse_ext_basic_constraints(cert, pos, len);
 +      default:
 +              return 1;
 +      }
 +}
 +
 +
 +static int x509_parse_extension(struct x509_certificate *cert,
 +                              const u8 *pos, size_t len, const u8 **next)
 +{
 +      const u8 *end;
 +      struct asn1_hdr hdr;
 +      struct asn1_oid oid;
 +      int critical_ext = 0, res;
 +      char buf[80];
 +
 +      /*
 +       * Extension  ::=  SEQUENCE  {
 +       *     extnID      OBJECT IDENTIFIER,
 +       *     critical    BOOLEAN DEFAULT FALSE,
 +       *     extnValue   OCTET STRING
 +       * }
 +       */
 +
 +      if (asn1_get_next(pos, len, &hdr) < 0 ||
 +          hdr.class != ASN1_CLASS_UNIVERSAL ||
 +          hdr.tag != ASN1_TAG_SEQUENCE) {
 +              wpa_printf(MSG_DEBUG, "X509: Unexpected ASN.1 header in "
 +                         "Extensions: class %d tag 0x%x; expected SEQUENCE",
 +                         hdr.class, hdr.tag);
 +              return -1;
 +      }
 +      pos = hdr.payload;
 +      *next = end = pos + hdr.length;
 +
 +      if (asn1_get_oid(pos, end - pos, &oid, &pos) < 0) {
 +              wpa_printf(MSG_DEBUG, "X509: Unexpected ASN.1 data for "
 +                         "Extension (expected OID)");
 +              return -1;
 +      }
 +
 +      if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
 +          hdr.class != ASN1_CLASS_UNIVERSAL ||
 +          (hdr.tag != ASN1_TAG_BOOLEAN &&
 +           hdr.tag != ASN1_TAG_OCTETSTRING)) {
 +              wpa_printf(MSG_DEBUG, "X509: Unexpected ASN.1 header in "
 +                         "Extensions: class %d tag 0x%x; expected BOOLEAN "
 +                         "or OCTET STRING", hdr.class, hdr.tag);
 +              return -1;
 +      }
 +
 +      if (hdr.tag == ASN1_TAG_BOOLEAN) {
 +              if (hdr.length != 1) {
 +                      wpa_printf(MSG_DEBUG, "X509: Unexpected "
 +                                 "Boolean length (%u)", hdr.length);
 +                      return -1;
 +              }
 +              critical_ext = hdr.payload[0];
 +              pos = hdr.payload;
 +              if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
 +                  (hdr.class != ASN1_CLASS_UNIVERSAL &&
 +                   hdr.class != ASN1_CLASS_PRIVATE) ||
 +                  hdr.tag != ASN1_TAG_OCTETSTRING) {
 +                      wpa_printf(MSG_DEBUG, "X509: Unexpected ASN.1 header "
 +                                 "in Extensions: class %d tag 0x%x; "
 +                                 "expected OCTET STRING",
 +                                 hdr.class, hdr.tag);
 +                      return -1;
 +              }
 +      }
 +
 +      asn1_oid_to_str(&oid, buf, sizeof(buf));
 +      wpa_printf(MSG_DEBUG, "X509: Extension: extnID=%s critical=%d",
 +                 buf, critical_ext);
 +      wpa_hexdump(MSG_MSGDUMP, "X509: extnValue", hdr.payload, hdr.length);
 +
 +      res = x509_parse_extension_data(cert, &oid, hdr.payload, hdr.length);
 +      if (res < 0)
 +              return res;
 +      if (res == 1 && critical_ext) {
 +              wpa_printf(MSG_INFO, "X509: Unknown critical extension %s",
 +                         buf);
 +              return -1;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +static int x509_parse_extensions(struct x509_certificate *cert,
 +                               const u8 *pos, size_t len)
 +{
 +      const u8 *end;
 +      struct asn1_hdr hdr;
 +
 +      /* Extensions  ::=  SEQUENCE SIZE (1..MAX) OF Extension */
 +
 +      if (asn1_get_next(pos, len, &hdr) < 0 ||
 +          hdr.class != ASN1_CLASS_UNIVERSAL ||
 +          hdr.tag != ASN1_TAG_SEQUENCE) {
 +              wpa_printf(MSG_DEBUG, "X509: Unexpected ASN.1 data "
 +                         "for Extensions: class %d tag 0x%x; "
 +                         "expected SEQUENCE", hdr.class, hdr.tag);
 +              return -1;
 +      }
 +
 +      pos = hdr.payload;
 +      end = pos + hdr.length;
 +
 +      while (pos < end) {
 +              if (x509_parse_extension(cert, pos, end - pos, &pos)
 +                  < 0)
 +                      return -1;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +static int x509_parse_tbs_certificate(const u8 *buf, size_t len,
 +                                    struct x509_certificate *cert,
 +                                    const u8 **next)
 +{
 +      struct asn1_hdr hdr;
 +      const u8 *pos, *end;
 +      size_t left;
 +      char sbuf[128];
 +      unsigned long value;
 +
 +      /* tbsCertificate TBSCertificate ::= SEQUENCE */
 +      if (asn1_get_next(buf, len, &hdr) < 0 ||
 +          hdr.class != ASN1_CLASS_UNIVERSAL ||
 +          hdr.tag != ASN1_TAG_SEQUENCE) {
 +              wpa_printf(MSG_DEBUG, "X509: tbsCertificate did not start "
 +                         "with a valid SEQUENCE - found class %d tag 0x%x",
 +                         hdr.class, hdr.tag);
 +              return -1;
 +      }
 +      pos = hdr.payload;
 +      end = *next = pos + hdr.length;
 +
 +      /*
 +       * version [0]  EXPLICIT Version DEFAULT v1
 +       * Version  ::=  INTEGER  {  v1(0), v2(1), v3(2)  }
 +       */
 +      if (asn1_get_next(pos, end - pos, &hdr) < 0)
 +              return -1;
 +      pos = hdr.payload;
 +
 +      if (hdr.class == ASN1_CLASS_CONTEXT_SPECIFIC) {
 +              if (asn1_get_next(pos, end - pos, &hdr) < 0)
 +                      return -1;
 +
 +              if (hdr.class != ASN1_CLASS_UNIVERSAL ||
 +                  hdr.tag != ASN1_TAG_INTEGER) {
 +                      wpa_printf(MSG_DEBUG, "X509: No INTEGER tag found for "
 +                                 "version field - found class %d tag 0x%x",
 +                                 hdr.class, hdr.tag);
 +                      return -1;
 +              }
 +              if (hdr.length != 1) {
 +                      wpa_printf(MSG_DEBUG, "X509: Unexpected version field "
 +                                 "length %u (expected 1)", hdr.length);
 +                      return -1;
 +              }
 +              pos = hdr.payload;
 +              left = hdr.length;
 +              value = 0;
 +              while (left) {
 +                      value <<= 8;
 +                      value |= *pos++;
 +                      left--;
 +              }
 +
 +              cert->version = value;
 +              if (cert->version != X509_CERT_V1 &&
 +                  cert->version != X509_CERT_V2 &&
 +                  cert->version != X509_CERT_V3) {
 +                      wpa_printf(MSG_DEBUG, "X509: Unsupported version %d",
 +                                 cert->version + 1);
 +                      return -1;
 +              }
 +
 +              if (asn1_get_next(pos, end - pos, &hdr) < 0)
 +                      return -1;
 +      } else
 +              cert->version = X509_CERT_V1;
 +      wpa_printf(MSG_MSGDUMP, "X509: Version X.509v%d", cert->version + 1);
 +
 +      /* serialNumber CertificateSerialNumber ::= INTEGER */
 +      if (hdr.class != ASN1_CLASS_UNIVERSAL ||
 +          hdr.tag != ASN1_TAG_INTEGER) {
 +              wpa_printf(MSG_DEBUG, "X509: No INTEGER tag found for "
 +                         "serialNumber; class=%d tag=0x%x",
 +                         hdr.class, hdr.tag);
 +              return -1;
 +      }
 +
 +      pos = hdr.payload;
 +      left = hdr.length;
 +      while (left) {
 +              cert->serial_number <<= 8;
 +              cert->serial_number |= *pos++;
 +              left--;
 +      }
 +      wpa_printf(MSG_MSGDUMP, "X509: serialNumber %lu", cert->serial_number);
 +
 +      /* signature AlgorithmIdentifier */
 +      if (x509_parse_algorithm_identifier(pos, end - pos, &cert->signature,
 +                                          &pos))
 +              return -1;
 +
 +      /* issuer Name */
 +      if (x509_parse_name(pos, end - pos, &cert->issuer, &pos))
 +              return -1;
 +      x509_name_string(&cert->issuer, sbuf, sizeof(sbuf));
 +      wpa_printf(MSG_MSGDUMP, "X509: issuer %s", sbuf);
 +
 +      /* validity Validity */
 +      if (x509_parse_validity(pos, end - pos, cert, &pos))
 +              return -1;
 +
 +      /* subject Name */
 +      if (x509_parse_name(pos, end - pos, &cert->subject, &pos))
 +              return -1;
 +      x509_name_string(&cert->subject, sbuf, sizeof(sbuf));
 +      wpa_printf(MSG_MSGDUMP, "X509: subject %s", sbuf);
 +
 +      /* subjectPublicKeyInfo SubjectPublicKeyInfo */
 +      if (x509_parse_public_key(pos, end - pos, cert, &pos))
 +              return -1;
 +
 +      if (pos == end)
 +              return 0;
 +
 +      if (cert->version == X509_CERT_V1)
 +              return 0;
 +
 +      if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
 +          hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC) {
 +              wpa_printf(MSG_DEBUG, "X509: Expected Context-Specific"
 +                         " tag to parse optional tbsCertificate "
 +                         "field(s); parsed class %d tag 0x%x",
 +                         hdr.class, hdr.tag);
 +              return -1;
 +      }
 +
 +      if (hdr.tag == 1) {
 +              /* issuerUniqueID  [1]  IMPLICIT UniqueIdentifier OPTIONAL */
 +              wpa_printf(MSG_DEBUG, "X509: issuerUniqueID");
 +              /* TODO: parse UniqueIdentifier ::= BIT STRING */
 +
 +              pos = hdr.payload + hdr.length;
 +              if (pos == end)
 +                      return 0;
 +
 +              if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
 +                  hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC) {
 +                      wpa_printf(MSG_DEBUG, "X509: Expected Context-Specific"
 +                                 " tag to parse optional tbsCertificate "
 +                                 "field(s); parsed class %d tag 0x%x",
 +                                 hdr.class, hdr.tag);
 +                      return -1;
 +              }
 +      }
 +
 +      if (hdr.tag == 2) {
 +              /* subjectUniqueID [2]  IMPLICIT UniqueIdentifier OPTIONAL */
 +              wpa_printf(MSG_DEBUG, "X509: subjectUniqueID");
 +              /* TODO: parse UniqueIdentifier ::= BIT STRING */
 +
 +              pos = hdr.payload + hdr.length;
 +              if (pos == end)
 +                      return 0;
 +
 +              if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
 +                  hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC) {
 +                      wpa_printf(MSG_DEBUG, "X509: Expected Context-Specific"
 +                                 " tag to parse optional tbsCertificate "
 +                                 "field(s); parsed class %d tag 0x%x",
 +                                 hdr.class, hdr.tag);
 +                      return -1;
 +              }
 +      }
 +
 +      if (hdr.tag != 3) {
 +              wpa_printf(MSG_DEBUG, "X509: Ignored unexpected "
 +                         "Context-Specific tag %d in optional "
 +                         "tbsCertificate fields", hdr.tag);
 +              return 0;
 +      }
 +
 +      /* extensions      [3]  EXPLICIT Extensions OPTIONAL */
 +
 +      if (cert->version != X509_CERT_V3) {
 +              wpa_printf(MSG_DEBUG, "X509: X.509%d certificate and "
 +                         "Extensions data which are only allowed for "
 +                         "version 3", cert->version + 1);
 +              return -1;
 +      }
 +
 +      if (x509_parse_extensions(cert, hdr.payload, hdr.length) < 0)
 +              return -1;
 +
 +      pos = hdr.payload + hdr.length;
 +      if (pos < end) {
 +              wpa_hexdump(MSG_DEBUG,
 +                          "X509: Ignored extra tbsCertificate data",
 +                          pos, end - pos);
 +      }
 +
 +      return 0;
 +}
 +
 +
 +static int x509_rsadsi_oid(struct asn1_oid *oid)
 +{
 +      return oid->len >= 4 &&
 +              oid->oid[0] == 1 /* iso */ &&
 +              oid->oid[1] == 2 /* member-body */ &&
 +              oid->oid[2] == 840 /* us */ &&
 +              oid->oid[3] == 113549 /* rsadsi */;
 +}
 +
 +
 +static int x509_pkcs_oid(struct asn1_oid *oid)
 +{
 +      return oid->len >= 5 &&
 +              x509_rsadsi_oid(oid) &&
 +              oid->oid[4] == 1 /* pkcs */;
 +}
 +
 +
 +static int x509_digest_oid(struct asn1_oid *oid)
 +{
 +      return oid->len >= 5 &&
 +              x509_rsadsi_oid(oid) &&
 +              oid->oid[4] == 2 /* digestAlgorithm */;
 +}
 +
 +
 +static int x509_sha1_oid(struct asn1_oid *oid)
 +{
 +      return oid->len == 6 &&
 +              oid->oid[0] == 1 /* iso */ &&
 +              oid->oid[1] == 3 /* identified-organization */ &&
 +              oid->oid[2] == 14 /* oiw */ &&
 +              oid->oid[3] == 3 /* secsig */ &&
 +              oid->oid[4] == 2 /* algorithms */ &&
 +              oid->oid[5] == 26 /* id-sha1 */;
 +}
 +
 +
 +static int x509_sha256_oid(struct asn1_oid *oid)
 +{
 +      return oid->len == 9 &&
 +              oid->oid[0] == 2 /* joint-iso-itu-t */ &&
 +              oid->oid[1] == 16 /* country */ &&
 +              oid->oid[2] == 840 /* us */ &&
 +              oid->oid[3] == 1 /* organization */ &&
 +              oid->oid[4] == 101 /* gov */ &&
 +              oid->oid[5] == 3 /* csor */ &&
 +              oid->oid[6] == 4 /* nistAlgorithm */ &&
 +              oid->oid[7] == 2 /* hashAlgs */ &&
 +              oid->oid[8] == 1 /* sha256 */;
 +}
 +
 +
 +/**
 + * x509_certificate_parse - Parse a X.509 certificate in DER format
 + * @buf: Pointer to the X.509 certificate in DER format
 + * @len: Buffer length
 + * Returns: Pointer to the parsed certificate or %NULL on failure
 + *
 + * Caller is responsible for freeing the returned certificate by calling
 + * x509_certificate_free().
 + */
 +struct x509_certificate * x509_certificate_parse(const u8 *buf, size_t len)
 +{
 +      struct asn1_hdr hdr;
 +      const u8 *pos, *end, *hash_start;
 +      struct x509_certificate *cert;
 +
 +      cert = os_zalloc(sizeof(*cert) + len);
 +      if (cert == NULL)
 +              return NULL;
 +      os_memcpy(cert + 1, buf, len);
 +      cert->cert_start = (u8 *) (cert + 1);
 +      cert->cert_len = len;
 +
 +      pos = buf;
 +      end = buf + len;
 +
 +      /* RFC 3280 - X.509 v3 certificate / ASN.1 DER */
 +
 +      /* Certificate ::= SEQUENCE */
 +      if (asn1_get_next(pos, len, &hdr) < 0 ||
 +          hdr.class != ASN1_CLASS_UNIVERSAL ||
 +          hdr.tag != ASN1_TAG_SEQUENCE) {
 +              wpa_printf(MSG_DEBUG, "X509: Certificate did not start with "
 +                         "a valid SEQUENCE - found class %d tag 0x%x",
 +                         hdr.class, hdr.tag);
 +              x509_certificate_free(cert);
 +              return NULL;
 +      }
 +      pos = hdr.payload;
 +
 +      if (pos + hdr.length > end) {
 +              x509_certificate_free(cert);
 +              return NULL;
 +      }
 +
 +      if (pos + hdr.length < end) {
 +              wpa_hexdump(MSG_MSGDUMP, "X509: Ignoring extra data after DER "
 +                          "encoded certificate",
++                          pos + hdr.length, end - (pos + hdr.length));
 +              end = pos + hdr.length;
 +      }
 +
 +      hash_start = pos;
 +      cert->tbs_cert_start = cert->cert_start + (hash_start - buf);
 +      if (x509_parse_tbs_certificate(pos, end - pos, cert, &pos)) {
 +              x509_certificate_free(cert);
 +              return NULL;
 +      }
 +      cert->tbs_cert_len = pos - hash_start;
 +
 +      /* signatureAlgorithm AlgorithmIdentifier */
 +      if (x509_parse_algorithm_identifier(pos, end - pos,
 +                                          &cert->signature_alg, &pos)) {
 +              x509_certificate_free(cert);
 +              return NULL;
 +      }
 +
 +      /* signatureValue BIT STRING */
 +      if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
 +          hdr.class != ASN1_CLASS_UNIVERSAL ||
 +          hdr.tag != ASN1_TAG_BITSTRING) {
 +              wpa_printf(MSG_DEBUG, "X509: Expected BITSTRING "
 +                         "(signatureValue) - found class %d tag 0x%x",
 +                         hdr.class, hdr.tag);
 +              x509_certificate_free(cert);
 +              return NULL;
 +      }
 +      if (hdr.length < 1) {
 +              x509_certificate_free(cert);
 +              return NULL;
 +      }
 +      pos = hdr.payload;
 +      if (*pos) {
 +              wpa_printf(MSG_DEBUG, "X509: BITSTRING - %d unused bits",
 +                         *pos);
 +              /* PKCS #1 v1.5 10.2.1:
 +               * It is an error if the length in bits of the signature S is
 +               * not a multiple of eight.
 +               */
 +              x509_certificate_free(cert);
 +              return NULL;
 +      }
 +      os_free(cert->sign_value);
 +      cert->sign_value = os_malloc(hdr.length - 1);
 +      if (cert->sign_value == NULL) {
 +              wpa_printf(MSG_DEBUG, "X509: Failed to allocate memory for "
 +                         "signatureValue");
 +              x509_certificate_free(cert);
 +              return NULL;
 +      }
 +      os_memcpy(cert->sign_value, pos + 1, hdr.length - 1);
 +      cert->sign_value_len = hdr.length - 1;
 +      wpa_hexdump(MSG_MSGDUMP, "X509: signature",
 +                  cert->sign_value, cert->sign_value_len);
 +
 +      return cert;
 +}
 +
 +
 +/**
 + * x509_certificate_check_signature - Verify certificate signature
 + * @issuer: Issuer certificate
 + * @cert: Certificate to be verified
 + * Returns: 0 if cert has a valid signature that was signed by the issuer,
 + * -1 if not
 + */
 +int x509_certificate_check_signature(struct x509_certificate *issuer,
 +                                   struct x509_certificate *cert)
 +{
 +      struct crypto_public_key *pk;
 +      u8 *data;
 +      const u8 *pos, *end, *next, *da_end;
 +      size_t data_len;
 +      struct asn1_hdr hdr;
 +      struct asn1_oid oid;
 +      u8 hash[32];
 +      size_t hash_len;
 +
 +      if (!x509_pkcs_oid(&cert->signature.oid) ||
 +          cert->signature.oid.len != 7 ||
 +          cert->signature.oid.oid[5] != 1 /* pkcs-1 */) {
 +              wpa_printf(MSG_DEBUG, "X509: Unrecognized signature "
 +                         "algorithm");
 +              return -1;
 +      }
 +
 +      pk = crypto_public_key_import(issuer->public_key,
 +                                    issuer->public_key_len);
 +      if (pk == NULL)
 +              return -1;
 +
 +      data_len = cert->sign_value_len;
 +      data = os_malloc(data_len);
 +      if (data == NULL) {
 +              crypto_public_key_free(pk);
 +              return -1;
 +      }
 +
 +      if (crypto_public_key_decrypt_pkcs1(pk, cert->sign_value,
 +                                          cert->sign_value_len, data,
 +                                          &data_len) < 0) {
 +              wpa_printf(MSG_DEBUG, "X509: Failed to decrypt signature");
 +              crypto_public_key_free(pk);
 +              os_free(data);
 +              return -1;
 +      }
 +      crypto_public_key_free(pk);
 +
 +      wpa_hexdump(MSG_MSGDUMP, "X509: Signature data D", data, data_len);
 +
 +      /*
 +       * PKCS #1 v1.5, 10.1.2:
 +       *
 +       * DigestInfo ::= SEQUENCE {
 +       *     digestAlgorithm DigestAlgorithmIdentifier,
 +       *     digest Digest
 +       * }
 +       *
 +       * DigestAlgorithmIdentifier ::= AlgorithmIdentifier
 +       *
 +       * Digest ::= OCTET STRING
 +       *
 +       */
 +      if (asn1_get_next(data, data_len, &hdr) < 0 ||
 +          hdr.class != ASN1_CLASS_UNIVERSAL ||
 +          hdr.tag != ASN1_TAG_SEQUENCE) {
 +              wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE "
 +                         "(DigestInfo) - found class %d tag 0x%x",
 +                         hdr.class, hdr.tag);
 +              os_free(data);
 +              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, "X509: Expected SEQUENCE "
 +                         "(AlgorithmIdentifier) - found class %d tag 0x%x",
 +                         hdr.class, hdr.tag);
 +              os_free(data);
 +              return -1;
 +      }
 +      da_end = hdr.payload + hdr.length;
 +
 +      if (asn1_get_oid(hdr.payload, hdr.length, &oid, &next)) {
 +              wpa_printf(MSG_DEBUG, "X509: Failed to parse digestAlgorithm");
 +              os_free(data);
 +              return -1;
 +      }
 +
 +      if (x509_sha1_oid(&oid)) {
 +              if (cert->signature.oid.oid[6] !=
 +                  5 /* sha-1WithRSAEncryption */) {
 +                      wpa_printf(MSG_DEBUG, "X509: digestAlgorithm SHA1 "
 +                                 "does not match with certificate "
 +                                 "signatureAlgorithm (%lu)",
 +                                 cert->signature.oid.oid[6]);
 +                      os_free(data);
 +                      return -1;
 +              }
 +              goto skip_digest_oid;
 +      }
 +
 +      if (x509_sha256_oid(&oid)) {
 +              if (cert->signature.oid.oid[6] !=
 +                  11 /* sha2561WithRSAEncryption */) {
 +                      wpa_printf(MSG_DEBUG, "X509: digestAlgorithm SHA256 "
 +                                 "does not match with certificate "
 +                                 "signatureAlgorithm (%lu)",
 +                                 cert->signature.oid.oid[6]);
 +                      os_free(data);
 +                      return -1;
 +              }
 +              goto skip_digest_oid;
 +      }
 +
 +      if (!x509_digest_oid(&oid)) {
 +              wpa_printf(MSG_DEBUG, "X509: Unrecognized digestAlgorithm");
 +              os_free(data);
 +              return -1;
 +      }
 +      switch (oid.oid[5]) {
 +      case 5: /* md5 */
 +              if (cert->signature.oid.oid[6] != 4 /* md5WithRSAEncryption */)
 +              {
 +                      wpa_printf(MSG_DEBUG, "X509: digestAlgorithm MD5 does "
 +                                 "not match with certificate "
 +                                 "signatureAlgorithm (%lu)",
 +                                 cert->signature.oid.oid[6]);
 +                      os_free(data);
 +                      return -1;
 +              }
 +              break;
 +      case 2: /* md2 */
 +      case 4: /* md4 */
 +      default:
 +              wpa_printf(MSG_DEBUG, "X509: Unsupported digestAlgorithm "
 +                         "(%lu)", oid.oid[5]);
 +              os_free(data);
 +              return -1;
 +      }
 +
 +skip_digest_oid:
 +      /* Digest ::= OCTET STRING */
 +      pos = da_end;
 +      end = data + data_len;
 +
 +      if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
 +          hdr.class != ASN1_CLASS_UNIVERSAL ||
 +          hdr.tag != ASN1_TAG_OCTETSTRING) {
 +              wpa_printf(MSG_DEBUG, "X509: Expected OCTETSTRING "
 +                         "(Digest) - found class %d tag 0x%x",
 +                         hdr.class, hdr.tag);
 +              os_free(data);
 +              return -1;
 +      }
 +      wpa_hexdump(MSG_MSGDUMP, "X509: Decrypted Digest",
 +                  hdr.payload, hdr.length);
 +
 +      switch (cert->signature.oid.oid[6]) {
 +      case 4: /* md5WithRSAEncryption */
 +              md5_vector(1, &cert->tbs_cert_start, &cert->tbs_cert_len,
 +                         hash);
 +              hash_len = 16;
 +              wpa_hexdump(MSG_MSGDUMP, "X509: Certificate hash (MD5)",
 +                          hash, hash_len);
 +              break;
 +      case 5: /* sha-1WithRSAEncryption */
 +              sha1_vector(1, &cert->tbs_cert_start, &cert->tbs_cert_len,
 +                          hash);
 +              hash_len = 20;
 +              wpa_hexdump(MSG_MSGDUMP, "X509: Certificate hash (SHA1)",
 +                          hash, hash_len);
 +              break;
 +      case 11: /* sha256WithRSAEncryption */
 +              sha256_vector(1, &cert->tbs_cert_start, &cert->tbs_cert_len,
 +                            hash);
 +              hash_len = 32;
 +              wpa_hexdump(MSG_MSGDUMP, "X509: Certificate hash (SHA256)",
 +                          hash, hash_len);
 +              break;
 +      case 2: /* md2WithRSAEncryption */
 +      case 12: /* sha384WithRSAEncryption */
 +      case 13: /* sha512WithRSAEncryption */
 +      default:
 +              wpa_printf(MSG_INFO, "X509: Unsupported certificate signature "
 +                         "algorithm (%lu)", cert->signature.oid.oid[6]);
 +              os_free(data);
 +              return -1;
 +      }
 +
 +      if (hdr.length != hash_len ||
 +          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 "
 +                 "calculated tbsCertificate hash");
 +
 +      return 0;
 +}
 +
 +
 +static int x509_valid_issuer(const struct x509_certificate *cert)
 +{
 +      if ((cert->extensions_present & X509_EXT_BASIC_CONSTRAINTS) &&
 +          !cert->ca) {
 +              wpa_printf(MSG_DEBUG, "X509: Non-CA certificate used as an "
 +                         "issuer");
 +              return -1;
 +      }
 +
 +      if (cert->version == X509_CERT_V3 &&
 +          !(cert->extensions_present & X509_EXT_BASIC_CONSTRAINTS)) {
 +              wpa_printf(MSG_DEBUG, "X509: v3 CA certificate did not "
 +                         "include BasicConstraints extension");
 +              return -1;
 +      }
 +
 +      if ((cert->extensions_present & X509_EXT_KEY_USAGE) &&
 +          !(cert->key_usage & X509_KEY_USAGE_KEY_CERT_SIGN)) {
 +              wpa_printf(MSG_DEBUG, "X509: Issuer certificate did not have "
 +                         "keyCertSign bit in Key Usage");
 +              return -1;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +/**
 + * x509_certificate_chain_validate - Validate X.509 certificate chain
 + * @trusted: List of trusted certificates
 + * @chain: Certificate chain to be validated (first chain must be issued by
 + * signed by the second certificate in the chain and so on)
 + * @reason: Buffer for returning failure reason (X509_VALIDATE_*)
 + * Returns: 0 if chain is valid, -1 if not
 + */
 +int x509_certificate_chain_validate(struct x509_certificate *trusted,
 +                                  struct x509_certificate *chain,
 +                                  int *reason, int disable_time_checks)
 +{
 +      long unsigned idx;
 +      int chain_trusted = 0;
 +      struct x509_certificate *cert, *trust;
 +      char buf[128];
 +      struct os_time now;
 +
 +      *reason = X509_VALIDATE_OK;
 +
 +      wpa_printf(MSG_DEBUG, "X509: Validate certificate chain");
 +      os_get_time(&now);
 +
 +      for (cert = chain, idx = 0; cert; cert = cert->next, idx++) {
 +              x509_name_string(&cert->subject, buf, sizeof(buf)); 
 +              wpa_printf(MSG_DEBUG, "X509: %lu: %s", idx, buf);
 +
 +              if (chain_trusted)
 +                      continue;
 +
 +              if (!disable_time_checks &&
 +                  ((unsigned long) now.sec <
 +                   (unsigned long) cert->not_before ||
 +                   (unsigned long) now.sec >
 +                   (unsigned long) cert->not_after)) {
 +                      wpa_printf(MSG_INFO, "X509: Certificate not valid "
 +                                 "(now=%lu not_before=%lu not_after=%lu)",
 +                                 now.sec, cert->not_before, cert->not_after);
 +                      *reason = X509_VALIDATE_CERTIFICATE_EXPIRED;
 +                      return -1;
 +              }
 +
 +              if (cert->next) {
 +                      if (x509_name_compare(&cert->issuer,
 +                                            &cert->next->subject) != 0) {
 +                              wpa_printf(MSG_DEBUG, "X509: Certificate "
 +                                         "chain issuer name mismatch");
 +                              x509_name_string(&cert->issuer, buf,
 +                                               sizeof(buf)); 
 +                              wpa_printf(MSG_DEBUG, "X509: cert issuer: %s",
 +                                         buf);
 +                              x509_name_string(&cert->next->subject, buf,
 +                                               sizeof(buf)); 
 +                              wpa_printf(MSG_DEBUG, "X509: next cert "
 +                                         "subject: %s", buf);
 +                              *reason = X509_VALIDATE_CERTIFICATE_UNKNOWN;
 +                              return -1;
 +                      }
 +
 +                      if (x509_valid_issuer(cert->next) < 0) {
 +                              *reason = X509_VALIDATE_BAD_CERTIFICATE;
 +                              return -1;
 +                      }
 +
 +                      if ((cert->next->extensions_present &
 +                           X509_EXT_PATH_LEN_CONSTRAINT) &&
 +                          idx > cert->next->path_len_constraint) {
 +                              wpa_printf(MSG_DEBUG, "X509: pathLenConstraint"
 +                                         " not met (idx=%lu issuer "
 +                                         "pathLenConstraint=%lu)", idx,
 +                                         cert->next->path_len_constraint);
 +                              *reason = X509_VALIDATE_BAD_CERTIFICATE;
 +                              return -1;
 +                      }
 +
 +                      if (x509_certificate_check_signature(cert->next, cert)
 +                          < 0) {
 +                              wpa_printf(MSG_DEBUG, "X509: Invalid "
 +                                         "certificate signature within "
 +                                         "chain");
 +                              *reason = X509_VALIDATE_BAD_CERTIFICATE;
 +                              return -1;
 +                      }
 +              }
 +
 +              for (trust = trusted; trust; trust = trust->next) {
 +                      if (x509_name_compare(&cert->issuer, &trust->subject)
 +                          == 0)
 +                              break;
 +              }
 +
 +              if (trust) {
 +                      wpa_printf(MSG_DEBUG, "X509: Found issuer from the "
 +                                 "list of trusted certificates");
 +                      if (x509_valid_issuer(trust) < 0) {
 +                              *reason = X509_VALIDATE_BAD_CERTIFICATE;
 +                              return -1;
 +                      }
 +
 +                      if (x509_certificate_check_signature(trust, cert) < 0)
 +                      {
 +                              wpa_printf(MSG_DEBUG, "X509: Invalid "
 +                                         "certificate signature");
 +                              *reason = X509_VALIDATE_BAD_CERTIFICATE;
 +                              return -1;
 +                      }
 +
 +                      wpa_printf(MSG_DEBUG, "X509: Trusted certificate "
 +                                 "found to complete the chain");
 +                      chain_trusted = 1;
 +              }
 +      }
 +
 +      if (!chain_trusted) {
 +              wpa_printf(MSG_DEBUG, "X509: Did not find any of the issuers "
 +                         "from the list of trusted certificates");
 +              if (trusted) {
 +                      *reason = X509_VALIDATE_UNKNOWN_CA;
 +                      return -1;
 +              }
 +              wpa_printf(MSG_DEBUG, "X509: Certificate chain validation "
 +                         "disabled - ignore unknown CA issue");
 +      }
 +
 +      wpa_printf(MSG_DEBUG, "X509: Certificate chain valid");
 +
 +      return 0;
 +}
 +
 +
 +/**
 + * x509_certificate_get_subject - Get a certificate based on Subject name
 + * @chain: Certificate chain to search through
 + * @name: Subject name to search for
 + * Returns: Pointer to the certificate with the given Subject name or
 + * %NULL on failure
 + */
 +struct x509_certificate *
 +x509_certificate_get_subject(struct x509_certificate *chain,
 +                           struct x509_name *name)
 +{
 +      struct x509_certificate *cert;
 +
 +      for (cert = chain; cert; cert = cert->next) {
 +              if (x509_name_compare(&cert->subject, name) == 0)
 +                      return cert;
 +      }
 +      return NULL;
 +}
 +
 +
 +/**
 + * x509_certificate_self_signed - Is the certificate self-signed?
 + * @cert: Certificate
 + * Returns: 1 if certificate is self-signed, 0 if not
 + */
 +int x509_certificate_self_signed(struct x509_certificate *cert)
 +{
 +      return x509_name_compare(&cert->issuer, &cert->subject) == 0;
 +}
index 5fc40fac610e9e7ee7c436f9af504bb1a115daef,0000000000000000000000000000000000000000..59ba4d1e02d86a879282d4e6a152f96e1b03873d
mode 100644,000000..100644
--- /dev/null
@@@ -1,136 -1,0 +1,138 @@@
-               char *argv[12];
 +/*
 + * 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 */
-               argv[11] = NULL;
++              char *argv[14];
 +
 +              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] = "--user";
++              argv[12] = "-3"; /* USER_CURRENT_OR_SELF */
++              argv[13] = 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;
 +}
index 5fd795f3f30364da665483060fc447b440d6064b,0000000000000000000000000000000000000000..660e9fc985d69ddebeb8764bd5a513c91ac3acba
mode 100644,000000..100644
--- /dev/null
@@@ -1,1064 -1,0 +1,1125 @@@
-       static char ssid_txt[32 * 4 + 1];
 +/*
 + * wpa_supplicant/hostapd / common helper functions, etc.
 + * Copyright (c) 2002-2007, Jouni Malinen <j@w1.fi>
 + *
 + * This software may be distributed under the terms of the BSD license.
 + * See README for more details.
 + */
 +
 +#include "includes.h"
 +
++#include "common/ieee802_11_defs.h"
 +#include "common.h"
 +
 +
 +static int hex2num(char c)
 +{
 +      if (c >= '0' && c <= '9')
 +              return c - '0';
 +      if (c >= 'a' && c <= 'f')
 +              return c - 'a' + 10;
 +      if (c >= 'A' && c <= 'F')
 +              return c - 'A' + 10;
 +      return -1;
 +}
 +
 +
 +int hex2byte(const char *hex)
 +{
 +      int a, b;
 +      a = hex2num(*hex++);
 +      if (a < 0)
 +              return -1;
 +      b = hex2num(*hex++);
 +      if (b < 0)
 +              return -1;
 +      return (a << 4) | b;
 +}
 +
 +
 +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")
 + * @addr: Buffer for the MAC address (ETH_ALEN = 6 bytes)
 + * Returns: 0 on success, -1 on failure (e.g., string not a MAC address)
 + */
 +int hwaddr_aton(const char *txt, u8 *addr)
 +{
 +      return hwaddr_parse(txt, addr) ? 0 : -1;
 +}
 +
 +
 +/**
 + * 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")
 + * @addr: Buffer for the MAC address (ETH_ALEN = 6 bytes)
 + * Returns: 0 on success, -1 on failure (e.g., string not a MAC address)
 + */
 +int hwaddr_compact_aton(const char *txt, u8 *addr)
 +{
 +      int i;
 +
 +      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;
 +      }
 +
 +      return 0;
 +}
 +
 +/**
 + * hwaddr_aton2 - Convert ASCII string to MAC address (in any known format)
 + * @txt: MAC address as a string (e.g., 00:11:22:33:44:55 or 0011.2233.4455)
 + * @addr: Buffer for the MAC address (ETH_ALEN = 6 bytes)
 + * Returns: Characters used (> 0) on success, -1 on failure
 + */
 +int hwaddr_aton2(const char *txt, u8 *addr)
 +{
 +      int i;
 +      const char *pos = txt;
 +
 +      for (i = 0; i < 6; i++) {
 +              int a, b;
 +
 +              while (*pos == ':' || *pos == '.' || *pos == '-')
 +                      pos++;
 +
 +              a = hex2num(*pos++);
 +              if (a < 0)
 +                      return -1;
 +              b = hex2num(*pos++);
 +              if (b < 0)
 +                      return -1;
 +              *addr++ = (a << 4) | b;
 +      }
 +
 +      return pos - txt;
 +}
 +
 +
 +/**
 + * hexstr2bin - Convert ASCII hex string into binary data
 + * @hex: ASCII hex string (e.g., "01ab")
 + * @buf: Buffer for the binary data
 + * @len: Length of the text to convert in bytes (of buf); hex will be double
 + * this size
 + * Returns: 0 on success, -1 on failure (invalid hex string)
 + */
 +int hexstr2bin(const char *hex, u8 *buf, size_t len)
 +{
 +      size_t i;
 +      int a;
 +      const char *ipos = hex;
 +      u8 *opos = buf;
 +
 +      for (i = 0; i < len; i++) {
 +              a = hex2byte(ipos);
 +              if (a < 0)
 +                      return -1;
 +              *opos++ = a;
 +              ipos += 2;
 +      }
 +      return 0;
 +}
 +
 +
 +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
 + * @len: Length of the counter in bytes
 + *
 + * This function increments the last byte of the counter by one and continues
 + * rolling over to more significant bytes if the byte was incremented from
 + * 0xff to 0x00.
 + */
 +void inc_byte_array(u8 *counter, size_t len)
 +{
 +      int pos = len - 1;
 +      while (pos >= 0) {
 +              counter[pos]++;
 +              if (counter[pos] != 0)
 +                      break;
 +              pos--;
 +      }
 +}
 +
 +
 +void wpa_get_ntp_timestamp(u8 *buf)
 +{
 +      struct os_time now;
 +      u32 sec, usec;
 +      be32 tmp;
 +
 +      /* 64-bit NTP timestamp (time from 1900-01-01 00:00:00) */
 +      os_get_time(&now);
 +      sec = now.sec + 2208988800U; /* Epoch to 1900 */
 +      /* Estimate 2^32/10^6 = 4295 - 1/32 - 1/512 */
 +      usec = now.usec;
 +      usec = 4295 * usec - (usec >> 5) - (usec >> 9);
 +      tmp = host_to_be32(sec);
 +      os_memcpy(buf, (u8 *) &tmp, 4);
 +      tmp = host_to_be32(usec);
 +      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;
 +}
 +
++
++int wpa_snprintf_hex_sep(char *buf, size_t buf_size, const u8 *data, size_t len,
++                       char sep)
++{
++      size_t i;
++      char *pos = buf, *end = buf + buf_size;
++      int ret;
++
++      if (buf_size == 0)
++              return 0;
++
++      for (i = 0; i < len; i++) {
++              ret = os_snprintf(pos, end - pos, "%02x%c",
++                                data[i], sep);
++              if (os_snprintf_error(end - pos, ret)) {
++                      end[-1] = '\0';
++                      return pos - buf;
++              }
++              pos += ret;
++      }
++      pos[-1] = '\0';
++      return pos - buf;
++}
++
++
 +static inline int _wpa_snprintf_hex(char *buf, size_t buf_size, const u8 *data,
 +                                  size_t len, int uppercase)
 +{
 +      size_t i;
 +      char *pos = buf, *end = buf + buf_size;
 +      int ret;
 +      if (buf_size == 0)
 +              return 0;
 +      for (i = 0; i < len; i++) {
 +              ret = os_snprintf(pos, end - pos, uppercase ? "%02X" : "%02x",
 +                                data[i]);
 +              if (os_snprintf_error(end - pos, ret)) {
 +                      end[-1] = '\0';
 +                      return pos - buf;
 +              }
 +              pos += ret;
 +      }
 +      end[-1] = '\0';
 +      return pos - buf;
 +}
 +
 +/**
 + * wpa_snprintf_hex - Print data as a hex string into a buffer
 + * @buf: Memory area to use as the output buffer
 + * @buf_size: Maximum buffer size in bytes (should be at least 2 * len + 1)
 + * @data: Data to be printed
 + * @len: Length of data in bytes
 + * Returns: Number of bytes written
 + */
 +int wpa_snprintf_hex(char *buf, size_t buf_size, const u8 *data, size_t len)
 +{
 +      return _wpa_snprintf_hex(buf, buf_size, data, len, 0);
 +}
 +
 +
 +/**
 + * wpa_snprintf_hex_uppercase - Print data as a upper case hex string into buf
 + * @buf: Memory area to use as the output buffer
 + * @buf_size: Maximum buffer size in bytes (should be at least 2 * len + 1)
 + * @data: Data to be printed
 + * @len: Length of data in bytes
 + * Returns: Number of bytes written
 + */
 +int wpa_snprintf_hex_uppercase(char *buf, size_t buf_size, const u8 *data,
 +                             size_t len)
 +{
 +      return _wpa_snprintf_hex(buf, buf_size, data, len, 1);
 +}
 +
 +
 +#ifdef CONFIG_ANSI_C_EXTRA
 +
 +#ifdef _WIN32_WCE
 +void perror(const char *s)
 +{
 +      wpa_printf(MSG_ERROR, "%s: GetLastError: %d",
 +                 s, (int) GetLastError());
 +}
 +#endif /* _WIN32_WCE */
 +
 +
 +int optind = 1;
 +int optopt;
 +char *optarg;
 +
 +int getopt(int argc, char *const argv[], const char *optstring)
 +{
 +      static int optchr = 1;
 +      char *cp;
 +
 +      if (optchr == 1) {
 +              if (optind >= argc) {
 +                      /* all arguments processed */
 +                      return EOF;
 +              }
 +
 +              if (argv[optind][0] != '-' || argv[optind][1] == '\0') {
 +                      /* no option characters */
 +                      return EOF;
 +              }
 +      }
 +
 +      if (os_strcmp(argv[optind], "--") == 0) {
 +              /* no more options */
 +              optind++;
 +              return EOF;
 +      }
 +
 +      optopt = argv[optind][optchr];
 +      cp = os_strchr(optstring, optopt);
 +      if (cp == NULL || optopt == ':') {
 +              if (argv[optind][++optchr] == '\0') {
 +                      optchr = 1;
 +                      optind++;
 +              }
 +              return '?';
 +      }
 +
 +      if (cp[1] == ':') {
 +              /* Argument required */
 +              optchr = 1;
 +              if (argv[optind][optchr + 1]) {
 +                      /* No space between option and argument */
 +                      optarg = &argv[optind++][optchr + 1];
 +              } else if (++optind >= argc) {
 +                      /* option requires an argument */
 +                      return '?';
 +              } else {
 +                      /* Argument in the next argv */
 +                      optarg = argv[optind++];
 +              }
 +      } else {
 +              /* No argument */
 +              if (argv[optind][++optchr] == '\0') {
 +                      optchr = 1;
 +                      optind++;
 +              }
 +              optarg = NULL;
 +      }
 +      return *cp;
 +}
 +#endif /* CONFIG_ANSI_C_EXTRA */
 +
 +
 +#ifdef CONFIG_NATIVE_WINDOWS
 +/**
 + * wpa_unicode2ascii_inplace - Convert unicode string into ASCII
 + * @str: Pointer to string to convert
 + *
 + * This function converts a unicode string to ASCII using the same
 + * buffer for output. If UNICODE is not set, the buffer is not
 + * modified.
 + */
 +void wpa_unicode2ascii_inplace(TCHAR *str)
 +{
 +#ifdef UNICODE
 +      char *dst = (char *) str;
 +      while (*str)
 +              *dst++ = (char) *str++;
 +      *dst = '\0';
 +#endif /* UNICODE */
 +}
 +
 +
 +TCHAR * wpa_strdup_tchar(const char *str)
 +{
 +#ifdef UNICODE
 +      TCHAR *buf;
 +      buf = os_malloc((strlen(str) + 1) * sizeof(TCHAR));
 +      if (buf == NULL)
 +              return NULL;
 +      wsprintf(buf, L"%S", str);
 +      return buf;
 +#else /* UNICODE */
 +      return os_strdup(str);
 +#endif /* UNICODE */
 +}
 +#endif /* CONFIG_NATIVE_WINDOWS */
 +
 +
 +void printf_encode(char *txt, size_t maxlen, const u8 *data, size_t len)
 +{
 +      char *end = txt + maxlen;
 +      size_t i;
 +
 +      for (i = 0; i < len; i++) {
 +              if (txt + 4 >= end)
 +                      break;
 +
 +              switch (data[i]) {
 +              case '\"':
 +                      *txt++ = '\\';
 +                      *txt++ = '\"';
 +                      break;
 +              case '\\':
 +                      *txt++ = '\\';
 +                      *txt++ = '\\';
 +                      break;
 +              case '\033':
 +                      *txt++ = '\\';
 +                      *txt++ = 'e';
 +                      break;
 +              case '\n':
 +                      *txt++ = '\\';
 +                      *txt++ = 'n';
 +                      break;
 +              case '\r':
 +                      *txt++ = '\\';
 +                      *txt++ = 'r';
 +                      break;
 +              case '\t':
 +                      *txt++ = '\\';
 +                      *txt++ = 't';
 +                      break;
 +              default:
 +                      if (data[i] >= 32 && data[i] <= 127) {
 +                              *txt++ = data[i];
 +                      } else {
 +                              txt += os_snprintf(txt, end - txt, "\\x%02x",
 +                                                 data[i]);
 +                      }
 +                      break;
 +              }
 +      }
 +
 +      *txt = '\0';
 +}
 +
 +
 +size_t printf_decode(u8 *buf, size_t maxlen, const char *str)
 +{
 +      const char *pos = str;
 +      size_t len = 0;
 +      int val;
 +
 +      while (*pos) {
 +              if (len + 1 >= maxlen)
 +                      break;
 +              switch (*pos) {
 +              case '\\':
 +                      pos++;
 +                      switch (*pos) {
 +                      case '\\':
 +                              buf[len++] = '\\';
 +                              pos++;
 +                              break;
 +                      case '"':
 +                              buf[len++] = '"';
 +                              pos++;
 +                              break;
 +                      case 'n':
 +                              buf[len++] = '\n';
 +                              pos++;
 +                              break;
 +                      case 'r':
 +                              buf[len++] = '\r';
 +                              pos++;
 +                              break;
 +                      case 't':
 +                              buf[len++] = '\t';
 +                              pos++;
 +                              break;
 +                      case 'e':
 +                              buf[len++] = '\033';
 +                              pos++;
 +                              break;
 +                      case 'x':
 +                              pos++;
 +                              val = hex2byte(pos);
 +                              if (val < 0) {
 +                                      val = hex2num(*pos);
 +                                      if (val < 0)
 +                                              break;
 +                                      buf[len++] = val;
 +                                      pos++;
 +                              } else {
 +                                      buf[len++] = val;
 +                                      pos += 2;
 +                              }
 +                              break;
 +                      case '0':
 +                      case '1':
 +                      case '2':
 +                      case '3':
 +                      case '4':
 +                      case '5':
 +                      case '6':
 +                      case '7':
 +                              val = *pos++ - '0';
 +                              if (*pos >= '0' && *pos <= '7')
 +                                      val = val * 8 + (*pos++ - '0');
 +                              if (*pos >= '0' && *pos <= '7')
 +                                      val = val * 8 + (*pos++ - '0');
 +                              buf[len++] = val;
 +                              break;
 +                      default:
 +                              break;
 +                      }
 +                      break;
 +              default:
 +                      buf[len++] = *pos++;
 +                      break;
 +              }
 +      }
 +      if (maxlen > len)
 +              buf[len] = '\0';
 +
 +      return len;
 +}
 +
 +
 +/**
 + * wpa_ssid_txt - Convert SSID to a printable string
 + * @ssid: SSID (32-octet string)
 + * @ssid_len: Length of ssid in octets
 + * Returns: Pointer to a printable string
 + *
 + * This function can be used to convert SSIDs into printable form. In most
 + * cases, SSIDs do not use unprintable characters, but IEEE 802.11 standard
 + * does not limit the used character set, so anything could be used in an SSID.
 + *
 + * This function uses a static buffer, so only one call can be used at the
 + * time, i.e., this is not re-entrant and the returned buffer must be used
 + * before calling this again.
 + */
 +const char * wpa_ssid_txt(const u8 *ssid, size_t ssid_len)
 +{
-       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++;
++      static char ssid_txt[SSID_MAX_LEN * 4 + 1];
 +
 +      if (ssid == NULL) {
 +              ssid_txt[0] = '\0';
 +              return ssid_txt;
 +      }
 +
 +      printf_encode(ssid_txt, sizeof(ssid_txt), ssid, ssid_len);
 +      return ssid_txt;
 +}
 +
 +
 +void * __hide_aliasing_typecast(void *foo)
 +{
 +      return foo;
 +}
 +
 +
 +char * wpa_config_parse_string(const char *value, size_t *len)
 +{
 +      if (*value == '"') {
 +              const char *pos;
 +              char *str;
 +              value++;
 +              pos = os_strrchr(value, '"');
 +              if (pos == NULL || pos[1] != '\0')
 +                      return NULL;
 +              *len = pos - value;
 +              str = dup_binstr(value, *len);
 +              if (str == NULL)
 +                      return NULL;
 +              return str;
 +      } else if (*value == 'P' && value[1] == '"') {
 +              const char *pos;
 +              char *tstr, *str;
 +              size_t tlen;
 +              value += 2;
 +              pos = os_strrchr(value, '"');
 +              if (pos == NULL || pos[1] != '\0')
 +                      return NULL;
 +              tlen = pos - value;
 +              tstr = dup_binstr(value, tlen);
 +              if (tstr == NULL)
 +                      return NULL;
 +
 +              str = os_malloc(tlen + 1);
 +              if (str == NULL) {
 +                      os_free(tstr);
 +                      return NULL;
 +              }
 +
 +              *len = printf_decode((u8 *) str, tlen + 1, tstr);
 +              os_free(tstr);
 +
 +              return str;
 +      } else {
 +              u8 *str;
 +              size_t tlen, hlen = os_strlen(value);
 +              if (hlen & 1)
 +                      return NULL;
 +              tlen = hlen / 2;
 +              str = os_malloc(tlen + 1);
 +              if (str == NULL)
 +                      return NULL;
 +              if (hexstr2bin(value, str, tlen)) {
 +                      os_free(str);
 +                      return NULL;
 +              }
 +              str[tlen] = '\0';
 +              *len = tlen;
 +              return (char *) str;
 +      }
 +}
 +
 +
 +int is_hex(const u8 *data, size_t len)
 +{
 +      size_t i;
 +
 +      for (i = 0; i < len; i++) {
 +              if (data[i] < 32 || data[i] >= 127)
 +                      return 1;
 +      }
 +      return 0;
 +}
 +
 +
 +size_t merge_byte_arrays(u8 *res, size_t res_len,
 +                       const u8 *src1, size_t src1_len,
 +                       const u8 *src2, size_t src2_len)
 +{
 +      size_t len = 0;
 +
 +      os_memset(res, 0, res_len);
 +
 +      if (src1) {
 +              if (src1_len >= res_len) {
 +                      os_memcpy(res, src1, res_len);
 +                      return res_len;
 +              }
 +
 +              os_memcpy(res, src1, src1_len);
 +              len += src1_len;
 +      }
 +
 +      if (src2) {
 +              if (len + src2_len >= res_len) {
 +                      os_memcpy(res + len, src2, res_len - len);
 +                      return res_len;
 +              }
 +
 +              os_memcpy(res + len, src2, src2_len);
 +              len += src2_len;
 +      }
 +
 +      return len;
 +}
 +
 +
 +char * dup_binstr(const void *src, size_t len)
 +{
 +      char *res;
 +
 +      if (src == NULL)
 +              return NULL;
 +      res = os_malloc(len + 1);
 +      if (res == NULL)
 +              return NULL;
 +      os_memcpy(res, src, len);
 +      res[len] = '\0';
 +
 +      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;
 +}
 +
 +
++/**
++ * cstr_token - Get next token from const char string
++ * @str: a constant string to tokenize
++ * @delim: a string of delimiters
++ * @last: a pointer to a character following the returned token
++ *      It has to be set to NULL for the first call and passed for any
++ *      futher call.
++ * Returns: a pointer to token position in str or NULL
++ *
++ * This function is similar to str_token, but it can be used with both
++ * char and const char strings. Differences:
++ * - The str buffer remains unmodified
++ * - The returned token is not a NULL terminated string, but a token
++ *   position in str buffer. If a return value is not NULL a size
++ *   of the returned token could be calculated as (last - token).
++ */
++const char * cstr_token(const char *str, const char *delim, const char **last)
++{
++      const char *end, *token = str;
++
++      if (!str || !delim || !last)
++              return NULL;
++
++      if (*last)
++              token = *last;
++
++      while (*token && os_strchr(delim, *token))
++              token++;
++
++      if (!*token)
++              return NULL;
++
++      end = token + 1;
++
++      while (*end && !os_strchr(delim, *end))
++              end++;
++
++      *last = end;
++      return token;
++}
++
++
 +/**
 + * 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)
 +{
-       if (*end)
-               *end++ = '\0';
++      char *token = (char *) cstr_token(str, delim, (const char **) context);
 +
-       *context = end;
-       return pos;
++      if (token && **context)
++              *(*context)++ = '\0';
 +
++      return token;
 +}
 +
 +
 +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;
 +}
++
++
++int is_ctrl_char(char c)
++{
++      return c > 0 && c < 32;
++}
index 576e8e7e2d4ebf80886255253dd8df25ebeddc0b,0000000000000000000000000000000000000000..0b9cc3d882090f1f476405e7554c19779cc38fbb
mode 100644,000000..100644
--- /dev/null
@@@ -1,576 -1,0 +1,559 @@@
- #ifdef CONFIG_TI_COMPILER
- #define __BIG_ENDIAN 4321
- #define __LITTLE_ENDIAN 1234
- #ifdef __big_endian__
- #define __BYTE_ORDER __BIG_ENDIAN
- #else
- #define __BYTE_ORDER __LITTLE_ENDIAN
- #endif
- #endif /* CONFIG_TI_COMPILER */
 +/*
 + * wpa_supplicant/hostapd / common helper functions, etc.
 + * Copyright (c) 2002-2007, Jouni Malinen <j@w1.fi>
 + *
 + * This software may be distributed under the terms of the BSD license.
 + * See README for more details.
 + */
 +
 +#ifndef COMMON_H
 +#define COMMON_H
 +
 +#include "os.h"
 +
 +#if defined(__linux__) || defined(__GLIBC__)
 +#include <endian.h>
 +#include <byteswap.h>
 +#endif /* __linux__ */
 +
 +#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__DragonFly__) || \
 +    defined(__OpenBSD__)
 +#include <sys/types.h>
 +#include <sys/endian.h>
 +#define __BYTE_ORDER  _BYTE_ORDER
 +#define       __LITTLE_ENDIAN _LITTLE_ENDIAN
 +#define       __BIG_ENDIAN    _BIG_ENDIAN
 +#ifdef __OpenBSD__
 +#define bswap_16 swap16
 +#define bswap_32 swap32
 +#define bswap_64 swap64
 +#else /* __OpenBSD__ */
 +#define bswap_16 bswap16
 +#define bswap_32 bswap32
 +#define bswap_64 bswap64
 +#endif /* __OpenBSD__ */
 +#endif /* defined(__FreeBSD__) || defined(__NetBSD__) ||
 +      * defined(__DragonFly__) || defined(__OpenBSD__) */
 +
 +#ifdef __APPLE__
 +#include <sys/types.h>
 +#include <machine/endian.h>
 +#define __BYTE_ORDER  _BYTE_ORDER
 +#define __LITTLE_ENDIAN       _LITTLE_ENDIAN
 +#define __BIG_ENDIAN  _BIG_ENDIAN
 +static inline unsigned short bswap_16(unsigned short v)
 +{
 +      return ((v & 0xff) << 8) | (v >> 8);
 +}
 +
 +static inline unsigned int bswap_32(unsigned int v)
 +{
 +      return ((v & 0xff) << 24) | ((v & 0xff00) << 8) |
 +              ((v & 0xff0000) >> 8) | (v >> 24);
 +}
 +#endif /* __APPLE__ */
 +
- #ifdef CONFIG_TI_COMPILER
- #ifdef _LLONG_AVAILABLE
- typedef unsigned long long u64;
- #else
- /*
-  * TODO: 64-bit variable not available. Using long as a workaround to test the
-  * build, but this will likely not work for all operations.
-  */
- typedef unsigned long u64;
- #endif
- typedef unsigned int u32;
- typedef unsigned short u16;
- typedef unsigned char u8;
- #define WPA_TYPES_DEFINED
- #endif /* CONFIG_TI_COMPILER */
 +#ifdef CONFIG_NATIVE_WINDOWS
 +#include <winsock.h>
 +
 +typedef int socklen_t;
 +
 +#ifndef MSG_DONTWAIT
 +#define MSG_DONTWAIT 0 /* not supported */
 +#endif
 +
 +#endif /* CONFIG_NATIVE_WINDOWS */
 +
 +#ifdef _MSC_VER
 +#define inline __inline
 +
 +#undef vsnprintf
 +#define vsnprintf _vsnprintf
 +#undef close
 +#define close closesocket
 +#endif /* _MSC_VER */
 +
 +
 +/* Define platform specific integer types */
 +
 +#ifdef _MSC_VER
 +typedef UINT64 u64;
 +typedef UINT32 u32;
 +typedef UINT16 u16;
 +typedef UINT8 u8;
 +typedef INT64 s64;
 +typedef INT32 s32;
 +typedef INT16 s16;
 +typedef INT8 s8;
 +#define WPA_TYPES_DEFINED
 +#endif /* _MSC_VER */
 +
 +#ifdef __vxworks
 +typedef unsigned long long u64;
 +typedef UINT32 u32;
 +typedef UINT16 u16;
 +typedef UINT8 u8;
 +typedef long long s64;
 +typedef INT32 s32;
 +typedef INT16 s16;
 +typedef INT8 s8;
 +#define WPA_TYPES_DEFINED
 +#endif /* __vxworks */
 +
-       return (a[0] << 24) | (a[1] << 16) | (a[2] << 8) | a[3];
 +#ifndef WPA_TYPES_DEFINED
 +#ifdef CONFIG_USE_INTTYPES_H
 +#include <inttypes.h>
 +#else
 +#include <stdint.h>
 +#endif
 +typedef uint64_t u64;
 +typedef uint32_t u32;
 +typedef uint16_t u16;
 +typedef uint8_t u8;
 +typedef int64_t s64;
 +typedef int32_t s32;
 +typedef int16_t s16;
 +typedef int8_t s8;
 +#define WPA_TYPES_DEFINED
 +#endif /* !WPA_TYPES_DEFINED */
 +
 +
 +/* Define platform specific byte swapping macros */
 +
 +#if defined(__CYGWIN__) || defined(CONFIG_NATIVE_WINDOWS)
 +
 +static inline unsigned short wpa_swap_16(unsigned short v)
 +{
 +      return ((v & 0xff) << 8) | (v >> 8);
 +}
 +
 +static inline unsigned int wpa_swap_32(unsigned int v)
 +{
 +      return ((v & 0xff) << 24) | ((v & 0xff00) << 8) |
 +              ((v & 0xff0000) >> 8) | (v >> 24);
 +}
 +
 +#define le_to_host16(n) (n)
 +#define host_to_le16(n) (n)
 +#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)
 +
 +#define WPA_BYTE_SWAP_DEFINED
 +
 +#endif /* __CYGWIN__ || CONFIG_NATIVE_WINDOWS */
 +
 +
 +#ifndef WPA_BYTE_SWAP_DEFINED
 +
 +#ifndef __BYTE_ORDER
 +#ifndef __LITTLE_ENDIAN
 +#ifndef __BIG_ENDIAN
 +#define __LITTLE_ENDIAN 1234
 +#define __BIG_ENDIAN 4321
 +#if defined(sparc)
 +#define __BYTE_ORDER __BIG_ENDIAN
 +#endif
 +#endif /* __BIG_ENDIAN */
 +#endif /* __LITTLE_ENDIAN */
 +#endif /* __BYTE_ORDER */
 +
 +#if __BYTE_ORDER == __LITTLE_ENDIAN
 +#define le_to_host16(n) ((__force u16) (le16) (n))
 +#define host_to_le16(n) ((__force le16) (u16) (n))
 +#define be_to_host16(n) bswap_16((__force u16) (be16) (n))
 +#define host_to_be16(n) ((__force be16) bswap_16((n)))
 +#define le_to_host32(n) ((__force u32) (le32) (n))
 +#define host_to_le32(n) ((__force le32) (u32) (n))
 +#define be_to_host32(n) bswap_32((__force u32) (be32) (n))
 +#define host_to_be32(n) ((__force be32) bswap_32((n)))
 +#define le_to_host64(n) ((__force u64) (le64) (n))
 +#define host_to_le64(n) ((__force le64) (u64) (n))
 +#define be_to_host64(n) bswap_64((__force u64) (be64) (n))
 +#define host_to_be64(n) ((__force be64) bswap_64((n)))
 +#elif __BYTE_ORDER == __BIG_ENDIAN
 +#define le_to_host16(n) bswap_16(n)
 +#define host_to_le16(n) bswap_16(n)
 +#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)
 +#define host_to_le64(n) bswap_64(n)
 +#define be_to_host64(n) (n)
 +#define host_to_be64(n) (n)
 +#ifndef WORDS_BIGENDIAN
 +#define WORDS_BIGENDIAN
 +#endif
 +#else
 +#error Could not determine CPU byte order
 +#endif
 +
 +#define WPA_BYTE_SWAP_DEFINED
 +#endif /* !WPA_BYTE_SWAP_DEFINED */
 +
 +
 +/* Macros for handling unaligned memory accesses */
 +
 +static inline u16 WPA_GET_BE16(const u8 *a)
 +{
 +      return (a[0] << 8) | a[1];
 +}
 +
 +static inline void WPA_PUT_BE16(u8 *a, u16 val)
 +{
 +      a[0] = val >> 8;
 +      a[1] = val & 0xff;
 +}
 +
 +static inline u16 WPA_GET_LE16(const u8 *a)
 +{
 +      return (a[1] << 8) | a[0];
 +}
 +
 +static inline void WPA_PUT_LE16(u8 *a, u16 val)
 +{
 +      a[1] = val >> 8;
 +      a[0] = val & 0xff;
 +}
 +
 +static inline u32 WPA_GET_BE24(const u8 *a)
 +{
 +      return (a[0] << 16) | (a[1] << 8) | a[2];
 +}
 +
 +static inline void WPA_PUT_BE24(u8 *a, u32 val)
 +{
 +      a[0] = (val >> 16) & 0xff;
 +      a[1] = (val >> 8) & 0xff;
 +      a[2] = val & 0xff;
 +}
 +
 +static inline u32 WPA_GET_BE32(const u8 *a)
 +{
-       return (a[3] << 24) | (a[2] << 16) | (a[1] << 8) | a[0];
++      return ((u32) a[0] << 24) | (a[1] << 16) | (a[2] << 8) | a[3];
 +}
 +
 +static inline void WPA_PUT_BE32(u8 *a, u32 val)
 +{
 +      a[0] = (val >> 24) & 0xff;
 +      a[1] = (val >> 16) & 0xff;
 +      a[2] = (val >> 8) & 0xff;
 +      a[3] = val & 0xff;
 +}
 +
 +static inline u32 WPA_GET_LE32(const u8 *a)
 +{
- #define BIT(x) (1 << (x))
++      return ((u32) a[3] << 24) | (a[2] << 16) | (a[1] << 8) | a[0];
 +}
 +
 +static inline void WPA_PUT_LE32(u8 *a, u32 val)
 +{
 +      a[3] = (val >> 24) & 0xff;
 +      a[2] = (val >> 16) & 0xff;
 +      a[1] = (val >> 8) & 0xff;
 +      a[0] = val & 0xff;
 +}
 +
 +static inline u64 WPA_GET_BE64(const u8 *a)
 +{
 +      return (((u64) a[0]) << 56) | (((u64) a[1]) << 48) |
 +              (((u64) a[2]) << 40) | (((u64) a[3]) << 32) |
 +              (((u64) a[4]) << 24) | (((u64) a[5]) << 16) |
 +              (((u64) a[6]) << 8) | ((u64) a[7]);
 +}
 +
 +static inline void WPA_PUT_BE64(u8 *a, u64 val)
 +{
 +      a[0] = val >> 56;
 +      a[1] = val >> 48;
 +      a[2] = val >> 40;
 +      a[3] = val >> 32;
 +      a[4] = val >> 24;
 +      a[5] = val >> 16;
 +      a[6] = val >> 8;
 +      a[7] = val & 0xff;
 +}
 +
 +static inline u64 WPA_GET_LE64(const u8 *a)
 +{
 +      return (((u64) a[7]) << 56) | (((u64) a[6]) << 48) |
 +              (((u64) a[5]) << 40) | (((u64) a[4]) << 32) |
 +              (((u64) a[3]) << 24) | (((u64) a[2]) << 16) |
 +              (((u64) a[1]) << 8) | ((u64) a[0]);
 +}
 +
 +static inline void WPA_PUT_LE64(u8 *a, u64 val)
 +{
 +      a[7] = val >> 56;
 +      a[6] = val >> 48;
 +      a[5] = val >> 40;
 +      a[4] = val >> 32;
 +      a[3] = val >> 24;
 +      a[2] = val >> 16;
 +      a[1] = val >> 8;
 +      a[0] = val & 0xff;
 +}
 +
 +
 +#ifndef ETH_ALEN
 +#define ETH_ALEN 6
 +#endif
 +#ifndef ETH_HLEN
 +#define ETH_HLEN 14
 +#endif
 +#ifndef IFNAMSIZ
 +#define IFNAMSIZ 16
 +#endif
 +#ifndef ETH_P_ALL
 +#define ETH_P_ALL 0x0003
 +#endif
 +#ifndef ETH_P_80211_ENCAP
 +#define ETH_P_80211_ENCAP 0x890d /* TDLS comes under this category */
 +#endif
 +#ifndef ETH_P_PAE
 +#define ETH_P_PAE 0x888E /* Port Access Entity (IEEE 802.1X) */
 +#endif /* ETH_P_PAE */
 +#ifndef ETH_P_EAPOL
 +#define ETH_P_EAPOL ETH_P_PAE
 +#endif /* ETH_P_EAPOL */
 +#ifndef ETH_P_RSN_PREAUTH
 +#define ETH_P_RSN_PREAUTH 0x88c7
 +#endif /* ETH_P_RSN_PREAUTH */
 +#ifndef ETH_P_RRB
 +#define ETH_P_RRB 0x890D
 +#endif /* ETH_P_RRB */
 +
 +
 +#ifdef __GNUC__
 +#define PRINTF_FORMAT(a,b) __attribute__ ((format (printf, (a), (b))))
 +#define STRUCT_PACKED __attribute__ ((packed))
 +#else
 +#define PRINTF_FORMAT(a,b)
 +#define STRUCT_PACKED
 +#endif
 +
 +
 +#ifdef CONFIG_ANSI_C_EXTRA
 +
 +#if !defined(_MSC_VER) || _MSC_VER < 1400
 +/* snprintf - used in number of places; sprintf() is _not_ a good replacement
 + * due to possible buffer overflow; see, e.g.,
 + * http://www.ijs.si/software/snprintf/ for portable implementation of
 + * snprintf. */
 +int snprintf(char *str, size_t size, const char *format, ...);
 +
 +/* vsnprintf - only used for wpa_msg() in wpa_supplicant.c */
 +int vsnprintf(char *str, size_t size, const char *format, va_list ap);
 +#endif /* !defined(_MSC_VER) || _MSC_VER < 1400 */
 +
 +/* getopt - only used in main.c */
 +int getopt(int argc, char *const argv[], const char *optstring);
 +extern char *optarg;
 +extern int optind;
 +
 +#ifndef CONFIG_NO_SOCKLEN_T_TYPEDEF
 +#ifndef __socklen_t_defined
 +typedef int socklen_t;
 +#endif
 +#endif
 +
 +/* inline - define as __inline or just define it to be empty, if needed */
 +#ifdef CONFIG_NO_INLINE
 +#define inline
 +#else
 +#define inline __inline
 +#endif
 +
 +#ifndef __func__
 +#define __func__ "__func__ not defined"
 +#endif
 +
 +#ifndef bswap_16
 +#define bswap_16(a) ((((u16) (a) << 8) & 0xff00) | (((u16) (a) >> 8) & 0xff))
 +#endif
 +
 +#ifndef bswap_32
 +#define bswap_32(a) ((((u32) (a) << 24) & 0xff000000) | \
 +                   (((u32) (a) << 8) & 0xff0000) | \
 +                   (((u32) (a) >> 8) & 0xff00) | \
 +                   (((u32) (a) >> 24) & 0xff))
 +#endif
 +
 +#ifndef MSG_DONTWAIT
 +#define MSG_DONTWAIT 0
 +#endif
 +
 +#ifdef _WIN32_WCE
 +void perror(const char *s);
 +#endif /* _WIN32_WCE */
 +
 +#endif /* CONFIG_ANSI_C_EXTRA */
 +
 +#ifndef MAC2STR
 +#define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5]
 +#define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x"
 +
 +/*
 + * Compact form for string representation of MAC address
 + * To be used, e.g., for constructing dbus paths for P2P Devices
 + */
 +#define COMPACT_MACSTR "%02x%02x%02x%02x%02x%02x"
 +#endif
 +
 +#ifndef BIT
++#define BIT(x) (1U << (x))
 +#endif
 +
 +/*
 + * Definitions for sparse validation
 + * (http://kernel.org/pub/linux/kernel/people/josh/sparse/)
 + */
 +#ifdef __CHECKER__
 +#define __force __attribute__((force))
 +#define __bitwise __attribute__((bitwise))
 +#else
 +#define __force
 +#define __bitwise
 +#endif
 +
 +typedef u16 __bitwise be16;
 +typedef u16 __bitwise le16;
 +typedef u32 __bitwise be32;
 +typedef u32 __bitwise le32;
 +typedef u64 __bitwise be64;
 +typedef u64 __bitwise le64;
 +
 +#ifndef __must_check
 +#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)
 +#define __must_check __attribute__((__warn_unused_result__))
 +#else
 +#define __must_check
 +#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_sep(char *buf, size_t buf_size, const u8 *data, size_t len,
++                       char sep);
 +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);
 +#else /* CONFIG_NATIVE_WINDOWS */
 +#define wpa_unicode2ascii_inplace(s) do { } while (0)
 +#define wpa_strdup_tchar(s) strdup((s))
 +#endif /* CONFIG_NATIVE_WINDOWS */
 +
 +void printf_encode(char *txt, size_t maxlen, const u8 *data, size_t len);
 +size_t printf_decode(u8 *buf, size_t maxlen, const char *str);
 +
 +const char * wpa_ssid_txt(const u8 *ssid, size_t ssid_len);
 +
 +char * wpa_config_parse_string(const char *value, size_t *len);
 +int is_hex(const u8 *data, size_t len);
 +size_t merge_byte_arrays(u8 *res, size_t res_len,
 +                       const u8 *src1, size_t src1_len,
 +                       const u8 *src2, size_t src2_len);
 +char * dup_binstr(const void *src, size_t len);
 +
 +static inline int is_zero_ether_addr(const u8 *a)
 +{
 +      return !(a[0] | a[1] | a[2] | a[3] | a[4] | a[5]);
 +}
 +
 +static inline int is_broadcast_ether_addr(const u8 *a)
 +{
 +      return (a[0] & a[1] & a[2] & a[3] & a[4] & a[5]) == 0xff;
 +}
 +
++static inline int is_multicast_ether_addr(const u8 *a)
++{
++      return a[0] & 0x01;
++}
++
 +#define broadcast_ether_addr (const u8 *) "\xff\xff\xff\xff\xff\xff"
 +
 +#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);
 +
++const char * cstr_token(const char *str, const char *delim, const char **last);
 +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);
++int is_ctrl_char(char c);
 +
 +
 +/*
 + * 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
 + * cannot be easily avoided with union-based type-punning due to struct
 + * definitions including another struct in system header files. To avoid having
 + * to fully disable strict-aliasing warnings, provide a mechanism to hide the
 + * typecast from aliasing for now. A cleaner solution will hopefully be found
 + * in the future to handle these cases.
 + */
 +void * __hide_aliasing_typecast(void *foo);
 +#define aliasing_hide_typecast(a,t) (t *) __hide_aliasing_typecast((a))
 +
 +#ifdef CONFIG_VALGRIND
 +#include <valgrind/memcheck.h>
 +#define WPA_MEM_DEFINED(ptr, len) VALGRIND_MAKE_MEM_DEFINED((ptr), (len))
 +#else /* CONFIG_VALGRIND */
 +#define WPA_MEM_DEFINED(ptr, len) do { } while (0)
 +#endif /* CONFIG_VALGRIND */
 +
 +#endif /* COMMON_H */
index 4a565ebdbd0a989d0ccd3de3989054e3e3f9a399,0000000000000000000000000000000000000000..8647229b8eb5ff4c95ab56246b87f736e0039202
mode 100644,000000..100644
--- /dev/null
@@@ -1,1113 -1,0 +1,1142 @@@
- #ifdef CONFIG_ELOOP_EPOLL
 +/*
 + * Event loop based on select() loop
 + * Copyright (c) 2002-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 <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 <poll.h>
 +#endif /* CONFIG_ELOOP_POLL */
 +
 +#ifdef CONFIG_ELOOP_EPOLL
 +#include <sys/epoll.h>
 +#endif /* CONFIG_ELOOP_EPOLL */
 +
 +struct eloop_sock {
 +      int sock;
 +      void *eloop_data;
 +      void *user_data;
 +      eloop_sock_handler handler;
 +      WPA_TRACE_REF(eloop);
 +      WPA_TRACE_REF(user);
 +      WPA_TRACE_INFO
 +};
 +
 +struct eloop_timeout {
 +      struct dl_list list;
 +      struct os_reltime time;
 +      void *eloop_data;
 +      void *user_data;
 +      eloop_timeout_handler handler;
 +      WPA_TRACE_REF(eloop);
 +      WPA_TRACE_REF(user);
 +      WPA_TRACE_INFO
 +};
 +
 +struct eloop_signal {
 +      int sig;
 +      void *user_data;
 +      eloop_signal_handler handler;
 +      int signaled;
 +};
 +
 +struct eloop_sock_table {
 +      int count;
 +      struct eloop_sock *table;
- #else /* CONFIG_ELOOP_EPOLL */
 +      eloop_event_type type;
- #endif /* CONFIG_ELOOP_EPOLL */
 +      int changed;
- #ifndef CONFIG_ELOOP_EPOLL
 +};
 +
 +struct eloop_data {
 +      int max_sock;
 +
 +      int count; /* sum of all table counts */
 +#ifdef CONFIG_ELOOP_POLL
 +      int max_pollfd_map; /* number of pollfds_map currently allocated */
 +      int max_poll_fds; /* number of pollfds currently allocated */
 +      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;
 +
 +      struct dl_list timeout;
 +
 +      int signal_count;
 +      struct eloop_signal *signals;
 +      int signaled;
 +      int pending_terminate;
 +
 +      int terminate;
 +};
 +
 +static struct eloop_data eloop;
 +
 +
 +#ifdef WPA_TRACE
 +
 +static void eloop_sigsegv_handler(int sig)
 +{
 +      wpa_trace_show("eloop SIGSEGV");
 +      abort();
 +}
 +
 +static void eloop_trace_sock_add_ref(struct eloop_sock_table *table)
 +{
 +      int i;
 +      if (table == NULL || table->table == NULL)
 +              return;
 +      for (i = 0; i < table->count; i++) {
 +              wpa_trace_add_ref(&table->table[i], eloop,
 +                                table->table[i].eloop_data);
 +              wpa_trace_add_ref(&table->table[i], user,
 +                                table->table[i].user_data);
 +      }
 +}
 +
 +
 +static void eloop_trace_sock_remove_ref(struct eloop_sock_table *table)
 +{
 +      int i;
 +      if (table == NULL || table->table == NULL)
 +              return;
 +      for (i = 0; i < table->count; i++) {
 +              wpa_trace_remove_ref(&table->table[i], eloop,
 +                                   table->table[i].eloop_data);
 +              wpa_trace_remove_ref(&table->table[i], user,
 +                                   table->table[i].user_data);
 +      }
 +}
 +
 +#else /* WPA_TRACE */
 +
 +#define eloop_trace_sock_add_ref(table) do { } while (0)
 +#define eloop_trace_sock_remove_ref(table) do { } while (0)
 +
 +#endif /* WPA_TRACE */
 +
 +
 +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 */
 +      return 0;
 +}
 +
 +
 +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;
 +
 +      if (sock > eloop.max_sock)
 +              new_max_sock = sock;
 +      else
 +              new_max_sock = eloop.max_sock;
 +
 +      if (table == NULL)
 +              return -1;
 +
 +#ifdef CONFIG_ELOOP_POLL
 +      if (new_max_sock >= eloop.max_pollfd_map) {
 +              struct pollfd **nmap;
 +              nmap = os_realloc_array(eloop.pollfds_map, new_max_sock + 50,
 +                                      sizeof(struct pollfd *));
 +              if (nmap == NULL)
 +                      return -1;
 +
 +              eloop.max_pollfd_map = new_max_sock + 50;
 +              eloop.pollfds_map = nmap;
 +      }
 +
 +      if (eloop.count + 1 > eloop.max_poll_fds) {
 +              struct pollfd *n;
 +              int nmax = eloop.count + 1 + 50;
 +              n = os_realloc_array(eloop.pollfds, nmax,
 +                                   sizeof(struct pollfd));
 +              if (n == NULL)
 +                      return -1;
 +
 +              eloop.max_poll_fds = nmax;
 +              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) {
 +              eloop_trace_sock_add_ref(table);
 +              return -1;
 +      }
 +
 +      tmp[table->count].sock = sock;
 +      tmp[table->count].eloop_data = eloop_data;
 +      tmp[table->count].user_data = user_data;
 +      tmp[table->count].handler = handler;
 +      wpa_trace_record(&tmp[table->count]);
 +      table->count++;
 +      table->table = tmp;
 +      eloop.max_sock = new_max_sock;
 +      eloop.count++;
- #endif /* CONFIG_ELOOP_EPOLL */
 +      table->changed = 1;
- #ifndef 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;
 +}
 +
 +
 +static void eloop_sock_table_remove_sock(struct eloop_sock_table *table,
 +                                         int sock)
 +{
 +      int i;
 +
 +      if (table == NULL || table->table == NULL || table->count == 0)
 +              return;
 +
 +      for (i = 0; i < table->count; i++) {
 +              if (table->table[i].sock == sock)
 +                      break;
 +      }
 +      if (i == table->count)
 +              return;
 +      eloop_trace_sock_remove_ref(table);
 +      if (i != table->count - 1) {
 +              os_memmove(&table->table[i], &table->table[i + 1],
 +                         (table->count - i - 1) *
 +                         sizeof(struct eloop_sock));
 +      }
 +      table->count--;
 +      eloop.count--;
- #endif /* CONFIG_ELOOP_EPOLL */
 +      table->changed = 1;
-       return eloop.terminate;
 +      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 */
 +}
 +
 +
 +#ifdef CONFIG_ELOOP_POLL
 +
 +static struct pollfd * find_pollfd(struct pollfd **pollfds_map, int fd, int mx)
 +{
 +      if (fd < mx && fd >= 0)
 +              return pollfds_map[fd];
 +      return NULL;
 +}
 +
 +
 +static int eloop_sock_table_set_fds(struct eloop_sock_table *readers,
 +                                  struct eloop_sock_table *writers,
 +                                  struct eloop_sock_table *exceptions,
 +                                  struct pollfd *pollfds,
 +                                  struct pollfd **pollfds_map,
 +                                  int max_pollfd_map)
 +{
 +      int i;
 +      int nxt = 0;
 +      int fd;
 +      struct pollfd *pfd;
 +
 +      /* Clear pollfd lookup map. It will be re-populated below. */
 +      os_memset(pollfds_map, 0, sizeof(struct pollfd *) * max_pollfd_map);
 +
 +      if (readers && readers->table) {
 +              for (i = 0; i < readers->count; i++) {
 +                      fd = readers->table[i].sock;
 +                      assert(fd >= 0 && fd < max_pollfd_map);
 +                      pollfds[nxt].fd = fd;
 +                      pollfds[nxt].events = POLLIN;
 +                      pollfds[nxt].revents = 0;
 +                      pollfds_map[fd] = &(pollfds[nxt]);
 +                      nxt++;
 +              }
 +      }
 +
 +      if (writers && writers->table) {
 +              for (i = 0; i < writers->count; i++) {
 +                      /*
 +                       * See if we already added this descriptor, update it
 +                       * if so.
 +                       */
 +                      fd = writers->table[i].sock;
 +                      assert(fd >= 0 && fd < max_pollfd_map);
 +                      pfd = pollfds_map[fd];
 +                      if (!pfd) {
 +                              pfd = &(pollfds[nxt]);
 +                              pfd->events = 0;
 +                              pfd->fd = fd;
 +                              pollfds[i].revents = 0;
 +                              pollfds_map[fd] = pfd;
 +                              nxt++;
 +                      }
 +                      pfd->events |= POLLOUT;
 +              }
 +      }
 +
 +      /*
 +       * Exceptions are always checked when using poll, but I suppose it's
 +       * possible that someone registered a socket *only* for exception
 +       * handling. Set the POLLIN bit in this case.
 +       */
 +      if (exceptions && exceptions->table) {
 +              for (i = 0; i < exceptions->count; i++) {
 +                      /*
 +                       * See if we already added this descriptor, just use it
 +                       * if so.
 +                       */
 +                      fd = exceptions->table[i].sock;
 +                      assert(fd >= 0 && fd < max_pollfd_map);
 +                      pfd = pollfds_map[fd];
 +                      if (!pfd) {
 +                              pfd = &(pollfds[nxt]);
 +                              pfd->events = POLLIN;
 +                              pfd->fd = fd;
 +                              pollfds[i].revents = 0;
 +                              pollfds_map[fd] = pfd;
 +                              nxt++;
 +                      }
 +              }
 +      }
 +
 +      return nxt;
 +}
 +
 +
 +static int eloop_sock_table_dispatch_table(struct eloop_sock_table *table,
 +                                         struct pollfd **pollfds_map,
 +                                         int max_pollfd_map,
 +                                         short int revents)
 +{
 +      int i;
 +      struct pollfd *pfd;
 +
 +      if (!table || !table->table)
 +              return 0;
 +
 +      table->changed = 0;
 +      for (i = 0; i < table->count; i++) {
 +              pfd = find_pollfd(pollfds_map, table->table[i].sock,
 +                                max_pollfd_map);
 +              if (!pfd)
 +                      continue;
 +
 +              if (!(pfd->revents & revents))
 +                      continue;
 +
 +              table->table[i].handler(table->table[i].sock,
 +                                      table->table[i].eloop_data,
 +                                      table->table[i].user_data);
 +              if (table->changed)
 +                      return 1;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +static void eloop_sock_table_dispatch(struct eloop_sock_table *readers,
 +                                    struct eloop_sock_table *writers,
 +                                    struct eloop_sock_table *exceptions,
 +                                    struct pollfd **pollfds_map,
 +                                    int max_pollfd_map)
 +{
 +      if (eloop_sock_table_dispatch_table(readers, pollfds_map,
 +                                          max_pollfd_map, POLLIN | POLLERR |
 +                                          POLLHUP))
 +              return; /* pollfds may be invalid at this point */
 +
 +      if (eloop_sock_table_dispatch_table(writers, pollfds_map,
 +                                          max_pollfd_map, POLLOUT))
 +              return; /* pollfds may be invalid at this point */
 +
 +      eloop_sock_table_dispatch_table(exceptions, pollfds_map,
 +                                      max_pollfd_map, POLLERR | POLLHUP);
 +}
 +
 +#endif /* CONFIG_ELOOP_POLL */
 +
 +#ifdef CONFIG_ELOOP_SELECT
 +
 +static void eloop_sock_table_set_fds(struct eloop_sock_table *table,
 +                                   fd_set *fds)
 +{
 +      int i;
 +
 +      FD_ZERO(fds);
 +
 +      if (table->table == NULL)
 +              return;
 +
 +      for (i = 0; i < table->count; i++) {
 +              assert(table->table[i].sock >= 0);
 +              FD_SET(table->table[i].sock, fds);
 +      }
 +}
 +
 +
 +static void eloop_sock_table_dispatch(struct eloop_sock_table *table,
 +                                    fd_set *fds)
 +{
 +      int i;
 +
 +      if (table == NULL || table->table == NULL)
 +              return;
 +
 +      table->changed = 0;
 +      for (i = 0; i < table->count; i++) {
 +              if (FD_ISSET(table->table[i].sock, fds)) {
 +                      table->table[i].handler(table->table[i].sock,
 +                                              table->table[i].eloop_data,
 +                                              table->table[i].user_data);
 +                      if (table->changed)
 +                              break;
 +              }
 +      }
 +}
 +
 +#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);
++              if (eloop.readers.changed ||
++                  eloop.writers.changed ||
++                  eloop.exceptions.changed)
++                      break;
 +      }
 +}
 +#endif /* CONFIG_ELOOP_EPOLL */
 +
 +
 +static void eloop_sock_table_destroy(struct eloop_sock_table *table)
 +{
 +      if (table) {
 +              int i;
 +              for (i = 0; i < table->count && table->table; i++) {
 +                      wpa_printf(MSG_INFO, "ELOOP: remaining socket: "
 +                                 "sock=%d eloop_data=%p user_data=%p "
 +                                 "handler=%p",
 +                                 table->table[i].sock,
 +                                 table->table[i].eloop_data,
 +                                 table->table[i].user_data,
 +                                 table->table[i].handler);
 +                      wpa_trace_dump_funcname("eloop unregistered socket "
 +                                              "handler",
 +                                              table->table[i].handler);
 +                      wpa_trace_dump("eloop sock", &table->table[i]);
 +              }
 +              os_free(table->table);
 +      }
 +}
 +
 +
 +int eloop_register_read_sock(int sock, eloop_sock_handler handler,
 +                           void *eloop_data, void *user_data)
 +{
 +      return eloop_register_sock(sock, EVENT_TYPE_READ, handler,
 +                                 eloop_data, user_data);
 +}
 +
 +
 +void eloop_unregister_read_sock(int sock)
 +{
 +      eloop_unregister_sock(sock, EVENT_TYPE_READ);
 +}
 +
 +
 +static struct eloop_sock_table *eloop_get_sock_table(eloop_event_type type)
 +{
 +      switch (type) {
 +      case EVENT_TYPE_READ:
 +              return &eloop.readers;
 +      case EVENT_TYPE_WRITE:
 +              return &eloop.writers;
 +      case EVENT_TYPE_EXCEPTION:
 +              return &eloop.exceptions;
 +      }
 +
 +      return NULL;
 +}
 +
 +
 +int eloop_register_sock(int sock, eloop_event_type type,
 +                      eloop_sock_handler handler,
 +                      void *eloop_data, void *user_data)
 +{
 +      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);
 +}
 +
 +
 +void eloop_unregister_sock(int sock, eloop_event_type type)
 +{
 +      struct eloop_sock_table *table;
 +
 +      table = eloop_get_sock_table(type);
 +      eloop_sock_table_remove_sock(table, sock);
 +}
 +
 +
 +int eloop_register_timeout(unsigned int secs, unsigned int usecs,
 +                         eloop_timeout_handler handler,
 +                         void *eloop_data, void *user_data)
 +{
 +      struct eloop_timeout *timeout, *tmp;
 +      os_time_t now_sec;
 +
 +      timeout = os_zalloc(sizeof(*timeout));
 +      if (timeout == NULL)
 +              return -1;
 +      if (os_get_reltime(&timeout->time) < 0) {
 +              os_free(timeout);
 +              return -1;
 +      }
 +      now_sec = timeout->time.sec;
 +      timeout->time.sec += secs;
 +      if (timeout->time.sec < now_sec) {
 +              /*
 +               * Integer overflow - assume long enough timeout to be assumed
 +               * to be infinite, i.e., the timeout would never happen.
 +               */
 +              wpa_printf(MSG_DEBUG, "ELOOP: Too long timeout (secs=%u) to "
 +                         "ever happen - ignore it", secs);
 +              os_free(timeout);
 +              return 0;
 +      }
 +      timeout->time.usec += usecs;
 +      while (timeout->time.usec >= 1000000) {
 +              timeout->time.sec++;
 +              timeout->time.usec -= 1000000;
 +      }
 +      timeout->eloop_data = eloop_data;
 +      timeout->user_data = user_data;
 +      timeout->handler = handler;
 +      wpa_trace_add_ref(timeout, eloop, eloop_data);
 +      wpa_trace_add_ref(timeout, user, user_data);
 +      wpa_trace_record(timeout);
 +
 +      /* Maintain timeouts in order of increasing time */
 +      dl_list_for_each(tmp, &eloop.timeout, struct eloop_timeout, list) {
 +              if (os_reltime_before(&timeout->time, &tmp->time)) {
 +                      dl_list_add(tmp->list.prev, &timeout->list);
 +                      return 0;
 +              }
 +      }
 +      dl_list_add_tail(&eloop.timeout, &timeout->list);
 +
 +      return 0;
 +}
 +
 +
 +static void eloop_remove_timeout(struct eloop_timeout *timeout)
 +{
 +      dl_list_del(&timeout->list);
 +      wpa_trace_remove_ref(timeout, eloop, timeout->eloop_data);
 +      wpa_trace_remove_ref(timeout, user, timeout->user_data);
 +      os_free(timeout);
 +}
 +
 +
 +int eloop_cancel_timeout(eloop_timeout_handler handler,
 +                       void *eloop_data, void *user_data)
 +{
 +      struct eloop_timeout *timeout, *prev;
 +      int removed = 0;
 +
 +      dl_list_for_each_safe(timeout, prev, &eloop.timeout,
 +                            struct eloop_timeout, list) {
 +              if (timeout->handler == handler &&
 +                  (timeout->eloop_data == eloop_data ||
 +                   eloop_data == ELOOP_ALL_CTX) &&
 +                  (timeout->user_data == user_data ||
 +                   user_data == ELOOP_ALL_CTX)) {
 +                      eloop_remove_timeout(timeout);
 +                      removed++;
 +              }
 +      }
 +
 +      return removed;
 +}
 +
 +
 +int eloop_cancel_timeout_one(eloop_timeout_handler handler,
 +                           void *eloop_data, void *user_data,
 +                           struct os_reltime *remaining)
 +{
 +      struct eloop_timeout *timeout, *prev;
 +      int removed = 0;
 +      struct os_reltime now;
 +
 +      os_get_reltime(&now);
 +      remaining->sec = remaining->usec = 0;
 +
 +      dl_list_for_each_safe(timeout, prev, &eloop.timeout,
 +                            struct eloop_timeout, list) {
 +              if (timeout->handler == handler &&
 +                  (timeout->eloop_data == eloop_data) &&
 +                  (timeout->user_data == user_data)) {
 +                      removed = 1;
 +                      if (os_reltime_before(&now, &timeout->time))
 +                              os_reltime_sub(&timeout->time, &now, remaining);
 +                      eloop_remove_timeout(timeout);
 +                      break;
 +              }
 +      }
 +      return removed;
 +}
 +
 +
 +int eloop_is_timeout_registered(eloop_timeout_handler handler,
 +                              void *eloop_data, void *user_data)
 +{
 +      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)
 +                      return 1;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +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)
 +{
 +      wpa_printf(MSG_ERROR, "eloop: could not process SIGINT or SIGTERM in "
 +                 "two seconds. Looks like there\n"
 +                 "is a bug that ends up in a busy loop that "
 +                 "prevents clean shutdown.\n"
 +                 "Killing program forcefully.\n");
 +      exit(1);
 +}
 +#endif /* CONFIG_NATIVE_WINDOWS */
 +
 +
 +static void eloop_handle_signal(int sig)
 +{
 +      int i;
 +
 +#ifndef CONFIG_NATIVE_WINDOWS
 +      if ((sig == SIGINT || sig == SIGTERM) && !eloop.pending_terminate) {
 +              /* Use SIGALRM to break out from potential busy loops that
 +               * would not allow the program to be killed. */
 +              eloop.pending_terminate = 1;
 +              signal(SIGALRM, eloop_handle_alarm);
 +              alarm(2);
 +      }
 +#endif /* CONFIG_NATIVE_WINDOWS */
 +
 +      eloop.signaled++;
 +      for (i = 0; i < eloop.signal_count; i++) {
 +              if (eloop.signals[i].sig == sig) {
 +                      eloop.signals[i].signaled++;
 +                      break;
 +              }
 +      }
 +}
 +
 +
 +static void eloop_process_pending_signals(void)
 +{
 +      int i;
 +
 +      if (eloop.signaled == 0)
 +              return;
 +      eloop.signaled = 0;
 +
 +      if (eloop.pending_terminate) {
 +#ifndef CONFIG_NATIVE_WINDOWS
 +              alarm(0);
 +#endif /* CONFIG_NATIVE_WINDOWS */
 +              eloop.pending_terminate = 0;
 +      }
 +
 +      for (i = 0; i < eloop.signal_count; i++) {
 +              if (eloop.signals[i].signaled) {
 +                      eloop.signals[i].signaled = 0;
 +                      eloop.signals[i].handler(eloop.signals[i].sig,
 +                                               eloop.signals[i].user_data);
 +              }
 +      }
 +}
 +
 +
 +int eloop_register_signal(int sig, eloop_signal_handler handler,
 +                        void *user_data)
 +{
 +      struct eloop_signal *tmp;
 +
 +      tmp = os_realloc_array(eloop.signals, eloop.signal_count + 1,
 +                             sizeof(struct eloop_signal));
 +      if (tmp == NULL)
 +              return -1;
 +
 +      tmp[eloop.signal_count].sig = sig;
 +      tmp[eloop.signal_count].user_data = user_data;
 +      tmp[eloop.signal_count].handler = handler;
 +      tmp[eloop.signal_count].signaled = 0;
 +      eloop.signal_count++;
 +      eloop.signals = tmp;
 +      signal(sig, eloop_handle_signal);
 +
 +      return 0;
 +}
 +
 +
 +int eloop_register_signal_terminate(eloop_signal_handler handler,
 +                                  void *user_data)
 +{
 +      int ret = eloop_register_signal(SIGINT, handler, user_data);
 +      if (ret == 0)
 +              ret = eloop_register_signal(SIGTERM, handler, user_data);
 +      return ret;
 +}
 +
 +
 +int eloop_register_signal_reconfig(eloop_signal_handler handler,
 +                                 void *user_data)
 +{
 +#ifdef CONFIG_NATIVE_WINDOWS
 +      return 0;
 +#else /* CONFIG_NATIVE_WINDOWS */
 +      return eloop_register_signal(SIGHUP, handler, user_data);
 +#endif /* CONFIG_NATIVE_WINDOWS */
 +}
 +
 +
 +void eloop_run(void)
 +{
 +#ifdef CONFIG_ELOOP_POLL
 +      int num_poll_fds;
 +      int timeout_ms = 0;
 +#endif /* CONFIG_ELOOP_POLL */
 +#ifdef CONFIG_ELOOP_SELECT
 +      fd_set *rfds, *wfds, *efds;
 +      struct timeval _tv;
 +#endif /* CONFIG_ELOOP_SELECT */
 +#ifdef CONFIG_ELOOP_EPOLL
 +      int timeout_ms = -1;
 +#endif /* CONFIG_ELOOP_EPOLL */
 +      int res;
 +      struct os_reltime tv, now;
 +
 +#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_SELECT */
 +
 +      while (!eloop.terminate &&
 +             (!dl_list_empty(&eloop.timeout) || eloop.readers.count > 0 ||
 +              eloop.writers.count > 0 || eloop.exceptions.count > 0)) {
 +              struct eloop_timeout *timeout;
++
++              if (eloop.pending_terminate) {
++                      /*
++                       * This may happen in some corner cases where a signal
++                       * is received during a blocking operation. We need to
++                       * process the pending signals and exit if requested to
++                       * avoid hitting the SIGALRM limit if the blocking
++                       * operation took more than two seconds.
++                       */
++                      eloop_process_pending_signals();
++                      if (eloop.terminate)
++                              break;
++              }
++
 +              timeout = dl_list_first(&eloop.timeout, struct eloop_timeout,
 +                                      list);
 +              if (timeout) {
 +                      os_get_reltime(&now);
 +                      if (os_reltime_before(&now, &timeout->time))
 +                              os_reltime_sub(&timeout->time, &now, &tv);
 +                      else
 +                              tv.sec = tv.usec = 0;
 +#if defined(CONFIG_ELOOP_POLL) || defined(CONFIG_ELOOP_EPOLL)
 +                      timeout_ms = tv.sec * 1000 + tv.usec / 1000;
 +#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_SELECT */
 +              }
 +
 +#ifdef CONFIG_ELOOP_POLL
 +              num_poll_fds = eloop_sock_table_set_fds(
 +                      &eloop.readers, &eloop.writers, &eloop.exceptions,
 +                      eloop.pollfds, eloop.pollfds_map,
 +                      eloop.max_pollfd_map);
 +              res = poll(eloop.pollfds, num_poll_fds,
 +                         timeout ? timeout_ms : -1);
 +#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) {
 +                      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;
 +              }
++
++              eloop.readers.changed = 0;
++              eloop.writers.changed = 0;
++              eloop.exceptions.changed = 0;
++
 +              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_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 =
 +                                      timeout->handler;
 +                              eloop_remove_timeout(timeout);
 +                              handler(eloop_data, user_data);
 +                      }
 +
 +              }
 +
 +              if (res <= 0)
 +                      continue;
 +
++              if (eloop.readers.changed ||
++                  eloop.writers.changed ||
++                  eloop.exceptions.changed) {
++                       /*
++                        * Sockets may have been closed and reopened with the
++                        * same FD in the signal or timeout handlers, so we
++                        * must skip the previous results and check again
++                        * whether any of the currently registered sockets have
++                        * events.
++                        */
++                      continue;
++              }
++
 +#ifdef CONFIG_ELOOP_POLL
 +              eloop_sock_table_dispatch(&eloop.readers, &eloop.writers,
 +                                        &eloop.exceptions, eloop.pollfds_map,
 +                                        eloop.max_pollfd_map);
 +#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_SELECT */
 +#ifdef CONFIG_ELOOP_EPOLL
 +              eloop_sock_table_dispatch(eloop.epoll_events, res);
 +#endif /* CONFIG_ELOOP_EPOLL */
 +      }
 +
 +      eloop.terminate = 0;
 +out:
 +#ifdef CONFIG_ELOOP_SELECT
 +      os_free(rfds);
 +      os_free(wfds);
 +      os_free(efds);
 +#endif /* CONFIG_ELOOP_SELECT */
 +      return;
 +}
 +
 +
 +void eloop_terminate(void)
 +{
 +      eloop.terminate = 1;
 +}
 +
 +
 +void eloop_destroy(void)
 +{
 +      struct eloop_timeout *timeout, *prev;
 +      struct os_reltime now;
 +
 +      os_get_reltime(&now);
 +      dl_list_for_each_safe(timeout, prev, &eloop.timeout,
 +                            struct eloop_timeout, list) {
 +              int sec, usec;
 +              sec = timeout->time.sec - now.sec;
 +              usec = timeout->time.usec - now.usec;
 +              if (timeout->time.usec < now.usec) {
 +                      sec--;
 +                      usec += 1000000;
 +              }
 +              wpa_printf(MSG_INFO, "ELOOP: remaining timeout: %d.%06d "
 +                         "eloop_data=%p user_data=%p handler=%p",
 +                         sec, usec, timeout->eloop_data, timeout->user_data,
 +                         timeout->handler);
 +              wpa_trace_dump_funcname("eloop unregistered timeout handler",
 +                                      timeout->handler);
 +              wpa_trace_dump("eloop timeout", timeout);
 +              eloop_remove_timeout(timeout);
 +      }
 +      eloop_sock_table_destroy(&eloop.readers);
 +      eloop_sock_table_destroy(&eloop.writers);
 +      eloop_sock_table_destroy(&eloop.exceptions);
 +      os_free(eloop.signals);
 +
 +#ifdef CONFIG_ELOOP_POLL
 +      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 */
 +}
 +
 +
 +int eloop_terminated(void)
 +{
++      return eloop.terminate || eloop.pending_terminate;
 +}
 +
 +
 +void eloop_wait_for_read_sock(int sock)
 +{
 +#ifdef CONFIG_ELOOP_POLL
 +      struct pollfd pfd;
 +
 +      if (sock < 0)
 +              return;
 +
 +      os_memset(&pfd, 0, sizeof(pfd));
 +      pfd.fd = sock;
 +      pfd.events = POLLIN;
 +
 +      poll(&pfd, 1, -1);
 +#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)
 +              return;
 +
 +      FD_ZERO(&rfds);
 +      FD_SET(sock, &rfds);
 +      select(sock + 1, &rfds, NULL, NULL, NULL);
 +#endif /* defined(CONFIG_ELOOP_SELECT) || defined(CONFIG_ELOOP_EPOLL) */
 +}
 +
 +#ifdef CONFIG_ELOOP_SELECT
 +#undef CONFIG_ELOOP_SELECT
 +#endif /* CONFIG_ELOOP_SELECT */
index b38cf796ca2ac74babaef12f74b4a731412c1b07,0000000000000000000000000000000000000000..653eb541ab472e36214152afe1e40b0b52c283d4
mode 100644,000000..100644
--- /dev/null
@@@ -1,1641 -1,0 +1,1649 @@@
-       if (ctx->cert_cb == NULL)
 +/*
 + * 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;
 +
-       wpa_printf(MSG_DEBUG, "curl_cb_ssl_verify");
++      if (ctx->cert_cb == NULL) {
++              wpa_printf(MSG_DEBUG, "%s: no cert_cb configured", __func__);
 +              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);
 +
-                  "username=%s", address, ca_fname, username);
++      wpa_printf(MSG_DEBUG, "curl_cb_ssl_verify, preverify_ok: %d",
++                 preverify_ok);
 +
 +      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;
++#ifdef EAP_TLS_OPENSSL
++      const char *extra = " tls=openssl";
++#else /* EAP_TLS_OPENSSL */
++      const char *extra = "";
++#endif /* EAP_TLS_OPENSSL */
 +
 +      wpa_printf(MSG_DEBUG, "Start HTTP client: address=%s ca_fname=%s "
++                 "username=%s%s", address, ca_fname, username, extra);
 +
 +      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 6c6ec87d0eaf43079bb9781dfa3406a466c60644,0000000000000000000000000000000000000000..75513fc8c1efd6eb72f18769ff38b90a3a422aba
mode 100644,000000..100644
--- /dev/null
@@@ -1,50 -1,0 +1,45 @@@
- #ifndef CONFIG_TI_COMPILER
 +/*
 + * wpa_supplicant/hostapd - Default include files
 + * Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi>
 + *
 + * This software may be distributed under the terms of the BSD license.
 + * See README for more details.
 + *
 + * This header file is included into all C files so that commonly used header
 + * files can be selected with OS specific ifdef blocks in one place instead of
 + * having to have OS/C library specific selection in many files.
 + */
 +
 +#ifndef INCLUDES_H
 +#define INCLUDES_H
 +
 +/* Include possible build time configuration before including anything else */
 +#include "build_config.h"
 +
 +#include <stdlib.h>
++#include <stddef.h>
 +#include <stdio.h>
 +#include <stdarg.h>
 +#include <string.h>
 +#ifndef _WIN32_WCE
- #endif /* CONFIG_TI_COMPILER */
 +#include <signal.h>
 +#include <sys/types.h>
- #ifndef CONFIG_TI_COMPILER
 +#include <errno.h>
 +#endif /* _WIN32_WCE */
 +#include <ctype.h>
 +
- #endif /* CONFIG_TI_COMPILER */
 +#ifndef _MSC_VER
 +#include <unistd.h>
 +#endif /* _MSC_VER */
- #ifndef CONFIG_TI_COMPILER
 +
 +#ifndef CONFIG_NATIVE_WINDOWS
- #endif /* CONFIG_TI_COMPILER */
 +#include <sys/socket.h>
 +#include <netinet/in.h>
 +#include <arpa/inet.h>
 +#ifndef __vxworks
 +#include <sys/uio.h>
 +#include <sys/time.h>
 +#endif /* __vxworks */
 +#endif /* CONFIG_NATIVE_WINDOWS */
 +
 +#endif /* INCLUDES_H */
index 77250d6371c9156dbb1218ae408511f7033747df,0000000000000000000000000000000000000000..9e496fb6597830fc5d5146065b4c5ca5b23984c6
mode 100644,000000..100644
--- /dev/null
@@@ -1,649 -1,0 +1,664 @@@
 +/*
 + * OS specific functions
 + * Copyright (c) 2005-2009, Jouni Malinen <j@w1.fi>
 + *
 + * This software may be distributed under the terms of the BSD license.
 + * See README for more details.
 + */
 +
 +#ifndef OS_H
 +#define OS_H
 +
 +typedef long os_time_t;
 +
 +/**
 + * os_sleep - Sleep (sec, usec)
 + * @sec: Number of seconds to sleep
 + * @usec: Number of microseconds to sleep
 + */
 +void os_sleep(os_time_t sec, os_time_t usec);
 +
 +struct os_time {
 +      os_time_t sec;
 +      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
 + * Returns: 0 on success, -1 on failure
 + */
 +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);
 +}
 +
 +
 +static inline int os_reltime_initialized(struct os_reltime *t)
 +{
 +      return t->sec != 0 || t->usec != 0;
 +}
 +
 +
 +/**
 + * os_mktime - Convert broken-down time into seconds since 1970-01-01
 + * @year: Four digit year
 + * @month: Month (1 .. 12)
 + * @day: Day of month (1 .. 31)
 + * @hour: Hour (0 .. 23)
 + * @min: Minute (0 .. 59)
 + * @sec: Second (0 .. 60)
 + * @t: Buffer for returning calendar time representation (seconds since
 + * 1970-01-01 00:00:00)
 + * Returns: 0 on success, -1 on failure
 + *
 + * Note: The result is in seconds from Epoch, i.e., in UTC, not in local time
 + * which is used by POSIX mktime().
 + */
 +int os_mktime(int year, int month, int day, int hour, int min, int sec,
 +            os_time_t *t);
 +
 +struct os_tm {
 +      int sec; /* 0..59 or 60 for leap seconds */
 +      int min; /* 0..59 */
 +      int hour; /* 0..23 */
 +      int day; /* 1..31 */
 +      int month; /* 1..12 */
 +      int year; /* Four digit year */
 +};
 +
 +int os_gmtime(os_time_t t, struct os_tm *tm);
 +
 +/**
 + * os_daemonize - Run in the background (detach from the controlling terminal)
 + * @pid_file: File name to write the process ID to or %NULL to skip this
 + * Returns: 0 on success, -1 on failure
 + */
 +int os_daemonize(const char *pid_file);
 +
 +/**
 + * os_daemonize_terminate - Stop running in the background (remove pid file)
 + * @pid_file: File name to write the process ID to or %NULL to skip this
 + */
 +void os_daemonize_terminate(const char *pid_file);
 +
 +/**
 + * os_get_random - Get cryptographically strong pseudo random data
 + * @buf: Buffer for pseudo random data
 + * @len: Length of the buffer
 + * Returns: 0 on success, -1 on failure
 + */
 +int os_get_random(unsigned char *buf, size_t len);
 +
 +/**
 + * os_random - Get pseudo random value (not necessarily very strong)
 + * Returns: Pseudo random value
 + */
 +unsigned long os_random(void);
 +
 +/**
 + * os_rel2abs_path - Get an absolute path for a file
 + * @rel_path: Relative path to a file
 + * Returns: Absolute path for the file or %NULL on failure
 + *
 + * This function tries to convert a relative path of a file to an absolute path
 + * in order for the file to be found even if current working directory has
 + * changed. The returned value is allocated and caller is responsible for
 + * freeing it. It is acceptable to just return the same path in an allocated
 + * buffer, e.g., return strdup(rel_path). This function is only used to find
 + * configuration files when os_daemonize() may have changed the current working
 + * directory and relative path would be pointing to a different location.
 + */
 +char * os_rel2abs_path(const char *rel_path);
 +
 +/**
 + * os_program_init - Program initialization (called at start)
 + * Returns: 0 on success, -1 on failure
 + *
 + * This function is called when a programs starts. If there are any OS specific
 + * processing that is needed, it can be placed here. It is also acceptable to
 + * just return 0 if not special processing is needed.
 + */
 +int os_program_init(void);
 +
 +/**
 + * os_program_deinit - Program deinitialization (called just before exit)
 + *
 + * This function is called just before a program exists. If there are any OS
 + * specific processing, e.g., freeing resourced allocated in os_program_init(),
 + * it should be done here. It is also acceptable for this function to do
 + * nothing.
 + */
 +void os_program_deinit(void);
 +
 +/**
 + * os_setenv - Set environment variable
 + * @name: Name of the variable
 + * @value: Value to set to the variable
 + * @overwrite: Whether existing variable should be overwritten
 + * Returns: 0 on success, -1 on error
 + *
 + * This function is only used for wpa_cli action scripts. OS wrapper does not
 + * need to implement this if such functionality is not needed.
 + */
 +int os_setenv(const char *name, const char *value, int overwrite);
 +
 +/**
 + * os_unsetenv - Delete environent variable
 + * @name: Name of the variable
 + * Returns: 0 on success, -1 on error
 + *
 + * This function is only used for wpa_cli action scripts. OS wrapper does not
 + * need to implement this if such functionality is not needed.
 + */
 +int os_unsetenv(const char *name);
 +
 +/**
 + * os_readfile - Read a file to an allocated memory buffer
 + * @name: Name of the file to read
 + * @len: For returning the length of the allocated buffer
 + * Returns: Pointer to the allocated buffer or %NULL on failure
 + *
 + * This function allocates memory and reads the given file to this buffer. Both
 + * binary and text files can be read with this function. The caller is
 + * responsible for freeing the returned buffer with os_free().
 + */
 +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_fdatasync - Sync a file's (for a given stream) state with storage device
++ * @stream: the stream to be flushed
++ * Returns: 0 if the operation succeeded or -1 on failure
++ */
++int os_fdatasync(FILE *stream);
++
 +/**
 + * os_zalloc - Allocate and zero memory
 + * @size: Number of bytes to allocate
 + * Returns: Pointer to allocated and zeroed memory or %NULL on failure
 + *
 + * Caller is responsible for freeing the returned buffer with os_free().
 + */
 +void * os_zalloc(size_t size);
 +
 +/**
 + * os_calloc - Allocate and zero memory for an array
 + * @nmemb: Number of members in the array
 + * @size: Number of bytes in each member
 + * Returns: Pointer to allocated and zeroed memory or %NULL on failure
 + *
 + * This function can be used as a wrapper for os_zalloc(nmemb * size) when an
 + * allocation is used for an array. The main benefit over os_zalloc() is in
 + * having an extra check to catch integer overflows in multiplication.
 + *
 + * Caller is responsible for freeing the returned buffer with os_free().
 + */
 +static inline void * os_calloc(size_t nmemb, size_t size)
 +{
 +      if (size && nmemb > (~(size_t) 0) / size)
 +              return NULL;
 +      return os_zalloc(nmemb * size);
 +}
 +
 +
 +/*
 + * The following functions are wrapper for standard ANSI C or POSIX functions.
 + * By default, they are just defined to use the standard function name and no
 + * os_*.c implementation is needed for them. This avoids extra function calls
 + * by allowing the C pre-processor take care of the function name mapping.
 + *
 + * If the target system uses a C library that does not provide these functions,
 + * build_config.h can be used to define the wrappers to use a different
 + * function name. This can be done on function-by-function basis since the
 + * defines here are only used if build_config.h does not define the os_* name.
 + * If needed, os_*.c file can be used to implement the functions that are not
 + * included in the C library on the target system. Alternatively,
 + * OS_NO_C_LIB_DEFINES can be defined to skip all defines here in which case
 + * these functions need to be implemented in os_*.c file for the target system.
 + */
 +
 +#ifdef OS_NO_C_LIB_DEFINES
 +
 +/**
 + * os_malloc - Allocate dynamic memory
 + * @size: Size of the buffer to allocate
 + * Returns: Allocated buffer or %NULL on failure
 + *
 + * Caller is responsible for freeing the returned buffer with os_free().
 + */
 +void * os_malloc(size_t size);
 +
 +/**
 + * os_realloc - Re-allocate dynamic memory
 + * @ptr: Old buffer from os_malloc() or os_realloc()
 + * @size: Size of the new buffer
 + * Returns: Allocated buffer or %NULL on failure
 + *
 + * Caller is responsible for freeing the returned buffer with os_free().
 + * If re-allocation fails, %NULL is returned and the original buffer (ptr) is
 + * not freed and caller is still responsible for freeing it.
 + */
 +void * os_realloc(void *ptr, size_t size);
 +
 +/**
 + * os_free - Free dynamic memory
 + * @ptr: Old buffer from os_malloc() or os_realloc(); can be %NULL
 + */
 +void os_free(void *ptr);
 +
 +/**
 + * os_memcpy - Copy memory area
 + * @dest: Destination
 + * @src: Source
 + * @n: Number of bytes to copy
 + * Returns: dest
 + *
 + * The memory areas src and dst must not overlap. os_memmove() can be used with
 + * overlapping memory.
 + */
 +void * os_memcpy(void *dest, const void *src, size_t n);
 +
 +/**
 + * os_memmove - Copy memory area
 + * @dest: Destination
 + * @src: Source
 + * @n: Number of bytes to copy
 + * Returns: dest
 + *
 + * The memory areas src and dst may overlap.
 + */
 +void * os_memmove(void *dest, const void *src, size_t n);
 +
 +/**
 + * os_memset - Fill memory with a constant byte
 + * @s: Memory area to be filled
 + * @c: Constant byte
 + * @n: Number of bytes started from s to fill with c
 + * Returns: s
 + */
 +void * os_memset(void *s, int c, size_t n);
 +
 +/**
 + * os_memcmp - Compare memory areas
 + * @s1: First buffer
 + * @s2: Second buffer
 + * @n: Maximum numbers of octets to compare
 + * Returns: An integer less than, equal to, or greater than zero if s1 is
 + * found to be less than, to match, or be greater than s2. Only first n
 + * characters will be compared.
 + */
 +int os_memcmp(const void *s1, const void *s2, size_t n);
 +
 +/**
 + * os_strdup - Duplicate a string
 + * @s: Source string
 + * Returns: Allocated buffer with the string copied into it or %NULL on failure
 + *
 + * Caller is responsible for freeing the returned buffer with os_free().
 + */
 +char * os_strdup(const char *s);
 +
 +/**
 + * os_strlen - Calculate the length of a string
 + * @s: '\0' terminated string
 + * Returns: Number of characters in s (not counting the '\0' terminator)
 + */
 +size_t os_strlen(const char *s);
 +
 +/**
 + * os_strcasecmp - Compare two strings ignoring case
 + * @s1: First string
 + * @s2: Second string
 + * Returns: An integer less than, equal to, or greater than zero if s1 is
 + * found to be less than, to match, or be greatred than s2
 + */
 +int os_strcasecmp(const char *s1, const char *s2);
 +
 +/**
 + * os_strncasecmp - Compare two strings ignoring case
 + * @s1: First string
 + * @s2: Second string
 + * @n: Maximum numbers of characters to compare
 + * Returns: An integer less than, equal to, or greater than zero if s1 is
 + * found to be less than, to match, or be greater than s2. Only first n
 + * characters will be compared.
 + */
 +int os_strncasecmp(const char *s1, const char *s2, size_t n);
 +
 +/**
 + * os_strchr - Locate the first occurrence of a character in string
 + * @s: String
 + * @c: Character to search for
 + * Returns: Pointer to the matched character or %NULL if not found
 + */
 +char * os_strchr(const char *s, int c);
 +
 +/**
 + * os_strrchr - Locate the last occurrence of a character in string
 + * @s: String
 + * @c: Character to search for
 + * Returns: Pointer to the matched character or %NULL if not found
 + */
 +char * os_strrchr(const char *s, int c);
 +
 +/**
 + * os_strcmp - Compare two strings
 + * @s1: First string
 + * @s2: Second string
 + * Returns: An integer less than, equal to, or greater than zero if s1 is
 + * found to be less than, to match, or be greatred than s2
 + */
 +int os_strcmp(const char *s1, const char *s2);
 +
 +/**
 + * os_strncmp - Compare two strings
 + * @s1: First string
 + * @s2: Second string
 + * @n: Maximum numbers of characters to compare
 + * Returns: An integer less than, equal to, or greater than zero if s1 is
 + * found to be less than, to match, or be greater than s2. Only first n
 + * characters will be compared.
 + */
 +int os_strncmp(const char *s1, const char *s2, size_t n);
 +
 +/**
 + * os_strstr - Locate a substring
 + * @haystack: String (haystack) to search from
 + * @needle: Needle to search from haystack
 + * Returns: Pointer to the beginning of the substring or %NULL if not found
 + */
 +char * os_strstr(const char *haystack, const char *needle);
 +
 +/**
 + * os_snprintf - Print to a memory buffer
 + * @str: Memory buffer to print into
 + * @size: Maximum length of the str buffer
 + * @format: printf format
 + * Returns: Number of characters printed (not including trailing '\0').
 + *
 + * If the output buffer is truncated, number of characters which would have
 + * been written is returned. Since some C libraries return -1 in such a case,
 + * the caller must be prepared on that value, too, to indicate truncation.
 + *
 + * Note: Some C library implementations of snprintf() may not guarantee null
 + * termination in case the output is truncated. The OS wrapper function of
 + * os_snprintf() should provide this guarantee, i.e., to null terminate the
 + * output buffer if a C library version of the function is used and if that
 + * function does not guarantee null termination.
 + *
 + * If the target system does not include snprintf(), see, e.g.,
 + * http://www.ijs.si/software/snprintf/ for an example of a portable
 + * implementation of snprintf.
 + */
 +int os_snprintf(char *str, size_t size, const char *format, ...);
 +
 +#else /* OS_NO_C_LIB_DEFINES */
 +
 +#ifdef WPA_TRACE
 +void * os_malloc(size_t size);
 +void * os_realloc(void *ptr, size_t size);
 +void os_free(void *ptr);
 +char * os_strdup(const char *s);
 +#else /* WPA_TRACE */
 +#ifndef os_malloc
 +#define os_malloc(s) malloc((s))
 +#endif
 +#ifndef os_realloc
 +#define os_realloc(p, s) realloc((p), (s))
 +#endif
 +#ifndef os_free
 +#define os_free(p) free((p))
 +#endif
 +#ifndef os_strdup
 +#ifdef _MSC_VER
 +#define os_strdup(s) _strdup(s)
 +#else
 +#define os_strdup(s) strdup(s)
 +#endif
 +#endif
 +#endif /* WPA_TRACE */
 +
 +#ifndef os_memcpy
 +#define os_memcpy(d, s, n) memcpy((d), (s), (n))
 +#endif
 +#ifndef os_memmove
 +#define os_memmove(d, s, n) memmove((d), (s), (n))
 +#endif
 +#ifndef os_memset
 +#define os_memset(s, c, n) memset(s, c, n)
 +#endif
 +#ifndef os_memcmp
 +#define os_memcmp(s1, s2, n) memcmp((s1), (s2), (n))
 +#endif
 +
 +#ifndef os_strlen
 +#define os_strlen(s) strlen(s)
 +#endif
 +#ifndef os_strcasecmp
 +#ifdef _MSC_VER
 +#define os_strcasecmp(s1, s2) _stricmp((s1), (s2))
 +#else
 +#define os_strcasecmp(s1, s2) strcasecmp((s1), (s2))
 +#endif
 +#endif
 +#ifndef os_strncasecmp
 +#ifdef _MSC_VER
 +#define os_strncasecmp(s1, s2, n) _strnicmp((s1), (s2), (n))
 +#else
 +#define os_strncasecmp(s1, s2, n) strncasecmp((s1), (s2), (n))
 +#endif
 +#endif
 +#ifndef os_strchr
 +#define os_strchr(s, c) strchr((s), (c))
 +#endif
 +#ifndef os_strcmp
 +#define os_strcmp(s1, s2) strcmp((s1), (s2))
 +#endif
 +#ifndef os_strncmp
 +#define os_strncmp(s1, s2, n) strncmp((s1), (s2), (n))
 +#endif
 +#ifndef os_strrchr
 +#define os_strrchr(s, c) strrchr((s), (c))
 +#endif
 +#ifndef os_strstr
 +#define os_strstr(h, n) strstr((h), (n))
 +#endif
 +
 +#ifndef os_snprintf
 +#ifdef _MSC_VER
 +#define os_snprintf _snprintf
 +#else
 +#define os_snprintf snprintf
 +#endif
 +#endif
 +
 +#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)
 +              return NULL;
 +      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
 + * @dest: Destination
 + * @src: Source
 + * @siz: Size of the target buffer
 + * Returns: Total length of the target string (length of src) (not including
 + * NUL-termination)
 + *
 + * This function matches in behavior with the strlcpy(3) function in OpenBSD.
 + */
 +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
 +#define realloc OS_DO_NOT_USE_realloc
 +#define free OS_DO_NOT_USE_free
 +#define memcpy OS_DO_NOT_USE_memcpy
 +#define memmove OS_DO_NOT_USE_memmove
 +#define memset OS_DO_NOT_USE_memset
 +#define memcmp OS_DO_NOT_USE_memcmp
 +#undef strdup
 +#define strdup OS_DO_NOT_USE_strdup
 +#define strlen OS_DO_NOT_USE_strlen
 +#define strcasecmp OS_DO_NOT_USE_strcasecmp
 +#define strncasecmp OS_DO_NOT_USE_strncasecmp
 +#undef strchr
 +#define strchr OS_DO_NOT_USE_strchr
 +#undef strcmp
 +#define strcmp OS_DO_NOT_USE_strcmp
 +#undef strncmp
 +#define strncmp OS_DO_NOT_USE_strncmp
 +#undef strncpy
 +#define strncpy OS_DO_NOT_USE_strncpy
 +#define strrchr OS_DO_NOT_USE_strrchr
 +#define strstr OS_DO_NOT_USE_strstr
 +#undef snprintf
 +#define snprintf OS_DO_NOT_USE_snprintf
 +
 +#define strcpy OS_DO_NOT_USE_strcpy
 +#endif /* OS_REJECT_C_LIB_FUNCTIONS */
 +
++
++#if defined(WPA_TRACE_BFD) && defined(CONFIG_TESTING_OPTIONS)
++#define TEST_FAIL() testing_test_fail()
++int testing_test_fail(void);
++#else
++#define TEST_FAIL() 0
++#endif
++
 +#endif /* OS_H */
index 77733ad916cd0026b0d08a9040617698154e0e6c,0000000000000000000000000000000000000000..ed6eb3c6b67704679883ecb0d5d2034900b7f492
mode 100644,000000..100644
--- /dev/null
@@@ -1,564 -1,0 +1,570 @@@
 +/*
 + * wpa_supplicant/hostapd / Internal implementation of OS specific functions
 + * Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi>
 + *
 + * This software may be distributed under the terms of the BSD license.
 + * See README for more details.
 + *
 + * This file is an example of operating system specific  wrapper functions.
 + * This version implements many of the functions internally, so it can be used
 + * to fill in missing functions from the target system C libraries.
 + *
 + * Some of the functions are using standard C library calls in order to keep
 + * this file in working condition to allow the functions to be tested on a
 + * Linux target. Please note that OS_NO_C_LIB_DEFINES needs to be defined for
 + * this file to work correctly. Note that these implementations are only
 + * examples and are not optimized for speed.
 + */
 +
 +#include "includes.h"
 +#include <time.h>
 +#include <sys/wait.h>
 +
 +#undef OS_REJECT_C_LIB_FUNCTIONS
 +#include "common.h"
 +
 +void os_sleep(os_time_t sec, os_time_t usec)
 +{
 +      if (sec)
 +              sleep(sec);
 +      if (usec)
 +              usleep(usec);
 +}
 +
 +
 +int os_get_time(struct os_time *t)
 +{
 +      int res;
 +      struct timeval tv;
 +      res = gettimeofday(&tv, NULL);
 +      t->sec = tv.tv_sec;
 +      t->usec = tv.tv_usec;
 +      return res;
 +}
 +
 +
 +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)
 +{
 +      struct tm tm;
 +
 +      if (year < 1970 || month < 1 || month > 12 || day < 1 || day > 31 ||
 +          hour < 0 || hour > 23 || min < 0 || min > 59 || sec < 0 ||
 +          sec > 60)
 +              return -1;
 +
 +      os_memset(&tm, 0, sizeof(tm));
 +      tm.tm_year = year - 1900;
 +      tm.tm_mon = month - 1;
 +      tm.tm_mday = day;
 +      tm.tm_hour = hour;
 +      tm.tm_min = min;
 +      tm.tm_sec = sec;
 +
 +      *t = (os_time_t) mktime(&tm);
 +      return 0;
 +}
 +
 +
 +int os_gmtime(os_time_t t, struct os_tm *tm)
 +{
 +      struct tm *tm2;
 +      time_t t2 = t;
 +
 +      tm2 = gmtime(&t2);
 +      if (tm2 == NULL)
 +              return -1;
 +      tm->sec = tm2->tm_sec;
 +      tm->min = tm2->tm_min;
 +      tm->hour = tm2->tm_hour;
 +      tm->day = tm2->tm_mday;
 +      tm->month = tm2->tm_mon + 1;
 +      tm->year = tm2->tm_year + 1900;
 +      return 0;
 +}
 +
 +
 +int os_daemonize(const char *pid_file)
 +{
 +      if (daemon(0, 0)) {
 +              wpa_printf(MSG_ERROR, "daemon: %s", strerror(errno));
 +              return -1;
 +      }
 +
 +      if (pid_file) {
 +              FILE *f = fopen(pid_file, "w");
 +              if (f) {
 +                      fprintf(f, "%u\n", getpid());
 +                      fclose(f);
 +              }
 +      }
 +
 +      return -0;
 +}
 +
 +
 +void os_daemonize_terminate(const char *pid_file)
 +{
 +      if (pid_file)
 +              unlink(pid_file);
 +}
 +
 +
 +int os_get_random(unsigned char *buf, size_t len)
 +{
 +      FILE *f;
 +      size_t rc;
 +
 +      f = fopen("/dev/urandom", "rb");
 +      if (f == NULL) {
 +              printf("Could not open /dev/urandom.\n");
 +              return -1;
 +      }
 +
 +      rc = fread(buf, 1, len, f);
 +      fclose(f);
 +
 +      return rc != len ? -1 : 0;
 +}
 +
 +
 +unsigned long os_random(void)
 +{
 +      return random();
 +}
 +
 +
 +char * os_rel2abs_path(const char *rel_path)
 +{
 +      char *buf = NULL, *cwd, *ret;
 +      size_t len = 128, cwd_len, rel_len, ret_len;
 +
 +      if (rel_path[0] == '/')
 +              return os_strdup(rel_path);
 +
 +      for (;;) {
 +              buf = os_malloc(len);
 +              if (buf == NULL)
 +                      return NULL;
 +              cwd = getcwd(buf, len);
 +              if (cwd == NULL) {
 +                      os_free(buf);
 +                      if (errno != ERANGE) {
 +                              return NULL;
 +                      }
 +                      len *= 2;
 +              } else {
 +                      break;
 +              }
 +      }
 +
 +      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) {
 +              os_memcpy(ret, cwd, cwd_len);
 +              ret[cwd_len] = '/';
 +              os_memcpy(ret + cwd_len + 1, rel_path, rel_len);
 +              ret[ret_len - 1] = '\0';
 +      }
 +      os_free(buf);
 +      return ret;
 +}
 +
 +
 +int os_program_init(void)
 +{
 +      return 0;
 +}
 +
 +
 +void os_program_deinit(void)
 +{
 +}
 +
 +
 +int os_setenv(const char *name, const char *value, int overwrite)
 +{
 +      return setenv(name, value, overwrite);
 +}
 +
 +
 +int os_unsetenv(const char *name)
 +{
 +#if defined(__FreeBSD__) || defined(__NetBSD__)
 +      unsetenv(name);
 +      return 0;
 +#else
 +      return unsetenv(name);
 +#endif
 +}
 +
 +
 +char * os_readfile(const char *name, size_t *len)
 +{
 +      FILE *f;
 +      char *buf;
 +
 +      f = fopen(name, "rb");
 +      if (f == NULL)
 +              return NULL;
 +
 +      fseek(f, 0, SEEK_END);
 +      *len = ftell(f);
 +      fseek(f, 0, SEEK_SET);
 +
 +      buf = os_malloc(*len);
 +      if (buf == NULL) {
 +              fclose(f);
 +              return NULL;
 +      }
 +
 +      if (fread(buf, 1, *len, f) != *len) {
 +              fclose(f);
 +              os_free(buf);
 +              return NULL;
 +      }
 +
 +      fclose(f);
 +
 +      return buf;
 +}
 +
 +
++int os_fdatasync(FILE *stream)
++{
++      return 0;
++}
++
++
 +void * os_zalloc(size_t size)
 +{
 +      void *n = os_malloc(size);
 +      if (n)
 +              os_memset(n, 0, size);
 +      return n;
 +}
 +
 +
 +void * os_malloc(size_t size)
 +{
 +      return malloc(size);
 +}
 +
 +
 +void * os_realloc(void *ptr, size_t size)
 +{
 +      return realloc(ptr, size);
 +}
 +
 +
 +void os_free(void *ptr)
 +{
 +      free(ptr);
 +}
 +
 +
 +void * os_memcpy(void *dest, const void *src, size_t n)
 +{
 +      char *d = dest;
 +      const char *s = src;
 +      while (n--)
 +              *d++ = *s++;
 +      return dest;
 +}
 +
 +
 +void * os_memmove(void *dest, const void *src, size_t n)
 +{
 +      if (dest < src)
 +              os_memcpy(dest, src, n);
 +      else {
 +              /* overlapping areas */
 +              char *d = (char *) dest + n;
 +              const char *s = (const char *) src + n;
 +              while (n--)
 +                      *--d = *--s;
 +      }
 +      return dest;
 +}
 +
 +
 +void * os_memset(void *s, int c, size_t n)
 +{
 +      char *p = s;
 +      while (n--)
 +              *p++ = c;
 +      return s;
 +}
 +
 +
 +int os_memcmp(const void *s1, const void *s2, size_t n)
 +{
 +      const unsigned char *p1 = s1, *p2 = s2;
 +
 +      if (n == 0)
 +              return 0;
 +
 +      while (*p1 == *p2) {
 +              p1++;
 +              p2++;
 +              n--;
 +              if (n == 0)
 +                      return 0;
 +      }
 +
 +      return *p1 - *p2;
 +}
 +
 +
 +char * os_strdup(const char *s)
 +{
 +      char *res;
 +      size_t len;
 +      if (s == NULL)
 +              return NULL;
 +      len = os_strlen(s);
 +      res = os_malloc(len + 1);
 +      if (res)
 +              os_memcpy(res, s, len + 1);
 +      return res;
 +}
 +
 +
 +size_t os_strlen(const char *s)
 +{
 +      const char *p = s;
 +      while (*p)
 +              p++;
 +      return p - s;
 +}
 +
 +
 +int os_strcasecmp(const char *s1, const char *s2)
 +{
 +      /*
 +       * Ignoring case is not required for main functionality, so just use
 +       * the case sensitive version of the function.
 +       */
 +      return os_strcmp(s1, s2);
 +}
 +
 +
 +int os_strncasecmp(const char *s1, const char *s2, size_t n)
 +{
 +      /*
 +       * Ignoring case is not required for main functionality, so just use
 +       * the case sensitive version of the function.
 +       */
 +      return os_strncmp(s1, s2, n);
 +}
 +
 +
 +char * os_strchr(const char *s, int c)
 +{
 +      while (*s) {
 +              if (*s == c)
 +                      return (char *) s;
 +              s++;
 +      }
 +      return NULL;
 +}
 +
 +
 +char * os_strrchr(const char *s, int c)
 +{
 +      const char *p = s;
 +      while (*p)
 +              p++;
 +      p--;
 +      while (p >= s) {
 +              if (*p == c)
 +                      return (char *) p;
 +              p--;
 +      }
 +      return NULL;
 +}
 +
 +
 +int os_strcmp(const char *s1, const char *s2)
 +{
 +      while (*s1 == *s2) {
 +              if (*s1 == '\0')
 +                      break;
 +              s1++;
 +              s2++;
 +      }
 +
 +      return *s1 - *s2;
 +}
 +
 +
 +int os_strncmp(const char *s1, const char *s2, size_t n)
 +{
 +      if (n == 0)
 +              return 0;
 +
 +      while (*s1 == *s2) {
 +              if (*s1 == '\0')
 +                      break;
 +              s1++;
 +              s2++;
 +              n--;
 +              if (n == 0)
 +                      return 0;
 +      }
 +
 +      return *s1 - *s2;
 +}
 +
 +
 +char * os_strncpy(char *dest, const char *src, size_t n)
 +{
 +      char *d = dest;
 +
 +      while (n--) {
 +              *d = *src;
 +              if (*src == '\0')
 +                      break;
 +              d++;
 +              src++;
 +      }
 +
 +      return dest;
 +}
 +
 +
 +size_t os_strlcpy(char *dest, const char *src, size_t siz)
 +{
 +      const char *s = src;
 +      size_t left = siz;
 +
 +      if (left) {
 +              /* Copy string up to the maximum size of the dest buffer */
 +              while (--left != 0) {
 +                      if ((*dest++ = *s++) == '\0')
 +                              break;
 +              }
 +      }
 +
 +      if (left == 0) {
 +              /* Not enough room for the string; force NUL-termination */
 +              if (siz != 0)
 +                      *dest = '\0';
 +              while (*s++)
 +                      ; /* determine total src string length */
 +      }
 +
 +      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;
 +}
 +
 +
 +char * os_strstr(const char *haystack, const char *needle)
 +{
 +      size_t len = os_strlen(needle);
 +      while (*haystack) {
 +              if (os_strncmp(haystack, needle, len) == 0)
 +                      return (char *) haystack;
 +              haystack++;
 +      }
 +
 +      return NULL;
 +}
 +
 +
 +int os_snprintf(char *str, size_t size, const char *format, ...)
 +{
 +      va_list ap;
 +      int ret;
 +
 +      /* See http://www.ijs.si/software/snprintf/ for portable
 +       * implementation of snprintf.
 +       */
 +
 +      va_start(ap, format);
 +      ret = vsnprintf(str, size, format, ap);
 +      va_end(ap);
 +      if (size > 0)
 +              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 83fe025167b6f9cd60d1e79525bc222a63651429,0000000000000000000000000000000000000000..0c3214d32ce50f2b9d77b6d3b9fb43b0e45190cb
mode 100644,000000..100644
--- /dev/null
@@@ -1,242 -1,0 +1,248 @@@
 +/*
 + * wpa_supplicant/hostapd / Empty OS specific functions
 + * Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi>
 + *
 + * This software may be distributed under the terms of the BSD license.
 + * See README for more details.
 + *
 + * This file can be used as a starting point when adding a new OS target. The
 + * functions here do not really work as-is since they are just empty or only
 + * return an error value. os_internal.c can be used as another starting point
 + * or reference since it has example implementation of many of these functions.
 + */
 +
 +#include "includes.h"
 +
 +#include "os.h"
 +
 +void os_sleep(os_time_t sec, os_time_t usec)
 +{
 +}
 +
 +
 +int os_get_time(struct os_time *t)
 +{
 +      return -1;
 +}
 +
 +
 +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)
 +{
 +      return -1;
 +}
 +
 +int os_gmtime(os_time_t t, struct os_tm *tm)
 +{
 +      return -1;
 +}
 +
 +
 +int os_daemonize(const char *pid_file)
 +{
 +      return -1;
 +}
 +
 +
 +void os_daemonize_terminate(const char *pid_file)
 +{
 +}
 +
 +
 +int os_get_random(unsigned char *buf, size_t len)
 +{
 +      return -1;
 +}
 +
 +
 +unsigned long os_random(void)
 +{
 +      return 0;
 +}
 +
 +
 +char * os_rel2abs_path(const char *rel_path)
 +{
 +      return NULL; /* strdup(rel_path) can be used here */
 +}
 +
 +
 +int os_program_init(void)
 +{
 +      return 0;
 +}
 +
 +
 +void os_program_deinit(void)
 +{
 +}
 +
 +
 +int os_setenv(const char *name, const char *value, int overwrite)
 +{
 +      return -1;
 +}
 +
 +
 +int os_unsetenv(const char *name)
 +{
 +      return -1;
 +}
 +
 +
 +char * os_readfile(const char *name, size_t *len)
 +{
 +      return NULL;
 +}
 +
 +
++int os_fdatasync(FILE *stream)
++{
++      return 0;
++}
++
++
 +void * os_zalloc(size_t size)
 +{
 +      return NULL;
 +}
 +
 +
 +#ifdef OS_NO_C_LIB_DEFINES
 +void * os_malloc(size_t size)
 +{
 +      return NULL;
 +}
 +
 +
 +void * os_realloc(void *ptr, size_t size)
 +{
 +      return NULL;
 +}
 +
 +
 +void os_free(void *ptr)
 +{
 +}
 +
 +
 +void * os_memcpy(void *dest, const void *src, size_t n)
 +{
 +      return dest;
 +}
 +
 +
 +void * os_memmove(void *dest, const void *src, size_t n)
 +{
 +      return dest;
 +}
 +
 +
 +void * os_memset(void *s, int c, size_t n)
 +{
 +      return s;
 +}
 +
 +
 +int os_memcmp(const void *s1, const void *s2, size_t n)
 +{
 +      return 0;
 +}
 +
 +
 +char * os_strdup(const char *s)
 +{
 +      return NULL;
 +}
 +
 +
 +size_t os_strlen(const char *s)
 +{
 +      return 0;
 +}
 +
 +
 +int os_strcasecmp(const char *s1, const char *s2)
 +{
 +      /*
 +       * Ignoring case is not required for main functionality, so just use
 +       * the case sensitive version of the function.
 +       */
 +      return os_strcmp(s1, s2);
 +}
 +
 +
 +int os_strncasecmp(const char *s1, const char *s2, size_t n)
 +{
 +      /*
 +       * Ignoring case is not required for main functionality, so just use
 +       * the case sensitive version of the function.
 +       */
 +      return os_strncmp(s1, s2, n);
 +}
 +
 +
 +char * os_strchr(const char *s, int c)
 +{
 +      return NULL;
 +}
 +
 +
 +char * os_strrchr(const char *s, int c)
 +{
 +      return NULL;
 +}
 +
 +
 +int os_strcmp(const char *s1, const char *s2)
 +{
 +      return 0;
 +}
 +
 +
 +int os_strncmp(const char *s1, const char *s2, size_t n)
 +{
 +      return 0;
 +}
 +
 +
 +char * os_strncpy(char *dest, const char *src, size_t n)
 +{
 +      return dest;
 +}
 +
 +
 +size_t os_strlcpy(char *dest, const char *src, size_t size)
 +{
 +      return 0;
 +}
 +
 +
 +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;
 +}
 +
 +
 +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 34cb87af6ad781a8d9c8cb9eaa03cf12629f90d9,0000000000000000000000000000000000000000..488995c5fa46fe1b177e5bb2d6764c7cc0bb23bd
mode 100644,000000..100644
--- /dev/null
@@@ -1,731 -1,0 +1,849 @@@
- };
 +/*
 + * OS specific functions for UNIX/POSIX systems
 + * Copyright (c) 2005-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 <time.h>
 +#include <sys/wait.h>
 +
 +#ifdef ANDROID
 +#include <sys/capability.h>
 +#include <sys/prctl.h>
 +#include <private/android_filesystem_config.h>
 +#endif /* ANDROID */
 +
++#ifdef __MACH__
++#include <CoreServices/CoreServices.h>
++#include <mach/mach.h>
++#include <mach/mach_time.h>
++#endif /* __MACH__ */
++
 +#include "os.h"
 +#include "common.h"
 +
 +#ifdef WPA_TRACE
 +
 +#include "wpa_debug.h"
 +#include "trace.h"
 +#include "list.h"
 +
 +static struct dl_list alloc_list = DL_LIST_HEAD_INIT(alloc_list);
 +
 +#define ALLOC_MAGIC 0xa84ef1b2
 +#define FREED_MAGIC 0x67fd487a
 +
 +struct os_alloc_trace {
 +      unsigned int magic;
 +      struct dl_list list;
 +      size_t len;
 +      WPA_TRACE_INFO
++} __attribute__((aligned(16)));
 +
 +#endif /* WPA_TRACE */
 +
 +
 +void os_sleep(os_time_t sec, os_time_t usec)
 +{
 +      if (sec)
 +              sleep(sec);
 +      if (usec)
 +              usleep(usec);
 +}
 +
 +
 +int os_get_time(struct os_time *t)
 +{
 +      int res;
 +      struct timeval tv;
 +      res = gettimeofday(&tv, NULL);
 +      t->sec = tv.tv_sec;
 +      t->usec = tv.tv_usec;
 +      return res;
 +}
 +
 +
 +int os_get_reltime(struct os_reltime *t)
 +{
++#ifndef __MACH__
 +#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;
 +              }
 +      }
++#else /* __MACH__ */
++      uint64_t abstime, nano;
++      static mach_timebase_info_data_t info = { 0, 0 };
++
++      if (!info.denom) {
++              if (mach_timebase_info(&info) != KERN_SUCCESS)
++                      return -1;
++      }
++
++      abstime = mach_absolute_time();
++      nano = (abstime * info.numer) / info.denom;
++
++      t->sec = nano / NSEC_PER_SEC;
++      t->usec = (nano - (((uint64_t) t->sec) * NSEC_PER_SEC)) / NSEC_PER_USEC;
++
++      return 0;
++#endif /* __MACH__ */
 +}
 +
 +
 +int os_mktime(int year, int month, int day, int hour, int min, int sec,
 +            os_time_t *t)
 +{
 +      struct tm tm, *tm1;
 +      time_t t_local, t1, t2;
 +      os_time_t tz_offset;
 +
 +      if (year < 1970 || month < 1 || month > 12 || day < 1 || day > 31 ||
 +          hour < 0 || hour > 23 || min < 0 || min > 59 || sec < 0 ||
 +          sec > 60)
 +              return -1;
 +
 +      memset(&tm, 0, sizeof(tm));
 +      tm.tm_year = year - 1900;
 +      tm.tm_mon = month - 1;
 +      tm.tm_mday = day;
 +      tm.tm_hour = hour;
 +      tm.tm_min = min;
 +      tm.tm_sec = sec;
 +
 +      t_local = mktime(&tm);
 +
 +      /* figure out offset to UTC */
 +      tm1 = localtime(&t_local);
 +      if (tm1) {
 +              t1 = mktime(tm1);
 +              tm1 = gmtime(&t_local);
 +              if (tm1) {
 +                      t2 = mktime(tm1);
 +                      tz_offset = t2 - t1;
 +              } else
 +                      tz_offset = 0;
 +      } else
 +              tz_offset = 0;
 +
 +      *t = (os_time_t) t_local - tz_offset;
 +      return 0;
 +}
 +
 +
 +int os_gmtime(os_time_t t, struct os_tm *tm)
 +{
 +      struct tm *tm2;
 +      time_t t2 = t;
 +
 +      tm2 = gmtime(&t2);
 +      if (tm2 == NULL)
 +              return -1;
 +      tm->sec = tm2->tm_sec;
 +      tm->min = tm2->tm_min;
 +      tm->hour = tm2->tm_hour;
 +      tm->day = tm2->tm_mday;
 +      tm->month = tm2->tm_mon + 1;
 +      tm->year = tm2->tm_year + 1900;
 +      return 0;
 +}
 +
 +
 +#ifdef __APPLE__
 +#include <fcntl.h>
 +static int os_daemon(int nochdir, int noclose)
 +{
 +      int devnull;
 +
 +      if (chdir("/") < 0)
 +              return -1;
 +
 +      devnull = open("/dev/null", O_RDWR);
 +      if (devnull < 0)
 +              return -1;
 +
 +      if (dup2(devnull, STDIN_FILENO) < 0) {
 +              close(devnull);
 +              return -1;
 +      }
 +
 +      if (dup2(devnull, STDOUT_FILENO) < 0) {
 +              close(devnull);
 +              return -1;
 +      }
 +
 +      if (dup2(devnull, STDERR_FILENO) < 0) {
 +              close(devnull);
 +              return -1;
 +      }
 +
 +      return 0;
 +}
 +#else /* __APPLE__ */
 +#define os_daemon daemon
 +#endif /* __APPLE__ */
 +
 +
 +#ifdef __FreeBSD__
 +#include <err.h>
 +#include <libutil.h>
 +#include <stdint.h>
 +#endif /* __FreeBSD__ */
 +
 +int os_daemonize(const char *pid_file)
 +{
 +#if defined(__uClinux__) || defined(__sun__)
 +      return -1;
 +#else /* defined(__uClinux__) || defined(__sun__) */
 +#ifdef __FreeBSD__
 +      pid_t otherpid;
 +      struct pidfh *pfh;
 +
 +      pfh = pidfile_open(pid_file, 0600, &otherpid);
 +      if (pfh == NULL) {
 +              if (errno == EEXIST) {
 +                      errx(1, "Daemon already running, pid: %jd.",
 +                          (intmax_t)otherpid);
 +              }
 +              warn("Cannot open or create pidfile.");
 +      }
 +#endif /* __FreeBSD__ */
 +
 +      if (os_daemon(0, 0)) {
 +              perror("daemon");
 +#ifdef __FreeBSD__
 +              pidfile_remove(pfh);
 +#endif /* __FreeBSD__ */
 +              return -1;
 +      }
 +
 +#ifndef __FreeBSD__
 +      if (pid_file) {
 +              FILE *f = fopen(pid_file, "w");
 +              if (f) {
 +                      fprintf(f, "%u\n", getpid());
 +                      fclose(f);
 +              }
 +      }
 +#else /* __FreeBSD__ */
 +      pidfile_write(pfh);
 +#endif /* __FreeBSD__ */
 +
 +      return -0;
 +#endif /* defined(__uClinux__) || defined(__sun__) */
 +}
 +
 +
 +void os_daemonize_terminate(const char *pid_file)
 +{
 +      if (pid_file)
 +              unlink(pid_file);
 +}
 +
 +
 +int os_get_random(unsigned char *buf, size_t len)
 +{
 +      FILE *f;
 +      size_t rc;
 +
++      if (TEST_FAIL())
++              return -1;
++
 +      f = fopen("/dev/urandom", "rb");
 +      if (f == NULL) {
 +              printf("Could not open /dev/urandom.\n");
 +              return -1;
 +      }
 +
 +      rc = fread(buf, 1, len, f);
 +      fclose(f);
 +
 +      return rc != len ? -1 : 0;
 +}
 +
 +
 +unsigned long os_random(void)
 +{
 +      return random();
 +}
 +
 +
 +char * os_rel2abs_path(const char *rel_path)
 +{
 +      char *buf = NULL, *cwd, *ret;
 +      size_t len = 128, cwd_len, rel_len, ret_len;
 +      int last_errno;
 +
 +      if (!rel_path)
 +              return NULL;
 +
 +      if (rel_path[0] == '/')
 +              return os_strdup(rel_path);
 +
 +      for (;;) {
 +              buf = os_malloc(len);
 +              if (buf == NULL)
 +                      return NULL;
 +              cwd = getcwd(buf, len);
 +              if (cwd == NULL) {
 +                      last_errno = errno;
 +                      os_free(buf);
 +                      if (last_errno != ERANGE)
 +                              return NULL;
 +                      len *= 2;
 +                      if (len > 2000)
 +                              return NULL;
 +              } else {
 +                      buf[len - 1] = '\0';
 +                      break;
 +              }
 +      }
 +
 +      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) {
 +              os_memcpy(ret, cwd, cwd_len);
 +              ret[cwd_len] = '/';
 +              os_memcpy(ret + cwd_len + 1, rel_path, rel_len);
 +              ret[ret_len - 1] = '\0';
 +      }
 +      os_free(buf);
 +      return ret;
 +}
 +
 +
 +int os_program_init(void)
 +{
 +#ifdef ANDROID
 +      /*
 +       * We ignore errors here since errors are normal if we
 +       * are already running as non-root.
 +       */
 +#ifdef ANDROID_SETGROUPS_OVERRIDE
 +      gid_t groups[] = { ANDROID_SETGROUPS_OVERRIDE };
 +#else /* ANDROID_SETGROUPS_OVERRIDE */
 +      gid_t groups[] = { AID_INET, AID_WIFI, AID_KEYSTORE };
 +#endif /* ANDROID_SETGROUPS_OVERRIDE */
 +      struct __user_cap_header_struct header;
 +      struct __user_cap_data_struct cap;
 +
 +      setgroups(ARRAY_SIZE(groups), groups);
 +
 +      prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0);
 +
 +      setgid(AID_WIFI);
 +      setuid(AID_WIFI);
 +
 +      header.version = _LINUX_CAPABILITY_VERSION;
 +      header.pid = 0;
 +      cap.effective = cap.permitted =
 +              (1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW);
 +      cap.inheritable = 0;
 +      capset(&header, &cap);
 +#endif /* ANDROID */
 +
 +      return 0;
 +}
 +
 +
 +void os_program_deinit(void)
 +{
 +#ifdef WPA_TRACE
 +      struct os_alloc_trace *a;
 +      unsigned long total = 0;
 +      dl_list_for_each(a, &alloc_list, struct os_alloc_trace, list) {
 +              total += a->len;
 +              if (a->magic != ALLOC_MAGIC) {
 +                      wpa_printf(MSG_INFO, "MEMLEAK[%p]: invalid magic 0x%x "
 +                                 "len %lu",
 +                                 a, a->magic, (unsigned long) a->len);
 +                      continue;
 +              }
 +              wpa_printf(MSG_INFO, "MEMLEAK[%p]: len %lu",
 +                         a, (unsigned long) a->len);
 +              wpa_trace_dump("memleak", a);
 +      }
 +      if (total)
 +              wpa_printf(MSG_INFO, "MEMLEAK: total %lu bytes",
 +                         (unsigned long) total);
 +#endif /* WPA_TRACE */
 +}
 +
 +
 +int os_setenv(const char *name, const char *value, int overwrite)
 +{
 +      return setenv(name, value, overwrite);
 +}
 +
 +
 +int os_unsetenv(const char *name)
 +{
 +#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__APPLE__) || \
 +    defined(__OpenBSD__)
 +      unsetenv(name);
 +      return 0;
 +#else
 +      return unsetenv(name);
 +#endif
 +}
 +
 +
 +char * os_readfile(const char *name, size_t *len)
 +{
 +      FILE *f;
 +      char *buf;
 +      long pos;
 +
 +      f = fopen(name, "rb");
 +      if (f == NULL)
 +              return NULL;
 +
 +      if (fseek(f, 0, SEEK_END) < 0 || (pos = ftell(f)) < 0) {
 +              fclose(f);
 +              return NULL;
 +      }
 +      *len = pos;
 +      if (fseek(f, 0, SEEK_SET) < 0) {
 +              fclose(f);
 +              return NULL;
 +      }
 +
 +      buf = os_malloc(*len);
 +      if (buf == NULL) {
 +              fclose(f);
 +              return NULL;
 +      }
 +
 +      if (fread(buf, 1, *len, f) != *len) {
 +              fclose(f);
 +              os_free(buf);
 +              return NULL;
 +      }
 +
 +      fclose(f);
 +
 +      return buf;
 +}
 +
 +
 +int os_file_exists(const char *fname)
 +{
 +      FILE *f = fopen(fname, "rb");
 +      if (f == NULL)
 +              return 0;
 +      fclose(f);
 +      return 1;
 +}
 +
 +
++int os_fdatasync(FILE *stream)
++{
++      if (!fflush(stream)) {
++#ifdef __linux__
++              return fdatasync(fileno(stream));
++#else /* !__linux__ */
++#ifdef F_FULLFSYNC
++              /* OS X does not implement fdatasync(). */
++              return fcntl(fileno(stream), F_FULLFSYNC);
++#else /* F_FULLFSYNC */
++              return fsync(fileno(stream));
++#endif /* F_FULLFSYNC */
++#endif /* __linux__ */
++      }
++
++      return -1;
++}
++
++
 +#ifndef WPA_TRACE
 +void * os_zalloc(size_t size)
 +{
 +      return calloc(1, size);
 +}
 +#endif /* WPA_TRACE */
 +
 +
 +size_t os_strlcpy(char *dest, const char *src, size_t siz)
 +{
 +      const char *s = src;
 +      size_t left = siz;
 +
 +      if (left) {
 +              /* Copy string up to the maximum size of the dest buffer */
 +              while (--left != 0) {
 +                      if ((*dest++ = *s++) == '\0')
 +                              break;
 +              }
 +      }
 +
 +      if (left == 0) {
 +              /* Not enough room for the string; force NUL-termination */
 +              if (siz != 0)
 +                      *dest = '\0';
 +              while (*s++)
 +                      ; /* determine total src string length */
 +      }
 +
 +      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;
 +}
 +
 +
 +#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;
 +}
 +
++
++char wpa_trace_test_fail_func[256] = { 0 };
++unsigned int wpa_trace_test_fail_after;
++
++int testing_test_fail(void)
++{
++      const char *func[WPA_TRACE_LEN];
++      size_t i, res, len;
++      char *pos, *next;
++      int match;
++
++      if (!wpa_trace_test_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++;
++
++      pos = wpa_trace_test_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_test_fail_after--;
++      if (wpa_trace_test_fail_after == 0) {
++              wpa_printf(MSG_INFO, "TESTING: fail at %s",
++                         wpa_trace_test_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;
 +      a->magic = ALLOC_MAGIC;
 +      dl_list_add(&alloc_list, &a->list);
 +      a->len = size;
 +      wpa_trace_record(a);
 +      return a + 1;
 +}
 +
 +
 +void * os_realloc(void *ptr, size_t size)
 +{
 +      struct os_alloc_trace *a;
 +      size_t copy_len;
 +      void *n;
 +
 +      if (ptr == NULL)
 +              return os_malloc(size);
 +
 +      a = (struct os_alloc_trace *) ptr - 1;
 +      if (a->magic != ALLOC_MAGIC) {
 +              wpa_printf(MSG_INFO, "REALLOC[%p]: invalid magic 0x%x%s",
 +                         a, a->magic,
 +                         a->magic == FREED_MAGIC ? " (already freed)" : "");
 +              wpa_trace_show("Invalid os_realloc() call");
 +              abort();
 +      }
 +      n = os_malloc(size);
 +      if (n == NULL)
 +              return NULL;
 +      copy_len = a->len;
 +      if (copy_len > size)
 +              copy_len = size;
 +      os_memcpy(n, a + 1, copy_len);
 +      os_free(ptr);
 +      return n;
 +}
 +
 +
 +void os_free(void *ptr)
 +{
 +      struct os_alloc_trace *a;
 +
 +      if (ptr == NULL)
 +              return;
 +      a = (struct os_alloc_trace *) ptr - 1;
 +      if (a->magic != ALLOC_MAGIC) {
 +              wpa_printf(MSG_INFO, "FREE[%p]: invalid magic 0x%x%s",
 +                         a, a->magic,
 +                         a->magic == FREED_MAGIC ? " (already freed)" : "");
 +              wpa_trace_show("Invalid os_free() call");
 +              abort();
 +      }
 +      dl_list_del(&a->list);
 +      a->magic = FREED_MAGIC;
 +
 +      wpa_trace_check_ref(ptr);
 +      free(a);
 +}
 +
 +
 +void * os_zalloc(size_t size)
 +{
 +      void *ptr = os_malloc(size);
 +      if (ptr)
 +              os_memset(ptr, 0, size);
 +      return ptr;
 +}
 +
 +
 +char * os_strdup(const char *s)
 +{
 +      size_t len;
 +      char *d;
 +      len = os_strlen(s);
 +      d = os_malloc(len + 1);
 +      if (d == NULL)
 +              return NULL;
 +      os_memcpy(d, s, len);
 +      d[len] = '\0';
 +      return d;
 +}
 +
 +#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 296ea13f153b4e5df54b7263d781f4f6648b21a8,0000000000000000000000000000000000000000..dea27b9f2ad846c709f6074b4ff0985b0970aade
mode 100644,000000..100644
--- /dev/null
@@@ -1,267 -1,0 +1,285 @@@
 +/*
 + * wpa_supplicant/hostapd / OS specific functions for Win32 systems
 + * Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi>
 + *
 + * This software may be distributed under the terms of the BSD license.
 + * See README for more details.
 + */
 +
 +#include "includes.h"
 +#include <time.h>
 +#include <winsock2.h>
 +#include <wincrypt.h>
 +
 +#include "os.h"
 +#include "common.h"
 +
 +void os_sleep(os_time_t sec, os_time_t usec)
 +{
 +      if (sec)
 +              Sleep(sec * 1000);
 +      if (usec)
 +              Sleep(usec / 1000);
 +}
 +
 +
 +int os_get_time(struct os_time *t)
 +{
 +#define EPOCHFILETIME (116444736000000000ULL)
 +      FILETIME ft;
 +      LARGE_INTEGER li;
 +      ULONGLONG tt;
 +
 +#ifdef _WIN32_WCE
 +      SYSTEMTIME st;
 +
 +      GetSystemTime(&st);
 +      SystemTimeToFileTime(&st, &ft);
 +#else /* _WIN32_WCE */
 +      GetSystemTimeAsFileTime(&ft);
 +#endif /* _WIN32_WCE */
 +      li.LowPart = ft.dwLowDateTime;
 +      li.HighPart = ft.dwHighDateTime;
 +      tt = (li.QuadPart - EPOCHFILETIME) / 10;
 +      t->sec = (os_time_t) (tt / 1000000);
 +      t->usec = (os_time_t) (tt % 1000000);
 +
 +      return 0;
 +}
 +
 +
 +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)
 +{
 +      struct tm tm, *tm1;
 +      time_t t_local, t1, t2;
 +      os_time_t tz_offset;
 +
 +      if (year < 1970 || month < 1 || month > 12 || day < 1 || day > 31 ||
 +          hour < 0 || hour > 23 || min < 0 || min > 59 || sec < 0 ||
 +          sec > 60)
 +              return -1;
 +
 +      memset(&tm, 0, sizeof(tm));
 +      tm.tm_year = year - 1900;
 +      tm.tm_mon = month - 1;
 +      tm.tm_mday = day;
 +      tm.tm_hour = hour;
 +      tm.tm_min = min;
 +      tm.tm_sec = sec;
 +
 +      t_local = mktime(&tm);
 +
 +      /* figure out offset to UTC */
 +      tm1 = localtime(&t_local);
 +      if (tm1) {
 +              t1 = mktime(tm1);
 +              tm1 = gmtime(&t_local);
 +              if (tm1) {
 +                      t2 = mktime(tm1);
 +                      tz_offset = t2 - t1;
 +              } else
 +                      tz_offset = 0;
 +      } else
 +              tz_offset = 0;
 +
 +      *t = (os_time_t) t_local - tz_offset;
 +      return 0;
 +}
 +
 +
 +int os_gmtime(os_time_t t, struct os_tm *tm)
 +{
 +      struct tm *tm2;
 +      time_t t2 = t;
 +
 +      tm2 = gmtime(&t2);
 +      if (tm2 == NULL)
 +              return -1;
 +      tm->sec = tm2->tm_sec;
 +      tm->min = tm2->tm_min;
 +      tm->hour = tm2->tm_hour;
 +      tm->day = tm2->tm_mday;
 +      tm->month = tm2->tm_mon + 1;
 +      tm->year = tm2->tm_year + 1900;
 +      return 0;
 +}
 +
 +
 +int os_daemonize(const char *pid_file)
 +{
 +      /* TODO */
 +      return -1;
 +}
 +
 +
 +void os_daemonize_terminate(const char *pid_file)
 +{
 +}
 +
 +
 +int os_get_random(unsigned char *buf, size_t len)
 +{
 +      HCRYPTPROV prov;
 +      BOOL ret;
 +
 +      if (!CryptAcquireContext(&prov, NULL, NULL, PROV_RSA_FULL,
 +                               CRYPT_VERIFYCONTEXT))
 +              return -1;
 +
 +      ret = CryptGenRandom(prov, len, buf);
 +      CryptReleaseContext(prov, 0);
 +
 +      return ret ? 0 : -1;
 +}
 +
 +
 +unsigned long os_random(void)
 +{
 +      return rand();
 +}
 +
 +
 +char * os_rel2abs_path(const char *rel_path)
 +{
 +      return _strdup(rel_path);
 +}
 +
 +
 +int os_program_init(void)
 +{
 +#ifdef CONFIG_NATIVE_WINDOWS
 +      WSADATA wsaData;
 +      if (WSAStartup(MAKEWORD(2, 0), &wsaData)) {
 +              printf("Could not find a usable WinSock.dll\n");
 +              return -1;
 +      }
 +#endif /* CONFIG_NATIVE_WINDOWS */
 +      return 0;
 +}
 +
 +
 +void os_program_deinit(void)
 +{
 +#ifdef CONFIG_NATIVE_WINDOWS
 +      WSACleanup();
 +#endif /* CONFIG_NATIVE_WINDOWS */
 +}
 +
 +
 +int os_setenv(const char *name, const char *value, int overwrite)
 +{
 +      return -1;
 +}
 +
 +
 +int os_unsetenv(const char *name)
 +{
 +      return -1;
 +}
 +
 +
 +char * os_readfile(const char *name, size_t *len)
 +{
 +      FILE *f;
 +      char *buf;
 +
 +      f = fopen(name, "rb");
 +      if (f == NULL)
 +              return NULL;
 +
 +      fseek(f, 0, SEEK_END);
 +      *len = ftell(f);
 +      fseek(f, 0, SEEK_SET);
 +
 +      buf = malloc(*len);
 +      if (buf == NULL) {
 +              fclose(f);
 +              return NULL;
 +      }
 +
 +      fread(buf, 1, *len, f);
 +      fclose(f);
 +
 +      return buf;
 +}
 +
 +
++int os_fdatasync(FILE *stream)
++{
++      HANDLE h;
++
++      if (stream == NULL)
++              return -1;
++
++      h = (HANDLE) _get_osfhandle(_fileno(stream));
++      if (h == INVALID_HANDLE_VALUE)
++              return -1;
++
++      if (!FlushFileBuffers(h))
++              return -1;
++
++      return 0;
++}
++
++
 +void * os_zalloc(size_t size)
 +{
 +      return calloc(1, size);
 +}
 +
 +
 +size_t os_strlcpy(char *dest, const char *src, size_t siz)
 +{
 +      const char *s = src;
 +      size_t left = siz;
 +
 +      if (left) {
 +              /* Copy string up to the maximum size of the dest buffer */
 +              while (--left != 0) {
 +                      if ((*dest++ = *s++) == '\0')
 +                              break;
 +              }
 +      }
 +
 +      if (left == 0) {
 +              /* Not enough room for the string; force NUL-termination */
 +              if (siz != 0)
 +                      *dest = '\0';
 +              while (*s++)
 +                      ; /* determine total src string length */
 +      }
 +
 +      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 f8f815a86be923531244232c85d025a08b2c2ae4,0000000000000000000000000000000000000000..c9a5023355922939ccf5c8d0723dbbcf06f9d402
mode 100644,000000..100644
--- /dev/null
@@@ -1,396 -1,0 +1,396 @@@
-       if (iterator->_bitmap_shifter & (1<<IEEE80211_RADIOTAP_EXT)) {
 +/*
 + * 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
 + * published by the Free Software Foundation.
 + *
 + * Alternatively, this software may be distributed under the terms of BSD
 + * license.
 + *
 + * See COPYING for more details.
 + */
 +#include "radiotap_iter.h"
 +#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
 + * @radiotap_header: radiotap header to parse
 + * @max_length: total length we can parse into (eg, whole packet length)
 + *
 + * Returns: 0 or a negative error code if there is a problem.
 + *
 + * This function initializes an opaque iterator struct which can then
 + * be passed to ieee80211_radiotap_iterator_next() to visit every radiotap
 + * argument which is present in the header.  It knows about extended
 + * present headers and handles them.
 + *
 + * How to use:
 + * call __ieee80211_radiotap_iterator_init() to init a semi-opaque iterator
 + * struct ieee80211_radiotap_iterator (no need to init the struct beforehand)
 + * checking for a good 0 return code.  Then loop calling
 + * __ieee80211_radiotap_iterator_next()... it returns either 0,
 + * -ENOENT if there are no more args to parse, or -EINVAL if there is a problem.
 + * The iterator's @this_arg member points to the start of the argument
 + * associated with the current argument index that is present, which can be
 + * found in the iterator's @this_arg_index member.  This arg index corresponds
 + * to the IEEE80211_RADIOTAP_... defines.
 + *
 + * Radiotap header length:
 + * You can find the CPU-endian total radiotap header length in
 + * iterator->max_length after executing ieee80211_radiotap_iterator_init()
 + * successfully.
 + *
 + * Alignment Gotcha:
 + * You must take care when dereferencing iterator.this_arg
 + * for multibyte types... the pointer is not aligned.  Use
 + * get_unaligned((type *)iterator.this_arg) to dereference
 + * iterator.this_arg for type "type" safely on all arches.
 + *
 + * Example code: parse.c
 + */
 +
 +int ieee80211_radiotap_iterator_init(
 +      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 < get_unaligned_le16(&radiotap_header->it_len))
 +              return -EINVAL;
 +
 +      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) */
 +
-                                       (1 << IEEE80211_RADIOTAP_EXT)) {
++      if (iterator->_bitmap_shifter & BIT(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) &
++                     BIT(IEEE80211_RADIOTAP_EXT)) {
 +                      iterator->_arg += sizeof(uint32_t);
 +
 +                      /*
 +                       * check for insanity where the present bitmaps
 +                       * keep claiming to extend up to or even beyond the
 +                       * stated radiotap header length
 +                       */
 +
 +                      if ((unsigned long)iterator->_arg -
 +                          (unsigned long)iterator->_rtheader +
 +                          sizeof(uint32_t) >
 +                          (unsigned long)iterator->_max_length)
 +                              return -EINVAL;
 +              }
 +
 +              iterator->_arg += sizeof(uint32_t);
 +
 +              /*
 +               * no need to check again for blowing past stated radiotap
 +               * header length, because ieee80211_radiotap_iterator_next
 +               * checks it before it is dereferenced
 +               */
 +      }
 +
 +      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
 + * @iterator: radiotap_iterator to move to next arg (if any)
 + *
 + * Returns: 0 if there is an argument to handle,
 + * -ENOENT if there are no more args or -EINVAL
 + * if there is something else wrong.
 + *
 + * This function provides the next radiotap arg index (IEEE80211_RADIOTAP_*)
 + * in @this_arg_index and sets @this_arg to point to the
 + * payload for the field.  It takes care of alignment handling and extended
 + * present fields.  @this_arg can be changed by the caller (eg,
 + * incremented to move inside a compound argument like
 + * IEEE80211_RADIOTAP_CHANNEL).  The args pointed to are in
 + * little-endian format whatever the endianess of your CPU.
 + *
 + * Alignment Gotcha:
 + * You must take care when dereferencing iterator.this_arg
 + * for multibyte types... the pointer is not aligned.  Use
 + * get_unaligned((type *)iterator.this_arg) to dereference
 + * iterator.this_arg for type "type" safely on all arches.
 + */
 +
 +int ieee80211_radiotap_iterator_next(
 +      struct ieee80211_radiotap_iterator *iterator)
 +{
 +      while (1) {
 +              int hit = 0;
 +              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))
 +                      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
 +               *
 +               * 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.
 +               */
 +
 +              pad = ((unsigned long)iterator->_arg -
 +                     (unsigned long)iterator->_rtheader) & (align - 1);
 +
 +              if (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;
 +              iterator->this_arg_size = size;
 +
 +              /* internally move on the size of this arg */
 +              iterator->_arg += size;
 +
 +              /*
 +               * check for insanity where we are given a bitmap that
 +               * claims to have more arg content than the length of the
 +               * radiotap section.  We will normally end up equalling this
 +               * max_length on the last arg, never exceeding it.
 +               */
 +
 +              if ((unsigned long)iterator->_arg -
 +                  (unsigned long)iterator->_rtheader >
 +                  (unsigned long)iterator->_max_length)
 +                      return -EINVAL;
 +
 +              /* 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;
 +      }
 +}
index 4b97dadd786c6c4f7a22ee236355fce0ee58571e,0000000000000000000000000000000000000000..41511b9999a62ca23e6dcb28f25e317a43fbaf81
mode 100644,000000..100644
--- /dev/null
@@@ -1,423 -1,0 +1,861 @@@
-       char buf[3];
 +/*
 + * 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 "common/ieee802_11_defs.h"
 +#include "utils/bitfield.h"
 +#include "utils/ext_password.h"
 +#include "utils/trace.h"
 +#include "utils/base64.h"
++#include "utils/ip_addr.h"
++#include "utils/eloop.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;
++      int array[10];
 +
 +      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, "\\xq") != 1 || bin[0] != 'q')
++              errors++;
++
 +      if (printf_decode(bin, 3, "\\a") != 1 || bin[0] != 'a')
 +              errors++;
 +
++      array[0] = 10;
++      array[1] = 10;
++      array[2] = 5;
++      array[3] = 10;
++      array[4] = 5;
++      array[5] = 0;
++      if (int_array_len(array) != 5)
++              errors++;
++      int_array_sort_unique(array);
++      if (int_array_len(array) != 2)
++              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], longbuf[100];
 +      u8 addr[ETH_ALEN] = { 1, 2, 3, 4, 5, 6 };
 +      u8 bin[3];
 +      int errors = 0;
 +      struct wpa_freq_range_list ranges;
++      size_t len;
++      const char *txt;
++      u8 ssid[255];
 +
 +      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++;
 +
++      os_memset(ssid, 0, sizeof(ssid));
++      txt = wpa_ssid_txt(ssid, sizeof(ssid));
++      len = os_strlen(txt);
++      /* Verify that SSID_MAX_LEN * 4 buffer limit is enforced. */
++      if (len != SSID_MAX_LEN * 4) {
++              wpa_printf(MSG_ERROR,
++                         "Unexpected wpa_ssid_txt() result with too long SSID");
++              errors++;
++      }
++
++      if (wpa_snprintf_hex_sep(longbuf, 0, addr, ETH_ALEN, '-') != 0 ||
++          wpa_snprintf_hex_sep(longbuf, 5, addr, ETH_ALEN, '-') != 3 ||
++          os_strcmp(longbuf, "01-0") != 0)
++              errors++;
++
 +      if (errors) {
 +              wpa_printf(MSG_ERROR, "%d common test(s) failed", errors);
 +              return -1;
 +      }
 +
 +      return 0;
 +}
 +
 +
++static int os_tests(void)
++{
++      int errors = 0;
++      void *ptr;
++      os_time_t t;
++
++      wpa_printf(MSG_INFO, "os tests");
++
++      ptr = os_calloc((size_t) -1, (size_t) -1);
++      if (ptr) {
++              errors++;
++              os_free(ptr);
++      }
++      ptr = os_calloc((size_t) 2, (size_t) -1);
++      if (ptr) {
++              errors++;
++              os_free(ptr);
++      }
++      ptr = os_calloc((size_t) -1, (size_t) 2);
++      if (ptr) {
++              errors++;
++              os_free(ptr);
++      }
++
++      ptr = os_realloc_array(NULL, (size_t) -1, (size_t) -1);
++      if (ptr) {
++              errors++;
++              os_free(ptr);
++      }
++
++      os_sleep(1, 1);
++
++      if (os_mktime(1969, 1, 1, 1, 1, 1, &t) == 0 ||
++          os_mktime(1971, 0, 1, 1, 1, 1, &t) == 0 ||
++          os_mktime(1971, 13, 1, 1, 1, 1, &t) == 0 ||
++          os_mktime(1971, 1, 0, 1, 1, 1, &t) == 0 ||
++          os_mktime(1971, 1, 32, 1, 1, 1, &t) == 0 ||
++          os_mktime(1971, 1, 1, -1, 1, 1, &t) == 0 ||
++          os_mktime(1971, 1, 1, 24, 1, 1, &t) == 0 ||
++          os_mktime(1971, 1, 1, 1, -1, 1, &t) == 0 ||
++          os_mktime(1971, 1, 1, 1, 60, 1, &t) == 0 ||
++          os_mktime(1971, 1, 1, 1, 1, -1, &t) == 0 ||
++          os_mktime(1971, 1, 1, 1, 1, 61, &t) == 0 ||
++          os_mktime(1971, 1, 1, 1, 1, 1, &t) != 0 ||
++          os_mktime(2020, 1, 2, 3, 4, 5, &t) != 0 ||
++          os_mktime(2015, 12, 31, 23, 59, 59, &t) != 0)
++              errors++;
++
++      if (os_setenv("hwsim_test_env", "test value", 0) != 0 ||
++          os_setenv("hwsim_test_env", "test value 2", 1) != 0 ||
++          os_unsetenv("hwsim_test_env") != 0)
++              errors++;
++
++      if (os_file_exists("/this-file-does-not-exists-hwsim") != 0)
++              errors++;
++
++      if (errors) {
++              wpa_printf(MSG_ERROR, "%d os test(s) failed", errors);
++              return -1;
++      }
++
++      return 0;
++}
++
++
++static int wpabuf_tests(void)
++{
++      int errors = 0;
++      void *ptr;
++      struct wpabuf *buf;
++
++      wpa_printf(MSG_INFO, "wpabuf tests");
++
++      ptr = os_malloc(100);
++      if (ptr) {
++              buf = wpabuf_alloc_ext_data(ptr, 100);
++              if (buf) {
++                      if (wpabuf_resize(&buf, 100) < 0)
++                              errors++;
++                      else
++                              wpabuf_put(buf, 100);
++                      wpabuf_free(buf);
++              } else {
++                      errors++;
++                      os_free(ptr);
++              }
++      } else {
++              errors++;
++      }
++
++      buf = wpabuf_alloc(100);
++      if (buf) {
++              struct wpabuf *buf2;
++
++              wpabuf_put(buf, 100);
++              if (wpabuf_resize(&buf, 100) < 0)
++                      errors++;
++              else
++                      wpabuf_put(buf, 100);
++              buf2 = wpabuf_concat(buf, NULL);
++              if (buf2 != buf)
++                      errors++;
++              wpabuf_free(buf2);
++      } else {
++              errors++;
++      }
++
++      buf = NULL;
++      buf = wpabuf_zeropad(buf, 10);
++      if (buf != NULL)
++              errors++;
++
++      if (errors) {
++              wpa_printf(MSG_ERROR, "%d wpabuf test(s) failed", errors);
++              return -1;
++      }
++
++      return 0;
++}
++
++
++static int ip_addr_tests(void)
++{
++      int errors = 0;
++      struct hostapd_ip_addr addr;
++      char buf[100];
++
++      wpa_printf(MSG_INFO, "ip_addr tests");
++
++      if (hostapd_parse_ip_addr("1.2.3.4", &addr) != 0 ||
++          addr.af != AF_INET ||
++          hostapd_ip_txt(NULL, buf, sizeof(buf)) != NULL ||
++          hostapd_ip_txt(&addr, buf, 1) != buf || buf[0] != '\0' ||
++          hostapd_ip_txt(&addr, buf, 0) != NULL ||
++          hostapd_ip_txt(&addr, buf, sizeof(buf)) != buf)
++              errors++;
++
++      if (hostapd_parse_ip_addr("::", &addr) != 0 ||
++          addr.af != AF_INET6 ||
++          hostapd_ip_txt(&addr, buf, 1) != buf || buf[0] != '\0' ||
++          hostapd_ip_txt(&addr, buf, sizeof(buf)) != buf)
++              errors++;
++
++      if (errors) {
++              wpa_printf(MSG_ERROR, "%d ip_addr test(s) failed", errors);
++              return -1;
++      }
++
++      return 0;
++}
++
++
++struct test_eloop {
++      unsigned int magic;
++      int close_in_timeout;
++      int pipefd1[2];
++      int pipefd2[2];
++};
++
++
++static void eloop_tests_start(int close_in_timeout);
++
++
++static void eloop_test_read_2(int sock, void *eloop_ctx, void *sock_ctx)
++{
++      struct test_eloop *t = eloop_ctx;
++      ssize_t res;
++      char buf[10];
++
++      wpa_printf(MSG_INFO, "%s: sock=%d", __func__, sock);
++
++      if (t->magic != 0x12345678) {
++              wpa_printf(MSG_INFO, "%s: unexpected magic 0x%x",
++                         __func__, t->magic);
++      }
++
++      if (t->pipefd2[0] != sock) {
++              wpa_printf(MSG_INFO, "%s: unexpected sock %d != %d",
++                         __func__, sock, t->pipefd2[0]);
++      }
++
++      res = read(sock, buf, sizeof(buf));
++      wpa_printf(MSG_INFO, "%s: sock=%d --> res=%d",
++                 __func__, sock, (int) res);
++}
++
++
++static void eloop_test_read_2_wrong(int sock, void *eloop_ctx, void *sock_ctx)
++{
++      struct test_eloop *t = eloop_ctx;
++
++      wpa_printf(MSG_INFO, "%s: sock=%d", __func__, sock);
++
++      if (t->magic != 0x12345678) {
++              wpa_printf(MSG_INFO, "%s: unexpected magic 0x%x",
++                         __func__, t->magic);
++      }
++
++      if (t->pipefd2[0] != sock) {
++              wpa_printf(MSG_INFO, "%s: unexpected sock %d != %d",
++                         __func__, sock, t->pipefd2[0]);
++      }
++
++      /*
++       * This is expected to block due to the original socket with data having
++       * been closed and no new data having been written to the new socket
++       * with the same fd. To avoid blocking the process during test, skip the
++       * read here.
++       */
++      wpa_printf(MSG_ERROR, "%s: FAIL - should not have called this function",
++                 __func__);
++}
++
++
++static void reopen_pipefd2(struct test_eloop *t)
++{
++      if (t->pipefd2[0] < 0) {
++              wpa_printf(MSG_INFO, "pipefd2 had been closed");
++      } else {
++              int res;
++
++              wpa_printf(MSG_INFO, "close pipefd2");
++              eloop_unregister_read_sock(t->pipefd2[0]);
++              close(t->pipefd2[0]);
++              t->pipefd2[0] = -1;
++              close(t->pipefd2[1]);
++              t->pipefd2[1] = -1;
++
++              res = pipe(t->pipefd2);
++              if (res < 0) {
++                      wpa_printf(MSG_INFO, "pipe: %s", strerror(errno));
++                      t->pipefd2[0] = -1;
++                      t->pipefd2[1] = -1;
++                      return;
++              }
++
++              wpa_printf(MSG_INFO,
++                         "re-register pipefd2 with new sockets %d,%d",
++                         t->pipefd2[0], t->pipefd2[1]);
++              eloop_register_read_sock(t->pipefd2[0], eloop_test_read_2_wrong,
++                                       t, NULL);
++      }
++}
++
++
++static void eloop_test_read_1(int sock, void *eloop_ctx, void *sock_ctx)
++{
++      struct test_eloop *t = eloop_ctx;
++      ssize_t res;
++      char buf[10];
++
++      wpa_printf(MSG_INFO, "%s: sock=%d", __func__, sock);
++
++      if (t->magic != 0x12345678) {
++              wpa_printf(MSG_INFO, "%s: unexpected magic 0x%x",
++                         __func__, t->magic);
++      }
++
++      if (t->pipefd1[0] != sock) {
++              wpa_printf(MSG_INFO, "%s: unexpected sock %d != %d",
++                         __func__, sock, t->pipefd1[0]);
++      }
++
++      res = read(sock, buf, sizeof(buf));
++      wpa_printf(MSG_INFO, "%s: sock=%d --> res=%d",
++                 __func__, sock, (int) res);
++
++      if (!t->close_in_timeout)
++              reopen_pipefd2(t);
++}
++
++
++static void eloop_test_cb(void *eloop_data, void *user_ctx)
++{
++      struct test_eloop *t = eloop_data;
++
++      wpa_printf(MSG_INFO, "%s", __func__);
++
++      if (t->magic != 0x12345678) {
++              wpa_printf(MSG_INFO, "%s: unexpected magic 0x%x",
++                         __func__, t->magic);
++      }
++
++      if (t->close_in_timeout)
++              reopen_pipefd2(t);
++}
++
++
++static void eloop_test_timeout(void *eloop_data, void *user_ctx)
++{
++      struct test_eloop *t = eloop_data;
++      int next_run = 0;
++
++      wpa_printf(MSG_INFO, "%s", __func__);
++
++      if (t->magic != 0x12345678) {
++              wpa_printf(MSG_INFO, "%s: unexpected magic 0x%x",
++                         __func__, t->magic);
++      }
++
++      if (t->pipefd1[0] >= 0) {
++              wpa_printf(MSG_INFO, "pipefd1 had not been closed");
++              eloop_unregister_read_sock(t->pipefd1[0]);
++              close(t->pipefd1[0]);
++              t->pipefd1[0] = -1;
++              close(t->pipefd1[1]);
++              t->pipefd1[1] = -1;
++      }
++
++      if (t->pipefd2[0] >= 0) {
++              wpa_printf(MSG_INFO, "pipefd2 had not been closed");
++              eloop_unregister_read_sock(t->pipefd2[0]);
++              close(t->pipefd2[0]);
++              t->pipefd2[0] = -1;
++              close(t->pipefd2[1]);
++              t->pipefd2[1] = -1;
++      }
++
++      next_run = t->close_in_timeout;
++      t->magic = 0;
++      wpa_printf(MSG_INFO, "%s - free(%p)", __func__, t);
++      os_free(t);
++
++      if (next_run)
++              eloop_tests_start(0);
++}
++
++
++static void eloop_tests_start(int close_in_timeout)
++{
++      struct test_eloop *t;
++      int res;
++
++      t = os_zalloc(sizeof(*t));
++      if (!t)
++              return;
++      t->magic = 0x12345678;
++      t->close_in_timeout = close_in_timeout;
++
++      wpa_printf(MSG_INFO, "starting eloop tests (%p) (close_in_timeout=%d)",
++                 t, close_in_timeout);
++
++      res = pipe(t->pipefd1);
++      if (res < 0) {
++              wpa_printf(MSG_INFO, "pipe: %s", strerror(errno));
++              os_free(t);
++              return;
++      }
++
++      res = pipe(t->pipefd2);
++      if (res < 0) {
++              wpa_printf(MSG_INFO, "pipe: %s", strerror(errno));
++              close(t->pipefd1[0]);
++              close(t->pipefd1[1]);
++              os_free(t);
++              return;
++      }
++
++      wpa_printf(MSG_INFO, "pipe fds: %d,%d %d,%d",
++                 t->pipefd1[0], t->pipefd1[1],
++                 t->pipefd2[0], t->pipefd2[1]);
++
++      eloop_register_read_sock(t->pipefd1[0], eloop_test_read_1, t, NULL);
++      eloop_register_read_sock(t->pipefd2[0], eloop_test_read_2, t, NULL);
++      eloop_register_timeout(0, 0, eloop_test_cb, t, NULL);
++      eloop_register_timeout(0, 200000, eloop_test_timeout, t, NULL);
++
++      if (write(t->pipefd1[1], "HELLO", 5) < 0)
++              wpa_printf(MSG_INFO, "write: %s", strerror(errno));
++      if (write(t->pipefd2[1], "TEST", 4) < 0)
++              wpa_printf(MSG_INFO, "write: %s", strerror(errno));
++      os_sleep(0, 50000);
++      wpa_printf(MSG_INFO, "waiting for eloop callbacks");
++}
++
++
++static void eloop_tests_run(void *eloop_data, void *user_ctx)
++{
++      eloop_tests_start(1);
++}
++
++
++static int eloop_tests(void)
++{
++      wpa_printf(MSG_INFO, "schedule eloop tests to be run");
++
++      /*
++       * Cannot return error from these without a significant design change,
++       * so for now, run the tests from a scheduled timeout and require
++       * separate verification of the results from the debug log.
++       */
++      eloop_register_timeout(0, 0, eloop_tests_run, NULL, NULL);
++
++      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 ||
++          os_tests() < 0 ||
++          wpabuf_tests() < 0 ||
++          ip_addr_tests() < 0 ||
++          eloop_tests() < 0 ||
 +          int_array_tests() < 0)
 +              ret = -1;
 +
 +      return ret;
 +}
index 0d11905185365692dfa3dea2c3bf7776c6e9e958,0000000000000000000000000000000000000000..61c0d5ce68c79b27d61dbdb4ec429f4bd22a1aa3
mode 100644,000000..100644
--- /dev/null
@@@ -1,794 -1,0 +1,860 @@@
-               os_free(strbuf);
 +/*
 + * wpa_supplicant/hostapd / Debug prints
 + * 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.
 + */
 +
 +#include "includes.h"
 +
 +#include "common.h"
 +
 +#ifdef CONFIG_DEBUG_SYSLOG
 +#include <syslog.h>
 +
 +static int wpa_debug_syslog = 0;
 +#endif /* CONFIG_DEBUG_SYSLOG */
 +
 +#ifdef CONFIG_DEBUG_LINUX_TRACING
 +#include <sys/types.h>
 +#include <sys/stat.h>
 +#include <fcntl.h>
 +#include <string.h>
 +#include <stdio.h>
 +
 +static FILE *wpa_debug_tracing_file = NULL;
 +
 +#define WPAS_TRACE_PFX "wpas <%d>: "
 +#endif /* CONFIG_DEBUG_LINUX_TRACING */
 +
 +
 +int wpa_debug_level = MSG_INFO;
 +int wpa_debug_show_keys = 0;
 +int wpa_debug_timestamp = 0;
 +
 +
 +#ifdef CONFIG_ANDROID_LOG
 +
 +#include <android/log.h>
 +
 +#ifndef ANDROID_LOG_NAME
 +#define ANDROID_LOG_NAME      "wpa_supplicant"
 +#endif /* ANDROID_LOG_NAME */
 +
 +static int wpa_to_android_level(int level)
 +{
 +      if (level == MSG_ERROR)
 +              return ANDROID_LOG_ERROR;
 +      if (level == MSG_WARNING)
 +              return ANDROID_LOG_WARN;
 +      if (level == MSG_INFO)
 +              return ANDROID_LOG_INFO;
 +      return ANDROID_LOG_DEBUG;
 +}
 +
 +#endif /* CONFIG_ANDROID_LOG */
 +
 +#ifndef CONFIG_NO_STDOUT_DEBUG
 +
 +#ifdef CONFIG_DEBUG_FILE
 +static FILE *out_file = NULL;
 +#endif /* CONFIG_DEBUG_FILE */
 +
 +
 +void wpa_debug_print_timestamp(void)
 +{
 +#ifndef CONFIG_ANDROID_LOG
 +      struct os_time tv;
 +
 +      if (!wpa_debug_timestamp)
 +              return;
 +
 +      os_get_time(&tv);
 +#ifdef CONFIG_DEBUG_FILE
 +      if (out_file) {
 +              fprintf(out_file, "%ld.%06u: ", (long) tv.sec,
 +                      (unsigned int) tv.usec);
 +      } else
 +#endif /* CONFIG_DEBUG_FILE */
 +      printf("%ld.%06u: ", (long) tv.sec, (unsigned int) tv.usec);
 +#endif /* CONFIG_ANDROID_LOG */
 +}
 +
 +
 +#ifdef CONFIG_DEBUG_SYSLOG
 +#ifndef LOG_HOSTAPD
 +#define LOG_HOSTAPD LOG_DAEMON
 +#endif /* LOG_HOSTAPD */
 +
 +void wpa_debug_open_syslog(void)
 +{
 +      openlog("wpa_supplicant", LOG_PID | LOG_NDELAY, LOG_HOSTAPD);
 +      wpa_debug_syslog++;
 +}
 +
 +
 +void wpa_debug_close_syslog(void)
 +{
 +      if (wpa_debug_syslog)
 +              closelog();
 +}
 +
 +
 +static int syslog_priority(int level)
 +{
 +      switch (level) {
 +      case MSG_MSGDUMP:
 +      case MSG_DEBUG:
 +              return LOG_DEBUG;
 +      case MSG_INFO:
 +              return LOG_NOTICE;
 +      case MSG_WARNING:
 +              return LOG_WARNING;
 +      case MSG_ERROR:
 +              return LOG_ERR;
 +      }
 +      return LOG_INFO;
 +}
 +#endif /* CONFIG_DEBUG_SYSLOG */
 +
 +
 +#ifdef CONFIG_DEBUG_LINUX_TRACING
 +
 +int wpa_debug_open_linux_tracing(void)
 +{
 +      int mounts, trace_fd;
 +      char buf[4096] = {};
 +      ssize_t buflen;
 +      char *line, *tmp1, *path = NULL;
 +
 +      mounts = open("/proc/mounts", O_RDONLY);
 +      if (mounts < 0) {
 +              printf("no /proc/mounts\n");
 +              return -1;
 +      }
 +
 +      buflen = read(mounts, buf, sizeof(buf) - 1);
 +      close(mounts);
 +      if (buflen < 0) {
 +              printf("failed to read /proc/mounts\n");
 +              return -1;
 +      }
 +
 +      line = strtok_r(buf, "\n", &tmp1);
 +      while (line) {
 +              char *tmp2, *tmp_path, *fstype;
 +              /* "<dev> <mountpoint> <fs type> ..." */
 +              strtok_r(line, " ", &tmp2);
 +              tmp_path = strtok_r(NULL, " ", &tmp2);
 +              fstype = strtok_r(NULL, " ", &tmp2);
 +              if (strcmp(fstype, "debugfs") == 0) {
 +                      path = tmp_path;
 +                      break;
 +              }
 +
 +              line = strtok_r(NULL, "\n", &tmp1);
 +      }
 +
 +      if (path == NULL) {
 +              printf("debugfs mountpoint not found\n");
 +              return -1;
 +      }
 +
 +      snprintf(buf, sizeof(buf) - 1, "%s/tracing/trace_marker", path);
 +
 +      trace_fd = open(buf, O_WRONLY);
 +      if (trace_fd < 0) {
 +              printf("failed to open trace_marker file\n");
 +              return -1;
 +      }
 +      wpa_debug_tracing_file = fdopen(trace_fd, "w");
 +      if (wpa_debug_tracing_file == NULL) {
 +              close(trace_fd);
 +              printf("failed to fdopen()\n");
 +              return -1;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +void wpa_debug_close_linux_tracing(void)
 +{
 +      if (wpa_debug_tracing_file == NULL)
 +              return;
 +      fclose(wpa_debug_tracing_file);
 +      wpa_debug_tracing_file = NULL;
 +}
 +
 +#endif /* CONFIG_DEBUG_LINUX_TRACING */
 +
 +
 +/**
 + * wpa_printf - conditional printf
 + * @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. The
 + * output may be directed to stdout, stderr, and/or syslog based on
 + * configuration.
 + *
 + * Note: New line '\n' is added to the end of the text when printing to stdout.
 + */
 +void wpa_printf(int level, const char *fmt, ...)
 +{
 +      va_list ap;
 +
 +      va_start(ap, fmt);
 +      if (level >= wpa_debug_level) {
 +#ifdef CONFIG_ANDROID_LOG
 +              __android_log_vprint(wpa_to_android_level(level),
 +                                   ANDROID_LOG_NAME, fmt, ap);
 +#else /* CONFIG_ANDROID_LOG */
 +#ifdef CONFIG_DEBUG_SYSLOG
 +              if (wpa_debug_syslog) {
 +                      vsyslog(syslog_priority(level), fmt, ap);
 +              } else {
 +#endif /* CONFIG_DEBUG_SYSLOG */
 +              wpa_debug_print_timestamp();
 +#ifdef CONFIG_DEBUG_FILE
 +              if (out_file) {
 +                      vfprintf(out_file, fmt, ap);
 +                      fprintf(out_file, "\n");
 +              } else {
 +#endif /* CONFIG_DEBUG_FILE */
 +              vprintf(fmt, ap);
 +              printf("\n");
 +#ifdef CONFIG_DEBUG_FILE
 +              }
 +#endif /* CONFIG_DEBUG_FILE */
 +#ifdef CONFIG_DEBUG_SYSLOG
 +              }
 +#endif /* CONFIG_DEBUG_SYSLOG */
 +#endif /* CONFIG_ANDROID_LOG */
 +      }
 +      va_end(ap);
 +
 +#ifdef CONFIG_DEBUG_LINUX_TRACING
 +      if (wpa_debug_tracing_file != NULL) {
 +              va_start(ap, fmt);
 +              fprintf(wpa_debug_tracing_file, WPAS_TRACE_PFX, level);
 +              vfprintf(wpa_debug_tracing_file, fmt, ap);
 +              fprintf(wpa_debug_tracing_file, "\n");
 +              fflush(wpa_debug_tracing_file);
 +              va_end(ap);
 +      }
 +#endif /* CONFIG_DEBUG_LINUX_TRACING */
 +}
 +
 +
 +static void _wpa_hexdump(int level, const char *title, const u8 *buf,
 +                       size_t len, int show)
 +{
 +      size_t i;
 +
 +#ifdef CONFIG_DEBUG_LINUX_TRACING
 +      if (wpa_debug_tracing_file != NULL) {
 +              fprintf(wpa_debug_tracing_file,
 +                      WPAS_TRACE_PFX "%s - hexdump(len=%lu):",
 +                      level, title, (unsigned long) len);
 +              if (buf == NULL) {
 +                      fprintf(wpa_debug_tracing_file, " [NULL]\n");
 +              } else if (!show) {
 +                      fprintf(wpa_debug_tracing_file, " [REMOVED]\n");
 +              } else {
 +                      for (i = 0; i < len; i++)
 +                              fprintf(wpa_debug_tracing_file,
 +                                      " %02x", buf[i]);
 +              }
 +              fflush(wpa_debug_tracing_file);
 +      }
 +#endif /* CONFIG_DEBUG_LINUX_TRACING */
 +
 +      if (level < wpa_debug_level)
 +              return;
 +#ifdef CONFIG_ANDROID_LOG
 +      {
 +              const char *display;
 +              char *strbuf = NULL;
 +              size_t slen = len;
 +              if (buf == NULL) {
 +                      display = " [NULL]";
 +              } else if (len == 0) {
 +                      display = "";
 +              } else if (show && len) {
 +                      /* Limit debug message length for Android log */
 +                      if (slen > 32)
 +                              slen = 32;
 +                      strbuf = os_malloc(1 + 3 * slen);
 +                      if (strbuf == NULL) {
 +                              wpa_printf(MSG_ERROR, "wpa_hexdump: Failed to "
 +                                         "allocate message buffer");
 +                              return;
 +                      }
 +
 +                      for (i = 0; i < slen; i++)
 +                              os_snprintf(&strbuf[i * 3], 4, " %02x",
 +                                          buf[i]);
 +
 +                      display = strbuf;
 +              } else {
 +                      display = " [REMOVED]";
 +              }
 +
 +              __android_log_print(wpa_to_android_level(level),
 +                                  ANDROID_LOG_NAME,
 +                                  "%s - hexdump(len=%lu):%s%s",
 +                                  title, (long unsigned int) len, display,
 +                                  len > slen ? " ..." : "");
-               os_free(strbuf);
++              bin_clear_free(strbuf, 1 + 3 * slen);
 +              return;
 +      }
 +#else /* CONFIG_ANDROID_LOG */
 +#ifdef CONFIG_DEBUG_SYSLOG
 +      if (wpa_debug_syslog) {
 +              const char *display;
 +              char *strbuf = NULL;
 +
 +              if (buf == NULL) {
 +                      display = " [NULL]";
 +              } else if (len == 0) {
 +                      display = "";
 +              } else if (show && len) {
 +                      strbuf = os_malloc(1 + 3 * len);
 +                      if (strbuf == NULL) {
 +                              wpa_printf(MSG_ERROR, "wpa_hexdump: Failed to "
 +                                         "allocate message buffer");
 +                              return;
 +                      }
 +
 +                      for (i = 0; i < len; i++)
 +                              os_snprintf(&strbuf[i * 3], 4, " %02x",
 +                                          buf[i]);
 +
 +                      display = strbuf;
 +              } else {
 +                      display = " [REMOVED]";
 +              }
 +
 +              syslog(syslog_priority(level), "%s - hexdump(len=%lu):%s",
 +                     title, (unsigned long) len, display);
-               wpa_msg_cb(ctx, level, 0, buf, len);
-       os_free(buf);
++              bin_clear_free(strbuf, 1 + 3 * len);
 +              return;
 +      }
 +#endif /* CONFIG_DEBUG_SYSLOG */
 +      wpa_debug_print_timestamp();
 +#ifdef CONFIG_DEBUG_FILE
 +      if (out_file) {
 +              fprintf(out_file, "%s - hexdump(len=%lu):",
 +                      title, (unsigned long) len);
 +              if (buf == NULL) {
 +                      fprintf(out_file, " [NULL]");
 +              } else if (show) {
 +                      for (i = 0; i < len; i++)
 +                              fprintf(out_file, " %02x", buf[i]);
 +              } else {
 +                      fprintf(out_file, " [REMOVED]");
 +              }
 +              fprintf(out_file, "\n");
 +      } else {
 +#endif /* CONFIG_DEBUG_FILE */
 +      printf("%s - hexdump(len=%lu):", title, (unsigned long) len);
 +      if (buf == NULL) {
 +              printf(" [NULL]");
 +      } else if (show) {
 +              for (i = 0; i < len; i++)
 +                      printf(" %02x", buf[i]);
 +      } else {
 +              printf(" [REMOVED]");
 +      }
 +      printf("\n");
 +#ifdef CONFIG_DEBUG_FILE
 +      }
 +#endif /* CONFIG_DEBUG_FILE */
 +#endif /* CONFIG_ANDROID_LOG */
 +}
 +
 +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 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 void *buf,
 +                             size_t len, int show)
 +{
 +      size_t i, llen;
 +      const u8 *pos = buf;
 +      const size_t line_len = 16;
 +
 +#ifdef CONFIG_DEBUG_LINUX_TRACING
 +      if (wpa_debug_tracing_file != NULL) {
 +              fprintf(wpa_debug_tracing_file,
 +                      WPAS_TRACE_PFX "%s - hexdump_ascii(len=%lu):",
 +                      level, title, (unsigned long) len);
 +              if (buf == NULL) {
 +                      fprintf(wpa_debug_tracing_file, " [NULL]\n");
 +              } else if (!show) {
 +                      fprintf(wpa_debug_tracing_file, " [REMOVED]\n");
 +              } else {
 +                      /* can do ascii processing in userspace */
 +                      for (i = 0; i < len; i++)
 +                              fprintf(wpa_debug_tracing_file,
 +                                      " %02x", pos[i]);
 +              }
 +              fflush(wpa_debug_tracing_file);
 +      }
 +#endif /* CONFIG_DEBUG_LINUX_TRACING */
 +
 +      if (level < wpa_debug_level)
 +              return;
 +#ifdef CONFIG_ANDROID_LOG
 +      _wpa_hexdump(level, title, buf, len, show);
 +#else /* CONFIG_ANDROID_LOG */
 +      wpa_debug_print_timestamp();
 +#ifdef CONFIG_DEBUG_FILE
 +      if (out_file) {
 +              if (!show) {
 +                      fprintf(out_file,
 +                              "%s - hexdump_ascii(len=%lu): [REMOVED]\n",
 +                              title, (unsigned long) len);
 +                      return;
 +              }
 +              if (buf == NULL) {
 +                      fprintf(out_file,
 +                              "%s - hexdump_ascii(len=%lu): [NULL]\n",
 +                              title, (unsigned long) len);
 +                      return;
 +              }
 +              fprintf(out_file, "%s - hexdump_ascii(len=%lu):\n",
 +                      title, (unsigned long) len);
 +              while (len) {
 +                      llen = len > line_len ? line_len : len;
 +                      fprintf(out_file, "    ");
 +                      for (i = 0; i < llen; i++)
 +                              fprintf(out_file, " %02x", pos[i]);
 +                      for (i = llen; i < line_len; i++)
 +                              fprintf(out_file, "   ");
 +                      fprintf(out_file, "   ");
 +                      for (i = 0; i < llen; i++) {
 +                              if (isprint(pos[i]))
 +                                      fprintf(out_file, "%c", pos[i]);
 +                              else
 +                                      fprintf(out_file, "_");
 +                      }
 +                      for (i = llen; i < line_len; i++)
 +                              fprintf(out_file, " ");
 +                      fprintf(out_file, "\n");
 +                      pos += llen;
 +                      len -= llen;
 +              }
 +      } else {
 +#endif /* CONFIG_DEBUG_FILE */
 +      if (!show) {
 +              printf("%s - hexdump_ascii(len=%lu): [REMOVED]\n",
 +                     title, (unsigned long) len);
 +              return;
 +      }
 +      if (buf == NULL) {
 +              printf("%s - hexdump_ascii(len=%lu): [NULL]\n",
 +                     title, (unsigned long) len);
 +              return;
 +      }
 +      printf("%s - hexdump_ascii(len=%lu):\n", title, (unsigned long) len);
 +      while (len) {
 +              llen = len > line_len ? line_len : len;
 +              printf("    ");
 +              for (i = 0; i < llen; i++)
 +                      printf(" %02x", pos[i]);
 +              for (i = llen; i < line_len; i++)
 +                      printf("   ");
 +              printf("   ");
 +              for (i = 0; i < llen; i++) {
 +                      if (isprint(pos[i]))
 +                              printf("%c", pos[i]);
 +                      else
 +                              printf("_");
 +              }
 +              for (i = llen; i < line_len; i++)
 +                      printf(" ");
 +              printf("\n");
 +              pos += llen;
 +              len -= llen;
 +      }
 +#ifdef CONFIG_DEBUG_FILE
 +      }
 +#endif /* CONFIG_DEBUG_FILE */
 +#endif /* CONFIG_ANDROID_LOG */
 +}
 +
 +
 +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 void *buf,
 +                         size_t len)
 +{
 +      _wpa_hexdump_ascii(level, title, buf, len, wpa_debug_show_keys);
 +}
 +
 +
 +#ifdef CONFIG_DEBUG_FILE
 +static char *last_path = NULL;
 +#endif /* CONFIG_DEBUG_FILE */
 +
 +int wpa_debug_reopen_file(void)
 +{
 +#ifdef CONFIG_DEBUG_FILE
 +      int rv;
 +      if (last_path) {
 +              char *tmp = os_strdup(last_path);
 +              wpa_debug_close_file();
 +              rv = wpa_debug_open_file(tmp);
 +              os_free(tmp);
 +      } else {
 +              wpa_printf(MSG_ERROR, "Last-path was not set, cannot "
 +                         "re-open log file.");
 +              rv = -1;
 +      }
 +      return rv;
 +#else /* CONFIG_DEBUG_FILE */
 +      return 0;
 +#endif /* CONFIG_DEBUG_FILE */
 +}
 +
 +
 +int wpa_debug_open_file(const char *path)
 +{
 +#ifdef CONFIG_DEBUG_FILE
 +      if (!path)
 +              return 0;
 +
 +      if (last_path == NULL || os_strcmp(last_path, path) != 0) {
 +              /* Save our path to enable re-open */
 +              os_free(last_path);
 +              last_path = os_strdup(path);
 +      }
 +
 +      out_file = fopen(path, "a");
 +      if (out_file == NULL) {
 +              wpa_printf(MSG_ERROR, "wpa_debug_open_file: Failed to open "
 +                         "output file, using standard output");
 +              return -1;
 +      }
 +#ifndef _WIN32
 +      setvbuf(out_file, NULL, _IOLBF, 0);
 +#endif /* _WIN32 */
 +#else /* CONFIG_DEBUG_FILE */
 +      (void)path;
 +#endif /* CONFIG_DEBUG_FILE */
 +      return 0;
 +}
 +
 +
 +void wpa_debug_close_file(void)
 +{
 +#ifdef CONFIG_DEBUG_FILE
 +      if (!out_file)
 +              return;
 +      fclose(out_file);
 +      out_file = NULL;
 +      os_free(last_path);
 +      last_path = NULL;
 +#endif /* CONFIG_DEBUG_FILE */
 +}
 +
 +
 +void wpa_debug_setup_stdout(void)
 +{
 +#ifndef _WIN32
 +      setvbuf(stdout, NULL, _IOLBF, 0);
 +#endif /* _WIN32 */
 +}
 +
 +#endif /* CONFIG_NO_STDOUT_DEBUG */
 +
 +
 +#ifndef CONFIG_NO_WPA_MSG
 +static wpa_msg_cb_func wpa_msg_cb = NULL;
 +
 +void wpa_msg_register_cb(wpa_msg_cb_func func)
 +{
 +      wpa_msg_cb = func;
 +}
 +
 +
 +static wpa_msg_get_ifname_func wpa_msg_ifname_cb = NULL;
 +
 +void wpa_msg_register_ifname_cb(wpa_msg_get_ifname_func func)
 +{
 +      wpa_msg_ifname_cb = func;
 +}
 +
 +
 +void wpa_msg(void *ctx, int level, const char *fmt, ...)
 +{
 +      va_list ap;
 +      char *buf;
 +      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 "
 +                         "buffer");
 +              return;
 +      }
 +      va_start(ap, fmt);
 +      prefix[0] = '\0';
 +      if (wpa_msg_ifname_cb) {
 +              const char *ifname = wpa_msg_ifname_cb(ctx);
 +              if (ifname) {
 +                      int res = os_snprintf(prefix, sizeof(prefix), "%s: ",
 +                                            ifname);
 +                      if (os_snprintf_error(sizeof(prefix), res))
 +                              prefix[0] = '\0';
 +              }
 +      }
 +      len = vsnprintf(buf, buflen, fmt, ap);
 +      va_end(ap);
 +      wpa_printf(level, "%s%s", prefix, buf);
 +      if (wpa_msg_cb)
-       wpa_msg_cb(ctx, level, 0, buf, len);
-       os_free(buf);
++              wpa_msg_cb(ctx, level, WPA_MSG_PER_INTERFACE, buf, len);
++      bin_clear_free(buf, buflen);
 +}
 +
 +
 +void wpa_msg_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_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);
++      wpa_msg_cb(ctx, level, WPA_MSG_PER_INTERFACE, buf, len);
++      bin_clear_free(buf, buflen);
 +}
 +
 +
 +void wpa_msg_global(void *ctx, int level, const char *fmt, ...)
 +{
 +      va_list ap;
 +      char *buf;
 +      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 "
 +                         "message buffer");
 +              return;
 +      }
 +      va_start(ap, fmt);
 +      len = vsnprintf(buf, buflen, fmt, ap);
 +      va_end(ap);
 +      wpa_printf(level, "%s", buf);
 +      if (wpa_msg_cb)
-       wpa_msg_cb(ctx, level, 1, buf, len);
-       os_free(buf);
++              wpa_msg_cb(ctx, level, WPA_MSG_GLOBAL, buf, len);
++      bin_clear_free(buf, buflen);
 +}
 +
 +
 +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, 2, buf, len);
++      wpa_msg_cb(ctx, level, WPA_MSG_GLOBAL, buf, len);
++      bin_clear_free(buf, buflen);
 +}
 +
 +
 +void wpa_msg_no_global(void *ctx, int level, const char *fmt, ...)
 +{
 +      va_list ap;
 +      char *buf;
 +      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 "
 +                         "message buffer");
 +              return;
 +      }
 +      va_start(ap, fmt);
 +      len = vsnprintf(buf, buflen, fmt, ap);
 +      va_end(ap);
 +      wpa_printf(level, "%s", buf);
 +      if (wpa_msg_cb)
-       os_free(buf);
++              wpa_msg_cb(ctx, level, WPA_MSG_NO_GLOBAL, buf, len);
++      bin_clear_free(buf, buflen);
++}
++
++
++void wpa_msg_global_only(void *ctx, int level, const char *fmt, ...)
++{
++      va_list ap;
++      char *buf;
++      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, "%s: Failed to allocate message buffer",
++                         __func__);
++              return;
++      }
++      va_start(ap, fmt);
++      len = vsnprintf(buf, buflen, fmt, ap);
++      va_end(ap);
++      wpa_printf(level, "%s", buf);
++      if (wpa_msg_cb)
++              wpa_msg_cb(ctx, level, WPA_MSG_ONLY_GLOBAL, buf, len);
 +      os_free(buf);
 +}
 +
 +#endif /* CONFIG_NO_WPA_MSG */
 +
 +
 +#ifndef CONFIG_NO_HOSTAPD_LOGGER
 +static hostapd_logger_cb_func hostapd_logger_cb = NULL;
 +
 +void hostapd_logger_register_cb(hostapd_logger_cb_func func)
 +{
 +      hostapd_logger_cb = func;
 +}
 +
 +
 +void hostapd_logger(void *ctx, const u8 *addr, unsigned int module, int level,
 +                  const char *fmt, ...)
 +{
 +      va_list ap;
 +      char *buf;
 +      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 "
 +                         "message buffer");
 +              return;
 +      }
 +      va_start(ap, fmt);
 +      len = vsnprintf(buf, buflen, fmt, ap);
 +      va_end(ap);
 +      if (hostapd_logger_cb)
 +              hostapd_logger_cb(ctx, addr, module, level, buf, len);
 +      else if (addr)
 +              wpa_printf(MSG_DEBUG, "hostapd_logger: STA " MACSTR " - %s",
 +                         MAC2STR(addr), buf);
 +      else
 +              wpa_printf(MSG_DEBUG, "hostapd_logger: %s", buf);
++      bin_clear_free(buf, buflen);
 +}
 +#endif /* CONFIG_NO_HOSTAPD_LOGGER */
++
++
++const char * debug_level_str(int level)
++{
++      switch (level) {
++      case MSG_EXCESSIVE:
++              return "EXCESSIVE";
++      case MSG_MSGDUMP:
++              return "MSGDUMP";
++      case MSG_DEBUG:
++              return "DEBUG";
++      case MSG_INFO:
++              return "INFO";
++      case MSG_WARNING:
++              return "WARNING";
++      case MSG_ERROR:
++              return "ERROR";
++      default:
++              return "?";
++      }
++}
++
++
++int str_to_debug_level(const char *s)
++{
++      if (os_strcasecmp(s, "EXCESSIVE") == 0)
++              return MSG_EXCESSIVE;
++      if (os_strcasecmp(s, "MSGDUMP") == 0)
++              return MSG_MSGDUMP;
++      if (os_strcasecmp(s, "DEBUG") == 0)
++              return MSG_DEBUG;
++      if (os_strcasecmp(s, "INFO") == 0)
++              return MSG_INFO;
++      if (os_strcasecmp(s, "WARNING") == 0)
++              return MSG_WARNING;
++      if (os_strcasecmp(s, "ERROR") == 0)
++              return MSG_ERROR;
++      return -1;
++}
index 400bea9e599fdedc3b6964d9b1146c85a2f86f61,0000000000000000000000000000000000000000..17d8f963802e4145fe8e52431b4ab8c5602ab2ca
mode 100644,000000..100644
--- /dev/null
@@@ -1,345 -1,0 +1,370 @@@
- typedef void (*wpa_msg_cb_func)(void *ctx, int level, int global,
 +/*
 + * wpa_supplicant/hostapd / Debug prints
 + * 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.
 + */
 +
 +#ifndef WPA_DEBUG_H
 +#define WPA_DEBUG_H
 +
 +#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. */
 +
 +enum {
 +      MSG_EXCESSIVE, MSG_MSGDUMP, MSG_DEBUG, MSG_INFO, MSG_WARNING, MSG_ERROR
 +};
 +
 +#ifdef CONFIG_NO_STDOUT_DEBUG
 +
 +#define wpa_debug_print_timestamp() do { } while (0)
 +#define wpa_printf(args...) do { } while (0)
 +#define wpa_hexdump(l,t,b,le) do { } while (0)
 +#define wpa_hexdump_buf(l,t,b) do { } while (0)
 +#define wpa_hexdump_key(l,t,b,le) do { } while (0)
 +#define wpa_hexdump_buf_key(l,t,b) do { } while (0)
 +#define wpa_hexdump_ascii(l,t,b,le) do { } while (0)
 +#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)
 +{
 +      return 0;
 +}
 +
 +#else /* CONFIG_NO_STDOUT_DEBUG */
 +
 +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
 + *
 + * This function prints a timestamp in seconds_from_1970.microsoconds
 + * format if debug output has been configured to include timestamps in debug
 + * messages.
 + */
 +void wpa_debug_print_timestamp(void);
 +
 +/**
 + * wpa_printf - conditional printf
 + * @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. The
 + * output may be directed to stdout, stderr, and/or syslog based on
 + * configuration.
 + *
 + * Note: New line '\n' is added to the end of the text when printing to stdout.
 + */
 +void wpa_printf(int level, const char *fmt, ...)
 +PRINTF_FORMAT(2, 3);
 +
 +/**
 + * wpa_hexdump - conditional hex dump
 + * @level: priority level (MSG_*) of the message
 + * @title: title of for the message
 + * @buf: data buffer to be dumped
 + * @len: length of the buf
 + *
 + * This function is used to print conditional debugging and error messages. The
 + * 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 void *buf, size_t len);
 +
 +static inline void wpa_hexdump_buf(int level, const char *title,
 +                                 const struct wpabuf *buf)
 +{
 +      wpa_hexdump(level, title, buf ? wpabuf_head(buf) : NULL,
 +                  buf ? wpabuf_len(buf) : 0);
 +}
 +
 +/**
 + * wpa_hexdump_key - conditional hex dump, hide keys
 + * @level: priority level (MSG_*) of the message
 + * @title: title of for the message
 + * @buf: data buffer to be dumped
 + * @len: length of the buf
 + *
 + * This function is used to print conditional debugging and error messages. The
 + * output may be directed to stdout, stderr, and/or syslog based on
 + * configuration. The contents of buf is printed out has hex dump. This works
 + * 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 void *buf, size_t len);
 +
 +static inline void wpa_hexdump_buf_key(int level, const char *title,
 +                                     const struct wpabuf *buf)
 +{
 +      wpa_hexdump_key(level, title, buf ? wpabuf_head(buf) : NULL,
 +                      buf ? wpabuf_len(buf) : 0);
 +}
 +
 +/**
 + * wpa_hexdump_ascii - conditional hex dump
 + * @level: priority level (MSG_*) of the message
 + * @title: title of for the message
 + * @buf: data buffer to be dumped
 + * @len: length of the buf
 + *
 + * This function is used to print conditional debugging and error messages. The
 + * output may be directed to stdout, stderr, and/or syslog based on
 + * configuration. The contents of buf is printed out has hex dump with both
 + * 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 void *buf,
 +                     size_t len);
 +
 +/**
 + * wpa_hexdump_ascii_key - conditional hex dump, hide keys
 + * @level: priority level (MSG_*) of the message
 + * @title: title of for the message
 + * @buf: data buffer to be dumped
 + * @len: length of the buf
 + *
 + * This function is used to print conditional debugging and error messages. The
 + * output may be directed to stdout, stderr, and/or syslog based on
 + * configuration. The contents of buf is printed out has hex dump with both
 + * the hex numbers and ASCII characters (for printable range) are shown. 16
 + * 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 void *buf,
 +                         size_t len);
 +
 +/*
 + * wpa_dbg() behaves like wpa_msg(), but it can be removed from build to reduce
 + * binary size. As such, it should be used with debugging messages that are not
 + * needed in the control interface while wpa_msg() has to be used for anything
 + * that needs to shown to control interface monitors.
 + */
 +#define wpa_dbg(args...) wpa_msg(args)
 +
 +#endif /* CONFIG_NO_STDOUT_DEBUG */
 +
 +
 +#ifdef CONFIG_NO_WPA_MSG
 +#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_global_only(args...) do { } while (0)
 +#define wpa_msg_register_cb(f) do { } while (0)
 +#define wpa_msg_register_ifname_cb(f) do { } while (0)
 +#else /* CONFIG_NO_WPA_MSG */
 +/**
 + * wpa_msg - Conditional printf for default target and 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. The
 + * output may be directed to stdout, stderr, and/or syslog based on
 + * configuration. This function is like wpa_printf(), but it also sends the
 + * same message to all attached ctrl_iface monitors.
 + *
 + * Note: New line '\n' is added to the end of the text when printing to stdout.
 + */
 +void wpa_msg(void *ctx, int level, const char *fmt, ...) PRINTF_FORMAT(3, 4);
 +
 +/**
 + * wpa_msg_ctrl - Conditional 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(), but it sends the output only to the
 + * attached 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_ctrl(void *ctx, int level, const char *fmt, ...)
 +PRINTF_FORMAT(3, 4);
 +
 +/**
 + * wpa_msg_global - 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(), but it sends the output as a global event,
 + * i.e., without being specific to an interface. For backwards compatibility,
 + * an old style event is also delivered on one of the interfaces (the one
 + * specified by the context data).
 + */
 +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()
 + * @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(), but it does not send the output as a global
 + * event.
 + */
 +void wpa_msg_no_global(void *ctx, int level, const char *fmt, ...)
 +PRINTF_FORMAT(3, 4);
 +
++/**
++ * wpa_msg_global_only - Conditional 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 as a
++ * global event.
++ */
++void wpa_msg_global_only(void *ctx, int level, const char *fmt, ...)
++PRINTF_FORMAT(3, 4);
++
++enum wpa_msg_type {
++      WPA_MSG_PER_INTERFACE,
++      WPA_MSG_GLOBAL,
++      WPA_MSG_NO_GLOBAL,
++      WPA_MSG_ONLY_GLOBAL,
++};
++
++typedef void (*wpa_msg_cb_func)(void *ctx, int level, enum wpa_msg_type type,
 +                              const char *txt, size_t len);
 +
 +/**
 + * wpa_msg_register_cb - Register callback function for wpa_msg() messages
 + * @func: Callback function (%NULL to unregister)
 + */
 +void wpa_msg_register_cb(wpa_msg_cb_func func);
 +
 +typedef const char * (*wpa_msg_get_ifname_func)(void *ctx);
 +void wpa_msg_register_ifname_cb(wpa_msg_get_ifname_func func);
 +
 +#endif /* CONFIG_NO_WPA_MSG */
 +
 +#ifdef CONFIG_NO_HOSTAPD_LOGGER
 +#define hostapd_logger(args...) do { } while (0)
 +#define hostapd_logger_register_cb(f) do { } while (0)
 +#else /* CONFIG_NO_HOSTAPD_LOGGER */
 +void hostapd_logger(void *ctx, const u8 *addr, unsigned int module, int level,
 +                  const char *fmt, ...) PRINTF_FORMAT(5, 6);
 +
 +typedef void (*hostapd_logger_cb_func)(void *ctx, const u8 *addr,
 +                                     unsigned int module, int level,
 +                                     const char *txt, size_t len);
 +
 +/**
 + * hostapd_logger_register_cb - Register callback function for hostapd_logger()
 + * @func: Callback function (%NULL to unregister)
 + */
 +void hostapd_logger_register_cb(hostapd_logger_cb_func func);
 +#endif /* CONFIG_NO_HOSTAPD_LOGGER */
 +
 +#define HOSTAPD_MODULE_IEEE80211      0x00000001
 +#define HOSTAPD_MODULE_IEEE8021X      0x00000002
 +#define HOSTAPD_MODULE_RADIUS         0x00000004
 +#define HOSTAPD_MODULE_WPA            0x00000008
 +#define HOSTAPD_MODULE_DRIVER         0x00000010
 +#define HOSTAPD_MODULE_IAPP           0x00000020
 +#define HOSTAPD_MODULE_MLME           0x00000040
 +
 +enum hostapd_logger_level {
 +      HOSTAPD_LEVEL_DEBUG_VERBOSE = 0,
 +      HOSTAPD_LEVEL_DEBUG = 1,
 +      HOSTAPD_LEVEL_INFO = 2,
 +      HOSTAPD_LEVEL_NOTICE = 3,
 +      HOSTAPD_LEVEL_WARNING = 4
 +};
 +
 +
 +#ifdef CONFIG_DEBUG_SYSLOG
 +
 +void wpa_debug_open_syslog(void);
 +void wpa_debug_close_syslog(void);
 +
 +#else /* CONFIG_DEBUG_SYSLOG */
 +
 +static inline void wpa_debug_open_syslog(void)
 +{
 +}
 +
 +static inline void wpa_debug_close_syslog(void)
 +{
 +}
 +
 +#endif /* CONFIG_DEBUG_SYSLOG */
 +
 +#ifdef CONFIG_DEBUG_LINUX_TRACING
 +
 +int wpa_debug_open_linux_tracing(void);
 +void wpa_debug_close_linux_tracing(void);
 +
 +#else /* CONFIG_DEBUG_LINUX_TRACING */
 +
 +static inline int wpa_debug_open_linux_tracing(void)
 +{
 +      return 0;
 +}
 +
 +static inline void wpa_debug_close_linux_tracing(void)
 +{
 +}
 +
 +#endif /* CONFIG_DEBUG_LINUX_TRACING */
 +
 +
 +#ifdef EAPOL_TEST
 +#define WPA_ASSERT(a)                                                \
 +      do {                                                           \
 +              if (!(a)) {                                            \
 +                      printf("WPA_ASSERT FAILED '" #a "' "           \
 +                             "%s %s:%d\n",                           \
 +                             __FUNCTION__, __FILE__, __LINE__);      \
 +                      exit(1);                                       \
 +              }                                                      \
 +      } while (0)
 +#else
 +#define WPA_ASSERT(a) do { } while (0)
 +#endif
 +
++const char * debug_level_str(int level);
++int str_to_debug_level(const char *s);
++
 +#endif /* WPA_DEBUG_H */
index 7aafa0a5169ba775bd681850bffe21def1097518,0000000000000000000000000000000000000000..11e7323619de8b2131e51ce00afb08c864f92582
mode 100644,000000..100644
--- /dev/null
@@@ -1,312 -1,0 +1,312 @@@
- };
 +/*
 + * Dynamic data buffer
 + * Copyright (c) 2007-2012, Jouni Malinen <j@w1.fi>
 + *
 + * This software may be distributed under the terms of the BSD license.
 + * See README for more details.
 + */
 +
 +#include "includes.h"
 +
 +#include "common.h"
 +#include "trace.h"
 +#include "wpabuf.h"
 +
 +#ifdef WPA_TRACE
 +#define WPABUF_MAGIC 0x51a974e3
 +
 +struct wpabuf_trace {
 +      unsigned int magic;
++} __attribute__((aligned(8)));
 +
 +static struct wpabuf_trace * wpabuf_get_trace(const struct wpabuf *buf)
 +{
 +      return (struct wpabuf_trace *)
 +              ((const u8 *) buf - sizeof(struct wpabuf_trace));
 +}
 +#endif /* WPA_TRACE */
 +
 +
 +static void wpabuf_overflow(const struct wpabuf *buf, size_t len)
 +{
 +#ifdef WPA_TRACE
 +      struct wpabuf_trace *trace = wpabuf_get_trace(buf);
 +      if (trace->magic != WPABUF_MAGIC) {
 +              wpa_printf(MSG_ERROR, "wpabuf: invalid magic %x",
 +                         trace->magic);
 +      }
 +#endif /* WPA_TRACE */
 +      wpa_printf(MSG_ERROR, "wpabuf %p (size=%lu used=%lu) overflow len=%lu",
 +                 buf, (unsigned long) buf->size, (unsigned long) buf->used,
 +                 (unsigned long) len);
 +      wpa_trace_show("wpabuf overflow");
 +      abort();
 +}
 +
 +
 +int wpabuf_resize(struct wpabuf **_buf, size_t add_len)
 +{
 +      struct wpabuf *buf = *_buf;
 +#ifdef WPA_TRACE
 +      struct wpabuf_trace *trace;
 +#endif /* WPA_TRACE */
 +
 +      if (buf == NULL) {
 +              *_buf = wpabuf_alloc(add_len);
 +              return *_buf == NULL ? -1 : 0;
 +      }
 +
 +#ifdef WPA_TRACE
 +      trace = wpabuf_get_trace(buf);
 +      if (trace->magic != WPABUF_MAGIC) {
 +              wpa_printf(MSG_ERROR, "wpabuf: invalid magic %x",
 +                         trace->magic);
 +              wpa_trace_show("wpabuf_resize invalid magic");
 +              abort();
 +      }
 +#endif /* WPA_TRACE */
 +
 +      if (buf->used + add_len > buf->size) {
 +              unsigned char *nbuf;
 +              if (buf->flags & WPABUF_FLAG_EXT_DATA) {
 +                      nbuf = os_realloc(buf->buf, buf->used + add_len);
 +                      if (nbuf == NULL)
 +                              return -1;
 +                      os_memset(nbuf + buf->used, 0, add_len);
 +                      buf->buf = nbuf;
 +              } else {
 +#ifdef WPA_TRACE
 +                      nbuf = os_realloc(trace, sizeof(struct wpabuf_trace) +
 +                                        sizeof(struct wpabuf) +
 +                                        buf->used + add_len);
 +                      if (nbuf == NULL)
 +                              return -1;
 +                      trace = (struct wpabuf_trace *) nbuf;
 +                      buf = (struct wpabuf *) (trace + 1);
 +                      os_memset(nbuf + sizeof(struct wpabuf_trace) +
 +                                sizeof(struct wpabuf) + buf->used, 0,
 +                                add_len);
 +#else /* WPA_TRACE */
 +                      nbuf = os_realloc(buf, sizeof(struct wpabuf) +
 +                                        buf->used + add_len);
 +                      if (nbuf == NULL)
 +                              return -1;
 +                      buf = (struct wpabuf *) nbuf;
 +                      os_memset(nbuf + sizeof(struct wpabuf) + buf->used, 0,
 +                                add_len);
 +#endif /* WPA_TRACE */
 +                      buf->buf = (u8 *) (buf + 1);
 +                      *_buf = buf;
 +              }
 +              buf->size = buf->used + add_len;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +/**
 + * wpabuf_alloc - Allocate a wpabuf of the given size
 + * @len: Length for the allocated buffer
 + * Returns: Buffer to the allocated wpabuf or %NULL on failure
 + */
 +struct wpabuf * wpabuf_alloc(size_t len)
 +{
 +#ifdef WPA_TRACE
 +      struct wpabuf_trace *trace = os_zalloc(sizeof(struct wpabuf_trace) +
 +                                             sizeof(struct wpabuf) + len);
 +      struct wpabuf *buf;
 +      if (trace == NULL)
 +              return NULL;
 +      trace->magic = WPABUF_MAGIC;
 +      buf = (struct wpabuf *) (trace + 1);
 +#else /* WPA_TRACE */
 +      struct wpabuf *buf = os_zalloc(sizeof(struct wpabuf) + len);
 +      if (buf == NULL)
 +              return NULL;
 +#endif /* WPA_TRACE */
 +
 +      buf->size = len;
 +      buf->buf = (u8 *) (buf + 1);
 +      return buf;
 +}
 +
 +
 +struct wpabuf * wpabuf_alloc_ext_data(u8 *data, size_t len)
 +{
 +#ifdef WPA_TRACE
 +      struct wpabuf_trace *trace = os_zalloc(sizeof(struct wpabuf_trace) +
 +                                             sizeof(struct wpabuf));
 +      struct wpabuf *buf;
 +      if (trace == NULL)
 +              return NULL;
 +      trace->magic = WPABUF_MAGIC;
 +      buf = (struct wpabuf *) (trace + 1);
 +#else /* WPA_TRACE */
 +      struct wpabuf *buf = os_zalloc(sizeof(struct wpabuf));
 +      if (buf == NULL)
 +              return NULL;
 +#endif /* WPA_TRACE */
 +
 +      buf->size = len;
 +      buf->used = len;
 +      buf->buf = data;
 +      buf->flags |= WPABUF_FLAG_EXT_DATA;
 +
 +      return buf;
 +}
 +
 +
 +struct wpabuf * wpabuf_alloc_copy(const void *data, size_t len)
 +{
 +      struct wpabuf *buf = wpabuf_alloc(len);
 +      if (buf)
 +              wpabuf_put_data(buf, data, len);
 +      return buf;
 +}
 +
 +
 +struct wpabuf * wpabuf_dup(const struct wpabuf *src)
 +{
 +      struct wpabuf *buf = wpabuf_alloc(wpabuf_len(src));
 +      if (buf)
 +              wpabuf_put_data(buf, wpabuf_head(src), wpabuf_len(src));
 +      return buf;
 +}
 +
 +
 +/**
 + * wpabuf_free - Free a wpabuf
 + * @buf: wpabuf buffer
 + */
 +void wpabuf_free(struct wpabuf *buf)
 +{
 +#ifdef WPA_TRACE
 +      struct wpabuf_trace *trace;
 +      if (buf == NULL)
 +              return;
 +      trace = wpabuf_get_trace(buf);
 +      if (trace->magic != WPABUF_MAGIC) {
 +              wpa_printf(MSG_ERROR, "wpabuf_free: invalid magic %x",
 +                         trace->magic);
 +              wpa_trace_show("wpabuf_free magic mismatch");
 +              abort();
 +      }
 +      if (buf->flags & WPABUF_FLAG_EXT_DATA)
 +              os_free(buf->buf);
 +      os_free(trace);
 +#else /* WPA_TRACE */
 +      if (buf == NULL)
 +              return;
 +      if (buf->flags & WPABUF_FLAG_EXT_DATA)
 +              os_free(buf->buf);
 +      os_free(buf);
 +#endif /* WPA_TRACE */
 +}
 +
 +
 +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);
 +      buf->used += len;
 +      if (buf->used > buf->size) {
 +              wpabuf_overflow(buf, len);
 +      }
 +      return tmp;
 +}
 +
 +
 +/**
 + * wpabuf_concat - Concatenate two buffers into a newly allocated one
 + * @a: First buffer
 + * @b: Second buffer
 + * Returns: wpabuf with concatenated a + b data or %NULL on failure
 + *
 + * Both buffers a and b will be freed regardless of the return value. Input
 + * buffers can be %NULL which is interpreted as an empty buffer.
 + */
 +struct wpabuf * wpabuf_concat(struct wpabuf *a, struct wpabuf *b)
 +{
 +      struct wpabuf *n = NULL;
 +      size_t len = 0;
 +
 +      if (b == NULL)
 +              return a;
 +
 +      if (a)
 +              len += wpabuf_len(a);
 +      if (b)
 +              len += wpabuf_len(b);
 +
 +      n = wpabuf_alloc(len);
 +      if (n) {
 +              if (a)
 +                      wpabuf_put_buf(n, a);
 +              if (b)
 +                      wpabuf_put_buf(n, b);
 +      }
 +
 +      wpabuf_free(a);
 +      wpabuf_free(b);
 +
 +      return n;
 +}
 +
 +
 +/**
 + * wpabuf_zeropad - Pad buffer with 0x00 octets (prefix) to specified length
 + * @buf: Buffer to be padded
 + * @len: Length for the padded buffer
 + * Returns: wpabuf padded to len octets or %NULL on failure
 + *
 + * If buf is longer than len octets or of same size, it will be returned as-is.
 + * Otherwise a new buffer is allocated and prefixed with 0x00 octets followed
 + * by the source data. The source buffer will be freed on error, i.e., caller
 + * will only be responsible on freeing the returned buffer. If buf is %NULL,
 + * %NULL will be returned.
 + */
 +struct wpabuf * wpabuf_zeropad(struct wpabuf *buf, size_t len)
 +{
 +      struct wpabuf *ret;
 +      size_t blen;
 +
 +      if (buf == NULL)
 +              return NULL;
 +
 +      blen = wpabuf_len(buf);
 +      if (blen >= len)
 +              return buf;
 +
 +      ret = wpabuf_alloc(len);
 +      if (ret) {
 +              os_memset(wpabuf_put(ret, len - blen), 0, len - blen);
 +              wpabuf_put_buf(ret, buf);
 +      }
 +      wpabuf_free(buf);
 +
 +      return ret;
 +}
 +
 +
 +void wpabuf_printf(struct wpabuf *buf, char *fmt, ...)
 +{
 +      va_list ap;
 +      void *tmp = wpabuf_mhead_u8(buf) + wpabuf_len(buf);
 +      int res;
 +
 +      va_start(ap, fmt);
 +      res = vsnprintf(tmp, buf->size - buf->used, fmt, ap);
 +      va_end(ap);
 +      if (res < 0 || (size_t) res >= buf->size - buf->used)
 +              wpabuf_overflow(buf, res);
 +      buf->used += res;
 +}
index 029001306cbe40c2243c30a34d9f2bc3476ce9cd,0000000000000000000000000000000000000000..cdf3a5128ed39edb7e90ee1f5b523c9a4da40c64
mode 100644,000000..100644
--- /dev/null
@@@ -1,368 -1,0 +1,362 @@@
-                  (unsigned long) wpabuf_len(c->req) - c->req_pos);
 +/*
 + * http_client - HTTP client
 + * 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 <fcntl.h>
 +
 +#include "common.h"
 +#include "eloop.h"
 +#include "httpread.h"
 +#include "http_client.h"
 +
 +
 +#define HTTP_CLIENT_TIMEOUT_SEC 30
 +
 +
 +struct http_client {
 +      struct sockaddr_in dst;
 +      int sd;
 +      struct wpabuf *req;
 +      size_t req_pos;
 +      size_t max_response;
 +
 +      void (*cb)(void *ctx, struct http_client *c,
 +                 enum http_client_event event);
 +      void *cb_ctx;
 +      struct httpread *hread;
 +      struct wpabuf body;
 +};
 +
 +
 +static void http_client_timeout(void *eloop_data, void *user_ctx)
 +{
 +      struct http_client *c = eloop_data;
 +      wpa_printf(MSG_DEBUG, "HTTP: Timeout (c=%p)", c);
 +      c->cb(c->cb_ctx, c, HTTP_CLIENT_TIMEOUT);
 +}
 +
 +
 +static void http_client_got_response(struct httpread *handle, void *cookie,
 +                                   enum httpread_event e)
 +{
 +      struct http_client *c = cookie;
 +
 +      wpa_printf(MSG_DEBUG, "HTTP: httpread callback: handle=%p cookie=%p "
 +                 "e=%d", handle, cookie, e);
 +
 +      eloop_cancel_timeout(http_client_timeout, c, NULL);
 +      switch (e) {
 +      case HTTPREAD_EVENT_FILE_READY:
 +              if (httpread_hdr_type_get(c->hread) == HTTPREAD_HDR_TYPE_REPLY)
 +              {
 +                      int reply_code = httpread_reply_code_get(c->hread);
 +                      if (reply_code == 200 /* OK */) {
 +                              wpa_printf(MSG_DEBUG, "HTTP: Response OK from "
 +                                         "%s:%d",
 +                                         inet_ntoa(c->dst.sin_addr),
 +                                         ntohs(c->dst.sin_port));
 +                              c->cb(c->cb_ctx, c, HTTP_CLIENT_OK);
 +                      } else {
 +                              wpa_printf(MSG_DEBUG, "HTTP: Error %d from "
 +                                         "%s:%d", reply_code,
 +                                         inet_ntoa(c->dst.sin_addr),
 +                                         ntohs(c->dst.sin_port));
 +                              c->cb(c->cb_ctx, c, HTTP_CLIENT_INVALID_REPLY);
 +                      }
 +              } else
 +                      c->cb(c->cb_ctx, c, HTTP_CLIENT_INVALID_REPLY);
 +              break;
 +      case HTTPREAD_EVENT_TIMEOUT:
 +              c->cb(c->cb_ctx, c, HTTP_CLIENT_TIMEOUT);
 +              break;
 +      case HTTPREAD_EVENT_ERROR:
 +              c->cb(c->cb_ctx, c, HTTP_CLIENT_FAILED);
 +              break;
 +      }
 +}
 +
 +
 +static void http_client_tx_ready(int sock, void *eloop_ctx, void *sock_ctx)
 +{
 +      struct http_client *c = eloop_ctx;
 +      int res;
++      size_t send_len;
 +
++      send_len = wpabuf_len(c->req) - c->req_pos;
 +      wpa_printf(MSG_DEBUG, "HTTP: Send client request to %s:%d (%lu of %lu "
 +                 "bytes remaining)",
 +                 inet_ntoa(c->dst.sin_addr), ntohs(c->dst.sin_port),
 +                 (unsigned long) wpabuf_len(c->req),
-       res = send(c->sd, wpabuf_head_u8(c->req) + c->req_pos,
-                  wpabuf_len(c->req) - c->req_pos, 0);
++                 (unsigned long) send_len);
 +
-       if ((size_t) res < wpabuf_len(c->req) - c->req_pos) {
++      res = send(c->sd, wpabuf_head_u8(c->req) + c->req_pos, send_len, 0);
 +      if (res < 0) {
 +              wpa_printf(MSG_DEBUG, "HTTP: Failed to send buffer: %s",
 +                         strerror(errno));
 +              eloop_unregister_sock(c->sd, EVENT_TYPE_WRITE);
 +              c->cb(c->cb_ctx, c, HTTP_CLIENT_FAILED);
 +              return;
 +      }
 +
-                          (unsigned long) wpabuf_len(c->req) - c->req_pos -
-                          res);
++      if ((size_t) res < send_len) {
 +              wpa_printf(MSG_DEBUG, "HTTP: Sent %d of %lu bytes; %lu bytes "
 +                         "remaining",
 +                         res, (unsigned long) wpabuf_len(c->req),
-       if (c->sd < 0) {
-               http_client_free(c);
-               return NULL;
-       }
++                         (unsigned long) send_len - res);
 +              c->req_pos += res;
 +              return;
 +      }
 +
 +      wpa_printf(MSG_DEBUG, "HTTP: Full client request sent to %s:%d",
 +                 inet_ntoa(c->dst.sin_addr), ntohs(c->dst.sin_port));
 +      eloop_unregister_sock(c->sd, EVENT_TYPE_WRITE);
 +      wpabuf_free(c->req);
 +      c->req = NULL;
 +
 +      c->hread = httpread_create(c->sd, http_client_got_response, c,
 +                                 c->max_response, HTTP_CLIENT_TIMEOUT_SEC);
 +      if (c->hread == NULL) {
 +              c->cb(c->cb_ctx, c, HTTP_CLIENT_FAILED);
 +              return;
 +      }
 +}
 +
 +
 +struct http_client * http_client_addr(struct sockaddr_in *dst,
 +                                    struct wpabuf *req, size_t max_response,
 +                                    void (*cb)(void *ctx,
 +                                               struct http_client *c,
 +                                               enum http_client_event event),
 +                                    void *cb_ctx)
 +{
 +      struct http_client *c;
 +
 +      c = os_zalloc(sizeof(*c));
 +      if (c == NULL)
 +              return NULL;
 +      c->sd = -1;
 +      c->dst = *dst;
 +      c->max_response = max_response;
 +      c->cb = cb;
 +      c->cb_ctx = cb_ctx;
 +
 +      c->sd = socket(AF_INET, SOCK_STREAM, 0);
-               http_client_free(c);
-               return NULL;
++      if (c->sd < 0)
++              goto fail;
 +
 +      if (fcntl(c->sd, F_SETFL, O_NONBLOCK) != 0) {
 +              wpa_printf(MSG_DEBUG, "HTTP: fnctl(O_NONBLOCK) failed: %s",
 +                         strerror(errno));
-                       http_client_free(c);
-                       return NULL;
++              goto fail;
 +      }
 +
 +      if (connect(c->sd, (struct sockaddr *) dst, sizeof(*dst))) {
 +              if (errno != EINPROGRESS) {
 +                      wpa_printf(MSG_DEBUG, "HTTP: Failed to connect: %s",
 +                                 strerror(errno));
-                               c, NULL)) {
-               http_client_free(c);
-               return NULL;
-       }
-       if (eloop_register_timeout(HTTP_CLIENT_TIMEOUT_SEC, 0,
-                                  http_client_timeout, c, NULL)) {
-               http_client_free(c);
-               return NULL;
-       }
++                      goto fail;
 +              }
 +
 +              /*
 +               * Continue connecting in the background; eloop will call us
 +               * once the connection is ready (or failed).
 +               */
 +      }
 +
 +      if (eloop_register_sock(c->sd, EVENT_TYPE_WRITE, http_client_tx_ready,
++                              c, NULL) ||
++          eloop_register_timeout(HTTP_CLIENT_TIMEOUT_SEC, 0,
++                                 http_client_timeout, c, NULL))
++              goto fail;
 +
 +      c->req = req;
 +
 +      return c;
++
++fail:
++      http_client_free(c);
++      return NULL;
 +}
 +
 +
 +char * http_client_url_parse(const char *url, struct sockaddr_in *dst,
 +                           char **ret_path)
 +{
 +      char *u, *addr, *port, *path;
 +
 +      u = os_strdup(url);
 +      if (u == NULL)
 +              return NULL;
 +
 +      os_memset(dst, 0, sizeof(*dst));
 +      dst->sin_family = AF_INET;
 +      addr = u + 7;
 +      path = os_strchr(addr, '/');
 +      port = os_strchr(addr, ':');
 +      if (path == NULL) {
 +              path = "/";
 +      } else {
 +              *path = '\0'; /* temporary nul termination for address */
 +              if (port > path)
 +                      port = NULL;
 +      }
 +      if (port)
 +              *port++ = '\0';
 +
 +      if (inet_aton(addr, &dst->sin_addr) == 0) {
 +              /* TODO: name lookup */
 +              wpa_printf(MSG_DEBUG, "HTTP: Unsupported address in URL '%s' "
 +                         "(addr='%s' port='%s')",
 +                         url, addr, port);
 +              os_free(u);
 +              return NULL;
 +      }
 +
 +      if (port)
 +              dst->sin_port = htons(atoi(port));
 +      else
 +              dst->sin_port = htons(80);
 +
 +      if (*path == '\0') {
 +              /* remove temporary nul termination for address */
 +              *path = '/';
 +      }
 +
 +      *ret_path = path;
 +
 +      return u;
 +}
 +
 +
 +struct http_client * http_client_url(const char *url,
 +                                   struct wpabuf *req, size_t max_response,
 +                                   void (*cb)(void *ctx,
 +                                              struct http_client *c,
 +                                              enum http_client_event event),
 +                                   void *cb_ctx)
 +{
 +      struct sockaddr_in dst;
 +      struct http_client *c;
 +      char *u, *path;
 +      struct wpabuf *req_buf = NULL;
 +
 +      if (os_strncmp(url, "http://", 7) != 0)
 +              return NULL;
 +      u = http_client_url_parse(url, &dst, &path);
 +      if (u == NULL)
 +              return NULL;
 +
 +      if (req == NULL) {
 +              req_buf = wpabuf_alloc(os_strlen(url) + 1000);
 +              if (req_buf == NULL) {
 +                      os_free(u);
 +                      return NULL;
 +              }
 +              req = req_buf;
 +              wpabuf_printf(req,
 +                            "GET %s HTTP/1.1\r\n"
 +                            "Cache-Control: no-cache\r\n"
 +                            "Pragma: no-cache\r\n"
 +                            "Accept: text/xml, application/xml\r\n"
 +                            "User-Agent: wpa_supplicant\r\n"
 +                            "Host: %s:%d\r\n"
 +                            "\r\n",
 +                            path, inet_ntoa(dst.sin_addr),
 +                            ntohs(dst.sin_port));
 +      }
 +      os_free(u);
 +
 +      c = http_client_addr(&dst, req, max_response, cb, cb_ctx);
 +      if (c == NULL) {
 +              wpabuf_free(req_buf);
 +              return NULL;
 +      }
 +
 +      return c;
 +}
 +
 +
 +void http_client_free(struct http_client *c)
 +{
 +      if (c == NULL)
 +              return;
 +      httpread_destroy(c->hread);
 +      wpabuf_free(c->req);
 +      if (c->sd >= 0) {
 +              eloop_unregister_sock(c->sd, EVENT_TYPE_WRITE);
 +              close(c->sd);
 +      }
 +      eloop_cancel_timeout(http_client_timeout, c, NULL);
 +      os_free(c);
 +}
 +
 +
 +struct wpabuf * http_client_get_body(struct http_client *c)
 +{
 +      if (c->hread == NULL)
 +              return NULL;
 +      wpabuf_set(&c->body, httpread_data_get(c->hread),
 +                 httpread_length_get(c->hread));
 +      return &c->body;
 +}
 +
 +
 +char * http_client_get_hdr_line(struct http_client *c, const char *tag)
 +{
 +      if (c->hread == NULL)
 +              return NULL;
 +      return httpread_hdr_line_get(c->hread, tag);
 +}
 +
 +
 +char * http_link_update(char *url, const char *base)
 +{
 +      char *n;
 +      size_t len;
 +      const char *pos;
 +
 +      /* RFC 2396, Chapter 5.2 */
 +      /* TODO: consider adding all cases described in RFC 2396 */
 +
 +      if (url == NULL)
 +              return NULL;
 +
 +      if (os_strncmp(url, "http://", 7) == 0)
 +              return url; /* absolute link */
 +
 +      if (os_strncmp(base, "http://", 7) != 0)
 +              return url; /* unable to handle base URL */
 +
 +      len = os_strlen(url) + 1 + os_strlen(base) + 1;
 +      n = os_malloc(len);
 +      if (n == NULL)
 +              return url; /* failed */
 +
 +      if (url[0] == '/') {
 +              pos = os_strchr(base + 7, '/');
 +              if (pos == NULL) {
 +                      os_snprintf(n, len, "%s%s", base, url);
 +              } else {
 +                      os_memcpy(n, base, pos - base);
 +                      os_memcpy(n + (pos - base), url, os_strlen(url) + 1);
 +              }
 +      } else {
 +              pos = os_strrchr(base + 7, '/');
 +              if (pos == NULL) {
 +                      os_snprintf(n, len, "%s/%s", base, url);
 +              } else {
 +                      os_memcpy(n, base, pos - base + 1);
 +                      os_memcpy(n + (pos - base) + 1, url, os_strlen(url) +
 +                                1);
 +              }
 +      }
 +
 +      os_free(url);
 +
 +      return n;
 +}
index ac088c429d606a0f8452ccc909969b49ef9d7add,0000000000000000000000000000000000000000..507abe87078086981d3f7c9f0d773f658510ffe6
mode 100644,000000..100644
--- /dev/null
@@@ -1,316 -1,0 +1,314 @@@
-       if (listen(srv->fd, 10 /* max backlog */) < 0)
-               goto fail;
-       if (fcntl(srv->fd, F_SETFL, O_NONBLOCK) < 0)
-               goto fail;
-       if (eloop_register_sock(srv->fd, EVENT_TYPE_READ, http_server_cb,
 +/*
 + * http_server - HTTP server
 + * 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 <fcntl.h>
 +
 +#include "common.h"
 +#include "eloop.h"
 +#include "httpread.h"
 +#include "http_server.h"
 +
 +#define HTTP_SERVER_TIMEOUT 30
 +#define HTTP_SERVER_MAX_REQ_LEN 8000
 +#define HTTP_SERVER_MAX_CONNECTIONS 10
 +
 +struct http_request {
 +      struct http_request *next;
 +      struct http_server *srv;
 +      int fd;
 +      struct sockaddr_in cli;
 +      struct httpread *hread;
 +};
 +
 +struct http_server {
 +      void (*cb)(void *ctx, struct http_request *req);
 +      void *cb_ctx;
 +
 +      int fd;
 +      int port;
 +
 +      struct http_request *requests;
 +      unsigned int request_count;
 +};
 +
 +
 +static void http_request_cb(struct httpread *handle, void *cookie,
 +                          enum httpread_event en)
 +{
 +      struct http_request *req = cookie;
 +      struct http_server *srv = req->srv;
 +
 +      if (en == HTTPREAD_EVENT_FILE_READY) {
 +              wpa_printf(MSG_DEBUG, "HTTP: Request from %s:%d received",
 +                         inet_ntoa(req->cli.sin_addr),
 +                         ntohs(req->cli.sin_port));
 +              srv->cb(srv->cb_ctx, req);
 +              return;
 +      }
 +      wpa_printf(MSG_DEBUG, "HTTP: Request from %s:%d could not be received "
 +                 "completely", inet_ntoa(req->cli.sin_addr),
 +                 ntohs(req->cli.sin_port));
 +      http_request_deinit(req);
 +}
 +
 +
 +static struct http_request * http_request_init(struct http_server *srv, int fd,
 +                                             struct sockaddr_in *cli)
 +{
 +      struct http_request *req;
 +
 +      if (srv->request_count >= HTTP_SERVER_MAX_CONNECTIONS) {
 +              wpa_printf(MSG_DEBUG, "HTTP: Too many concurrent requests");
 +              return NULL;
 +      }
 +
 +      req = os_zalloc(sizeof(*req));
 +      if (req == NULL)
 +              return NULL;
 +
 +      req->srv = srv;
 +      req->fd = fd;
 +      req->cli = *cli;
 +
 +      req->hread = httpread_create(req->fd, http_request_cb, req,
 +                                   HTTP_SERVER_MAX_REQ_LEN,
 +                                   HTTP_SERVER_TIMEOUT);
 +      if (req->hread == NULL) {
 +              http_request_deinit(req);
 +              return NULL;
 +      }
 +
 +      return req;
 +}
 +
 +
 +void http_request_deinit(struct http_request *req)
 +{
 +      struct http_request *r, *p;
 +      struct http_server *srv;
 +
 +      if (req == NULL)
 +              return;
 +
 +      srv = req->srv;
 +      p = NULL;
 +      r = srv->requests;
 +      while (r) {
 +              if (r == req) {
 +                      if (p)
 +                              p->next = r->next;
 +                      else
 +                              srv->requests = r->next;
 +                      srv->request_count--;
 +                      break;
 +              }
 +              p = r;
 +              r = r->next;
 +      }
 +
 +      httpread_destroy(req->hread);
 +      close(req->fd);
 +      os_free(req);
 +}
 +
 +
 +static void http_request_free_all(struct http_request *req)
 +{
 +      struct http_request *prev;
 +      while (req) {
 +              prev = req;
 +              req = req->next;
 +              http_request_deinit(prev);
 +      }
 +}
 +
 +
 +void http_request_send(struct http_request *req, struct wpabuf *resp)
 +{
 +      int res;
 +
 +      wpa_printf(MSG_DEBUG, "HTTP: Send %lu byte response to %s:%d",
 +                 (unsigned long) wpabuf_len(resp),
 +                 inet_ntoa(req->cli.sin_addr),
 +                 ntohs(req->cli.sin_port));
 +
 +      res = send(req->fd, wpabuf_head(resp), wpabuf_len(resp), 0);
 +      if (res < 0) {
 +              wpa_printf(MSG_DEBUG, "HTTP: Send failed: %s",
 +                         strerror(errno));
 +      } else if ((size_t) res < wpabuf_len(resp)) {
 +              wpa_printf(MSG_DEBUG, "HTTP: Sent only %d of %lu bytes",
 +                         res, (unsigned long) wpabuf_len(resp));
 +              /* TODO: add eloop handler for sending rest of the data */
 +      }
 +
 +      wpabuf_free(resp);
 +}
 +
 +
 +void http_request_send_and_deinit(struct http_request *req,
 +                                struct wpabuf *resp)
 +{
 +      http_request_send(req, resp);
 +      http_request_deinit(req);
 +}
 +
 +
 +enum httpread_hdr_type http_request_get_type(struct http_request *req)
 +{
 +      return httpread_hdr_type_get(req->hread);
 +}
 +
 +
 +char * http_request_get_uri(struct http_request *req)
 +{
 +      return httpread_uri_get(req->hread);
 +}
 +
 +
 +char * http_request_get_hdr(struct http_request *req)
 +{
 +      return httpread_hdr_get(req->hread);
 +}
 +
 +
 +char * http_request_get_data(struct http_request *req)
 +{
 +      return httpread_data_get(req->hread);
 +}
 +
 +
 +char * http_request_get_hdr_line(struct http_request *req, const char *tag)
 +{
 +      return httpread_hdr_line_get(req->hread, tag);
 +}
 +
 +
 +struct sockaddr_in * http_request_get_cli_addr(struct http_request *req)
 +{
 +      return &req->cli;
 +}
 +
 +
 +static void http_server_cb(int sd, void *eloop_ctx, void *sock_ctx)
 +{
 +      struct sockaddr_in addr;
 +      socklen_t addr_len = sizeof(addr);
 +      struct http_server *srv = eloop_ctx;
 +      int conn;
 +      struct http_request *req;
 +
 +      conn = accept(srv->fd, (struct sockaddr *) &addr, &addr_len);
 +      if (conn < 0) {
 +              wpa_printf(MSG_DEBUG, "HTTP: Failed to accept new connection: "
 +                         "%s", strerror(errno));
 +              return;
 +      }
 +      wpa_printf(MSG_DEBUG, "HTTP: Connection from %s:%d",
 +                 inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
 +
 +      req = http_request_init(srv, conn, &addr);
 +      if (req == NULL) {
 +              close(conn);
 +              return;
 +      }
 +
 +      req->next = srv->requests;
 +      srv->requests = req;
 +      srv->request_count++;
 +}
 +
 +
 +struct http_server * http_server_init(struct in_addr *addr, int port,
 +                                    void (*cb)(void *ctx,
 +                                               struct http_request *req),
 +                                    void *cb_ctx)
 +{
 +      struct sockaddr_in sin;
 +      struct http_server *srv;
 +      int on = 1;
 +
 +      srv = os_zalloc(sizeof(*srv));
 +      if (srv == NULL)
 +              return NULL;
 +      srv->cb = cb;
 +      srv->cb_ctx = cb_ctx;
 +
 +      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)
 +              srv->port = 49152;
 +      else
 +              srv->port = port;
 +
 +      os_memset(&sin, 0, sizeof(sin));
 +      sin.sin_family = AF_INET;
 +      sin.sin_addr.s_addr = addr->s_addr;
 +
 +      for (;;) {
 +              sin.sin_port = htons(srv->port);
 +              if (bind(srv->fd, (struct sockaddr *) &sin, sizeof(sin)) == 0)
 +                      break;
 +              if (errno == EADDRINUSE) {
 +                      /* search for unused port */
 +                      if (++srv->port == 65535 || port >= 0)
 +                              goto fail;
 +                      continue;
 +              }
 +              wpa_printf(MSG_DEBUG, "HTTP: Failed to bind server port %d: "
 +                         "%s", srv->port, strerror(errno));
 +              goto fail;
 +      }
++      if (listen(srv->fd, 10 /* max backlog */) < 0 ||
++          fcntl(srv->fd, F_SETFL, O_NONBLOCK) < 0 ||
++          eloop_register_sock(srv->fd, EVENT_TYPE_READ, http_server_cb,
 +                              srv, NULL))
 +              goto fail;
 +
 +      wpa_printf(MSG_DEBUG, "HTTP: Started server on %s:%d",
 +                 inet_ntoa(*addr), srv->port);
 +
 +      return srv;
 +
 +fail:
 +      http_server_deinit(srv);
 +      return NULL;
 +}
 +
 +
 +void http_server_deinit(struct http_server *srv)
 +{
 +      if (srv == NULL)
 +              return;
 +      if (srv->fd >= 0) {
 +              eloop_unregister_sock(srv->fd, EVENT_TYPE_READ);
 +              close(srv->fd);
 +      }
 +      http_request_free_all(srv->requests);
 +
 +      os_free(srv);
 +}
 +
 +
 +int http_server_get_port(struct http_server *srv)
 +{
 +      return srv->port;
 +}
index 2f08f37275c011bb06fb8303f150e2c6d7a56e19,0000000000000000000000000000000000000000..7a2ba50a9f31427841282f661420f12f4fc21ae0
mode 100644,000000..100644
--- /dev/null
@@@ -1,825 -1,0 +1,848 @@@
- #if 0
- /* httpread_debug -- set this global variable > 0 e.g. from debugger
-  * to enable debugs (larger numbers for more debugs)
-  * Make this a #define of 0 to eliminate the debugging code.
-  */
- int httpread_debug = 99;
- #else
- #define httpread_debug 0        /* eliminates even the debugging code */
- #endif
 +/*
 + * httpread - Manage reading file(s) from HTTP/TCP socket
 + * Author: Ted Merrill
 + * Copyright 2008 Atheros Communications
 + *
 + * This software may be distributed under the terms of the BSD license.
 + * See README for more details.
 + *
 + * The files are buffered via internal callbacks from eloop, then presented to
 + * an application callback routine when completely read into memory. May also
 + * be used if no file is expected but just to get the header, including HTTP
 + * replies (e.g. HTTP/1.1 200 OK etc.).
 + *
 + * This does not attempt to be an optimally efficient implementation, but does
 + * attempt to be of reasonably small size and memory consumption; assuming that
 + * only small files are to be read. A maximum file size is provided by
 + * application and enforced.
 + *
 + * It is assumed that the application does not expect any of the following:
 + * -- transfer encoding other than chunked
 + * -- trailer fields
 + * It is assumed that, even if the other side requested that the connection be
 + * kept open, that we will close it (thus HTTP messages sent by application
 + * should have the connection closed field); this is allowed by HTTP/1.1 and
 + * simplifies things for us.
 + *
 + * Other limitations:
 + * -- HTTP header may not exceed a hard-coded size.
 + *
 + * Notes:
 + * This code would be massively simpler without some of the new features of
 + * HTTP/1.1, especially chunked data.
 + */
 +
 +#include "includes.h"
 +
 +#include "common.h"
 +#include "eloop.h"
 +#include "httpread.h"
 +
 +
 +/* Tunable parameters */
 +#define HTTPREAD_READBUF_SIZE 1024      /* read in chunks of this size */
 +#define HTTPREAD_HEADER_MAX_SIZE 4096   /* max allowed for headers */
 +#define HTTPREAD_BODYBUF_DELTA 4096     /* increase allocation by this */
 +
-       if (httpread_debug >= 10)
-               wpa_printf(MSG_DEBUG, "ENTER httpread_destroy(%p)", h);
 +
 +/* control instance -- actual definition (opaque to application)
 + */
 +struct httpread {
 +      /* information from creation */
 +      int sd;         /* descriptor of TCP socket to read from */
 +      void (*cb)(struct httpread *handle, void *cookie,
 +                  enum httpread_event e);  /* call on event */
 +      void *cookie;   /* pass to callback */
 +      int max_bytes;          /* maximum file size else abort it */
 +      int timeout_seconds;            /* 0 or total duration timeout period */
 +
 +      /* dynamically used information follows */
 +
 +      int got_hdr;            /* nonzero when header is finalized */
 +      char hdr[HTTPREAD_HEADER_MAX_SIZE+1];   /* headers stored here */
 +      int hdr_nbytes;
 +
 +      enum httpread_hdr_type hdr_type;
 +      int version;            /* 1 if we've seen 1.1 */
 +      int reply_code;         /* for type REPLY, e.g. 200 for HTTP/1.1 200 OK */
 +      int got_content_length; /* true if we know content length for sure */
 +      int content_length;     /* body length,  iff got_content_length */
 +      int chunked;            /* nonzero for chunked data */
 +      char *uri;
 +
 +      int got_body;           /* nonzero when body is finalized */
 +      char *body;
 +      int body_nbytes;
 +      int body_alloc_nbytes;  /* amount allocated */
 +
 +      int got_file;           /* here when we are done */
 +
 +      /* The following apply if data is chunked: */
 +      int in_chunk_data;      /* 0=in/at header, 1=in the data or tail*/
 +      int chunk_start;        /* offset in body of chunk hdr or data */
 +      int chunk_size;         /* data of chunk (not hdr or ending CRLF)*/
 +      int in_trailer;         /* in header fields after data (chunked only)*/
 +      enum trailer_state {
 +              trailer_line_begin = 0,
 +              trailer_empty_cr,       /* empty line + CR */
 +              trailer_nonempty,
 +              trailer_nonempty_cr,
 +      } trailer_state;
 +};
 +
 +
 +/* Check words for equality, where words consist of graphical characters
 + * delimited by whitespace
 + * Returns nonzero if "equal" doing case insensitive comparison.
 + */
 +static int word_eq(char *s1, char *s2)
 +{
 +      int c1;
 +      int c2;
 +      int end1 = 0;
 +      int end2 = 0;
 +      for (;;) {
 +              c1 = *s1++;
 +              c2 = *s2++;
 +              if (isalpha(c1) && isupper(c1))
 +                      c1 = tolower(c1);
 +              if (isalpha(c2) && isupper(c2))
 +                      c2 = tolower(c2);
 +              end1 = !isgraph(c1);
 +              end2 = !isgraph(c2);
 +              if (end1 || end2 || c1 != c2)
 +                      break;
 +      }
 +      return end1 && end2;  /* reached end of both words? */
 +}
 +
 +
 +static void httpread_timeout_handler(void *eloop_data, void *user_ctx);
 +
 +/* httpread_destroy -- if h is non-NULL, clean up
 + * This must eventually be called by the application following
 + * call of the application's callback and may be called
 + * earlier if desired.
 + */
 +void httpread_destroy(struct httpread *h)
 +{
-               while (isgraph(*hbp))
-                       hbp++;
++      wpa_printf(MSG_DEBUG, "httpread_destroy(%p)", h);
 +      if (!h)
 +              return;
 +
 +      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 */
 +      h->sd = -1;     /* aid debugging */
 +      os_free(h);
 +}
 +
 +
 +/* httpread_timeout_handler -- called on excessive total duration
 + */
 +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->cb)(h, h->cookie, HTTPREAD_EVENT_TIMEOUT);
 +}
 +
 +
 +/* Analyze options only so far as is needed to correctly obtain the file.
 + * The application can look at the raw header to find other options.
 + */
 +static int httpread_hdr_option_analyze(
 +      struct httpread *h,
 +      char *hbp       /* pointer to current line in header buffer */
 +      )
 +{
 +      if (word_eq(hbp, "CONTENT-LENGTH:")) {
 +              while (isgraph(*hbp))
 +                      hbp++;
 +              while (*hbp == ' ' || *hbp == '\t')
 +                      hbp++;
 +              if (!isdigit(*hbp))
 +                      return -1;
 +              h->content_length = atol(hbp);
++              if (h->content_length < 0 || h->content_length > h->max_bytes) {
++                      wpa_printf(MSG_DEBUG,
++                                 "httpread: Unacceptable Content-Length %d",
++                                 h->content_length);
++                      return -1;
++              }
 +              h->got_content_length = 1;
 +              return 0;
 +      }
 +      if (word_eq(hbp, "TRANSFER_ENCODING:") ||
 +          word_eq(hbp, "TRANSFER-ENCODING:")) {
 +              while (isgraph(*hbp))
 +                      hbp++;
 +              while (*hbp == ' ' || *hbp == '\t')
 +                      hbp++;
 +              /* There should (?) be no encodings of interest
 +               * other than chunked...
 +               */
 +              if (word_eq(hbp, "CHUNKED")) {
 +                      h->chunked = 1;
 +                      h->in_chunk_data = 0;
 +                      /* ignore possible ;<parameters> */
 +              }
 +              return 0;
 +      }
 +      /* skip anything we don't know, which is a lot */
 +      return 0;
 +}
 +
 +
 +static int httpread_hdr_analyze(struct httpread *h)
 +{
 +      char *hbp = h->hdr;      /* pointer into h->hdr */
 +      int standard_first_line = 1;
 +
 +      /* First line is special */
 +      h->hdr_type = HTTPREAD_HDR_TYPE_UNKNOWN;
 +      if (!isgraph(*hbp))
 +              goto bad;
 +      if (os_strncmp(hbp, "HTTP/", 5) == 0) {
 +              h->hdr_type = HTTPREAD_HDR_TYPE_REPLY;
 +              standard_first_line = 0;
 +              hbp += 5;
 +              if (hbp[0] == '1' && hbp[1] == '.' &&
 +                  isdigit(hbp[2]) && hbp[2] != '0')
 +                      h->version = 1;
 +              while (isgraph(*hbp))
 +                      hbp++;
 +              while (*hbp == ' ' || *hbp == '\t')
 +                      hbp++;
 +              if (!isdigit(*hbp))
 +                      goto bad;
 +              h->reply_code = atol(hbp);
 +      } else if (word_eq(hbp, "GET"))
 +              h->hdr_type = HTTPREAD_HDR_TYPE_GET;
 +      else if (word_eq(hbp, "HEAD"))
 +              h->hdr_type = HTTPREAD_HDR_TYPE_HEAD;
 +      else if (word_eq(hbp, "POST"))
 +              h->hdr_type = HTTPREAD_HDR_TYPE_POST;
 +      else if (word_eq(hbp, "PUT"))
 +              h->hdr_type = HTTPREAD_HDR_TYPE_PUT;
 +      else if (word_eq(hbp, "DELETE"))
 +              h->hdr_type = HTTPREAD_HDR_TYPE_DELETE;
 +      else if (word_eq(hbp, "TRACE"))
 +              h->hdr_type = HTTPREAD_HDR_TYPE_TRACE;
 +      else if (word_eq(hbp, "CONNECT"))
 +              h->hdr_type = HTTPREAD_HDR_TYPE_CONNECT;
 +      else if (word_eq(hbp, "NOTIFY"))
 +              h->hdr_type = HTTPREAD_HDR_TYPE_NOTIFY;
 +      else if (word_eq(hbp, "M-SEARCH"))
 +              h->hdr_type = HTTPREAD_HDR_TYPE_M_SEARCH;
 +      else if (word_eq(hbp, "M-POST"))
 +              h->hdr_type = HTTPREAD_HDR_TYPE_M_POST;
 +      else if (word_eq(hbp, "SUBSCRIBE"))
 +              h->hdr_type = HTTPREAD_HDR_TYPE_SUBSCRIBE;
 +      else if (word_eq(hbp, "UNSUBSCRIBE"))
 +              h->hdr_type = HTTPREAD_HDR_TYPE_UNSUBSCRIBE;
 +      else {
 +      }
 +
 +      if (standard_first_line) {
 +              char *rawuri;
 +              char *uri;
 +              /* skip type */
 +              while (isgraph(*hbp))
 +                      hbp++;
 +              while (*hbp == ' ' || *hbp == '\t')
 +                      hbp++;
 +              /* parse uri.
 +               * Find length, allocate memory for translated
 +               * copy, then translate by changing %<hex><hex>
 +               * into represented value.
 +               */
 +              rawuri = hbp;
 +              while (isgraph(*hbp))
 +                      hbp++;
 +              h->uri = os_malloc((hbp - rawuri) + 1);
 +              if (h->uri == NULL)
 +                      goto bad;
 +              uri = h->uri;
 +              while (rawuri < hbp) {
 +                      int c = *rawuri;
 +                      if (c == '%' &&
 +                          isxdigit(rawuri[1]) && isxdigit(rawuri[2])) {
 +                              *uri++ = hex2byte(rawuri + 1);
 +                              rawuri += 3;
 +                      } else {
 +                              *uri++ = c;
 +                              rawuri++;
 +                      }
 +              }
 +              *uri = 0;       /* null terminate */
-       if (httpread_debug >= 20)
-               wpa_printf(MSG_DEBUG, "ENTER httpread_read_handler(%p)", h);
 +              while (*hbp == ' ' || *hbp == '\t')
 +                      hbp++;
 +              /* get version */
 +              if (0 == strncmp(hbp, "HTTP/", 5)) {
 +                      hbp += 5;
 +                      if (hbp[0] == '1' && hbp[1] == '.' &&
 +                          isdigit(hbp[2]) && hbp[2] != '0')
 +                              h->version = 1;
 +              }
 +      }
 +      /* skip rest of line */
 +      while (*hbp)
 +              if (*hbp++ == '\n')
 +                      break;
 +
 +      /* Remainder of lines are options, in any order;
 +       * or empty line to terminate
 +       */
 +      for (;;) {
 +              /* Empty line to terminate */
 +              if (hbp[0] == '\n' ||
 +                  (hbp[0] == '\r' && hbp[1] == '\n'))
 +                      break;
 +              if (!isgraph(*hbp))
 +                      goto bad;
 +              if (httpread_hdr_option_analyze(h, hbp))
 +                      goto bad;
 +              /* skip line */
 +              while (*hbp)
 +                      if (*hbp++ == '\n')
 +                              break;
 +      }
 +
 +      /* chunked overrides content-length always */
 +      if (h->chunked)
 +              h->got_content_length = 0;
 +
 +      /* For some types, we should not try to read a body
 +       * This is in addition to the application determining
 +       * that we should not read a body.
 +       */
 +      switch (h->hdr_type) {
 +      case HTTPREAD_HDR_TYPE_REPLY:
 +              /* Some codes can have a body and some not.
 +               * For now, just assume that any other than 200
 +               * do not...
 +               */
 +              if (h->reply_code != 200)
 +                      h->max_bytes = 0;
 +              break;
 +      case HTTPREAD_HDR_TYPE_GET:
 +      case HTTPREAD_HDR_TYPE_HEAD:
 +              /* in practice it appears that it is assumed
 +               * that GETs have a body length of 0... ?
 +               */
 +              if (h->chunked == 0 && h->got_content_length == 0)
 +                      h->max_bytes = 0;
 +              break;
 +      case HTTPREAD_HDR_TYPE_POST:
 +      case HTTPREAD_HDR_TYPE_PUT:
 +      case HTTPREAD_HDR_TYPE_DELETE:
 +      case HTTPREAD_HDR_TYPE_TRACE:
 +      case HTTPREAD_HDR_TYPE_CONNECT:
 +      case HTTPREAD_HDR_TYPE_NOTIFY:
 +      case HTTPREAD_HDR_TYPE_M_SEARCH:
 +      case HTTPREAD_HDR_TYPE_M_POST:
 +      case HTTPREAD_HDR_TYPE_SUBSCRIBE:
 +      case HTTPREAD_HDR_TYPE_UNSUBSCRIBE:
 +      default:
 +              break;
 +      }
 +
 +      return 0;
 +
 +bad:
 +      /* Error */
 +      return -1;
 +}
 +
 +
 +/* httpread_read_handler -- called when socket ready to read
 + *
 + * Note: any extra data we read past end of transmitted file is ignored;
 + * if we were to support keeping connections open for multiple files then
 + * this would have to be addressed.
 + */
 +static void httpread_read_handler(int sd, void *eloop_ctx, void *sock_ctx)
 +{
 +      struct httpread *h = sock_ctx;
 +      int nread;
 +      char *rbp;      /* pointer into read buffer */
 +      char *hbp;      /* pointer into header buffer */
 +      char *bbp;      /* pointer into body buffer */
 +      char readbuf[HTTPREAD_READBUF_SIZE];  /* temp use to read into */
 +
-       if (nread < 0)
 +      /* read some at a time, then search for the interal
 +       * boundaries between header and data and etc.
 +       */
++      wpa_printf(MSG_DEBUG, "httpread: Trying to read more data(%p)", h);
 +      nread = read(h->sd, readbuf, sizeof(readbuf));
-               if (httpread_debug >= 10)
-                       wpa_printf(MSG_DEBUG, "httpread ok eof(%p)", h);
++      if (nread < 0) {
++              wpa_printf(MSG_DEBUG, "httpread failed: %s", strerror(errno));
 +              goto bad;
++      }
++      wpa_hexdump_ascii(MSG_MSGDUMP, "httpread - read", readbuf, nread);
 +      if (nread == 0) {
 +              /* end of transmission... this may be normal
 +               * or may be an error... in some cases we can't
 +               * tell which so we must assume it is normal then.
 +               */
 +              if (!h->got_hdr) {
 +                      /* Must at least have completed header */
 +                      wpa_printf(MSG_DEBUG, "httpread premature eof(%p)", h);
 +                      goto bad;
 +              }
 +              if (h->chunked || h->got_content_length) {
 +                      /* Premature EOF; e.g. dropped connection */
 +                      wpa_printf(MSG_DEBUG,
 +                                 "httpread premature eof(%p) %d/%d",
 +                                 h, h->body_nbytes,
 +                                 h->content_length);
 +                      goto bad;
 +              }
 +              /* No explicit length, hopefully we have all the data
 +               * although dropped connections can cause false
 +               * end
 +               */
-                       if (httpread_debug >= 10)
-                               wpa_printf(MSG_DEBUG,
-                                          "httpread no body hdr end(%p)", h);
++              wpa_printf(MSG_DEBUG, "httpread ok eof(%p)", h);
 +              h->got_body = 1;
 +              goto got_file;
 +      }
 +      rbp = readbuf;
 +
 +      /* Header consists of text lines (terminated by both CR and LF)
 +       * and an empty line (CR LF only).
 +       */
 +      if (!h->got_hdr) {
 +              hbp = h->hdr + h->hdr_nbytes;
 +              /* add to headers until:
 +               *      -- we run out of data in read buffer
 +               *      -- or, we run out of header buffer room
 +               *      -- or, we get double CRLF in headers
 +               */
 +              for (;;) {
 +                      if (nread == 0)
 +                              goto get_more;
 +                      if (h->hdr_nbytes == HTTPREAD_HEADER_MAX_SIZE) {
++                              wpa_printf(MSG_DEBUG,
++                                         "httpread: Too long header");
 +                              goto bad;
 +                      }
 +                      *hbp++ = *rbp++;
 +                      nread--;
 +                      h->hdr_nbytes++;
 +                      if (h->hdr_nbytes >= 4 &&
 +                          hbp[-1] == '\n' &&
 +                          hbp[-2] == '\r' &&
 +                          hbp[-3] == '\n' &&
 +                          hbp[-4] == '\r' ) {
 +                              h->got_hdr = 1;
 +                              *hbp = 0;       /* null terminate */
 +                              break;
 +                      }
 +              }
 +              /* here we've just finished reading the header */
 +              if (httpread_hdr_analyze(h)) {
 +                      wpa_printf(MSG_DEBUG, "httpread bad hdr(%p)", h);
 +                      goto bad;
 +              }
 +              if (h->max_bytes == 0) {
-                       if (httpread_debug >= 10)
-                               wpa_printf(MSG_DEBUG,
-                                          "httpread zero content length(%p)",
-                                          h);
++                      wpa_printf(MSG_DEBUG, "httpread no body hdr end(%p)",
++                                 h);
 +                      goto got_file;
 +              }
 +              if (h->got_content_length && h->content_length == 0) {
-                       if (httpread_debug >= 10)
-                               wpa_printf(MSG_DEBUG,
-                                          "httpread NO BODY for sp. type");
++                      wpa_printf(MSG_DEBUG,
++                                 "httpread zero content length(%p)", h);
 +                      goto got_file;
 +              }
 +      }
 +
 +      /* Certain types of requests never have data and so
 +       * must be specially recognized.
 +       */
 +      if (!os_strncasecmp(h->hdr, "SUBSCRIBE", 9) ||
 +          !os_strncasecmp(h->hdr, "UNSUBSCRIBE", 11) ||
 +          !os_strncasecmp(h->hdr, "HEAD", 4) ||
 +          !os_strncasecmp(h->hdr, "GET", 3)) {
 +              if (!h->got_body) {
-                       if (h->body_nbytes >= h->max_bytes)
++                      wpa_printf(MSG_DEBUG, "httpread NO BODY for sp. type");
 +              }
 +              h->got_body = 1;
 +              goto got_file;
 +      }
 +
 +      /* Data can be just plain binary data, or if "chunked"
 +       * consists of chunks each with a header, ending with
 +       * an ending header.
 +       */
 +      if (nread == 0)
 +              goto get_more;
 +      if (!h->got_body) {
 +              /* Here to get (more of) body */
 +              /* ensure we have enough room for worst case for body
 +               * plus a null termination character
 +               */
 +              if (h->body_alloc_nbytes < (h->body_nbytes + nread + 1)) {
 +                      char *new_body;
 +                      int new_alloc_nbytes;
 +
-                           == NULL)
++                      if (h->body_nbytes >= h->max_bytes) {
++                              wpa_printf(MSG_DEBUG,
++                                         "httpread: body_nbytes=%d >= max_bytes=%d",
++                                         h->body_nbytes, h->max_bytes);
 +                              goto bad;
++                      }
 +                      new_alloc_nbytes = h->body_alloc_nbytes +
 +                              HTTPREAD_BODYBUF_DELTA;
 +                      /* For content-length case, the first time
 +                       * through we allocate the whole amount
 +                       * we need.
 +                       */
 +                      if (h->got_content_length &&
 +                          new_alloc_nbytes < (h->content_length + 1))
 +                              new_alloc_nbytes = h->content_length + 1;
++                      if (new_alloc_nbytes < h->body_alloc_nbytes ||
++                          new_alloc_nbytes > h->max_bytes +
++                          HTTPREAD_BODYBUF_DELTA) {
++                              wpa_printf(MSG_DEBUG,
++                                         "httpread: Unacceptable body length %d (body_alloc_nbytes=%u max_bytes=%u)",
++                                         new_alloc_nbytes,
++                                         h->body_alloc_nbytes,
++                                         h->max_bytes);
++                              goto bad;
++                      }
 +                      if ((new_body = os_realloc(h->body, new_alloc_nbytes))
-                                       if (!isxdigit(*cbp))
++                          == NULL) {
++                              wpa_printf(MSG_DEBUG,
++                                         "httpread: Failed to reallocate buffer (len=%d)",
++                                         new_alloc_nbytes);
 +                              goto bad;
++                      }
 +
 +                      h->body = new_body;
 +                      h->body_alloc_nbytes = new_alloc_nbytes;
 +              }
 +              /* add bytes */
 +              bbp = h->body + h->body_nbytes;
 +              for (;;) {
 +                      int ncopy;
 +                      /* See if we need to stop */
 +                      if (h->chunked && h->in_chunk_data == 0) {
 +                              /* in chunk header */
 +                              char *cbp = h->body + h->chunk_start;
 +                              if (bbp-cbp >= 2 && bbp[-2] == '\r' &&
 +                                  bbp[-1] == '\n') {
 +                                      /* end of chunk hdr line */
 +                                      /* hdr line consists solely
 +                                       * of a hex numeral and CFLF
 +                                       */
-                                               if (httpread_debug >= 20)
-                                                       wpa_printf(
-                                                               MSG_DEBUG,
-                                                               "httpread end chunks(%p)", h);
++                                      if (!isxdigit(*cbp)) {
++                                              wpa_printf(MSG_DEBUG,
++                                                         "httpread: Unexpected chunk header value (not a hex digit)");
 +                                              goto bad;
++                                      }
 +                                      h->chunk_size = strtoul(cbp, NULL, 16);
++                                      if (h->chunk_size < 0 ||
++                                          h->chunk_size > h->max_bytes) {
++                                              wpa_printf(MSG_DEBUG,
++                                                         "httpread: Invalid chunk size %d",
++                                                         h->chunk_size);
++                                              goto bad;
++                                      }
 +                                      /* throw away chunk header
 +                                       * so we have only real data
 +                                       */
 +                                      h->body_nbytes = h->chunk_start;
 +                                      bbp = cbp;
 +                                      if (h->chunk_size == 0) {
 +                                              /* end of chunking */
 +                                              /* trailer follows */
 +                                              h->in_trailer = 1;
-                                       } else
++                                              wpa_printf(MSG_DEBUG,
++                                                         "httpread end chunks(%p)",
++                                                         h);
 +                                              break;
 +                                      }
 +                                      h->in_chunk_data = 1;
 +                                      /* leave chunk_start alone */
 +                              }
 +                      } else if (h->chunked) {
 +                              /* in chunk data */
 +                              if ((h->body_nbytes - h->chunk_start) ==
 +                                  (h->chunk_size + 2)) {
 +                                      /* end of chunk reached,
 +                                       * new chunk starts
 +                                       */
 +                                      /* check chunk ended w/ CRLF
 +                                       * which we'll throw away
 +                                       */
 +                                      if (bbp[-1] == '\n' &&
 +                                          bbp[-2] == '\r') {
-                               if (httpread_debug >= 10)
-                                       wpa_printf(
-                                               MSG_DEBUG,
-                                               "httpread got content(%p)", h);
++                                      } else {
++                                              wpa_printf(MSG_DEBUG,
++                                                         "httpread: Invalid chunk end");
 +                                              goto bad;
++                                      }
 +                                      h->body_nbytes -= 2;
 +                                      bbp -= 2;
 +                                      h->chunk_start = h->body_nbytes;
 +                                      h->in_chunk_data = 0;
 +                                      h->chunk_size = 0; /* just in case */
 +                              }
 +                      } else if (h->got_content_length &&
 +                                 h->body_nbytes >= h->content_length) {
 +                              h->got_body = 1;
-                                       if (httpread_debug >= 10)
-                                               wpa_printf(
-                                                       MSG_DEBUG,
-                                                       "httpread got content(%p)", h);
++                              wpa_printf(MSG_DEBUG,
++                                         "httpread got content(%p)", h);
 +                              goto got_file;
 +                      }
 +                      if (nread <= 0)
 +                              break;
 +                      /* Now transfer. Optimize using memcpy where we can. */
 +                      if (h->chunked && h->in_chunk_data) {
 +                              /* copy up to remainder of chunk data
 +                               * plus the required CR+LF at end
 +                               */
 +                              ncopy = (h->chunk_start + h->chunk_size + 2) -
 +                                      h->body_nbytes;
 +                      } else if (h->chunked) {
 +                              /*in chunk header -- don't optimize */
 +                              *bbp++ = *rbp++;
 +                              nread--;
 +                              h->body_nbytes++;
 +                              continue;
 +                      } else if (h->got_content_length) {
 +                              ncopy = h->content_length - h->body_nbytes;
 +                      } else {
 +                              ncopy = nread;
 +                      }
 +                      /* Note: should never be 0 */
++                      if (ncopy < 0) {
++                              wpa_printf(MSG_DEBUG,
++                                         "httpread: Invalid ncopy=%d", ncopy);
++                              goto bad;
++                      }
 +                      if (ncopy > nread)
 +                              ncopy = nread;
 +                      os_memcpy(bbp, rbp, ncopy);
 +                      bbp += ncopy;
 +                      h->body_nbytes += ncopy;
 +                      rbp += ncopy;
 +                      nread -= ncopy;
 +              }       /* body copy loop */
 +      }       /* !got_body */
 +      if (h->chunked && h->in_trailer) {
 +              /* If "chunked" then there is always a trailer,
 +               * consisting of zero or more non-empty lines
 +               * ending with CR LF and then an empty line w/ CR LF.
 +               * We do NOT support trailers except to skip them --
 +               * this is supported (generally) by the http spec.
 +               */
 +              for (;;) {
 +                      int c;
 +                      if (nread <= 0)
 +                              break;
 +                      c = *rbp++;
 +                      nread--;
 +                      switch (h->trailer_state) {
 +                      case trailer_line_begin:
 +                              if (c == '\r')
 +                                      h->trailer_state = trailer_empty_cr;
 +                              else
 +                                      h->trailer_state = trailer_nonempty;
 +                              break;
 +                      case trailer_empty_cr:
 +                              /* end empty line */
 +                              if (c == '\n') {
 +                                      h->trailer_state = trailer_line_begin;
 +                                      h->in_trailer = 0;
-       if (httpread_debug >= 10)
-               wpa_printf(MSG_DEBUG,
-                          "httpread got file %d bytes type %d",
-                          h->body_nbytes, h->hdr_type);
++                                      wpa_printf(MSG_DEBUG,
++                                                 "httpread got content(%p)",
++                                                 h);
 +                                      h->got_body = 1;
 +                                      goto got_file;
 +                              }
 +                              h->trailer_state = trailer_nonempty;
 +                              break;
 +                      case trailer_nonempty:
 +                              if (c == '\r')
 +                                      h->trailer_state = trailer_nonempty_cr;
 +                              break;
 +                      case trailer_nonempty_cr:
 +                              if (c == '\n')
 +                                      h->trailer_state = trailer_line_begin;
 +                              else
 +                                      h->trailer_state = trailer_nonempty;
 +                              break;
 +                      }
 +              }
 +      }
 +      goto get_more;
 +
 +bad:
 +      /* Error */
 +      wpa_printf(MSG_DEBUG, "httpread read/parse failure (%p)", h);
 +      (*h->cb)(h, h->cookie, HTTPREAD_EVENT_ERROR);
 +      return;
 +
 +get_more:
++      wpa_printf(MSG_DEBUG, "httpread: get more (%p)", h);
 +      return;
 +
 +got_file:
++      wpa_printf(MSG_DEBUG, "httpread got file %d bytes type %d",
++                 h->body_nbytes, h->hdr_type);
++      wpa_hexdump_ascii(MSG_MSGDUMP, "httpread: body",
++                        h->body, h->body_nbytes);
 +      /* Null terminate for convenience of some applications */
 +      if (h->body)
 +              h->body[h->body_nbytes] = 0; /* null terminate */
 +      h->got_file = 1;
 +      /* Assume that we do NOT support keeping connection alive,
 +       * and just in case somehow we don't get destroyed right away,
 +       * unregister now.
 +       */
 +      eloop_unregister_sock(h->sd, EVENT_TYPE_READ);
 +      /* The application can destroy us whenever they feel like...
 +       * cancel timeout.
 +       */
 +      eloop_cancel_timeout(httpread_timeout_handler, NULL, h);
 +      (*h->cb)(h, h->cookie, HTTPREAD_EVENT_FILE_READY);
 +}
 +
 +
 +/* httpread_create -- start a new reading session making use of eloop.
 + * The new instance will use the socket descriptor for reading (until
 + * it gets a file and not after) but will not close the socket, even
 + * when the instance is destroyed (the application must do that).
 + * Return NULL on error.
 + *
 + * Provided that httpread_create successfully returns a handle,
 + * the callback fnc is called to handle httpread_event events.
 + * The caller should do destroy on any errors or unknown events.
 + *
 + * Pass max_bytes == 0 to not read body at all (required for e.g.
 + * reply to HEAD request).
 + */
 +struct httpread * httpread_create(
 +      int sd,  /* descriptor of TCP socket to read from */
 +      void (*cb)(struct httpread *handle, void *cookie,
 +                 enum httpread_event e),  /* call on event */
 +      void *cookie,    /* pass to callback */
 +      int max_bytes,    /* maximum body size else abort it */
 +      int timeout_seconds     /* 0; or total duration timeout period */
 +      )
 +{
 +      struct httpread *h = NULL;
 +
 +      h = os_zalloc(sizeof(*h));
 +      if (h == NULL)
 +              goto fail;
 +      h->sd = sd;
 +      h->cb = cb;
 +      h->cookie = cookie;
 +      h->max_bytes = max_bytes;
 +      h->timeout_seconds = timeout_seconds;
 +
 +      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;
 +      }
 +      return h;
 +
 +fail:
 +
 +      /* Error */
 +      httpread_destroy(h);
 +      return NULL;
 +}
 +
 +
 +/* httpread_hdr_type_get -- When file is ready, returns header type. */
 +enum httpread_hdr_type httpread_hdr_type_get(struct httpread *h)
 +{
 +      return h->hdr_type;
 +}
 +
 +
 +/* httpread_uri_get -- When file is ready, uri_get returns (translated) URI
 + * or possibly NULL (which would be an error).
 + */
 +char * httpread_uri_get(struct httpread *h)
 +{
 +      return h->uri;
 +}
 +
 +
 +/* httpread_reply_code_get -- When reply is ready, returns reply code */
 +int httpread_reply_code_get(struct httpread *h)
 +{
 +      return h->reply_code;
 +}
 +
 +
 +/* httpread_length_get -- When file is ready, returns file length. */
 +int httpread_length_get(struct httpread *h)
 +{
 +      return h->body_nbytes;
 +}
 +
 +
 +/* httpread_data_get -- When file is ready, returns file content
 + * with null byte appened.
 + * Might return NULL in some error condition.
 + */
 +void * httpread_data_get(struct httpread *h)
 +{
 +      return h->body ? h->body : "";
 +}
 +
 +
 +/* httpread_hdr_get -- When file is ready, returns header content
 + * with null byte appended.
 + * Might return NULL in some error condition.
 + */
 +char * httpread_hdr_get(struct httpread *h)
 +{
 +      return h->hdr;
 +}
 +
 +
 +/* httpread_hdr_line_get -- When file is ready, returns pointer
 + * to line within header content matching the given tag
 + * (after the tag itself and any spaces/tabs).
 + *
 + * The tag should end with a colon for reliable matching.
 + *
 + * If not found, returns NULL;
 + */
 +char * httpread_hdr_line_get(struct httpread *h, const char *tag)
 +{
 +      int tag_len = os_strlen(tag);
 +      char *hdr = h->hdr;
 +      hdr = os_strchr(hdr, '\n');
 +      if (hdr == NULL)
 +              return NULL;
 +      hdr++;
 +      for (;;) {
 +              if (!os_strncasecmp(hdr, tag, tag_len)) {
 +                      hdr += tag_len;
 +                      while (*hdr == ' ' || *hdr == '\t')
 +                              hdr++;
 +                      return hdr;
 +              }
 +              hdr = os_strchr(hdr, '\n');
 +              if (hdr == NULL)
 +                      return NULL;
 +              hdr++;
 +      }
 +}
index d45dfc8efee645de1fc217656b1ffb4923ac56f0,0000000000000000000000000000000000000000..bb3c055486c027ca994054440aa13c4a8df63f53
mode 100644,000000..100644
--- /dev/null
@@@ -1,198 -1,0 +1,204 @@@
- static char wifi_handover_type[] = "application/vnd.wfa.wsc";
- static char p2p_handover_type[] = "application/vnd.wfa.p2p";
 +/*
 + * NDEF(NFC Data Exchange Format) routines for Wi-Fi Protected Setup
 + *   Reference is "NFCForum-TS-NDEF_1.0 2006-07-24".
 + * Copyright (c) 2009-2012, Masashi Honma <masashi.honma@gmail.com>
 + *
 + * This software may be distributed under the terms of the BSD license.
 + * See README for more details.
 + */
 +
 +#include "includes.h"
 +#include "common.h"
 +#include "wps/wps.h"
 +
 +#define FLAG_MESSAGE_BEGIN (1 << 7)
 +#define FLAG_MESSAGE_END (1 << 6)
 +#define FLAG_CHUNK (1 << 5)
 +#define FLAG_SHORT_RECORD (1 << 4)
 +#define FLAG_ID_LENGTH_PRESENT (1 << 3)
 +#define FLAG_TNF_NFC_FORUM (0x01)
 +#define FLAG_TNF_RFC2046 (0x02)
 +
 +struct ndef_record {
 +      const u8 *type;
 +      const u8 *id;
 +      const u8 *payload;
 +      u8 type_length;
 +      u8 id_length;
 +      u32 payload_length;
 +      u32 total_length;
 +};
 +
-               record->payload_length = ntohl(*(u32 *)pos);
++static const char wifi_handover_type[] = "application/vnd.wfa.wsc";
++static const char p2p_handover_type[] = "application/vnd.wfa.p2p";
 +
 +static int ndef_parse_record(const u8 *data, u32 size,
 +                           struct ndef_record *record)
 +{
 +      const u8 *pos = data + 1;
 +
 +      if (size < 2)
 +              return -1;
 +      record->type_length = *pos++;
 +      if (data[0] & FLAG_SHORT_RECORD) {
 +              if (size < 3)
 +                      return -1;
 +              record->payload_length = *pos++;
 +      } else {
++              u32 len;
++
 +              if (size < 6)
 +                      return -1;
-       if (record->total_length > size)
++              len = WPA_GET_BE32(pos);
++              if (len > size - 6 || len > 20000)
++                      return -1;
++              record->payload_length = len;
 +              pos += sizeof(u32);
 +      }
 +
 +      if (data[0] & FLAG_ID_LENGTH_PRESENT) {
 +              if ((int) size < pos - data + 1)
 +                      return -1;
 +              record->id_length = *pos++;
 +      } else
 +              record->id_length = 0;
 +
 +      record->type = record->type_length == 0 ? NULL : pos;
 +      pos += record->type_length;
 +
 +      record->id = record->id_length == 0 ? NULL : pos;
 +      pos += record->id_length;
 +
 +      record->payload = record->payload_length == 0 ? NULL : pos;
 +      pos += record->payload_length;
 +
 +      record->total_length = pos - data;
- static struct wpabuf * ndef_build_record(u8 flags, void *type,
++      if (record->total_length > size ||
++          record->total_length < record->payload_length)
 +              return -1;
 +      return 0;
 +}
 +
 +
 +static struct wpabuf * ndef_parse_records(const struct wpabuf *buf,
 +                                        int (*filter)(struct ndef_record *))
 +{
 +      struct ndef_record record;
 +      int len = wpabuf_len(buf);
 +      const u8 *data = wpabuf_head(buf);
 +
 +      while (len > 0) {
 +              if (ndef_parse_record(data, len, &record) < 0) {
 +                      wpa_printf(MSG_ERROR, "NDEF : Failed to parse");
 +                      return NULL;
 +              }
 +              if (filter == NULL || filter(&record))
 +                      return wpabuf_alloc_copy(record.payload,
 +                                               record.payload_length);
 +              data += record.total_length;
 +              len -= record.total_length;
 +      }
 +      wpa_printf(MSG_ERROR, "NDEF : Record not found");
 +      return NULL;
 +}
 +
 +
++static struct wpabuf * ndef_build_record(u8 flags, const void *type,
 +                                       u8 type_length, void *id,
 +                                       u8 id_length,
 +                                       const struct wpabuf *payload)
 +{
 +      struct wpabuf *record;
 +      size_t total_len;
 +      int short_record;
 +      u8 local_flag;
 +      size_t payload_length = wpabuf_len(payload);
 +
 +      short_record = payload_length < 256 ? 1 : 0;
 +
 +      total_len = 2; /* flag + type length */
 +      /* payload length */
 +      total_len += short_record ? sizeof(u8) : sizeof(u32);
 +      if (id_length > 0)
 +              total_len += 1;
 +      total_len += type_length + id_length + payload_length;
 +      record = wpabuf_alloc(total_len);
 +      if (record == NULL) {
 +              wpa_printf(MSG_ERROR, "NDEF : Failed to allocate "
 +                         "record for build");
 +              return NULL;
 +      }
 +
 +      local_flag = flags;
 +      if (id_length > 0)
 +              local_flag |= FLAG_ID_LENGTH_PRESENT;
 +      if (short_record)
 +              local_flag |= FLAG_SHORT_RECORD;
 +      wpabuf_put_u8(record, local_flag);
 +
 +      wpabuf_put_u8(record, type_length);
 +
 +      if (short_record)
 +              wpabuf_put_u8(record, payload_length);
 +      else
 +              wpabuf_put_be32(record, payload_length);
 +
 +      if (id_length > 0)
 +              wpabuf_put_u8(record, id_length);
 +      wpabuf_put_data(record, type, type_length);
 +      wpabuf_put_data(record, id, id_length);
 +      wpabuf_put_buf(record, payload);
 +      return record;
 +}
 +
 +
 +static int wifi_filter(struct ndef_record *record)
 +{
 +      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)
 +              return 0;
 +      return 1;
 +}
 +
 +
 +struct wpabuf * ndef_parse_wifi(const struct wpabuf *buf)
 +{
 +      return ndef_parse_records(buf, wifi_filter);
 +}
 +
 +
 +struct wpabuf * ndef_build_wifi(const struct wpabuf *buf)
 +{
 +      return ndef_build_record(FLAG_MESSAGE_BEGIN | FLAG_MESSAGE_END |
 +                               FLAG_TNF_RFC2046, wifi_handover_type,
 +                               os_strlen(wifi_handover_type), NULL, 0, buf);
 +}
 +
 +
 +static int p2p_filter(struct ndef_record *record)
 +{
 +      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_parse_p2p(const struct wpabuf *buf)
 +{
 +      return ndef_parse_records(buf, p2p_filter);
 +}
 +
 +
 +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 2c68be8c62ea05394ca4b8ee2f5746261439dccd,0000000000000000000000000000000000000000..fbaf85aabab4afc949ebb39dbcb77020fa717742
mode 100644,000000..100644
--- /dev/null
@@@ -1,661 -1,0 +1,662 @@@
-       struct wps_parse_attr attr_a, attr_b;
 +/*
 + * Wi-Fi Protected Setup
 + * Copyright (c) 2007-2009, Jouni Malinen <j@w1.fi>
 + *
 + * This software may be distributed under the terms of the BSD license.
 + * See README for more details.
 + */
 +
 +#include "includes.h"
 +
 +#include "common.h"
 +#include "crypto/dh_group5.h"
 +#include "common/ieee802_11_defs.h"
 +#include "wps_i.h"
 +#include "wps_dev_attr.h"
 +
 +
 +#ifdef CONFIG_WPS_TESTING
 +int wps_version_number = 0x20;
 +int wps_testing_dummy_cred = 0;
 +int wps_corrupt_pkhash = 0;
 +#endif /* CONFIG_WPS_TESTING */
 +
 +
 +/**
 + * wps_init - Initialize WPS Registration protocol data
 + * @cfg: WPS configuration
 + * Returns: Pointer to allocated data or %NULL on failure
 + *
 + * This function is used to initialize WPS data for a registration protocol
 + * instance (i.e., each run of registration protocol as a Registrar of
 + * Enrollee. The caller is responsible for freeing this data after the
 + * registration run has been completed by calling wps_deinit().
 + */
 +struct wps_data * wps_init(const struct wps_config *cfg)
 +{
 +      struct wps_data *data = os_zalloc(sizeof(*data));
 +      if (data == NULL)
 +              return NULL;
 +      data->wps = cfg->wps;
 +      data->registrar = cfg->registrar;
 +      if (cfg->registrar) {
 +              os_memcpy(data->uuid_r, cfg->wps->uuid, WPS_UUID_LEN);
 +      } else {
 +              os_memcpy(data->mac_addr_e, cfg->wps->dev.mac_addr, ETH_ALEN);
 +              os_memcpy(data->uuid_e, cfg->wps->uuid, WPS_UUID_LEN);
 +      }
 +      if (cfg->pin) {
 +              data->dev_pw_id = cfg->dev_pw_id;
 +              data->dev_password = os_malloc(cfg->pin_len);
 +              if (data->dev_password == NULL) {
 +                      os_free(data);
 +                      return NULL;
 +              }
 +              os_memcpy(data->dev_password, cfg->pin, cfg->pin_len);
 +              data->dev_password_len = cfg->pin_len;
 +              wpa_hexdump_key(MSG_DEBUG, "WPS: AP PIN dev_password",
 +                              data->dev_password, data->dev_password_len);
 +      }
 +
 +#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;
 +              data->alt_dev_password = data->dev_password;
 +              data->alt_dev_password_len = data->dev_password_len;
 +
 +              data->dev_pw_id = cfg->wps->ap_nfc_dev_pw_id;
 +              data->dev_password =
 +                      os_malloc(wpabuf_len(cfg->wps->ap_nfc_dev_pw));
 +              if (data->dev_password == NULL) {
 +                      os_free(data);
 +                      return NULL;
 +              }
 +              os_memcpy(data->dev_password,
 +                        wpabuf_head(cfg->wps->ap_nfc_dev_pw),
 +                        wpabuf_len(cfg->wps->ap_nfc_dev_pw));
 +              data->dev_password_len = wpabuf_len(cfg->wps->ap_nfc_dev_pw);
 +              wpa_hexdump_key(MSG_DEBUG, "WPS: NFC dev_password",
 +                          data->dev_password, data->dev_password_len);
 +      }
 +#endif /* CONFIG_WPS_NFC */
 +
 +      data->pbc = cfg->pbc;
 +      if (cfg->pbc) {
 +              /* Use special PIN '00000000' for PBC */
 +              data->dev_pw_id = DEV_PW_PUSHBUTTON;
 +              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);
 +                      return NULL;
 +              }
 +              data->dev_password_len = 8;
 +      }
 +
 +      data->state = data->registrar ? RECV_M1 : SEND_M1;
 +
 +      if (cfg->assoc_wps_ie) {
 +              struct wps_parse_attr attr;
 +              wpa_hexdump_buf(MSG_DEBUG, "WPS: WPS IE from (Re)AssocReq",
 +                              cfg->assoc_wps_ie);
 +              if (wps_parse_msg(cfg->assoc_wps_ie, &attr) < 0) {
 +                      wpa_printf(MSG_DEBUG, "WPS: Failed to parse WPS IE "
 +                                 "from (Re)AssocReq");
 +              } else if (attr.request_type == NULL) {
 +                      wpa_printf(MSG_DEBUG, "WPS: No Request Type attribute "
 +                                 "in (Re)AssocReq WPS IE");
 +              } else {
 +                      wpa_printf(MSG_DEBUG, "WPS: Request Type (from WPS IE "
 +                                 "in (Re)AssocReq WPS IE): %d",
 +                                 *attr.request_type);
 +                      data->request_type = *attr.request_type;
 +              }
 +      }
 +
 +      if (cfg->new_ap_settings) {
 +              data->new_ap_settings =
 +                      os_malloc(sizeof(*data->new_ap_settings));
 +              if (data->new_ap_settings == NULL) {
 +                      bin_clear_free(data->dev_password,
 +                                     data->dev_password_len);
 +                      os_free(data);
 +                      return NULL;
 +              }
 +              os_memcpy(data->new_ap_settings, cfg->new_ap_settings,
 +                        sizeof(*data->new_ap_settings));
 +      }
 +
 +      if (cfg->peer_addr)
 +              os_memcpy(data->peer_dev.mac_addr, cfg->peer_addr, ETH_ALEN);
 +      if (cfg->p2p_dev_addr)
 +              os_memcpy(data->p2p_dev_addr, cfg->p2p_dev_addr, ETH_ALEN);
 +
 +      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;
 +}
 +
 +
 +/**
 + * wps_deinit - Deinitialize WPS Registration protocol data
 + * @data: WPS Registration protocol data from wps_init()
 + */
 +void wps_deinit(struct wps_data *data)
 +{
 +#ifdef CONFIG_WPS_NFC
 +      if (data->registrar && data->nfc_pw_token)
 +              wps_registrar_remove_nfc_pw_token(data->wps->registrar,
 +                                                data->nfc_pw_token);
 +#endif /* CONFIG_WPS_NFC */
 +
 +      if (data->wps_pin_revealed) {
 +              wpa_printf(MSG_DEBUG, "WPS: Full PIN information revealed and "
 +                         "negotiation failed");
 +              if (data->registrar)
 +                      wps_registrar_invalidate_pin(data->wps->registrar,
 +                                                   data->uuid_e);
 +      } else if (data->registrar)
 +              wps_registrar_unlock_pin(data->wps->registrar, data->uuid_e);
 +
 +      wpabuf_free(data->dh_privkey);
 +      wpabuf_free(data->dh_pubkey_e);
 +      wpabuf_free(data->dh_pubkey_r);
 +      wpabuf_free(data->last_msg);
 +      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);
 +      bin_clear_free(data->new_ap_settings, sizeof(*data->new_ap_settings));
 +      dh5_free(data->dh_ctx);
 +      os_free(data);
 +}
 +
 +
 +/**
 + * wps_process_msg - Process a WPS message
 + * @wps: WPS Registration protocol data from wps_init()
 + * @op_code: Message OP Code
 + * @msg: Message data
 + * Returns: Processing result
 + *
 + * This function is used to process WPS messages with OP Codes WSC_ACK,
 + * WSC_NACK, WSC_MSG, and WSC_Done. The caller (e.g., EAP server/peer) is
 + * responsible for reassembling the messages before calling this function.
 + * Response to this message is built by calling wps_get_msg().
 + */
 +enum wps_process_res wps_process_msg(struct wps_data *wps,
 +                                   enum wsc_op_code op_code,
 +                                   const struct wpabuf *msg)
 +{
 +      if (wps->registrar)
 +              return wps_registrar_process_msg(wps, op_code, msg);
 +      else
 +              return wps_enrollee_process_msg(wps, op_code, msg);
 +}
 +
 +
 +/**
 + * wps_get_msg - Build a WPS message
 + * @wps: WPS Registration protocol data from wps_init()
 + * @op_code: Buffer for returning message OP Code
 + * Returns: The generated WPS message or %NULL on failure
 + *
 + * This function is used to build a response to a message processed by calling
 + * wps_process_msg(). The caller is responsible for freeing the buffer.
 + */
 +struct wpabuf * wps_get_msg(struct wps_data *wps, enum wsc_op_code *op_code)
 +{
 +      if (wps->registrar)
 +              return wps_registrar_get_msg(wps, op_code);
 +      else
 +              return wps_enrollee_get_msg(wps, op_code);
 +}
 +
 +
 +/**
 + * wps_is_selected_pbc_registrar - Check whether WPS IE indicates active PBC
 + * @msg: WPS IE contents from Beacon or Probe Response frame
 + * Returns: 1 if PBC Registrar is active, 0 if not
 + */
 +int wps_is_selected_pbc_registrar(const struct wpabuf *msg)
 +{
 +      struct wps_parse_attr attr;
 +
 +      /*
 +       * In theory, this could also verify that attr.sel_reg_config_methods
 +       * includes WPS_CONFIG_PUSHBUTTON, but some deployed AP implementations
 +       * do not set Selected Registrar Config Methods attribute properly, so
 +       * it is safer to just use Device Password ID here.
 +       */
 +
 +      if (wps_parse_msg(msg, &attr) < 0 ||
 +          !attr.selected_registrar || *attr.selected_registrar == 0 ||
 +          !attr.dev_password_id ||
 +          WPA_GET_BE16(attr.dev_password_id) != DEV_PW_PUSHBUTTON)
 +              return 0;
 +
 +#ifdef CONFIG_WPS_STRICT
 +      if (!attr.sel_reg_config_methods ||
 +          !(WPA_GET_BE16(attr.sel_reg_config_methods) &
 +            WPS_CONFIG_PUSHBUTTON))
 +              return 0;
 +#endif /* CONFIG_WPS_STRICT */
 +
 +      return 1;
 +}
 +
 +
 +static int is_selected_pin_registrar(struct wps_parse_attr *attr)
 +{
 +      /*
 +       * In theory, this could also verify that attr.sel_reg_config_methods
 +       * includes WPS_CONFIG_LABEL, WPS_CONFIG_DISPLAY, or WPS_CONFIG_KEYPAD,
 +       * but some deployed AP implementations do not set Selected Registrar
 +       * Config Methods attribute properly, so it is safer to just use
 +       * Device Password ID here.
 +       */
 +
 +      if (!attr->selected_registrar || *attr->selected_registrar == 0)
 +              return 0;
 +
 +      if (attr->dev_password_id != NULL &&
 +          WPA_GET_BE16(attr->dev_password_id) == DEV_PW_PUSHBUTTON)
 +              return 0;
 +
 +#ifdef CONFIG_WPS_STRICT
 +      if (!attr->sel_reg_config_methods ||
 +          !(WPA_GET_BE16(attr->sel_reg_config_methods) &
 +            (WPS_CONFIG_LABEL | WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD)))
 +              return 0;
 +#endif /* CONFIG_WPS_STRICT */
 +
 +      return 1;
 +}
 +
 +
 +/**
 + * wps_is_selected_pin_registrar - Check whether WPS IE indicates active PIN
 + * @msg: WPS IE contents from Beacon or Probe Response frame
 + * Returns: 1 if PIN Registrar is active, 0 if not
 + */
 +int wps_is_selected_pin_registrar(const struct wpabuf *msg)
 +{
 +      struct wps_parse_attr attr;
 +
 +      if (wps_parse_msg(msg, &attr) < 0)
 +              return 0;
 +
 +      return is_selected_pin_registrar(&attr);
 +}
 +
 +
 +/**
 + * wps_is_addr_authorized - Check whether WPS IE authorizes MAC address
 + * @msg: WPS IE contents from Beacon or Probe Response frame
 + * @addr: MAC address to search for
 + * @ver1_compat: Whether to use version 1 compatibility mode
 + * Returns: 2 if the specified address is explicit authorized, 1 if address is
 + * authorized (broadcast), 0 if not
 + */
 +int wps_is_addr_authorized(const struct wpabuf *msg, const u8 *addr,
 +                         int ver1_compat)
 +{
 +      struct wps_parse_attr attr;
 +      unsigned int i;
 +      const u8 *pos;
 +      const u8 bcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
 +
 +      if (wps_parse_msg(msg, &attr) < 0)
 +              return 0;
 +
 +      if (!attr.version2 && ver1_compat) {
 +              /*
 +               * Version 1.0 AP - AuthorizedMACs not used, so revert back to
 +               * old mechanism of using SelectedRegistrar.
 +               */
 +              return is_selected_pin_registrar(&attr);
 +      }
 +
 +      if (!attr.authorized_macs)
 +              return 0;
 +
 +      pos = attr.authorized_macs;
 +      for (i = 0; i < attr.authorized_macs_len / ETH_ALEN; i++) {
 +              if (os_memcmp(pos, addr, ETH_ALEN) == 0)
 +                      return 2;
 +              if (os_memcmp(pos, bcast, ETH_ALEN) == 0)
 +                      return 1;
 +              pos += ETH_ALEN;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +/**
 + * wps_ap_priority_compar - Prioritize WPS IE from two APs
 + * @wps_a: WPS IE contents from Beacon or Probe Response frame
 + * @wps_b: WPS IE contents from Beacon or Probe Response frame
 + * Returns: 1 if wps_b is considered more likely selection for WPS
 + * provisioning, -1 if wps_a is considered more like, or 0 if no preference
 + */
 +int wps_ap_priority_compar(const struct wpabuf *wps_a,
 +                         const struct wpabuf *wps_b)
 +{
-       if (wps_a == NULL || wps_parse_msg(wps_a, &attr_a) < 0)
++      struct wps_parse_attr attr;
 +      int sel_a, sel_b;
 +
-       if (wps_b == NULL || wps_parse_msg(wps_b, &attr_b) < 0)
-               return -1;
++      if (wps_a == NULL || wps_parse_msg(wps_a, &attr) < 0)
 +              return 1;
-       sel_a = attr_a.selected_registrar && *attr_a.selected_registrar != 0;
-       sel_b = attr_b.selected_registrar && *attr_b.selected_registrar != 0;
++      sel_a = attr.selected_registrar && *attr.selected_registrar != 0;
 +
-                       if (attr.dev_name[i] < 32)
++      if (wps_b == NULL || wps_parse_msg(wps_b, &attr) < 0)
++              return -1;
++      sel_b = attr.selected_registrar && *attr.selected_registrar != 0;
 +
 +      if (sel_a && !sel_b)
 +              return -1;
 +      if (!sel_a && sel_b)
 +              return 1;
 +
 +      return 0;
 +}
 +
 +
 +/**
 + * wps_get_uuid_e - Get UUID-E from WPS IE
 + * @msg: WPS IE contents from Beacon or Probe Response frame
 + * Returns: Pointer to UUID-E or %NULL if not included
 + *
 + * The returned pointer is to the msg contents and it remains valid only as
 + * long as the msg buffer is valid.
 + */
 +const u8 * wps_get_uuid_e(const struct wpabuf *msg)
 +{
 +      struct wps_parse_attr attr;
 +
 +      if (wps_parse_msg(msg, &attr) < 0)
 +              return NULL;
 +      return attr.uuid_e;
 +}
 +
 +
 +/**
 + * wps_is_20 - Check whether WPS attributes claim support for WPS 2.0
 + */
 +int wps_is_20(const struct wpabuf *msg)
 +{
 +      struct wps_parse_attr attr;
 +
 +      if (msg == NULL || wps_parse_msg(msg, &attr) < 0)
 +              return 0;
 +      return attr.version2 != NULL;
 +}
 +
 +
 +/**
 + * wps_build_assoc_req_ie - Build WPS IE for (Re)Association Request
 + * @req_type: Value for Request Type attribute
 + * Returns: WPS IE or %NULL on failure
 + *
 + * The caller is responsible for freeing the buffer.
 + */
 +struct wpabuf * wps_build_assoc_req_ie(enum wps_request_type req_type)
 +{
 +      struct wpabuf *ie;
 +      u8 *len;
 +
 +      wpa_printf(MSG_DEBUG, "WPS: Building WPS IE for (Re)Association "
 +                 "Request");
 +      ie = wpabuf_alloc(100);
 +      if (ie == NULL)
 +              return NULL;
 +
 +      wpabuf_put_u8(ie, WLAN_EID_VENDOR_SPECIFIC);
 +      len = wpabuf_put(ie, 1);
 +      wpabuf_put_be32(ie, WPS_DEV_OUI_WFA);
 +
 +      if (wps_build_version(ie) ||
 +          wps_build_req_type(ie, req_type) ||
 +          wps_build_wfa_ext(ie, 0, NULL, 0)) {
 +              wpabuf_free(ie);
 +              return NULL;
 +      }
 +
 +      *len = wpabuf_len(ie) - 2;
 +
 +      return ie;
 +}
 +
 +
 +/**
 + * wps_build_assoc_resp_ie - Build WPS IE for (Re)Association Response
 + * Returns: WPS IE or %NULL on failure
 + *
 + * The caller is responsible for freeing the buffer.
 + */
 +struct wpabuf * wps_build_assoc_resp_ie(void)
 +{
 +      struct wpabuf *ie;
 +      u8 *len;
 +
 +      wpa_printf(MSG_DEBUG, "WPS: Building WPS IE for (Re)Association "
 +                 "Response");
 +      ie = wpabuf_alloc(100);
 +      if (ie == NULL)
 +              return NULL;
 +
 +      wpabuf_put_u8(ie, WLAN_EID_VENDOR_SPECIFIC);
 +      len = wpabuf_put(ie, 1);
 +      wpabuf_put_be32(ie, WPS_DEV_OUI_WFA);
 +
 +      if (wps_build_version(ie) ||
 +          wps_build_resp_type(ie, WPS_RESP_AP) ||
 +          wps_build_wfa_ext(ie, 0, NULL, 0)) {
 +              wpabuf_free(ie);
 +              return NULL;
 +      }
 +
 +      *len = wpabuf_len(ie) - 2;
 +
 +      return ie;
 +}
 +
 +
 +/**
 + * wps_build_probe_req_ie - Build WPS IE for Probe Request
 + * @pw_id: Password ID (DEV_PW_PUSHBUTTON for active PBC and DEV_PW_DEFAULT for
 + * most other use cases)
 + * @dev: Device attributes
 + * @uuid: Own UUID
 + * @req_type: Value for Request Type attribute
 + * @num_req_dev_types: Number of requested device types
 + * @req_dev_types: Requested device types (8 * num_req_dev_types octets) or
 + *    %NULL if none
 + * Returns: WPS IE or %NULL on failure
 + *
 + * The caller is responsible for freeing the buffer.
 + */
 +struct wpabuf * wps_build_probe_req_ie(u16 pw_id, struct wps_device_data *dev,
 +                                     const u8 *uuid,
 +                                     enum wps_request_type req_type,
 +                                     unsigned int num_req_dev_types,
 +                                     const u8 *req_dev_types)
 +{
 +      struct wpabuf *ie;
 +
 +      wpa_printf(MSG_DEBUG, "WPS: Building WPS IE for Probe Request");
 +
 +      ie = wpabuf_alloc(500);
 +      if (ie == NULL)
 +              return NULL;
 +
 +      if (wps_build_version(ie) ||
 +          wps_build_req_type(ie, req_type) ||
 +          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, 0) ||
 +          wps_build_assoc_state(NULL, ie) ||
 +          wps_build_config_error(ie, WPS_CFG_NO_ERROR) ||
 +          wps_build_dev_password_id(ie, pw_id) ||
 +          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) ||
 +          wps_build_req_dev_type(dev, ie, num_req_dev_types, req_dev_types)
 +          ||
 +          wps_build_secondary_dev_type(dev, ie)
 +              ) {
 +              wpabuf_free(ie);
 +              return NULL;
 +      }
 +
 +      return wps_ie_encapsulate(ie);
 +}
 +
 +
 +void wps_free_pending_msgs(struct upnp_pending_message *msgs)
 +{
 +      struct upnp_pending_message *p, *prev;
 +      p = msgs;
 +      while (p) {
 +              prev = p;
 +              p = p->next;
 +              wpabuf_free(prev->msg);
 +              os_free(prev);
 +      }
 +}
 +
 +
 +int wps_attr_text(struct wpabuf *data, char *buf, char *end)
 +{
 +      struct wps_parse_attr attr;
 +      char *pos = buf;
 +      int ret;
 +
 +      if (wps_parse_msg(data, &attr) < 0)
 +              return -1;
 +
 +      if (attr.wps_state) {
 +              if (*attr.wps_state == WPS_STATE_NOT_CONFIGURED)
 +                      ret = os_snprintf(pos, end - pos,
 +                                        "wps_state=unconfigured\n");
 +              else if (*attr.wps_state == WPS_STATE_CONFIGURED)
 +                      ret = os_snprintf(pos, end - pos,
 +                                        "wps_state=configured\n");
 +              else
 +                      ret = 0;
 +              if (os_snprintf_error(end - pos, ret))
 +                      return pos - buf;
 +              pos += ret;
 +      }
 +
 +      if (attr.ap_setup_locked && *attr.ap_setup_locked) {
 +              ret = os_snprintf(pos, end - pos,
 +                                "wps_ap_setup_locked=1\n");
 +              if (os_snprintf_error(end - pos, ret))
 +                      return pos - buf;
 +              pos += ret;
 +      }
 +
 +      if (attr.selected_registrar && *attr.selected_registrar) {
 +              ret = os_snprintf(pos, end - pos,
 +                                "wps_selected_registrar=1\n");
 +              if (os_snprintf_error(end - pos, ret))
 +                      return pos - buf;
 +              pos += ret;
 +      }
 +
 +      if (attr.dev_password_id) {
 +              ret = os_snprintf(pos, end - pos,
 +                                "wps_device_password_id=%u\n",
 +                                WPA_GET_BE16(attr.dev_password_id));
 +              if (os_snprintf_error(end - pos, ret))
 +                      return pos - buf;
 +              pos += ret;
 +      }
 +
 +      if (attr.sel_reg_config_methods) {
 +              ret = os_snprintf(pos, end - pos,
 +                                "wps_selected_registrar_config_methods="
 +                                "0x%04x\n",
 +                                WPA_GET_BE16(attr.sel_reg_config_methods));
 +              if (os_snprintf_error(end - pos, ret))
 +                      return pos - buf;
 +              pos += ret;
 +      }
 +
 +      if (attr.primary_dev_type) {
 +              char devtype[WPS_DEV_TYPE_BUFSIZE];
 +              ret = os_snprintf(pos, end - pos,
 +                                "wps_primary_device_type=%s\n",
 +                                wps_dev_type_bin2str(attr.primary_dev_type,
 +                                                     devtype,
 +                                                     sizeof(devtype)));
 +              if (os_snprintf_error(end - pos, ret))
 +                      return pos - buf;
 +              pos += ret;
 +      }
 +
 +      if (attr.dev_name) {
 +              char *str = os_malloc(attr.dev_name_len + 1);
 +              size_t i;
 +              if (str == NULL)
 +                      return pos - buf;
 +              for (i = 0; i < attr.dev_name_len; i++) {
++                      if (attr.dev_name[i] == 0 ||
++                          is_ctrl_char(attr.dev_name[i]))
 +                              str[i] = '_';
 +                      else
 +                              str[i] = attr.dev_name[i];
 +              }
 +              str[i] = '\0';
 +              ret = os_snprintf(pos, end - pos, "wps_device_name=%s\n", str);
 +              os_free(str);
 +              if (os_snprintf_error(end - pos, ret))
 +                      return pos - buf;
 +              pos += ret;
 +      }
 +
 +      if (attr.config_methods) {
 +              ret = os_snprintf(pos, end - pos,
 +                                "wps_config_methods=0x%04x\n",
 +                                WPA_GET_BE16(attr.config_methods));
 +              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 0a7f65dfd6cb5220a3f41bbd56ff125a7cdc7fc4,0000000000000000000000000000000000000000..2c91d1678c157e07498631097ccb6e8ea09f91f8
mode 100644,000000..100644
--- /dev/null
@@@ -1,1040 -1,0 +1,1041 @@@
-       u8 ssid[32];
 +/*
 + * Wi-Fi Protected Setup
 + * Copyright (c) 2007-2013, Jouni Malinen <j@w1.fi>
 + *
 + * This software may be distributed under the terms of the BSD license.
 + * See README for more details.
 + */
 +
 +#ifndef WPS_H
 +#define WPS_H
 +
++#include "common/ieee802_11_defs.h"
 +#include "wps_defs.h"
 +
 +/**
 + * enum wsc_op_code - EAP-WSC OP-Code values
 + */
 +enum wsc_op_code {
 +      WSC_UPnP = 0 /* No OP Code in UPnP transport */,
 +      WSC_Start = 0x01,
 +      WSC_ACK = 0x02,
 +      WSC_NACK = 0x03,
 +      WSC_MSG = 0x04,
 +      WSC_Done = 0x05,
 +      WSC_FRAG_ACK = 0x06
 +};
 +
 +struct wps_registrar;
 +struct upnp_wps_device_sm;
 +struct wps_er;
 +struct wps_parse_attr;
 +
 +/**
 + * struct wps_credential - WPS Credential
 + * @ssid: SSID
 + * @ssid_len: Length of SSID
 + * @auth_type: Authentication Type (WPS_AUTH_OPEN, .. flags)
 + * @encr_type: Encryption Type (WPS_ENCR_NONE, .. flags)
 + * @key_idx: Key index
 + * @key: Key
 + * @key_len: Key length in octets
 + * @mac_addr: MAC address of the Credential receiver
 + * @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
 + */
 +struct wps_credential {
-  * @rf_bands: RF bands (WPS_RF_24GHZ, WPS_RF_50GHZ flags)
++      u8 ssid[SSID_MAX_LEN];
 +      size_t ssid_len;
 +      u16 auth_type;
 +      u16 encr_type;
 +      u8 key_idx;
 +      u8 key[64];
 +      size_t key_len;
 +      u8 mac_addr[ETH_ALEN];
 +      const u8 *cred_attr;
 +      size_t cred_attr_len;
 +};
 +
 +#define WPS_DEV_TYPE_LEN 8
 +#define WPS_DEV_TYPE_BUFSIZE 21
 +#define WPS_SEC_DEV_TYPE_MAX_LEN 128
 +/* maximum number of advertised WPS vendor extension attributes */
 +#define MAX_WPS_VENDOR_EXTENSIONS 10
 +/* maximum size of WPS Vendor extension attribute */
 +#define WPS_MAX_VENDOR_EXT_LEN 1024
 +/* maximum number of parsed WPS vendor extension attributes */
 +#define MAX_WPS_PARSE_VENDOR_EXT 10
 +
 +/**
 + * struct wps_device_data - WPS Device Data
 + * @mac_addr: Device MAC address
 + * @device_name: Device Name (0..32 octets encoded in UTF-8)
 + * @manufacturer: Manufacturer (0..64 octets encoded in UTF-8)
 + * @model_name: Model Name (0..32 octets encoded in UTF-8)
 + * @model_number: Model Number (0..32 octets encoded in UTF-8)
 + * @serial_number: Serial Number (0..32 octets encoded in UTF-8)
 + * @pri_dev_type: Primary Device Type
 + * @sec_dev_type: Array of secondary device types
 + * @num_sec_dev_type: Number of secondary device types
 + * @os_version: OS Version
-       u8 ssid[32];
++ * @rf_bands: RF bands (WPS_RF_24GHZ, WPS_RF_50GHZ, WPS_RF_60GHZ flags)
 + * @p2p: Whether the device is a P2P device
 + */
 +struct wps_device_data {
 +      u8 mac_addr[ETH_ALEN];
 +      char *device_name;
 +      char *manufacturer;
 +      char *model_name;
 +      char *model_number;
 +      char *serial_number;
 +      u8 pri_dev_type[WPS_DEV_TYPE_LEN];
 +#define WPS_SEC_DEVICE_TYPES 5
 +      u8 sec_dev_type[WPS_SEC_DEVICE_TYPES][WPS_DEV_TYPE_LEN];
 +      u8 num_sec_dev_types;
 +      u32 os_version;
 +      u8 rf_bands;
 +      u16 config_methods;
 +      struct wpabuf *vendor_ext_m1;
 +      struct wpabuf *vendor_ext[MAX_WPS_VENDOR_EXTENSIONS];
 +
 +      int p2p;
 +};
 +
 +/**
 + * struct wps_config - WPS configuration for a single registration protocol run
 + */
 +struct wps_config {
 +      /**
 +       * wps - Pointer to long term WPS context
 +       */
 +      struct wps_context *wps;
 +
 +      /**
 +       * registrar - Whether this end is a Registrar
 +       */
 +      int registrar;
 +
 +      /**
 +       * pin - Enrollee Device Password (%NULL for Registrar or PBC)
 +       */
 +      const u8 *pin;
 +
 +      /**
 +       * pin_len - Length on pin in octets
 +       */
 +      size_t pin_len;
 +
 +      /**
 +       * pbc - Whether this is protocol run uses PBC
 +       */
 +      int pbc;
 +
 +      /**
 +       * assoc_wps_ie: (Re)AssocReq WPS IE (in AP; %NULL if not AP)
 +       */
 +      const struct wpabuf *assoc_wps_ie;
 +
 +      /**
 +       * new_ap_settings - New AP settings (%NULL if not used)
 +       *
 +       * This parameter provides new AP settings when using a wireless
 +       * stations as a Registrar to configure the AP. %NULL means that AP
 +       * will not be reconfigured, i.e., the station will only learn the
 +       * current AP settings by using AP PIN.
 +       */
 +      const struct wps_credential *new_ap_settings;
 +
 +      /**
 +       * peer_addr: MAC address of the peer in AP; %NULL if not AP
 +       */
 +      const u8 *peer_addr;
 +
 +      /**
 +       * use_psk_key - Use PSK format key in Credential
 +       *
 +       * Force PSK format to be used instead of ASCII passphrase when
 +       * building Credential for an Enrollee. The PSK value is set in
 +       * struct wpa_context::psk.
 +       */
 +      int use_psk_key;
 +
 +      /**
 +       * dev_pw_id - Device Password ID for Enrollee when PIN is used
 +       */
 +      u16 dev_pw_id;
 +
 +      /**
 +       * p2p_dev_addr - P2P Device Address from (Re)Association Request
 +       *
 +       * On AP/GO, this is set to the P2P Device Address of the associating
 +       * P2P client if a P2P IE is included in the (Re)Association Request
 +       * frame and the P2P Device Address is included. Otherwise, this is set
 +       * to %NULL to indicate the station does not have a P2P Device Address.
 +       */
 +      const u8 *p2p_dev_addr;
 +
 +      /**
 +       * pbc_in_m1 - Do not remove PushButton config method in M1 (AP)
 +       *
 +       * This can be used to enable a workaround to allow Windows 7 to use
 +       * 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);
 +
 +void wps_deinit(struct wps_data *data);
 +
 +/**
 + * enum wps_process_res - WPS message processing result
 + */
 +enum wps_process_res {
 +      /**
 +       * WPS_DONE - Processing done
 +       */
 +      WPS_DONE,
 +
 +      /**
 +       * WPS_CONTINUE - Processing continues
 +       */
 +      WPS_CONTINUE,
 +
 +      /**
 +       * WPS_FAILURE - Processing failed
 +       */
 +      WPS_FAILURE,
 +
 +      /**
 +       * WPS_PENDING - Processing continues, but waiting for an external
 +       *      event (e.g., UPnP message from an external Registrar)
 +       */
 +      WPS_PENDING
 +};
 +enum wps_process_res wps_process_msg(struct wps_data *wps,
 +                                   enum wsc_op_code op_code,
 +                                   const struct wpabuf *msg);
 +
 +struct wpabuf * wps_get_msg(struct wps_data *wps, enum wsc_op_code *op_code);
 +
 +int wps_is_selected_pbc_registrar(const struct wpabuf *msg);
 +int wps_is_selected_pin_registrar(const struct wpabuf *msg);
 +int wps_ap_priority_compar(const struct wpabuf *wps_a,
 +                         const struct wpabuf *wps_b);
 +int wps_is_addr_authorized(const struct wpabuf *msg, const u8 *addr,
 +                         int ver1_compat);
 +const u8 * wps_get_uuid_e(const struct wpabuf *msg);
 +int wps_is_20(const struct wpabuf *msg);
 +
 +struct wpabuf * wps_build_assoc_req_ie(enum wps_request_type req_type);
 +struct wpabuf * wps_build_assoc_resp_ie(void);
 +struct wpabuf * wps_build_probe_req_ie(u16 pw_id, struct wps_device_data *dev,
 +                                     const u8 *uuid,
 +                                     enum wps_request_type req_type,
 +                                     unsigned int num_req_dev_types,
 +                                     const u8 *req_dev_types);
 +
 +
 +/**
 + * struct wps_registrar_config - WPS Registrar configuration
 + */
 +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 *p2p_dev_addr,
 +                        const u8 *psk, size_t psk_len);
 +
 +      /**
 +       * set_ie_cb - Callback for WPS IE changes
 +       * @ctx: Higher layer context data (cb_ctx)
 +       * @beacon_ie: WPS IE for Beacon
 +       * @probe_resp_ie: WPS IE for Probe Response
 +       * Returns: 0 on success, -1 on failure
 +       *
 +       * This callback is called whenever the WPS IE in Beacon or Probe
 +       * Response frames needs to be changed (AP only). Callee is responsible
 +       * for freeing the buffers.
 +       */
 +      int (*set_ie_cb)(void *ctx, struct wpabuf *beacon_ie,
 +                       struct wpabuf *probe_resp_ie);
 +
 +      /**
 +       * pin_needed_cb - Callback for requesting a PIN
 +       * @ctx: Higher layer context data (cb_ctx)
 +       * @uuid_e: UUID-E of the unknown Enrollee
 +       * @dev: Device Data from the unknown Enrollee
 +       *
 +       * This callback is called whenever an unknown Enrollee requests to use
 +       * PIN method and a matching PIN (Device Password) is not found in
 +       * Registrar data.
 +       */
 +      void (*pin_needed_cb)(void *ctx, const u8 *uuid_e,
 +                            const struct wps_device_data *dev);
 +
 +      /**
 +       * reg_success_cb - Callback for reporting successful registration
 +       * @ctx: Higher layer context data (cb_ctx)
 +       * @mac_addr: MAC address of the Enrollee
 +       * @uuid_e: UUID-E of the Enrollee
 +       * @dev_pw: Device Password (PIN) used during registration
 +       * @dev_pw_len: Length of dev_pw in octets
 +       *
 +       * This callback is called whenever an Enrollee completes registration
 +       * successfully.
 +       */
 +      void (*reg_success_cb)(void *ctx, const u8 *mac_addr,
 +                             const u8 *uuid_e, const u8 *dev_pw,
 +                             size_t dev_pw_len);
 +
 +      /**
 +       * set_sel_reg_cb - Callback for reporting selected registrar changes
 +       * @ctx: Higher layer context data (cb_ctx)
 +       * @sel_reg: Whether the Registrar is selected
 +       * @dev_passwd_id: Device Password ID to indicate with method or
 +       *      specific password the Registrar intends to use
 +       * @sel_reg_config_methods: Bit field of active config methods
 +       *
 +       * This callback is called whenever the Selected Registrar state
 +       * changes (e.g., a new PIN becomes available or PBC is invoked). This
 +       * callback is only used by External Registrar implementation;
 +       * set_ie_cb() is used by AP implementation in similar caes, but it
 +       * provides the full WPS IE data instead of just the minimal Registrar
 +       * state information.
 +       */
 +      void (*set_sel_reg_cb)(void *ctx, int sel_reg, u16 dev_passwd_id,
 +                             u16 sel_reg_config_methods);
 +
 +      /**
 +       * enrollee_seen_cb - Callback for reporting Enrollee based on ProbeReq
 +       * @ctx: Higher layer context data (cb_ctx)
 +       * @addr: MAC address of the Enrollee
 +       * @uuid_e: UUID of the Enrollee
 +       * @pri_dev_type: Primary device type
 +       * @config_methods: Config Methods
 +       * @dev_password_id: Device Password ID
 +       * @request_type: Request Type
 +       * @dev_name: Device Name (if available)
 +       */
 +      void (*enrollee_seen_cb)(void *ctx, const u8 *addr, const u8 *uuid_e,
 +                               const u8 *pri_dev_type, u16 config_methods,
 +                               u16 dev_password_id, u8 request_type,
 +                               const char *dev_name);
 +
 +      /**
 +       * cb_ctx: Higher layer context data for Registrar callbacks
 +       */
 +      void *cb_ctx;
 +
 +      /**
 +       * skip_cred_build: Do not build credential
 +       *
 +       * This option can be used to disable internal code that builds
 +       * Credential attribute into M8 based on the current network
 +       * configuration and Enrollee capabilities. The extra_cred data will
 +       * then be used as the Credential(s).
 +       */
 +      int skip_cred_build;
 +
 +      /**
 +       * extra_cred: Additional Credential attribute(s)
 +       *
 +       * This optional data (set to %NULL to disable) can be used to add
 +       * Credential attribute(s) for other networks into M8. If
 +       * skip_cred_build is set, this will also override the automatically
 +       * generated Credential attribute.
 +       */
 +      const u8 *extra_cred;
 +
 +      /**
 +       * extra_cred_len: Length of extra_cred in octets
 +       */
 +      size_t extra_cred_len;
 +
 +      /**
 +       * disable_auto_conf - Disable auto-configuration on first registration
 +       *
 +       * By default, the AP that is started in not configured state will
 +       * generate a random PSK and move to configured state when the first
 +       * registration protocol run is completed successfully. This option can
 +       * be used to disable this functionality and leave it up to an external
 +       * program to take care of configuration. This requires the extra_cred
 +       * to be set with a suitable Credential and skip_cred_build being used.
 +       */
 +      int disable_auto_conf;
 +
 +      /**
 +       * static_wep_only - Whether the BSS supports only static WEP
 +       */
 +      int static_wep_only;
 +
 +      /**
 +       * 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;
 +};
 +
 +
 +/**
 + * enum wps_event - WPS event types
 + */
 +enum wps_event {
 +      /**
 +       * WPS_EV_M2D - M2D received (Registrar did not know us)
 +       */
 +      WPS_EV_M2D,
 +
 +      /**
 +       * WPS_EV_FAIL - Registration failed
 +       */
 +      WPS_EV_FAIL,
 +
 +      /**
 +       * WPS_EV_SUCCESS - Registration succeeded
 +       */
 +      WPS_EV_SUCCESS,
 +
 +      /**
 +       * WPS_EV_PWD_AUTH_FAIL - Password authentication failed
 +       */
 +      WPS_EV_PWD_AUTH_FAIL,
 +
 +      /**
 +       * WPS_EV_PBC_OVERLAP - PBC session overlap detected
 +       */
 +      WPS_EV_PBC_OVERLAP,
 +
 +      /**
 +       * WPS_EV_PBC_TIMEOUT - PBC walktime expired before protocol run start
 +       */
 +      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,
 +
 +      /**
 +       * WPS_EV_ER_AP_REMOVE - ER: AP removed
 +       */
 +      WPS_EV_ER_AP_REMOVE,
 +
 +      /**
 +       * WPS_EV_ER_ENROLLEE_ADD - ER: Enrollee added
 +       */
 +      WPS_EV_ER_ENROLLEE_ADD,
 +
 +      /**
 +       * WPS_EV_ER_ENROLLEE_REMOVE - ER: Enrollee removed
 +       */
 +      WPS_EV_ER_ENROLLEE_REMOVE,
 +
 +      /**
 +       * WPS_EV_ER_AP_SETTINGS - ER: AP Settings learned
 +       */
 +      WPS_EV_ER_AP_SETTINGS,
 +
 +      /**
 +       * WPS_EV_ER_SET_SELECTED_REGISTRAR - ER: SetSelectedRegistrar event
 +       */
 +      WPS_EV_ER_SET_SELECTED_REGISTRAR,
 +
 +      /**
 +       * WPS_EV_AP_PIN_SUCCESS - External Registrar used correct AP PIN
 +       */
 +      WPS_EV_AP_PIN_SUCCESS
 +};
 +
 +/**
 + * union wps_event_data - WPS event data
 + */
 +union wps_event_data {
 +      /**
 +       * struct wps_event_m2d - M2D event data
 +       */
 +      struct wps_event_m2d {
 +              u16 config_methods;
 +              const u8 *manufacturer;
 +              size_t manufacturer_len;
 +              const u8 *model_name;
 +              size_t model_name_len;
 +              const u8 *model_number;
 +              size_t model_number_len;
 +              const u8 *serial_number;
 +              size_t serial_number_len;
 +              const u8 *dev_name;
 +              size_t dev_name_len;
 +              const u8 *primary_dev_type; /* 8 octets */
 +              u16 config_error;
 +              u16 dev_password_id;
 +      } m2d;
 +
 +      /**
 +       * struct wps_event_fail - Registration failure information
 +       * @msg: enum wps_msg_type
 +       */
 +      struct wps_event_fail {
 +              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 {
 +              const u8 *uuid;
 +              const u8 *mac_addr;
 +              const char *friendly_name;
 +              const char *manufacturer;
 +              const char *manufacturer_url;
 +              const char *model_description;
 +              const char *model_name;
 +              const char *model_number;
 +              const char *model_url;
 +              const char *serial_number;
 +              const char *upc;
 +              const u8 *pri_dev_type;
 +              u8 wps_state;
 +      } ap;
 +
 +      struct wps_event_er_enrollee {
 +              const u8 *uuid;
 +              const u8 *mac_addr;
 +              int m1_received;
 +              u16 config_methods;
 +              u16 dev_passwd_id;
 +              const u8 *pri_dev_type;
 +              const char *dev_name;
 +              const char *manufacturer;
 +              const char *model_name;
 +              const char *model_number;
 +              const char *serial_number;
 +      } enrollee;
 +
 +      struct wps_event_er_ap_settings {
 +              const u8 *uuid;
 +              const struct wps_credential *cred;
 +      } ap_settings;
 +
 +      struct wps_event_er_set_selected_registrar {
 +              const u8 *uuid;
 +              int sel_reg;
 +              u16 dev_passwd_id;
 +              u16 sel_reg_config_methods;
 +              enum {
 +                      WPS_ER_SET_SEL_REG_START,
 +                      WPS_ER_SET_SEL_REG_DONE,
 +                      WPS_ER_SET_SEL_REG_FAILED
 +              } state;
 +      } set_sel_reg;
 +};
 +
 +/**
 + * struct upnp_pending_message - Pending PutWLANResponse messages
 + * @next: Pointer to next pending message or %NULL
 + * @addr: NewWLANEventMAC
 + * @msg: NewMessage
 + * @type: Message Type
 + */
 +struct upnp_pending_message {
 +      struct upnp_pending_message *next;
 +      u8 addr[ETH_ALEN];
 +      struct wpabuf *msg;
 +      enum wps_msg_type type;
 +};
 +
 +/**
 + * struct wps_context - Long term WPS context data
 + *
 + * This data is stored at the higher layer Authenticator or Supplicant data
 + * structures and it is maintained over multiple registration protocol runs.
 + */
 +struct wps_context {
 +      /**
 +       * ap - Whether the local end is an access point
 +       */
 +      int ap;
 +
 +      /**
 +       * registrar - Pointer to WPS registrar data from wps_registrar_init()
 +       */
 +      struct wps_registrar *registrar;
 +
 +      /**
 +       * wps_state - Current WPS state
 +       */
 +      enum wps_state wps_state;
 +
 +      /**
 +       * ap_setup_locked - Whether AP setup is locked (only used at AP)
 +       */
 +      int ap_setup_locked;
 +
 +      /**
 +       * uuid - Own UUID
 +       */
 +      u8 uuid[16];
 +
 +      /**
 +       * ssid - SSID
 +       *
 +       * This SSID is used by the Registrar to fill in information for
 +       * Credentials. In addition, AP uses it when acting as an Enrollee to
 +       * notify Registrar of the current configuration.
 +       */
++      u8 ssid[SSID_MAX_LEN];
 +
 +      /**
 +       * ssid_len - Length of ssid in octets
 +       */
 +      size_t ssid_len;
 +
 +      /**
 +       * dev - Own WPS device data
 +       */
 +      struct wps_device_data dev;
 +
 +      /**
 +       * dh_ctx - Context data for Diffie-Hellman operation
 +       */
 +      void *dh_ctx;
 +
 +      /**
 +       * dh_privkey - Diffie-Hellman private key
 +       */
 +      struct wpabuf *dh_privkey;
 +
 +      /**
 +       * dh_pubkey_oob - Diffie-Hellman public key
 +       */
 +      struct wpabuf *dh_pubkey;
 +
 +      /**
 +       * config_methods - Enabled configuration methods
 +       *
 +       * Bit field of WPS_CONFIG_*
 +       */
 +      u16 config_methods;
 +
 +      /**
 +       * encr_types - Enabled encryption types (bit field of WPS_ENCR_*)
 +       */
 +      u16 encr_types;
 +
 +      /**
 +       * auth_types - Authentication types (bit field of WPS_AUTH_*)
 +       */
 +      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
 +       * uses this when acting as an Enrollee to notify Registrar of the
 +       * current configuration.
 +       *
 +       * When using WPA/WPA2-Person, this key can be either the ASCII
 +       * passphrase (8..63 characters) or the 32-octet PSK (64 hex
 +       * characters). When this is set to the ASCII passphrase, the PSK can
 +       * be provided in the psk buffer and used per-Enrollee to control which
 +       * key type is included in the Credential (e.g., to reduce calculation
 +       * need on low-powered devices by provisioning PSK while still allowing
 +       * other devices to get the passphrase).
 +       */
 +      u8 *network_key;
 +
 +      /**
 +       * network_key_len - Length of network_key in octets
 +       */
 +      size_t network_key_len;
 +
 +      /**
 +       * psk - The current network PSK
 +       *
 +       * This optional value can be used to provide the current PSK if
 +       * network_key is set to the ASCII passphrase.
 +       */
 +      u8 psk[32];
 +
 +      /**
 +       * psk_set - Whether psk value is set
 +       */
 +      int psk_set;
 +
 +      /**
 +       * ap_settings - AP Settings override for M7 (only used at AP)
 +       *
 +       * If %NULL, AP Settings attributes will be generated based on the
 +       * current network configuration.
 +       */
 +      u8 *ap_settings;
 +
 +      /**
 +       * ap_settings_len - Length of ap_settings in octets
 +       */
 +      size_t ap_settings_len;
 +
 +      /**
 +       * friendly_name - Friendly Name (required for UPnP)
 +       */
 +      char *friendly_name;
 +
 +      /**
 +       * manufacturer_url - Manufacturer URL (optional for UPnP)
 +       */
 +      char *manufacturer_url;
 +
 +      /**
 +       * model_description - Model Description (recommended for UPnP)
 +       */
 +      char *model_description;
 +
 +      /**
 +       * model_url - Model URL (optional for UPnP)
 +       */
 +      char *model_url;
 +
 +      /**
 +       * upc - Universal Product Code (optional for UPnP)
 +       */
 +      char *upc;
 +
 +      /**
 +       * cred_cb - Callback to notify that new Credentials were received
 +       * @ctx: Higher layer context data (cb_ctx)
 +       * @cred: The received Credential
 +       * Return: 0 on success, -1 on failure
 +       */
 +      int (*cred_cb)(void *ctx, const struct wps_credential *cred);
 +
 +      /**
 +       * event_cb - Event callback (state information about progress)
 +       * @ctx: Higher layer context data (cb_ctx)
 +       * @event: Event type
 +       * @data: Event data
 +       */
 +      void (*event_cb)(void *ctx, enum wps_event event,
 +                       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;
 +
 +      struct upnp_wps_device_sm *wps_upnp;
 +
 +      /* Pending messages from UPnP PutWLANResponse */
 +      struct upnp_pending_message *upnp_msgs;
 +
 +      u16 ap_nfc_dev_pw_id;
 +      struct wpabuf *ap_nfc_dh_pubkey;
 +      struct wpabuf *ap_nfc_dh_privkey;
 +      struct wpabuf *ap_nfc_dev_pw;
 +};
 +
 +struct wps_registrar *
 +wps_registrar_init(struct wps_context *wps,
 +                 const struct wps_registrar_config *cfg);
 +void wps_registrar_deinit(struct wps_registrar *reg);
 +int wps_registrar_add_pin(struct wps_registrar *reg, const u8 *addr,
 +                        const u8 *uuid, const u8 *pin, size_t pin_len,
 +                        int timeout);
 +int wps_registrar_invalidate_pin(struct wps_registrar *reg, const u8 *uuid);
 +int wps_registrar_wps_cancel(struct wps_registrar *reg);
 +int wps_registrar_unlock_pin(struct wps_registrar *reg, const u8 *uuid);
 +int wps_registrar_button_pushed(struct wps_registrar *reg,
 +                              const u8 *p2p_dev_addr);
 +void wps_registrar_complete(struct wps_registrar *registrar, const u8 *uuid_e,
 +                          const u8 *dev_pw, size_t dev_pw_len);
 +void wps_registrar_probe_req_rx(struct wps_registrar *reg, const u8 *addr,
 +                              const struct wpabuf *wps_data,
 +                              int p2p_wildcard);
 +int wps_registrar_update_ie(struct wps_registrar *reg);
 +int wps_registrar_get_info(struct wps_registrar *reg, const u8 *addr,
 +                         char *buf, size_t buflen);
 +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,
 +                                 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);
 +
 +unsigned int wps_pin_checksum(unsigned int pin);
 +unsigned int wps_pin_valid(unsigned int pin);
 +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, 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);
 +void wps_er_refresh(struct wps_er *er);
 +void wps_er_deinit(struct wps_er *er, void (*cb)(void *ctx), void *ctx);
 +void wps_er_set_sel_reg(struct wps_er *er, int sel_reg, u16 dev_passwd_id,
 +                      u16 sel_reg_config_methods);
 +int wps_er_pbc(struct wps_er *er, const u8 *uuid, const u8 *addr);
 +const u8 * wps_er_get_sta_uuid(struct wps_er *er, const u8 *addr);
 +int wps_er_learn(struct wps_er *er, const u8 *uuid, const u8 *addr,
 +               const u8 *pin, size_t pin_len);
 +int wps_er_set_config(struct wps_er *er, const u8 *uuid, const u8 *addr,
 +                    const struct wps_credential *cred);
 +int wps_er_config(struct wps_er *er, const u8 *uuid, const u8 *addr,
 +                const u8 *pin, size_t pin_len,
 +                const struct wps_credential *cred);
 +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,
 +                          size_t buf_len);
 +void uuid_gen_mac_addr(const u8 *mac_addr, u8 *uuid);
 +u16 wps_config_methods_str2bin(const char *str);
 +struct wpabuf * wps_build_nfc_pw_token(u16 dev_pw_id,
 +                                     const struct wpabuf *pubkey,
 +                                     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_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);
 +int wps_validate_beacon_probe_resp(const struct wpabuf *wps_ie, int probe,
 +                                 const u8 *addr);
 +int wps_validate_probe_req(const struct wpabuf *wps_ie, const u8 *addr);
 +int wps_validate_assoc_req(const struct wpabuf *wps_ie);
 +int wps_validate_assoc_resp(const struct wpabuf *wps_ie);
 +int wps_validate_m1(const struct wpabuf *tlvs);
 +int wps_validate_m2(const struct wpabuf *tlvs);
 +int wps_validate_m2d(const struct wpabuf *tlvs);
 +int wps_validate_m3(const struct wpabuf *tlvs);
 +int wps_validate_m4(const struct wpabuf *tlvs);
 +int wps_validate_m4_encr(const struct wpabuf *tlvs, int wps2);
 +int wps_validate_m5(const struct wpabuf *tlvs);
 +int wps_validate_m5_encr(const struct wpabuf *tlvs, int wps2);
 +int wps_validate_m6(const struct wpabuf *tlvs);
 +int wps_validate_m6_encr(const struct wpabuf *tlvs, int wps2);
 +int wps_validate_m7(const struct wpabuf *tlvs);
 +int wps_validate_m7_encr(const struct wpabuf *tlvs, int ap, int wps2);
 +int wps_validate_m8(const struct wpabuf *tlvs);
 +int wps_validate_m8_encr(const struct wpabuf *tlvs, int ap, int wps2);
 +int wps_validate_wsc_ack(const struct wpabuf *tlvs);
 +int wps_validate_wsc_nack(const struct wpabuf *tlvs);
 +int wps_validate_wsc_done(const struct wpabuf *tlvs);
 +int wps_validate_upnp_set_selected_registrar(const struct wpabuf *tlvs);
 +#else /* CONFIG_WPS_STRICT */
 +static inline int wps_validate_beacon(const struct wpabuf *wps_ie){
 +      return 0;
 +}
 +
 +static inline int wps_validate_beacon_probe_resp(const struct wpabuf *wps_ie,
 +                                               int probe, const u8 *addr)
 +{
 +      return 0;
 +}
 +
 +static inline int wps_validate_probe_req(const struct wpabuf *wps_ie,
 +                                       const u8 *addr)
 +{
 +      return 0;
 +}
 +
 +static inline int wps_validate_assoc_req(const struct wpabuf *wps_ie)
 +{
 +      return 0;
 +}
 +
 +static inline int wps_validate_assoc_resp(const struct wpabuf *wps_ie)
 +{
 +      return 0;
 +}
 +
 +static inline int wps_validate_m1(const struct wpabuf *tlvs)
 +{
 +      return 0;
 +}
 +
 +static inline int wps_validate_m2(const struct wpabuf *tlvs)
 +{
 +      return 0;
 +}
 +
 +static inline int wps_validate_m2d(const struct wpabuf *tlvs)
 +{
 +      return 0;
 +}
 +
 +static inline int wps_validate_m3(const struct wpabuf *tlvs)
 +{
 +      return 0;
 +}
 +
 +static inline int wps_validate_m4(const struct wpabuf *tlvs)
 +{
 +      return 0;
 +}
 +
 +static inline int wps_validate_m4_encr(const struct wpabuf *tlvs, int wps2)
 +{
 +      return 0;
 +}
 +
 +static inline int wps_validate_m5(const struct wpabuf *tlvs)
 +{
 +      return 0;
 +}
 +
 +static inline int wps_validate_m5_encr(const struct wpabuf *tlvs, int wps2)
 +{
 +      return 0;
 +}
 +
 +static inline int wps_validate_m6(const struct wpabuf *tlvs)
 +{
 +      return 0;
 +}
 +
 +static inline int wps_validate_m6_encr(const struct wpabuf *tlvs, int wps2)
 +{
 +      return 0;
 +}
 +
 +static inline int wps_validate_m7(const struct wpabuf *tlvs)
 +{
 +      return 0;
 +}
 +
 +static inline int wps_validate_m7_encr(const struct wpabuf *tlvs, int ap,
 +                                     int wps2)
 +{
 +      return 0;
 +}
 +
 +static inline int wps_validate_m8(const struct wpabuf *tlvs)
 +{
 +      return 0;
 +}
 +
 +static inline int wps_validate_m8_encr(const struct wpabuf *tlvs, int ap,
 +                                     int wps2)
 +{
 +      return 0;
 +}
 +
 +static inline int wps_validate_wsc_ack(const struct wpabuf *tlvs)
 +{
 +      return 0;
 +}
 +
 +static inline int wps_validate_wsc_nack(const struct wpabuf *tlvs)
 +{
 +      return 0;
 +}
 +
 +static inline int wps_validate_wsc_done(const struct wpabuf *tlvs)
 +{
 +      return 0;
 +}
 +
 +static inline int wps_validate_upnp_set_selected_registrar(
 +      const struct wpabuf *tlvs)
 +{
 +      return 0;
 +}
 +#endif /* CONFIG_WPS_STRICT */
 +
 +#endif /* WPS_H */
index 40bc1ad2d2c5bd665669e138775d4061b72454f8,0000000000000000000000000000000000000000..11a967ba0ef182f2a7ddc882b160f7425f6d5d50
mode 100644,000000..100644
--- /dev/null
@@@ -1,628 -1,0 +1,663 @@@
-               attr->manufacturer_len = len;
 +/*
 + * Wi-Fi Protected Setup - attribute parsing
 + * Copyright (c) 2008, 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 "wps_defs.h"
 +#include "wps_attr_parse.h"
 +
 +#ifndef CONFIG_WPS_STRICT
 +#define WPS_WORKAROUNDS
 +#endif /* CONFIG_WPS_STRICT */
 +
 +
 +static int wps_set_vendor_ext_wfa_subelem(struct wps_parse_attr *attr,
 +                                        u8 id, u8 len, const u8 *pos)
 +{
 +      wpa_printf(MSG_EXCESSIVE, "WPS: WFA subelement id=%u len=%u",
 +                 id, len);
 +      switch (id) {
 +      case WFA_ELEM_VERSION2:
 +              if (len != 1) {
 +                      wpa_printf(MSG_DEBUG, "WPS: Invalid Version2 length "
 +                                 "%u", len);
 +                      return -1;
 +              }
 +              attr->version2 = pos;
 +              break;
 +      case WFA_ELEM_AUTHORIZEDMACS:
 +              attr->authorized_macs = pos;
 +              attr->authorized_macs_len = len;
 +              break;
 +      case WFA_ELEM_NETWORK_KEY_SHAREABLE:
 +              if (len != 1) {
 +                      wpa_printf(MSG_DEBUG, "WPS: Invalid Network Key "
 +                                 "Shareable length %u", len);
 +                      return -1;
 +              }
 +              attr->network_key_shareable = pos;
 +              break;
 +      case WFA_ELEM_REQUEST_TO_ENROLL:
 +              if (len != 1) {
 +                      wpa_printf(MSG_DEBUG, "WPS: Invalid Request to Enroll "
 +                                 "length %u", len);
 +                      return -1;
 +              }
 +              attr->request_to_enroll = pos;
 +              break;
 +      case WFA_ELEM_SETTINGS_DELAY_TIME:
 +              if (len != 1) {
 +                      wpa_printf(MSG_DEBUG, "WPS: Invalid Settings Delay "
 +                                 "Time length %u", len);
 +                      return -1;
 +              }
 +              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);
 +              break;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +static int wps_parse_vendor_ext_wfa(struct wps_parse_attr *attr, const u8 *pos,
 +                                  u16 len)
 +{
 +      const u8 *end = pos + len;
 +      u8 id, elen;
 +
 +      while (pos + 2 <= end) {
 +              id = *pos++;
 +              elen = *pos++;
 +              if (pos + elen > end)
 +                      break;
 +              if (wps_set_vendor_ext_wfa_subelem(attr, id, elen, pos) < 0)
 +                      return -1;
 +              pos += elen;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +static int wps_parse_vendor_ext(struct wps_parse_attr *attr, const u8 *pos,
 +                              u16 len)
 +{
 +      u32 vendor_id;
 +
 +      if (len < 3) {
 +              wpa_printf(MSG_DEBUG, "WPS: Skip invalid Vendor Extension");
 +              return 0;
 +      }
 +
 +      vendor_id = WPA_GET_BE24(pos);
 +      switch (vendor_id) {
 +      case WPS_VENDOR_ID_WFA:
 +              return wps_parse_vendor_ext_wfa(attr, pos + 3, len - 3);
 +      }
 +
 +      /* Handle unknown vendor extensions */
 +
 +      wpa_printf(MSG_MSGDUMP, "WPS: Unknown Vendor Extension (Vendor ID %u)",
 +                 vendor_id);
 +
 +      if (len > WPS_MAX_VENDOR_EXT_LEN) {
 +              wpa_printf(MSG_DEBUG, "WPS: Too long Vendor Extension (%u)",
 +                         len);
 +              return -1;
 +      }
 +
 +      if (attr->num_vendor_ext >= MAX_WPS_PARSE_VENDOR_EXT) {
 +              wpa_printf(MSG_DEBUG, "WPS: Skipped Vendor Extension "
 +                         "attribute (max %d vendor extensions)",
 +                         MAX_WPS_PARSE_VENDOR_EXT);
 +              return -1;
 +      }
 +      attr->vendor_ext[attr->num_vendor_ext] = pos;
 +      attr->vendor_ext_len[attr->num_vendor_ext] = len;
 +      attr->num_vendor_ext++;
 +
 +      return 0;
 +}
 +
 +
 +static int wps_set_attr(struct wps_parse_attr *attr, u16 type,
 +                      const u8 *pos, u16 len)
 +{
 +      switch (type) {
 +      case ATTR_VERSION:
 +              if (len != 1) {
 +                      wpa_printf(MSG_DEBUG, "WPS: Invalid Version length %u",
 +                                 len);
 +                      return -1;
 +              }
 +              attr->version = pos;
 +              break;
 +      case ATTR_MSG_TYPE:
 +              if (len != 1) {
 +                      wpa_printf(MSG_DEBUG, "WPS: Invalid Message Type "
 +                                 "length %u", len);
 +                      return -1;
 +              }
 +              attr->msg_type = pos;
 +              break;
 +      case ATTR_ENROLLEE_NONCE:
 +              if (len != WPS_NONCE_LEN) {
 +                      wpa_printf(MSG_DEBUG, "WPS: Invalid Enrollee Nonce "
 +                                 "length %u", len);
 +                      return -1;
 +              }
 +              attr->enrollee_nonce = pos;
 +              break;
 +      case ATTR_REGISTRAR_NONCE:
 +              if (len != WPS_NONCE_LEN) {
 +                      wpa_printf(MSG_DEBUG, "WPS: Invalid Registrar Nonce "
 +                                 "length %u", len);
 +                      return -1;
 +              }
 +              attr->registrar_nonce = pos;
 +              break;
 +      case ATTR_UUID_E:
 +              if (len != WPS_UUID_LEN) {
 +                      wpa_printf(MSG_DEBUG, "WPS: Invalid UUID-E length %u",
 +                                 len);
 +                      return -1;
 +              }
 +              attr->uuid_e = pos;
 +              break;
 +      case ATTR_UUID_R:
 +              if (len != WPS_UUID_LEN) {
 +                      wpa_printf(MSG_DEBUG, "WPS: Invalid UUID-R length %u",
 +                                 len);
 +                      return -1;
 +              }
 +              attr->uuid_r = pos;
 +              break;
 +      case ATTR_AUTH_TYPE_FLAGS:
 +              if (len != 2) {
 +                      wpa_printf(MSG_DEBUG, "WPS: Invalid Authentication "
 +                                 "Type Flags length %u", len);
 +                      return -1;
 +              }
 +              attr->auth_type_flags = pos;
 +              break;
 +      case ATTR_ENCR_TYPE_FLAGS:
 +              if (len != 2) {
 +                      wpa_printf(MSG_DEBUG, "WPS: Invalid Encryption Type "
 +                                 "Flags length %u", len);
 +                      return -1;
 +              }
 +              attr->encr_type_flags = pos;
 +              break;
 +      case ATTR_CONN_TYPE_FLAGS:
 +              if (len != 1) {
 +                      wpa_printf(MSG_DEBUG, "WPS: Invalid Connection Type "
 +                                 "Flags length %u", len);
 +                      return -1;
 +              }
 +              attr->conn_type_flags = pos;
 +              break;
 +      case ATTR_CONFIG_METHODS:
 +              if (len != 2) {
 +                      wpa_printf(MSG_DEBUG, "WPS: Invalid Config Methods "
 +                                 "length %u", len);
 +                      return -1;
 +              }
 +              attr->config_methods = pos;
 +              break;
 +      case ATTR_SELECTED_REGISTRAR_CONFIG_METHODS:
 +              if (len != 2) {
 +                      wpa_printf(MSG_DEBUG, "WPS: Invalid Selected "
 +                                 "Registrar Config Methods length %u", len);
 +                      return -1;
 +              }
 +              attr->sel_reg_config_methods = pos;
 +              break;
 +      case ATTR_PRIMARY_DEV_TYPE:
 +              if (len != WPS_DEV_TYPE_LEN) {
 +                      wpa_printf(MSG_DEBUG, "WPS: Invalid Primary Device "
 +                                 "Type length %u", len);
 +                      return -1;
 +              }
 +              attr->primary_dev_type = pos;
 +              break;
 +      case ATTR_RF_BANDS:
 +              if (len != 1) {
 +                      wpa_printf(MSG_DEBUG, "WPS: Invalid RF Bands length "
 +                                 "%u", len);
 +                      return -1;
 +              }
 +              attr->rf_bands = pos;
 +              break;
 +      case ATTR_ASSOC_STATE:
 +              if (len != 2) {
 +                      wpa_printf(MSG_DEBUG, "WPS: Invalid Association State "
 +                                 "length %u", len);
 +                      return -1;
 +              }
 +              attr->assoc_state = pos;
 +              break;
 +      case ATTR_CONFIG_ERROR:
 +              if (len != 2) {
 +                      wpa_printf(MSG_DEBUG, "WPS: Invalid Configuration "
 +                                 "Error length %u", len);
 +                      return -1;
 +              }
 +              attr->config_error = pos;
 +              break;
 +      case ATTR_DEV_PASSWORD_ID:
 +              if (len != 2) {
 +                      wpa_printf(MSG_DEBUG, "WPS: Invalid Device Password "
 +                                 "ID length %u", len);
 +                      return -1;
 +              }
 +              attr->dev_password_id = pos;
 +              break;
 +      case ATTR_OOB_DEVICE_PASSWORD:
 +              if (len < WPS_OOB_PUBKEY_HASH_LEN + 2 ||
 +                  len > WPS_OOB_PUBKEY_HASH_LEN + 2 +
 +                  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;
 +              }
 +              attr->oob_dev_password = pos;
 +              attr->oob_dev_password_len = len;
 +              break;
 +      case ATTR_OS_VERSION:
 +              if (len != 4) {
 +                      wpa_printf(MSG_DEBUG, "WPS: Invalid OS Version length "
 +                                 "%u", len);
 +                      return -1;
 +              }
 +              attr->os_version = pos;
 +              break;
 +      case ATTR_WPS_STATE:
 +              if (len != 1) {
 +                      wpa_printf(MSG_DEBUG, "WPS: Invalid Wi-Fi Protected "
 +                                 "Setup State length %u", len);
 +                      return -1;
 +              }
 +              attr->wps_state = pos;
 +              break;
 +      case ATTR_AUTHENTICATOR:
 +              if (len != WPS_AUTHENTICATOR_LEN) {
 +                      wpa_printf(MSG_DEBUG, "WPS: Invalid Authenticator "
 +                                 "length %u", len);
 +                      return -1;
 +              }
 +              attr->authenticator = pos;
 +              break;
 +      case ATTR_R_HASH1:
 +              if (len != WPS_HASH_LEN) {
 +                      wpa_printf(MSG_DEBUG, "WPS: Invalid R-Hash1 length %u",
 +                                 len);
 +                      return -1;
 +              }
 +              attr->r_hash1 = pos;
 +              break;
 +      case ATTR_R_HASH2:
 +              if (len != WPS_HASH_LEN) {
 +                      wpa_printf(MSG_DEBUG, "WPS: Invalid R-Hash2 length %u",
 +                                 len);
 +                      return -1;
 +              }
 +              attr->r_hash2 = pos;
 +              break;
 +      case ATTR_E_HASH1:
 +              if (len != WPS_HASH_LEN) {
 +                      wpa_printf(MSG_DEBUG, "WPS: Invalid E-Hash1 length %u",
 +                                 len);
 +                      return -1;
 +              }
 +              attr->e_hash1 = pos;
 +              break;
 +      case ATTR_E_HASH2:
 +              if (len != WPS_HASH_LEN) {
 +                      wpa_printf(MSG_DEBUG, "WPS: Invalid E-Hash2 length %u",
 +                                 len);
 +                      return -1;
 +              }
 +              attr->e_hash2 = pos;
 +              break;
 +      case ATTR_R_SNONCE1:
 +              if (len != WPS_SECRET_NONCE_LEN) {
 +                      wpa_printf(MSG_DEBUG, "WPS: Invalid R-SNonce1 length "
 +                                 "%u", len);
 +                      return -1;
 +              }
 +              attr->r_snonce1 = pos;
 +              break;
 +      case ATTR_R_SNONCE2:
 +              if (len != WPS_SECRET_NONCE_LEN) {
 +                      wpa_printf(MSG_DEBUG, "WPS: Invalid R-SNonce2 length "
 +                                 "%u", len);
 +                      return -1;
 +              }
 +              attr->r_snonce2 = pos;
 +              break;
 +      case ATTR_E_SNONCE1:
 +              if (len != WPS_SECRET_NONCE_LEN) {
 +                      wpa_printf(MSG_DEBUG, "WPS: Invalid E-SNonce1 length "
 +                                 "%u", len);
 +                      return -1;
 +              }
 +              attr->e_snonce1 = pos;
 +              break;
 +      case ATTR_E_SNONCE2:
 +              if (len != WPS_SECRET_NONCE_LEN) {
 +                      wpa_printf(MSG_DEBUG, "WPS: Invalid E-SNonce2 length "
 +                                 "%u", len);
 +                      return -1;
 +              }
 +              attr->e_snonce2 = pos;
 +              break;
 +      case ATTR_KEY_WRAP_AUTH:
 +              if (len != WPS_KWA_LEN) {
 +                      wpa_printf(MSG_DEBUG, "WPS: Invalid Key Wrap "
 +                                 "Authenticator length %u", len);
 +                      return -1;
 +              }
 +              attr->key_wrap_auth = pos;
 +              break;
 +      case ATTR_AUTH_TYPE:
 +              if (len != 2) {
 +                      wpa_printf(MSG_DEBUG, "WPS: Invalid Authentication "
 +                                 "Type length %u", len);
 +                      return -1;
 +              }
 +              attr->auth_type = pos;
 +              break;
 +      case ATTR_ENCR_TYPE:
 +              if (len != 2) {
 +                      wpa_printf(MSG_DEBUG, "WPS: Invalid Encryption "
 +                                 "Type length %u", len);
 +                      return -1;
 +              }
 +              attr->encr_type = pos;
 +              break;
 +      case ATTR_NETWORK_INDEX:
 +              if (len != 1) {
 +                      wpa_printf(MSG_DEBUG, "WPS: Invalid Network Index "
 +                                 "length %u", len);
 +                      return -1;
 +              }
 +              attr->network_idx = pos;
 +              break;
 +      case ATTR_NETWORK_KEY_INDEX:
 +              if (len != 1) {
 +                      wpa_printf(MSG_DEBUG, "WPS: Invalid Network Key Index "
 +                                 "length %u", len);
 +                      return -1;
 +              }
 +              attr->network_key_idx = pos;
 +              break;
 +      case ATTR_MAC_ADDR:
 +              if (len != ETH_ALEN) {
 +                      wpa_printf(MSG_DEBUG, "WPS: Invalid MAC Address "
 +                                 "length %u", len);
 +                      return -1;
 +              }
 +              attr->mac_addr = pos;
 +              break;
 +      case ATTR_SELECTED_REGISTRAR:
 +              if (len != 1) {
 +                      wpa_printf(MSG_DEBUG, "WPS: Invalid Selected Registrar"
 +                                 " length %u", len);
 +                      return -1;
 +              }
 +              attr->selected_registrar = pos;
 +              break;
 +      case ATTR_REQUEST_TYPE:
 +              if (len != 1) {
 +                      wpa_printf(MSG_DEBUG, "WPS: Invalid Request Type "
 +                                 "length %u", len);
 +                      return -1;
 +              }
 +              attr->request_type = pos;
 +              break;
 +      case ATTR_RESPONSE_TYPE:
 +              if (len != 1) {
 +                      wpa_printf(MSG_DEBUG, "WPS: Invalid Response Type "
 +                                 "length %u", len);
 +                      return -1;
 +              }
 +              attr->response_type = pos;
 +              break;
 +      case ATTR_MANUFACTURER:
 +              attr->manufacturer = pos;
-               attr->model_name_len = len;
++              if (len > WPS_MANUFACTURER_MAX_LEN)
++                      attr->manufacturer_len = WPS_MANUFACTURER_MAX_LEN;
++              else
++                      attr->manufacturer_len = len;
 +              break;
 +      case ATTR_MODEL_NAME:
 +              attr->model_name = pos;
-               attr->model_number_len = len;
++              if (len > WPS_MODEL_NAME_MAX_LEN)
++                      attr->model_name_len = WPS_MODEL_NAME_MAX_LEN;
++              else
++                      attr->model_name_len = len;
 +              break;
 +      case ATTR_MODEL_NUMBER:
 +              attr->model_number = pos;
-               attr->serial_number_len = len;
++              if (len > WPS_MODEL_NUMBER_MAX_LEN)
++                      attr->model_number_len = WPS_MODEL_NUMBER_MAX_LEN;
++              else
++                      attr->model_number_len = len;
 +              break;
 +      case ATTR_SERIAL_NUMBER:
 +              attr->serial_number = pos;
++              if (len > WPS_SERIAL_NUMBER_MAX_LEN)
++                      attr->serial_number_len = WPS_SERIAL_NUMBER_MAX_LEN;
++              else
++                      attr->serial_number_len = len;
 +              break;
 +      case ATTR_DEV_NAME:
++              if (len > WPS_DEV_NAME_MAX_LEN) {
++                      wpa_printf(MSG_DEBUG,
++                                 "WPS: Ignore too long Device Name (len=%u)",
++                                 len);
++                      break;
++              }
 +              attr->dev_name = pos;
 +              attr->dev_name_len = len;
 +              break;
 +      case ATTR_PUBLIC_KEY:
++              /*
++               * The Public Key attribute is supposed to be exactly 192 bytes
++               * in length. Allow couple of bytes shorter one to try to
++               * interoperate with implementations that do not use proper
++               * zero-padding.
++               */
++              if (len < 190 || len > 192) {
++                      wpa_printf(MSG_DEBUG,
++                                 "WPS: Ignore Public Key with unexpected length %u",
++                                 len);
++                      break;
++              }
 +              attr->public_key = pos;
 +              attr->public_key_len = len;
 +              break;
 +      case ATTR_ENCR_SETTINGS:
 +              attr->encr_settings = pos;
 +              attr->encr_settings_len = len;
 +              break;
 +      case ATTR_CRED:
 +              if (attr->num_cred >= MAX_CRED_COUNT) {
 +                      wpa_printf(MSG_DEBUG, "WPS: Skipped Credential "
 +                                 "attribute (max %d credentials)",
 +                                 MAX_CRED_COUNT);
 +                      break;
 +              }
 +              attr->cred[attr->num_cred] = pos;
 +              attr->cred_len[attr->num_cred] = len;
 +              attr->num_cred++;
 +              break;
 +      case ATTR_SSID:
++              if (len > SSID_MAX_LEN) {
++                      wpa_printf(MSG_DEBUG,
++                                 "WPS: Ignore too long SSID (len=%u)", len);
++                      break;
++              }
 +              attr->ssid = pos;
 +              attr->ssid_len = len;
 +              break;
 +      case ATTR_NETWORK_KEY:
 +              attr->network_key = pos;
 +              attr->network_key_len = len;
 +              break;
 +      case ATTR_AP_SETUP_LOCKED:
 +              if (len != 1) {
 +                      wpa_printf(MSG_DEBUG, "WPS: Invalid AP Setup Locked "
 +                                 "length %u", len);
 +                      return -1;
 +              }
 +              attr->ap_setup_locked = pos;
 +              break;
 +      case ATTR_REQUESTED_DEV_TYPE:
 +              if (len != WPS_DEV_TYPE_LEN) {
 +                      wpa_printf(MSG_DEBUG, "WPS: Invalid Requested Device "
 +                                 "Type length %u", len);
 +                      return -1;
 +              }
 +              if (attr->num_req_dev_type >= MAX_REQ_DEV_TYPE_COUNT) {
 +                      wpa_printf(MSG_DEBUG, "WPS: Skipped Requested Device "
 +                                 "Type attribute (max %u types)",
 +                                 MAX_REQ_DEV_TYPE_COUNT);
 +                      break;
 +              }
 +              attr->req_dev_type[attr->num_req_dev_type] = pos;
 +              attr->num_req_dev_type++;
 +              break;
 +      case ATTR_SECONDARY_DEV_TYPE_LIST:
 +              if (len > WPS_SEC_DEV_TYPE_MAX_LEN ||
 +                  (len % WPS_DEV_TYPE_LEN) > 0) {
 +                      wpa_printf(MSG_DEBUG, "WPS: Invalid Secondary Device "
 +                                 "Type length %u", len);
 +                      return -1;
 +              }
 +              attr->sec_dev_type_list = pos;
 +              attr->sec_dev_type_list_len = len;
 +              break;
 +      case ATTR_VENDOR_EXT:
 +              if (wps_parse_vendor_ext(attr, pos, len) < 0)
 +                      return -1;
 +              break;
 +      case ATTR_AP_CHANNEL:
 +              if (len != 2) {
 +                      wpa_printf(MSG_DEBUG, "WPS: Invalid AP Channel "
 +                                 "length %u", len);
 +                      return -1;
 +              }
 +              attr->ap_channel = pos;
 +              break;
 +      default:
 +              wpa_printf(MSG_DEBUG, "WPS: Unsupported attribute type 0x%x "
 +                         "len=%u", type, len);
 +              break;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +int wps_parse_msg(const struct wpabuf *msg, struct wps_parse_attr *attr)
 +{
 +      const u8 *pos, *end;
 +      u16 type, len;
 +#ifdef WPS_WORKAROUNDS
 +      u16 prev_type = 0;
 +#endif /* WPS_WORKAROUNDS */
 +
 +      os_memset(attr, 0, sizeof(*attr));
 +      pos = wpabuf_head(msg);
 +      end = pos + wpabuf_len(msg);
 +
 +      while (pos < end) {
 +              if (end - pos < 4) {
 +                      wpa_printf(MSG_DEBUG, "WPS: Invalid message - "
 +                                 "%lu bytes remaining",
 +                                 (unsigned long) (end - pos));
 +                      return -1;
 +              }
 +
 +              type = WPA_GET_BE16(pos);
 +              pos += 2;
 +              len = WPA_GET_BE16(pos);
 +              pos += 2;
 +              wpa_printf(MSG_EXCESSIVE, "WPS: attr type=0x%x len=%u",
 +                         type, len);
 +              if (len > end - pos) {
 +                      wpa_printf(MSG_DEBUG, "WPS: Attribute overflow");
 +                      wpa_hexdump_buf(MSG_MSGDUMP, "WPS: Message data", msg);
 +#ifdef WPS_WORKAROUNDS
 +                      /*
 +                       * Some deployed APs seem to have a bug in encoding of
 +                       * Network Key attribute in the Credential attribute
 +                       * where they add an extra octet after the Network Key
 +                       * attribute at least when open network is being
 +                       * provisioned.
 +                       */
 +                      if ((type & 0xff00) != 0x1000 &&
 +                          prev_type == ATTR_NETWORK_KEY) {
 +                              wpa_printf(MSG_DEBUG, "WPS: Workaround - try "
 +                                         "to skip unexpected octet after "
 +                                         "Network Key");
 +                              pos -= 3;
 +                              continue;
 +                      }
 +#endif /* WPS_WORKAROUNDS */
 +                      return -1;
 +              }
 +
 +#ifdef WPS_WORKAROUNDS
 +              if (type == 0 && len == 0) {
 +                      /*
 +                       * Mac OS X 10.6 seems to be adding 0x00 padding to the
 +                       * end of M1. Skip those to avoid interop issues.
 +                       */
 +                      int i;
 +                      for (i = 0; i < end - pos; i++) {
 +                              if (pos[i])
 +                                      break;
 +                      }
 +                      if (i == end - pos) {
 +                              wpa_printf(MSG_DEBUG, "WPS: Workaround - skip "
 +                                         "unexpected message padding");
 +                              break;
 +                      }
 +              }
 +#endif /* WPS_WORKAROUNDS */
 +
 +              if (wps_set_attr(attr, type, pos, len) < 0)
 +                      return -1;
 +
 +#ifdef WPS_WORKAROUNDS
 +              prev_type = type;
 +#endif /* WPS_WORKAROUNDS */
 +              pos += len;
 +      }
 +
 +      return 0;
 +}
index 82c4739f61f530a1b3e790be1c96a323cdb41f5a,0000000000000000000000000000000000000000..8188fe9173d44aa86f74cf67ba740fb89ae6bc82
mode 100644,000000..100644
--- /dev/null
@@@ -1,103 -1,0 +1,104 @@@
-       size_t manufacturer_len;
 +/*
 + * Wi-Fi Protected Setup - attribute parsing
 + * Copyright (c) 2008-2012, Jouni Malinen <j@w1.fi>
 + *
 + * This software may be distributed under the terms of the BSD license.
 + * See README for more details.
 + */
 +
 +#ifndef WPS_ATTR_PARSE_H
 +#define WPS_ATTR_PARSE_H
 +
 +#include "wps.h"
 +
 +struct wps_parse_attr {
 +      /* fixed length fields */
 +      const u8 *version; /* 1 octet */
 +      const u8 *version2; /* 1 octet */
 +      const u8 *msg_type; /* 1 octet */
 +      const u8 *enrollee_nonce; /* WPS_NONCE_LEN (16) octets */
 +      const u8 *registrar_nonce; /* WPS_NONCE_LEN (16) octets */
 +      const u8 *uuid_r; /* WPS_UUID_LEN (16) octets */
 +      const u8 *uuid_e; /* WPS_UUID_LEN (16) octets */
 +      const u8 *auth_type_flags; /* 2 octets */
 +      const u8 *encr_type_flags; /* 2 octets */
 +      const u8 *conn_type_flags; /* 1 octet */
 +      const u8 *config_methods; /* 2 octets */
 +      const u8 *sel_reg_config_methods; /* 2 octets */
 +      const u8 *primary_dev_type; /* 8 octets */
 +      const u8 *rf_bands; /* 1 octet */
 +      const u8 *assoc_state; /* 2 octets */
 +      const u8 *config_error; /* 2 octets */
 +      const u8 *dev_password_id; /* 2 octets */
 +      const u8 *os_version; /* 4 octets */
 +      const u8 *wps_state; /* 1 octet */
 +      const u8 *authenticator; /* WPS_AUTHENTICATOR_LEN (8) octets */
 +      const u8 *r_hash1; /* WPS_HASH_LEN (32) octets */
 +      const u8 *r_hash2; /* WPS_HASH_LEN (32) octets */
 +      const u8 *e_hash1; /* WPS_HASH_LEN (32) octets */
 +      const u8 *e_hash2; /* WPS_HASH_LEN (32) octets */
 +      const u8 *r_snonce1; /* WPS_SECRET_NONCE_LEN (16) octets */
 +      const u8 *r_snonce2; /* WPS_SECRET_NONCE_LEN (16) octets */
 +      const u8 *e_snonce1; /* WPS_SECRET_NONCE_LEN (16) octets */
 +      const u8 *e_snonce2; /* WPS_SECRET_NONCE_LEN (16) octets */
 +      const u8 *key_wrap_auth; /* WPS_KWA_LEN (8) octets */
 +      const u8 *auth_type; /* 2 octets */
 +      const u8 *encr_type; /* 2 octets */
 +      const u8 *network_idx; /* 1 octet */
 +      const u8 *network_key_idx; /* 1 octet */
 +      const u8 *mac_addr; /* ETH_ALEN (6) octets */
 +      const u8 *selected_registrar; /* 1 octet (Bool) */
 +      const u8 *request_type; /* 1 octet */
 +      const u8 *response_type; /* 1 octet */
 +      const u8 *ap_setup_locked; /* 1 octet */
 +      const u8 *settings_delay_time; /* 1 octet */
 +      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;
-       size_t model_name_len;
 +      const u8 *model_name;
-       size_t model_number_len;
 +      const u8 *model_number;
-       size_t serial_number_len;
 +      const u8 *serial_number;
-       size_t dev_name_len;
 +      const u8 *dev_name;
-       size_t public_key_len;
 +      const u8 *public_key;
-       size_t encr_settings_len;
 +      const u8 *encr_settings;
-       size_t ssid_len;
 +      const u8 *ssid; /* <= 32 octets */
-       size_t network_key_len;
 +      const u8 *network_key; /* <= 64 octets */
-       size_t authorized_macs_len;
 +      const u8 *authorized_macs; /* <= 30 octets */
-       size_t sec_dev_type_list_len;
 +      const u8 *sec_dev_type_list; /* <= 128 octets */
-       size_t oob_dev_password_len;
 +      const u8 *oob_dev_password; /* 38..54 octets */
-       const u8 *cred[MAX_CRED_COUNT];
-       size_t cred_len[MAX_CRED_COUNT];
-       size_t num_cred;
++      u16 manufacturer_len;
++      u16 model_name_len;
++      u16 model_number_len;
++      u16 serial_number_len;
++      u16 dev_name_len;
++      u16 public_key_len;
++      u16 encr_settings_len;
++      u16 ssid_len;
++      u16 network_key_len;
++      u16 authorized_macs_len;
++      u16 sec_dev_type_list_len;
++      u16 oob_dev_password_len;
 +
 +      /* attributes that can occur multiple times */
 +#define MAX_CRED_COUNT 10
-       const u8 *req_dev_type[MAX_REQ_DEV_TYPE_COUNT];
-       size_t num_req_dev_type;
 +#define MAX_REQ_DEV_TYPE_COUNT 10
-       size_t vendor_ext_len[MAX_WPS_PARSE_VENDOR_EXT];
-       size_t num_vendor_ext;
 +
++      unsigned int num_cred;
++      unsigned int num_req_dev_type;
++      unsigned int num_vendor_ext;
++
++      u16 cred_len[MAX_CRED_COUNT];
++      u16 vendor_ext_len[MAX_WPS_PARSE_VENDOR_EXT];
++
++      const u8 *cred[MAX_CRED_COUNT];
++      const u8 *req_dev_type[MAX_REQ_DEV_TYPE_COUNT];
 +      const u8 *vendor_ext[MAX_WPS_PARSE_VENDOR_EXT];
 +};
 +
 +int wps_parse_msg(const struct wpabuf *msg, struct wps_parse_attr *attr);
 +
 +#endif /* WPS_ATTR_PARSE_H */
index c1ede6a9ea834a37a2a77b36aa41f4d6ba8560ff,0000000000000000000000000000000000000000..88f85fe83f057aed893dcafbd15c802bd4200caf
mode 100644,000000..100644
--- /dev/null
@@@ -1,907 -1,0 +1,909 @@@
-       if (str == NULL) {
 +/*
 + * Wi-Fi Protected Setup - common functionality
 + * Copyright (c) 2008-2012, Jouni Malinen <j@w1.fi>
 + *
 + * This software may be distributed under the terms of the BSD license.
 + * See README for more details.
 + */
 +
 +#include "includes.h"
 +
 +#include "common.h"
 +#include "common/defs.h"
 +#include "common/ieee802_11_common.h"
 +#include "crypto/aes_wrap.h"
 +#include "crypto/crypto.h"
 +#include "crypto/dh_group5.h"
 +#include "crypto/sha1.h"
 +#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,
 +           const char *label, u8 *res, size_t res_len)
 +{
 +      u8 i_buf[4], key_bits[4];
 +      const u8 *addr[4];
 +      size_t len[4];
 +      int i, iter;
 +      u8 hash[SHA256_MAC_LEN], *opos;
 +      size_t left;
 +
 +      WPA_PUT_BE32(key_bits, res_len * 8);
 +
 +      addr[0] = i_buf;
 +      len[0] = sizeof(i_buf);
 +      addr[1] = label_prefix;
 +      len[1] = label_prefix_len;
 +      addr[2] = (const u8 *) label;
 +      len[2] = os_strlen(label);
 +      addr[3] = key_bits;
 +      len[3] = sizeof(key_bits);
 +
 +      iter = (res_len + SHA256_MAC_LEN - 1) / SHA256_MAC_LEN;
 +      opos = res;
 +      left = res_len;
 +
 +      for (i = 1; i <= iter; i++) {
 +              WPA_PUT_BE32(i_buf, i);
 +              hmac_sha256_vector(key, SHA256_MAC_LEN, 4, addr, len, hash);
 +              if (i < iter) {
 +                      os_memcpy(opos, hash, SHA256_MAC_LEN);
 +                      opos += SHA256_MAC_LEN;
 +                      left -= SHA256_MAC_LEN;
 +              } else
 +                      os_memcpy(opos, hash, left);
 +      }
 +}
 +
 +
 +int wps_derive_keys(struct wps_data *wps)
 +{
 +      struct wpabuf *pubkey, *dh_shared;
 +      u8 dhkey[SHA256_MAC_LEN], kdk[SHA256_MAC_LEN];
 +      const u8 *addr[3];
 +      size_t len[3];
 +      u8 keys[WPS_AUTHKEY_LEN + WPS_KEYWRAPKEY_LEN + WPS_EMSK_LEN];
 +
 +      if (wps->dh_privkey == NULL) {
 +              wpa_printf(MSG_DEBUG, "WPS: Own DH private key not available");
 +              return -1;
 +      }
 +
 +      pubkey = wps->registrar ? wps->dh_pubkey_e : wps->dh_pubkey_r;
 +      if (pubkey == NULL) {
 +              wpa_printf(MSG_DEBUG, "WPS: Peer DH public key not available");
 +              return -1;
 +      }
 +
 +      wpa_hexdump_buf_key(MSG_DEBUG, "WPS: DH Private Key", wps->dh_privkey);
 +      wpa_hexdump_buf(MSG_DEBUG, "WPS: DH peer Public Key", pubkey);
 +      dh_shared = dh5_derive_shared(wps->dh_ctx, pubkey, wps->dh_privkey);
 +      dh5_free(wps->dh_ctx);
 +      wps->dh_ctx = NULL;
 +      dh_shared = wpabuf_zeropad(dh_shared, 192);
 +      if (dh_shared == NULL) {
 +              wpa_printf(MSG_DEBUG, "WPS: Failed to derive DH shared key");
 +              return -1;
 +      }
 +
 +      /* Own DH private key is not needed anymore */
 +      wpabuf_free(wps->dh_privkey);
 +      wps->dh_privkey = NULL;
 +
 +      wpa_hexdump_buf_key(MSG_DEBUG, "WPS: DH shared key", dh_shared);
 +
 +      /* DHKey = SHA-256(g^AB mod p) */
 +      addr[0] = wpabuf_head(dh_shared);
 +      len[0] = wpabuf_len(dh_shared);
 +      sha256_vector(1, addr, len, dhkey);
 +      wpa_hexdump_key(MSG_DEBUG, "WPS: DHKey", dhkey, sizeof(dhkey));
 +      wpabuf_free(dh_shared);
 +
 +      /* KDK = HMAC-SHA-256_DHKey(N1 || EnrolleeMAC || N2) */
 +      addr[0] = wps->nonce_e;
 +      len[0] = WPS_NONCE_LEN;
 +      addr[1] = wps->mac_addr_e;
 +      len[1] = ETH_ALEN;
 +      addr[2] = wps->nonce_r;
 +      len[2] = WPS_NONCE_LEN;
 +      hmac_sha256_vector(dhkey, sizeof(dhkey), 3, addr, len, kdk);
 +      wpa_hexdump_key(MSG_DEBUG, "WPS: KDK", kdk, sizeof(kdk));
 +
 +      wps_kdf(kdk, NULL, 0, "Wi-Fi Easy and Secure Key Derivation",
 +              keys, sizeof(keys));
 +      os_memcpy(wps->authkey, keys, WPS_AUTHKEY_LEN);
 +      os_memcpy(wps->keywrapkey, keys + WPS_AUTHKEY_LEN, WPS_KEYWRAPKEY_LEN);
 +      os_memcpy(wps->emsk, keys + WPS_AUTHKEY_LEN + WPS_KEYWRAPKEY_LEN,
 +                WPS_EMSK_LEN);
 +
 +      wpa_hexdump_key(MSG_DEBUG, "WPS: AuthKey",
 +                      wps->authkey, WPS_AUTHKEY_LEN);
 +      wpa_hexdump_key(MSG_DEBUG, "WPS: KeyWrapKey",
 +                      wps->keywrapkey, WPS_KEYWRAPKEY_LEN);
 +      wpa_hexdump_key(MSG_DEBUG, "WPS: EMSK", wps->emsk, WPS_EMSK_LEN);
 +
 +      return 0;
 +}
 +
 +
 +void wps_derive_psk(struct wps_data *wps, const u8 *dev_passwd,
 +                  size_t dev_passwd_len)
 +{
 +      u8 hash[SHA256_MAC_LEN];
 +
 +      hmac_sha256(wps->authkey, WPS_AUTHKEY_LEN, dev_passwd,
 +                  (dev_passwd_len + 1) / 2, hash);
 +      os_memcpy(wps->psk1, hash, WPS_PSK_LEN);
 +      hmac_sha256(wps->authkey, WPS_AUTHKEY_LEN,
 +                  dev_passwd + (dev_passwd_len + 1) / 2,
 +                  dev_passwd_len / 2, hash);
 +      os_memcpy(wps->psk2, hash, WPS_PSK_LEN);
 +
 +      wpa_hexdump_ascii_key(MSG_DEBUG, "WPS: Device Password",
 +                            dev_passwd, dev_passwd_len);
 +      wpa_hexdump_key(MSG_DEBUG, "WPS: PSK1", wps->psk1, WPS_PSK_LEN);
 +      wpa_hexdump_key(MSG_DEBUG, "WPS: PSK2", wps->psk2, WPS_PSK_LEN);
 +}
 +
 +
 +struct wpabuf * wps_decrypt_encr_settings(struct wps_data *wps, const u8 *encr,
 +                                        size_t encr_len)
 +{
 +      struct wpabuf *decrypted;
 +      const size_t block_size = 16;
 +      size_t i;
 +      u8 pad;
 +      const u8 *pos;
 +
 +      /* AES-128-CBC */
 +      if (encr == NULL || encr_len < 2 * block_size || encr_len % block_size)
 +      {
 +              wpa_printf(MSG_DEBUG, "WPS: No Encrypted Settings received");
 +              return NULL;
 +      }
 +
 +      decrypted = wpabuf_alloc(encr_len - block_size);
 +      if (decrypted == NULL)
 +              return NULL;
 +
 +      wpa_hexdump(MSG_MSGDUMP, "WPS: Encrypted Settings", encr, encr_len);
 +      wpabuf_put_data(decrypted, encr + block_size, encr_len - block_size);
 +      if (aes_128_cbc_decrypt(wps->keywrapkey, encr, wpabuf_mhead(decrypted),
 +                              wpabuf_len(decrypted))) {
 +              wpabuf_free(decrypted);
 +              return NULL;
 +      }
 +
 +      wpa_hexdump_buf_key(MSG_MSGDUMP, "WPS: Decrypted Encrypted Settings",
 +                          decrypted);
 +
 +      pos = wpabuf_head_u8(decrypted) + wpabuf_len(decrypted) - 1;
 +      pad = *pos;
 +      if (pad > wpabuf_len(decrypted)) {
 +              wpa_printf(MSG_DEBUG, "WPS: Invalid PKCS#5 v2.0 pad value");
 +              wpabuf_free(decrypted);
 +              return NULL;
 +      }
 +      for (i = 0; i < pad; i++) {
 +              if (*pos-- != pad) {
 +                      wpa_printf(MSG_DEBUG, "WPS: Invalid PKCS#5 v2.0 pad "
 +                                 "string");
 +                      wpabuf_free(decrypted);
 +                      return NULL;
 +              }
 +      }
 +      decrypted->used -= pad;
 +
 +      return decrypted;
 +}
 +
 +
 +/**
 + * wps_pin_checksum - Compute PIN checksum
 + * @pin: Seven digit PIN (i.e., eight digit PIN without the checksum digit)
 + * Returns: Checksum digit
 + */
 +unsigned int wps_pin_checksum(unsigned int pin)
 +{
 +      unsigned int accum = 0;
 +      while (pin) {
 +              accum += 3 * (pin % 10);
 +              pin /= 10;
 +              accum += pin % 10;
 +              pin /= 10;
 +      }
 +
 +      return (10 - accum % 10) % 10;
 +}
 +
 +
 +/**
 + * wps_pin_valid - Check whether a PIN has a valid checksum
 + * @pin: Eight digit PIN (i.e., including the checksum digit)
 + * Returns: 1 if checksum digit is valid, or 0 if not
 + */
 +unsigned int wps_pin_valid(unsigned int pin)
 +{
 +      return wps_pin_checksum(pin / 10) == (pin % 10);
 +}
 +
 +
 +/**
 + * wps_generate_pin - Generate a random PIN
 + * Returns: Eight digit PIN (i.e., including the checksum digit)
 + */
 +unsigned int wps_generate_pin(void)
 +{
 +      unsigned int val;
 +
 +      /* Generate seven random digits for the PIN */
 +      if (random_get_bytes((unsigned char *) &val, sizeof(val)) < 0) {
 +              struct os_time now;
 +              os_get_time(&now);
 +              val = os_random() ^ now.sec ^ now.usec;
 +      }
 +      val %= 10000000;
 +
 +      /* Append checksum digit */
 +      return val * 10 + wps_pin_checksum(val);
 +}
 +
 +
 +int wps_pin_str_valid(const char *pin)
 +{
 +      const char *p;
 +      size_t len;
 +
 +      p = pin;
 +      while (*p >= '0' && *p <= '9')
 +              p++;
 +      if (*p != '\0')
 +              return 0;
 +
 +      len = p - pin;
 +      return len == 4 || len == 8;
 +}
 +
 +
 +void wps_fail_event(struct wps_context *wps, enum wps_msg_type msg,
 +                  u16 config_error, u16 error_indication, const u8 *mac_addr)
 +{
 +      union wps_event_data data;
 +
 +      if (wps->event_cb == NULL)
 +              return;
 +
 +      os_memset(&data, 0, sizeof(data));
 +      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, const u8 *mac_addr)
 +{
 +      union wps_event_data data;
 +
 +      if (wps->event_cb == NULL)
 +              return;
 +
 +      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,
 +                           const u8 *mac_addr)
 +{
 +      union wps_event_data data;
 +
 +      if (wps->event_cb == NULL)
 +              return;
 +
 +      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);
 +}
 +
 +
 +void wps_pbc_overlap_event(struct wps_context *wps)
 +{
 +      if (wps->event_cb == NULL)
 +              return;
 +
 +      wps->event_cb(wps->cb_ctx, WPS_EV_PBC_OVERLAP, NULL);
 +}
 +
 +
 +void wps_pbc_timeout_event(struct wps_context *wps)
 +{
 +      if (wps->event_cb == NULL)
 +              return;
 +
 +      wps->event_cb(wps->cb_ctx, WPS_EV_PBC_TIMEOUT, NULL);
 +}
 +
 +
 +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, int rf_band,
 +                               int channel)
 +{
 +      struct wps_data data;
 +      struct wpabuf *plain;
 +
 +      plain = wpabuf_alloc(500);
 +      if (plain == NULL) {
 +              wpa_printf(MSG_ERROR, "WPS: Failed to allocate memory for OOB "
 +                         "credential");
 +              return NULL;
 +      }
 +
 +      os_memset(&data, 0, sizeof(data));
 +      data.wps = wps;
 +      data.auth_type = wps->auth_types;
 +      data.encr_type = wps->encr_types;
 +      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);
 +              return NULL;
 +      }
 +
 +      if (wps->wps_state == WPS_STATE_NOT_CONFIGURED && data.new_psk &&
 +          wps->ap) {
 +              struct wps_credential cred;
 +
 +              wpa_printf(MSG_DEBUG, "WPS: Moving to Configured state based "
 +                         "on credential token generation");
 +
 +              os_memset(&cred, 0, sizeof(cred));
 +              os_memcpy(cred.ssid, wps->ssid, wps->ssid_len);
 +              cred.ssid_len = wps->ssid_len;
 +              cred.auth_type = WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK;
 +              cred.encr_type = WPS_ENCR_TKIP | WPS_ENCR_AES;
 +              os_memcpy(cred.key, data.new_psk, data.new_psk_len);
 +              cred.key_len = data.new_psk_len;
 +
 +              wps->wps_state = WPS_STATE_CONFIGURED;
 +              wpa_hexdump_ascii_key(MSG_DEBUG,
 +                                    "WPS: Generated random passphrase",
 +                                    data.new_psk, data.new_psk_len);
 +              if (wps->cred_cb)
 +                      wps->cred_cb(wps->cb_ctx, &cred);
 +      }
 +
 +      os_free(data.new_psk);
 +
 +      return plain;
 +}
 +
 +
 +struct wpabuf * wps_build_nfc_pw_token(u16 dev_pw_id,
 +                                     const struct wpabuf *pubkey,
 +                                     const struct wpabuf *dev_pw)
 +{
 +      struct wpabuf *data;
 +
 +      data = wpabuf_alloc(200);
 +      if (data == NULL)
 +              return NULL;
 +
 +      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 "
 +                         "token");
 +              wpabuf_free(data);
 +              return NULL;
 +      }
 +
 +      return data;
 +}
 +
 +
 +int wps_oob_use_cred(struct wps_context *wps, struct wps_parse_attr *attr)
 +{
 +      struct wpabuf msg;
 +      size_t i;
 +
 +      for (i = 0; i < attr->num_cred; i++) {
 +              struct wps_credential local_cred;
 +              struct wps_parse_attr cattr;
 +
 +              os_memset(&local_cred, 0, sizeof(local_cred));
 +              wpabuf_set(&msg, attr->cred[i], attr->cred_len[i]);
 +              if (wps_parse_msg(&msg, &cattr) < 0 ||
 +                  wps_process_cred(&cattr, &local_cred)) {
 +                      wpa_printf(MSG_ERROR, "WPS: Failed to parse OOB "
 +                                 "credential");
 +                      return -1;
 +              }
 +              wps->cred_cb(wps->cb_ctx, &local_cred);
 +      }
 +
 +      return 0;
 +}
 +
 +
 +#endif /* CONFIG_WPS_OOB */
 +
 +
 +int wps_dev_type_str2bin(const char *str, u8 dev_type[WPS_DEV_TYPE_LEN])
 +{
 +      const char *pos;
 +
 +      /* <categ>-<OUI>-<subcateg> */
 +      WPA_PUT_BE16(dev_type, atoi(str));
 +      pos = os_strchr(str, '-');
 +      if (pos == NULL)
 +              return -1;
 +      pos++;
 +      if (hexstr2bin(pos, &dev_type[2], 4))
 +              return -1;
 +      pos = os_strchr(pos, '-');
 +      if (pos == NULL)
 +              return -1;
 +      pos++;
 +      WPA_PUT_BE16(&dev_type[6], atoi(pos));
 +
 +
 +      return 0;
 +}
 +
 +
 +char * wps_dev_type_bin2str(const u8 dev_type[WPS_DEV_TYPE_LEN], char *buf,
 +                          size_t buf_len)
 +{
 +      int ret;
 +
 +      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 (os_snprintf_error(buf_len, ret))
 +              return NULL;
 +
 +      return buf;
 +}
 +
 +
 +void uuid_gen_mac_addr(const u8 *mac_addr, u8 *uuid)
 +{
 +      const u8 *addr[2];
 +      size_t len[2];
 +      u8 hash[SHA1_MAC_LEN];
 +      u8 nsid[16] = {
 +              0x52, 0x64, 0x80, 0xf8,
 +              0xc9, 0x9b,
 +              0x4b, 0xe5,
 +              0xa6, 0x55,
 +              0x58, 0xed, 0x5f, 0x5d, 0x60, 0x84
 +      };
 +
 +      addr[0] = nsid;
 +      len[0] = sizeof(nsid);
 +      addr[1] = mac_addr;
 +      len[1] = 6;
 +      sha1_vector(2, addr, len, hash);
 +      os_memcpy(uuid, hash, 16);
 +
 +      /* Version: 5 = named-based version using SHA-1 */
 +      uuid[6] = (5 << 4) | (uuid[6] & 0x0f);
 +
 +      /* Variant specified in RFC 4122 */
 +      uuid[8] = 0x80 | (uuid[8] & 0x3f);
 +}
 +
 +
 +u16 wps_config_methods_str2bin(const char *str)
 +{
 +      u16 methods = 0;
 +
++      if (str == NULL || str[0] == '\0') {
 +              /* Default to enabling methods based on build configuration */
 +              methods |= WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD;
 +              methods |= WPS_CONFIG_VIRT_DISPLAY;
 +#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;
 +              if (os_strstr(str, "label"))
 +                      methods |= WPS_CONFIG_LABEL;
 +              if (os_strstr(str, "display"))
 +                      methods |= WPS_CONFIG_DISPLAY;
 +              if (os_strstr(str, "ext_nfc_token"))
 +                      methods |= WPS_CONFIG_EXT_NFC_TOKEN;
 +              if (os_strstr(str, "int_nfc_token"))
 +                      methods |= WPS_CONFIG_INT_NFC_TOKEN;
 +              if (os_strstr(str, "nfc_interface"))
 +                      methods |= WPS_CONFIG_NFC_INTERFACE;
 +              if (os_strstr(str, "push_button"))
 +                      methods |= WPS_CONFIG_PUSHBUTTON;
 +              if (os_strstr(str, "keypad"))
 +                      methods |= WPS_CONFIG_KEYPAD;
 +              if (os_strstr(str, "virtual_display"))
 +                      methods |= WPS_CONFIG_VIRT_DISPLAY;
 +              if (os_strstr(str, "physical_display"))
 +                      methods |= WPS_CONFIG_PHY_DISPLAY;
 +              if (os_strstr(str, "virtual_push_button"))
 +                      methods |= WPS_CONFIG_VIRT_PUSHBUTTON;
 +              if (os_strstr(str, "physical_push_button"))
 +                      methods |= WPS_CONFIG_PHY_PUSHBUTTON;
 +              if (os_strstr(str, "p2ps"))
 +                      methods |= WPS_CONFIG_P2PS;
 +      }
 +
 +      return methods;
 +}
 +
 +
 +struct wpabuf * wps_build_wsc_ack(struct wps_data *wps)
 +{
 +      struct wpabuf *msg;
 +
 +      wpa_printf(MSG_DEBUG, "WPS: Building Message WSC_ACK");
 +
 +      msg = wpabuf_alloc(1000);
 +      if (msg == NULL)
 +              return NULL;
 +
 +      if (wps_build_version(msg) ||
 +          wps_build_msg_type(msg, WPS_WSC_ACK) ||
 +          wps_build_enrollee_nonce(wps, msg) ||
 +          wps_build_registrar_nonce(wps, msg) ||
 +          wps_build_wfa_ext(msg, 0, NULL, 0)) {
 +              wpabuf_free(msg);
 +              return NULL;
 +      }
 +
 +      return msg;
 +}
 +
 +
 +struct wpabuf * wps_build_wsc_nack(struct wps_data *wps)
 +{
 +      struct wpabuf *msg;
 +
 +      wpa_printf(MSG_DEBUG, "WPS: Building Message WSC_NACK");
 +
 +      msg = wpabuf_alloc(1000);
 +      if (msg == NULL)
 +              return NULL;
 +
 +      if (wps_build_version(msg) ||
 +          wps_build_msg_type(msg, WPS_WSC_NACK) ||
 +          wps_build_enrollee_nonce(wps, msg) ||
 +          wps_build_registrar_nonce(wps, msg) ||
 +          wps_build_config_error(msg, wps->config_error) ||
 +          wps_build_wfa_ext(msg, 0, NULL, 0)) {
 +              wpabuf_free(msg);
 +              return NULL;
 +      }
 +
 +      return msg;
 +}
 +
 +
 +#ifdef CONFIG_WPS_NFC
 +
 +struct wpabuf * wps_nfc_token_build(int ndef, int id, struct wpabuf *pubkey,
 +                                  struct wpabuf *dev_pw)
 +{
 +      struct wpabuf *ret;
 +
 +      if (pubkey == NULL || dev_pw == NULL)
 +              return NULL;
 +
 +      ret = wps_build_nfc_pw_token(id, pubkey, dev_pw);
 +      if (ndef && ret) {
 +              struct wpabuf *tmp;
 +              tmp = ndef_build_wifi(ret);
 +              wpabuf_free(ret);
 +              if (tmp == NULL)
 +                      return NULL;
 +              ret = tmp;
 +      }
 +
 +      return ret;
 +}
 +
 +
 +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 *pw;
 +      u16 val;
 +
 +      pw = wpabuf_alloc(WPS_OOB_DEVICE_PASSWORD_LEN);
 +      if (pw == NULL)
 +              return NULL;
 +
 +      if (random_get_bytes(wpabuf_put(pw, WPS_OOB_DEVICE_PASSWORD_LEN),
 +                           WPS_OOB_DEVICE_PASSWORD_LEN) ||
 +          random_get_bytes((u8 *) &val, sizeof(val))) {
 +              wpabuf_free(pw);
 +              return NULL;
 +      }
 +
 +      if (wps_nfc_gen_dh(pubkey, privkey) < 0) {
 +              wpabuf_free(pw);
 +              return NULL;
 +      }
 +
 +      *id = 0x10 + val % 0xfff0;
 +      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 if (mode == HOSTAPD_MODE_IEEE80211AD)
++              rf_band = WPS_RF_60GHZ;
 +      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 25cd14a0b32a0311cacb7ce6609560552403280b,0000000000000000000000000000000000000000..a23b979d2e3c7d6835f5d61c6e42edf60c3bc323
mode 100644,000000..100644
--- /dev/null
@@@ -1,373 -1,0 +1,379 @@@
 +/*
 + * Wi-Fi Protected Setup - message definitions
 + * Copyright (c) 2008, Jouni Malinen <j@w1.fi>
 + *
 + * This software may be distributed under the terms of the BSD license.
 + * See README for more details.
 + */
 +
 +#ifndef WPS_DEFS_H
 +#define WPS_DEFS_H
 +
 +#ifdef CONFIG_WPS_TESTING
 +
 +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 */
 +
 +#define WPS_VERSION 0x20
 +
 +#endif /* CONFIG_WPS_TESTING */
 +
 +/* Diffie-Hellman 1536-bit MODP Group; RFC 3526, Group 5 */
 +#define WPS_DH_GROUP 5
 +
 +#define WPS_UUID_LEN 16
 +#define WPS_NONCE_LEN 16
 +#define WPS_AUTHENTICATOR_LEN 8
 +#define WPS_AUTHKEY_LEN 32
 +#define WPS_KEYWRAPKEY_LEN 16
 +#define WPS_EMSK_LEN 32
 +#define WPS_PSK_LEN 16
 +#define WPS_SECRET_NONCE_LEN 16
 +#define WPS_HASH_LEN 32
 +#define WPS_KWA_LEN 8
 +#define WPS_MGMTAUTHKEY_LEN 32
 +#define WPS_MGMTENCKEY_LEN 16
 +#define WPS_MGMT_KEY_ID_LEN 16
 +#define WPS_OOB_DEVICE_PASSWORD_MIN_LEN 16
 +#define WPS_OOB_DEVICE_PASSWORD_LEN 32
 +#define WPS_OOB_PUBKEY_HASH_LEN 20
++#define WPS_DEV_NAME_MAX_LEN 32
++#define WPS_MANUFACTURER_MAX_LEN 64
++#define WPS_MODEL_NAME_MAX_LEN 32
++#define WPS_MODEL_NUMBER_MAX_LEN 32
++#define WPS_SERIAL_NUMBER_MAX_LEN 32
 +
 +/* Attribute Types */
 +enum wps_attribute {
 +      ATTR_AP_CHANNEL = 0x1001,
 +      ATTR_ASSOC_STATE = 0x1002,
 +      ATTR_AUTH_TYPE = 0x1003,
 +      ATTR_AUTH_TYPE_FLAGS = 0x1004,
 +      ATTR_AUTHENTICATOR = 0x1005,
 +      ATTR_CONFIG_METHODS = 0x1008,
 +      ATTR_CONFIG_ERROR = 0x1009,
 +      ATTR_CONFIRM_URL4 = 0x100a,
 +      ATTR_CONFIRM_URL6 = 0x100b,
 +      ATTR_CONN_TYPE = 0x100c,
 +      ATTR_CONN_TYPE_FLAGS = 0x100d,
 +      ATTR_CRED = 0x100e,
 +      ATTR_ENCR_TYPE = 0x100f,
 +      ATTR_ENCR_TYPE_FLAGS = 0x1010,
 +      ATTR_DEV_NAME = 0x1011,
 +      ATTR_DEV_PASSWORD_ID = 0x1012,
 +      ATTR_E_HASH1 = 0x1014,
 +      ATTR_E_HASH2 = 0x1015,
 +      ATTR_E_SNONCE1 = 0x1016,
 +      ATTR_E_SNONCE2 = 0x1017,
 +      ATTR_ENCR_SETTINGS = 0x1018,
 +      ATTR_ENROLLEE_NONCE = 0x101a,
 +      ATTR_FEATURE_ID = 0x101b,
 +      ATTR_IDENTITY = 0x101c,
 +      ATTR_IDENTITY_PROOF = 0x101d,
 +      ATTR_KEY_WRAP_AUTH = 0x101e,
 +      ATTR_KEY_ID = 0x101f,
 +      ATTR_MAC_ADDR = 0x1020,
 +      ATTR_MANUFACTURER = 0x1021,
 +      ATTR_MSG_TYPE = 0x1022,
 +      ATTR_MODEL_NAME = 0x1023,
 +      ATTR_MODEL_NUMBER = 0x1024,
 +      ATTR_NETWORK_INDEX = 0x1026,
 +      ATTR_NETWORK_KEY = 0x1027,
 +      ATTR_NETWORK_KEY_INDEX = 0x1028,
 +      ATTR_NEW_DEVICE_NAME = 0x1029,
 +      ATTR_NEW_PASSWORD = 0x102a,
 +      ATTR_OOB_DEVICE_PASSWORD = 0x102c,
 +      ATTR_OS_VERSION = 0x102d,
 +      ATTR_POWER_LEVEL = 0x102f,
 +      ATTR_PSK_CURRENT = 0x1030,
 +      ATTR_PSK_MAX = 0x1031,
 +      ATTR_PUBLIC_KEY = 0x1032,
 +      ATTR_RADIO_ENABLE = 0x1033,
 +      ATTR_REBOOT = 0x1034,
 +      ATTR_REGISTRAR_CURRENT = 0x1035,
 +      ATTR_REGISTRAR_ESTABLISHED = 0x1036,
 +      ATTR_REGISTRAR_LIST = 0x1037,
 +      ATTR_REGISTRAR_MAX = 0x1038,
 +      ATTR_REGISTRAR_NONCE = 0x1039,
 +      ATTR_REQUEST_TYPE = 0x103a,
 +      ATTR_RESPONSE_TYPE = 0x103b,
 +      ATTR_RF_BANDS = 0x103c,
 +      ATTR_R_HASH1 = 0x103d,
 +      ATTR_R_HASH2 = 0x103e,
 +      ATTR_R_SNONCE1 = 0x103f,
 +      ATTR_R_SNONCE2 = 0x1040,
 +      ATTR_SELECTED_REGISTRAR = 0x1041,
 +      ATTR_SERIAL_NUMBER = 0x1042,
 +      ATTR_WPS_STATE = 0x1044,
 +      ATTR_SSID = 0x1045,
 +      ATTR_TOTAL_NETWORKS = 0x1046,
 +      ATTR_UUID_E = 0x1047,
 +      ATTR_UUID_R = 0x1048,
 +      ATTR_VENDOR_EXT = 0x1049,
 +      ATTR_VERSION = 0x104a,
 +      ATTR_X509_CERT_REQ = 0x104b,
 +      ATTR_X509_CERT = 0x104c,
 +      ATTR_EAP_IDENTITY = 0x104d,
 +      ATTR_MSG_COUNTER = 0x104e,
 +      ATTR_PUBKEY_HASH = 0x104f,
 +      ATTR_REKEY_KEY = 0x1050,
 +      ATTR_KEY_LIFETIME = 0x1051,
 +      ATTR_PERMITTED_CFG_METHODS = 0x1052,
 +      ATTR_SELECTED_REGISTRAR_CONFIG_METHODS = 0x1053,
 +      ATTR_PRIMARY_DEV_TYPE = 0x1054,
 +      ATTR_SECONDARY_DEV_TYPE_LIST = 0x1055,
 +      ATTR_PORTABLE_DEV = 0x1056,
 +      ATTR_AP_SETUP_LOCKED = 0x1057,
 +      ATTR_APPLICATION_EXT = 0x1058,
 +      ATTR_EAP_TYPE = 0x1059,
 +      ATTR_IV = 0x1060,
 +      ATTR_KEY_PROVIDED_AUTO = 0x1061,
 +      ATTR_802_1X_ENABLED = 0x1062,
 +      ATTR_APPSESSIONKEY = 0x1063,
 +      ATTR_WEPTRANSMITKEY = 0x1064,
 +      ATTR_REQUESTED_DEV_TYPE = 0x106a,
 +      ATTR_EXTENSIBILITY_TEST = 0x10fa /* _NOT_ defined in the spec */
 +};
 +
 +#define WPS_VENDOR_ID_WFA 14122
 +
 +/* WFA Vendor Extension subelements */
 +enum {
 +      WFA_ELEM_VERSION2 = 0x00,
 +      WFA_ELEM_AUTHORIZEDMACS = 0x01,
 +      WFA_ELEM_NETWORK_KEY_SHAREABLE = 0x02,
 +      WFA_ELEM_REQUEST_TO_ENROLL = 0x03,
 +      WFA_ELEM_SETTINGS_DELAY_TIME = 0x04,
 +      WFA_ELEM_REGISTRAR_CONFIGURATION_METHODS = 0x05
 +};
 +
 +/* Device Password ID */
 +enum wps_dev_password_id {
 +      DEV_PW_DEFAULT = 0x0000,
 +      DEV_PW_USER_SPECIFIED = 0x0001,
 +      DEV_PW_MACHINE_SPECIFIED = 0x0002,
 +      DEV_PW_REKEY = 0x0003,
 +      DEV_PW_PUSHBUTTON = 0x0004,
 +      DEV_PW_REGISTRAR_SPECIFIED = 0x0005,
 +      DEV_PW_NFC_CONNECTION_HANDOVER = 0x0007,
 +      DEV_PW_P2PS_DEFAULT = 0x0008
 +};
 +
 +/* Message Type */
 +enum wps_msg_type {
 +      WPS_Beacon = 0x01,
 +      WPS_ProbeRequest = 0x02,
 +      WPS_ProbeResponse = 0x03,
 +      WPS_M1 = 0x04,
 +      WPS_M2 = 0x05,
 +      WPS_M2D = 0x06,
 +      WPS_M3 = 0x07,
 +      WPS_M4 = 0x08,
 +      WPS_M5 = 0x09,
 +      WPS_M6 = 0x0a,
 +      WPS_M7 = 0x0b,
 +      WPS_M8 = 0x0c,
 +      WPS_WSC_ACK = 0x0d,
 +      WPS_WSC_NACK = 0x0e,
 +      WPS_WSC_DONE = 0x0f
 +};
 +
 +/* Authentication Type Flags */
 +#define WPS_AUTH_OPEN 0x0001
 +#define WPS_AUTH_WPAPSK 0x0002
 +#define WPS_AUTH_SHARED 0x0004 /* deprecated */
 +#define WPS_AUTH_WPA 0x0008
 +#define WPS_AUTH_WPA2 0x0010
 +#define WPS_AUTH_WPA2PSK 0x0020
 +#define WPS_AUTH_TYPES (WPS_AUTH_OPEN | WPS_AUTH_WPAPSK | WPS_AUTH_SHARED | \
 +                      WPS_AUTH_WPA | WPS_AUTH_WPA2 | WPS_AUTH_WPA2PSK)
 +
 +/* Encryption Type Flags */
 +#define WPS_ENCR_NONE 0x0001
 +#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 | \
 +                      WPS_ENCR_AES)
 +
 +/* Configuration Error */
 +enum wps_config_error {
 +      WPS_CFG_NO_ERROR = 0,
 +      WPS_CFG_OOB_IFACE_READ_ERROR = 1,
 +      WPS_CFG_DECRYPTION_CRC_FAILURE = 2,
 +      WPS_CFG_24_CHAN_NOT_SUPPORTED = 3,
 +      WPS_CFG_50_CHAN_NOT_SUPPORTED = 4,
 +      WPS_CFG_SIGNAL_TOO_WEAK = 5,
 +      WPS_CFG_NETWORK_AUTH_FAILURE = 6,
 +      WPS_CFG_NETWORK_ASSOC_FAILURE = 7,
 +      WPS_CFG_NO_DHCP_RESPONSE = 8,
 +      WPS_CFG_FAILED_DHCP_CONFIG = 9,
 +      WPS_CFG_IP_ADDR_CONFLICT = 10,
 +      WPS_CFG_NO_CONN_TO_REGISTRAR = 11,
 +      WPS_CFG_MULTIPLE_PBC_DETECTED = 12,
 +      WPS_CFG_ROGUE_SUSPECTED = 13,
 +      WPS_CFG_DEVICE_BUSY = 14,
 +      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_60G_CHAN_NOT_SUPPORTED = 19,
 +      WPS_CFG_PUBLIC_KEY_HASH_MISMATCH = 20
 +};
 +
 +/* Vendor specific Error Indication for WPS event messages */
 +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
 +};
 +
 +/* RF Bands */
 +#define WPS_RF_24GHZ 0x01
 +#define WPS_RF_50GHZ 0x02
++#define WPS_RF_60GHZ 0x04
 +
 +/* Config Methods */
 +#define WPS_CONFIG_USBA 0x0001
 +#define WPS_CONFIG_ETHERNET 0x0002
 +#define WPS_CONFIG_LABEL 0x0004
 +#define WPS_CONFIG_DISPLAY 0x0008
 +#define WPS_CONFIG_EXT_NFC_TOKEN 0x0010
 +#define WPS_CONFIG_INT_NFC_TOKEN 0x0020
 +#define WPS_CONFIG_NFC_INTERFACE 0x0040
 +#define WPS_CONFIG_PUSHBUTTON 0x0080
 +#define WPS_CONFIG_KEYPAD 0x0100
 +#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
 +
 +/* Connection Type Flags */
 +#define WPS_CONN_ESS 0x01
 +#define WPS_CONN_IBSS 0x02
 +
 +/* Wi-Fi Protected Setup State */
 +enum wps_state {
 +      WPS_STATE_NOT_CONFIGURED = 1,
 +      WPS_STATE_CONFIGURED = 2
 +};
 +
 +/* Association State */
 +enum wps_assoc_state {
 +      WPS_ASSOC_NOT_ASSOC = 0,
 +      WPS_ASSOC_CONN_SUCCESS = 1,
 +      WPS_ASSOC_CFG_FAILURE = 2,
 +      WPS_ASSOC_FAILURE = 3,
 +      WPS_ASSOC_IP_FAILURE = 4
 +};
 +
 +
 +#define WPS_DEV_OUI_WFA 0x0050f204
 +
 +enum wps_dev_categ {
 +      WPS_DEV_COMPUTER = 1,
 +      WPS_DEV_INPUT = 2,
 +      WPS_DEV_PRINTER = 3,
 +      WPS_DEV_CAMERA = 4,
 +      WPS_DEV_STORAGE = 5,
 +      WPS_DEV_NETWORK_INFRA = 6,
 +      WPS_DEV_DISPLAY = 7,
 +      WPS_DEV_MULTIMEDIA = 8,
 +      WPS_DEV_GAMING = 9,
 +      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_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,
 +};
 +
 +
 +/* Request Type */
 +enum wps_request_type {
 +      WPS_REQ_ENROLLEE_INFO = 0,
 +      WPS_REQ_ENROLLEE = 1,
 +      WPS_REQ_REGISTRAR = 2,
 +      WPS_REQ_WLAN_MANAGER_REGISTRAR = 3
 +};
 +
 +/* Response Type */
 +enum wps_response_type {
 +      WPS_RESP_ENROLLEE_INFO = 0,
 +      WPS_RESP_ENROLLEE = 1,
 +      WPS_RESP_REGISTRAR = 2,
 +      WPS_RESP_AP = 3
 +};
 +
 +/* Walk Time for push button configuration (in seconds) */
 +#define WPS_PBC_WALK_TIME 120
 +
 +#define WPS_MAX_AUTHORIZED_MACS 5
 +
 +#endif /* WPS_DEFS_H */
index 89957b1a818ad621a5e0179fa2a008d27ebed6ac,0000000000000000000000000000000000000000..9321b721abd758eca980181e3f8197c2d8c4538e
mode 100644,000000..100644
--- /dev/null
@@@ -1,1507 -1,0 +1,1508 @@@
-                            size_t cred_len[], size_t num_cred, int wps2)
 +/*
 + * Wi-Fi Protected Setup - Enrollee
 + * Copyright (c) 2008, Jouni Malinen <j@w1.fi>
 + *
 + * This software may be distributed under the terms of the BSD license.
 + * See README for more details.
 + */
 +
 +#include "includes.h"
 +
 +#include "common.h"
 +#include "crypto/crypto.h"
 +#include "crypto/sha256.h"
 +#include "crypto/random.h"
 +#include "wps_i.h"
 +#include "wps_dev_attr.h"
 +
 +
 +static int wps_build_wps_state(struct wps_data *wps, struct wpabuf *msg)
 +{
 +      u8 state;
 +      if (wps->wps->ap)
 +              state = wps->wps->wps_state;
 +      else
 +              state = WPS_STATE_NOT_CONFIGURED;
 +      wpa_printf(MSG_DEBUG, "WPS:  * Wi-Fi Protected Setup State (%d)",
 +                 state);
 +      wpabuf_put_be16(msg, ATTR_WPS_STATE);
 +      wpabuf_put_be16(msg, 1);
 +      wpabuf_put_u8(msg, state);
 +      return 0;
 +}
 +
 +
 +static int wps_build_e_hash(struct wps_data *wps, struct wpabuf *msg)
 +{
 +      u8 *hash;
 +      const u8 *addr[4];
 +      size_t len[4];
 +
 +      if (random_get_bytes(wps->snonce, 2 * WPS_SECRET_NONCE_LEN) < 0)
 +              return -1;
 +      wpa_hexdump(MSG_DEBUG, "WPS: E-S1", wps->snonce, WPS_SECRET_NONCE_LEN);
 +      wpa_hexdump(MSG_DEBUG, "WPS: E-S2",
 +                  wps->snonce + WPS_SECRET_NONCE_LEN, WPS_SECRET_NONCE_LEN);
 +
 +      if (wps->dh_pubkey_e == NULL || wps->dh_pubkey_r == NULL) {
 +              wpa_printf(MSG_DEBUG, "WPS: DH public keys not available for "
 +                         "E-Hash derivation");
 +              return -1;
 +      }
 +
 +      wpa_printf(MSG_DEBUG, "WPS:  * E-Hash1");
 +      wpabuf_put_be16(msg, ATTR_E_HASH1);
 +      wpabuf_put_be16(msg, SHA256_MAC_LEN);
 +      hash = wpabuf_put(msg, SHA256_MAC_LEN);
 +      /* E-Hash1 = HMAC_AuthKey(E-S1 || PSK1 || PK_E || PK_R) */
 +      addr[0] = wps->snonce;
 +      len[0] = WPS_SECRET_NONCE_LEN;
 +      addr[1] = wps->psk1;
 +      len[1] = WPS_PSK_LEN;
 +      addr[2] = wpabuf_head(wps->dh_pubkey_e);
 +      len[2] = wpabuf_len(wps->dh_pubkey_e);
 +      addr[3] = wpabuf_head(wps->dh_pubkey_r);
 +      len[3] = wpabuf_len(wps->dh_pubkey_r);
 +      hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash);
 +      wpa_hexdump(MSG_DEBUG, "WPS: E-Hash1", hash, SHA256_MAC_LEN);
 +
 +      wpa_printf(MSG_DEBUG, "WPS:  * E-Hash2");
 +      wpabuf_put_be16(msg, ATTR_E_HASH2);
 +      wpabuf_put_be16(msg, SHA256_MAC_LEN);
 +      hash = wpabuf_put(msg, SHA256_MAC_LEN);
 +      /* E-Hash2 = HMAC_AuthKey(E-S2 || PSK2 || PK_E || PK_R) */
 +      addr[0] = wps->snonce + WPS_SECRET_NONCE_LEN;
 +      addr[1] = wps->psk2;
 +      hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash);
 +      wpa_hexdump(MSG_DEBUG, "WPS: E-Hash2", hash, SHA256_MAC_LEN);
 +
 +      return 0;
 +}
 +
 +
 +static int wps_build_e_snonce1(struct wps_data *wps, struct wpabuf *msg)
 +{
 +      wpa_printf(MSG_DEBUG, "WPS:  * E-SNonce1");
 +      wpabuf_put_be16(msg, ATTR_E_SNONCE1);
 +      wpabuf_put_be16(msg, WPS_SECRET_NONCE_LEN);
 +      wpabuf_put_data(msg, wps->snonce, WPS_SECRET_NONCE_LEN);
 +      return 0;
 +}
 +
 +
 +static int wps_build_e_snonce2(struct wps_data *wps, struct wpabuf *msg)
 +{
 +      wpa_printf(MSG_DEBUG, "WPS:  * E-SNonce2");
 +      wpabuf_put_be16(msg, ATTR_E_SNONCE2);
 +      wpabuf_put_be16(msg, WPS_SECRET_NONCE_LEN);
 +      wpabuf_put_data(msg, wps->snonce + WPS_SECRET_NONCE_LEN,
 +                      WPS_SECRET_NONCE_LEN);
 +      return 0;
 +}
 +
 +
 +static struct wpabuf * wps_build_m1(struct wps_data *wps)
 +{
 +      struct wpabuf *msg;
 +      u16 config_methods;
 +
 +      if (random_get_bytes(wps->nonce_e, WPS_NONCE_LEN) < 0)
 +              return NULL;
 +      wpa_hexdump(MSG_DEBUG, "WPS: Enrollee Nonce",
 +                  wps->nonce_e, WPS_NONCE_LEN);
 +
 +      wpa_printf(MSG_DEBUG, "WPS: Building Message M1");
 +      msg = wpabuf_alloc(1000);
 +      if (msg == NULL)
 +              return NULL;
 +
 +      config_methods = wps->wps->config_methods;
 +      if (wps->wps->ap && !wps->pbc_in_m1 &&
 +          (wps->dev_password_len != 0 ||
 +           (config_methods & WPS_CONFIG_DISPLAY))) {
 +              /*
 +               * These are the methods that the AP supports as an Enrollee
 +               * for adding external Registrars, so remove PushButton.
 +               *
 +               * As a workaround for Windows 7 mechanism for probing WPS
 +               * capabilities from M1, leave PushButton option if no PIN
 +               * method is available or if WPS configuration enables PBC
 +               * workaround.
 +               */
 +              config_methods &= ~WPS_CONFIG_PUSHBUTTON;
 +              config_methods &= ~(WPS_CONFIG_VIRT_PUSHBUTTON |
 +                                  WPS_CONFIG_PHY_PUSHBUTTON);
 +      }
 +
 +      if (wps_build_version(msg) ||
 +          wps_build_msg_type(msg, WPS_M1) ||
 +          wps_build_uuid_e(msg, wps->uuid_e) ||
 +          wps_build_mac_addr(msg, wps->mac_addr_e) ||
 +          wps_build_enrollee_nonce(wps, msg) ||
 +          wps_build_public_key(wps, msg) ||
 +          wps_build_auth_type_flags(wps, msg) ||
 +          wps_build_encr_type_flags(wps, msg) ||
 +          wps_build_conn_type_flags(wps, msg) ||
 +          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->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) ||
 +          wps_build_os_version(&wps->wps->dev, msg) ||
 +          wps_build_wfa_ext(msg, 0, NULL, 0) ||
 +          wps_build_vendor_ext_m1(&wps->wps->dev, msg)) {
 +              wpabuf_free(msg);
 +              return NULL;
 +      }
 +
 +      wps->state = RECV_M2;
 +      return msg;
 +}
 +
 +
 +static struct wpabuf * wps_build_m3(struct wps_data *wps)
 +{
 +      struct wpabuf *msg;
 +
 +      wpa_printf(MSG_DEBUG, "WPS: Building Message M3");
 +
 +      if (wps->dev_password == NULL) {
 +              wpa_printf(MSG_DEBUG, "WPS: No Device Password available");
 +              return NULL;
 +      }
 +      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;
 +
 +      if (wps_build_version(msg) ||
 +          wps_build_msg_type(msg, WPS_M3) ||
 +          wps_build_registrar_nonce(wps, msg) ||
 +          wps_build_e_hash(wps, msg) ||
 +          wps_build_wfa_ext(msg, 0, NULL, 0) ||
 +          wps_build_authenticator(wps, msg)) {
 +              wpabuf_free(msg);
 +              return NULL;
 +      }
 +
 +      wps->state = RECV_M4;
 +      return msg;
 +}
 +
 +
 +static struct wpabuf * wps_build_m5(struct wps_data *wps)
 +{
 +      struct wpabuf *msg, *plain;
 +
 +      wpa_printf(MSG_DEBUG, "WPS: Building Message M5");
 +
 +      plain = wpabuf_alloc(200);
 +      if (plain == NULL)
 +              return NULL;
 +
 +      msg = wpabuf_alloc(1000);
 +      if (msg == NULL) {
 +              wpabuf_free(plain);
 +              return NULL;
 +      }
 +
 +      if (wps_build_version(msg) ||
 +          wps_build_msg_type(msg, WPS_M5) ||
 +          wps_build_registrar_nonce(wps, msg) ||
 +          wps_build_e_snonce1(wps, plain) ||
 +          wps_build_key_wrap_auth(wps, plain) ||
 +          wps_build_encr_settings(wps, msg, plain) ||
 +          wps_build_wfa_ext(msg, 0, NULL, 0) ||
 +          wps_build_authenticator(wps, msg)) {
 +              wpabuf_free(plain);
 +              wpabuf_free(msg);
 +              return NULL;
 +      }
 +      wpabuf_free(plain);
 +
 +      wps->state = RECV_M6;
 +      return msg;
 +}
 +
 +
 +static int wps_build_cred_ssid(struct wps_data *wps, struct wpabuf *msg)
 +{
 +      wpa_printf(MSG_DEBUG, "WPS:  * SSID");
 +      wpabuf_put_be16(msg, ATTR_SSID);
 +      wpabuf_put_be16(msg, wps->wps->ssid_len);
 +      wpabuf_put_data(msg, wps->wps->ssid, wps->wps->ssid_len);
 +      return 0;
 +}
 +
 +
 +static int wps_build_cred_auth_type(struct wps_data *wps, struct wpabuf *msg)
 +{
 +      u16 auth_type = wps->wps->ap_auth_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;
 +
 +      wpa_printf(MSG_DEBUG, "WPS:  * Authentication Type (0x%x)", auth_type);
 +      wpabuf_put_be16(msg, ATTR_AUTH_TYPE);
 +      wpabuf_put_be16(msg, 2);
 +      wpabuf_put_be16(msg, auth_type);
 +      return 0;
 +}
 +
 +
 +static int wps_build_cred_encr_type(struct wps_data *wps, struct wpabuf *msg)
 +{
 +      u16 encr_type = wps->wps->ap_encr_type;
 +
 +      /*
 +       * 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;
 +      }
 +
 +      wpa_printf(MSG_DEBUG, "WPS:  * Encryption Type (0x%x)", encr_type);
 +      wpabuf_put_be16(msg, ATTR_ENCR_TYPE);
 +      wpabuf_put_be16(msg, 2);
 +      wpabuf_put_be16(msg, encr_type);
 +      return 0;
 +}
 +
 +
 +static int wps_build_cred_network_key(struct wps_data *wps, struct wpabuf *msg)
 +{
 +      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);
 +      return 0;
 +}
 +
 +
 +static int wps_build_cred_mac_addr(struct wps_data *wps, struct wpabuf *msg)
 +{
 +      wpa_printf(MSG_DEBUG, "WPS:  * MAC Address (AP BSSID)");
 +      wpabuf_put_be16(msg, ATTR_MAC_ADDR);
 +      wpabuf_put_be16(msg, ETH_ALEN);
 +      wpabuf_put_data(msg, wps->wps->dev.mac_addr, ETH_ALEN);
 +      return 0;
 +}
 +
 +
 +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,
 +                              wps->wps->ap_settings_len);
 +              return 0;
 +      }
 +
 +      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;
 +}
 +
 +
 +static struct wpabuf * wps_build_m7(struct wps_data *wps)
 +{
 +      struct wpabuf *msg, *plain;
 +
 +      wpa_printf(MSG_DEBUG, "WPS: Building Message M7");
 +
 +      plain = wpabuf_alloc(500 + wps->wps->ap_settings_len);
 +      if (plain == NULL)
 +              return NULL;
 +
 +      msg = wpabuf_alloc(1000 + wps->wps->ap_settings_len);
 +      if (msg == NULL) {
 +              wpabuf_free(plain);
 +              return NULL;
 +      }
 +
 +      if (wps_build_version(msg) ||
 +          wps_build_msg_type(msg, WPS_M7) ||
 +          wps_build_registrar_nonce(wps, msg) ||
 +          wps_build_e_snonce2(wps, plain) ||
 +          (wps->wps->ap && wps_build_ap_settings(wps, plain)) ||
 +          wps_build_key_wrap_auth(wps, plain) ||
 +          wps_build_encr_settings(wps, msg, plain) ||
 +          wps_build_wfa_ext(msg, 0, NULL, 0) ||
 +          wps_build_authenticator(wps, msg)) {
 +              wpabuf_free(plain);
 +              wpabuf_free(msg);
 +              return NULL;
 +      }
 +      wpabuf_free(plain);
 +
 +      if (wps->wps->ap && wps->wps->registrar) {
 +              /*
 +               * If the Registrar is only learning our current configuration,
 +               * it may not continue protocol run to successful completion.
 +               * Store information here to make sure it remains available.
 +               */
 +              wps_device_store(wps->wps->registrar, &wps->peer_dev,
 +                               wps->uuid_r);
 +      }
 +
 +      wps->state = RECV_M8;
 +      return msg;
 +}
 +
 +
 +static struct wpabuf * wps_build_wsc_done(struct wps_data *wps)
 +{
 +      struct wpabuf *msg;
 +
 +      wpa_printf(MSG_DEBUG, "WPS: Building Message WSC_Done");
 +
 +      msg = wpabuf_alloc(1000);
 +      if (msg == NULL)
 +              return NULL;
 +
 +      if (wps_build_version(msg) ||
 +          wps_build_msg_type(msg, WPS_WSC_DONE) ||
 +          wps_build_enrollee_nonce(wps, msg) ||
 +          wps_build_registrar_nonce(wps, msg) ||
 +          wps_build_wfa_ext(msg, 0, NULL, 0)) {
 +              wpabuf_free(msg);
 +              return NULL;
 +      }
 +
 +      if (wps->wps->ap)
 +              wps->state = RECV_ACK;
 +      else {
 +              wps_success_event(wps->wps, wps->peer_dev.mac_addr);
 +              wps->state = WPS_FINISHED;
 +      }
 +      return msg;
 +}
 +
 +
 +struct wpabuf * wps_enrollee_get_msg(struct wps_data *wps,
 +                                   enum wsc_op_code *op_code)
 +{
 +      struct wpabuf *msg;
 +
 +      switch (wps->state) {
 +      case SEND_M1:
 +              msg = wps_build_m1(wps);
 +              *op_code = WSC_MSG;
 +              break;
 +      case SEND_M3:
 +              msg = wps_build_m3(wps);
 +              *op_code = WSC_MSG;
 +              break;
 +      case SEND_M5:
 +              msg = wps_build_m5(wps);
 +              *op_code = WSC_MSG;
 +              break;
 +      case SEND_M7:
 +              msg = wps_build_m7(wps);
 +              *op_code = WSC_MSG;
 +              break;
 +      case RECEIVED_M2D:
 +              if (wps->wps->ap) {
 +                      msg = wps_build_wsc_nack(wps);
 +                      *op_code = WSC_NACK;
 +                      break;
 +              }
 +              msg = wps_build_wsc_ack(wps);
 +              *op_code = WSC_ACK;
 +              if (msg) {
 +                      /* Another M2/M2D may be received */
 +                      wps->state = RECV_M2;
 +              }
 +              break;
 +      case SEND_WSC_NACK:
 +              msg = wps_build_wsc_nack(wps);
 +              *op_code = WSC_NACK;
 +              break;
 +      case WPS_MSG_DONE:
 +              msg = wps_build_wsc_done(wps);
 +              *op_code = WSC_Done;
 +              break;
 +      default:
 +              wpa_printf(MSG_DEBUG, "WPS: Unsupported state %d for building "
 +                         "a message", wps->state);
 +              msg = NULL;
 +              break;
 +      }
 +
 +      if (*op_code == WSC_MSG && msg) {
 +              /* Save a copy of the last message for Authenticator derivation
 +               */
 +              wpabuf_free(wps->last_msg);
 +              wps->last_msg = wpabuf_dup(msg);
 +      }
 +
 +      return msg;
 +}
 +
 +
 +static int wps_process_registrar_nonce(struct wps_data *wps, const u8 *r_nonce)
 +{
 +      if (r_nonce == NULL) {
 +              wpa_printf(MSG_DEBUG, "WPS: No Registrar Nonce received");
 +              return -1;
 +      }
 +
 +      os_memcpy(wps->nonce_r, r_nonce, WPS_NONCE_LEN);
 +      wpa_hexdump(MSG_DEBUG, "WPS: Registrar Nonce",
 +                  wps->nonce_r, WPS_NONCE_LEN);
 +
 +      return 0;
 +}
 +
 +
 +static int wps_process_enrollee_nonce(struct wps_data *wps, const u8 *e_nonce)
 +{
 +      if (e_nonce == NULL) {
 +              wpa_printf(MSG_DEBUG, "WPS: No Enrollee Nonce received");
 +              return -1;
 +      }
 +
 +      if (os_memcmp(wps->nonce_e, e_nonce, WPS_NONCE_LEN) != 0) {
 +              wpa_printf(MSG_DEBUG, "WPS: Invalid Enrollee Nonce received");
 +              return -1;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +static int wps_process_uuid_r(struct wps_data *wps, const u8 *uuid_r)
 +{
 +      if (uuid_r == NULL) {
 +              wpa_printf(MSG_DEBUG, "WPS: No UUID-R received");
 +              return -1;
 +      }
 +
 +      os_memcpy(wps->uuid_r, uuid_r, WPS_UUID_LEN);
 +      wpa_hexdump(MSG_DEBUG, "WPS: UUID-R", wps->uuid_r, WPS_UUID_LEN);
 +
 +      return 0;
 +}
 +
 +
 +static int wps_process_pubkey(struct wps_data *wps, const u8 *pk,
 +                            size_t pk_len)
 +{
 +      if (pk == NULL || pk_len == 0) {
 +              wpa_printf(MSG_DEBUG, "WPS: No Public Key received");
 +              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)
 +              return -1;
 +
 +      if (wps_derive_keys(wps) < 0)
 +              return -1;
 +
 +      return 0;
 +}
 +
 +
 +static int wps_process_r_hash1(struct wps_data *wps, const u8 *r_hash1)
 +{
 +      if (r_hash1 == NULL) {
 +              wpa_printf(MSG_DEBUG, "WPS: No R-Hash1 received");
 +              return -1;
 +      }
 +
 +      os_memcpy(wps->peer_hash1, r_hash1, WPS_HASH_LEN);
 +      wpa_hexdump(MSG_DEBUG, "WPS: R-Hash1", wps->peer_hash1, WPS_HASH_LEN);
 +
 +      return 0;
 +}
 +
 +
 +static int wps_process_r_hash2(struct wps_data *wps, const u8 *r_hash2)
 +{
 +      if (r_hash2 == NULL) {
 +              wpa_printf(MSG_DEBUG, "WPS: No R-Hash2 received");
 +              return -1;
 +      }
 +
 +      os_memcpy(wps->peer_hash2, r_hash2, WPS_HASH_LEN);
 +      wpa_hexdump(MSG_DEBUG, "WPS: R-Hash2", wps->peer_hash2, WPS_HASH_LEN);
 +
 +      return 0;
 +}
 +
 +
 +static int wps_process_r_snonce1(struct wps_data *wps, const u8 *r_snonce1)
 +{
 +      u8 hash[SHA256_MAC_LEN];
 +      const u8 *addr[4];
 +      size_t len[4];
 +
 +      if (r_snonce1 == NULL) {
 +              wpa_printf(MSG_DEBUG, "WPS: No R-SNonce1 received");
 +              return -1;
 +      }
 +
 +      wpa_hexdump_key(MSG_DEBUG, "WPS: R-SNonce1", r_snonce1,
 +                      WPS_SECRET_NONCE_LEN);
 +
 +      /* R-Hash1 = HMAC_AuthKey(R-S1 || PSK1 || PK_E || PK_R) */
 +      addr[0] = r_snonce1;
 +      len[0] = WPS_SECRET_NONCE_LEN;
 +      addr[1] = wps->psk1;
 +      len[1] = WPS_PSK_LEN;
 +      addr[2] = wpabuf_head(wps->dh_pubkey_e);
 +      len[2] = wpabuf_len(wps->dh_pubkey_e);
 +      addr[3] = wpabuf_head(wps->dh_pubkey_r);
 +      len[3] = wpabuf_len(wps->dh_pubkey_r);
 +      hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash);
 +
 +      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->peer_dev.mac_addr);
 +              return -1;
 +      }
 +
 +      wpa_printf(MSG_DEBUG, "WPS: Registrar proved knowledge of the first "
 +                 "half of the device password");
 +
 +      return 0;
 +}
 +
 +
 +static int wps_process_r_snonce2(struct wps_data *wps, const u8 *r_snonce2)
 +{
 +      u8 hash[SHA256_MAC_LEN];
 +      const u8 *addr[4];
 +      size_t len[4];
 +
 +      if (r_snonce2 == NULL) {
 +              wpa_printf(MSG_DEBUG, "WPS: No R-SNonce2 received");
 +              return -1;
 +      }
 +
 +      wpa_hexdump_key(MSG_DEBUG, "WPS: R-SNonce2", r_snonce2,
 +                      WPS_SECRET_NONCE_LEN);
 +
 +      /* R-Hash2 = HMAC_AuthKey(R-S2 || PSK2 || PK_E || PK_R) */
 +      addr[0] = r_snonce2;
 +      len[0] = WPS_SECRET_NONCE_LEN;
 +      addr[1] = wps->psk2;
 +      len[1] = WPS_PSK_LEN;
 +      addr[2] = wpabuf_head(wps->dh_pubkey_e);
 +      len[2] = wpabuf_len(wps->dh_pubkey_e);
 +      addr[3] = wpabuf_head(wps->dh_pubkey_r);
 +      len[3] = wpabuf_len(wps->dh_pubkey_r);
 +      hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash);
 +
 +      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->peer_dev.mac_addr);
 +              return -1;
 +      }
 +
 +      wpa_printf(MSG_DEBUG, "WPS: Registrar proved knowledge of the second "
 +                 "half of the device password");
 +
 +      return 0;
 +}
 +
 +
 +static int wps_process_cred_e(struct wps_data *wps, const u8 *cred,
 +                            size_t cred_len, int wps2)
 +{
 +      struct wps_parse_attr attr;
 +      struct wpabuf msg;
 +      int ret = 0;
 +
 +      wpa_printf(MSG_DEBUG, "WPS: Received Credential");
 +      os_memset(&wps->cred, 0, sizeof(wps->cred));
 +      wpabuf_set(&msg, cred, cred_len);
 +      if (wps_parse_msg(&msg, &attr) < 0 ||
 +          wps_process_cred(&attr, &wps->cred))
 +              return -1;
 +
 +      if (os_memcmp(wps->cred.mac_addr, wps->wps->dev.mac_addr, ETH_ALEN) !=
 +          0) {
 +              wpa_printf(MSG_DEBUG, "WPS: MAC Address in the Credential ("
 +                         MACSTR ") does not match with own address (" MACSTR
 +                         ")", MAC2STR(wps->cred.mac_addr),
 +                         MAC2STR(wps->wps->dev.mac_addr));
 +              /*
 +               * In theory, this could be consider fatal error, but there are
 +               * number of deployed implementations using other address here
 +               * due to unclarity in the specification. For interoperability
 +               * reasons, allow this to be processed since we do not really
 +               * use the MAC Address information for anything.
 +               */
 +#ifdef CONFIG_WPS_STRICT
 +              if (wps2) {
 +                      wpa_printf(MSG_INFO, "WPS: Do not accept incorrect "
 +                                 "MAC Address in AP Settings");
 +                      return -1;
 +              }
 +#endif /* CONFIG_WPS_STRICT */
 +      }
 +
 +      if (!(wps->cred.encr_type &
 +            (WPS_ENCR_NONE | WPS_ENCR_TKIP | WPS_ENCR_AES))) {
 +              if (wps->cred.encr_type & WPS_ENCR_WEP) {
 +                      wpa_printf(MSG_INFO, "WPS: Reject Credential "
 +                                 "due to WEP configuration");
 +                      wps->error_indication = WPS_EI_SECURITY_WEP_PROHIBITED;
 +                      return -2;
 +              }
 +
 +              wpa_printf(MSG_INFO, "WPS: Reject Credential due to "
 +                         "invalid encr_type 0x%x", wps->cred.encr_type);
 +              return -1;
 +      }
 +
 +      if (wps->wps->cred_cb) {
 +              wps->cred.cred_attr = cred - 4;
 +              wps->cred.cred_attr_len = cred_len + 4;
 +              ret = wps->wps->cred_cb(wps->wps->cb_ctx, &wps->cred);
 +              wps->cred.cred_attr = NULL;
 +              wps->cred.cred_attr_len = 0;
 +      }
 +
 +      return ret;
 +}
 +
 +
 +static int wps_process_creds(struct wps_data *wps, const u8 *cred[],
-               wps->wps->cred_cb(wps->wps->cb_ctx, &cred);
++                           u16 cred_len[], unsigned int num_cred, int wps2)
 +{
 +      size_t i;
 +      int ok = 0;
 +
 +      if (wps->wps->ap)
 +              return 0;
 +
 +      if (num_cred == 0) {
 +              wpa_printf(MSG_DEBUG, "WPS: No Credential attributes "
 +                         "received");
 +              return -1;
 +      }
 +
 +      for (i = 0; i < num_cred; i++) {
 +              int res;
 +              res = wps_process_cred_e(wps, cred[i], cred_len[i], wps2);
 +              if (res == 0)
 +                      ok++;
 +              else if (res == -2)
 +                      wpa_printf(MSG_DEBUG, "WPS: WEP credential skipped");
 +              else
 +                      return -1;
 +      }
 +
 +      if (ok == 0) {
 +              wpa_printf(MSG_DEBUG, "WPS: No valid Credential attribute "
 +                         "received");
 +              return -1;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +static int wps_process_ap_settings_e(struct wps_data *wps,
 +                                   struct wps_parse_attr *attr,
 +                                   struct wpabuf *attrs, int wps2)
 +{
 +      struct wps_credential cred;
++      int ret = 0;
 +
 +      if (!wps->wps->ap)
 +              return 0;
 +
 +      if (wps_process_ap_settings(attr, &cred) < 0)
 +              return -1;
 +
 +      wpa_printf(MSG_INFO, "WPS: Received new AP configuration from "
 +                 "Registrar");
 +
 +      if (os_memcmp(cred.mac_addr, wps->wps->dev.mac_addr, ETH_ALEN) !=
 +          0) {
 +              wpa_printf(MSG_DEBUG, "WPS: MAC Address in the AP Settings ("
 +                         MACSTR ") does not match with own address (" MACSTR
 +                         ")", MAC2STR(cred.mac_addr),
 +                         MAC2STR(wps->wps->dev.mac_addr));
 +              /*
 +               * In theory, this could be consider fatal error, but there are
 +               * number of deployed implementations using other address here
 +               * due to unclarity in the specification. For interoperability
 +               * reasons, allow this to be processed since we do not really
 +               * use the MAC Address information for anything.
 +               */
 +#ifdef CONFIG_WPS_STRICT
 +              if (wps2) {
 +                      wpa_printf(MSG_INFO, "WPS: Do not accept incorrect "
 +                                 "MAC Address in AP Settings");
 +                      return -1;
 +              }
 +#endif /* CONFIG_WPS_STRICT */
 +      }
 +
 +      if (!(cred.encr_type & (WPS_ENCR_NONE | WPS_ENCR_TKIP | WPS_ENCR_AES)))
 +      {
 +              if (cred.encr_type & WPS_ENCR_WEP) {
 +                      wpa_printf(MSG_INFO, "WPS: Reject new AP settings "
 +                                 "due to WEP configuration");
 +                      wps->error_indication = WPS_EI_SECURITY_WEP_PROHIBITED;
 +                      return -1;
 +              }
 +
 +              wpa_printf(MSG_INFO, "WPS: Reject new AP settings due to "
 +                         "invalid encr_type 0x%x", cred.encr_type);
 +              return -1;
 +      }
 +
 +#ifdef CONFIG_WPS_STRICT
 +      if (wps2) {
 +              if ((cred.encr_type & (WPS_ENCR_TKIP | WPS_ENCR_AES)) ==
 +                  WPS_ENCR_TKIP ||
 +                  (cred.auth_type & (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK)) ==
 +                  WPS_AUTH_WPAPSK) {
 +                      wpa_printf(MSG_INFO, "WPS-STRICT: Invalid WSC 2.0 "
 +                                 "AP Settings: WPA-Personal/TKIP only");
 +                      wps->error_indication =
 +                              WPS_EI_SECURITY_TKIP_ONLY_PROHIBITED;
 +                      return -1;
 +              }
 +      }
 +#endif /* CONFIG_WPS_STRICT */
 +
 +      if ((cred.encr_type & (WPS_ENCR_TKIP | WPS_ENCR_AES)) == WPS_ENCR_TKIP)
 +      {
 +              wpa_printf(MSG_DEBUG, "WPS: Upgrade encr_type TKIP -> "
 +                         "TKIP+AES");
 +              cred.encr_type |= WPS_ENCR_AES;
 +      }
 +
 +      if ((cred.auth_type & (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK)) ==
 +          WPS_AUTH_WPAPSK) {
 +              wpa_printf(MSG_DEBUG, "WPS: Upgrade auth_type WPAPSK -> "
 +                         "WPAPSK+WPA2PSK");
 +              cred.auth_type |= WPS_AUTH_WPA2PSK;
 +      }
 +
 +      if (wps->wps->cred_cb) {
 +              cred.cred_attr = wpabuf_head(attrs);
 +              cred.cred_attr_len = wpabuf_len(attrs);
-       return 0;
++              ret = wps->wps->cred_cb(wps->wps->cb_ctx, &cred);
 +      }
 +
++      return ret;
 +}
 +
 +
 +static int wps_process_dev_pw_id(struct wps_data *wps, const u8 *dev_pw_id)
 +{
 +      u16 id;
 +
 +      if (dev_pw_id == NULL) {
 +              wpa_printf(MSG_DEBUG, "WPS: Device Password ID");
 +              return -1;
 +      }
 +
 +      id = WPA_GET_BE16(dev_pw_id);
 +      if (wps->dev_pw_id == id) {
 +              wpa_printf(MSG_DEBUG, "WPS: Device Password ID %u", id);
 +              return 0;
 +      }
 +
 +#ifdef CONFIG_P2P
 +      if ((id == DEV_PW_DEFAULT &&
 +           wps->dev_pw_id == DEV_PW_REGISTRAR_SPECIFIED) ||
 +          (id == DEV_PW_REGISTRAR_SPECIFIED &&
 +           wps->dev_pw_id == DEV_PW_DEFAULT)) {
 +              /*
 +               * Common P2P use cases indicate whether the PIN is from the
 +               * client or GO using Device Password Id in M1/M2 in a way that
 +               * does not look fully compliant with WSC specification. Anyway,
 +               * this is deployed and needs to be allowed, so ignore changes
 +               * between Registrar-Specified and Default PIN.
 +               */
 +              wpa_printf(MSG_DEBUG, "WPS: Allow PIN Device Password ID "
 +                         "change");
 +              return 0;
 +      }
 +#endif /* CONFIG_P2P */
 +
 +      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");
 +              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;
 +              wps->alt_dev_password = NULL;
 +              wps->alt_dev_password_len = 0;
 +              return 0;
 +      }
 +
 +      return -1;
 +}
 +
 +
 +static enum wps_process_res wps_process_m2(struct wps_data *wps,
 +                                         const struct wpabuf *msg,
 +                                         struct wps_parse_attr *attr)
 +{
 +      wpa_printf(MSG_DEBUG, "WPS: Received M2");
 +
 +      if (wps->state != RECV_M2) {
 +              wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
 +                         "receiving M2", wps->state);
 +              wps->state = SEND_WSC_NACK;
 +              return WPS_CONTINUE;
 +      }
 +
 +      if (wps_process_registrar_nonce(wps, attr->registrar_nonce) ||
 +          wps_process_enrollee_nonce(wps, attr->enrollee_nonce) ||
 +          wps_process_uuid_r(wps, attr->uuid_r) ||
 +          wps_process_dev_pw_id(wps, attr->dev_password_id)) {
 +              wps->state = SEND_WSC_NACK;
 +              return WPS_CONTINUE;
 +      }
 +
 +      /*
 +       * Stop here on an AP as an Enrollee if AP Setup is locked unless the
 +       * special locked mode is used to allow protocol run up to M7 in order
 +       * to support external Registrars that only learn the current AP
 +       * configuration without changing it.
 +       */
 +      if (wps->wps->ap &&
 +          ((wps->wps->ap_setup_locked && wps->wps->ap_setup_locked != 2) ||
 +           wps->dev_password == NULL)) {
 +              wpa_printf(MSG_DEBUG, "WPS: AP Setup is locked - refuse "
 +                         "registration of a new Registrar");
 +              wps->config_error = WPS_CFG_SETUP_LOCKED;
 +              wps->state = SEND_WSC_NACK;
 +              return WPS_CONTINUE;
 +      }
 +
 +      if (wps_process_pubkey(wps, attr->public_key, attr->public_key_len) ||
 +          wps_process_authenticator(wps, attr->authenticator, msg) ||
 +          wps_process_device_attrs(&wps->peer_dev, attr)) {
 +              wps->state = SEND_WSC_NACK;
 +              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;
 +}
 +
 +
 +static enum wps_process_res wps_process_m2d(struct wps_data *wps,
 +                                          struct wps_parse_attr *attr)
 +{
 +      wpa_printf(MSG_DEBUG, "WPS: Received M2D");
 +
 +      if (wps->state != RECV_M2) {
 +              wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
 +                         "receiving M2D", wps->state);
 +              wps->state = SEND_WSC_NACK;
 +              return WPS_CONTINUE;
 +      }
 +
 +      wpa_hexdump_ascii(MSG_DEBUG, "WPS: Manufacturer",
 +                        attr->manufacturer, attr->manufacturer_len);
 +      wpa_hexdump_ascii(MSG_DEBUG, "WPS: Model Name",
 +                        attr->model_name, attr->model_name_len);
 +      wpa_hexdump_ascii(MSG_DEBUG, "WPS: Model Number",
 +                        attr->model_number, attr->model_number_len);
 +      wpa_hexdump_ascii(MSG_DEBUG, "WPS: Serial Number",
 +                        attr->serial_number, attr->serial_number_len);
 +      wpa_hexdump_ascii(MSG_DEBUG, "WPS: Device Name",
 +                        attr->dev_name, attr->dev_name_len);
 +
 +      if (wps->wps->event_cb) {
 +              union wps_event_data data;
 +              struct wps_event_m2d *m2d = &data.m2d;
 +              os_memset(&data, 0, sizeof(data));
 +              if (attr->config_methods)
 +                      m2d->config_methods =
 +                              WPA_GET_BE16(attr->config_methods);
 +              m2d->manufacturer = attr->manufacturer;
 +              m2d->manufacturer_len = attr->manufacturer_len;
 +              m2d->model_name = attr->model_name;
 +              m2d->model_name_len = attr->model_name_len;
 +              m2d->model_number = attr->model_number;
 +              m2d->model_number_len = attr->model_number_len;
 +              m2d->serial_number = attr->serial_number;
 +              m2d->serial_number_len = attr->serial_number_len;
 +              m2d->dev_name = attr->dev_name;
 +              m2d->dev_name_len = attr->dev_name_len;
 +              m2d->primary_dev_type = attr->primary_dev_type;
 +              if (attr->config_error)
 +                      m2d->config_error =
 +                              WPA_GET_BE16(attr->config_error);
 +              if (attr->dev_password_id)
 +                      m2d->dev_password_id =
 +                              WPA_GET_BE16(attr->dev_password_id);
 +              wps->wps->event_cb(wps->wps->cb_ctx, WPS_EV_M2D, &data);
 +      }
 +
 +      wps->state = RECEIVED_M2D;
 +      return WPS_CONTINUE;
 +}
 +
 +
 +static enum wps_process_res wps_process_m4(struct wps_data *wps,
 +                                         const struct wpabuf *msg,
 +                                         struct wps_parse_attr *attr)
 +{
 +      struct wpabuf *decrypted;
 +      struct wps_parse_attr eattr;
 +
 +      wpa_printf(MSG_DEBUG, "WPS: Received M4");
 +
 +      if (wps->state != RECV_M4) {
 +              wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
 +                         "receiving M4", wps->state);
 +              wps->state = SEND_WSC_NACK;
 +              return WPS_CONTINUE;
 +      }
 +
 +      if (wps_process_enrollee_nonce(wps, attr->enrollee_nonce) ||
 +          wps_process_authenticator(wps, attr->authenticator, msg) ||
 +          wps_process_r_hash1(wps, attr->r_hash1) ||
 +          wps_process_r_hash2(wps, attr->r_hash2)) {
 +              wps->state = SEND_WSC_NACK;
 +              return WPS_CONTINUE;
 +      }
 +
 +      decrypted = wps_decrypt_encr_settings(wps, attr->encr_settings,
 +                                            attr->encr_settings_len);
 +      if (decrypted == NULL) {
 +              wpa_printf(MSG_DEBUG, "WPS: Failed to decrypted Encrypted "
 +                         "Settings attribute");
 +              wps->state = SEND_WSC_NACK;
 +              return WPS_CONTINUE;
 +      }
 +
 +      if (wps_validate_m4_encr(decrypted, attr->version2 != NULL) < 0) {
 +              wpabuf_free(decrypted);
 +              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_r_snonce1(wps, eattr.r_snonce1)) {
 +              wpabuf_free(decrypted);
 +              wps->state = SEND_WSC_NACK;
 +              return WPS_CONTINUE;
 +      }
 +      wpabuf_free(decrypted);
 +
 +      wps->state = SEND_M5;
 +      return WPS_CONTINUE;
 +}
 +
 +
 +static enum wps_process_res wps_process_m6(struct wps_data *wps,
 +                                         const struct wpabuf *msg,
 +                                         struct wps_parse_attr *attr)
 +{
 +      struct wpabuf *decrypted;
 +      struct wps_parse_attr eattr;
 +
 +      wpa_printf(MSG_DEBUG, "WPS: Received M6");
 +
 +      if (wps->state != RECV_M6) {
 +              wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
 +                         "receiving M6", wps->state);
 +              wps->state = SEND_WSC_NACK;
 +              return WPS_CONTINUE;
 +      }
 +
 +      if (wps_process_enrollee_nonce(wps, attr->enrollee_nonce) ||
 +          wps_process_authenticator(wps, attr->authenticator, msg)) {
 +              wps->state = SEND_WSC_NACK;
 +              return WPS_CONTINUE;
 +      }
 +
 +      decrypted = wps_decrypt_encr_settings(wps, attr->encr_settings,
 +                                            attr->encr_settings_len);
 +      if (decrypted == NULL) {
 +              wpa_printf(MSG_DEBUG, "WPS: Failed to decrypted Encrypted "
 +                         "Settings attribute");
 +              wps->state = SEND_WSC_NACK;
 +              return WPS_CONTINUE;
 +      }
 +
 +      if (wps_validate_m6_encr(decrypted, attr->version2 != NULL) < 0) {
 +              wpabuf_free(decrypted);
 +              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_r_snonce2(wps, eattr.r_snonce2)) {
 +              wpabuf_free(decrypted);
 +              wps->state = SEND_WSC_NACK;
 +              return WPS_CONTINUE;
 +      }
 +      wpabuf_free(decrypted);
 +
 +      if (wps->wps->ap)
 +              wps->wps->event_cb(wps->wps->cb_ctx, WPS_EV_AP_PIN_SUCCESS,
 +                                 NULL);
 +
 +      wps->state = SEND_M7;
 +      return WPS_CONTINUE;
 +}
 +
 +
 +static enum wps_process_res wps_process_m8(struct wps_data *wps,
 +                                         const struct wpabuf *msg,
 +                                         struct wps_parse_attr *attr)
 +{
 +      struct wpabuf *decrypted;
 +      struct wps_parse_attr eattr;
 +
 +      wpa_printf(MSG_DEBUG, "WPS: Received M8");
 +
 +      if (wps->state != RECV_M8) {
 +              wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
 +                         "receiving M8", wps->state);
 +              wps->state = SEND_WSC_NACK;
 +              return WPS_CONTINUE;
 +      }
 +
 +      if (wps_process_enrollee_nonce(wps, attr->enrollee_nonce) ||
 +          wps_process_authenticator(wps, attr->authenticator, msg)) {
 +              wps->state = SEND_WSC_NACK;
 +              return WPS_CONTINUE;
 +      }
 +
 +      if (wps->wps->ap && wps->wps->ap_setup_locked) {
 +              /*
 +               * Stop here if special ap_setup_locked == 2 mode allowed the
 +               * protocol to continue beyond M2. This allows ER to learn the
 +               * current AP settings without changing them.
 +               */
 +              wpa_printf(MSG_DEBUG, "WPS: AP Setup is locked - refuse "
 +                         "registration of a new Registrar");
 +              wps->config_error = WPS_CFG_SETUP_LOCKED;
 +              wps->state = SEND_WSC_NACK;
 +              return WPS_CONTINUE;
 +      }
 +
 +      decrypted = wps_decrypt_encr_settings(wps, attr->encr_settings,
 +                                            attr->encr_settings_len);
 +      if (decrypted == NULL) {
 +              wpa_printf(MSG_DEBUG, "WPS: Failed to decrypted Encrypted "
 +                         "Settings attribute");
 +              wps->state = SEND_WSC_NACK;
 +              return WPS_CONTINUE;
 +      }
 +
 +      if (wps_validate_m8_encr(decrypted, wps->wps->ap,
 +                               attr->version2 != NULL) < 0) {
 +              wpabuf_free(decrypted);
 +              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) ||
 +          wps_process_ap_settings_e(wps, &eattr, decrypted,
 +                                    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;
 +}
 +
 +
 +static enum wps_process_res wps_process_wsc_msg(struct wps_data *wps,
 +                                              const struct wpabuf *msg)
 +{
 +      struct wps_parse_attr attr;
 +      enum wps_process_res ret = WPS_CONTINUE;
 +
 +      wpa_printf(MSG_DEBUG, "WPS: Received WSC_MSG");
 +
 +      if (wps_parse_msg(msg, &attr) < 0)
 +              return WPS_FAILURE;
 +
 +      if (attr.enrollee_nonce == NULL ||
 +          os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN) != 0) {
 +              wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce");
 +              return WPS_FAILURE;
 +      }
 +
 +      if (attr.msg_type == NULL) {
 +              wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute");
 +              wps->state = SEND_WSC_NACK;
 +              return WPS_CONTINUE;
 +      }
 +
 +      switch (*attr.msg_type) {
 +      case WPS_M2:
 +              if (wps_validate_m2(msg) < 0)
 +                      return WPS_FAILURE;
 +              ret = wps_process_m2(wps, msg, &attr);
 +              break;
 +      case WPS_M2D:
 +              if (wps_validate_m2d(msg) < 0)
 +                      return WPS_FAILURE;
 +              ret = wps_process_m2d(wps, &attr);
 +              break;
 +      case WPS_M4:
 +              if (wps_validate_m4(msg) < 0)
 +                      return WPS_FAILURE;
 +              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->peer_dev.mac_addr);
 +              break;
 +      case WPS_M6:
 +              if (wps_validate_m6(msg) < 0)
 +                      return WPS_FAILURE;
 +              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->peer_dev.mac_addr);
 +              break;
 +      case WPS_M8:
 +              if (wps_validate_m8(msg) < 0)
 +                      return WPS_FAILURE;
 +              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->peer_dev.mac_addr);
 +              break;
 +      default:
 +              wpa_printf(MSG_DEBUG, "WPS: Unsupported Message Type %d",
 +                         *attr.msg_type);
 +              return WPS_FAILURE;
 +      }
 +
 +      /*
 +       * Save a copy of the last message for Authenticator derivation if we
 +       * are continuing. However, skip M2D since it is not authenticated and
 +       * neither is the ACK/NACK response frame. This allows the possibly
 +       * following M2 to be processed correctly by using the previously sent
 +       * M1 in Authenticator derivation.
 +       */
 +      if (ret == WPS_CONTINUE && *attr.msg_type != WPS_M2D) {
 +              /* Save a copy of the last message for Authenticator derivation
 +               */
 +              wpabuf_free(wps->last_msg);
 +              wps->last_msg = wpabuf_dup(msg);
 +      }
 +
 +      return ret;
 +}
 +
 +
 +static enum wps_process_res wps_process_wsc_ack(struct wps_data *wps,
 +                                              const struct wpabuf *msg)
 +{
 +      struct wps_parse_attr attr;
 +
 +      wpa_printf(MSG_DEBUG, "WPS: Received WSC_ACK");
 +
 +      if (wps_parse_msg(msg, &attr) < 0)
 +              return WPS_FAILURE;
 +
 +      if (attr.msg_type == NULL) {
 +              wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute");
 +              return WPS_FAILURE;
 +      }
 +
 +      if (*attr.msg_type != WPS_WSC_ACK) {
 +              wpa_printf(MSG_DEBUG, "WPS: Invalid Message Type %d",
 +                         *attr.msg_type);
 +              return WPS_FAILURE;
 +      }
 +
 +      if (attr.registrar_nonce == NULL ||
 +          os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN) != 0)
 +      {
 +              wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce");
 +              return WPS_FAILURE;
 +      }
 +
 +      if (attr.enrollee_nonce == NULL ||
 +          os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN) != 0) {
 +              wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce");
 +              return WPS_FAILURE;
 +      }
 +
 +      if (wps->state == RECV_ACK && wps->wps->ap) {
 +              wpa_printf(MSG_DEBUG, "WPS: External Registrar registration "
 +                         "completed successfully");
 +              wps_success_event(wps->wps, wps->peer_dev.mac_addr);
 +              wps->state = WPS_FINISHED;
 +              return WPS_DONE;
 +      }
 +
 +      return WPS_FAILURE;
 +}
 +
 +
 +static enum wps_process_res wps_process_wsc_nack(struct wps_data *wps,
 +                                               const struct wpabuf *msg)
 +{
 +      struct wps_parse_attr attr;
 +      u16 config_error;
 +
 +      wpa_printf(MSG_DEBUG, "WPS: Received WSC_NACK");
 +
 +      if (wps_parse_msg(msg, &attr) < 0)
 +              return WPS_FAILURE;
 +
 +      if (attr.msg_type == NULL) {
 +              wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute");
 +              return WPS_FAILURE;
 +      }
 +
 +      if (*attr.msg_type != WPS_WSC_NACK) {
 +              wpa_printf(MSG_DEBUG, "WPS: Invalid Message Type %d",
 +                         *attr.msg_type);
 +              return WPS_FAILURE;
 +      }
 +
 +      if (attr.registrar_nonce == NULL ||
 +          os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN) != 0)
 +      {
 +              wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce");
 +              wpa_hexdump(MSG_DEBUG, "WPS: Received Registrar Nonce",
 +                          attr.registrar_nonce, WPS_NONCE_LEN);
 +              wpa_hexdump(MSG_DEBUG, "WPS: Expected Registrar Nonce",
 +                          wps->nonce_r, WPS_NONCE_LEN);
 +              return WPS_FAILURE;
 +      }
 +
 +      if (attr.enrollee_nonce == NULL ||
 +          os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN) != 0) {
 +              wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce");
 +              wpa_hexdump(MSG_DEBUG, "WPS: Received Enrollee Nonce",
 +                          attr.enrollee_nonce, WPS_NONCE_LEN);
 +              wpa_hexdump(MSG_DEBUG, "WPS: Expected Enrollee Nonce",
 +                          wps->nonce_e, WPS_NONCE_LEN);
 +              return WPS_FAILURE;
 +      }
 +
 +      if (attr.config_error == NULL) {
 +              wpa_printf(MSG_DEBUG, "WPS: No Configuration Error attribute "
 +                         "in WSC_NACK");
 +              return WPS_FAILURE;
 +      }
 +
 +      config_error = WPA_GET_BE16(attr.config_error);
 +      wpa_printf(MSG_DEBUG, "WPS: Registrar terminated negotiation with "
 +                 "Configuration Error %d", config_error);
 +
 +      switch (wps->state) {
 +      case RECV_M4:
 +              wps_fail_event(wps->wps, WPS_M3, config_error,
 +                             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->peer_dev.mac_addr);
 +              break;
 +      case RECV_M8:
 +              wps_fail_event(wps->wps, WPS_M7, config_error,
 +                             wps->error_indication, wps->peer_dev.mac_addr);
 +              break;
 +      default:
 +              break;
 +      }
 +
 +      /* Followed by NACK if Enrollee is Supplicant or EAP-Failure if
 +       * Enrollee is Authenticator */
 +      wps->state = SEND_WSC_NACK;
 +
 +      return WPS_FAILURE;
 +}
 +
 +
 +enum wps_process_res wps_enrollee_process_msg(struct wps_data *wps,
 +                                            enum wsc_op_code op_code,
 +                                            const struct wpabuf *msg)
 +{
 +
 +      wpa_printf(MSG_DEBUG, "WPS: Processing received message (len=%lu "
 +                 "op_code=%d)",
 +                 (unsigned long) wpabuf_len(msg), op_code);
 +
 +      if (op_code == WSC_UPnP) {
 +              /* Determine the OpCode based on message type attribute */
 +              struct wps_parse_attr attr;
 +              if (wps_parse_msg(msg, &attr) == 0 && attr.msg_type) {
 +                      if (*attr.msg_type == WPS_WSC_ACK)
 +                              op_code = WSC_ACK;
 +                      else if (*attr.msg_type == WPS_WSC_NACK)
 +                              op_code = WSC_NACK;
 +              }
 +      }
 +
 +      switch (op_code) {
 +      case WSC_MSG:
 +      case WSC_UPnP:
 +              return wps_process_wsc_msg(wps, msg);
 +      case WSC_ACK:
 +              if (wps_validate_wsc_ack(msg) < 0)
 +                      return WPS_FAILURE;
 +              return wps_process_wsc_ack(wps, msg);
 +      case WSC_NACK:
 +              if (wps_validate_wsc_nack(msg) < 0)
 +                      return WPS_FAILURE;
 +              return wps_process_wsc_nack(wps, msg);
 +      default:
 +              wpa_printf(MSG_DEBUG, "WPS: Unsupported op_code %d", op_code);
 +              return WPS_FAILURE;
 +      }
 +}
index 078ff72781a6f37cf06c4947e0f4f38222a8868f,0000000000000000000000000000000000000000..b840acd924a74996fddeec8bf13c81634a03028f
mode 100644,000000..100644
--- /dev/null
@@@ -1,2095 -1,0 +1,2108 @@@
-               if (reply == NULL)
-                       break;
-               msg = os_zalloc(wpabuf_len(reply) + 1);
-               if (msg == NULL)
 +/*
 + * Wi-Fi Protected Setup - External Registrar
 + * 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.
 + */
 +
 +#include "includes.h"
 +
 +#include "common.h"
 +#include "base64.h"
 +#include "uuid.h"
 +#include "eloop.h"
 +#include "httpread.h"
 +#include "http_client.h"
 +#include "http_server.h"
 +#include "upnp_xml.h"
 +#include "wps_i.h"
 +#include "wps_upnp.h"
 +#include "wps_upnp_i.h"
 +#include "wps_er.h"
 +
 +
 +static void wps_er_deinit_finish(void *eloop_data, void *user_ctx);
 +static void wps_er_ap_timeout(void *eloop_data, void *user_ctx);
 +static void wps_er_sta_timeout(void *eloop_data, void *user_ctx);
 +static void wps_er_ap_process(struct wps_er_ap *ap, struct wpabuf *msg);
 +static int wps_er_send_get_device_info(struct wps_er_ap *ap,
 +                                     void (*m1_handler)(struct wps_er_ap *ap,
 +                                                        struct wpabuf *m1));
 +
 +
 +static void wps_er_sta_event(struct wps_context *wps, struct wps_er_sta *sta,
 +                           enum wps_event event)
 +{
 +      union wps_event_data data;
 +      struct wps_event_er_enrollee *ev = &data.enrollee;
 +
 +      if (wps->event_cb == NULL)
 +              return;
 +
 +      os_memset(&data, 0, sizeof(data));
 +      ev->uuid = sta->uuid;
 +      ev->mac_addr = sta->addr;
 +      ev->m1_received = sta->m1_received;
 +      ev->config_methods = sta->config_methods;
 +      ev->dev_passwd_id = sta->dev_passwd_id;
 +      ev->pri_dev_type = sta->pri_dev_type;
 +      ev->dev_name = sta->dev_name;
 +      ev->manufacturer = sta->manufacturer;
 +      ev->model_name = sta->model_name;
 +      ev->model_number = sta->model_number;
 +      ev->serial_number = sta->serial_number;
 +      wps->event_cb(wps->cb_ctx, event, &data);
 +}
 +
 +
 +static struct wps_er_sta * wps_er_sta_get(struct wps_er_ap *ap, const u8 *addr,
 +                                        const u8 *uuid)
 +{
 +      struct wps_er_sta *sta;
 +      dl_list_for_each(sta, &ap->sta, struct wps_er_sta, list) {
 +              if ((addr == NULL ||
 +                   os_memcmp(sta->addr, addr, ETH_ALEN) == 0) &&
 +                  (uuid == NULL ||
 +                   os_memcmp(uuid, sta->uuid, WPS_UUID_LEN) == 0))
 +                      return sta;
 +      }
 +      return NULL;
 +}
 +
 +
 +static void wps_er_sta_free(struct wps_er_sta *sta)
 +{
 +      wps_er_sta_event(sta->ap->er->wps, sta, WPS_EV_ER_ENROLLEE_REMOVE);
 +      if (sta->wps)
 +              wps_deinit(sta->wps);
 +      os_free(sta->manufacturer);
 +      os_free(sta->model_name);
 +      os_free(sta->model_number);
 +      os_free(sta->serial_number);
 +      os_free(sta->dev_name);
 +      http_client_free(sta->http);
 +      eloop_cancel_timeout(wps_er_sta_timeout, sta, NULL);
 +      os_free(sta->cred);
 +      os_free(sta);
 +}
 +
 +
 +static void wps_er_sta_remove_all(struct wps_er_ap *ap)
 +{
 +      struct wps_er_sta *prev, *sta;
 +      dl_list_for_each_safe(sta, prev, &ap->sta, struct wps_er_sta, list)
 +              wps_er_sta_free(sta);
 +}
 +
 +
 +static struct wps_er_ap * wps_er_ap_get(struct wps_er *er,
 +                                      struct in_addr *addr, const u8 *uuid,
 +                                      const u8 *mac_addr)
 +{
 +      struct wps_er_ap *ap;
 +      dl_list_for_each(ap, &er->ap, struct wps_er_ap, list) {
 +              if ((addr == NULL || ap->addr.s_addr == addr->s_addr) &&
 +                  (uuid == NULL ||
 +                   os_memcmp(uuid, ap->uuid, WPS_UUID_LEN) == 0) &&
 +                  (mac_addr == NULL ||
 +                   os_memcmp(mac_addr, ap->mac_addr, ETH_ALEN) == 0))
 +                      return ap;
 +      }
 +      return NULL;
 +}
 +
 +
 +static struct wps_er_ap * wps_er_ap_get_id(struct wps_er *er, unsigned int id)
 +{
 +      struct wps_er_ap *ap;
 +      dl_list_for_each(ap, &er->ap, struct wps_er_ap, list) {
 +              if (ap->id == id)
 +                      return ap;
 +      }
 +      return NULL;
 +}
 +
 +
 +static void wps_er_ap_event(struct wps_context *wps, struct wps_er_ap *ap,
 +                          enum wps_event event)
 +{
 +      union wps_event_data data;
 +      struct wps_event_er_ap *evap = &data.ap;
 +
 +      if (wps->event_cb == NULL)
 +              return;
 +
 +      os_memset(&data, 0, sizeof(data));
 +      evap->uuid = ap->uuid;
 +      evap->friendly_name = ap->friendly_name;
 +      evap->manufacturer = ap->manufacturer;
 +      evap->manufacturer_url = ap->manufacturer_url;
 +      evap->model_description = ap->model_description;
 +      evap->model_name = ap->model_name;
 +      evap->model_number = ap->model_number;
 +      evap->model_url = ap->model_url;
 +      evap->serial_number = ap->serial_number;
 +      evap->upc = ap->upc;
 +      evap->pri_dev_type = ap->pri_dev_type;
 +      evap->wps_state = ap->wps_state;
 +      evap->mac_addr = ap->mac_addr;
 +      wps->event_cb(wps->cb_ctx, event, &data);
 +}
 +
 +
 +static void wps_er_ap_free(struct wps_er_ap *ap)
 +{
 +      http_client_free(ap->http);
 +      ap->http = NULL;
 +
 +      os_free(ap->location);
 +      os_free(ap->friendly_name);
 +      os_free(ap->manufacturer);
 +      os_free(ap->manufacturer_url);
 +      os_free(ap->model_description);
 +      os_free(ap->model_name);
 +      os_free(ap->model_number);
 +      os_free(ap->model_url);
 +      os_free(ap->serial_number);
 +      os_free(ap->udn);
 +      os_free(ap->upc);
 +
 +      os_free(ap->scpd_url);
 +      os_free(ap->control_url);
 +      os_free(ap->event_sub_url);
 +
 +      os_free(ap->ap_settings);
 +
 +      os_free(ap);
 +}
 +
 +
 +static void wps_er_ap_unsubscribed(struct wps_er *er, struct wps_er_ap *ap)
 +{
 +      wpa_printf(MSG_DEBUG, "WPS ER: Unsubscribed from AP %s (%s)",
 +                 inet_ntoa(ap->addr), ap->location);
 +      dl_list_del(&ap->list);
 +      wps_er_ap_free(ap);
 +
 +      if (er->deinitializing && dl_list_empty(&er->ap_unsubscribing))
 +              wps_er_deinit_finish(er, NULL);
 +}
 +
 +
 +static void wps_er_http_unsubscribe_cb(void *ctx, struct http_client *c,
 +                                     enum http_client_event event)
 +{
 +      struct wps_er_ap *ap = ctx;
 +
 +      switch (event) {
 +      case HTTP_CLIENT_OK:
 +              wpa_printf(MSG_DEBUG, "WPS ER: Unsubscribed from events");
 +              ap->subscribed = 0;
 +              break;
 +      case HTTP_CLIENT_FAILED:
 +      case HTTP_CLIENT_INVALID_REPLY:
 +      case HTTP_CLIENT_TIMEOUT:
 +              wpa_printf(MSG_DEBUG, "WPS ER: Failed to unsubscribe from "
 +                         "events");
 +              break;
 +      }
 +      http_client_free(ap->http);
 +      ap->http = NULL;
 +
 +      /*
 +       * Need to get rid of the AP entry regardless of whether we managed to
 +       * unsubscribe cleanly or not.
 +       */
 +      wps_er_ap_unsubscribed(ap->er, ap);
 +}
 +
 +
 +static void wps_er_ap_unsubscribe(struct wps_er *er, struct wps_er_ap *ap)
 +{
 +      struct wpabuf *req;
 +      struct sockaddr_in dst;
 +      char *url, *path;
 +      char sid[100];
 +
 +      if (ap->event_sub_url == NULL) {
 +              wpa_printf(MSG_DEBUG, "WPS ER: No eventSubURL - cannot "
 +                         "subscribe");
 +              goto fail;
 +      }
 +      if (ap->http) {
 +              wpa_printf(MSG_DEBUG, "WPS ER: Pending HTTP request - cannot "
 +                         "send subscribe request");
 +              goto fail;
 +      }
 +
 +      url = http_client_url_parse(ap->event_sub_url, &dst, &path);
 +      if (url == NULL) {
 +              wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse eventSubURL");
 +              goto fail;
 +      }
 +
 +      req = wpabuf_alloc(os_strlen(ap->event_sub_url) + 1000);
 +      if (req == NULL) {
 +              os_free(url);
 +              goto fail;
 +      }
 +      uuid_bin2str(ap->sid, sid, sizeof(sid));
 +      wpabuf_printf(req,
 +                    "UNSUBSCRIBE %s HTTP/1.1\r\n"
 +                    "HOST: %s:%d\r\n"
 +                    "SID: uuid:%s\r\n"
 +                    "\r\n",
 +                    path, inet_ntoa(dst.sin_addr), ntohs(dst.sin_port), sid);
 +      os_free(url);
 +      wpa_hexdump_ascii(MSG_MSGDUMP, "WPS ER: Unsubscription request",
 +                        wpabuf_head(req), wpabuf_len(req));
 +
 +      ap->http = http_client_addr(&dst, req, 1000,
 +                                  wps_er_http_unsubscribe_cb, ap);
 +      if (ap->http == NULL) {
 +              wpabuf_free(req);
 +              goto fail;
 +      }
 +      return;
 +
 +fail:
 +      /*
 +       * Need to get rid of the AP entry even when we fail to unsubscribe
 +       * cleanly.
 +       */
 +      wps_er_ap_unsubscribed(ap->er, ap);
 +}
 +
 +
 +static struct wps_er_ap_settings * wps_er_ap_get_settings(struct wps_er *er,
 +                                                        const u8 *uuid)
 +{
 +      struct wps_er_ap_settings *s;
 +      dl_list_for_each(s, &er->ap_settings, struct wps_er_ap_settings, list)
 +              if (os_memcmp(uuid, s->uuid, WPS_UUID_LEN) == 0)
 +                      return s;
 +      return NULL;
 +}
 +
 +
 +int wps_er_ap_cache_settings(struct wps_er *er, struct in_addr *addr)
 +{
 +      struct wps_er_ap *ap;
 +      struct wps_er_ap_settings *settings;
 +
 +      ap = wps_er_ap_get(er, addr, NULL, NULL);
 +      if (ap == NULL || ap->ap_settings == NULL)
 +              return -1;
 +
 +      settings = wps_er_ap_get_settings(er, ap->uuid);
 +      if (!settings) {
 +              settings = os_zalloc(sizeof(*settings));
 +              if (settings == NULL)
 +                      return -1;
 +              os_memcpy(settings->uuid, ap->uuid, WPS_UUID_LEN);
 +              dl_list_add(&er->ap_settings, &settings->list);
 +      }
 +      os_memcpy(&settings->ap_settings, ap->ap_settings,
 +                sizeof(struct wps_credential));
 +
 +      return 0;
 +}
 +
 +
 +static int wps_er_ap_use_cached_settings(struct wps_er *er,
 +                                       struct wps_er_ap *ap)
 +{
 +      struct wps_er_ap_settings *s;
 +
 +      if (ap->ap_settings)
 +              return 0;
 +
 +      s = wps_er_ap_get_settings(ap->er, ap->uuid);
 +      if (!s)
 +              return -1;
 +
 +      ap->ap_settings = os_malloc(sizeof(*ap->ap_settings));
 +      if (ap->ap_settings == NULL)
 +              return -1;
 +
 +      os_memcpy(ap->ap_settings, &s->ap_settings, sizeof(*ap->ap_settings));
 +      wpa_printf(MSG_DEBUG, "WPS ER: Use cached AP settings");
 +      return 0;
 +}
 +
 +
 +static void wps_er_ap_remove_entry(struct wps_er *er, struct wps_er_ap *ap)
 +{
 +      wpa_printf(MSG_DEBUG, "WPS ER: Removing AP entry for %s (%s)",
 +                 inet_ntoa(ap->addr), ap->location);
 +      eloop_cancel_timeout(wps_er_ap_timeout, er, ap);
 +      wps_er_sta_remove_all(ap);
 +      wps_er_ap_event(er->wps, ap, WPS_EV_ER_AP_REMOVE);
 +      http_client_free(ap->http);
 +      ap->http = NULL;
 +      if (ap->wps) {
 +              wps_deinit(ap->wps);
 +              ap->wps = NULL;
 +      }
 +
 +      dl_list_del(&ap->list);
 +      if (ap->subscribed) {
 +              dl_list_add(&er->ap_unsubscribing, &ap->list);
 +              wps_er_ap_unsubscribe(er, ap);
 +      } else
 +              wps_er_ap_free(ap);
 +}
 +
 +
 +static void wps_er_ap_timeout(void *eloop_data, void *user_ctx)
 +{
 +      struct wps_er *er = eloop_data;
 +      struct wps_er_ap *ap = user_ctx;
 +      wpa_printf(MSG_DEBUG, "WPS ER: AP advertisement timed out");
 +      wps_er_ap_remove_entry(er, ap);
 +}
 +
 +
 +static int wps_er_get_sid(struct wps_er_ap *ap, char *sid)
 +{
 +      char *pos;
 +      char txt[100];
 +
 +      if (!sid) {
 +              wpa_printf(MSG_DEBUG, "WPS ER: No SID received from %s (%s)",
 +                         inet_ntoa(ap->addr), ap->location);
 +              return -1;
 +      }
 +
 +      pos = os_strstr(sid, "uuid:");
 +      if (!pos) {
 +              wpa_printf(MSG_DEBUG, "WPS ER: Invalid SID received from "
 +                         "%s (%s): '%s'", inet_ntoa(ap->addr), ap->location,
 +                         sid);
 +              return -1;
 +      }
 +
 +      pos += 5;
 +      if (uuid_str2bin(pos, ap->sid) < 0) {
 +              wpa_printf(MSG_DEBUG, "WPS ER: Invalid SID received from "
 +                         "%s (%s): '%s'", inet_ntoa(ap->addr), ap->location,
 +                         sid);
 +              return -1;
 +      }
 +
 +      uuid_bin2str(ap->sid, txt, sizeof(txt));
 +      wpa_printf(MSG_DEBUG, "WPS ER: SID for subscription with %s (%s): %s",
 +                 inet_ntoa(ap->addr), ap->location, txt);
 +
 +      return 0;
 +}
 +
 +
 +static void wps_er_http_subscribe_cb(void *ctx, struct http_client *c,
 +                                   enum http_client_event event)
 +{
 +      struct wps_er_ap *ap = ctx;
 +
 +      switch (event) {
 +      case HTTP_CLIENT_OK:
 +              wpa_printf(MSG_DEBUG, "WPS ER: Subscribed to events");
 +              ap->subscribed = 1;
 +              wps_er_get_sid(ap, http_client_get_hdr_line(c, "SID"));
 +              wps_er_ap_use_cached_settings(ap->er, ap);
 +              wps_er_ap_event(ap->er->wps, ap, WPS_EV_ER_AP_ADD);
 +              break;
 +      case HTTP_CLIENT_FAILED:
 +      case HTTP_CLIENT_INVALID_REPLY:
 +      case HTTP_CLIENT_TIMEOUT:
 +              wpa_printf(MSG_DEBUG, "WPS ER: Failed to subscribe to events");
 +              break;
 +      }
 +      http_client_free(ap->http);
 +      ap->http = NULL;
 +}
 +
 +
 +static void wps_er_subscribe(struct wps_er_ap *ap)
 +{
 +      struct wpabuf *req;
 +      struct sockaddr_in dst;
 +      char *url, *path;
 +
 +      if (ap->event_sub_url == NULL) {
 +              wpa_printf(MSG_DEBUG, "WPS ER: No eventSubURL - cannot "
 +                         "subscribe");
 +              return;
 +      }
 +      if (ap->http) {
 +              wpa_printf(MSG_DEBUG, "WPS ER: Pending HTTP request - cannot "
 +                         "send subscribe request");
 +              return;
 +      }
 +
 +      url = http_client_url_parse(ap->event_sub_url, &dst, &path);
 +      if (url == NULL) {
 +              wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse eventSubURL");
 +              return;
 +      }
 +
 +      req = wpabuf_alloc(os_strlen(ap->event_sub_url) + 1000);
 +      if (req == NULL) {
 +              os_free(url);
 +              return;
 +      }
 +      wpabuf_printf(req,
 +                    "SUBSCRIBE %s HTTP/1.1\r\n"
 +                    "HOST: %s:%d\r\n"
 +                    "CALLBACK: <http://%s:%d/event/%u/%u>\r\n"
 +                    "NT: upnp:event\r\n"
 +                    "TIMEOUT: Second-%d\r\n"
 +                    "\r\n",
 +                    path, inet_ntoa(dst.sin_addr), ntohs(dst.sin_port),
 +                    ap->er->ip_addr_text, ap->er->http_port,
 +                    ap->er->event_id, ap->id, 1800);
 +      os_free(url);
 +      wpa_hexdump_ascii(MSG_MSGDUMP, "WPS ER: Subscription request",
 +                        wpabuf_head(req), wpabuf_len(req));
 +
 +      ap->http = http_client_addr(&dst, req, 1000, wps_er_http_subscribe_cb,
 +                                  ap);
 +      if (ap->http == NULL)
 +              wpabuf_free(req);
 +}
 +
 +
 +static void wps_er_ap_get_m1(struct wps_er_ap *ap, struct wpabuf *m1)
 +{
 +      struct wps_parse_attr attr;
 +
 +      if (wps_parse_msg(m1, &attr) < 0) {
 +              wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse M1");
 +              return;
 +      }
 +      if (attr.primary_dev_type)
 +              os_memcpy(ap->pri_dev_type, attr.primary_dev_type, 8);
 +      if (attr.wps_state)
 +              ap->wps_state = *attr.wps_state;
 +      if (attr.mac_addr)
 +              os_memcpy(ap->mac_addr, attr.mac_addr, ETH_ALEN);
 +
 +      wps_er_subscribe(ap);
 +}
 +
 +
 +static void wps_er_get_device_info(struct wps_er_ap *ap)
 +{
 +      wps_er_send_get_device_info(ap, wps_er_ap_get_m1);
 +}
 +
 +
 +static const char * wps_er_find_wfadevice(const char *data)
 +{
 +      const char *tag, *tagname, *end;
 +      char *val;
 +      int found = 0;
 +
 +      while (!found) {
 +              /* Find next <device> */
 +              for (;;) {
 +                      if (xml_next_tag(data, &tag, &tagname, &end))
 +                              return NULL;
 +                      data = end;
 +                      if (!os_strncasecmp(tagname, "device", 6) &&
 +                          *tag != '/' &&
 +                          (tagname[6] == '>' || !isgraph(tagname[6]))) {
 +                              break;
 +                      }
 +              }
 +
 +              /* Check whether deviceType is WFADevice */
 +              val = xml_get_first_item(data, "deviceType");
 +              if (val == NULL)
 +                      return NULL;
 +              wpa_printf(MSG_DEBUG, "WPS ER: Found deviceType '%s'", val);
 +              found = os_strcasecmp(val, "urn:schemas-wifialliance-org:"
 +                                    "device:WFADevice:1") == 0;
 +              os_free(val);
 +      }
 +
 +      return data;
 +}
 +
 +
 +static void wps_er_parse_device_description(struct wps_er_ap *ap,
 +                                          struct wpabuf *reply)
 +{
 +      /* Note: reply includes null termination after the buffer data */
 +      const char *tmp, *data = wpabuf_head(reply);
 +      char *pos;
 +
 +      wpa_hexdump_ascii(MSG_MSGDUMP, "WPS ER: Device info",
 +                        wpabuf_head(reply), wpabuf_len(reply));
 +
 +      /*
 +       * The root device description may include multiple devices, so first
 +       * find the beginning of the WFADevice description to allow the
 +       * simplistic parser to pick the correct entries.
 +       */
 +      tmp = wps_er_find_wfadevice(data);
 +      if (tmp == NULL) {
 +              wpa_printf(MSG_DEBUG, "WPS ER: WFADevice:1 device not found - "
 +                         "trying to parse invalid data");
 +      } else
 +              data = tmp;
 +
 +      ap->friendly_name = xml_get_first_item(data, "friendlyName");
 +      wpa_printf(MSG_DEBUG, "WPS ER: friendlyName='%s'", ap->friendly_name);
 +
 +      ap->manufacturer = xml_get_first_item(data, "manufacturer");
 +      wpa_printf(MSG_DEBUG, "WPS ER: manufacturer='%s'", ap->manufacturer);
 +
 +      ap->manufacturer_url = xml_get_first_item(data, "manufacturerURL");
 +      wpa_printf(MSG_DEBUG, "WPS ER: manufacturerURL='%s'",
 +                 ap->manufacturer_url);
 +
 +      ap->model_description = xml_get_first_item(data, "modelDescription");
 +      wpa_printf(MSG_DEBUG, "WPS ER: modelDescription='%s'",
 +                 ap->model_description);
 +
 +      ap->model_name = xml_get_first_item(data, "modelName");
 +      wpa_printf(MSG_DEBUG, "WPS ER: modelName='%s'", ap->model_name);
 +
 +      ap->model_number = xml_get_first_item(data, "modelNumber");
 +      wpa_printf(MSG_DEBUG, "WPS ER: modelNumber='%s'", ap->model_number);
 +
 +      ap->model_url = xml_get_first_item(data, "modelURL");
 +      wpa_printf(MSG_DEBUG, "WPS ER: modelURL='%s'", ap->model_url);
 +
 +      ap->serial_number = xml_get_first_item(data, "serialNumber");
 +      wpa_printf(MSG_DEBUG, "WPS ER: serialNumber='%s'", ap->serial_number);
 +
 +      ap->udn = xml_get_first_item(data, "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");
 +      wpa_printf(MSG_DEBUG, "WPS ER: UPC='%s'", ap->upc);
 +
 +      ap->scpd_url = http_link_update(
 +              xml_get_first_item(data, "SCPDURL"), ap->location);
 +      wpa_printf(MSG_DEBUG, "WPS ER: SCPDURL='%s'", ap->scpd_url);
 +
 +      ap->control_url = http_link_update(
 +              xml_get_first_item(data, "controlURL"), ap->location);
 +      wpa_printf(MSG_DEBUG, "WPS ER: controlURL='%s'", ap->control_url);
 +
 +      ap->event_sub_url = http_link_update(
 +              xml_get_first_item(data, "eventSubURL"), ap->location);
 +      wpa_printf(MSG_DEBUG, "WPS ER: eventSubURL='%s'", ap->event_sub_url);
 +}
 +
 +
 +static void wps_er_http_dev_desc_cb(void *ctx, struct http_client *c,
 +                                  enum http_client_event event)
 +{
 +      struct wps_er_ap *ap = ctx;
 +      struct wpabuf *reply;
 +      int ok = 0;
 +
 +      switch (event) {
 +      case HTTP_CLIENT_OK:
 +              reply = http_client_get_body(c);
 +              if (reply == NULL)
 +                      break;
 +              wps_er_parse_device_description(ap, reply);
 +              ok = 1;
 +              break;
 +      case HTTP_CLIENT_FAILED:
 +      case HTTP_CLIENT_INVALID_REPLY:
 +      case HTTP_CLIENT_TIMEOUT:
 +              wpa_printf(MSG_DEBUG, "WPS ER: Failed to fetch device info");
 +              break;
 +      }
 +      http_client_free(ap->http);
 +      ap->http = NULL;
 +      if (ok)
 +              wps_er_get_device_info(ap);
 +}
 +
 +
 +void wps_er_ap_add(struct wps_er *er, const u8 *uuid, struct in_addr *addr,
 +                 const char *location, int max_age)
 +{
 +      struct wps_er_ap *ap;
 +
 +      ap = wps_er_ap_get(er, addr, uuid, NULL);
 +      if (ap) {
 +              /* Update advertisement timeout */
 +              eloop_cancel_timeout(wps_er_ap_timeout, er, ap);
 +              eloop_register_timeout(max_age, 0, wps_er_ap_timeout, er, ap);
 +              return;
 +      }
 +
 +      ap = os_zalloc(sizeof(*ap));
 +      if (ap == NULL)
 +              return;
 +      dl_list_init(&ap->sta);
 +      ap->er = er;
 +      ap->id = ++er->next_ap_id;
 +      ap->location = os_strdup(location);
 +      if (ap->location == NULL) {
 +              os_free(ap);
 +              return;
 +      }
 +      dl_list_add(&er->ap, &ap->list);
 +
 +      ap->addr.s_addr = addr->s_addr;
 +      os_memcpy(ap->uuid, uuid, WPS_UUID_LEN);
 +      eloop_register_timeout(max_age, 0, wps_er_ap_timeout, er, ap);
 +
 +      wpa_printf(MSG_DEBUG, "WPS ER: Added AP entry for %s (%s)",
 +                 inet_ntoa(ap->addr), ap->location);
 +
 +      /* Fetch device description */
 +      ap->http = http_client_url(ap->location, NULL, 10000,
 +                                 wps_er_http_dev_desc_cb, ap);
 +}
 +
 +
 +void wps_er_ap_remove(struct wps_er *er, struct in_addr *addr)
 +{
 +      struct wps_er_ap *ap;
 +      dl_list_for_each(ap, &er->ap, struct wps_er_ap, list) {
 +              if (ap->addr.s_addr == addr->s_addr) {
 +                      wps_er_ap_remove_entry(er, ap);
 +                      return;
 +              }
 +      }
 +}
 +
 +
 +static void wps_er_ap_remove_all(struct wps_er *er)
 +{
 +      struct wps_er_ap *prev, *ap;
 +      struct wps_er_ap_settings *prev_s, *s;
 +      dl_list_for_each_safe(ap, prev, &er->ap, struct wps_er_ap, list)
 +              wps_er_ap_remove_entry(er, ap);
 +      dl_list_for_each_safe(s, prev_s, &er->ap_settings,
 +                            struct wps_er_ap_settings, list)
 +              os_free(s);
 +}
 +
 +
 +static void http_put_date(struct wpabuf *buf)
 +{
 +      wpabuf_put_str(buf, "Date: ");
 +      format_date(buf);
 +      wpabuf_put_str(buf, "\r\n");
 +}
 +
 +
 +static void wps_er_http_resp_not_found(struct http_request *req)
 +{
 +      struct wpabuf *buf;
 +      buf = wpabuf_alloc(200);
 +      if (buf == NULL) {
 +              http_request_deinit(req);
 +              return;
 +      }
 +
 +      wpabuf_put_str(buf,
 +                     "HTTP/1.1 404 Not Found\r\n"
 +                     "Server: unspecified, UPnP/1.0, unspecified\r\n"
 +                     "Connection: close\r\n");
 +      http_put_date(buf);
 +      wpabuf_put_str(buf, "\r\n");
 +      http_request_send_and_deinit(req, buf);
 +}
 +
 +
 +static void wps_er_http_resp_ok(struct http_request *req)
 +{
 +      struct wpabuf *buf;
 +      buf = wpabuf_alloc(200);
 +      if (buf == NULL) {
 +              http_request_deinit(req);
 +              return;
 +      }
 +
 +      wpabuf_put_str(buf,
 +                     "HTTP/1.1 200 OK\r\n"
 +                     "Server: unspecified, UPnP/1.0, unspecified\r\n"
 +                     "Connection: close\r\n"
 +                     "Content-Length: 0\r\n");
 +      http_put_date(buf);
 +      wpabuf_put_str(buf, "\r\n");
 +      http_request_send_and_deinit(req, buf);
 +}
 +
 +
 +static void wps_er_sta_timeout(void *eloop_data, void *user_ctx)
 +{
 +      struct wps_er_sta *sta = eloop_data;
 +      wpa_printf(MSG_DEBUG, "WPS ER: STA entry timed out");
 +      dl_list_del(&sta->list);
 +      wps_er_sta_free(sta);
 +}
 +
 +
 +static struct wps_er_sta * wps_er_add_sta_data(struct wps_er_ap *ap,
 +                                             const u8 *addr,
 +                                             struct wps_parse_attr *attr,
 +                                             int probe_req)
 +{
 +      struct wps_er_sta *sta = wps_er_sta_get(ap, addr, NULL);
 +      int new_sta = 0;
 +      int m1;
 +
 +      m1 = !probe_req && attr->msg_type && *attr->msg_type == WPS_M1;
 +
 +      if (sta == NULL) {
 +              /*
 +               * Only allow new STA entry to be added based on Probe Request
 +               * or M1. This will filter out bogus events and anything that
 +               * may have been ongoing at the time ER subscribed for events.
 +               */
 +              if (!probe_req && !m1)
 +                      return NULL;
 +
 +              sta = os_zalloc(sizeof(*sta));
 +              if (sta == NULL)
 +                      return NULL;
 +              os_memcpy(sta->addr, addr, ETH_ALEN);
 +              sta->ap = ap;
 +              dl_list_add(&ap->sta, &sta->list);
 +              new_sta = 1;
 +      }
 +
 +      if (m1)
 +              sta->m1_received = 1;
 +
 +      if (attr->config_methods && (!probe_req || !sta->m1_received))
 +              sta->config_methods = WPA_GET_BE16(attr->config_methods);
 +      if (attr->uuid_e && (!probe_req || !sta->m1_received))
 +              os_memcpy(sta->uuid, attr->uuid_e, WPS_UUID_LEN);
 +      if (attr->primary_dev_type && (!probe_req || !sta->m1_received))
 +              os_memcpy(sta->pri_dev_type, attr->primary_dev_type, 8);
 +      if (attr->dev_password_id && (!probe_req || !sta->m1_received))
 +              sta->dev_passwd_id = WPA_GET_BE16(attr->dev_password_id);
 +
 +      if (attr->manufacturer) {
 +              os_free(sta->manufacturer);
 +              sta->manufacturer = dup_binstr(attr->manufacturer,
 +                                             attr->manufacturer_len);
 +      }
 +
 +      if (attr->model_name) {
 +              os_free(sta->model_name);
 +              sta->model_name = dup_binstr(attr->model_name,
 +                                           attr->model_name_len);
 +      }
 +
 +      if (attr->model_number) {
 +              os_free(sta->model_number);
 +              sta->model_number = dup_binstr(attr->model_number,
 +                                             attr->model_number_len);
 +      }
 +
 +      if (attr->serial_number) {
 +              os_free(sta->serial_number);
 +              sta->serial_number = dup_binstr(attr->serial_number,
 +                                              attr->serial_number_len);
 +      }
 +
 +      if (attr->dev_name) {
 +              os_free(sta->dev_name);
 +              sta->dev_name = dup_binstr(attr->dev_name, attr->dev_name_len);
 +      }
 +
 +      eloop_cancel_timeout(wps_er_sta_timeout, sta, NULL);
 +      eloop_register_timeout(300, 0, wps_er_sta_timeout, sta, NULL);
 +
 +      if (m1 || new_sta)
 +              wps_er_sta_event(ap->er->wps, sta, WPS_EV_ER_ENROLLEE_ADD);
 +
 +      return sta;
 +}
 +
 +
 +static void wps_er_process_wlanevent_probe_req(struct wps_er_ap *ap,
 +                                             const u8 *addr,
 +                                             struct wpabuf *msg)
 +{
 +      struct wps_parse_attr attr;
 +
 +      wpa_printf(MSG_DEBUG, "WPS ER: WLANEvent - Probe Request - from "
 +                 MACSTR, MAC2STR(addr));
 +      wpa_hexdump_buf(MSG_MSGDUMP, "WPS ER: WLANEvent - Enrollee's message "
 +                      "(TLVs from Probe Request)", msg);
 +
 +      if (wps_validate_probe_req(msg, addr) < 0) {
 +              wpa_printf(MSG_INFO, "WPS-STRICT: ER: Ignore invalid proxied "
 +                         "Probe Request frame from " MACSTR, MAC2STR(addr));
 +              return;
 +      }
 +
 +      if (wps_parse_msg(msg, &attr) < 0) {
 +              wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse TLVs in "
 +                         "WLANEvent message");
 +              return;
 +      }
 +
 +      wps_er_add_sta_data(ap, addr, &attr, 1);
 +      wps_registrar_probe_req_rx(ap->er->wps->registrar, addr, msg, 0);
 +}
 +
 +
 +static void wps_er_http_put_wlan_response_cb(void *ctx, struct http_client *c,
 +                                           enum http_client_event event)
 +{
 +      struct wps_er_sta *sta = ctx;
 +
 +      switch (event) {
 +      case HTTP_CLIENT_OK:
 +              wpa_printf(MSG_DEBUG, "WPS ER: PutWLANResponse OK");
 +              break;
 +      case HTTP_CLIENT_FAILED:
 +      case HTTP_CLIENT_INVALID_REPLY:
 +      case HTTP_CLIENT_TIMEOUT:
 +              wpa_printf(MSG_DEBUG, "WPS ER: PutWLANResponse failed");
 +              break;
 +      }
 +      http_client_free(sta->http);
 +      sta->http = NULL;
 +}
 +
 +
 +static const char *soap_prefix =
 +      "<?xml version=\"1.0\"?>\n"
 +      "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" "
 +      "s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">\n"
 +      "<s:Body>\n";
 +static const char *soap_postfix =
 +      "</s:Body>\n</s:Envelope>\n";
 +static const char *urn_wfawlanconfig =
 +      "urn:schemas-wifialliance-org:service:WFAWLANConfig:1";
 +
 +static struct wpabuf * wps_er_soap_hdr(const struct wpabuf *msg,
 +                                     const char *name, const char *arg_name,
 +                                     const char *path,
 +                                     const struct sockaddr_in *dst,
 +                                     char **len_ptr, char **body_ptr)
 +{
 +      unsigned char *encoded;
 +      size_t encoded_len;
 +      struct wpabuf *buf;
 +
 +      if (msg) {
 +              encoded = base64_encode(wpabuf_head(msg), wpabuf_len(msg),
 +                                      &encoded_len);
 +              if (encoded == NULL)
 +                      return NULL;
 +      } else {
 +              encoded = NULL;
 +              encoded_len = 0;
 +      }
 +
 +      buf = wpabuf_alloc(1000 + encoded_len);
 +      if (buf == NULL) {
 +              os_free(encoded);
 +              return NULL;
 +      }
 +
 +      wpabuf_printf(buf,
 +                    "POST %s HTTP/1.1\r\n"
 +                    "Host: %s:%d\r\n"
 +                    "Content-Type: text/xml; charset=\"utf-8\"\r\n"
 +                    "Content-Length: ",
 +                    path, inet_ntoa(dst->sin_addr), ntohs(dst->sin_port));
 +
 +      *len_ptr = wpabuf_put(buf, 0);
 +      wpabuf_printf(buf,
 +                    "        \r\n"
 +                    "SOAPACTION: \"%s#%s\"\r\n"
 +                    "\r\n",
 +                    urn_wfawlanconfig, name);
 +
 +      *body_ptr = wpabuf_put(buf, 0);
 +
 +      wpabuf_put_str(buf, soap_prefix);
 +      wpabuf_printf(buf, "<u:%s xmlns:u=\"", name);
 +      wpabuf_put_str(buf, urn_wfawlanconfig);
 +      wpabuf_put_str(buf, "\">\n");
 +      if (encoded) {
 +              wpabuf_printf(buf, "<%s>%s</%s>\n",
 +                            arg_name, (char *) encoded, arg_name);
 +              os_free(encoded);
 +      }
 +
 +      return buf;
 +}
 +
 +
 +static void wps_er_soap_end(struct wpabuf *buf, const char *name,
 +                          char *len_ptr, char *body_ptr)
 +{
 +      char len_buf[10];
 +      wpabuf_printf(buf, "</u:%s>\n", name);
 +      wpabuf_put_str(buf, soap_postfix);
 +      os_snprintf(len_buf, sizeof(len_buf), "%d",
 +                  (int) ((char *) wpabuf_put(buf, 0) - body_ptr));
 +      os_memcpy(len_ptr, len_buf, os_strlen(len_buf));
 +}
 +
 +
 +static void wps_er_sta_send_msg(struct wps_er_sta *sta, struct wpabuf *msg)
 +{
 +      struct wpabuf *buf;
 +      char *len_ptr, *body_ptr;
 +      struct sockaddr_in dst;
 +      char *url, *path;
 +
 +      if (sta->http) {
 +              wpa_printf(MSG_DEBUG, "WPS ER: Pending HTTP request for STA - "
 +                         "ignore new request");
 +              wpabuf_free(msg);
 +              return;
 +      }
 +
 +      if (sta->ap->control_url == NULL) {
 +              wpa_printf(MSG_DEBUG, "WPS ER: No controlURL for AP");
 +              wpabuf_free(msg);
 +              return;
 +      }
 +
 +      url = http_client_url_parse(sta->ap->control_url, &dst, &path);
 +      if (url == NULL) {
 +              wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse controlURL");
 +              wpabuf_free(msg);
 +              return;
 +      }
 +
 +      buf = wps_er_soap_hdr(msg, "PutWLANResponse", "NewMessage", path, &dst,
 +                            &len_ptr, &body_ptr);
 +      wpabuf_free(msg);
 +      os_free(url);
 +      if (buf == NULL)
 +              return;
 +      wpabuf_printf(buf, "<NewWLANEventType>%d</NewWLANEventType>\n",
 +                    UPNP_WPS_WLANEVENT_TYPE_EAP);
 +      wpabuf_printf(buf, "<NewWLANEventMAC>" MACSTR "</NewWLANEventMAC>\n",
 +                    MAC2STR(sta->addr));
 +
 +      wps_er_soap_end(buf, "PutWLANResponse", len_ptr, body_ptr);
 +
 +      sta->http = http_client_addr(&dst, buf, 1000,
 +                                   wps_er_http_put_wlan_response_cb, sta);
 +      if (sta->http == NULL)
 +              wpabuf_free(buf);
 +}
 +
 +
 +static void wps_er_sta_process(struct wps_er_sta *sta, struct wpabuf *msg,
 +                             enum wsc_op_code op_code)
 +{
 +      enum wps_process_res res;
 +
 +      res = wps_process_msg(sta->wps, op_code, msg);
 +      if (res == WPS_CONTINUE) {
 +              struct wpabuf *next = wps_get_msg(sta->wps, &op_code);
 +              if (next)
 +                      wps_er_sta_send_msg(sta, next);
 +      } else {
 +              wpa_printf(MSG_DEBUG, "WPS ER: Protocol run %s with the "
 +                         "enrollee (res=%d)",
 +                         res == WPS_DONE ? "succeeded" : "failed", res);
 +              wps_deinit(sta->wps);
 +              sta->wps = NULL;
 +              if (res == WPS_DONE) {
 +                      /* Remove the STA entry after short timeout */
 +                      eloop_cancel_timeout(wps_er_sta_timeout, sta, NULL);
 +                      eloop_register_timeout(10, 0, wps_er_sta_timeout, sta,
 +                                             NULL);
 +              }
 +      }
 +}
 +
 +
 +static void wps_er_sta_start(struct wps_er_sta *sta, struct wpabuf *msg)
 +{
 +      struct wps_config cfg;
 +
 +      if (sta->wps)
 +              wps_deinit(sta->wps);
 +
 +      os_memset(&cfg, 0, sizeof(cfg));
 +      cfg.wps = sta->ap->er->wps;
 +      cfg.registrar = 1;
 +      cfg.peer_addr = sta->addr;
 +
 +      sta->wps = wps_init(&cfg);
 +      if (sta->wps == NULL)
 +              return;
 +      sta->wps->er = 1;
 +      sta->wps->use_cred = sta->ap->ap_settings;
 +      if (sta->ap->ap_settings) {
 +              os_free(sta->cred);
 +              sta->cred = os_malloc(sizeof(*sta->cred));
 +              if (sta->cred) {
 +                      os_memcpy(sta->cred, sta->ap->ap_settings,
 +                                sizeof(*sta->cred));
 +                      sta->cred->cred_attr = NULL;
 +                      os_memcpy(sta->cred->mac_addr, sta->addr, ETH_ALEN);
 +                      sta->wps->use_cred = sta->cred;
 +              }
 +      }
 +
 +      wps_er_sta_process(sta, msg, WSC_MSG);
 +}
 +
 +
 +static void wps_er_process_wlanevent_eap(struct wps_er_ap *ap, const u8 *addr,
 +                                       struct wpabuf *msg)
 +{
 +      struct wps_parse_attr attr;
 +      struct wps_er_sta *sta;
 +
 +      wpa_printf(MSG_DEBUG, "WPS ER: WLANEvent - EAP - from " MACSTR,
 +                 MAC2STR(addr));
 +      wpa_hexdump_buf(MSG_MSGDUMP, "WPS ER: WLANEvent - Enrollee's message "
 +                      "(TLVs from EAP-WSC)", msg);
 +
 +      if (wps_parse_msg(msg, &attr) < 0) {
 +              wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse TLVs in "
 +                         "WLANEvent message");
 +              return;
 +      }
 +
 +      sta = wps_er_add_sta_data(ap, addr, &attr, 0);
 +      if (sta == NULL)
 +              return;
 +
 +      if (attr.msg_type && *attr.msg_type == WPS_M1)
 +              wps_er_sta_start(sta, msg);
 +      else if (sta->wps) {
 +              enum wsc_op_code op_code = WSC_MSG;
 +              if (attr.msg_type) {
 +                      switch (*attr.msg_type) {
 +                      case WPS_WSC_ACK:
 +                              op_code = WSC_ACK;
 +                              break;
 +                      case WPS_WSC_NACK:
 +                              op_code = WSC_NACK;
 +                              break;
 +                      case WPS_WSC_DONE:
 +                              op_code = WSC_Done;
 +                              break;
 +                      }
 +              }
 +              wps_er_sta_process(sta, msg, op_code);
 +      }
 +}
 +
 +
 +static void wps_er_process_wlanevent(struct wps_er_ap *ap,
 +                                   struct wpabuf *event)
 +{
 +      u8 *data;
 +      u8 wlan_event_type;
 +      u8 wlan_event_mac[ETH_ALEN];
 +      struct wpabuf msg;
 +
 +      wpa_hexdump(MSG_MSGDUMP, "WPS ER: Received WLANEvent",
 +                  wpabuf_head(event), wpabuf_len(event));
 +      if (wpabuf_len(event) < 1 + 17) {
 +              wpa_printf(MSG_DEBUG, "WPS ER: Too short WLANEvent");
 +              return;
 +      }
 +
 +      data = wpabuf_mhead(event);
 +      wlan_event_type = data[0];
 +      if (hwaddr_aton((char *) data + 1, wlan_event_mac) < 0) {
 +              wpa_printf(MSG_DEBUG, "WPS ER: Invalid WLANEventMAC in "
 +                         "WLANEvent");
 +              return;
 +      }
 +
 +      wpabuf_set(&msg, data + 1 + 17, wpabuf_len(event) - (1 + 17));
 +
 +      switch (wlan_event_type) {
 +      case 1:
 +              wps_er_process_wlanevent_probe_req(ap, wlan_event_mac, &msg);
 +              break;
 +      case 2:
 +              wps_er_process_wlanevent_eap(ap, wlan_event_mac, &msg);
 +              break;
 +      default:
 +              wpa_printf(MSG_DEBUG, "WPS ER: Unknown WLANEventType %d",
 +                         wlan_event_type);
 +              break;
 +      }
 +}
 +
 +
 +static void wps_er_http_event(struct wps_er *er, struct http_request *req,
 +                            unsigned int ap_id)
 +{
 +      struct wps_er_ap *ap = wps_er_ap_get_id(er, ap_id);
 +      struct wpabuf *event;
 +      enum http_reply_code ret;
 +
 +      if (ap == NULL) {
 +              wpa_printf(MSG_DEBUG, "WPS ER: HTTP event from unknown AP id "
 +                         "%u", ap_id);
 +              wps_er_http_resp_not_found(req);
 +              return;
 +      }
 +      wpa_printf(MSG_MSGDUMP, "WPS ER: HTTP event from AP id %u: %s",
 +                 ap_id, http_request_get_data(req));
 +
 +      event = xml_get_base64_item(http_request_get_data(req), "WLANEvent",
 +                                  &ret);
 +      if (event == NULL) {
 +              wpa_printf(MSG_DEBUG, "WPS ER: Could not extract WLANEvent "
 +                         "from the event notification");
 +              /*
 +               * Reply with OK anyway to avoid getting unregistered from
 +               * events.
 +               */
 +              wps_er_http_resp_ok(req);
 +              return;
 +      }
 +
 +      wps_er_process_wlanevent(ap, event);
 +
 +      wpabuf_free(event);
 +      wps_er_http_resp_ok(req);
 +}
 +
 +
 +static void wps_er_http_notify(struct wps_er *er, struct http_request *req)
 +{
 +      char *uri = http_request_get_uri(req);
 +
 +      if (os_strncmp(uri, "/event/", 7) == 0) {
 +              unsigned int event_id;
 +              char *pos;
 +              event_id = atoi(uri + 7);
 +              if (event_id != er->event_id) {
 +                      wpa_printf(MSG_DEBUG, "WPS ER: HTTP event for an "
 +                                 "unknown event id %u", event_id);
 +                      return;
 +              }
 +              pos = os_strchr(uri + 7, '/');
 +              if (pos == NULL)
 +                      return;
 +              pos++;
 +              wps_er_http_event(er, req, atoi(pos));
 +      } else {
 +              wpa_printf(MSG_DEBUG, "WPS ER: Unknown HTTP NOTIFY for '%s'",
 +                         uri);
 +              wps_er_http_resp_not_found(req);
 +      }
 +}
 +
 +
 +static void wps_er_http_req(void *ctx, struct http_request *req)
 +{
 +      struct wps_er *er = ctx;
 +      struct sockaddr_in *cli = http_request_get_cli_addr(req);
 +      enum httpread_hdr_type type = http_request_get_type(req);
 +      struct wpabuf *buf;
 +
 +      wpa_printf(MSG_DEBUG, "WPS ER: HTTP request: '%s' (type %d) from "
 +                 "%s:%d",
 +                 http_request_get_uri(req), type,
 +                 inet_ntoa(cli->sin_addr), ntohs(cli->sin_port));
 +
 +      switch (type) {
 +      case HTTPREAD_HDR_TYPE_NOTIFY:
 +              wps_er_http_notify(er, req);
 +              break;
 +      default:
 +              wpa_printf(MSG_DEBUG, "WPS ER: Unsupported HTTP request type "
 +                         "%d", type);
 +              buf = wpabuf_alloc(200);
 +              if (buf == NULL) {
 +                      http_request_deinit(req);
 +                      return;
 +              }
 +              wpabuf_put_str(buf,
 +                             "HTTP/1.1 501 Unimplemented\r\n"
 +                             "Connection: close\r\n");
 +              http_put_date(buf);
 +              wpabuf_put_str(buf, "\r\n");
 +              http_request_send_and_deinit(req, buf);
 +              break;
 +      }
 +}
 +
 +
 +struct wps_er *
 +wps_er_init(struct wps_context *wps, const char *ifname, const char *filter)
 +{
 +      struct wps_er *er;
 +      struct in_addr addr;
 +
 +      er = os_zalloc(sizeof(*er));
 +      if (er == NULL)
 +              return NULL;
 +      dl_list_init(&er->ap);
 +      dl_list_init(&er->ap_unsubscribing);
 +      dl_list_init(&er->ap_settings);
 +
 +      er->multicast_sd = -1;
 +      er->ssdp_sd = -1;
 +
 +      os_strlcpy(er->ifname, ifname, sizeof(er->ifname));
 +      er->wps = wps;
 +      if (os_get_random((unsigned char *) &er->event_id,
 +                        sizeof(er->event_id)) < 0) {
 +              wps_er_deinit(er, NULL, NULL);
 +              return NULL;
 +      }
 +      /* Limit event_id to < 32 bits to avoid issues with atoi() */
 +      er->event_id &= 0x0fffffff;
 +
 +      if (filter && os_strncmp(filter, "ifname=", 7) == 0) {
 +              const char *pos, *end;
 +              pos = filter + 7;
 +              end = os_strchr(pos, ' ');
 +              if (end) {
 +                      size_t len = end - pos;
 +                      os_strlcpy(er->ifname, pos, len < sizeof(er->ifname) ?
 +                                 len + 1 : sizeof(er->ifname));
 +                      filter = end + 1;
 +              } else {
 +                      os_strlcpy(er->ifname, pos, sizeof(er->ifname));
 +                      filter = NULL;
 +              }
 +              er->forced_ifname = 1;
 +      }
 +
 +      if (filter) {
 +              if (inet_aton(filter, &er->filter_addr) == 0) {
 +                      wpa_printf(MSG_INFO, "WPS UPnP: Invalid filter "
 +                                 "address %s", filter);
 +                      wps_er_deinit(er, NULL, NULL);
 +                      return NULL;
 +              }
 +              wpa_printf(MSG_DEBUG, "WPS UPnP: Only accepting connections "
 +                         "with %s", filter);
 +      }
 +      if (get_netif_info(er->ifname, &er->ip_addr, &er->ip_addr_text,
 +                         er->mac_addr)) {
 +              wpa_printf(MSG_INFO, "WPS UPnP: Could not get IP/MAC address "
 +                         "for %s. Does it have IP address?", er->ifname);
 +              wps_er_deinit(er, NULL, NULL);
 +              return NULL;
 +      }
 +
 +      if (wps_er_ssdp_init(er) < 0) {
 +              wpa_printf(MSG_INFO, "WPS UPnP: SSDP initialization failed");
 +              wps_er_deinit(er, NULL, NULL);
 +              return NULL;
 +      }
 +
 +      addr.s_addr = er->ip_addr;
 +      er->http_srv = http_server_init(&addr, -1, wps_er_http_req, er);
 +      if (er->http_srv == NULL) {
 +              wpa_printf(MSG_INFO, "WPS UPnP: HTTP initialization failed");
 +              wps_er_deinit(er, NULL, NULL);
 +              return NULL;
 +      }
 +      er->http_port = http_server_get_port(er->http_srv);
 +
 +      wpa_printf(MSG_DEBUG, "WPS ER: Start (ifname=%s ip_addr=%s)",
 +                 er->ifname, er->ip_addr_text);
 +
 +      return er;
 +}
 +
 +
 +void wps_er_refresh(struct wps_er *er)
 +{
 +      struct wps_er_ap *ap;
 +      struct wps_er_sta *sta;
 +
 +      dl_list_for_each(ap, &er->ap, struct wps_er_ap, list) {
 +              wps_er_ap_event(er->wps, ap, WPS_EV_ER_AP_ADD);
 +              dl_list_for_each(sta, &ap->sta, struct wps_er_sta, list)
 +                      wps_er_sta_event(er->wps, sta, WPS_EV_ER_ENROLLEE_ADD);
 +      }
 +
 +      wps_er_send_ssdp_msearch(er);
 +}
 +
 +
 +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);
 +      os_free(er);
 +
 +      if (deinit_done_cb)
 +              deinit_done_cb(deinit_done_ctx);
 +}
 +
 +
 +void wps_er_deinit(struct wps_er *er, void (*cb)(void *ctx), void *ctx)
 +{
 +      if (er == NULL)
 +              return;
 +      http_server_deinit(er->http_srv);
 +      wps_er_ap_remove_all(er);
 +      wps_er_ssdp_deinit(er);
 +      eloop_register_timeout(dl_list_empty(&er->ap_unsubscribing) ? 0 : 5, 0,
 +                             wps_er_deinit_finish, er, NULL);
 +      wpa_printf(MSG_DEBUG, "WPS ER: Finish deinit from timeout");
 +      er->deinitializing = 1;
 +      er->deinit_done_cb = cb;
 +      er->deinit_done_ctx = ctx;
 +}
 +
 +
 +static void wps_er_http_set_sel_reg_cb(void *ctx, struct http_client *c,
 +                                     enum http_client_event event)
 +{
 +      struct wps_er_ap *ap = ctx;
 +      union wps_event_data data;
 +
 +      os_memset(&data, 0, sizeof(data));
 +
 +      switch (event) {
 +      case HTTP_CLIENT_OK:
 +              wpa_printf(MSG_DEBUG, "WPS ER: SetSelectedRegistrar OK");
 +              data.set_sel_reg.state = WPS_ER_SET_SEL_REG_DONE;
 +              data.set_sel_reg.uuid = ap->uuid;
 +              break;
 +      case HTTP_CLIENT_FAILED:
 +      case HTTP_CLIENT_INVALID_REPLY:
 +      case HTTP_CLIENT_TIMEOUT:
 +              wpa_printf(MSG_DEBUG, "WPS ER: SetSelectedRegistrar failed");
 +              data.set_sel_reg.state = WPS_ER_SET_SEL_REG_FAILED;
 +              data.set_sel_reg.uuid = ap->uuid;
 +              break;
 +      }
 +      http_client_free(ap->http);
 +      ap->http = NULL;
 +
 +      if (data.set_sel_reg.uuid)
 +              ap->er->wps->event_cb(ap->er->wps->cb_ctx,
 +                                    WPS_EV_ER_SET_SELECTED_REGISTRAR, &data);
 +}
 +
 +
 +static void wps_er_send_set_sel_reg(struct wps_er_ap *ap, struct wpabuf *msg)
 +{
 +      struct wpabuf *buf;
 +      char *len_ptr, *body_ptr;
 +      struct sockaddr_in dst;
 +      char *url, *path;
 +
 +      if (ap->control_url == NULL) {
 +              wpa_printf(MSG_DEBUG, "WPS ER: No controlURL for AP");
 +              return;
 +      }
 +
 +      if (ap->http) {
 +              wpa_printf(MSG_DEBUG, "WPS ER: Pending HTTP request for AP - "
 +                         "ignore new request");
 +              return;
 +      }
 +
 +      if (ap->wps) {
 +              wpa_printf(MSG_DEBUG, "WPS ER: Pending WPS operation for AP - "
 +                         "skip SetSelectedRegistrar");
 +              return;
 +      }
 +
 +      url = http_client_url_parse(ap->control_url, &dst, &path);
 +      if (url == NULL) {
 +              wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse controlURL");
 +              return;
 +      }
 +
 +      buf = wps_er_soap_hdr(msg, "SetSelectedRegistrar", "NewMessage", path,
 +                            &dst, &len_ptr, &body_ptr);
 +      os_free(url);
 +      if (buf == NULL)
 +              return;
 +
 +      wps_er_soap_end(buf, "SetSelectedRegistrar", len_ptr, body_ptr);
 +
 +      ap->http = http_client_addr(&dst, buf, 1000,
 +                                  wps_er_http_set_sel_reg_cb, ap);
 +      if (ap->http == NULL)
 +              wpabuf_free(buf);
 +}
 +
 +
 +static int wps_er_build_selected_registrar(struct wpabuf *msg, int sel_reg)
 +{
 +      wpabuf_put_be16(msg, ATTR_SELECTED_REGISTRAR);
 +      wpabuf_put_be16(msg, 1);
 +      wpabuf_put_u8(msg, !!sel_reg);
 +      return 0;
 +}
 +
 +
 +static int wps_er_build_dev_password_id(struct wpabuf *msg, u16 dev_passwd_id)
 +{
 +      wpabuf_put_be16(msg, ATTR_DEV_PASSWORD_ID);
 +      wpabuf_put_be16(msg, 2);
 +      wpabuf_put_be16(msg, dev_passwd_id);
 +      return 0;
 +}
 +
 +
 +static int wps_er_build_sel_reg_config_methods(struct wpabuf *msg,
 +                                             u16 sel_reg_config_methods)
 +{
 +      wpabuf_put_be16(msg, ATTR_SELECTED_REGISTRAR_CONFIG_METHODS);
 +      wpabuf_put_be16(msg, 2);
 +      wpabuf_put_be16(msg, sel_reg_config_methods);
 +      return 0;
 +}
 +
 +
 +static int wps_er_build_uuid_r(struct wpabuf *msg, const u8 *uuid_r)
 +{
 +      wpabuf_put_be16(msg, ATTR_UUID_R);
 +      wpabuf_put_be16(msg, WPS_UUID_LEN);
 +      wpabuf_put_data(msg, uuid_r, WPS_UUID_LEN);
 +      return 0;
 +}
 +
 +
 +void wps_er_set_sel_reg(struct wps_er *er, int sel_reg, u16 dev_passwd_id,
 +                      u16 sel_reg_config_methods)
 +{
 +      struct wpabuf *msg;
 +      struct wps_er_ap *ap;
 +      struct wps_registrar *reg = er->wps->registrar;
 +      const u8 *auth_macs;
 +      u8 bcast[ETH_ALEN];
 +      size_t count;
 +      union wps_event_data data;
 +
 +      if (er->skip_set_sel_reg) {
 +              wpa_printf(MSG_DEBUG, "WPS ER: Skip SetSelectedRegistrar");
 +              return;
 +      }
 +
 +      msg = wpabuf_alloc(500);
 +      if (msg == NULL)
 +              return;
 +
 +      auth_macs = wps_authorized_macs(reg, &count);
 +      if (count == 0) {
 +              os_memset(bcast, 0xff, ETH_ALEN);
 +              auth_macs = bcast;
 +              count = 1;
 +      }
 +
 +      if (wps_build_version(msg) ||
 +          wps_er_build_selected_registrar(msg, sel_reg) ||
 +          wps_er_build_dev_password_id(msg, dev_passwd_id) ||
 +          wps_er_build_sel_reg_config_methods(msg, sel_reg_config_methods) ||
 +          wps_build_wfa_ext(msg, 0, auth_macs, count) ||
 +          wps_er_build_uuid_r(msg, er->wps->uuid)) {
 +              wpabuf_free(msg);
 +              return;
 +      }
 +
 +      os_memset(&data, 0, sizeof(data));
 +      data.set_sel_reg.sel_reg = sel_reg;
 +      data.set_sel_reg.dev_passwd_id = dev_passwd_id;
 +      data.set_sel_reg.sel_reg_config_methods = sel_reg_config_methods;
 +      data.set_sel_reg.state = WPS_ER_SET_SEL_REG_START;
 +
 +      dl_list_for_each(ap, &er->ap, struct wps_er_ap, list) {
 +              if (er->set_sel_reg_uuid_filter &&
 +                  os_memcmp(ap->uuid, er->set_sel_reg_uuid_filter,
 +                            WPS_UUID_LEN) != 0)
 +                      continue;
 +              data.set_sel_reg.uuid = ap->uuid;
 +              er->wps->event_cb(er->wps->cb_ctx,
 +                                WPS_EV_ER_SET_SELECTED_REGISTRAR, &data);
 +              wps_er_send_set_sel_reg(ap, msg);
 +      }
 +
 +      wpabuf_free(msg);
 +}
 +
 +
 +int wps_er_pbc(struct wps_er *er, const u8 *uuid, const u8 *addr)
 +{
 +      int res;
 +      struct wps_er_ap *ap;
 +
 +      if (er == NULL || er->wps == NULL)
 +              return -1;
 +
 +      if (wps_registrar_pbc_overlap(er->wps->registrar, NULL, NULL)) {
 +              wpa_printf(MSG_DEBUG, "WPS ER: PBC overlap - do not start PBC "
 +                         "mode");
 +              return -2;
 +      }
 +
 +      if (uuid)
 +              ap = wps_er_ap_get(er, NULL, uuid, NULL);
 +      else
 +              ap = NULL;
 +      if (ap == NULL) {
 +              struct wps_er_sta *sta = NULL;
 +              dl_list_for_each(ap, &er->ap, struct wps_er_ap, list) {
 +                      sta = wps_er_sta_get(ap, addr, uuid);
 +                      if (sta) {
 +                              uuid = ap->uuid;
 +                              break;
 +                      }
 +              }
 +              if (sta == NULL)
 +                      return -3; /* Unknown UUID */
 +      }
 +
 +      if (ap->ap_settings == NULL) {
 +              wpa_printf(MSG_DEBUG, "WPS ER: AP settings not known");
 +              return -4;
 +      }
 +
 +      er->set_sel_reg_uuid_filter = uuid;
 +      res = wps_registrar_button_pushed(er->wps->registrar, NULL);
 +      er->set_sel_reg_uuid_filter = NULL;
 +      if (res)
 +              return -1;
 +
 +      return 0;
 +}
 +
 +
 +static void wps_er_ap_settings_cb(void *ctx, const struct wps_credential *cred)
 +{
 +      struct wps_er_ap *ap = ctx;
 +      union wps_event_data data;
 +
 +      wpa_printf(MSG_DEBUG, "WPS ER: AP Settings received");
 +      os_free(ap->ap_settings);
 +      ap->ap_settings = os_malloc(sizeof(*cred));
 +      if (ap->ap_settings) {
 +              os_memcpy(ap->ap_settings, cred, sizeof(*cred));
 +              ap->ap_settings->cred_attr = NULL;
 +      }
 +
 +      os_memset(&data, 0, sizeof(data));
 +      data.ap_settings.uuid = ap->uuid;
 +      data.ap_settings.cred = cred;
 +      ap->er->wps->event_cb(ap->er->wps->cb_ctx, WPS_EV_ER_AP_SETTINGS,
 +                            &data);
 +}
 +
 +
 +const u8 * wps_er_get_sta_uuid(struct wps_er *er, const u8 *addr)
 +{
 +      struct wps_er_ap *ap;
 +      dl_list_for_each(ap, &er->ap, struct wps_er_ap, list) {
 +              struct wps_er_sta *sta;
 +              sta = wps_er_sta_get(ap, addr, NULL);
 +              if (sta)
 +                      return sta->uuid;
 +      }
 +      return NULL;
 +}
 +
 +
 +static void wps_er_http_put_message_cb(void *ctx, struct http_client *c,
 +                                     enum http_client_event event)
 +{
 +      struct wps_er_ap *ap = ctx;
 +      struct wpabuf *reply;
 +      char *msg = NULL;
 +
 +      switch (event) {
 +      case HTTP_CLIENT_OK:
 +              wpa_printf(MSG_DEBUG, "WPS ER: PutMessage OK");
 +              reply = http_client_get_body(c);
-               return;
++              if (reply)
++                      msg = os_zalloc(wpabuf_len(reply) + 1);
++              if (msg == NULL) {
++                      if (ap->wps) {
++                              wps_deinit(ap->wps);
++                              ap->wps = NULL;
++                      }
 +                      break;
++              }
 +              os_memcpy(msg, wpabuf_head(reply), wpabuf_len(reply));
 +              break;
 +      case HTTP_CLIENT_FAILED:
 +      case HTTP_CLIENT_INVALID_REPLY:
 +      case HTTP_CLIENT_TIMEOUT:
 +              wpa_printf(MSG_DEBUG, "WPS ER: PutMessage failed");
 +              if (ap->wps) {
 +                      wps_deinit(ap->wps);
 +                      ap->wps = NULL;
 +              }
 +              break;
 +      }
 +      http_client_free(ap->http);
 +      ap->http = NULL;
 +
 +      if (msg) {
 +              struct wpabuf *buf;
 +              enum http_reply_code ret;
 +              buf = xml_get_base64_item(msg, "NewOutMessage", &ret);
 +              os_free(msg);
 +              if (buf == NULL) {
 +                      wpa_printf(MSG_DEBUG, "WPS ER: Could not extract "
 +                                 "NewOutMessage from PutMessage response");
 +                      wps_deinit(ap->wps);
 +                      ap->wps = NULL;
 +                      return;
 +              }
 +              wps_er_ap_process(ap, buf);
 +              wpabuf_free(buf);
 +      }
 +}
 +
 +
 +static void wps_er_ap_put_message(struct wps_er_ap *ap,
 +                                const struct wpabuf *msg)
 +{
 +      struct wpabuf *buf;
 +      char *len_ptr, *body_ptr;
 +      struct sockaddr_in dst;
 +      char *url, *path;
 +
 +      if (ap->http) {
 +              wpa_printf(MSG_DEBUG, "WPS ER: Pending HTTP operation ongoing "
 +                         "with the AP - cannot continue learn");
 +              return;
 +      }
 +
 +      if (ap->control_url == NULL) {
 +              wpa_printf(MSG_DEBUG, "WPS ER: No controlURL for AP");
 +              return;
 +      }
 +
 +      url = http_client_url_parse(ap->control_url, &dst, &path);
 +      if (url == NULL) {
 +              wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse controlURL");
-               return;
++              goto fail;
 +      }
 +
 +      buf = wps_er_soap_hdr(msg, "PutMessage", "NewInMessage", path, &dst,
 +                            &len_ptr, &body_ptr);
 +      os_free(url);
 +      if (buf == NULL)
-       if (ap->http == NULL)
++              goto fail;
 +
 +      wps_er_soap_end(buf, "PutMessage", len_ptr, body_ptr);
 +
 +      ap->http = http_client_addr(&dst, buf, 10000,
 +                                  wps_er_http_put_message_cb, ap);
++      if (ap->http == NULL) {
 +              wpabuf_free(buf);
++              goto fail;
++      }
++      return;
++
++fail:
++      if (ap->wps) {
++              wps_deinit(ap->wps);
++              ap->wps = NULL;
++      }
 +}
 +
 +
 +static void wps_er_ap_process(struct wps_er_ap *ap, struct wpabuf *msg)
 +{
 +      enum wps_process_res res;
 +      struct wps_parse_attr attr;
 +      enum wsc_op_code op_code;
 +
 +      op_code = WSC_MSG;
 +      if (wps_parse_msg(msg, &attr) == 0 && attr.msg_type) {
 +              switch (*attr.msg_type) {
 +              case WPS_WSC_ACK:
 +                      op_code = WSC_ACK;
 +                      break;
 +              case WPS_WSC_NACK:
 +                      op_code = WSC_NACK;
 +                      break;
 +              case WPS_WSC_DONE:
 +                      op_code = WSC_Done;
 +                      break;
 +              }
 +      }
 +
 +      res = wps_process_msg(ap->wps, op_code, msg);
 +      if (res == WPS_CONTINUE) {
 +              struct wpabuf *next = wps_get_msg(ap->wps, &op_code);
 +              if (next) {
 +                      wps_er_ap_put_message(ap, next);
 +                      wpabuf_free(next);
 +              } else {
 +                      wpa_printf(MSG_DEBUG, "WPS ER: Failed to build "
 +                                 "message");
 +                      wps_deinit(ap->wps);
 +                      ap->wps = NULL;
 +              }
 +      } else if (res == WPS_DONE) {
 +              wpa_printf(MSG_DEBUG, "WPS ER: Protocol run done");
 +              wps_deinit(ap->wps);
 +              ap->wps = NULL;
 +      } else {
 +              wpa_printf(MSG_DEBUG, "WPS ER: Failed to process message from "
 +                         "AP (res=%d)", res);
 +              wps_deinit(ap->wps);
 +              ap->wps = NULL;
 +      }
 +}
 +
 +
 +static void wps_er_ap_learn_m1(struct wps_er_ap *ap, struct wpabuf *m1)
 +{
 +      struct wps_config cfg;
 +
 +      if (ap->wps) {
 +              wpa_printf(MSG_DEBUG, "WPS ER: Protocol run already in "
 +                         "progress with this AP");
 +              return;
 +      }
 +
 +      os_memset(&cfg, 0, sizeof(cfg));
 +      cfg.wps = ap->er->wps;
 +      cfg.registrar = 1;
 +      ap->wps = wps_init(&cfg);
 +      if (ap->wps == NULL)
 +              return;
 +      ap->wps->ap_settings_cb = wps_er_ap_settings_cb;
 +      ap->wps->ap_settings_cb_ctx = ap;
 +
 +      wps_er_ap_process(ap, m1);
 +}
 +
 +
 +static void wps_er_ap_learn(struct wps_er_ap *ap, const char *dev_info)
 +{
 +      struct wpabuf *info;
 +      enum http_reply_code ret;
 +
 +      wpa_printf(MSG_DEBUG, "WPS ER: Received GetDeviceInfo response (M1) "
 +                 "from the AP");
 +      info = xml_get_base64_item(dev_info, "NewDeviceInfo", &ret);
 +      if (info == NULL) {
 +              wpa_printf(MSG_DEBUG, "WPS ER: Could not extract "
 +                         "NewDeviceInfo from GetDeviceInfo response");
 +              return;
 +      }
 +
 +      ap->m1_handler(ap, info);
 +      wpabuf_free(info);
 +}
 +
 +
 +static void wps_er_http_get_dev_info_cb(void *ctx, struct http_client *c,
 +                                      enum http_client_event event)
 +{
 +      struct wps_er_ap *ap = ctx;
 +      struct wpabuf *reply;
 +      char *dev_info = NULL;
 +
 +      switch (event) {
 +      case HTTP_CLIENT_OK:
 +              wpa_printf(MSG_DEBUG, "WPS ER: GetDeviceInfo OK");
 +              reply = http_client_get_body(c);
 +              if (reply == NULL)
 +                      break;
 +              dev_info = os_zalloc(wpabuf_len(reply) + 1);
 +              if (dev_info == NULL)
 +                      break;
 +              os_memcpy(dev_info, wpabuf_head(reply), wpabuf_len(reply));
 +              break;
 +      case HTTP_CLIENT_FAILED:
 +      case HTTP_CLIENT_INVALID_REPLY:
 +      case HTTP_CLIENT_TIMEOUT:
 +              wpa_printf(MSG_DEBUG, "WPS ER: GetDeviceInfo failed");
 +              break;
 +      }
 +      http_client_free(ap->http);
 +      ap->http = NULL;
 +
 +      if (dev_info) {
 +              wps_er_ap_learn(ap, dev_info);
 +              os_free(dev_info);
 +      }
 +}
 +
 +
 +static int wps_er_send_get_device_info(struct wps_er_ap *ap,
 +                                     void (*m1_handler)(struct wps_er_ap *ap,
 +                                                        struct wpabuf *m1))
 +{
 +      struct wpabuf *buf;
 +      char *len_ptr, *body_ptr;
 +      struct sockaddr_in dst;
 +      char *url, *path;
 +
 +      if (ap->http) {
 +              wpa_printf(MSG_DEBUG, "WPS ER: Pending HTTP operation ongoing "
 +                         "with the AP - cannot get device info");
 +              return -1;
 +      }
 +
 +      if (ap->control_url == NULL) {
 +              wpa_printf(MSG_DEBUG, "WPS ER: No controlURL for AP");
 +              return -1;
 +      }
 +
 +      url = http_client_url_parse(ap->control_url, &dst, &path);
 +      if (url == NULL) {
 +              wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse controlURL");
 +              return -1;
 +      }
 +
 +      buf = wps_er_soap_hdr(NULL, "GetDeviceInfo", NULL, path, &dst,
 +                            &len_ptr, &body_ptr);
 +      os_free(url);
 +      if (buf == NULL)
 +              return -1;
 +
 +      wps_er_soap_end(buf, "GetDeviceInfo", len_ptr, body_ptr);
 +
 +      ap->http = http_client_addr(&dst, buf, 10000,
 +                                  wps_er_http_get_dev_info_cb, ap);
 +      if (ap->http == NULL) {
 +              wpabuf_free(buf);
 +              return -1;
 +      }
 +
 +      ap->m1_handler = m1_handler;
 +
 +      return 0;
 +}
 +
 +
 +int wps_er_learn(struct wps_er *er, const u8 *uuid, const u8 *addr,
 +               const u8 *pin, size_t pin_len)
 +{
 +      struct wps_er_ap *ap;
 +
 +      if (er == NULL)
 +              return -1;
 +
 +      ap = wps_er_ap_get(er, NULL, uuid, addr);
 +      if (ap == NULL) {
 +              wpa_printf(MSG_DEBUG, "WPS ER: AP not found for learn "
 +                         "request");
 +              return -1;
 +      }
 +      if (uuid == NULL)
 +              uuid = ap->uuid;
 +      if (ap->wps) {
 +              wpa_printf(MSG_DEBUG, "WPS ER: Pending operation ongoing "
 +                         "with the AP - cannot start learn");
 +              return -1;
 +      }
 +
 +      if (wps_er_send_get_device_info(ap, wps_er_ap_learn_m1) < 0)
 +              return -1;
 +
 +      er->skip_set_sel_reg = 1;
 +      wps_registrar_add_pin(er->wps->registrar, NULL, uuid, pin, pin_len, 0);
 +      er->skip_set_sel_reg = 0;
 +
 +      return 0;
 +}
 +
 +
 +int wps_er_set_config(struct wps_er *er, const u8 *uuid, const u8 *addr,
 +                    const struct wps_credential *cred)
 +{
 +      struct wps_er_ap *ap;
 +
 +      if (er == NULL)
 +              return -1;
 +
 +      ap = wps_er_ap_get(er, NULL, uuid, addr);
 +      if (ap == NULL) {
 +              wpa_printf(MSG_DEBUG, "WPS ER: AP not found for set config "
 +                         "request");
 +              return -1;
 +      }
 +
 +      os_free(ap->ap_settings);
 +      ap->ap_settings = os_malloc(sizeof(*cred));
 +      if (ap->ap_settings == NULL)
 +              return -1;
 +      os_memcpy(ap->ap_settings, cred, sizeof(*cred));
 +      ap->ap_settings->cred_attr = NULL;
 +      wpa_printf(MSG_DEBUG, "WPS ER: Updated local AP settings based set "
 +                 "config request");
 +
 +      return 0;
 +}
 +
 +
 +static void wps_er_ap_config_m1(struct wps_er_ap *ap, struct wpabuf *m1)
 +{
 +      struct wps_config cfg;
 +
 +      if (ap->wps) {
 +              wpa_printf(MSG_DEBUG, "WPS ER: Protocol run already in "
 +                         "progress with this AP");
 +              return;
 +      }
 +
 +      os_memset(&cfg, 0, sizeof(cfg));
 +      cfg.wps = ap->er->wps;
 +      cfg.registrar = 1;
 +      cfg.new_ap_settings = ap->ap_settings;
 +      ap->wps = wps_init(&cfg);
 +      if (ap->wps == NULL)
 +              return;
 +      ap->wps->ap_settings_cb = NULL;
 +      ap->wps->ap_settings_cb_ctx = NULL;
 +
 +      wps_er_ap_process(ap, m1);
 +}
 +
 +
 +int wps_er_config(struct wps_er *er, const u8 *uuid, const u8 *addr,
 +                const u8 *pin, size_t pin_len,
 +                const struct wps_credential *cred)
 +{
 +      struct wps_er_ap *ap;
 +
 +      if (er == NULL)
 +              return -1;
 +
 +      ap = wps_er_ap_get(er, NULL, uuid, addr);
 +      if (ap == NULL) {
 +              wpa_printf(MSG_DEBUG, "WPS ER: AP not found for config "
 +                         "request");
 +              return -1;
 +      }
 +      if (uuid == NULL)
 +              uuid = ap->uuid;
 +      if (ap->wps) {
 +              wpa_printf(MSG_DEBUG, "WPS ER: Pending operation ongoing "
 +                         "with the AP - cannot start config");
 +              return -1;
 +      }
 +
 +      os_free(ap->ap_settings);
 +      ap->ap_settings = os_malloc(sizeof(*cred));
 +      if (ap->ap_settings == NULL)
 +              return -1;
 +      os_memcpy(ap->ap_settings, cred, sizeof(*cred));
 +      ap->ap_settings->cred_attr = NULL;
 +
 +      if (wps_er_send_get_device_info(ap, wps_er_ap_config_m1) < 0)
 +              return -1;
 +
 +      er->skip_set_sel_reg = 1;
 +      wps_registrar_add_pin(er->wps->registrar, NULL, uuid, pin, pin_len, 0);
 +      er->skip_set_sel_reg = 0;
 +
 +      return 0;
 +}
 +
 +
 +#ifdef CONFIG_WPS_NFC
 +
 +struct wpabuf * wps_er_config_token_from_cred(struct wps_context *wps,
 +                                            struct wps_credential *cred)
 +{
 +      struct wpabuf *ret;
 +      struct wps_data data;
 +
 +      ret = wpabuf_alloc(500);
 +      if (ret == NULL)
 +              return NULL;
 +
 +      os_memset(&data, 0, sizeof(data));
 +      data.wps = wps;
 +      data.use_cred = cred;
 +      if (wps_build_cred(&data, ret) ||
 +          wps_build_wfa_ext(ret, 0, NULL, 0)) {
 +              wpabuf_free(ret);
 +              return NULL;
 +      }
 +
 +      return ret;
 +}
 +
 +
 +struct wpabuf * wps_er_nfc_config_token(struct wps_er *er, const u8 *uuid,
 +                                      const u8 *addr)
 +{
 +      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;
 +      }
 +
 +      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 e381fecbdc86a51a4a4cacd46579340d20abefa5,0000000000000000000000000000000000000000..280b2b3bde19518e590ca6b93334b3111d0cefa4
mode 100644,000000..100644
--- /dev/null
@@@ -1,207 -1,0 +1,205 @@@
-                       start += 9;
-                       while (*start == ' ')
-                               start++;
 +/*
 + * Wi-Fi Protected Setup - External Registrar (SSDP)
 + * 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 "common.h"
 +#include "uuid.h"
 +#include "eloop.h"
 +#include "wps_i.h"
 +#include "wps_upnp.h"
 +#include "wps_upnp_i.h"
 +#include "wps_er.h"
 +
 +
 +static void wps_er_ssdp_rx(int sd, void *eloop_ctx, void *sock_ctx)
 +{
 +      struct wps_er *er = eloop_ctx;
 +      struct sockaddr_in addr; /* client address */
 +      socklen_t addr_len;
 +      int nread;
 +      char buf[MULTICAST_MAX_READ], *pos, *pos2, *start;
 +      int wfa = 0, byebye = 0;
 +      int max_age = -1;
 +      char *location = NULL;
 +      u8 uuid[WPS_UUID_LEN];
 +
 +      addr_len = sizeof(addr);
 +      nread = recvfrom(sd, buf, sizeof(buf) - 1, 0,
 +                       (struct sockaddr *) &addr, &addr_len);
 +      if (nread <= 0)
 +              return;
 +      buf[nread] = '\0';
 +      if (er->filter_addr.s_addr &&
 +          er->filter_addr.s_addr != addr.sin_addr.s_addr)
 +              return;
 +
 +      wpa_printf(MSG_DEBUG, "WPS ER: Received SSDP from %s",
 +                 inet_ntoa(addr.sin_addr));
 +      wpa_hexdump_ascii(MSG_MSGDUMP, "WPS ER: Received SSDP contents",
 +                        (u8 *) buf, nread);
 +
 +      if (sd == er->multicast_sd) {
 +              /* Reply to M-SEARCH */
 +              if (os_strncmp(buf, "HTTP/1.1 200 OK", 15) != 0)
 +                      return; /* unexpected response header */
 +      } else {
 +              /* Unsolicited message (likely NOTIFY or M-SEARCH) */
 +              if (os_strncmp(buf, "NOTIFY ", 7) != 0)
 +                      return; /* only process notifications */
 +      }
 +
 +      os_memset(uuid, 0, sizeof(uuid));
 +
 +      for (start = buf; start && *start; start = pos) {
 +              pos = os_strchr(start, '\n');
 +              if (pos) {
 +                      if (pos[-1] == '\r')
 +                              pos[-1] = '\0';
 +                      *pos++ = '\0';
 +              }
 +              if (os_strstr(start, "schemas-wifialliance-org:device:"
 +                            "WFADevice:1"))
 +                      wfa = 1;
 +              if (os_strstr(start, "schemas-wifialliance-org:service:"
 +                            "WFAWLANConfig:1"))
 +                      wfa = 1;
 +              if (os_strncasecmp(start, "LOCATION:", 9) == 0) {
 +                      start += 9;
 +                      while (*start == ' ')
 +                              start++;
 +                      location = start;
 +              } else if (os_strncasecmp(start, "NTS:", 4) == 0) {
 +                      if (os_strstr(start, "ssdp:byebye"))
 +                              byebye = 1;
 +              } else if (os_strncasecmp(start, "CACHE-CONTROL:", 14) == 0) {
++                      start += 14;
 +                      pos2 = os_strstr(start, "max-age=");
 +                      if (pos2 == NULL)
 +                              continue;
 +                      pos2 += 8;
 +                      max_age = atoi(pos2);
 +              } else if (os_strncasecmp(start, "USN:", 4) == 0) {
 +                      start += 4;
 +                      pos2 = os_strstr(start, "uuid:");
 +                      if (pos2) {
 +                              pos2 += 5;
 +                              while (*pos2 == ' ')
 +                                      pos2++;
 +                              if (uuid_str2bin(pos2, uuid) < 0) {
 +                                      wpa_printf(MSG_DEBUG, "WPS ER: "
 +                                                 "Invalid UUID in USN: %s",
 +                                                 pos2);
 +                                      return;
 +                              }
 +                      }
 +              }
 +      }
 +
 +      if (!wfa)
 +              return; /* Not WPS advertisement/reply */
 +
 +      if (byebye) {
 +              wps_er_ap_cache_settings(er, &addr.sin_addr);
 +              wps_er_ap_remove(er, &addr.sin_addr);
 +              return;
 +      }
 +
 +      if (!location)
 +              return; /* Unknown location */
 +
 +      if (max_age < 1)
 +              return; /* No max-age reported */
 +
 +      wpa_printf(MSG_DEBUG, "WPS ER: AP discovered: %s "
 +                 "(packet source: %s  max-age: %d)",
 +                 location, inet_ntoa(addr.sin_addr), max_age);
 +
 +      wps_er_ap_add(er, uuid, &addr.sin_addr, location, max_age);
 +}
 +
 +
 +void wps_er_send_ssdp_msearch(struct wps_er *er)
 +{
 +      struct wpabuf *msg;
 +      struct sockaddr_in dest;
 +
 +      msg = wpabuf_alloc(500);
 +      if (msg == NULL)
 +              return;
 +
 +      wpabuf_put_str(msg,
 +                     "M-SEARCH * HTTP/1.1\r\n"
 +                     "HOST: 239.255.255.250:1900\r\n"
 +                     "MAN: \"ssdp:discover\"\r\n"
 +                     "MX: 3\r\n"
 +                     "ST: urn:schemas-wifialliance-org:device:WFADevice:1"
 +                     "\r\n"
 +                     "\r\n");
 +
 +      os_memset(&dest, 0, sizeof(dest));
 +      dest.sin_family = AF_INET;
 +      dest.sin_addr.s_addr = inet_addr(UPNP_MULTICAST_ADDRESS);
 +      dest.sin_port = htons(UPNP_MULTICAST_PORT);
 +
 +      if (sendto(er->multicast_sd, wpabuf_head(msg), wpabuf_len(msg), 0,
 +                 (struct sockaddr *) &dest, sizeof(dest)) < 0)
 +              wpa_printf(MSG_DEBUG, "WPS ER: M-SEARCH sendto failed: "
 +                         "%d (%s)", errno, strerror(errno));
 +
 +      wpabuf_free(msg);
 +}
 +
 +
 +int wps_er_ssdp_init(struct wps_er *er)
 +{
 +      if (add_ssdp_network(er->ifname)) {
 +              wpa_printf(MSG_INFO, "WPS ER: Failed to add routing entry for "
 +                         "SSDP");
 +              return -1;
 +      }
 +
 +      er->multicast_sd = ssdp_open_multicast_sock(er->ip_addr,
 +                                                  er->forced_ifname ?
 +                                                  er->ifname : NULL);
 +      if (er->multicast_sd < 0) {
 +              wpa_printf(MSG_INFO, "WPS ER: Failed to open multicast socket "
 +                         "for SSDP");
 +              return -1;
 +      }
 +
 +      er->ssdp_sd = ssdp_listener_open();
 +      if (er->ssdp_sd < 0) {
 +              wpa_printf(MSG_INFO, "WPS ER: Failed to open SSDP listener "
 +                         "socket");
 +              return -1;
 +      }
 +
 +      if (eloop_register_sock(er->multicast_sd, EVENT_TYPE_READ,
 +                              wps_er_ssdp_rx, er, NULL) ||
 +          eloop_register_sock(er->ssdp_sd, EVENT_TYPE_READ,
 +                              wps_er_ssdp_rx, er, NULL))
 +              return -1;
 +
 +      wps_er_send_ssdp_msearch(er);
 +
 +      return 0;
 +}
 +
 +
 +void wps_er_ssdp_deinit(struct wps_er *er)
 +{
 +      if (er->multicast_sd >= 0) {
 +              eloop_unregister_sock(er->multicast_sd, EVENT_TYPE_READ);
 +              close(er->multicast_sd);
 +      }
 +      if (er->ssdp_sd >= 0) {
 +              eloop_unregister_sock(er->ssdp_sd, EVENT_TYPE_READ);
 +              close(er->ssdp_sd);
 +      }
 +}
index 6800e86db91195983497e6b8549cabd40dad96f4,0000000000000000000000000000000000000000..350630768be4b07f801cf10e2032b340e61e6bac
mode 100644,000000..100644
--- /dev/null
@@@ -1,337 -1,0 +1,337 @@@
- struct wps_attr_parse_test wps_attr_parse_test_cases[] = {
 +/*
 + * 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 *test =
++const 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;
++              const 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 48b7e1288af08d185087badf79e2b95a62a0f9d8,0000000000000000000000000000000000000000..4ca3a42d4c738a90dd552eb092f1315907d076e8
mode 100644,000000..100644
--- /dev/null
@@@ -1,3662 -1,0 +1,3670 @@@
-                       sha256_vector(1, addr, &attr->public_key_len, hash);
 +/*
 + * Wi-Fi Protected Setup - Registrar
 + * 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.
 + */
 +
 +#include "utils/includes.h"
 +
 +#include "utils/common.h"
 +#include "utils/base64.h"
 +#include "utils/eloop.h"
 +#include "utils/uuid.h"
 +#include "utils/list.h"
 +#include "crypto/crypto.h"
 +#include "crypto/sha256.h"
 +#include "crypto/random.h"
 +#include "common/ieee802_11_defs.h"
 +#include "wps_i.h"
 +#include "wps_dev_attr.h"
 +#include "wps_upnp.h"
 +#include "wps_upnp_i.h"
 +
 +#ifndef CONFIG_WPS_STRICT
 +#define WPS_WORKAROUNDS
 +#endif /* CONFIG_WPS_STRICT */
 +
 +#ifdef CONFIG_WPS_NFC
 +
 +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);
 +      bin_clear_free(token, sizeof(*token));
 +}
 +
 +
 +static void wps_free_nfc_pw_tokens(struct dl_list *tokens, u16 pw_id)
 +{
 +      struct wps_nfc_pw_token *token, *prev;
 +      dl_list_for_each_safe(token, prev, tokens, struct wps_nfc_pw_token,
 +                            list) {
 +              if (pw_id == 0 || pw_id == token->pw_id)
 +                      wps_remove_nfc_pw_token(token);
 +      }
 +}
 +
 +
 +static struct wps_nfc_pw_token * wps_get_nfc_pw_token(struct dl_list *tokens,
 +                                                    u16 pw_id)
 +{
 +      struct wps_nfc_pw_token *token;
 +      dl_list_for_each(token, tokens, struct wps_nfc_pw_token, list) {
 +              if (pw_id == token->pw_id)
 +                      return token;
 +      }
 +      return NULL;
 +}
 +
 +#else /* CONFIG_WPS_NFC */
 +
 +#define wps_free_nfc_pw_tokens(t, p) do { } while (0)
 +
 +#endif /* CONFIG_WPS_NFC */
 +
 +
 +struct wps_uuid_pin {
 +      struct dl_list list;
 +      u8 uuid[WPS_UUID_LEN];
 +      int wildcard_uuid;
 +      u8 *pin;
 +      size_t pin_len;
 +#define PIN_LOCKED BIT(0)
 +#define PIN_EXPIRES BIT(1)
 +      int flags;
 +      struct os_reltime expiration;
 +      u8 enrollee_addr[ETH_ALEN];
 +};
 +
 +
 +static void wps_free_pin(struct wps_uuid_pin *pin)
 +{
 +      bin_clear_free(pin->pin, pin->pin_len);
 +      os_free(pin);
 +}
 +
 +
 +static void wps_remove_pin(struct wps_uuid_pin *pin)
 +{
 +      dl_list_del(&pin->list);
 +      wps_free_pin(pin);
 +}
 +
 +
 +static void wps_free_pins(struct dl_list *pins)
 +{
 +      struct wps_uuid_pin *pin, *prev;
 +      dl_list_for_each_safe(pin, prev, pins, struct wps_uuid_pin, list)
 +              wps_remove_pin(pin);
 +}
 +
 +
 +struct wps_pbc_session {
 +      struct wps_pbc_session *next;
 +      u8 addr[ETH_ALEN];
 +      u8 uuid_e[WPS_UUID_LEN];
 +      struct os_reltime timestamp;
 +};
 +
 +
 +static void wps_free_pbc_sessions(struct wps_pbc_session *pbc)
 +{
 +      struct wps_pbc_session *prev;
 +
 +      while (pbc) {
 +              prev = pbc;
 +              pbc = pbc->next;
 +              os_free(prev);
 +      }
 +}
 +
 +
 +struct wps_registrar_device {
 +      struct wps_registrar_device *next;
 +      struct wps_device_data dev;
 +      u8 uuid[WPS_UUID_LEN];
 +};
 +
 +
 +struct wps_registrar {
 +      struct wps_context *wps;
 +
 +      int pbc;
 +      int selected_registrar;
 +
 +      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,
 +                            const struct wps_device_data *dev);
 +      void (*reg_success_cb)(void *ctx, const u8 *mac_addr,
 +                             const u8 *uuid_e, const u8 *dev_pw,
 +                             size_t dev_pw_len);
 +      void (*set_sel_reg_cb)(void *ctx, int sel_reg, u16 dev_passwd_id,
 +                             u16 sel_reg_config_methods);
 +      void (*enrollee_seen_cb)(void *ctx, const u8 *addr, const u8 *uuid_e,
 +                               const u8 *pri_dev_type, u16 config_methods,
 +                               u16 dev_password_id, u8 request_type,
 +                               const char *dev_name);
 +      void *cb_ctx;
 +
 +      struct dl_list pins;
 +      struct dl_list nfc_pw_tokens;
 +      struct wps_pbc_session *pbc_sessions;
 +
 +      int skip_cred_build;
 +      struct wpabuf *extra_cred;
 +      int disable_auto_conf;
 +      int sel_reg_union;
 +      int sel_reg_dev_password_id_override;
 +      int sel_reg_config_methods_override;
 +      int static_wep_only;
 +      int dualband;
 +      int force_per_enrollee_psk;
 +
 +      struct wps_registrar_device *devices;
 +
 +      int force_pbc_overlap;
 +
 +      u8 authorized_macs[WPS_MAX_AUTHORIZED_MACS][ETH_ALEN];
 +      u8 authorized_macs_union[WPS_MAX_AUTHORIZED_MACS][ETH_ALEN];
 +
 +      u8 p2p_dev_addr[ETH_ALEN];
 +
 +      u8 pbc_ignore_uuid[WPS_UUID_LEN];
 +#ifdef WPS_WORKAROUNDS
 +      struct os_reltime pbc_ignore_start;
 +#endif /* WPS_WORKAROUNDS */
 +};
 +
 +
 +static int wps_set_ie(struct wps_registrar *reg);
 +static void wps_registrar_pbc_timeout(void *eloop_ctx, void *timeout_ctx);
 +static void wps_registrar_set_selected_timeout(void *eloop_ctx,
 +                                             void *timeout_ctx);
 +static void wps_registrar_remove_pin(struct wps_registrar *reg,
 +                                   struct wps_uuid_pin *pin);
 +
 +
 +static void wps_registrar_add_authorized_mac(struct wps_registrar *reg,
 +                                           const u8 *addr)
 +{
 +      int i;
 +      wpa_printf(MSG_DEBUG, "WPS: Add authorized MAC " MACSTR,
 +                 MAC2STR(addr));
 +      for (i = 0; i < WPS_MAX_AUTHORIZED_MACS; i++)
 +              if (os_memcmp(reg->authorized_macs[i], addr, ETH_ALEN) == 0) {
 +                      wpa_printf(MSG_DEBUG, "WPS: Authorized MAC was "
 +                                 "already in the list");
 +                      return; /* already in list */
 +              }
 +      for (i = WPS_MAX_AUTHORIZED_MACS - 1; i > 0; i--)
 +              os_memcpy(reg->authorized_macs[i], reg->authorized_macs[i - 1],
 +                        ETH_ALEN);
 +      os_memcpy(reg->authorized_macs[0], addr, ETH_ALEN);
 +      wpa_hexdump(MSG_DEBUG, "WPS: Authorized MACs",
 +                  (u8 *) reg->authorized_macs, sizeof(reg->authorized_macs));
 +}
 +
 +
 +static void wps_registrar_remove_authorized_mac(struct wps_registrar *reg,
 +                                              const u8 *addr)
 +{
 +      int i;
 +      wpa_printf(MSG_DEBUG, "WPS: Remove authorized MAC " MACSTR,
 +                 MAC2STR(addr));
 +      for (i = 0; i < WPS_MAX_AUTHORIZED_MACS; i++) {
 +              if (os_memcmp(reg->authorized_macs, addr, ETH_ALEN) == 0)
 +                      break;
 +      }
 +      if (i == WPS_MAX_AUTHORIZED_MACS) {
 +              wpa_printf(MSG_DEBUG, "WPS: Authorized MAC was not in the "
 +                         "list");
 +              return; /* not in the list */
 +      }
 +      for (; i + 1 < WPS_MAX_AUTHORIZED_MACS; i++)
 +              os_memcpy(reg->authorized_macs[i], reg->authorized_macs[i + 1],
 +                        ETH_ALEN);
 +      os_memset(reg->authorized_macs[WPS_MAX_AUTHORIZED_MACS - 1], 0,
 +                ETH_ALEN);
 +      wpa_hexdump(MSG_DEBUG, "WPS: Authorized MACs",
 +                  (u8 *) reg->authorized_macs, sizeof(reg->authorized_macs));
 +}
 +
 +
 +static void wps_free_devices(struct wps_registrar_device *dev)
 +{
 +      struct wps_registrar_device *prev;
 +
 +      while (dev) {
 +              prev = dev;
 +              dev = dev->next;
 +              wps_device_data_free(&prev->dev);
 +              os_free(prev);
 +      }
 +}
 +
 +
 +static struct wps_registrar_device * wps_device_get(struct wps_registrar *reg,
 +                                                  const u8 *addr)
 +{
 +      struct wps_registrar_device *dev;
 +
 +      for (dev = reg->devices; dev; dev = dev->next) {
 +              if (os_memcmp(dev->dev.mac_addr, addr, ETH_ALEN) == 0)
 +                      return dev;
 +      }
 +      return NULL;
 +}
 +
 +
 +static void wps_device_clone_data(struct wps_device_data *dst,
 +                                struct wps_device_data *src)
 +{
 +      os_memcpy(dst->mac_addr, src->mac_addr, ETH_ALEN);
 +      os_memcpy(dst->pri_dev_type, src->pri_dev_type, WPS_DEV_TYPE_LEN);
 +
 +#define WPS_STRDUP(n) \
 +      os_free(dst->n); \
 +      dst->n = src->n ? os_strdup(src->n) : NULL
 +
 +      WPS_STRDUP(device_name);
 +      WPS_STRDUP(manufacturer);
 +      WPS_STRDUP(model_name);
 +      WPS_STRDUP(model_number);
 +      WPS_STRDUP(serial_number);
 +#undef WPS_STRDUP
 +}
 +
 +
 +int wps_device_store(struct wps_registrar *reg,
 +                   struct wps_device_data *dev, const u8 *uuid)
 +{
 +      struct wps_registrar_device *d;
 +
 +      d = wps_device_get(reg, dev->mac_addr);
 +      if (d == NULL) {
 +              d = os_zalloc(sizeof(*d));
 +              if (d == NULL)
 +                      return -1;
 +              d->next = reg->devices;
 +              reg->devices = d;
 +      }
 +
 +      wps_device_clone_data(&d->dev, dev);
 +      os_memcpy(d->uuid, uuid, WPS_UUID_LEN);
 +
 +      return 0;
 +}
 +
 +
 +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_reltime now;
 +
 +      os_get_reltime(&now);
 +
 +      pbc = reg->pbc_sessions;
 +      while (pbc) {
 +              if (os_memcmp(pbc->addr, addr, ETH_ALEN) == 0 &&
 +                  os_memcmp(pbc->uuid_e, uuid_e, WPS_UUID_LEN) == 0) {
 +                      if (prev)
 +                              prev->next = pbc->next;
 +                      else
 +                              reg->pbc_sessions = pbc->next;
 +                      break;
 +              }
 +              prev = pbc;
 +              pbc = pbc->next;
 +      }
 +
 +      if (!pbc) {
 +              pbc = os_zalloc(sizeof(*pbc));
 +              if (pbc == NULL)
 +                      return;
 +              os_memcpy(pbc->addr, addr, ETH_ALEN);
 +              if (uuid_e)
 +                      os_memcpy(pbc->uuid_e, uuid_e, WPS_UUID_LEN);
 +      }
 +
 +      pbc->next = reg->pbc_sessions;
 +      reg->pbc_sessions = pbc;
 +      pbc->timestamp = now;
 +
 +      /* remove entries that have timed out */
 +      prev = pbc;
 +      pbc = pbc->next;
 +
 +      while (pbc) {
 +              if (os_reltime_expired(&now, &pbc->timestamp,
 +                                     WPS_PBC_WALK_TIME)) {
 +                      prev->next = NULL;
 +                      wps_free_pbc_sessions(pbc);
 +                      break;
 +              }
 +              prev = pbc;
 +              pbc = pbc->next;
 +      }
 +}
 +
 +
 +static void wps_registrar_remove_pbc_session(struct wps_registrar *reg,
 +                                           const u8 *uuid_e,
 +                                           const u8 *p2p_dev_addr)
 +{
 +      struct wps_pbc_session *pbc, *prev = NULL, *tmp;
 +
 +      pbc = reg->pbc_sessions;
 +      while (pbc) {
 +              if (os_memcmp(pbc->uuid_e, uuid_e, WPS_UUID_LEN) == 0 ||
 +                  (p2p_dev_addr && !is_zero_ether_addr(reg->p2p_dev_addr) &&
 +                   os_memcmp(reg->p2p_dev_addr, p2p_dev_addr, ETH_ALEN) ==
 +                   0)) {
 +                      if (prev)
 +                              prev->next = pbc->next;
 +                      else
 +                              reg->pbc_sessions = pbc->next;
 +                      tmp = pbc;
 +                      pbc = pbc->next;
 +                      wpa_printf(MSG_DEBUG, "WPS: Removing PBC session for "
 +                                 "addr=" MACSTR, MAC2STR(tmp->addr));
 +                      wpa_hexdump(MSG_DEBUG, "WPS: Removed UUID-E",
 +                                  tmp->uuid_e, WPS_UUID_LEN);
 +                      os_free(tmp);
 +                      continue;
 +              }
 +              prev = pbc;
 +              pbc = pbc->next;
 +      }
 +}
 +
 +
 +int wps_registrar_pbc_overlap(struct wps_registrar *reg,
 +                            const u8 *addr, const u8 *uuid_e)
 +{
 +      int count = 0;
 +      struct wps_pbc_session *pbc;
 +      struct wps_pbc_session *first = NULL;
 +      struct os_reltime now;
 +
 +      os_get_reltime(&now);
 +
 +      wpa_printf(MSG_DEBUG, "WPS: Checking active PBC sessions for overlap");
 +
 +      if (uuid_e) {
 +              wpa_printf(MSG_DEBUG, "WPS: Add one for the requested UUID");
 +              wpa_hexdump(MSG_DEBUG, "WPS: Requested UUID",
 +                          uuid_e, WPS_UUID_LEN);
 +              count++;
 +      }
 +
 +      for (pbc = reg->pbc_sessions; pbc; pbc = pbc->next) {
 +              wpa_printf(MSG_DEBUG, "WPS: Consider PBC session with " MACSTR,
 +                         MAC2STR(pbc->addr));
 +              wpa_hexdump(MSG_DEBUG, "WPS: UUID-E",
 +                          pbc->uuid_e, WPS_UUID_LEN);
 +              if (os_reltime_expired(&now, &pbc->timestamp,
 +                                     WPS_PBC_WALK_TIME)) {
 +                      wpa_printf(MSG_DEBUG, "WPS: PBC walk time has expired");
 +                      break;
 +              }
 +              if (first &&
 +                  os_memcmp(pbc->uuid_e, first->uuid_e, WPS_UUID_LEN) == 0) {
 +                      wpa_printf(MSG_DEBUG, "WPS: Same Enrollee");
 +                      continue; /* same Enrollee */
 +              }
 +              if (uuid_e == NULL ||
 +                  os_memcmp(uuid_e, pbc->uuid_e, WPS_UUID_LEN)) {
 +                      wpa_printf(MSG_DEBUG, "WPS: New Enrollee");
 +                      count++;
 +              }
 +              if (first == NULL)
 +                      first = pbc;
 +      }
 +
 +      wpa_printf(MSG_DEBUG, "WPS: %u active PBC session(s) found", count);
 +
 +      return count > 1 ? 1 : 0;
 +}
 +
 +
 +static int wps_build_wps_state(struct wps_context *wps, struct wpabuf *msg)
 +{
 +      wpa_printf(MSG_DEBUG, "WPS:  * Wi-Fi Protected Setup State (%d)",
 +                 wps->wps_state);
 +      wpabuf_put_be16(msg, ATTR_WPS_STATE);
 +      wpabuf_put_be16(msg, 1);
 +      wpabuf_put_u8(msg, wps->wps_state);
 +      return 0;
 +}
 +
 +
 +#ifdef CONFIG_WPS_UPNP
 +static void wps_registrar_free_pending_m2(struct wps_context *wps)
 +{
 +      struct upnp_pending_message *p, *p2, *prev = NULL;
 +      p = wps->upnp_msgs;
 +      while (p) {
 +              if (p->type == WPS_M2 || p->type == WPS_M2D) {
 +                      if (prev == NULL)
 +                              wps->upnp_msgs = p->next;
 +                      else
 +                              prev->next = p->next;
 +                      wpa_printf(MSG_DEBUG, "WPS UPnP: Drop pending M2/M2D");
 +                      p2 = p;
 +                      p = p->next;
 +                      wpabuf_free(p2->msg);
 +                      os_free(p2);
 +                      continue;
 +              }
 +              prev = p;
 +              p = p->next;
 +      }
 +}
 +#endif /* CONFIG_WPS_UPNP */
 +
 +
 +static int wps_build_ap_setup_locked(struct wps_context *wps,
 +                                   struct wpabuf *msg)
 +{
 +      if (wps->ap_setup_locked && wps->ap_setup_locked != 2) {
 +              wpa_printf(MSG_DEBUG, "WPS:  * AP Setup Locked");
 +              wpabuf_put_be16(msg, ATTR_AP_SETUP_LOCKED);
 +              wpabuf_put_be16(msg, 1);
 +              wpabuf_put_u8(msg, 1);
 +      }
 +      return 0;
 +}
 +
 +
 +static int wps_build_selected_registrar(struct wps_registrar *reg,
 +                                      struct wpabuf *msg)
 +{
 +      if (!reg->sel_reg_union)
 +              return 0;
 +      wpa_printf(MSG_DEBUG, "WPS:  * Selected Registrar");
 +      wpabuf_put_be16(msg, ATTR_SELECTED_REGISTRAR);
 +      wpabuf_put_be16(msg, 1);
 +      wpabuf_put_u8(msg, 1);
 +      return 0;
 +}
 +
 +
 +static int wps_build_sel_reg_dev_password_id(struct wps_registrar *reg,
 +                                           struct wpabuf *msg)
 +{
 +      u16 id = reg->pbc ? DEV_PW_PUSHBUTTON : DEV_PW_DEFAULT;
 +      if (!reg->sel_reg_union)
 +              return 0;
 +      if (reg->sel_reg_dev_password_id_override >= 0)
 +              id = reg->sel_reg_dev_password_id_override;
 +      wpa_printf(MSG_DEBUG, "WPS:  * Device Password ID (%d)", id);
 +      wpabuf_put_be16(msg, ATTR_DEV_PASSWORD_ID);
 +      wpabuf_put_be16(msg, 2);
 +      wpabuf_put_be16(msg, id);
 +      return 0;
 +}
 +
 +
 +static int wps_build_sel_pbc_reg_uuid_e(struct wps_registrar *reg,
 +                                      struct wpabuf *msg)
 +{
 +      u16 id = reg->pbc ? DEV_PW_PUSHBUTTON : DEV_PW_DEFAULT;
 +      if (!reg->sel_reg_union)
 +              return 0;
 +      if (reg->sel_reg_dev_password_id_override >= 0)
 +              id = reg->sel_reg_dev_password_id_override;
 +      if (id != DEV_PW_PUSHBUTTON || !reg->dualband)
 +              return 0;
 +      return wps_build_uuid_e(msg, reg->wps->uuid);
 +}
 +
 +
 +static void wps_set_pushbutton(u16 *methods, u16 conf_methods)
 +{
 +      *methods |= WPS_CONFIG_PUSHBUTTON;
 +      if ((conf_methods & WPS_CONFIG_VIRT_PUSHBUTTON) ==
 +          WPS_CONFIG_VIRT_PUSHBUTTON)
 +              *methods |= WPS_CONFIG_VIRT_PUSHBUTTON;
 +      if ((conf_methods & WPS_CONFIG_PHY_PUSHBUTTON) ==
 +          WPS_CONFIG_PHY_PUSHBUTTON)
 +              *methods |= WPS_CONFIG_PHY_PUSHBUTTON;
 +      if ((*methods & WPS_CONFIG_VIRT_PUSHBUTTON) !=
 +          WPS_CONFIG_VIRT_PUSHBUTTON &&
 +          (*methods & WPS_CONFIG_PHY_PUSHBUTTON) !=
 +          WPS_CONFIG_PHY_PUSHBUTTON) {
 +              /*
 +               * Required to include virtual/physical flag, but we were not
 +               * configured with push button type, so have to default to one
 +               * of them.
 +               */
 +              *methods |= WPS_CONFIG_PHY_PUSHBUTTON;
 +      }
 +}
 +
 +
 +static int wps_build_sel_reg_config_methods(struct wps_registrar *reg,
 +                                          struct wpabuf *msg)
 +{
 +      u16 methods;
 +      if (!reg->sel_reg_union)
 +              return 0;
 +      methods = reg->wps->config_methods;
 +      methods &= ~WPS_CONFIG_PUSHBUTTON;
 +      methods &= ~(WPS_CONFIG_VIRT_PUSHBUTTON |
 +                   WPS_CONFIG_PHY_PUSHBUTTON);
 +      if (reg->pbc)
 +              wps_set_pushbutton(&methods, reg->wps->config_methods);
 +      if (reg->sel_reg_config_methods_override >= 0)
 +              methods = reg->sel_reg_config_methods_override;
 +      wpa_printf(MSG_DEBUG, "WPS:  * Selected Registrar Config Methods (%x)",
 +                 methods);
 +      wpabuf_put_be16(msg, ATTR_SELECTED_REGISTRAR_CONFIG_METHODS);
 +      wpabuf_put_be16(msg, 2);
 +      wpabuf_put_be16(msg, methods);
 +      return 0;
 +}
 +
 +
 +static int wps_build_probe_config_methods(struct wps_registrar *reg,
 +                                        struct wpabuf *msg)
 +{
 +      u16 methods;
 +      /*
 +       * These are the methods that the AP supports as an Enrollee for adding
 +       * external Registrars.
 +       */
 +      methods = reg->wps->config_methods & ~WPS_CONFIG_PUSHBUTTON;
 +      methods &= ~(WPS_CONFIG_VIRT_PUSHBUTTON |
 +                   WPS_CONFIG_PHY_PUSHBUTTON);
 +      wpa_printf(MSG_DEBUG, "WPS:  * Config Methods (%x)", methods);
 +      wpabuf_put_be16(msg, ATTR_CONFIG_METHODS);
 +      wpabuf_put_be16(msg, 2);
 +      wpabuf_put_be16(msg, methods);
 +      return 0;
 +}
 +
 +
 +static int wps_build_config_methods_r(struct wps_registrar *reg,
 +                                    struct wpabuf *msg)
 +{
 +      return wps_build_config_methods(msg, reg->wps->config_methods);
 +}
 +
 +
 +const u8 * wps_authorized_macs(struct wps_registrar *reg, size_t *count)
 +{
 +      *count = 0;
 +
 +      while (*count < WPS_MAX_AUTHORIZED_MACS) {
 +              if (is_zero_ether_addr(reg->authorized_macs_union[*count]))
 +                      break;
 +              (*count)++;
 +      }
 +
 +      return (const u8 *) reg->authorized_macs_union;
 +}
 +
 +
 +/**
 + * wps_registrar_init - Initialize WPS Registrar data
 + * @wps: Pointer to longterm WPS context
 + * @cfg: Registrar configuration
 + * Returns: Pointer to allocated Registrar data or %NULL on failure
 + *
 + * This function is used to initialize WPS Registrar functionality. It can be
 + * used for a single Registrar run (e.g., when run in a supplicant) or multiple
 + * runs (e.g., when run as an internal Registrar in an AP). Caller is
 + * responsible for freeing the returned data with wps_registrar_deinit() when
 + * Registrar functionality is not needed anymore.
 + */
 +struct wps_registrar *
 +wps_registrar_init(struct wps_context *wps,
 +                 const struct wps_registrar_config *cfg)
 +{
 +      struct wps_registrar *reg = os_zalloc(sizeof(*reg));
 +      if (reg == NULL)
 +              return NULL;
 +
 +      dl_list_init(&reg->pins);
 +      dl_list_init(&reg->nfc_pw_tokens);
 +      reg->wps = wps;
 +      reg->new_psk_cb = cfg->new_psk_cb;
 +      reg->set_ie_cb = cfg->set_ie_cb;
 +      reg->pin_needed_cb = cfg->pin_needed_cb;
 +      reg->reg_success_cb = cfg->reg_success_cb;
 +      reg->set_sel_reg_cb = cfg->set_sel_reg_cb;
 +      reg->enrollee_seen_cb = cfg->enrollee_seen_cb;
 +      reg->cb_ctx = cfg->cb_ctx;
 +      reg->skip_cred_build = cfg->skip_cred_build;
 +      if (cfg->extra_cred) {
 +              reg->extra_cred = wpabuf_alloc_copy(cfg->extra_cred,
 +                                                  cfg->extra_cred_len);
 +              if (reg->extra_cred == NULL) {
 +                      os_free(reg);
 +                      return NULL;
 +              }
 +      }
 +      reg->disable_auto_conf = cfg->disable_auto_conf;
 +      reg->sel_reg_dev_password_id_override = -1;
 +      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);
 +              return NULL;
 +      }
 +
 +      return reg;
 +}
 +
 +
 +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()
 + */
 +void wps_registrar_deinit(struct wps_registrar *reg)
 +{
 +      if (reg == NULL)
 +              return;
 +      eloop_cancel_timeout(wps_registrar_pbc_timeout, reg, NULL);
 +      eloop_cancel_timeout(wps_registrar_set_selected_timeout, reg, NULL);
 +      wps_registrar_flush(reg);
 +      wpabuf_free(reg->extra_cred);
 +      os_free(reg);
 +}
 +
 +
 +static void wps_registrar_invalidate_unused(struct wps_registrar *reg)
 +{
 +      struct wps_uuid_pin *pin;
 +
 +      dl_list_for_each(pin, &reg->pins, struct wps_uuid_pin, list) {
 +              if (pin->wildcard_uuid == 1 && !(pin->flags & PIN_LOCKED)) {
 +                      wpa_printf(MSG_DEBUG, "WPS: Invalidate previously "
 +                                 "configured wildcard PIN");
 +                      wps_registrar_remove_pin(reg, pin);
 +                      break;
 +              }
 +      }
 +}
 +
 +
 +/**
 + * wps_registrar_add_pin - Configure a new PIN for Registrar
 + * @reg: Registrar data from wps_registrar_init()
 + * @addr: Enrollee MAC address or %NULL if not known
 + * @uuid: UUID-E or %NULL for wildcard (any UUID)
 + * @pin: PIN (Device Password)
 + * @pin_len: Length of pin in octets
 + * @timeout: Time (in seconds) when the PIN will be invalidated; 0 = no timeout
 + * Returns: 0 on success, -1 on failure
 + */
 +int wps_registrar_add_pin(struct wps_registrar *reg, const u8 *addr,
 +                        const u8 *uuid, const u8 *pin, size_t pin_len,
 +                        int timeout)
 +{
 +      struct wps_uuid_pin *p;
 +
 +      p = os_zalloc(sizeof(*p));
 +      if (p == NULL)
 +              return -1;
 +      if (addr)
 +              os_memcpy(p->enrollee_addr, addr, ETH_ALEN);
 +      if (uuid == NULL)
 +              p->wildcard_uuid = 1;
 +      else
 +              os_memcpy(p->uuid, uuid, WPS_UUID_LEN);
 +      p->pin = os_malloc(pin_len);
 +      if (p->pin == NULL) {
 +              os_free(p);
 +              return -1;
 +      }
 +      os_memcpy(p->pin, pin, pin_len);
 +      p->pin_len = pin_len;
 +
 +      if (timeout) {
 +              p->flags |= PIN_EXPIRES;
 +              os_get_reltime(&p->expiration);
 +              p->expiration.sec += timeout;
 +      }
 +
 +      if (p->wildcard_uuid)
 +              wps_registrar_invalidate_unused(reg);
 +
 +      dl_list_add(&reg->pins, &p->list);
 +
 +      wpa_printf(MSG_DEBUG, "WPS: A new PIN configured (timeout=%d)",
 +                 timeout);
 +      wpa_hexdump(MSG_DEBUG, "WPS: UUID", uuid, WPS_UUID_LEN);
 +      wpa_hexdump_ascii_key(MSG_DEBUG, "WPS: PIN", pin, pin_len);
 +      reg->selected_registrar = 1;
 +      reg->pbc = 0;
 +      if (addr)
 +              wps_registrar_add_authorized_mac(reg, addr);
 +      else
 +              wps_registrar_add_authorized_mac(
 +                      reg, (u8 *) "\xff\xff\xff\xff\xff\xff");
 +      wps_registrar_selected_registrar_changed(reg, 0);
 +      eloop_cancel_timeout(wps_registrar_set_selected_timeout, reg, NULL);
 +      eloop_register_timeout(WPS_PBC_WALK_TIME, 0,
 +                             wps_registrar_set_selected_timeout,
 +                             reg, NULL);
 +
 +      return 0;
 +}
 +
 +
 +static void wps_registrar_remove_pin(struct wps_registrar *reg,
 +                                   struct wps_uuid_pin *pin)
 +{
 +      u8 *addr;
 +      u8 bcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
 +
 +      if (is_zero_ether_addr(pin->enrollee_addr))
 +              addr = bcast;
 +      else
 +              addr = pin->enrollee_addr;
 +      wps_registrar_remove_authorized_mac(reg, addr);
 +      wps_remove_pin(pin);
 +      wps_registrar_selected_registrar_changed(reg, 0);
 +}
 +
 +
 +static void wps_registrar_expire_pins(struct wps_registrar *reg)
 +{
 +      struct wps_uuid_pin *pin, *prev;
 +      struct os_reltime 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_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);
 +              }
 +      }
 +}
 +
 +
 +/**
 + * wps_registrar_invalidate_wildcard_pin - Invalidate a wildcard PIN
 + * @reg: Registrar data from wps_registrar_init()
 + * @dev_pw: PIN to search for or %NULL to match any
 + * @dev_pw_len: Length of dev_pw in octets
 + * Returns: 0 on success, -1 if not wildcard PIN is enabled
 + */
 +static int wps_registrar_invalidate_wildcard_pin(struct wps_registrar *reg,
 +                                               const u8 *dev_pw,
 +                                               size_t dev_pw_len)
 +{
 +      struct wps_uuid_pin *pin, *prev;
 +
 +      dl_list_for_each_safe(pin, prev, &reg->pins, struct wps_uuid_pin, list)
 +      {
 +              if (dev_pw && pin->pin &&
 +                  (dev_pw_len != pin->pin_len ||
 +                   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",
 +                                  pin->uuid, WPS_UUID_LEN);
 +                      wps_registrar_remove_pin(reg, pin);
 +                      return 0;
 +              }
 +      }
 +
 +      return -1;
 +}
 +
 +
 +/**
 + * wps_registrar_invalidate_pin - Invalidate a PIN for a specific UUID-E
 + * @reg: Registrar data from wps_registrar_init()
 + * @uuid: UUID-E
 + * Returns: 0 on success, -1 on failure (e.g., PIN not found)
 + */
 +int wps_registrar_invalidate_pin(struct wps_registrar *reg, const u8 *uuid)
 +{
 +      struct wps_uuid_pin *pin, *prev;
 +
 +      dl_list_for_each_safe(pin, prev, &reg->pins, struct wps_uuid_pin, list)
 +      {
 +              if (os_memcmp(pin->uuid, uuid, WPS_UUID_LEN) == 0) {
 +                      wpa_hexdump(MSG_DEBUG, "WPS: Invalidated PIN for UUID",
 +                                  pin->uuid, WPS_UUID_LEN);
 +                      wps_registrar_remove_pin(reg, pin);
 +                      return 0;
 +              }
 +      }
 +
 +      return -1;
 +}
 +
 +
 +static const u8 * wps_registrar_get_pin(struct wps_registrar *reg,
 +                                      const u8 *uuid, size_t *pin_len)
 +{
 +      struct wps_uuid_pin *pin, *found = NULL;
 +
 +      wps_registrar_expire_pins(reg);
 +
 +      dl_list_for_each(pin, &reg->pins, struct wps_uuid_pin, list) {
 +              if (!pin->wildcard_uuid &&
 +                  os_memcmp(pin->uuid, uuid, WPS_UUID_LEN) == 0) {
 +                      found = pin;
 +                      break;
 +              }
 +      }
 +
 +      if (!found) {
 +              /* Check for wildcard UUIDs since none of the UUID-specific
 +               * PINs matched */
 +              dl_list_for_each(pin, &reg->pins, struct wps_uuid_pin, list) {
 +                      if (pin->wildcard_uuid == 1 ||
 +                          pin->wildcard_uuid == 2) {
 +                              wpa_printf(MSG_DEBUG, "WPS: Found a wildcard "
 +                                         "PIN. Assigned it for this UUID-E");
 +                              pin->wildcard_uuid++;
 +                              os_memcpy(pin->uuid, uuid, WPS_UUID_LEN);
 +                              found = pin;
 +                              break;
 +                      }
 +              }
 +      }
 +
 +      if (!found)
 +              return NULL;
 +
 +      /*
 +       * Lock the PIN to avoid attacks based on concurrent re-use of the PIN
 +       * that could otherwise avoid PIN invalidations.
 +       */
 +      if (found->flags & PIN_LOCKED) {
 +              wpa_printf(MSG_DEBUG, "WPS: Selected PIN locked - do not "
 +                         "allow concurrent re-use");
 +              return NULL;
 +      }
 +      *pin_len = found->pin_len;
 +      found->flags |= PIN_LOCKED;
 +      return found->pin;
 +}
 +
 +
 +/**
 + * wps_registrar_unlock_pin - Unlock a PIN for a specific UUID-E
 + * @reg: Registrar data from wps_registrar_init()
 + * @uuid: UUID-E
 + * Returns: 0 on success, -1 on failure
 + *
 + * PINs are locked to enforce only one concurrent use. This function unlocks a
 + * PIN to allow it to be used again. If the specified PIN was configured using
 + * a wildcard UUID, it will be removed instead of allowing multiple uses.
 + */
 +int wps_registrar_unlock_pin(struct wps_registrar *reg, const u8 *uuid)
 +{
 +      struct wps_uuid_pin *pin;
 +
 +      dl_list_for_each(pin, &reg->pins, struct wps_uuid_pin, list) {
 +              if (os_memcmp(pin->uuid, uuid, WPS_UUID_LEN) == 0) {
 +                      if (pin->wildcard_uuid == 3) {
 +                              wpa_printf(MSG_DEBUG, "WPS: Invalidating used "
 +                                         "wildcard PIN");
 +                              return wps_registrar_invalidate_pin(reg, uuid);
 +                      }
 +                      pin->flags &= ~PIN_LOCKED;
 +                      return 0;
 +              }
 +      }
 +
 +      return -1;
 +}
 +
 +
 +static void wps_registrar_stop_pbc(struct wps_registrar *reg)
 +{
 +      reg->selected_registrar = 0;
 +      reg->pbc = 0;
 +      os_memset(reg->p2p_dev_addr, 0, ETH_ALEN);
 +      wps_registrar_remove_authorized_mac(reg,
 +                                          (u8 *) "\xff\xff\xff\xff\xff\xff");
 +      wps_registrar_selected_registrar_changed(reg, 0);
 +}
 +
 +
 +static void wps_registrar_pbc_timeout(void *eloop_ctx, void *timeout_ctx)
 +{
 +      struct wps_registrar *reg = eloop_ctx;
 +
 +      wpa_printf(MSG_DEBUG, "WPS: PBC timed out - disable PBC mode");
 +      wps_pbc_timeout_event(reg->wps);
 +      wps_registrar_stop_pbc(reg);
 +}
 +
 +
 +/**
 + * wps_registrar_button_pushed - Notify Registrar that AP button was pushed
 + * @reg: Registrar data from wps_registrar_init()
 + * @p2p_dev_addr: Limit allowed PBC devices to the specified P2P device, %NULL
 + *    indicates no such filtering
 + * Returns: 0 on success, -1 on failure, -2 on session overlap
 + *
 + * This function is called on an AP when a push button is pushed to activate
 + * PBC mode. The PBC mode will be stopped after walk time (2 minutes) timeout
 + * or when a PBC registration is completed. If more than one Enrollee in active
 + * PBC mode has been detected during the monitor time (previous 2 minutes), the
 + * PBC mode is not activated and -2 is returned to indicate session overlap.
 + * This is skipped if a specific Enrollee is selected.
 + */
 +int wps_registrar_button_pushed(struct wps_registrar *reg,
 +                              const u8 *p2p_dev_addr)
 +{
 +      if (p2p_dev_addr == NULL &&
 +          wps_registrar_pbc_overlap(reg, NULL, NULL)) {
 +              wpa_printf(MSG_DEBUG, "WPS: PBC overlap - do not start PBC "
 +                         "mode");
 +              wps_pbc_overlap_event(reg->wps);
 +              return -2;
 +      }
 +      wpa_printf(MSG_DEBUG, "WPS: Button pushed - PBC mode started");
 +      reg->force_pbc_overlap = 0;
 +      reg->selected_registrar = 1;
 +      reg->pbc = 1;
 +      if (p2p_dev_addr)
 +              os_memcpy(reg->p2p_dev_addr, p2p_dev_addr, ETH_ALEN);
 +      else
 +              os_memset(reg->p2p_dev_addr, 0, ETH_ALEN);
 +      wps_registrar_add_authorized_mac(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,
 +                             reg, NULL);
 +      return 0;
 +}
 +
 +
 +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);
 +}
 +
 +
 +static void wps_registrar_pin_completed(struct wps_registrar *reg)
 +{
 +      wpa_printf(MSG_DEBUG, "WPS: PIN completed using internal Registrar");
 +      eloop_cancel_timeout(wps_registrar_set_selected_timeout, reg, NULL);
 +      reg->selected_registrar = 0;
 +      wps_registrar_selected_registrar_changed(reg, 0);
 +}
 +
 +
 +void wps_registrar_complete(struct wps_registrar *registrar, const u8 *uuid_e,
 +                          const u8 *dev_pw, size_t dev_pw_len)
 +{
 +      if (registrar->pbc) {
 +              wps_registrar_remove_pbc_session(registrar,
 +                                               uuid_e, NULL);
 +              wps_registrar_pbc_completed(registrar);
 +#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);
 +      }
 +
 +      if (dev_pw &&
 +          wps_registrar_invalidate_wildcard_pin(registrar, dev_pw,
 +                                                dev_pw_len) == 0) {
 +              wpa_hexdump_key(MSG_DEBUG, "WPS: Invalidated wildcard PIN",
 +                              dev_pw, dev_pw_len);
 +      }
 +}
 +
 +
 +int wps_registrar_wps_cancel(struct wps_registrar *reg)
 +{
 +      if (reg->pbc) {
 +              wpa_printf(MSG_DEBUG, "WPS: PBC is set - cancelling it");
 +              wps_registrar_pbc_timeout(reg, NULL);
 +              eloop_cancel_timeout(wps_registrar_pbc_timeout, reg, NULL);
 +              return 1;
 +      } else if (reg->selected_registrar) {
 +              /* PIN Method */
 +              wpa_printf(MSG_DEBUG, "WPS: PIN is set - cancelling it");
 +              wps_registrar_pin_completed(reg);
 +              wps_registrar_invalidate_wildcard_pin(reg, NULL, 0);
 +              return 1;
 +      }
 +      return 0;
 +}
 +
 +
 +/**
 + * wps_registrar_probe_req_rx - Notify Registrar of Probe Request
 + * @reg: Registrar data from wps_registrar_init()
 + * @addr: MAC address of the Probe Request sender
 + * @wps_data: WPS IE contents
 + *
 + * This function is called on an AP when a Probe Request with WPS IE is
 + * received. This is used to track PBC mode use and to detect possible overlap
 + * situation with other WPS APs.
 + */
 +void wps_registrar_probe_req_rx(struct wps_registrar *reg, const u8 *addr,
 +                              const struct wpabuf *wps_data,
 +                              int p2p_wildcard)
 +{
 +      struct wps_parse_attr attr;
 +      int skip_add = 0;
 +
 +      wpa_hexdump_buf(MSG_MSGDUMP,
 +                      "WPS: Probe Request with WPS data received",
 +                      wps_data);
 +
 +      if (wps_parse_msg(wps_data, &attr) < 0)
 +              return;
 +
 +      if (attr.config_methods == NULL) {
 +              wpa_printf(MSG_DEBUG, "WPS: No Config Methods attribute in "
 +                         "Probe Request");
 +              return;
 +      }
 +
 +      if (attr.dev_password_id == NULL) {
 +              wpa_printf(MSG_DEBUG, "WPS: No Device Password Id attribute "
 +                         "in Probe Request");
 +              return;
 +      }
 +
 +      if (reg->enrollee_seen_cb && attr.uuid_e &&
 +          attr.primary_dev_type && attr.request_type && !p2p_wildcard) {
 +              char *dev_name = NULL;
 +              if (attr.dev_name) {
 +                      dev_name = os_zalloc(attr.dev_name_len + 1);
 +                      if (dev_name) {
 +                              os_memcpy(dev_name, attr.dev_name,
 +                                        attr.dev_name_len);
 +                      }
 +              }
 +              reg->enrollee_seen_cb(reg->cb_ctx, addr, attr.uuid_e,
 +                                    attr.primary_dev_type,
 +                                    WPA_GET_BE16(attr.config_methods),
 +                                    WPA_GET_BE16(attr.dev_password_id),
 +                                    *attr.request_type, dev_name);
 +              os_free(dev_name);
 +      }
 +
 +      if (WPA_GET_BE16(attr.dev_password_id) != DEV_PW_PUSHBUTTON)
 +              return; /* Not PBC */
 +
 +      wpa_printf(MSG_DEBUG, "WPS: Probe Request for PBC received from "
 +                 MACSTR, MAC2STR(addr));
 +      if (attr.uuid_e == NULL) {
 +              wpa_printf(MSG_DEBUG, "WPS: Invalid Probe Request WPS IE: No "
 +                         "UUID-E included");
 +              return;
 +      }
 +      wpa_hexdump(MSG_DEBUG, "WPS: UUID-E from Probe Request", attr.uuid_e,
 +                  WPS_UUID_LEN);
 +
 +#ifdef WPS_WORKAROUNDS
 +      if (reg->pbc_ignore_start.sec &&
 +          os_memcmp(attr.uuid_e, reg->pbc_ignore_uuid, WPS_UUID_LEN) == 0) {
 +              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 "
 +                                 "that just completed PBC provisioning");
 +                      skip_add = 1;
 +              } else
 +                      reg->pbc_ignore_start.sec = 0;
 +      }
 +#endif /* WPS_WORKAROUNDS */
 +
 +      if (!skip_add)
 +              wps_registrar_add_pbc_session(reg, addr, attr.uuid_e);
 +      if (wps_registrar_pbc_overlap(reg, addr, attr.uuid_e)) {
 +              wpa_printf(MSG_DEBUG, "WPS: PBC session overlap detected");
 +              reg->force_pbc_overlap = 1;
 +              wps_pbc_overlap_event(reg->wps);
 +      }
 +}
 +
 +
 +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, p2p_dev_addr, psk,
 +                             psk_len);
 +}
 +
 +
 +static void wps_cb_pin_needed(struct wps_registrar *reg, const u8 *uuid_e,
 +                            const struct wps_device_data *dev)
 +{
 +      if (reg->pin_needed_cb == NULL)
 +              return;
 +
 +      reg->pin_needed_cb(reg->cb_ctx, uuid_e, dev);
 +}
 +
 +
 +static void wps_cb_reg_success(struct wps_registrar *reg, const u8 *mac_addr,
 +                             const u8 *uuid_e, const u8 *dev_pw,
 +                             size_t dev_pw_len)
 +{
 +      if (reg->reg_success_cb == NULL)
 +              return;
 +
 +      reg->reg_success_cb(reg->cb_ctx, mac_addr, uuid_e, dev_pw, dev_pw_len);
 +}
 +
 +
 +static int wps_cb_set_ie(struct wps_registrar *reg, struct wpabuf *beacon_ie,
 +                       struct wpabuf *probe_resp_ie)
 +{
 +      return reg->set_ie_cb(reg->cb_ctx, beacon_ie, probe_resp_ie);
 +}
 +
 +
 +static void wps_cb_set_sel_reg(struct wps_registrar *reg)
 +{
 +      u16 methods = 0;
 +      if (reg->set_sel_reg_cb == NULL)
 +              return;
 +
 +      if (reg->selected_registrar) {
 +              methods = reg->wps->config_methods & ~WPS_CONFIG_PUSHBUTTON;
 +              methods &= ~(WPS_CONFIG_VIRT_PUSHBUTTON |
 +                           WPS_CONFIG_PHY_PUSHBUTTON);
 +              if (reg->pbc)
 +                      wps_set_pushbutton(&methods, reg->wps->config_methods);
 +      }
 +
 +      wpa_printf(MSG_DEBUG, "WPS: wps_cb_set_sel_reg: sel_reg=%d "
 +                 "config_methods=0x%x pbc=%d methods=0x%x",
 +                 reg->selected_registrar, reg->wps->config_methods,
 +                 reg->pbc, methods);
 +
 +      reg->set_sel_reg_cb(reg->cb_ctx, reg->selected_registrar,
 +                          reg->pbc ? DEV_PW_PUSHBUTTON : DEV_PW_DEFAULT,
 +                          methods);
 +}
 +
 +
 +static int wps_set_ie(struct wps_registrar *reg)
 +{
 +      struct wpabuf *beacon;
 +      struct wpabuf *probe;
 +      const u8 *auth_macs;
 +      size_t count;
 +      size_t vendor_len = 0;
 +      int i;
 +
 +      if (reg->set_ie_cb == NULL)
 +              return 0;
 +
 +      for (i = 0; i < MAX_WPS_VENDOR_EXTENSIONS; i++) {
 +              if (reg->wps->dev.vendor_ext[i]) {
 +                      vendor_len += 2 + 2;
 +                      vendor_len += wpabuf_len(reg->wps->dev.vendor_ext[i]);
 +              }
 +      }
 +
 +      beacon = wpabuf_alloc(400 + vendor_len);
 +      if (beacon == NULL)
 +              return -1;
 +      probe = wpabuf_alloc(500 + vendor_len);
 +      if (probe == NULL) {
 +              wpabuf_free(beacon);
 +              return -1;
 +      }
 +
 +      auth_macs = wps_authorized_macs(reg, &count);
 +
 +      wpa_printf(MSG_DEBUG, "WPS: Build Beacon IEs");
 +
 +      if (wps_build_version(beacon) ||
 +          wps_build_wps_state(reg->wps, beacon) ||
 +          wps_build_ap_setup_locked(reg->wps, beacon) ||
 +          wps_build_selected_registrar(reg, beacon) ||
 +          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, 0)) ||
 +          wps_build_wfa_ext(beacon, 0, auth_macs, count) ||
 +          wps_build_vendor_ext(&reg->wps->dev, beacon)) {
 +              wpabuf_free(beacon);
 +              wpabuf_free(probe);
 +              return -1;
 +      }
 +
 +#ifdef CONFIG_P2P
 +      if (wps_build_dev_name(&reg->wps->dev, beacon) ||
 +          wps_build_primary_dev_type(&reg->wps->dev, beacon)) {
 +              wpabuf_free(beacon);
 +              wpabuf_free(probe);
 +              return -1;
 +      }
 +#endif /* CONFIG_P2P */
 +
 +      wpa_printf(MSG_DEBUG, "WPS: Build Probe Response IEs");
 +
 +      if (wps_build_version(probe) ||
 +          wps_build_wps_state(reg->wps, probe) ||
 +          wps_build_ap_setup_locked(reg->wps, probe) ||
 +          wps_build_selected_registrar(reg, probe) ||
 +          wps_build_sel_reg_dev_password_id(reg, probe) ||
 +          wps_build_sel_reg_config_methods(reg, probe) ||
 +          wps_build_resp_type(probe, reg->wps->ap ? WPS_RESP_AP :
 +                              WPS_RESP_REGISTRAR) ||
 +          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, 0)) ||
 +          wps_build_wfa_ext(probe, 0, auth_macs, count) ||
 +          wps_build_vendor_ext(&reg->wps->dev, probe)) {
 +              wpabuf_free(beacon);
 +              wpabuf_free(probe);
 +              return -1;
 +      }
 +
 +      beacon = wps_ie_encapsulate(beacon);
 +      probe = wps_ie_encapsulate(probe);
 +
 +      if (!beacon || !probe) {
 +              wpabuf_free(beacon);
 +              wpabuf_free(probe);
 +              return -1;
 +      }
 +
 +      if (reg->static_wep_only) {
 +              /*
 +               * Windows XP and Vista clients can get confused about
 +               * EAP-Identity/Request when they probe the network with
 +               * EAPOL-Start. In such a case, they may assume the network is
 +               * using IEEE 802.1X and prompt user for a certificate while
 +               * the correct (non-WPS) behavior would be to ask for the
 +               * static WEP key. As a workaround, use Microsoft Provisioning
 +               * IE to advertise that legacy 802.1X is not supported.
 +               */
 +              const u8 ms_wps[7] = {
 +                      WLAN_EID_VENDOR_SPECIFIC, 5,
 +                      /* Microsoft Provisioning IE (00:50:f2:5) */
 +                      0x00, 0x50, 0xf2, 5,
 +                      0x00 /* no legacy 802.1X or MS WPS */
 +              };
 +              wpa_printf(MSG_DEBUG, "WPS: Add Microsoft Provisioning IE "
 +                         "into Beacon/Probe Response frames");
 +              wpabuf_put_data(beacon, ms_wps, sizeof(ms_wps));
 +              wpabuf_put_data(probe, ms_wps, sizeof(ms_wps));
 +      }
 +
 +      return wps_cb_set_ie(reg, beacon, probe);
 +}
 +
 +
 +static int wps_get_dev_password(struct wps_data *wps)
 +{
 +      const u8 *pin;
 +      size_t pin_len = 0;
 +
 +      bin_clear_free(wps->dev_password, wps->dev_password_len);
 +      wps->dev_password = NULL;
 +
 +      if (wps->pbc) {
 +              wpa_printf(MSG_DEBUG, "WPS: Use default PIN for PBC");
 +              pin = (const u8 *) "00000000";
 +              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,
 +                                          &pin_len);
 +              if (pin && wps->dev_pw_id >= 0x10) {
 +                      wpa_printf(MSG_DEBUG, "WPS: No match for OOB Device "
 +                                 "Password ID, but PIN found");
 +                      /*
 +                       * See whether Enrollee is willing to use PIN instead.
 +                       */
 +                      wps->dev_pw_id = DEV_PW_DEFAULT;
 +              }
 +      }
 +      if (pin == NULL) {
 +              wpa_printf(MSG_DEBUG, "WPS: No Device Password available for "
 +                         "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;
 +      }
 +
 +      wps->dev_password = os_malloc(pin_len);
 +      if (wps->dev_password == NULL)
 +              return -1;
 +      os_memcpy(wps->dev_password, pin, pin_len);
 +      wps->dev_password_len = pin_len;
 +
 +      return 0;
 +}
 +
 +
 +static int wps_build_uuid_r(struct wps_data *wps, struct wpabuf *msg)
 +{
 +      wpa_printf(MSG_DEBUG, "WPS:  * UUID-R");
 +      wpabuf_put_be16(msg, ATTR_UUID_R);
 +      wpabuf_put_be16(msg, WPS_UUID_LEN);
 +      wpabuf_put_data(msg, wps->uuid_r, WPS_UUID_LEN);
 +      return 0;
 +}
 +
 +
 +static int wps_build_r_hash(struct wps_data *wps, struct wpabuf *msg)
 +{
 +      u8 *hash;
 +      const u8 *addr[4];
 +      size_t len[4];
 +
 +      if (random_get_bytes(wps->snonce, 2 * WPS_SECRET_NONCE_LEN) < 0)
 +              return -1;
 +      wpa_hexdump(MSG_DEBUG, "WPS: R-S1", wps->snonce, WPS_SECRET_NONCE_LEN);
 +      wpa_hexdump(MSG_DEBUG, "WPS: R-S2",
 +                  wps->snonce + WPS_SECRET_NONCE_LEN, WPS_SECRET_NONCE_LEN);
 +
 +      if (wps->dh_pubkey_e == NULL || wps->dh_pubkey_r == NULL) {
 +              wpa_printf(MSG_DEBUG, "WPS: DH public keys not available for "
 +                         "R-Hash derivation");
 +              return -1;
 +      }
 +
 +      wpa_printf(MSG_DEBUG, "WPS:  * R-Hash1");
 +      wpabuf_put_be16(msg, ATTR_R_HASH1);
 +      wpabuf_put_be16(msg, SHA256_MAC_LEN);
 +      hash = wpabuf_put(msg, SHA256_MAC_LEN);
 +      /* R-Hash1 = HMAC_AuthKey(R-S1 || PSK1 || PK_E || PK_R) */
 +      addr[0] = wps->snonce;
 +      len[0] = WPS_SECRET_NONCE_LEN;
 +      addr[1] = wps->psk1;
 +      len[1] = WPS_PSK_LEN;
 +      addr[2] = wpabuf_head(wps->dh_pubkey_e);
 +      len[2] = wpabuf_len(wps->dh_pubkey_e);
 +      addr[3] = wpabuf_head(wps->dh_pubkey_r);
 +      len[3] = wpabuf_len(wps->dh_pubkey_r);
 +      hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash);
 +      wpa_hexdump(MSG_DEBUG, "WPS: R-Hash1", hash, SHA256_MAC_LEN);
 +
 +      wpa_printf(MSG_DEBUG, "WPS:  * R-Hash2");
 +      wpabuf_put_be16(msg, ATTR_R_HASH2);
 +      wpabuf_put_be16(msg, SHA256_MAC_LEN);
 +      hash = wpabuf_put(msg, SHA256_MAC_LEN);
 +      /* R-Hash2 = HMAC_AuthKey(R-S2 || PSK2 || PK_E || PK_R) */
 +      addr[0] = wps->snonce + WPS_SECRET_NONCE_LEN;
 +      addr[1] = wps->psk2;
 +      hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash);
 +      wpa_hexdump(MSG_DEBUG, "WPS: R-Hash2", hash, SHA256_MAC_LEN);
 +
 +      return 0;
 +}
 +
 +
 +static int wps_build_r_snonce1(struct wps_data *wps, struct wpabuf *msg)
 +{
 +      wpa_printf(MSG_DEBUG, "WPS:  * R-SNonce1");
 +      wpabuf_put_be16(msg, ATTR_R_SNONCE1);
 +      wpabuf_put_be16(msg, WPS_SECRET_NONCE_LEN);
 +      wpabuf_put_data(msg, wps->snonce, WPS_SECRET_NONCE_LEN);
 +      return 0;
 +}
 +
 +
 +static int wps_build_r_snonce2(struct wps_data *wps, struct wpabuf *msg)
 +{
 +      wpa_printf(MSG_DEBUG, "WPS:  * R-SNonce2");
 +      wpabuf_put_be16(msg, ATTR_R_SNONCE2);
 +      wpabuf_put_be16(msg, WPS_SECRET_NONCE_LEN);
 +      wpabuf_put_data(msg, wps->snonce + WPS_SECRET_NONCE_LEN,
 +                      WPS_SECRET_NONCE_LEN);
 +      return 0;
 +}
 +
 +
 +static int wps_build_cred_network_idx(struct wpabuf *msg,
 +                                    const struct wps_credential *cred)
 +{
 +      wpa_printf(MSG_DEBUG, "WPS:  * Network Index (1)");
 +      wpabuf_put_be16(msg, ATTR_NETWORK_INDEX);
 +      wpabuf_put_be16(msg, 1);
 +      wpabuf_put_u8(msg, 1);
 +      return 0;
 +}
 +
 +
 +static int wps_build_cred_ssid(struct wpabuf *msg,
 +                             const struct wps_credential *cred)
 +{
 +      wpa_printf(MSG_DEBUG, "WPS:  * SSID");
 +      wpa_hexdump_ascii(MSG_DEBUG, "WPS: SSID for Credential",
 +                        cred->ssid, cred->ssid_len);
 +      wpabuf_put_be16(msg, ATTR_SSID);
 +      wpabuf_put_be16(msg, cred->ssid_len);
 +      wpabuf_put_data(msg, cred->ssid, cred->ssid_len);
 +      return 0;
 +}
 +
 +
 +static int wps_build_cred_auth_type(struct wpabuf *msg,
 +                                  const struct wps_credential *cred)
 +{
 +      wpa_printf(MSG_DEBUG, "WPS:  * Authentication Type (0x%x)",
 +                 cred->auth_type);
 +      wpabuf_put_be16(msg, ATTR_AUTH_TYPE);
 +      wpabuf_put_be16(msg, 2);
 +      wpabuf_put_be16(msg, cred->auth_type);
 +      return 0;
 +}
 +
 +
 +static int wps_build_cred_encr_type(struct wpabuf *msg,
 +                                  const struct wps_credential *cred)
 +{
 +      wpa_printf(MSG_DEBUG, "WPS:  * Encryption Type (0x%x)",
 +                 cred->encr_type);
 +      wpabuf_put_be16(msg, ATTR_ENCR_TYPE);
 +      wpabuf_put_be16(msg, 2);
 +      wpabuf_put_be16(msg, cred->encr_type);
 +      return 0;
 +}
 +
 +
 +static int wps_build_cred_network_key(struct wpabuf *msg,
 +                                    const struct wps_credential *cred)
 +{
 +      wpa_printf(MSG_DEBUG, "WPS:  * Network Key (len=%d)",
 +                 (int) cred->key_len);
 +      wpa_hexdump_key(MSG_DEBUG, "WPS: Network Key",
 +                      cred->key, cred->key_len);
 +      wpabuf_put_be16(msg, ATTR_NETWORK_KEY);
 +      wpabuf_put_be16(msg, cred->key_len);
 +      wpabuf_put_data(msg, cred->key, cred->key_len);
 +      return 0;
 +}
 +
 +
 +static int wps_build_credential(struct wpabuf *msg,
 +                              const struct wps_credential *cred)
 +{
 +      if (wps_build_cred_network_idx(msg, cred) ||
 +          wps_build_cred_ssid(msg, cred) ||
 +          wps_build_cred_auth_type(msg, cred) ||
 +          wps_build_cred_encr_type(msg, cred) ||
 +          wps_build_cred_network_key(msg, cred) ||
 +          wps_build_mac_addr(msg, cred->mac_addr))
 +              return -1;
 +      return 0;
 +}
 +
 +
 +int wps_build_credential_wrap(struct wpabuf *msg,
 +                            const struct wps_credential *cred)
 +{
 +      struct wpabuf *wbuf;
 +      wbuf = wpabuf_alloc(200);
 +      if (wbuf == NULL)
 +              return -1;
 +      if (wps_build_credential(wbuf, cred)) {
 +              wpabuf_free(wbuf);
 +              return -1;
 +      }
 +      wpabuf_put_be16(msg, ATTR_CRED);
 +      wpabuf_put_be16(msg, wpabuf_len(wbuf));
 +      wpabuf_put_buf(msg, wbuf);
 +      wpabuf_free(wbuf);
 +      return 0;
 +}
 +
 +
 +int wps_build_cred(struct wps_data *wps, struct wpabuf *msg)
 +{
 +      struct wpabuf *cred;
 +
 +      if (wps->wps->registrar->skip_cred_build)
 +              goto skip_cred_build;
 +
 +      wpa_printf(MSG_DEBUG, "WPS:  * Credential");
 +      if (wps->use_cred) {
 +              os_memcpy(&wps->cred, wps->use_cred, sizeof(wps->cred));
 +              goto use_provided;
 +      }
 +      os_memset(&wps->cred, 0, sizeof(wps->cred));
 +
 +      os_memcpy(wps->cred.ssid, wps->wps->ssid, wps->wps->ssid_len);
 +      wps->cred.ssid_len = wps->wps->ssid_len;
 +
 +      /* Select the best authentication and encryption type */
 +      if (wps->auth_type & WPS_AUTH_WPA2PSK)
 +              wps->auth_type = WPS_AUTH_WPA2PSK;
 +      else if (wps->auth_type & WPS_AUTH_WPAPSK)
 +              wps->auth_type = WPS_AUTH_WPAPSK;
 +      else if (wps->auth_type & WPS_AUTH_OPEN)
 +              wps->auth_type = WPS_AUTH_OPEN;
 +      else {
 +              wpa_printf(MSG_DEBUG, "WPS: Unsupported auth_type 0x%x",
 +                         wps->auth_type);
 +              return -1;
 +      }
 +      wps->cred.auth_type = wps->auth_type;
 +
 +      if (wps->auth_type == WPS_AUTH_WPA2PSK ||
 +          wps->auth_type == WPS_AUTH_WPAPSK) {
 +              if (wps->encr_type & WPS_ENCR_AES)
 +                      wps->encr_type = WPS_ENCR_AES;
 +              else if (wps->encr_type & WPS_ENCR_TKIP)
 +                      wps->encr_type = WPS_ENCR_TKIP;
 +              else {
 +                      wpa_printf(MSG_DEBUG, "WPS: No suitable encryption "
 +                                 "type for WPA/WPA2");
 +                      return -1;
 +              }
 +      } else {
 +              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");
 +                      return -1;
 +              }
 +      }
 +      wps->cred.encr_type = wps->encr_type;
 +      /*
 +       * Set MAC address in the Credential to be the Enrollee's MAC address
 +       */
 +      os_memcpy(wps->cred.mac_addr, wps->mac_addr_e, ETH_ALEN);
 +
 +      if (wps->wps->wps_state == WPS_STATE_NOT_CONFIGURED && wps->wps->ap &&
 +          !wps->wps->registrar->disable_auto_conf) {
 +              u8 r[16];
 +              /* Generate a random passphrase */
 +              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)
 +                      return -1;
 +              wps->new_psk_len--; /* remove newline */
 +              while (wps->new_psk_len &&
 +                     wps->new_psk[wps->new_psk_len - 1] == '=')
 +                      wps->new_psk_len--;
 +              wpa_hexdump_ascii_key(MSG_DEBUG, "WPS: Generated passphrase",
 +                                    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->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->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;
 +      } else if (wps->auth_type & (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK)) {
 +              char hex[65];
 +              /* Generate a random per-device PSK */
 +              os_free(wps->new_psk);
 +              wps->new_psk_len = 32;
 +              wps->new_psk = os_malloc(wps->new_psk_len);
 +              if (wps->new_psk == NULL)
 +                      return -1;
 +              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;
 +              }
 +              wpa_hexdump_key(MSG_DEBUG, "WPS: Generated per-device PSK",
 +                              wps->new_psk, wps->new_psk_len);
 +              wpa_snprintf_hex(hex, sizeof(hex), wps->new_psk,
 +                               wps->new_psk_len);
 +              os_memcpy(wps->cred.key, hex, wps->new_psk_len * 2);
 +              wps->cred.key_len = wps->new_psk_len * 2;
 +      }
 +
 +use_provided:
 +#ifdef CONFIG_WPS_TESTING
 +      if (wps_testing_dummy_cred)
 +              cred = wpabuf_alloc(200);
 +      else
 +              cred = NULL;
 +      if (cred) {
 +              struct wps_credential dummy;
 +              wpa_printf(MSG_DEBUG, "WPS: Add dummy credential");
 +              os_memset(&dummy, 0, sizeof(dummy));
 +              os_memcpy(dummy.ssid, "dummy", 5);
 +              dummy.ssid_len = 5;
 +              dummy.auth_type = WPS_AUTH_WPA2PSK;
 +              dummy.encr_type = WPS_ENCR_AES;
 +              os_memcpy(dummy.key, "dummy psk", 9);
 +              dummy.key_len = 9;
 +              os_memcpy(dummy.mac_addr, wps->mac_addr_e, ETH_ALEN);
 +              wps_build_credential(cred, &dummy);
 +              wpa_hexdump_buf(MSG_DEBUG, "WPS: Dummy Credential", cred);
 +
 +              wpabuf_put_be16(msg, ATTR_CRED);
 +              wpabuf_put_be16(msg, wpabuf_len(cred));
 +              wpabuf_put_buf(msg, cred);
 +
 +              wpabuf_free(cred);
 +      }
 +#endif /* CONFIG_WPS_TESTING */
 +
 +      cred = wpabuf_alloc(200);
 +      if (cred == NULL)
 +              return -1;
 +
 +      if (wps_build_credential(cred, &wps->cred)) {
 +              wpabuf_free(cred);
 +              return -1;
 +      }
 +
 +      wpabuf_put_be16(msg, ATTR_CRED);
 +      wpabuf_put_be16(msg, wpabuf_len(cred));
 +      wpabuf_put_buf(msg, cred);
 +      wpabuf_free(cred);
 +
 +skip_cred_build:
 +      if (wps->wps->registrar->extra_cred) {
 +              wpa_printf(MSG_DEBUG, "WPS:  * Credential (pre-configured)");
 +              wpabuf_put_buf(msg, wps->wps->registrar->extra_cred);
 +      }
 +
 +      return 0;
 +}
 +
 +
 +static int wps_build_ap_settings(struct wps_data *wps, struct wpabuf *msg)
 +{
 +      wpa_printf(MSG_DEBUG, "WPS:  * AP Settings");
 +
 +      if (wps_build_credential(msg, &wps->cred))
 +              return -1;
 +
 +      return 0;
 +}
 +
 +
 +static struct wpabuf * wps_build_ap_cred(struct wps_data *wps)
 +{
 +      struct wpabuf *msg, *plain;
 +
 +      msg = wpabuf_alloc(1000);
 +      if (msg == NULL)
 +              return NULL;
 +
 +      plain = wpabuf_alloc(200);
 +      if (plain == NULL) {
 +              wpabuf_free(msg);
 +              return NULL;
 +      }
 +
 +      if (wps_build_ap_settings(wps, plain)) {
 +              wpabuf_free(plain);
 +              wpabuf_free(msg);
 +              return NULL;
 +      }
 +
 +      wpabuf_put_be16(msg, ATTR_CRED);
 +      wpabuf_put_be16(msg, wpabuf_len(plain));
 +      wpabuf_put_buf(msg, plain);
 +      wpabuf_free(plain);
 +
 +      return msg;
 +}
 +
 +
 +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;
 +      wpa_hexdump(MSG_DEBUG, "WPS: Registrar Nonce",
 +                  wps->nonce_r, WPS_NONCE_LEN);
 +      wpa_hexdump(MSG_DEBUG, "WPS: UUID-R", wps->uuid_r, WPS_UUID_LEN);
 +
 +      wpa_printf(MSG_DEBUG, "WPS: Building Message M2");
 +      msg = wpabuf_alloc(1000);
 +      if (msg == NULL)
 +              return NULL;
 +
 +      if (wps_build_version(msg) ||
 +          wps_build_msg_type(msg, WPS_M2) ||
 +          wps_build_enrollee_nonce(wps, msg) ||
 +          wps_build_registrar_nonce(wps, msg) ||
 +          wps_build_uuid_r(wps, msg) ||
 +          wps_build_public_key(wps, msg) ||
 +          wps_derive_keys(wps) ||
 +          wps_build_auth_type_flags(wps, msg) ||
 +          wps_build_encr_type_flags(wps, msg) ||
 +          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->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)) {
 +              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 = config_in_m2 ? RECV_DONE : RECV_M3;
 +      return msg;
 +}
 +
 +
 +static struct wpabuf * wps_build_m2d(struct wps_data *wps)
 +{
 +      struct wpabuf *msg;
 +      u16 err = wps->config_error;
 +
 +      wpa_printf(MSG_DEBUG, "WPS: Building Message M2D");
 +      msg = wpabuf_alloc(1000);
 +      if (msg == NULL)
 +              return NULL;
 +
 +      if (wps->wps->ap && wps->wps->ap_setup_locked &&
 +          err == WPS_CFG_NO_ERROR)
 +              err = WPS_CFG_SETUP_LOCKED;
 +
 +      if (wps_build_version(msg) ||
 +          wps_build_msg_type(msg, WPS_M2D) ||
 +          wps_build_enrollee_nonce(wps, msg) ||
 +          wps_build_registrar_nonce(wps, msg) ||
 +          wps_build_uuid_r(wps, msg) ||
 +          wps_build_auth_type_flags(wps, msg) ||
 +          wps_build_encr_type_flags(wps, msg) ||
 +          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->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) ||
 +          wps_build_wfa_ext(msg, 0, NULL, 0)) {
 +              wpabuf_free(msg);
 +              return NULL;
 +      }
 +
 +      wps->state = RECV_M2D_ACK;
 +      return msg;
 +}
 +
 +
 +static struct wpabuf * wps_build_m4(struct wps_data *wps)
 +{
 +      struct wpabuf *msg, *plain;
 +
 +      wpa_printf(MSG_DEBUG, "WPS: Building Message M4");
 +
 +      wps_derive_psk(wps, wps->dev_password, wps->dev_password_len);
 +
 +      plain = wpabuf_alloc(200);
 +      if (plain == NULL)
 +              return NULL;
 +
 +      msg = wpabuf_alloc(1000);
 +      if (msg == NULL) {
 +              wpabuf_free(plain);
 +              return NULL;
 +      }
 +
 +      if (wps_build_version(msg) ||
 +          wps_build_msg_type(msg, WPS_M4) ||
 +          wps_build_enrollee_nonce(wps, msg) ||
 +          wps_build_r_hash(wps, msg) ||
 +          wps_build_r_snonce1(wps, plain) ||
 +          wps_build_key_wrap_auth(wps, plain) ||
 +          wps_build_encr_settings(wps, msg, plain) ||
 +          wps_build_wfa_ext(msg, 0, NULL, 0) ||
 +          wps_build_authenticator(wps, msg)) {
 +              wpabuf_free(plain);
 +              wpabuf_free(msg);
 +              return NULL;
 +      }
 +      wpabuf_free(plain);
 +
 +      wps->state = RECV_M5;
 +      return msg;
 +}
 +
 +
 +static struct wpabuf * wps_build_m6(struct wps_data *wps)
 +{
 +      struct wpabuf *msg, *plain;
 +
 +      wpa_printf(MSG_DEBUG, "WPS: Building Message M6");
 +
 +      plain = wpabuf_alloc(200);
 +      if (plain == NULL)
 +              return NULL;
 +
 +      msg = wpabuf_alloc(1000);
 +      if (msg == NULL) {
 +              wpabuf_free(plain);
 +              return NULL;
 +      }
 +
 +      if (wps_build_version(msg) ||
 +          wps_build_msg_type(msg, WPS_M6) ||
 +          wps_build_enrollee_nonce(wps, msg) ||
 +          wps_build_r_snonce2(wps, plain) ||
 +          wps_build_key_wrap_auth(wps, plain) ||
 +          wps_build_encr_settings(wps, msg, plain) ||
 +          wps_build_wfa_ext(msg, 0, NULL, 0) ||
 +          wps_build_authenticator(wps, msg)) {
 +              wpabuf_free(plain);
 +              wpabuf_free(msg);
 +              return NULL;
 +      }
 +      wpabuf_free(plain);
 +
 +      wps->wps_pin_revealed = 1;
 +      wps->state = RECV_M7;
 +      return msg;
 +}
 +
 +
 +static struct wpabuf * wps_build_m8(struct wps_data *wps)
 +{
 +      struct wpabuf *msg, *plain;
 +
 +      wpa_printf(MSG_DEBUG, "WPS: Building Message M8");
 +
 +      plain = wpabuf_alloc(500);
 +      if (plain == NULL)
 +              return NULL;
 +
 +      msg = wpabuf_alloc(1000);
 +      if (msg == NULL) {
 +              wpabuf_free(plain);
 +              return NULL;
 +      }
 +
 +      if (wps_build_version(msg) ||
 +          wps_build_msg_type(msg, WPS_M8) ||
 +          wps_build_enrollee_nonce(wps, msg) ||
 +          ((wps->wps->ap || wps->er) && wps_build_cred(wps, plain)) ||
 +          (!wps->wps->ap && !wps->er && wps_build_ap_settings(wps, plain)) ||
 +          wps_build_key_wrap_auth(wps, plain) ||
 +          wps_build_encr_settings(wps, msg, plain) ||
 +          wps_build_wfa_ext(msg, 0, NULL, 0) ||
 +          wps_build_authenticator(wps, msg)) {
 +              wpabuf_free(plain);
 +              wpabuf_free(msg);
 +              return NULL;
 +      }
 +      wpabuf_free(plain);
 +
 +      wps->state = RECV_DONE;
 +      return msg;
 +}
 +
 +
 +struct wpabuf * wps_registrar_get_msg(struct wps_data *wps,
 +                                    enum wsc_op_code *op_code)
 +{
 +      struct wpabuf *msg;
 +
 +#ifdef CONFIG_WPS_UPNP
 +      if (!wps->int_reg && wps->wps->wps_upnp) {
 +              struct upnp_pending_message *p, *prev = NULL;
 +              if (wps->ext_reg > 1)
 +                      wps_registrar_free_pending_m2(wps->wps);
 +              p = wps->wps->upnp_msgs;
 +              /* TODO: check pending message MAC address */
 +              while (p && p->next) {
 +                      prev = p;
 +                      p = p->next;
 +              }
 +              if (p) {
 +                      wpa_printf(MSG_DEBUG, "WPS: Use pending message from "
 +                                 "UPnP");
 +                      if (prev)
 +                              prev->next = NULL;
 +                      else
 +                              wps->wps->upnp_msgs = NULL;
 +                      msg = p->msg;
 +                      switch (p->type) {
 +                      case WPS_WSC_ACK:
 +                              *op_code = WSC_ACK;
 +                              break;
 +                      case WPS_WSC_NACK:
 +                              *op_code = WSC_NACK;
 +                              break;
 +                      default:
 +                              *op_code = WSC_MSG;
 +                              break;
 +                      }
 +                      os_free(p);
 +                      if (wps->ext_reg == 0)
 +                              wps->ext_reg = 1;
 +                      return msg;
 +              }
 +      }
 +      if (wps->ext_reg) {
 +              wpa_printf(MSG_DEBUG, "WPS: Using external Registrar, but no "
 +                         "pending message available");
 +              return NULL;
 +      }
 +#endif /* CONFIG_WPS_UPNP */
 +
 +      switch (wps->state) {
 +      case SEND_M2:
 +              if (wps_get_dev_password(wps) < 0)
 +                      msg = wps_build_m2d(wps);
 +              else
 +                      msg = wps_build_m2(wps);
 +              *op_code = WSC_MSG;
 +              break;
 +      case SEND_M2D:
 +              msg = wps_build_m2d(wps);
 +              *op_code = WSC_MSG;
 +              break;
 +      case SEND_M4:
 +              msg = wps_build_m4(wps);
 +              *op_code = WSC_MSG;
 +              break;
 +      case SEND_M6:
 +              msg = wps_build_m6(wps);
 +              *op_code = WSC_MSG;
 +              break;
 +      case SEND_M8:
 +              msg = wps_build_m8(wps);
 +              *op_code = WSC_MSG;
 +              break;
 +      case RECV_DONE:
 +              msg = wps_build_wsc_ack(wps);
 +              *op_code = WSC_ACK;
 +              break;
 +      case SEND_WSC_NACK:
 +              msg = wps_build_wsc_nack(wps);
 +              *op_code = WSC_NACK;
 +              break;
 +      default:
 +              wpa_printf(MSG_DEBUG, "WPS: Unsupported state %d for building "
 +                         "a message", wps->state);
 +              msg = NULL;
 +              break;
 +      }
 +
 +      if (*op_code == WSC_MSG && msg) {
 +              /* Save a copy of the last message for Authenticator derivation
 +               */
 +              wpabuf_free(wps->last_msg);
 +              wps->last_msg = wpabuf_dup(msg);
 +      }
 +
 +      return msg;
 +}
 +
 +
 +static int wps_process_enrollee_nonce(struct wps_data *wps, const u8 *e_nonce)
 +{
 +      if (e_nonce == NULL) {
 +              wpa_printf(MSG_DEBUG, "WPS: No Enrollee Nonce received");
 +              return -1;
 +      }
 +
 +      os_memcpy(wps->nonce_e, e_nonce, WPS_NONCE_LEN);
 +      wpa_hexdump(MSG_DEBUG, "WPS: Enrollee Nonce",
 +                  wps->nonce_e, WPS_NONCE_LEN);
 +
 +      return 0;
 +}
 +
 +
 +static int wps_process_registrar_nonce(struct wps_data *wps, const u8 *r_nonce)
 +{
 +      if (r_nonce == NULL) {
 +              wpa_printf(MSG_DEBUG, "WPS: No Registrar Nonce received");
 +              return -1;
 +      }
 +
 +      if (os_memcmp(wps->nonce_r, r_nonce, WPS_NONCE_LEN) != 0) {
 +              wpa_printf(MSG_DEBUG, "WPS: Invalid Registrar Nonce received");
 +              return -1;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +static int wps_process_uuid_e(struct wps_data *wps, const u8 *uuid_e)
 +{
 +      if (uuid_e == NULL) {
 +              wpa_printf(MSG_DEBUG, "WPS: No UUID-E received");
 +              return -1;
 +      }
 +
 +      os_memcpy(wps->uuid_e, uuid_e, WPS_UUID_LEN);
 +      wpa_hexdump(MSG_DEBUG, "WPS: UUID-E", wps->uuid_e, WPS_UUID_LEN);
 +
 +      return 0;
 +}
 +
 +
 +static int wps_process_dev_password_id(struct wps_data *wps, const u8 *pw_id)
 +{
 +      if (pw_id == NULL) {
 +              wpa_printf(MSG_DEBUG, "WPS: No Device Password ID received");
 +              return -1;
 +      }
 +
 +      wps->dev_pw_id = WPA_GET_BE16(pw_id);
 +      wpa_printf(MSG_DEBUG, "WPS: Device Password ID %d", wps->dev_pw_id);
 +
 +      return 0;
 +}
 +
 +
 +static int wps_process_e_hash1(struct wps_data *wps, const u8 *e_hash1)
 +{
 +      if (e_hash1 == NULL) {
 +              wpa_printf(MSG_DEBUG, "WPS: No E-Hash1 received");
 +              return -1;
 +      }
 +
 +      os_memcpy(wps->peer_hash1, e_hash1, WPS_HASH_LEN);
 +      wpa_hexdump(MSG_DEBUG, "WPS: E-Hash1", wps->peer_hash1, WPS_HASH_LEN);
 +
 +      return 0;
 +}
 +
 +
 +static int wps_process_e_hash2(struct wps_data *wps, const u8 *e_hash2)
 +{
 +      if (e_hash2 == NULL) {
 +              wpa_printf(MSG_DEBUG, "WPS: No E-Hash2 received");
 +              return -1;
 +      }
 +
 +      os_memcpy(wps->peer_hash2, e_hash2, WPS_HASH_LEN);
 +      wpa_hexdump(MSG_DEBUG, "WPS: E-Hash2", wps->peer_hash2, WPS_HASH_LEN);
 +
 +      return 0;
 +}
 +
 +
 +static int wps_process_e_snonce1(struct wps_data *wps, const u8 *e_snonce1)
 +{
 +      u8 hash[SHA256_MAC_LEN];
 +      const u8 *addr[4];
 +      size_t len[4];
 +
 +      if (e_snonce1 == NULL) {
 +              wpa_printf(MSG_DEBUG, "WPS: No E-SNonce1 received");
 +              return -1;
 +      }
 +
 +      wpa_hexdump_key(MSG_DEBUG, "WPS: E-SNonce1", e_snonce1,
 +                      WPS_SECRET_NONCE_LEN);
 +
 +      /* E-Hash1 = HMAC_AuthKey(E-S1 || PSK1 || PK_E || PK_R) */
 +      addr[0] = e_snonce1;
 +      len[0] = WPS_SECRET_NONCE_LEN;
 +      addr[1] = wps->psk1;
 +      len[1] = WPS_PSK_LEN;
 +      addr[2] = wpabuf_head(wps->dh_pubkey_e);
 +      len[2] = wpabuf_len(wps->dh_pubkey_e);
 +      addr[3] = wpabuf_head(wps->dh_pubkey_r);
 +      len[3] = wpabuf_len(wps->dh_pubkey_r);
 +      hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash);
 +
 +      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->mac_addr_e);
 +              return -1;
 +      }
 +
 +      wpa_printf(MSG_DEBUG, "WPS: Enrollee proved knowledge of the first "
 +                 "half of the device password");
 +
 +      return 0;
 +}
 +
 +
 +static int wps_process_e_snonce2(struct wps_data *wps, const u8 *e_snonce2)
 +{
 +      u8 hash[SHA256_MAC_LEN];
 +      const u8 *addr[4];
 +      size_t len[4];
 +
 +      if (e_snonce2 == NULL) {
 +              wpa_printf(MSG_DEBUG, "WPS: No E-SNonce2 received");
 +              return -1;
 +      }
 +
 +      wpa_hexdump_key(MSG_DEBUG, "WPS: E-SNonce2", e_snonce2,
 +                      WPS_SECRET_NONCE_LEN);
 +
 +      /* E-Hash2 = HMAC_AuthKey(E-S2 || PSK2 || PK_E || PK_R) */
 +      addr[0] = e_snonce2;
 +      len[0] = WPS_SECRET_NONCE_LEN;
 +      addr[1] = wps->psk2;
 +      len[1] = WPS_PSK_LEN;
 +      addr[2] = wpabuf_head(wps->dh_pubkey_e);
 +      len[2] = wpabuf_len(wps->dh_pubkey_e);
 +      addr[3] = wpabuf_head(wps->dh_pubkey_r);
 +      len[3] = wpabuf_len(wps->dh_pubkey_r);
 +      hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash);
 +
 +      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->mac_addr_e);
 +              return -1;
 +      }
 +
 +      wpa_printf(MSG_DEBUG, "WPS: Enrollee proved knowledge of the second "
 +                 "half of the device password");
 +      wps->wps_pin_revealed = 0;
 +      wps_registrar_unlock_pin(wps->wps->registrar, wps->uuid_e);
 +
 +      /*
 +       * In case wildcard PIN is used and WPS handshake succeeds in the first
 +       * attempt, wps_registrar_unlock_pin() would not free the PIN, so make
 +       * sure the PIN gets invalidated here.
 +       */
 +      wps_registrar_invalidate_pin(wps->wps->registrar, wps->uuid_e);
 +
 +      return 0;
 +}
 +
 +
 +static int wps_process_mac_addr(struct wps_data *wps, const u8 *mac_addr)
 +{
 +      if (mac_addr == NULL) {
 +              wpa_printf(MSG_DEBUG, "WPS: No MAC Address received");
 +              return -1;
 +      }
 +
 +      wpa_printf(MSG_DEBUG, "WPS: Enrollee MAC Address " MACSTR,
 +                 MAC2STR(mac_addr));
 +      os_memcpy(wps->mac_addr_e, mac_addr, ETH_ALEN);
 +      os_memcpy(wps->peer_dev.mac_addr, mac_addr, ETH_ALEN);
 +
 +      return 0;
 +}
 +
 +
 +static int wps_process_pubkey(struct wps_data *wps, const u8 *pk,
 +                            size_t pk_len)
 +{
 +      if (pk == NULL || pk_len == 0) {
 +              wpa_printf(MSG_DEBUG, "WPS: No Public Key received");
 +              return -1;
 +      }
 +
 +      wpabuf_free(wps->dh_pubkey_e);
 +      wps->dh_pubkey_e = wpabuf_alloc_copy(pk, pk_len);
 +      if (wps->dh_pubkey_e == NULL)
 +              return -1;
 +
 +      return 0;
 +}
 +
 +
 +static int wps_process_auth_type_flags(struct wps_data *wps, const u8 *auth)
 +{
 +      u16 auth_types;
 +
 +      if (auth == NULL) {
 +              wpa_printf(MSG_DEBUG, "WPS: No Authentication Type flags "
 +                         "received");
 +              return -1;
 +      }
 +
 +      auth_types = WPA_GET_BE16(auth);
 +
 +      wpa_printf(MSG_DEBUG, "WPS: Enrollee Authentication Type flags 0x%x",
 +                 auth_types);
 +      wps->auth_type = wps->wps->auth_types & auth_types;
 +      if (wps->auth_type == 0) {
 +              wpa_printf(MSG_DEBUG, "WPS: No match in supported "
 +                         "authentication types (own 0x%x Enrollee 0x%x)",
 +                         wps->wps->auth_types, auth_types);
 +#ifdef WPS_WORKAROUNDS
 +              /*
 +               * Some deployed implementations seem to advertise incorrect
 +               * information in this attribute. For example, Linksys WRT350N
 +               * seems to have a byteorder bug that breaks this negotiation.
 +               * In order to interoperate with existing implementations,
 +               * assume that the Enrollee supports everything we do.
 +               */
 +              wpa_printf(MSG_DEBUG, "WPS: Workaround - assume Enrollee "
 +                         "does not advertise supported authentication types "
 +                         "correctly");
 +              wps->auth_type = wps->wps->auth_types;
 +#else /* WPS_WORKAROUNDS */
 +              return -1;
 +#endif /* WPS_WORKAROUNDS */
 +      }
 +
 +      return 0;
 +}
 +
 +
 +static int wps_process_encr_type_flags(struct wps_data *wps, const u8 *encr)
 +{
 +      u16 encr_types;
 +
 +      if (encr == NULL) {
 +              wpa_printf(MSG_DEBUG, "WPS: No Encryption Type flags "
 +                         "received");
 +              return -1;
 +      }
 +
 +      encr_types = WPA_GET_BE16(encr);
 +
 +      wpa_printf(MSG_DEBUG, "WPS: Enrollee Encryption Type flags 0x%x",
 +                 encr_types);
 +      wps->encr_type = wps->wps->encr_types & encr_types;
 +      if (wps->encr_type == 0) {
 +              wpa_printf(MSG_DEBUG, "WPS: No match in supported "
 +                         "encryption types (own 0x%x Enrollee 0x%x)",
 +                         wps->wps->encr_types, encr_types);
 +#ifdef WPS_WORKAROUNDS
 +              /*
 +               * Some deployed implementations seem to advertise incorrect
 +               * information in this attribute. For example, Linksys WRT350N
 +               * seems to have a byteorder bug that breaks this negotiation.
 +               * In order to interoperate with existing implementations,
 +               * assume that the Enrollee supports everything we do.
 +               */
 +              wpa_printf(MSG_DEBUG, "WPS: Workaround - assume Enrollee "
 +                         "does not advertise supported encryption types "
 +                         "correctly");
 +              wps->encr_type = wps->wps->encr_types;
 +#else /* WPS_WORKAROUNDS */
 +              return -1;
 +#endif /* WPS_WORKAROUNDS */
 +      }
 +
 +      return 0;
 +}
 +
 +
 +static int wps_process_conn_type_flags(struct wps_data *wps, const u8 *conn)
 +{
 +      if (conn == NULL) {
 +              wpa_printf(MSG_DEBUG, "WPS: No Connection Type flags "
 +                         "received");
 +              return -1;
 +      }
 +
 +      wpa_printf(MSG_DEBUG, "WPS: Enrollee Connection Type flags 0x%x",
 +                 *conn);
 +
 +      return 0;
 +}
 +
 +
 +static int wps_process_config_methods(struct wps_data *wps, const u8 *methods)
 +{
 +      u16 m;
 +
 +      if (methods == NULL) {
 +              wpa_printf(MSG_DEBUG, "WPS: No Config Methods received");
 +              return -1;
 +      }
 +
 +      m = WPA_GET_BE16(methods);
 +
 +      wpa_printf(MSG_DEBUG, "WPS: Enrollee Config Methods 0x%x"
 +                 "%s%s%s%s%s%s%s%s%s", m,
 +                 m & WPS_CONFIG_USBA ? " [USBA]" : "",
 +                 m & WPS_CONFIG_ETHERNET ? " [Ethernet]" : "",
 +                 m & WPS_CONFIG_LABEL ? " [Label]" : "",
 +                 m & WPS_CONFIG_DISPLAY ? " [Display]" : "",
 +                 m & WPS_CONFIG_EXT_NFC_TOKEN ? " [Ext NFC Token]" : "",
 +                 m & WPS_CONFIG_INT_NFC_TOKEN ? " [Int NFC Token]" : "",
 +                 m & WPS_CONFIG_NFC_INTERFACE ? " [NFC]" : "",
 +                 m & WPS_CONFIG_PUSHBUTTON ? " [PBC]" : "",
 +                 m & WPS_CONFIG_KEYPAD ? " [Keypad]" : "");
 +
 +      if (!(m & WPS_CONFIG_DISPLAY) && !wps->use_psk_key) {
 +              /*
 +               * The Enrollee does not have a display so it is unlikely to be
 +               * able to show the passphrase to a user and as such, could
 +               * benefit from receiving PSK to reduce key derivation time.
 +               */
 +              wpa_printf(MSG_DEBUG, "WPS: Prefer PSK format key due to "
 +                         "Enrollee not supporting display");
 +              wps->use_psk_key = 1;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +static int wps_process_wps_state(struct wps_data *wps, const u8 *state)
 +{
 +      if (state == NULL) {
 +              wpa_printf(MSG_DEBUG, "WPS: No Wi-Fi Protected Setup State "
 +                         "received");
 +              return -1;
 +      }
 +
 +      wpa_printf(MSG_DEBUG, "WPS: Enrollee Wi-Fi Protected Setup State %d",
 +                 *state);
 +
 +      return 0;
 +}
 +
 +
 +static int wps_process_assoc_state(struct wps_data *wps, const u8 *assoc)
 +{
 +      u16 a;
 +
 +      if (assoc == NULL) {
 +              wpa_printf(MSG_DEBUG, "WPS: No Association State received");
 +              return -1;
 +      }
 +
 +      a = WPA_GET_BE16(assoc);
 +      wpa_printf(MSG_DEBUG, "WPS: Enrollee Association State %d", a);
 +
 +      return 0;
 +}
 +
 +
 +static int wps_process_config_error(struct wps_data *wps, const u8 *err)
 +{
 +      u16 e;
 +
 +      if (err == NULL) {
 +              wpa_printf(MSG_DEBUG, "WPS: No Configuration Error received");
 +              return -1;
 +      }
 +
 +      e = WPA_GET_BE16(err);
 +      wpa_printf(MSG_DEBUG, "WPS: Enrollee Configuration Error %d", e);
 +
 +      return 0;
 +}
 +
 +
 +static int wps_registrar_p2p_dev_addr_match(struct wps_data *wps)
 +{
 +#ifdef CONFIG_P2P
 +      struct wps_registrar *reg = wps->wps->registrar;
 +
 +      if (is_zero_ether_addr(reg->p2p_dev_addr))
 +              return 1; /* no filtering in use */
 +
 +      if (os_memcmp(reg->p2p_dev_addr, wps->p2p_dev_addr, ETH_ALEN) != 0) {
 +              wpa_printf(MSG_DEBUG, "WPS: No match on P2P Device Address "
 +                         "filtering for PBC: expected " MACSTR " was "
 +                         MACSTR " - indicate PBC session overlap",
 +                         MAC2STR(reg->p2p_dev_addr),
 +                         MAC2STR(wps->p2p_dev_addr));
 +              return 0;
 +      }
 +#endif /* CONFIG_P2P */
 +      return 1;
 +}
 +
 +
 +static int wps_registrar_skip_overlap(struct wps_data *wps)
 +{
 +#ifdef CONFIG_P2P
 +      struct wps_registrar *reg = wps->wps->registrar;
 +
 +      if (is_zero_ether_addr(reg->p2p_dev_addr))
 +              return 0; /* no specific Enrollee selected */
 +
 +      if (os_memcmp(reg->p2p_dev_addr, wps->p2p_dev_addr, ETH_ALEN) == 0) {
 +              wpa_printf(MSG_DEBUG, "WPS: Skip PBC overlap due to selected "
 +                         "Enrollee match");
 +              return 1;
 +      }
 +#endif /* CONFIG_P2P */
 +      return 0;
 +}
 +
 +
 +static enum wps_process_res wps_process_m1(struct wps_data *wps,
 +                                         struct wps_parse_attr *attr)
 +{
 +      wpa_printf(MSG_DEBUG, "WPS: Received M1");
 +
 +      if (wps->state != RECV_M1) {
 +              wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
 +                         "receiving M1", wps->state);
 +              return WPS_FAILURE;
 +      }
 +
 +      if (wps_process_uuid_e(wps, attr->uuid_e) ||
 +          wps_process_mac_addr(wps, attr->mac_addr) ||
 +          wps_process_enrollee_nonce(wps, attr->enrollee_nonce) ||
 +          wps_process_pubkey(wps, attr->public_key, attr->public_key_len) ||
 +          wps_process_auth_type_flags(wps, attr->auth_type_flags) ||
 +          wps_process_encr_type_flags(wps, attr->encr_type_flags) ||
 +          wps_process_conn_type_flags(wps, attr->conn_type_flags) ||
 +          wps_process_config_methods(wps, attr->config_methods) ||
 +          wps_process_wps_state(wps, attr->wps_state) ||
 +          wps_process_device_attrs(&wps->peer_dev, attr) ||
 +          wps_process_rf_bands(&wps->peer_dev, attr->rf_bands) ||
 +          wps_process_assoc_state(wps, attr->assoc_state) ||
 +          wps_process_dev_password_id(wps, attr->dev_password_id) ||
 +          wps_process_config_error(wps, attr->config_error) ||
 +          wps_process_os_version(&wps->peer_dev, attr->os_version))
 +              return WPS_FAILURE;
 +
 +      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",
 +                         wps->dev_pw_id);
 +              wps->state = SEND_M2D;
 +              return WPS_CONTINUE;
 +      }
 +
 +#ifdef CONFIG_WPS_NFC
 +      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 && token->peer_pk_hash_known) {
++                      size_t len;
++
 +                      wpa_printf(MSG_DEBUG, "WPS: Found matching NFC "
 +                                 "Password Token");
 +                      dl_list_del(&token->list);
 +                      wps->nfc_pw_token = token;
 +
 +                      addr[0] = attr->public_key;
-               cred.auth_type = WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK;
-               cred.encr_type = WPS_ENCR_TKIP | WPS_ENCR_AES;
++                      len = attr->public_key_len;
++                      sha256_vector(1, addr, &len, hash);
 +                      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");
 +                              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 */
 +
 +      if (wps->dev_pw_id == DEV_PW_PUSHBUTTON) {
 +              if ((wps->wps->registrar->force_pbc_overlap ||
 +                   wps_registrar_pbc_overlap(wps->wps->registrar,
 +                                             wps->mac_addr_e, wps->uuid_e) ||
 +                   !wps_registrar_p2p_dev_addr_match(wps)) &&
 +                  !wps_registrar_skip_overlap(wps)) {
 +                      wpa_printf(MSG_DEBUG, "WPS: PBC overlap - deny PBC "
 +                                 "negotiation");
 +                      wps->state = SEND_M2D;
 +                      wps->config_error = WPS_CFG_MULTIPLE_PBC_DETECTED;
 +                      wps_pbc_overlap_event(wps->wps);
 +                      wps_fail_event(wps->wps, WPS_M1,
 +                                     WPS_CFG_MULTIPLE_PBC_DETECTED,
 +                                     WPS_EI_NO_ERROR, wps->mac_addr_e);
 +                      wps->wps->registrar->force_pbc_overlap = 1;
 +                      return WPS_CONTINUE;
 +              }
 +              wps_registrar_add_pbc_session(wps->wps->registrar,
 +                                            wps->mac_addr_e, wps->uuid_e);
 +              wps->pbc = 1;
 +      }
 +
 +#ifdef WPS_WORKAROUNDS
 +      /*
 +       * It looks like Mac OS X 10.6.3 and 10.6.4 do not like Network Key in
 +       * passphrase format. To avoid interop issues, force PSK format to be
 +       * used.
 +       */
 +      if (!wps->use_psk_key &&
 +          wps->peer_dev.manufacturer &&
 +          os_strncmp(wps->peer_dev.manufacturer, "Apple ", 6) == 0 &&
 +          wps->peer_dev.model_name &&
 +          os_strcmp(wps->peer_dev.model_name, "AirPort") == 0) {
 +              wpa_printf(MSG_DEBUG, "WPS: Workaround - Force Network Key in "
 +                         "PSK format");
 +              wps->use_psk_key = 1;
 +      }
 +#endif /* WPS_WORKAROUNDS */
 +
 +      wps->state = SEND_M2;
 +      return WPS_CONTINUE;
 +}
 +
 +
 +static enum wps_process_res wps_process_m3(struct wps_data *wps,
 +                                         const struct wpabuf *msg,
 +                                         struct wps_parse_attr *attr)
 +{
 +      wpa_printf(MSG_DEBUG, "WPS: Received M3");
 +
 +      if (wps->state != RECV_M3) {
 +              wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
 +                         "receiving M3", wps->state);
 +              wps->state = SEND_WSC_NACK;
 +              return WPS_CONTINUE;
 +      }
 +
 +      if (wps->pbc && wps->wps->registrar->force_pbc_overlap &&
 +          !wps_registrar_skip_overlap(wps)) {
 +              wpa_printf(MSG_DEBUG, "WPS: Reject negotiation due to PBC "
 +                         "session overlap");
 +              wps->state = SEND_WSC_NACK;
 +              wps->config_error = WPS_CFG_MULTIPLE_PBC_DETECTED;
 +              return WPS_CONTINUE;
 +      }
 +
 +      if (wps_process_registrar_nonce(wps, attr->registrar_nonce) ||
 +          wps_process_authenticator(wps, attr->authenticator, msg) ||
 +          wps_process_e_hash1(wps, attr->e_hash1) ||
 +          wps_process_e_hash2(wps, attr->e_hash2)) {
 +              wps->state = SEND_WSC_NACK;
 +              return WPS_CONTINUE;
 +      }
 +
 +      wps->state = SEND_M4;
 +      return WPS_CONTINUE;
 +}
 +
 +
 +static enum wps_process_res wps_process_m5(struct wps_data *wps,
 +                                         const struct wpabuf *msg,
 +                                         struct wps_parse_attr *attr)
 +{
 +      struct wpabuf *decrypted;
 +      struct wps_parse_attr eattr;
 +
 +      wpa_printf(MSG_DEBUG, "WPS: Received M5");
 +
 +      if (wps->state != RECV_M5) {
 +              wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
 +                         "receiving M5", wps->state);
 +              wps->state = SEND_WSC_NACK;
 +              return WPS_CONTINUE;
 +      }
 +
 +      if (wps->pbc && wps->wps->registrar->force_pbc_overlap &&
 +          !wps_registrar_skip_overlap(wps)) {
 +              wpa_printf(MSG_DEBUG, "WPS: Reject negotiation due to PBC "
 +                         "session overlap");
 +              wps->state = SEND_WSC_NACK;
 +              wps->config_error = WPS_CFG_MULTIPLE_PBC_DETECTED;
 +              return WPS_CONTINUE;
 +      }
 +
 +      if (wps_process_registrar_nonce(wps, attr->registrar_nonce) ||
 +          wps_process_authenticator(wps, attr->authenticator, msg)) {
 +              wps->state = SEND_WSC_NACK;
 +              return WPS_CONTINUE;
 +      }
 +
 +      decrypted = wps_decrypt_encr_settings(wps, attr->encr_settings,
 +                                            attr->encr_settings_len);
 +      if (decrypted == NULL) {
 +              wpa_printf(MSG_DEBUG, "WPS: Failed to decrypted Encrypted "
 +                         "Settings attribute");
 +              wps->state = SEND_WSC_NACK;
 +              return WPS_CONTINUE;
 +      }
 +
 +      if (wps_validate_m5_encr(decrypted, attr->version2 != NULL) < 0) {
 +              wpabuf_free(decrypted);
 +              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_e_snonce1(wps, eattr.e_snonce1)) {
 +              wpabuf_free(decrypted);
 +              wps->state = SEND_WSC_NACK;
 +              return WPS_CONTINUE;
 +      }
 +      wpabuf_free(decrypted);
 +
 +      wps->state = SEND_M6;
 +      return WPS_CONTINUE;
 +}
 +
 +
 +static void wps_sta_cred_cb(struct wps_data *wps)
 +{
 +      /*
 +       * Update credential to only include a single authentication and
 +       * encryption type in case the AP configuration includes more than one
 +       * option.
 +       */
 +      if (wps->cred.auth_type & WPS_AUTH_WPA2PSK)
 +              wps->cred.auth_type = WPS_AUTH_WPA2PSK;
 +      else if (wps->cred.auth_type & WPS_AUTH_WPAPSK)
 +              wps->cred.auth_type = WPS_AUTH_WPAPSK;
 +      if (wps->cred.encr_type & WPS_ENCR_AES)
 +              wps->cred.encr_type = WPS_ENCR_AES;
 +      else if (wps->cred.encr_type & WPS_ENCR_TKIP)
 +              wps->cred.encr_type = WPS_ENCR_TKIP;
 +      wpa_printf(MSG_DEBUG, "WPS: Update local configuration based on the "
 +                 "AP configuration");
 +      if (wps->wps->cred_cb)
 +              wps->wps->cred_cb(wps->wps->cb_ctx, &wps->cred);
 +}
 +
 +
 +static void wps_cred_update(struct wps_credential *dst,
 +                          struct wps_credential *src)
 +{
 +      os_memcpy(dst->ssid, src->ssid, sizeof(dst->ssid));
 +      dst->ssid_len = src->ssid_len;
 +      dst->auth_type = src->auth_type;
 +      dst->encr_type = src->encr_type;
 +      dst->key_idx = src->key_idx;
 +      os_memcpy(dst->key, src->key, sizeof(dst->key));
 +      dst->key_len = src->key_len;
 +}
 +
 +
 +static int wps_process_ap_settings_r(struct wps_data *wps,
 +                                   struct wps_parse_attr *attr)
 +{
 +      struct wpabuf *msg;
 +
 +      if (wps->wps->ap || wps->er)
 +              return 0;
 +
 +      /* AP Settings Attributes in M7 when Enrollee is an AP */
 +      if (wps_process_ap_settings(attr, &wps->cred) < 0)
 +              return -1;
 +
 +      wpa_printf(MSG_INFO, "WPS: Received old AP configuration from AP");
 +
 +      if (wps->new_ap_settings) {
 +              wpa_printf(MSG_INFO, "WPS: Update AP configuration based on "
 +                         "new settings");
 +              wps_cred_update(&wps->cred, wps->new_ap_settings);
 +              return 0;
 +      } else {
 +              /*
 +               * Use the AP PIN only to receive the current AP settings, not
 +               * to reconfigure the AP.
 +               */
 +
 +              /*
 +               * Clear selected registrar here since we do not get to
 +               * WSC_Done in this protocol run.
 +               */
 +              wps_registrar_pin_completed(wps->wps->registrar);
 +
 +              msg = wps_build_ap_cred(wps);
 +              if (msg == NULL)
 +                      return -1;
 +              wps->cred.cred_attr = wpabuf_head(msg);
 +              wps->cred.cred_attr_len = wpabuf_len(msg);
 +
 +              if (wps->ap_settings_cb) {
 +                      wps->ap_settings_cb(wps->ap_settings_cb_ctx,
 +                                          &wps->cred);
 +                      wpabuf_free(msg);
 +                      return 1;
 +              }
 +              wps_sta_cred_cb(wps);
 +
 +              wps->cred.cred_attr = NULL;
 +              wps->cred.cred_attr_len = 0;
 +              wpabuf_free(msg);
 +
 +              return 1;
 +      }
 +}
 +
 +
 +static enum wps_process_res wps_process_m7(struct wps_data *wps,
 +                                         const struct wpabuf *msg,
 +                                         struct wps_parse_attr *attr)
 +{
 +      struct wpabuf *decrypted;
 +      struct wps_parse_attr eattr;
 +
 +      wpa_printf(MSG_DEBUG, "WPS: Received M7");
 +
 +      if (wps->state != RECV_M7) {
 +              wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
 +                         "receiving M7", wps->state);
 +              wps->state = SEND_WSC_NACK;
 +              return WPS_CONTINUE;
 +      }
 +
 +      if (wps->pbc && wps->wps->registrar->force_pbc_overlap &&
 +          !wps_registrar_skip_overlap(wps)) {
 +              wpa_printf(MSG_DEBUG, "WPS: Reject negotiation due to PBC "
 +                         "session overlap");
 +              wps->state = SEND_WSC_NACK;
 +              wps->config_error = WPS_CFG_MULTIPLE_PBC_DETECTED;
 +              return WPS_CONTINUE;
 +      }
 +
 +      if (wps_process_registrar_nonce(wps, attr->registrar_nonce) ||
 +          wps_process_authenticator(wps, attr->authenticator, msg)) {
 +              wps->state = SEND_WSC_NACK;
 +              return WPS_CONTINUE;
 +      }
 +
 +      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;
 +      }
 +
 +      if (wps_validate_m7_encr(decrypted, wps->wps->ap || wps->er,
 +                               attr->version2 != NULL) < 0) {
 +              wpabuf_free(decrypted);
 +              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_e_snonce2(wps, eattr.e_snonce2) ||
 +          wps_process_ap_settings_r(wps, &eattr)) {
 +              wpabuf_free(decrypted);
 +              wps->state = SEND_WSC_NACK;
 +              return WPS_CONTINUE;
 +      }
 +
 +      wpabuf_free(decrypted);
 +
 +      wps->state = SEND_M8;
 +      return WPS_CONTINUE;
 +}
 +
 +
 +static enum wps_process_res wps_process_wsc_msg(struct wps_data *wps,
 +                                              const struct wpabuf *msg)
 +{
 +      struct wps_parse_attr attr;
 +      enum wps_process_res ret = WPS_CONTINUE;
 +
 +      wpa_printf(MSG_DEBUG, "WPS: Received WSC_MSG");
 +
 +      if (wps_parse_msg(msg, &attr) < 0)
 +              return WPS_FAILURE;
 +
 +      if (attr.msg_type == NULL) {
 +              wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute");
 +              wps->state = SEND_WSC_NACK;
 +              return WPS_CONTINUE;
 +      }
 +
 +      if (*attr.msg_type != WPS_M1 &&
 +          (attr.registrar_nonce == NULL ||
 +           os_memcmp(wps->nonce_r, attr.registrar_nonce,
 +                     WPS_NONCE_LEN) != 0)) {
 +              wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce");
 +              return WPS_FAILURE;
 +      }
 +
 +      switch (*attr.msg_type) {
 +      case WPS_M1:
 +              if (wps_validate_m1(msg) < 0)
 +                      return WPS_FAILURE;
 +#ifdef CONFIG_WPS_UPNP
 +              if (wps->wps->wps_upnp && attr.mac_addr) {
 +                      /* Remove old pending messages when starting new run */
 +                      wps_free_pending_msgs(wps->wps->upnp_msgs);
 +                      wps->wps->upnp_msgs = NULL;
 +
 +                      upnp_wps_device_send_wlan_event(
 +                              wps->wps->wps_upnp, attr.mac_addr,
 +                              UPNP_WPS_WLANEVENT_TYPE_EAP, msg);
 +              }
 +#endif /* CONFIG_WPS_UPNP */
 +              ret = wps_process_m1(wps, &attr);
 +              break;
 +      case WPS_M3:
 +              if (wps_validate_m3(msg) < 0)
 +                      return WPS_FAILURE;
 +              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->mac_addr_e);
 +              break;
 +      case WPS_M5:
 +              if (wps_validate_m5(msg) < 0)
 +                      return WPS_FAILURE;
 +              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->mac_addr_e);
 +              break;
 +      case WPS_M7:
 +              if (wps_validate_m7(msg) < 0)
 +                      return WPS_FAILURE;
 +              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->mac_addr_e);
 +              break;
 +      default:
 +              wpa_printf(MSG_DEBUG, "WPS: Unsupported Message Type %d",
 +                         *attr.msg_type);
 +              return WPS_FAILURE;
 +      }
 +
 +      if (ret == WPS_CONTINUE) {
 +              /* Save a copy of the last message for Authenticator derivation
 +               */
 +              wpabuf_free(wps->last_msg);
 +              wps->last_msg = wpabuf_dup(msg);
 +      }
 +
 +      return ret;
 +}
 +
 +
 +static enum wps_process_res wps_process_wsc_ack(struct wps_data *wps,
 +                                              const struct wpabuf *msg)
 +{
 +      struct wps_parse_attr attr;
 +
 +      wpa_printf(MSG_DEBUG, "WPS: Received WSC_ACK");
 +
 +      if (wps_parse_msg(msg, &attr) < 0)
 +              return WPS_FAILURE;
 +
 +      if (attr.msg_type == NULL) {
 +              wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute");
 +              return WPS_FAILURE;
 +      }
 +
 +      if (*attr.msg_type != WPS_WSC_ACK) {
 +              wpa_printf(MSG_DEBUG, "WPS: Invalid Message Type %d",
 +                         *attr.msg_type);
 +              return WPS_FAILURE;
 +      }
 +
 +#ifdef CONFIG_WPS_UPNP
 +      if (wps->wps->wps_upnp && wps->ext_reg && wps->state == RECV_M2D_ACK &&
 +          upnp_wps_subscribers(wps->wps->wps_upnp)) {
 +              if (wps->wps->upnp_msgs)
 +                      return WPS_CONTINUE;
 +              wpa_printf(MSG_DEBUG, "WPS: Wait for response from an "
 +                         "external Registrar");
 +              return WPS_PENDING;
 +      }
 +#endif /* CONFIG_WPS_UPNP */
 +
 +      if (attr.registrar_nonce == NULL ||
 +          os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN) != 0)
 +      {
 +              wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce");
 +              return WPS_FAILURE;
 +      }
 +
 +      if (attr.enrollee_nonce == NULL ||
 +          os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN) != 0) {
 +              wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce");
 +              return WPS_FAILURE;
 +      }
 +
 +      if (wps->state == RECV_M2D_ACK) {
 +#ifdef CONFIG_WPS_UPNP
 +              if (wps->wps->wps_upnp &&
 +                  upnp_wps_subscribers(wps->wps->wps_upnp)) {
 +                      if (wps->wps->upnp_msgs)
 +                              return WPS_CONTINUE;
 +                      if (wps->ext_reg == 0)
 +                              wps->ext_reg = 1;
 +                      wpa_printf(MSG_DEBUG, "WPS: Wait for response from an "
 +                                 "external Registrar");
 +                      return WPS_PENDING;
 +              }
 +#endif /* CONFIG_WPS_UPNP */
 +
 +              wpa_printf(MSG_DEBUG, "WPS: No more registrars available - "
 +                         "terminate negotiation");
 +      }
 +
 +      return WPS_FAILURE;
 +}
 +
 +
 +static enum wps_process_res wps_process_wsc_nack(struct wps_data *wps,
 +                                               const struct wpabuf *msg)
 +{
 +      struct wps_parse_attr attr;
 +      int old_state;
 +      u16 config_error;
 +
 +      wpa_printf(MSG_DEBUG, "WPS: Received WSC_NACK");
 +
 +      old_state = wps->state;
 +      wps->state = SEND_WSC_NACK;
 +
 +      if (wps_parse_msg(msg, &attr) < 0)
 +              return WPS_FAILURE;
 +
 +      if (attr.msg_type == NULL) {
 +              wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute");
 +              return WPS_FAILURE;
 +      }
 +
 +      if (*attr.msg_type != WPS_WSC_NACK) {
 +              wpa_printf(MSG_DEBUG, "WPS: Invalid Message Type %d",
 +                         *attr.msg_type);
 +              return WPS_FAILURE;
 +      }
 +
 +#ifdef CONFIG_WPS_UPNP
 +      if (wps->wps->wps_upnp && wps->ext_reg) {
 +              wpa_printf(MSG_DEBUG, "WPS: Negotiation using external "
 +                         "Registrar terminated by the Enrollee");
 +              return WPS_FAILURE;
 +      }
 +#endif /* CONFIG_WPS_UPNP */
 +
 +      if (attr.registrar_nonce == NULL ||
 +          os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN) != 0)
 +      {
 +              wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce");
 +              return WPS_FAILURE;
 +      }
 +
 +      if (attr.enrollee_nonce == NULL ||
 +          os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN) != 0) {
 +              wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce");
 +              return WPS_FAILURE;
 +      }
 +
 +      if (attr.config_error == NULL) {
 +              wpa_printf(MSG_DEBUG, "WPS: No Configuration Error attribute "
 +                         "in WSC_NACK");
 +              return WPS_FAILURE;
 +      }
 +
 +      config_error = WPA_GET_BE16(attr.config_error);
 +      wpa_printf(MSG_DEBUG, "WPS: Enrollee terminated negotiation with "
 +                 "Configuration Error %d", config_error);
 +
 +      switch (old_state) {
 +      case RECV_M3:
 +              wps_fail_event(wps->wps, WPS_M2, config_error,
 +                             wps->error_indication, wps->mac_addr_e);
 +              break;
 +      case RECV_M5:
 +              wps_fail_event(wps->wps, WPS_M4, config_error,
 +                             wps->error_indication, wps->mac_addr_e);
 +              break;
 +      case RECV_M7:
 +              wps_fail_event(wps->wps, WPS_M6, config_error,
 +                             wps->error_indication, wps->mac_addr_e);
 +              break;
 +      case RECV_DONE:
 +              wps_fail_event(wps->wps, WPS_M8, config_error,
 +                             wps->error_indication, wps->mac_addr_e);
 +              break;
 +      default:
 +              break;
 +      }
 +
 +      return WPS_FAILURE;
 +}
 +
 +
 +static enum wps_process_res wps_process_wsc_done(struct wps_data *wps,
 +                                               const struct wpabuf *msg)
 +{
 +      struct wps_parse_attr attr;
 +
 +      wpa_printf(MSG_DEBUG, "WPS: Received WSC_Done");
 +
 +      if (wps->state != RECV_DONE &&
 +          (!wps->wps->wps_upnp || !wps->ext_reg)) {
 +              wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
 +                         "receiving WSC_Done", wps->state);
 +              return WPS_FAILURE;
 +      }
 +
 +      if (wps_parse_msg(msg, &attr) < 0)
 +              return WPS_FAILURE;
 +
 +      if (attr.msg_type == NULL) {
 +              wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute");
 +              return WPS_FAILURE;
 +      }
 +
 +      if (*attr.msg_type != WPS_WSC_DONE) {
 +              wpa_printf(MSG_DEBUG, "WPS: Invalid Message Type %d",
 +                         *attr.msg_type);
 +              return WPS_FAILURE;
 +      }
 +
 +#ifdef CONFIG_WPS_UPNP
 +      if (wps->wps->wps_upnp && wps->ext_reg) {
 +              wpa_printf(MSG_DEBUG, "WPS: Negotiation using external "
 +                         "Registrar completed successfully");
 +              wps_device_store(wps->wps->registrar, &wps->peer_dev,
 +                               wps->uuid_e);
 +              return WPS_DONE;
 +      }
 +#endif /* CONFIG_WPS_UPNP */
 +
 +      if (attr.registrar_nonce == NULL ||
 +          os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN) != 0)
 +      {
 +              wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce");
 +              return WPS_FAILURE;
 +      }
 +
 +      if (attr.enrollee_nonce == NULL ||
 +          os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN) != 0) {
 +              wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce");
 +              return WPS_FAILURE;
 +      }
 +
 +      wpa_printf(MSG_DEBUG, "WPS: Negotiation completed successfully");
 +      wps_device_store(wps->wps->registrar, &wps->peer_dev,
 +                       wps->uuid_e);
 +
 +      if (wps->wps->wps_state == WPS_STATE_NOT_CONFIGURED && wps->new_psk &&
 +          wps->wps->ap && !wps->wps->registrar->disable_auto_conf) {
 +              struct wps_credential cred;
 +
 +              wpa_printf(MSG_DEBUG, "WPS: Moving to Configured state based "
 +                         "on first Enrollee connection");
 +
 +              os_memset(&cred, 0, sizeof(cred));
 +              os_memcpy(cred.ssid, wps->wps->ssid, wps->wps->ssid_len);
 +              cred.ssid_len = wps->wps->ssid_len;
++              if (wps->wps->rf_band_cb(wps->wps->cb_ctx) == WPS_RF_60GHZ) {
++                      cred.auth_type = WPS_AUTH_WPA2PSK;
++                      cred.encr_type = WPS_ENCR_AES;
++              } else {
++                      cred.auth_type = WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK;
++                      cred.encr_type = WPS_ENCR_TKIP | WPS_ENCR_AES;
++              }
 +              os_memcpy(cred.key, wps->new_psk, wps->new_psk_len);
 +              cred.key_len = wps->new_psk_len;
 +
 +              wps->wps->wps_state = WPS_STATE_CONFIGURED;
 +              wpa_hexdump_ascii_key(MSG_DEBUG,
 +                                    "WPS: Generated random passphrase",
 +                                    wps->new_psk, wps->new_psk_len);
 +              if (wps->wps->cred_cb)
 +                      wps->wps->cred_cb(wps->wps->cb_ctx, &cred);
 +
 +              os_free(wps->new_psk);
 +              wps->new_psk = NULL;
 +      }
 +
 +      if (!wps->wps->ap && !wps->er)
 +              wps_sta_cred_cb(wps);
 +
 +      if (wps->new_psk) {
 +              if (wps_cb_new_psk(wps->wps->registrar, wps->mac_addr_e,
 +                                 wps->p2p_dev_addr, wps->new_psk,
 +                                 wps->new_psk_len)) {
 +                      wpa_printf(MSG_DEBUG, "WPS: Failed to configure the "
 +                                 "new PSK");
 +              }
 +              os_free(wps->new_psk);
 +              wps->new_psk = NULL;
 +      }
 +
 +      wps_cb_reg_success(wps->wps->registrar, wps->mac_addr_e, wps->uuid_e,
 +                         wps->dev_password, wps->dev_password_len);
 +
 +      if (wps->pbc) {
 +              wps_registrar_remove_pbc_session(wps->wps->registrar,
 +                                               wps->uuid_e,
 +                                               wps->p2p_dev_addr);
 +              wps_registrar_pbc_completed(wps->wps->registrar);
 +#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 {
 +              wps_registrar_pin_completed(wps->wps->registrar);
 +      }
 +      /* TODO: maintain AuthorizedMACs somewhere separately for each ER and
 +       * merge them into APs own list.. */
 +
 +      wps_success_event(wps->wps, wps->mac_addr_e);
 +
 +      return WPS_DONE;
 +}
 +
 +
 +enum wps_process_res wps_registrar_process_msg(struct wps_data *wps,
 +                                             enum wsc_op_code op_code,
 +                                             const struct wpabuf *msg)
 +{
 +      enum wps_process_res ret;
 +
 +      wpa_printf(MSG_DEBUG, "WPS: Processing received message (len=%lu "
 +                 "op_code=%d)",
 +                 (unsigned long) wpabuf_len(msg), op_code);
 +
 +#ifdef CONFIG_WPS_UPNP
 +      if (wps->wps->wps_upnp && op_code == WSC_MSG && wps->ext_reg == 1) {
 +              struct wps_parse_attr attr;
 +              if (wps_parse_msg(msg, &attr) == 0 && attr.msg_type &&
 +                  *attr.msg_type == WPS_M3)
 +                      wps->ext_reg = 2; /* past M2/M2D phase */
 +      }
 +      if (wps->ext_reg > 1)
 +              wps_registrar_free_pending_m2(wps->wps);
 +      if (wps->wps->wps_upnp && wps->ext_reg &&
 +          wps->wps->upnp_msgs == NULL &&
 +          (op_code == WSC_MSG || op_code == WSC_Done || op_code == WSC_NACK))
 +      {
 +              struct wps_parse_attr attr;
 +              int type;
 +              if (wps_parse_msg(msg, &attr) < 0 || attr.msg_type == NULL)
 +                      type = -1;
 +              else
 +                      type = *attr.msg_type;
 +              wpa_printf(MSG_DEBUG, "WPS: Sending received message (type %d)"
 +                         " to external Registrar for processing", type);
 +              upnp_wps_device_send_wlan_event(wps->wps->wps_upnp,
 +                                              wps->mac_addr_e,
 +                                              UPNP_WPS_WLANEVENT_TYPE_EAP,
 +                                              msg);
 +              if (op_code == WSC_MSG)
 +                      return WPS_PENDING;
 +      } else if (wps->wps->wps_upnp && wps->ext_reg && op_code == WSC_MSG) {
 +              wpa_printf(MSG_DEBUG, "WPS: Skip internal processing - using "
 +                         "external Registrar");
 +              return WPS_CONTINUE;
 +      }
 +#endif /* CONFIG_WPS_UPNP */
 +
 +      switch (op_code) {
 +      case WSC_MSG:
 +              return wps_process_wsc_msg(wps, msg);
 +      case WSC_ACK:
 +              if (wps_validate_wsc_ack(msg) < 0)
 +                      return WPS_FAILURE;
 +              return wps_process_wsc_ack(wps, msg);
 +      case WSC_NACK:
 +              if (wps_validate_wsc_nack(msg) < 0)
 +                      return WPS_FAILURE;
 +              return wps_process_wsc_nack(wps, msg);
 +      case WSC_Done:
 +              if (wps_validate_wsc_done(msg) < 0)
 +                      return WPS_FAILURE;
 +              ret = wps_process_wsc_done(wps, msg);
 +              if (ret == WPS_FAILURE) {
 +                      wps->state = SEND_WSC_NACK;
 +                      wps_fail_event(wps->wps, WPS_WSC_DONE,
 +                                     wps->config_error,
 +                                     wps->error_indication, wps->mac_addr_e);
 +              }
 +              return ret;
 +      default:
 +              wpa_printf(MSG_DEBUG, "WPS: Unsupported op_code %d", op_code);
 +              return WPS_FAILURE;
 +      }
 +}
 +
 +
 +int wps_registrar_update_ie(struct wps_registrar *reg)
 +{
 +      return wps_set_ie(reg);
 +}
 +
 +
 +static void wps_registrar_set_selected_timeout(void *eloop_ctx,
 +                                             void *timeout_ctx)
 +{
 +      struct wps_registrar *reg = eloop_ctx;
 +
 +      wpa_printf(MSG_DEBUG, "WPS: Selected Registrar timeout - "
 +                 "unselect internal Registrar");
 +      reg->selected_registrar = 0;
 +      reg->pbc = 0;
 +      wps_registrar_selected_registrar_changed(reg, 0);
 +}
 +
 +
 +#ifdef CONFIG_WPS_UPNP
 +static void wps_registrar_sel_reg_add(struct wps_registrar *reg,
 +                                    struct subscription *s)
 +{
 +      int i, j;
 +      wpa_printf(MSG_DEBUG, "WPS: External Registrar selected (dev_pw_id=%d "
 +                 "config_methods=0x%x)",
 +                 s->dev_password_id, s->config_methods);
 +      reg->sel_reg_union = 1;
 +      if (reg->sel_reg_dev_password_id_override != DEV_PW_PUSHBUTTON)
 +              reg->sel_reg_dev_password_id_override = s->dev_password_id;
 +      if (reg->sel_reg_config_methods_override == -1)
 +              reg->sel_reg_config_methods_override = 0;
 +      reg->sel_reg_config_methods_override |= s->config_methods;
 +      for (i = 0; i < WPS_MAX_AUTHORIZED_MACS; i++)
 +              if (is_zero_ether_addr(reg->authorized_macs_union[i]))
 +                      break;
 +      for (j = 0; i < WPS_MAX_AUTHORIZED_MACS && j < WPS_MAX_AUTHORIZED_MACS;
 +           j++) {
 +              if (is_zero_ether_addr(s->authorized_macs[j]))
 +                      break;
 +              wpa_printf(MSG_DEBUG, "WPS: Add authorized MAC into union: "
 +                         MACSTR, MAC2STR(s->authorized_macs[j]));
 +              os_memcpy(reg->authorized_macs_union[i],
 +                        s->authorized_macs[j], ETH_ALEN);
 +              i++;
 +      }
 +      wpa_hexdump(MSG_DEBUG, "WPS: Authorized MACs union",
 +                  (u8 *) reg->authorized_macs_union,
 +                  sizeof(reg->authorized_macs_union));
 +}
 +#endif /* CONFIG_WPS_UPNP */
 +
 +
 +static void wps_registrar_sel_reg_union(struct wps_registrar *reg)
 +{
 +#ifdef CONFIG_WPS_UPNP
 +      struct subscription *s;
 +
 +      if (reg->wps->wps_upnp == NULL)
 +              return;
 +
 +      dl_list_for_each(s, &reg->wps->wps_upnp->subscriptions,
 +                       struct subscription, list) {
 +              struct subscr_addr *sa;
 +              sa = dl_list_first(&s->addr_list, struct subscr_addr, list);
 +              if (sa) {
 +                      wpa_printf(MSG_DEBUG, "WPS: External Registrar %s:%d",
 +                                 inet_ntoa(sa->saddr.sin_addr),
 +                                 ntohs(sa->saddr.sin_port));
 +              }
 +              if (s->selected_registrar)
 +                      wps_registrar_sel_reg_add(reg, s);
 +              else
 +                      wpa_printf(MSG_DEBUG, "WPS: External Registrar not "
 +                                 "selected");
 +      }
 +#endif /* CONFIG_WPS_UPNP */
 +}
 +
 +
 +/**
 + * wps_registrar_selected_registrar_changed - SetSelectedRegistrar change
 + * @reg: Registrar data from wps_registrar_init()
 + *
 + * This function is called when selected registrar state changes, e.g., when an
 + * AP receives a SetSelectedRegistrar UPnP message.
 + */
 +void wps_registrar_selected_registrar_changed(struct wps_registrar *reg,
 +                                            u16 dev_pw_id)
 +{
 +      wpa_printf(MSG_DEBUG, "WPS: Selected registrar information changed");
 +
 +      reg->sel_reg_union = reg->selected_registrar;
 +      reg->sel_reg_dev_password_id_override = -1;
 +      reg->sel_reg_config_methods_override = -1;
 +      os_memcpy(reg->authorized_macs_union, reg->authorized_macs,
 +                WPS_MAX_AUTHORIZED_MACS * ETH_ALEN);
 +      wpa_hexdump(MSG_DEBUG, "WPS: Authorized MACs union (start with own)",
 +                  (u8 *) reg->authorized_macs_union,
 +                  sizeof(reg->authorized_macs_union));
 +      if (reg->selected_registrar) {
 +              u16 methods;
 +
 +              methods = reg->wps->config_methods & ~WPS_CONFIG_PUSHBUTTON;
 +              methods &= ~(WPS_CONFIG_VIRT_PUSHBUTTON |
 +                           WPS_CONFIG_PHY_PUSHBUTTON);
 +              if (reg->pbc) {
 +                      reg->sel_reg_dev_password_id_override =
 +                              DEV_PW_PUSHBUTTON;
 +                      wps_set_pushbutton(&methods, reg->wps->config_methods);
 +              } else if (dev_pw_id)
 +                      reg->sel_reg_dev_password_id_override = dev_pw_id;
 +              wpa_printf(MSG_DEBUG, "WPS: Internal Registrar selected "
 +                         "(pbc=%d)", reg->pbc);
 +              reg->sel_reg_config_methods_override = methods;
 +      } else
 +              wpa_printf(MSG_DEBUG, "WPS: Internal Registrar not selected");
 +
 +      wps_registrar_sel_reg_union(reg);
 +
 +      wps_set_ie(reg);
 +      wps_cb_set_sel_reg(reg);
 +}
 +
 +
 +int wps_registrar_get_info(struct wps_registrar *reg, const u8 *addr,
 +                         char *buf, size_t buflen)
 +{
 +      struct wps_registrar_device *d;
 +      int len = 0, ret;
 +      char uuid[40];
 +      char devtype[WPS_DEV_TYPE_BUFSIZE];
 +
 +      d = wps_device_get(reg, addr);
 +      if (d == NULL)
 +              return 0;
 +      if (uuid_bin2str(d->uuid, uuid, sizeof(uuid)))
 +              return 0;
 +
 +      ret = os_snprintf(buf + len, buflen - len,
 +                        "wpsUuid=%s\n"
 +                        "wpsPrimaryDeviceType=%s\n"
 +                        "wpsDeviceName=%s\n"
 +                        "wpsManufacturer=%s\n"
 +                        "wpsModelName=%s\n"
 +                        "wpsModelNumber=%s\n"
 +                        "wpsSerialNumber=%s\n",
 +                        uuid,
 +                        wps_dev_type_bin2str(d->dev.pri_dev_type, devtype,
 +                                             sizeof(devtype)),
 +                        d->dev.device_name ? d->dev.device_name : "",
 +                        d->dev.manufacturer ? d->dev.manufacturer : "",
 +                        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 (os_snprintf_error(buflen - len, ret))
 +              return len;
 +      len += ret;
 +
 +      return len;
 +}
 +
 +
 +int wps_registrar_config_ap(struct wps_registrar *reg,
 +                          struct wps_credential *cred)
 +{
 +      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) {
 +                      wpa_printf(MSG_INFO, "WPS: Reject new AP settings "
 +                                 "due to WEP configuration");
 +                      return -1;
 +              }
 +
 +              wpa_printf(MSG_INFO, "WPS: Reject new AP settings due to "
 +                         "invalid encr_type 0x%x", cred->encr_type);
 +              return -1;
 +      }
 +
 +      if ((cred->encr_type & (WPS_ENCR_TKIP | WPS_ENCR_AES)) ==
 +          WPS_ENCR_TKIP) {
 +              wpa_printf(MSG_DEBUG, "WPS: Upgrade encr_type TKIP -> "
 +                         "TKIP+AES");
 +              cred->encr_type |= WPS_ENCR_AES;
 +      }
 +
 +      if ((cred->auth_type & (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK)) ==
 +          WPS_AUTH_WPAPSK) {
 +              wpa_printf(MSG_DEBUG, "WPS: Upgrade auth_type WPAPSK -> "
 +                         "WPAPSK+WPA2PSK");
 +              cred->auth_type |= WPS_AUTH_WPA2PSK;
 +      }
 +
 +      if (reg->wps->cred_cb)
 +              return reg->wps->cred_cb(reg->wps->cb_ctx, cred);
 +
 +      return -1;
 +}
 +
 +
 +#ifdef CONFIG_WPS_NFC
 +
 +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,
 +                                 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;
 +
 +      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;
 +      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);
 +
 +      reg->selected_registrar = 1;
 +      reg->pbc = 0;
 +      wps_registrar_add_authorized_mac(reg,
 +                                       (u8 *) "\xff\xff\xff\xff\xff\xff");
 +      wps_registrar_selected_registrar_changed(reg, pw_id);
 +      eloop_cancel_timeout(wps_registrar_set_selected_timeout, reg, NULL);
 +      eloop_register_timeout(WPS_PBC_WALK_TIME, 0,
 +                             wps_registrar_set_selected_timeout,
 +                             reg, NULL);
 +
 +      wpa_printf(MSG_DEBUG, "WPS: Added NFC Device Password %u to Registrar",
 +                 pw_id);
 +
 +      return 0;
 +}
 +
 +
 +int wps_registrar_add_nfc_password_token(struct wps_registrar *reg,
 +                                       const u8 *oob_dev_pw,
 +                                       size_t oob_dev_pw_len)
 +{
 +      const u8 *pos, *hash, *dev_pw;
 +      u16 id;
 +      size_t dev_pw_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;
 +
 +      hash = oob_dev_pw;
 +      pos = oob_dev_pw + WPS_OOB_PUBKEY_HASH_LEN;
 +      id = WPA_GET_BE16(pos);
 +      dev_pw = pos + 2;
 +      dev_pw_len = oob_dev_pw + oob_dev_pw_len - dev_pw;
 +
 +      wpa_printf(MSG_DEBUG, "WPS: Add NFC Password Token for Password ID %u",
 +                 id);
 +
 +      wpa_hexdump(MSG_DEBUG, "WPS: Public Key Hash",
 +                  hash, WPS_OOB_PUBKEY_HASH_LEN);
 +      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, 0);
 +}
 +
 +
 +void wps_registrar_remove_nfc_pw_token(struct wps_registrar *reg,
 +                                     struct wps_nfc_pw_token *token)
 +{
 +      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 933d7340ee8ec91d602c23ab1ffe5937949a1783,0000000000000000000000000000000000000000..44318e09425204d8cf70fa8b2fecaec3baef1c9f
mode 100644,000000..100644
--- /dev/null
@@@ -1,1209 -1,0 +1,1212 @@@
-       wpa_printf(MSG_DEBUG, "WPS UPnP: Subscription %p started with %s",
-                  s, callback_urls);
 +/*
 + * UPnP WPS Device
 + * Copyright (c) 2000-2003 Intel Corporation
 + * Copyright (c) 2006-2007 Sony Corporation
 + * Copyright (c) 2008-2009 Atheros Communications
 + * Copyright (c) 2009-2010, Jouni Malinen <j@w1.fi>
 + *
 + * See below for more details on licensing and code history.
 + */
 +
 +/*
 + * This has been greatly stripped down from the original file
 + * (upnp_wps_device.c) by Ted Merrill, Atheros Communications
 + * in order to eliminate use of the bulky libupnp library etc.
 + *
 + * History:
 + * upnp_wps_device.c is/was a shim layer between wps_opt_upnp.c and
 + * the libupnp library.
 + * The layering (by Sony) was well done; only a very minor modification
 + * to API of upnp_wps_device.c was required.
 + * libupnp was found to be undesirable because:
 + * -- It consumed too much code and data space
 + * -- It uses multiple threads, making debugging more difficult
 + *      and possibly reducing reliability.
 + * -- It uses static variables and only supports one instance.
 + * The shim and libupnp are here replaced by special code written
 + * specifically for the needs of hostapd.
 + * Various shortcuts can and are taken to keep the code size small.
 + * Generally, execution time is not as crucial.
 + *
 + * BUGS:
 + * -- UPnP requires that we be able to resolve domain names.
 + * While uncommon, if we have to do it then it will stall the entire
 + * hostapd program, which is bad.
 + * This is because we use the standard linux getaddrinfo() function
 + * which is syncronous.
 + * An asyncronous solution would be to use the free "ares" library.
 + * -- Does not have a robust output buffering scheme.  Uses a single
 + * fixed size output buffer per TCP/HTTP connection, with possible (although
 + * unlikely) possibility of overflow and likely excessive use of RAM.
 + * A better solution would be to write the HTTP output as a buffered stream,
 + * using chunking: (handle header specially, then) generate data with
 + * a printf-like function into a buffer, catching buffer full condition,
 + * then send it out surrounded by http chunking.
 + * -- There is some code that could be separated out into the common
 + * library to be shared with wpa_supplicant.
 + * -- Needs renaming with module prefix to avoid polluting the debugger
 + * namespace and causing possible collisions with other static fncs
 + * and structure declarations when using the debugger.
 + * -- The http error code generation is pretty bogus, hopefully no one cares.
 + *
 + * Author: Ted Merrill, Atheros Communications, based upon earlier work
 + * as explained above and below.
 + *
 + * Copyright:
 + * Copyright 2008 Atheros Communications.
 + *
 + * The original header (of upnp_wps_device.c) reads:
 + *
 + *  Copyright (c) 2006-2007 Sony Corporation. All Rights Reserved.
 + *
 + *  File Name: upnp_wps_device.c
 + *  Description: EAP-WPS UPnP device source
 + *
 + *   Redistribution and use in source and binary forms, with or without
 + *   modification, are permitted provided that the following conditions
 + *   are met:
 + *
 + *     * Redistributions of source code must retain the above copyright
 + *       notice, this list of conditions and the following disclaimer.
 + *     * 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.
 + *     * Neither the name of Sony Corporation nor the names of its
 + *       contributors may be used to endorse or promote products derived
 + *       from this software without specific prior written permission.
 + *
 + *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 + *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 + *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 + *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 + *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 + *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 + *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 + *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 + *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 + *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 + *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 + *
 + * Portions from Intel libupnp files, e.g. genlib/net/http/httpreadwrite.c
 + * typical header:
 + *
 + * Copyright (c) 2000-2003 Intel Corporation
 + * All rights reserved.
 + *
 + * Redistribution and use in source and binary forms, with or without
 + * modification, are permitted provided that the following conditions are met:
 + *
 + * * Redistributions of source code must retain the above copyright notice,
 + * this list of conditions and the following disclaimer.
 + * * 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.
 + * * Neither name of Intel Corporation nor the names of its contributors
 + * may be used to endorse or promote products derived from this software
 + * without specific prior written permission.
 + *
 + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR
 + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 +*/
 +
 +/*
 + * Overview of WPS over UPnP:
 + *
 + * UPnP is a protocol that allows devices to discover each other and control
 + * each other. In UPnP terminology, a device is either a "device" (a server
 + * that provides information about itself and allows itself to be controlled)
 + * or a "control point" (a client that controls "devices") or possibly both.
 + * This file implements a UPnP "device".
 + *
 + * For us, we use mostly basic UPnP discovery, but the control part of interest
 + * is WPS carried via UPnP messages. There is quite a bit of basic UPnP
 + * discovery to do before we can get to WPS, however.
 + *
 + * UPnP discovery begins with "devices" send out multicast UDP packets to a
 + * certain fixed multicast IP address and port, and "control points" sending
 + * out other such UDP packets.
 + *
 + * The packets sent by devices are NOTIFY packets (not to be confused with TCP
 + * NOTIFY packets that are used later) and those sent by control points are
 + * M-SEARCH packets. These packets contain a simple HTTP style header. The
 + * packets are sent redundantly to get around packet loss. Devices respond to
 + * M-SEARCH packets with HTTP-like UDP packets containing HTTP/1.1 200 OK
 + * messages, which give similar information as the UDP NOTIFY packets.
 + *
 + * The above UDP packets advertise the (arbitrary) TCP ports that the
 + * respective parties will listen to. The control point can then do a HTTP
 + * SUBSCRIBE (something like an HTTP PUT) after which the device can do a
 + * separate HTTP NOTIFY (also like an HTTP PUT) to do event messaging.
 + *
 + * The control point will also do HTTP GET of the "device file" listed in the
 + * original UDP information from the device (see UPNP_WPS_DEVICE_XML_FILE
 + * data), and based on this will do additional GETs... HTTP POSTs are done to
 + * cause an action.
 + *
 + * Beyond some basic information in HTTP headers, additional information is in
 + * the HTTP bodies, in a format set by the SOAP and XML standards, a markup
 + * language related to HTML used for web pages. This language is intended to
 + * provide the ultimate in self-documentation by providing a universal
 + * namespace based on pseudo-URLs called URIs. Note that although a URI looks
 + * like a URL (a web address), they are never accessed as such but are used
 + * only as identifiers.
 + *
 + * The POST of a GetDeviceInfo gets information similar to what might be
 + * obtained from a probe request or response on Wi-Fi. WPS messages M1-M8
 + * are passed via a POST of a PutMessage; the M1-M8 WPS messages are converted
 + * to a bin64 ascii representation for encapsulation. When proxying messages,
 + * WLANEvent and PutWLANResponse are used.
 + *
 + * This of course glosses over a lot of details.
 + */
 +
 +#include "includes.h"
 +
 +#include <time.h>
 +#include <net/if.h>
 +#include <netdb.h>
 +#include <sys/ioctl.h>
 +
 +#include "common.h"
 +#include "uuid.h"
 +#include "base64.h"
 +#include "wps.h"
 +#include "wps_i.h"
 +#include "wps_upnp.h"
 +#include "wps_upnp_i.h"
 +
 +
 +/*
 + * UPnP allows a client ("control point") to send a server like us ("device")
 + * a domain name for registration, and we are supposed to resolve it. This is
 + * bad because, using the standard Linux library, we will stall the entire
 + * hostapd waiting for resolution.
 + *
 + * The "correct" solution would be to use an event driven library for domain
 + * name resolution such as "ares". However, this would increase code size
 + * further. Since it is unlikely that we'll actually see such domain names, we
 + * can just refuse to accept them.
 + */
 +#define NO_DOMAIN_NAME_RESOLUTION 1  /* 1 to allow only dotted ip addresses */
 +
 +
 +/*
 + * UPnP does not scale well. If we were in a room with thousands of control
 + * points then potentially we could be expected to handle subscriptions for
 + * each of them, which would exhaust our memory. So we must set a limit. In
 + * practice we are unlikely to see more than one or two.
 + */
 +#define MAX_SUBSCRIPTIONS 4    /* how many subscribing clients we handle */
 +#define MAX_ADDR_PER_SUBSCRIPTION 8
 +
 +/* Maximum number of Probe Request events per second */
 +#define MAX_EVENTS_PER_SEC 5
 +
 +
 +static struct upnp_wps_device_sm *shared_upnp_device = NULL;
 +
 +
 +/* Write the current date/time per RFC */
 +void format_date(struct wpabuf *buf)
 +{
 +      const char *weekday_str = "Sun\0Mon\0Tue\0Wed\0Thu\0Fri\0Sat";
 +      const char *month_str = "Jan\0Feb\0Mar\0Apr\0May\0Jun\0"
 +              "Jul\0Aug\0Sep\0Oct\0Nov\0Dec";
 +      struct tm *date;
 +      time_t t;
 +
 +      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,
 +                    date->tm_hour, date->tm_min, date->tm_sec);
 +}
 +
 +
 +/***************************************************************************
 + * UUIDs (unique identifiers)
 + *
 + * These are supposed to be unique in all the world.
 + * Sometimes permanent ones are used, sometimes temporary ones
 + * based on random numbers... there are different rules for valid content
 + * of different types.
 + * Each uuid is 16 bytes long.
 + **************************************************************************/
 +
 +/* uuid_make -- construct a random UUID
 + * The UPnP documents don't seem to offer any guidelines as to which method to
 + * use for constructing UUIDs for subscriptions. Presumably any method from
 + * rfc4122 is good enough; I've chosen random number method.
 + */
 +static int uuid_make(u8 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;
 +}
 +
 +
 +/*
 + * Subscriber address handling.
 + * Since a subscriber may have an arbitrary number of addresses, we have to
 + * add a bunch of code to handle them.
 + *
 + * Addresses are passed in text, and MAY be domain names instead of the (usual
 + * and expected) dotted IP addresses. Resolving domain names consumes a lot of
 + * resources. Worse, we are currently using the standard Linux getaddrinfo()
 + * which will block the entire program until complete or timeout! The proper
 + * solution would be to use the "ares" library or similar with more state
 + * machine steps etc. or just disable domain name resolution by setting
 + * NO_DOMAIN_NAME_RESOLUTION to 1 at top of this file.
 + */
 +
 +/* subscr_addr_delete -- delete single unlinked subscriber address
 + * (be sure to unlink first if need be)
 + */
 +void subscr_addr_delete(struct subscr_addr *a)
 +{
 +      /*
 +       * Note: do NOT free domain_and_port or path because they point to
 +       * memory within the allocation of "a".
 +       */
 +      os_free(a);
 +}
 +
 +
 +/* subscr_addr_free_all -- unlink and delete list of subscriber addresses. */
 +static void subscr_addr_free_all(struct subscription *s)
 +{
 +      struct subscr_addr *a, *tmp;
 +      dl_list_for_each_safe(a, tmp, &s->addr_list, struct subscr_addr, list)
 +      {
 +              dl_list_del(&a->list);
 +              subscr_addr_delete(a);
 +      }
 +}
 +
 +
 +/* subscr_addr_add_url -- add address(es) for one url to subscription */
 +static void subscr_addr_add_url(struct subscription *s, const char *url,
 +                              size_t url_len)
 +{
 +      int alloc_len;
 +      char *scratch_mem = NULL;
 +      char *mem;
 +      char *host;
 +      char *delim;
 +      char *path;
 +      int port = 80;  /* port to send to (default is port 80) */
 +      struct addrinfo hints;
 +      struct addrinfo *result = NULL;
 +      struct addrinfo *rp;
 +      int rerr;
 +      size_t host_len, path_len;
 +
 +      /* url MUST begin with http: */
 +      if (url_len < 7 || os_strncasecmp(url, "http://", 7))
 +              goto fail;
 +      url += 7;
 +      url_len -= 7;
 +
 +      /* Make a copy of the string to allow modification during parsing */
 +      scratch_mem = dup_binstr(url, url_len);
 +      if (scratch_mem == NULL)
 +              goto fail;
 +      wpa_printf(MSG_DEBUG, "WPS UPnP: Adding URL '%s'", scratch_mem);
 +      host = scratch_mem;
 +      path = os_strchr(host, '/');
 +      if (path)
 +              *path++ = '\0'; /* null terminate host */
 +
 +      /* Process and remove optional port component */
 +      delim = os_strchr(host, ':');
 +      if (delim) {
 +              *delim = '\0'; /* null terminate host name for now */
 +              if (isdigit(delim[1]))
 +                      port = atol(delim + 1);
 +      }
 +
 +      /*
 +       * getaddrinfo does the right thing with dotted decimal notations, or
 +       * will resolve domain names. Resolving domain names will unfortunately
 +       * hang the entire program until it is resolved or it times out
 +       * internal to getaddrinfo; fortunately we think that the use of actual
 +       * domain names (vs. dotted decimal notations) should be uncommon.
 +       */
 +      os_memset(&hints, 0, sizeof(struct addrinfo));
 +      hints.ai_family = AF_INET;      /* IPv4 */
 +      hints.ai_socktype = SOCK_STREAM;
 +#if NO_DOMAIN_NAME_RESOLUTION
 +      /* Suppress domain name resolutions that would halt
 +       * the program for periods of time
 +       */
 +      hints.ai_flags = AI_NUMERICHOST;
 +#else
 +      /* Allow domain name resolution. */
 +      hints.ai_flags = 0;
 +#endif
 +      hints.ai_protocol = 0;          /* Any protocol? */
 +      rerr = getaddrinfo(host, NULL /* fill in port ourselves */,
 +                         &hints, &result);
 +      if (rerr) {
 +              wpa_printf(MSG_INFO, "WPS UPnP: Resolve error %d (%s) on: %s",
 +                         rerr, gai_strerror(rerr), host);
 +              goto fail;
 +      }
 +
 +      if (delim)
 +              *delim = ':'; /* Restore port */
 +
 +      host_len = os_strlen(host);
 +      path_len = path ? os_strlen(path) : 0;
 +      alloc_len = host_len + 1 + 1 + path_len + 1;
 +
 +      for (rp = result; rp; rp = rp->ai_next) {
 +              struct subscr_addr *a;
 +
 +              /* Limit no. of address to avoid denial of service attack */
 +              if (dl_list_len(&s->addr_list) >= MAX_ADDR_PER_SUBSCRIPTION) {
 +                      wpa_printf(MSG_INFO, "WPS UPnP: subscr_addr_add_url: "
 +                                 "Ignoring excessive addresses");
 +                      break;
 +              }
 +
 +              a = os_zalloc(sizeof(*a) + alloc_len);
 +              if (a == NULL)
 +                      break;
 +              mem = (char *) (a + 1);
 +              a->domain_and_port = mem;
 +              os_memcpy(mem, host, host_len);
 +              mem += host_len + 1;
 +              a->path = mem;
 +              if (path == NULL || path[0] != '/')
 +                      *mem++ = '/';
 +              if (path)
 +                      os_memcpy(mem, path, path_len);
 +              os_memcpy(&a->saddr, rp->ai_addr, sizeof(a->saddr));
 +              a->saddr.sin_port = htons(port);
 +
 +              dl_list_add(&s->addr_list, &a->list);
 +      }
 +
 +fail:
 +      if (result)
 +              freeaddrinfo(result);
 +      os_free(scratch_mem);
 +}
 +
 +
 +/* subscr_addr_list_create -- create list from urls in string.
 + *      Each url is enclosed by angle brackets.
 + */
 +static void subscr_addr_list_create(struct subscription *s,
 +                                  const char *url_list)
 +{
 +      const char *end;
 +      wpa_printf(MSG_DEBUG, "WPS UPnP: Parsing URL list '%s'", url_list);
 +      for (;;) {
 +              while (*url_list == ' ' || *url_list == '\t')
 +                      url_list++;
 +              if (*url_list != '<')
 +                      break;
 +              url_list++;
 +              end = os_strchr(url_list, '>');
 +              if (end == NULL)
 +                      break;
 +              subscr_addr_add_url(s, url_list, end - url_list);
 +              url_list = end + 1;
 +      }
 +}
 +
 +
 +static void wpabuf_put_property(struct wpabuf *buf, const char *name,
 +                              const char *value)
 +{
 +      wpabuf_put_str(buf, "<e:property>");
 +      wpabuf_printf(buf, "<%s>", name);
 +      if (value)
 +              wpabuf_put_str(buf, value);
 +      wpabuf_printf(buf, "</%s>", name);
 +      wpabuf_put_str(buf, "</e:property>\n");
 +}
 +
 +
 +/**
 + * upnp_wps_device_send_event - Queue event messages for subscribers
 + * @sm: WPS UPnP state machine from upnp_wps_device_init()
 + *
 + * This function queues the last WLANEvent to be sent for all currently
 + * subscribed UPnP control points. sm->wlanevent must have been set with the
 + * encoded data before calling this function.
 + */
 +static void upnp_wps_device_send_event(struct upnp_wps_device_sm *sm)
 +{
 +      /* Enqueue event message for all subscribers */
 +      struct wpabuf *buf; /* holds event message */
 +      int buf_size = 0;
 +      struct subscription *s, *tmp;
 +      /* Actually, utf-8 is the default, but it doesn't hurt to specify it */
 +      const char *format_head =
 +              "<?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_reltime now;
 +
 +      if (dl_list_empty(&sm->subscriptions)) {
 +              /* optimize */
 +              return;
 +      }
 +
 +      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;
 +              } else {
 +                      sm->num_events_in_sec++;
 +                      /*
 +                       * In theory, this should apply to all WLANEvent
 +                       * notifications, but EAP messages are of much higher
 +                       * priority and Probe Request notifications should not
 +                       * be allowed to drop EAP messages, so only throttle
 +                       * Probe Request notifications.
 +                       */
 +                      if (sm->num_events_in_sec > MAX_EVENTS_PER_SEC &&
 +                          sm->wlanevent_type ==
 +                          UPNP_WPS_WLANEVENT_TYPE_PROBE) {
 +                              wpa_printf(MSG_DEBUG, "WPS UPnP: Throttle "
 +                                         "event notifications (%u seen "
 +                                         "during one second)",
 +                                         sm->num_events_in_sec);
 +                              return;
 +                      }
 +              }
 +      }
 +
 +      /* Determine buffer size needed first */
 +      buf_size += os_strlen(format_head);
 +      buf_size += 50 + 2 * os_strlen("WLANEvent");
 +      if (sm->wlanevent)
 +              buf_size += os_strlen(sm->wlanevent);
 +      buf_size += os_strlen(format_tail);
 +
 +      buf = wpabuf_alloc(buf_size);
 +      if (buf == NULL)
 +              return;
 +      wpabuf_put_str(buf, format_head);
 +      wpabuf_put_property(buf, "WLANEvent", sm->wlanevent);
 +      wpabuf_put_str(buf, format_tail);
 +
 +      wpa_printf(MSG_MSGDUMP, "WPS UPnP: WLANEvent message:\n%s",
 +                 (char *) wpabuf_head(buf));
 +
 +      dl_list_for_each_safe(s, tmp, &sm->subscriptions, struct subscription,
 +                            list) {
 +              event_add(s, buf,
 +                        sm->wlanevent_type == UPNP_WPS_WLANEVENT_TYPE_PROBE);
 +      }
 +
 +      wpabuf_free(buf);
 +}
 +
 +
 +/*
 + * Event subscription (subscriber machines register with us to receive event
 + * messages).
 + * This is the result of an incoming HTTP over TCP SUBSCRIBE request.
 + */
 +
 +/* subscription_destroy -- destroy an unlinked subscription
 + * Be sure to unlink first if necessary.
 + */
 +void subscription_destroy(struct subscription *s)
 +{
 +      struct upnp_wps_device_interface *iface;
 +      wpa_printf(MSG_DEBUG, "WPS UPnP: Destroy subscription %p", s);
 +      subscr_addr_free_all(s);
 +      event_delete_all(s);
 +      dl_list_for_each(iface, &s->sm->interfaces,
 +                       struct upnp_wps_device_interface, list)
 +              upnp_er_remove_notification(iface->wps->registrar, s);
 +      os_free(s);
 +}
 +
 +
 +/* subscription_list_age -- remove expired subscriptions */
 +static void subscription_list_age(struct upnp_wps_device_sm *sm, time_t now)
 +{
 +      struct subscription *s, *tmp;
 +      dl_list_for_each_safe(s, tmp, &sm->subscriptions,
 +                            struct subscription, list) {
 +              if (s->timeout_time > now)
 +                      break;
 +              wpa_printf(MSG_DEBUG, "WPS UPnP: Removing aged subscription");
 +              dl_list_del(&s->list);
 +              subscription_destroy(s);
 +      }
 +}
 +
 +
 +/* subscription_find -- return existing subscription matching uuid, if any
 + * returns NULL if not found
 + */
 +struct subscription * subscription_find(struct upnp_wps_device_sm *sm,
 +                                      const u8 uuid[UUID_LEN])
 +{
 +      struct subscription *s;
 +      dl_list_for_each(s, &sm->subscriptions, struct subscription, list) {
 +              if (os_memcmp(s->uuid, uuid, UUID_LEN) == 0)
 +                      return s; /* Found match */
 +      }
 +      return NULL;
 +}
 +
 +
 +static struct wpabuf * build_fake_wsc_ack(void)
 +{
 +      struct wpabuf *msg = wpabuf_alloc(100);
 +      if (msg == NULL)
 +              return NULL;
 +      wpabuf_put_u8(msg, UPNP_WPS_WLANEVENT_TYPE_EAP);
 +      wpabuf_put_str(msg, "00:00:00:00:00:00");
 +      if (wps_build_version(msg) ||
 +          wps_build_msg_type(msg, WPS_WSC_ACK)) {
 +              wpabuf_free(msg);
 +              return NULL;
 +      }
 +      /* Enrollee Nonce */
 +      wpabuf_put_be16(msg, ATTR_ENROLLEE_NONCE);
 +      wpabuf_put_be16(msg, WPS_NONCE_LEN);
 +      wpabuf_put(msg, WPS_NONCE_LEN);
 +      /* Registrar Nonce */
 +      wpabuf_put_be16(msg, ATTR_REGISTRAR_NONCE);
 +      wpabuf_put_be16(msg, WPS_NONCE_LEN);
 +      wpabuf_put(msg, WPS_NONCE_LEN);
 +      if (wps_build_wfa_ext(msg, 0, NULL, 0)) {
 +              wpabuf_free(msg);
 +              return NULL;
 +      }
 +      return msg;
 +}
 +
 +
 +/* subscription_first_event -- send format/queue event that is automatically
 + * sent on a new subscription.
 + */
 +static int subscription_first_event(struct subscription *s)
 +{
 +      /*
 +       * Actually, utf-8 is the default, but it doesn't hurt to specify it.
 +       *
 +       * APStatus is apparently a bit set,
 +       * 0x1 = configuration change (but is always set?)
 +       * 0x10 = ap is locked
 +       *
 +       * Per UPnP spec, we send out the last value of each variable, even
 +       * for WLANEvent, whatever it was.
 +       */
 +      char *wlan_event;
 +      struct wpabuf *buf;
 +      int ap_status = 1;      /* TODO: add 0x10 if access point is locked */
 +      const char *head =
 +              "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
 +              "<e:propertyset xmlns:e=\"urn:schemas-upnp-org:event-1-0\">\n";
 +      const char *tail = "</e:propertyset>\n";
 +      char txt[10];
 +      int ret;
 +
 +      if (s->sm->wlanevent == NULL) {
 +              /*
 +               * There has been no events before the subscription. However,
 +               * UPnP device architecture specification requires all the
 +               * evented variables to be included, so generate a dummy event
 +               * for this particular case using a WSC_ACK and all-zeros
 +               * nonces. The ER (UPnP control point) will ignore this, but at
 +               * least it will learn that WLANEvent variable will be used in
 +               * event notifications in the future.
 +               */
 +              struct wpabuf *msg;
 +              wpa_printf(MSG_DEBUG, "WPS UPnP: Use a fake WSC_ACK as the "
 +                         "initial WLANEvent");
 +              msg = build_fake_wsc_ack();
 +              if (msg) {
 +                      s->sm->wlanevent = (char *)
 +                              base64_encode(wpabuf_head(msg),
 +                                            wpabuf_len(msg), NULL);
 +                      wpabuf_free(msg);
 +              }
 +      }
 +
 +      wlan_event = s->sm->wlanevent;
 +      if (wlan_event == NULL || *wlan_event == '\0') {
 +              wpa_printf(MSG_DEBUG, "WPS UPnP: WLANEvent not known for "
 +                         "initial event message");
 +              wlan_event = "";
 +      }
 +      buf = wpabuf_alloc(500 + os_strlen(wlan_event));
 +      if (buf == NULL)
 +              return -1;
 +
 +      wpabuf_put_str(buf, head);
 +      wpabuf_put_property(buf, "STAStatus", "1");
 +      os_snprintf(txt, sizeof(txt), "%d", ap_status);
 +      wpabuf_put_property(buf, "APStatus", txt);
 +      if (*wlan_event)
 +              wpabuf_put_property(buf, "WLANEvent", wlan_event);
 +      wpabuf_put_str(buf, tail);
 +
 +      ret = event_add(s, buf, 0);
 +      if (ret) {
 +              wpabuf_free(buf);
 +              return ret;
 +      }
 +      wpabuf_free(buf);
 +
 +      return 0;
 +}
 +
 +
 +/**
 + * subscription_start - Remember a UPnP control point to send events to.
 + * @sm: WPS UPnP state machine from upnp_wps_device_init()
 + * @callback_urls: Callback URLs
 + * Returns: %NULL on error, or pointer to new subscription structure.
 + */
 +struct subscription * subscription_start(struct upnp_wps_device_sm *sm,
 +                                       const char *callback_urls)
 +{
 +      struct subscription *s;
 +      time_t now = time(NULL);
 +      time_t expire = now + UPNP_SUBSCRIBE_SEC;
++      char str[80];
 +
 +      /* Get rid of expired subscriptions so we have room */
 +      subscription_list_age(sm, now);
 +
 +      /* If too many subscriptions, remove oldest */
 +      if (dl_list_len(&sm->subscriptions) >= MAX_SUBSCRIPTIONS) {
 +              s = dl_list_first(&sm->subscriptions, struct subscription,
 +                                list);
 +              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));
 +      if (s == NULL)
 +              return NULL;
 +      dl_list_init(&s->addr_list);
 +      dl_list_init(&s->event_queue);
 +
 +      s->sm = sm;
 +      s->timeout_time = expire;
 +      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 "
 +                         "'%s' - drop subscription", callback_urls);
 +              subscription_destroy(s);
 +              return NULL;
 +      }
 +
 +      /* Add to end of list, since it has the highest expiration time */
 +      dl_list_add_tail(&sm->subscriptions, &s->list);
 +      /* Queue up immediate event message (our last event)
 +       * as required by UPnP spec.
 +       */
 +      if (subscription_first_event(s)) {
 +              wpa_printf(MSG_INFO, "WPS UPnP: Dropping subscriber due to "
 +                         "event backlog");
 +              dl_list_del(&s->list);
 +              subscription_destroy(s);
 +              return NULL;
 +      }
++      uuid_bin2str(s->uuid, str, sizeof(str));
++      wpa_printf(MSG_DEBUG,
++                 "WPS UPnP: Subscription %p (SID %s) started with %s",
++                 s, str, callback_urls);
 +      /* Schedule sending this */
 +      event_send_all_later(sm);
 +      return s;
 +}
 +
 +
 +/* subscription_renew -- find subscription and reset timeout */
 +struct subscription * subscription_renew(struct upnp_wps_device_sm *sm,
 +                                       const u8 uuid[UUID_LEN])
 +{
 +      time_t now = time(NULL);
 +      time_t expire = now + UPNP_SUBSCRIBE_SEC;
 +      struct subscription *s = subscription_find(sm, uuid);
 +      if (s == NULL)
 +              return NULL;
 +      wpa_printf(MSG_DEBUG, "WPS UPnP: Subscription renewed");
 +      dl_list_del(&s->list);
 +      s->timeout_time = expire;
 +      /* add back to end of list, since it now has highest expiry */
 +      dl_list_add_tail(&sm->subscriptions, &s->list);
 +      return s;
 +}
 +
 +
 +/**
 + * upnp_wps_device_send_wlan_event - Event notification
 + * @sm: WPS UPnP state machine from upnp_wps_device_init()
 + * @from_mac_addr: Source (Enrollee) MAC address for the event
 + * @ev_type: Event type
 + * @msg: Event data
 + * Returns: 0 on success, -1 on failure
 + *
 + * Tell external Registrars (UPnP control points) that something happened. In
 + * particular, events include WPS messages from clients that are proxied to
 + * external Registrars.
 + */
 +int upnp_wps_device_send_wlan_event(struct upnp_wps_device_sm *sm,
 +                                  const u8 from_mac_addr[ETH_ALEN],
 +                                  enum upnp_wps_wlanevent_type ev_type,
 +                                  const struct wpabuf *msg)
 +{
 +      int ret = -1;
 +      char type[2];
 +      const u8 *mac = from_mac_addr;
 +      char mac_text[18];
 +      u8 *raw = NULL;
 +      size_t raw_len;
 +      char *val;
 +      size_t val_len;
 +      int pos = 0;
 +
 +      if (!sm)
 +              goto fail;
 +
 +      os_snprintf(type, sizeof(type), "%1u", ev_type);
 +
 +      raw_len = 1 + 17 + (msg ? wpabuf_len(msg) : 0);
 +      raw = os_zalloc(raw_len);
 +      if (!raw)
 +              goto fail;
 +
 +      *(raw + pos) = (u8) ev_type;
 +      pos += 1;
 +      os_snprintf(mac_text, sizeof(mac_text), MACSTR, MAC2STR(mac));
 +      wpa_printf(MSG_DEBUG, "WPS UPnP: Proxying WLANEvent from %s",
 +                 mac_text);
 +      os_memcpy(raw + pos, mac_text, 17);
 +      pos += 17;
 +      if (msg) {
 +              os_memcpy(raw + pos, wpabuf_head(msg), wpabuf_len(msg));
 +              pos += wpabuf_len(msg);
 +      }
 +      raw_len = pos;
 +
 +      val = (char *) base64_encode(raw, raw_len, &val_len);
 +      if (val == NULL)
 +              goto fail;
 +
 +      os_free(sm->wlanevent);
 +      sm->wlanevent = val;
 +      sm->wlanevent_type = ev_type;
 +      upnp_wps_device_send_event(sm);
 +
 +      ret = 0;
 +
 +fail:
 +      os_free(raw);
 +
 +      return ret;
 +}
 +
 +
 +#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
 +#include <sys/sysctl.h>
 +#include <net/route.h>
 +#include <net/if_dl.h>
 +
 +static int eth_get(const char *device, u8 ea[ETH_ALEN])
 +{
 +      struct if_msghdr *ifm;
 +      struct sockaddr_dl *sdl;
 +      u_char *p, *buf;
 +      size_t len;
 +      int mib[] = { CTL_NET, AF_ROUTE, 0, AF_LINK, NET_RT_IFLIST, 0 };
 +
 +      if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0)
 +              return -1;
 +      if ((buf = os_malloc(len)) == NULL)
 +              return -1;
 +      if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) {
 +              os_free(buf);
 +              return -1;
 +      }
 +      for (p = buf; p < buf + len; p += ifm->ifm_msglen) {
 +              ifm = (struct if_msghdr *)p;
 +              sdl = (struct sockaddr_dl *)(ifm + 1);
 +              if (ifm->ifm_type != RTM_IFINFO ||
 +                  (ifm->ifm_addrs & RTA_IFP) == 0)
 +                      continue;
 +              if (sdl->sdl_family != AF_LINK || sdl->sdl_nlen == 0 ||
 +                  os_memcmp(sdl->sdl_data, device, sdl->sdl_nlen) != 0)
 +                      continue;
 +              os_memcpy(ea, LLADDR(sdl), sdl->sdl_alen);
 +              break;
 +      }
 +      os_free(buf);
 +
 +      if (p >= buf + len) {
 +              errno = ESRCH;
 +              return -1;
 +      }
 +      return 0;
 +}
 +#endif /* __FreeBSD__ */
 +
 +
 +/**
 + * get_netif_info - Get hw and IP addresses for network device
 + * @net_if: Selected network interface name
 + * @ip_addr: Buffer for returning IP address in network byte order
 + * @ip_addr_text: Buffer for returning a pointer to allocated IP address text
 + * @mac: Buffer for returning MAC address
 + * Returns: 0 on success, -1 on failure
 + */
 +int get_netif_info(const char *net_if, unsigned *ip_addr, char **ip_addr_text,
 +                 u8 mac[ETH_ALEN])
 +{
 +      struct ifreq req;
 +      int sock = -1;
 +      struct sockaddr_in *addr;
 +      struct in_addr in_addr;
 +
 +      *ip_addr_text = os_zalloc(16);
 +      if (*ip_addr_text == NULL)
 +              goto fail;
 +
 +      sock = socket(AF_INET, SOCK_DGRAM, 0);
 +      if (sock < 0)
 +              goto fail;
 +
 +      os_strlcpy(req.ifr_name, net_if, sizeof(req.ifr_name));
 +      if (ioctl(sock, SIOCGIFADDR, &req) < 0) {
 +              wpa_printf(MSG_ERROR, "WPS UPnP: SIOCGIFADDR failed: %d (%s)",
 +                         errno, strerror(errno));
 +              goto fail;
 +      }
 +      addr = (void *) &req.ifr_addr;
 +      *ip_addr = addr->sin_addr.s_addr;
 +      in_addr.s_addr = *ip_addr;
 +      os_snprintf(*ip_addr_text, 16, "%s", inet_ntoa(in_addr));
 +
 +#ifdef __linux__
 +      os_strlcpy(req.ifr_name, net_if, sizeof(req.ifr_name));
 +      if (ioctl(sock, SIOCGIFHWADDR, &req) < 0) {
 +              wpa_printf(MSG_ERROR, "WPS UPnP: SIOCGIFHWADDR failed: "
 +                         "%d (%s)", errno, strerror(errno));
 +              goto fail;
 +      }
 +      os_memcpy(mac, req.ifr_addr.sa_data, 6);
 +#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
 +      if (eth_get(net_if, mac) < 0) {
 +              wpa_printf(MSG_ERROR, "WPS UPnP: Failed to get MAC address");
 +              goto fail;
 +      }
 +#else
 +#error MAC address fetch not implemented
 +#endif
 +
 +      close(sock);
 +      return 0;
 +
 +fail:
 +      if (sock >= 0)
 +              close(sock);
 +      os_free(*ip_addr_text);
 +      *ip_addr_text = NULL;
 +      return -1;
 +}
 +
 +
 +static void upnp_wps_free_msearchreply(struct dl_list *head)
 +{
 +      struct advertisement_state_machine *a, *tmp;
 +      dl_list_for_each_safe(a, tmp, head, struct advertisement_state_machine,
 +                            list)
 +              msearchreply_state_machine_stop(a);
 +}
 +
 +
 +static void upnp_wps_free_subscriptions(struct dl_list *head,
 +                                      struct wps_registrar *reg)
 +{
 +      struct subscription *s, *tmp;
 +      dl_list_for_each_safe(s, tmp, head, struct subscription, list) {
 +              if (reg && s->reg != reg)
 +                      continue;
 +              dl_list_del(&s->list);
 +              subscription_destroy(s);
 +      }
 +}
 +
 +
 +/**
 + * upnp_wps_device_stop - Stop WPS UPnP operations on an interface
 + * @sm: WPS UPnP state machine from upnp_wps_device_init()
 + */
 +static void upnp_wps_device_stop(struct upnp_wps_device_sm *sm)
 +{
 +      if (!sm || !sm->started)
 +              return;
 +
 +      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);
 +
 +      advertisement_state_machine_stop(sm, 1);
 +
 +      event_send_stop_all(sm);
 +      os_free(sm->wlanevent);
 +      sm->wlanevent = NULL;
 +      os_free(sm->ip_addr_text);
 +      sm->ip_addr_text = NULL;
 +      if (sm->multicast_sd >= 0)
 +              close(sm->multicast_sd);
 +      sm->multicast_sd = -1;
 +
 +      sm->started = 0;
 +}
 +
 +
 +/**
 + * upnp_wps_device_start - Start WPS UPnP operations on an interface
 + * @sm: WPS UPnP state machine from upnp_wps_device_init()
 + * @net_if: Selected network interface name
 + * Returns: 0 on success, -1 on failure
 + */
 +static int upnp_wps_device_start(struct upnp_wps_device_sm *sm, char *net_if)
 +{
 +      if (!sm || !net_if)
 +              return -1;
 +
 +      if (sm->started)
 +              upnp_wps_device_stop(sm);
 +
 +      sm->multicast_sd = -1;
 +      sm->ssdp_sd = -1;
 +      sm->started = 1;
 +      sm->advertise_count = 0;
 +
 +      /* Fix up linux multicast handling */
 +      if (add_ssdp_network(net_if))
 +              goto fail;
 +
 +      /* Determine which IP and mac address we're using */
 +      if (get_netif_info(net_if, &sm->ip_addr, &sm->ip_addr_text,
 +                         sm->mac_addr)) {
 +              wpa_printf(MSG_INFO, "WPS UPnP: Could not get IP/MAC address "
 +                         "for %s. Does it have IP address?", net_if);
 +              goto fail;
 +      }
 +
 +      /* Listen for incoming TCP connections so that others
 +       * can fetch our "xml files" from us.
 +       */
 +      if (web_listener_start(sm))
 +              goto fail;
 +
 +      /* Set up for receiving discovery (UDP) packets */
 +      if (ssdp_listener_start(sm))
 +              goto fail;
 +
 +      /* Set up for sending multicast */
 +      if (ssdp_open_multicast(sm) < 0)
 +              goto fail;
 +
 +      /*
 +       * Broadcast NOTIFY messages to let the world know we exist.
 +       * This is done via a state machine since the messages should not be
 +       * all sent out at once.
 +       */
 +      if (advertisement_state_machine_start(sm))
 +              goto fail;
 +
 +      return 0;
 +
 +fail:
 +      upnp_wps_device_stop(sm);
 +      return -1;
 +}
 +
 +
 +static struct upnp_wps_device_interface *
 +upnp_wps_get_iface(struct upnp_wps_device_sm *sm, void *priv)
 +{
 +      struct upnp_wps_device_interface *iface;
 +      dl_list_for_each(iface, &sm->interfaces,
 +                       struct upnp_wps_device_interface, list) {
 +              if (iface->priv == priv)
 +                      return iface;
 +      }
 +      return NULL;
 +}
 +
 +
 +/**
 + * upnp_wps_device_deinit - Deinitialize WPS UPnP
 + * @sm: WPS UPnP state machine from upnp_wps_device_init()
 + * @priv: External context data that was used in upnp_wps_device_init() call
 + */
 +void upnp_wps_device_deinit(struct upnp_wps_device_sm *sm, void *priv)
 +{
 +      struct upnp_wps_device_interface *iface;
 +
 +      if (!sm)
 +              return;
 +
 +      iface = upnp_wps_get_iface(sm, priv);
 +      if (iface == NULL) {
 +              wpa_printf(MSG_ERROR, "WPS UPnP: Could not find the interface "
 +                         "instance to deinit");
 +              return;
 +      }
 +      wpa_printf(MSG_DEBUG, "WPS UPnP: Deinit interface instance %p", iface);
 +      if (dl_list_len(&sm->interfaces) == 1) {
 +              wpa_printf(MSG_DEBUG, "WPS UPnP: Deinitializing last instance "
 +                         "- free global device instance");
 +              upnp_wps_device_stop(sm);
 +      } else
 +              upnp_wps_free_subscriptions(&sm->subscriptions,
 +                                          iface->wps->registrar);
 +      dl_list_del(&iface->list);
 +
 +      if (iface->peer.wps)
 +              wps_deinit(iface->peer.wps);
 +      os_free(iface->ctx->ap_pin);
 +      os_free(iface->ctx);
 +      os_free(iface);
 +
 +      if (dl_list_empty(&sm->interfaces)) {
 +              os_free(sm->root_dir);
 +              os_free(sm->desc_url);
 +              os_free(sm);
 +              shared_upnp_device = NULL;
 +      }
 +}
 +
 +
 +/**
 + * upnp_wps_device_init - Initialize WPS UPnP
 + * @ctx: callback table; we must eventually free it
 + * @wps: Pointer to longterm WPS context
 + * @priv: External context data that will be used in callbacks
 + * @net_if: Selected network interface name
 + * Returns: WPS UPnP state or %NULL on failure
 + */
 +struct upnp_wps_device_sm *
 +upnp_wps_device_init(struct upnp_wps_device_ctx *ctx, struct wps_context *wps,
 +                   void *priv, char *net_if)
 +{
 +      struct upnp_wps_device_sm *sm;
 +      struct upnp_wps_device_interface *iface;
 +      int start = 0;
 +
 +      iface = os_zalloc(sizeof(*iface));
 +      if (iface == NULL) {
 +              os_free(ctx->ap_pin);
 +              os_free(ctx);
 +              return NULL;
 +      }
 +      wpa_printf(MSG_DEBUG, "WPS UPnP: Init interface instance %p", iface);
 +
 +      iface->ctx = ctx;
 +      iface->wps = wps;
 +      iface->priv = priv;
 +
 +      if (shared_upnp_device) {
 +              wpa_printf(MSG_DEBUG, "WPS UPnP: Share existing device "
 +                         "context");
 +              sm = shared_upnp_device;
 +      } else {
 +              wpa_printf(MSG_DEBUG, "WPS UPnP: Initialize device context");
 +              sm = os_zalloc(sizeof(*sm));
 +              if (!sm) {
 +                      wpa_printf(MSG_ERROR, "WPS UPnP: upnp_wps_device_init "
 +                                 "failed");
 +                      os_free(iface);
 +                      os_free(ctx->ap_pin);
 +                      os_free(ctx);
 +                      return NULL;
 +              }
 +              shared_upnp_device = sm;
 +
 +              dl_list_init(&sm->msearch_replies);
 +              dl_list_init(&sm->subscriptions);
 +              dl_list_init(&sm->interfaces);
 +              start = 1;
 +      }
 +
 +      dl_list_add(&sm->interfaces, &iface->list);
 +
 +      if (start && upnp_wps_device_start(sm, net_if)) {
 +              upnp_wps_device_deinit(sm, priv);
 +              return NULL;
 +      }
 +
 +
 +      return sm;
 +}
 +
 +
 +/**
 + * upnp_wps_subscribers - Check whether there are any event subscribers
 + * @sm: WPS UPnP state machine from upnp_wps_device_init()
 + * Returns: 0 if no subscribers, 1 if subscribers
 + */
 +int upnp_wps_subscribers(struct upnp_wps_device_sm *sm)
 +{
 +      return !dl_list_empty(&sm->subscriptions);
 +}
 +
 +
 +int upnp_wps_set_ap_pin(struct upnp_wps_device_sm *sm, const char *ap_pin)
 +{
 +      struct upnp_wps_device_interface *iface;
 +      if (sm == NULL)
 +              return 0;
 +
 +      dl_list_for_each(iface, &sm->interfaces,
 +                       struct upnp_wps_device_interface, list) {
 +              os_free(iface->ctx->ap_pin);
 +              if (ap_pin) {
 +                      iface->ctx->ap_pin = os_strdup(ap_pin);
 +                      if (iface->ctx->ap_pin == NULL)
 +                              return -1;
 +              } else
 +                      iface->ctx->ap_pin = NULL;
 +      }
 +
 +      return 0;
 +}
index 2949f141220b54aa5847a79bd46578251872af19,0000000000000000000000000000000000000000..cca390530a16938085184091a451d683d46ebaac
mode 100644,000000..100644
--- /dev/null
@@@ -1,85 -1,0 +1,83 @@@
-       if (wps_validate_upnp_set_selected_registrar(msg) < 0)
-               return -1;
-       if (wps_parse_msg(msg, &attr) < 0)
 +/*
 + * Wi-Fi Protected Setup - UPnP AP functionality
 + * 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 "common.h"
 +#include "eloop.h"
 +#include "uuid.h"
 +#include "wps_i.h"
 +#include "wps_upnp.h"
 +#include "wps_upnp_i.h"
 +
 +
 +static void upnp_er_set_selected_timeout(void *eloop_ctx, void *timeout_ctx)
 +{
 +      struct subscription *s = eloop_ctx;
 +      struct wps_registrar *reg = timeout_ctx;
 +      wpa_printf(MSG_DEBUG, "WPS: SetSelectedRegistrar from ER timed out");
 +      s->selected_registrar = 0;
 +      wps_registrar_selected_registrar_changed(reg, 0);
 +}
 +
 +
 +int upnp_er_set_selected_registrar(struct wps_registrar *reg,
 +                                 struct subscription *s,
 +                                 const struct wpabuf *msg)
 +{
 +      struct wps_parse_attr attr;
 +
 +      wpa_hexdump_buf(MSG_MSGDUMP, "WPS: SetSelectedRegistrar attributes",
 +                      msg);
++      if (wps_validate_upnp_set_selected_registrar(msg) < 0 ||
++          wps_parse_msg(msg, &attr) < 0)
 +              return -1;
 +
 +      s->reg = reg;
 +      eloop_cancel_timeout(upnp_er_set_selected_timeout, s, reg);
 +
 +      os_memset(s->authorized_macs, 0, sizeof(s->authorized_macs));
 +      if (attr.selected_registrar == NULL || *attr.selected_registrar == 0) {
 +              wpa_printf(MSG_DEBUG, "WPS: SetSelectedRegistrar: Disable "
 +                         "Selected Registrar");
 +              s->selected_registrar = 0;
 +      } else {
 +              s->selected_registrar = 1;
 +              s->dev_password_id = attr.dev_password_id ?
 +                      WPA_GET_BE16(attr.dev_password_id) : DEV_PW_DEFAULT;
 +              s->config_methods = attr.sel_reg_config_methods ?
 +                      WPA_GET_BE16(attr.sel_reg_config_methods) : -1;
 +              if (attr.authorized_macs) {
 +                      int count = attr.authorized_macs_len / ETH_ALEN;
 +                      if (count > WPS_MAX_AUTHORIZED_MACS)
 +                              count = WPS_MAX_AUTHORIZED_MACS;
 +                      os_memcpy(s->authorized_macs, attr.authorized_macs,
 +                                count * ETH_ALEN);
 +              } else if (!attr.version2) {
 +                      wpa_printf(MSG_DEBUG, "WPS: Add broadcast "
 +                                 "AuthorizedMACs for WPS 1.0 ER");
 +                      os_memset(s->authorized_macs, 0xff, ETH_ALEN);
 +              }
 +              eloop_register_timeout(WPS_PBC_WALK_TIME, 0,
 +                                     upnp_er_set_selected_timeout, s, reg);
 +      }
 +
 +      wps_registrar_selected_registrar_changed(reg, 0);
 +
 +      return 0;
 +}
 +
 +
 +void upnp_er_remove_notification(struct wps_registrar *reg,
 +                               struct subscription *s)
 +{
 +      s->selected_registrar = 0;
 +      eloop_cancel_timeout(upnp_er_set_selected_timeout, s, reg);
 +      if (reg)
 +              wps_registrar_selected_registrar_changed(reg, 0);
 +}
index 2c8ed4f13996befce092ee44dcdfd7c2fd0c96b0,0000000000000000000000000000000000000000..94aae7542b2e53ada96410cb96bd3f17304595e8
mode 100644,000000..100644
--- /dev/null
@@@ -1,423 -1,0 +1,421 @@@
-       if (dl_list_empty(&s->addr_list))
-               return -1;
-       if (s->current_event)
-               return -1;
-       if (dl_list_empty(&s->event_queue))
 +/*
 + * UPnP WPS Device - Event processing
 + * Copyright (c) 2000-2003 Intel Corporation
 + * Copyright (c) 2006-2007 Sony Corporation
 + * Copyright (c) 2008-2009 Atheros Communications
 + * Copyright (c) 2009-2010, Jouni Malinen <j@w1.fi>
 + *
 + * See wps_upnp.c for more details on licensing and code history.
 + */
 +
 +#include "includes.h"
 +#include <assert.h>
 +
 +#include "common.h"
 +#include "eloop.h"
 +#include "uuid.h"
 +#include "http_client.h"
 +#include "wps_defs.h"
 +#include "wps_upnp.h"
 +#include "wps_upnp_i.h"
 +
 +/*
 + * Event message generation (to subscribers)
 + *
 + * We make a separate copy for each message for each subscriber. This memory
 + * wasted could be limited (adding code complexity) by sharing copies, keeping
 + * a usage count and freeing when zero.
 + *
 + * Sending a message requires using a HTTP over TCP NOTIFY
 + * (like a PUT) which requires a number of states..
 + */
 +
 +#define MAX_EVENTS_QUEUED 20   /* How far behind queued events */
 +#define MAX_FAILURES 10 /* Drop subscription after this many failures */
 +
 +/* How long to wait before sending event */
 +#define EVENT_DELAY_SECONDS 0
 +#define EVENT_DELAY_MSEC 0
 +
 +/*
 + * Event information that we send to each subscriber is remembered in this
 + * struct. The event cannot be sent by simple UDP; it has to be sent by a HTTP
 + * over TCP transaction which requires various states.. It may also need to be
 + * retried at a different address (if more than one is available).
 + *
 + * TODO: As an optimization we could share data between subscribers.
 + */
 +struct wps_event_ {
 +      struct dl_list list;
 +      struct subscription *s;         /* parent */
 +      unsigned subscriber_sequence;   /* which event for this subscription*/
 +      unsigned int retry;             /* which retry */
 +      struct subscr_addr *addr;       /* address to connect to */
 +      struct wpabuf *data;            /* event data to send */
 +      struct http_client *http_event;
 +};
 +
 +
 +/* event_clean -- clean sockets etc. of event
 + * Leaves data, retry count etc. alone.
 + */
 +static void event_clean(struct wps_event_ *e)
 +{
 +      if (e->s->current_event == e)
 +              e->s->current_event = NULL;
 +      http_client_free(e->http_event);
 +      e->http_event = NULL;
 +}
 +
 +
 +/* event_delete -- delete single unqueued event
 + * (be sure to dequeue first if need be)
 + */
 +static void event_delete(struct wps_event_ *e)
 +{
 +      wpa_printf(MSG_DEBUG, "WPS UPnP: Delete event %p", e);
 +      event_clean(e);
 +      wpabuf_free(e->data);
 +      os_free(e);
 +}
 +
 +
 +/* event_dequeue -- get next event from the queue
 + * Returns NULL if empty.
 + */
 +static struct wps_event_ *event_dequeue(struct subscription *s)
 +{
 +      struct wps_event_ *e;
 +      e = dl_list_first(&s->event_queue, struct wps_event_, list);
 +      if (e) {
 +              wpa_printf(MSG_DEBUG, "WPS UPnP: Dequeue event %p for "
 +                         "subscription %p", e, s);
 +              dl_list_del(&e->list);
 +      }
 +      return e;
 +}
 +
 +
 +/* event_delete_all -- delete entire event queue and current event */
 +void event_delete_all(struct subscription *s)
 +{
 +      struct wps_event_ *e;
 +      while ((e = event_dequeue(s)) != NULL)
 +              event_delete(e);
 +      if (s->current_event) {
 +              event_delete(s->current_event);
 +              /* will set: s->current_event = NULL;  */
 +      }
 +}
 +
 +
 +/**
 + * event_retry - Called when we had a failure delivering event msg
 + * @e: Event
 + * @do_next_address: skip address e.g. on connect fail
 + */
 +static void event_retry(struct wps_event_ *e, int do_next_address)
 +{
 +      struct subscription *s = e->s;
 +      struct upnp_wps_device_sm *sm = s->sm;
 +
 +      wpa_printf(MSG_DEBUG, "WPS UPnP: Retry event %p for subscription %p",
 +                 e, s);
 +      event_clean(e);
 +      /* will set: s->current_event = NULL; */
 +
 +      if (do_next_address) {
 +              e->retry++;
 +              wpa_printf(MSG_DEBUG, "WPS UPnP: Try address %d", e->retry);
 +      }
 +      if (e->retry >= dl_list_len(&s->addr_list)) {
 +              wpa_printf(MSG_DEBUG, "WPS UPnP: Giving up on sending event "
 +                         "for %s", e->addr->domain_and_port);
 +              event_delete(e);
 +              s->last_event_failed = 1;
 +              if (!dl_list_empty(&s->event_queue))
 +                      event_send_all_later(s->sm);
 +              return;
 +      }
 +      dl_list_add(&s->event_queue, &e->list);
 +      event_send_all_later(sm);
 +}
 +
 +
 +static struct wpabuf * event_build_message(struct wps_event_ *e)
 +{
 +      struct wpabuf *buf;
 +      char *b;
 +
 +      buf = wpabuf_alloc(1000 + wpabuf_len(e->data));
 +      if (buf == NULL)
 +              return NULL;
 +      wpabuf_printf(buf, "NOTIFY %s HTTP/1.1\r\n", e->addr->path);
 +      wpabuf_put_str(buf, "SERVER: Unspecified, UPnP/1.0, Unspecified\r\n");
 +      wpabuf_printf(buf, "HOST: %s\r\n", e->addr->domain_and_port);
 +      wpabuf_put_str(buf, "CONTENT-TYPE: text/xml; charset=\"utf-8\"\r\n"
 +                     "NT: upnp:event\r\n"
 +                     "NTS: upnp:propchange\r\n");
 +      wpabuf_put_str(buf, "SID: uuid:");
 +      b = wpabuf_put(buf, 0);
 +      uuid_bin2str(e->s->uuid, b, 80);
 +      wpabuf_put(buf, os_strlen(b));
 +      wpabuf_put_str(buf, "\r\n");
 +      wpabuf_printf(buf, "SEQ: %u\r\n", e->subscriber_sequence);
 +      wpabuf_printf(buf, "CONTENT-LENGTH: %d\r\n",
 +                    (int) wpabuf_len(e->data));
 +      wpabuf_put_str(buf, "\r\n"); /* terminating empty line */
 +      wpabuf_put_buf(buf, e->data);
 +      return buf;
 +}
 +
 +
 +static void event_addr_failure(struct wps_event_ *e)
 +{
 +      struct subscription *s = e->s;
 +
 +      e->addr->num_failures++;
 +      wpa_printf(MSG_DEBUG, "WPS UPnP: Failed to send event %p to %s "
 +                 "(num_failures=%u)",
 +                 e, e->addr->domain_and_port, e->addr->num_failures);
 +
 +      if (e->addr->num_failures < MAX_FAILURES) {
 +              /* Try other addresses, if available */
 +              event_retry(e, 1);
 +              return;
 +      }
 +
 +      /*
 +       * If other side doesn't like what we say, forget about them.
 +       * (There is no way to tell other side that we are dropping them...).
 +       */
 +      wpa_printf(MSG_DEBUG, "WPS UPnP: Deleting subscription %p "
 +                 "address %s due to errors", s, e->addr->domain_and_port);
 +      dl_list_del(&e->addr->list);
 +      subscr_addr_delete(e->addr);
 +      e->addr = NULL;
 +
 +      if (dl_list_empty(&s->addr_list)) {
 +              /* if we've given up on all addresses */
 +              wpa_printf(MSG_DEBUG, "WPS UPnP: Removing subscription %p "
 +                         "with no addresses", s);
 +              dl_list_del(&s->list);
 +              subscription_destroy(s);
 +              return;
 +      }
 +
 +      /* Try other addresses, if available */
 +      event_retry(e, 0);
 +}
 +
 +
 +static void event_http_cb(void *ctx, struct http_client *c,
 +                        enum http_client_event event)
 +{
 +      struct wps_event_ *e = ctx;
 +      struct subscription *s = e->s;
 +
 +      wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP client callback: e=%p c=%p "
 +                 "event=%d", e, c, event);
 +      switch (event) {
 +      case HTTP_CLIENT_OK:
 +              wpa_printf(MSG_DEBUG,
 +                         "WPS UPnP: Got event %p reply OK from %s",
 +                         e, e->addr->domain_and_port);
 +              e->addr->num_failures = 0;
 +              s->last_event_failed = 0;
 +              event_delete(e);
 +
 +              /* Schedule sending more if there is more to send */
 +              if (!dl_list_empty(&s->event_queue))
 +                      event_send_all_later(s->sm);
 +              break;
 +      case HTTP_CLIENT_FAILED:
 +              wpa_printf(MSG_DEBUG, "WPS UPnP: Event send failure");
 +              event_addr_failure(e);
 +              break;
 +      case HTTP_CLIENT_INVALID_REPLY:
 +              wpa_printf(MSG_DEBUG, "WPS UPnP: Invalid reply");
 +              event_addr_failure(e);
 +              break;
 +      case HTTP_CLIENT_TIMEOUT:
 +              wpa_printf(MSG_DEBUG, "WPS UPnP: Event send timeout");
 +              event_addr_failure(e);
 +              break;
 +      }
 +}
 +
 +
 +/* event_send_start -- prepare to send a event message to subscriber
 + *
 + * This gets complicated because:
 + * -- The message is sent via TCP and we have to keep the stream open
 + *      for 30 seconds to get a response... then close it.
 + * -- But we might have other event happen in the meantime...
 + *      we have to queue them, if we lose them then the subscriber will
 + *      be forced to unsubscribe and subscribe again.
 + * -- If multiple URLs are provided then we are supposed to try successive
 + *      ones after 30 second timeout.
 + * -- The URLs might use domain names instead of dotted decimal addresses,
 + *      and resolution of those may cause unwanted sleeping.
 + * -- Doing the initial TCP connect can take a while, so we have to come
 + *      back after connection and then send the data.
 + *
 + * Returns nonzero on error;
 + *
 + * Prerequisite: No current event send (s->current_event == NULL)
 + *      and non-empty queue.
 + */
 +static int event_send_start(struct subscription *s)
 +{
 +      struct wps_event_ *e;
 +      unsigned int itry;
 +      struct wpabuf *buf;
 +
 +      /*
 +       * Assume we are called ONLY with no current event and ONLY with
 +       * nonempty event queue and ONLY with at least one address to send to.
 +       */
++      if (dl_list_empty(&s->addr_list) ||
++          s->current_event ||
++          dl_list_empty(&s->event_queue))
 +              return -1;
 +
 +      s->current_event = e = event_dequeue(s);
 +
 +      /* Use address according to number of retries */
 +      itry = 0;
 +      dl_list_for_each(e->addr, &s->addr_list, struct subscr_addr, list)
 +              if (itry++ == e->retry)
 +                      break;
 +      if (itry < e->retry)
 +              return -1;
 +
 +      buf = event_build_message(e);
 +      if (buf == NULL) {
 +              event_retry(e, 0);
 +              return -1;
 +      }
 +
 +      e->http_event = http_client_addr(&e->addr->saddr, buf, 0,
 +                                       event_http_cb, e);
 +      if (e->http_event == NULL) {
 +              wpabuf_free(buf);
 +              event_retry(e, 0);
 +              return -1;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +/* event_send_all_later_handler -- actually send events as needed */
 +static void event_send_all_later_handler(void *eloop_data, void *user_ctx)
 +{
 +      struct upnp_wps_device_sm *sm = user_ctx;
 +      struct subscription *s, *tmp;
 +      int nerrors = 0;
 +
 +      sm->event_send_all_queued = 0;
 +      dl_list_for_each_safe(s, tmp, &sm->subscriptions, struct subscription,
 +                            list) {
 +              if (s->current_event == NULL /* not busy */ &&
 +                  !dl_list_empty(&s->event_queue) /* more to do */) {
 +                      if (event_send_start(s))
 +                              nerrors++;
 +              }
 +      }
 +
 +      if (nerrors) {
 +              /* Try again later */
 +              event_send_all_later(sm);
 +      }
 +}
 +
 +
 +/* event_send_all_later -- schedule sending events to all subscribers
 + * that need it.
 + * This avoids two problems:
 + * -- After getting a subscription, we should not send the first event
 + *      until after our reply is fully queued to be sent back,
 + * -- Possible stack depth or infinite recursion issues.
 + */
 +void event_send_all_later(struct upnp_wps_device_sm *sm)
 +{
 +      /*
 +       * The exact time in the future isn't too important. Waiting a bit
 +       * might let us do several together.
 +       */
 +      if (sm->event_send_all_queued)
 +              return;
 +      sm->event_send_all_queued = 1;
 +      eloop_register_timeout(EVENT_DELAY_SECONDS, EVENT_DELAY_MSEC,
 +                             event_send_all_later_handler, NULL, sm);
 +}
 +
 +
 +/* event_send_stop_all -- cleanup */
 +void event_send_stop_all(struct upnp_wps_device_sm *sm)
 +{
 +      if (sm->event_send_all_queued)
 +              eloop_cancel_timeout(event_send_all_later_handler, NULL, sm);
 +      sm->event_send_all_queued = 0;
 +}
 +
 +
 +/**
 + * event_add - Add a new event to a queue
 + * @s: Subscription
 + * @data: Event data (is copied; caller retains ownership)
 + * @probereq: Whether this is a Probe Request event
 + * Returns: 0 on success, -1 on error, 1 on max event queue limit reached
 + */
 +int event_add(struct subscription *s, const struct wpabuf *data, int probereq)
 +{
 +      struct wps_event_ *e;
 +      unsigned int len;
 +
 +      len = dl_list_len(&s->event_queue);
 +      if (len >= MAX_EVENTS_QUEUED) {
 +              wpa_printf(MSG_DEBUG, "WPS UPnP: Too many events queued for "
 +                         "subscriber %p", s);
 +              if (probereq)
 +                      return 1;
 +
 +              /* Drop oldest entry to allow EAP event to be stored. */
 +              e = event_dequeue(s);
 +              if (!e)
 +                      return 1;
 +              event_delete(e);
 +      }
 +
 +      if (s->last_event_failed && probereq && len > 0) {
 +              /*
 +               * Avoid queuing frames for subscribers that may have left
 +               * without unsubscribing.
 +               */
 +              wpa_printf(MSG_DEBUG, "WPS UPnP: Do not queue more Probe "
 +                         "Request frames for subscription %p since last "
 +                         "delivery failed", s);
 +              return -1;
 +      }
 +
 +      e = os_zalloc(sizeof(*e));
 +      if (e == NULL)
 +              return -1;
 +      dl_list_init(&e->list);
 +      e->s = s;
 +      e->data = wpabuf_dup(data);
 +      if (e->data == NULL) {
 +              os_free(e);
 +              return -1;
 +      }
 +      e->subscriber_sequence = s->next_subscriber_sequence++;
 +      if (s->next_subscriber_sequence == 0)
 +              s->next_subscriber_sequence++;
 +      wpa_printf(MSG_DEBUG, "WPS UPnP: Queue event %p for subscriber %p "
 +                 "(queue len %u)", e, s, len + 1);
 +      dl_list_add_tail(&s->event_queue, &e->list);
 +      event_send_all_later(s->sm);
 +      return 0;
 +}
index 26a740d252243f3679166242dfe50996bb6162b0,0000000000000000000000000000000000000000..968fc03f92e7c654c24527a46a985df3c5f1b87a
mode 100644,000000..100644
--- /dev/null
@@@ -1,955 -1,0 +1,948 @@@
-               goto fail;
 +/*
 + * UPnP SSDP for WPS
 + * Copyright (c) 2000-2003 Intel Corporation
 + * Copyright (c) 2006-2007 Sony Corporation
 + * Copyright (c) 2008-2009 Atheros Communications
 + * Copyright (c) 2009-2013, Jouni Malinen <j@w1.fi>
 + *
 + * See wps_upnp.c for more details on licensing and code history.
 + */
 +
 +#include "includes.h"
 +
 +#include <fcntl.h>
 +#include <sys/ioctl.h>
 +#include <net/route.h>
 +#ifdef __linux__
 +#include <net/if.h>
 +#endif /* __linux__ */
 +
 +#include "common.h"
 +#include "uuid.h"
 +#include "eloop.h"
 +#include "wps.h"
 +#include "wps_upnp.h"
 +#include "wps_upnp_i.h"
 +
 +#define UPNP_CACHE_SEC (UPNP_CACHE_SEC_MIN + 1) /* cache time we use */
 +#define UPNP_CACHE_SEC_MIN 1800 /* min cachable time per UPnP standard */
 +#define UPNP_ADVERTISE_REPEAT 2 /* no more than 3 */
 +#define MAX_MSEARCH 20          /* max simultaneous M-SEARCH replies ongoing */
 +#define SSDP_TARGET  "239.0.0.0"
 +#define SSDP_NETMASK "255.0.0.0"
 +
 +
 +/* Check tokens for equality, where tokens consist of letters, digits,
 + * underscore and hyphen, and are matched case insensitive.
 + */
 +static int token_eq(const char *s1, const char *s2)
 +{
 +      int c1;
 +      int c2;
 +      int end1 = 0;
 +      int end2 = 0;
 +      for (;;) {
 +              c1 = *s1++;
 +              c2 = *s2++;
 +              if (isalpha(c1) && isupper(c1))
 +                      c1 = tolower(c1);
 +              if (isalpha(c2) && isupper(c2))
 +                      c2 = tolower(c2);
 +              end1 = !(isalnum(c1) || c1 == '_' || c1 == '-');
 +              end2 = !(isalnum(c2) || c2 == '_' || c2 == '-');
 +              if (end1 || end2 || c1 != c2)
 +                      break;
 +      }
 +      return end1 && end2; /* reached end of both words? */
 +}
 +
 +
 +/* Return length of token (see above for definition of token) */
 +static int token_length(const char *s)
 +{
 +      const char *begin = s;
 +      for (;; s++) {
 +              int c = *s;
 +              int end = !(isalnum(c) || c == '_' || c == '-');
 +              if (end)
 +                      break;
 +      }
 +      return s - begin;
 +}
 +
 +
 +/* return length of interword separation.
 + * This accepts only spaces/tabs and thus will not traverse a line
 + * or buffer ending.
 + */
 +static int word_separation_length(const char *s)
 +{
 +      const char *begin = s;
 +      for (;; s++) {
 +              int c = *s;
 +              if (c == ' ' || c == '\t')
 +                      continue;
 +              break;
 +      }
 +      return s - begin;
 +}
 +
 +
 +/* No. of chars through (including) end of line */
 +static int line_length(const char *l)
 +{
 +      const char *lp = l;
 +      while (*lp && *lp != '\n')
 +              lp++;
 +      if (*lp == '\n')
 +              lp++;
 +      return lp - l;
 +}
 +
 +
 +static int str_starts(const char *str, const char *start)
 +{
 +      return os_strncmp(str, start, os_strlen(start)) == 0;
 +}
 +
 +
 +/***************************************************************************
 + * Advertisements.
 + * These are multicast to the world to tell them we are here.
 + * The individual packets are spread out in time to limit loss,
 + * and then after a much longer period of time the whole sequence
 + * is repeated again (for NOTIFYs only).
 + **************************************************************************/
 +
 +/**
 + * next_advertisement - Build next message and advance the state machine
 + * @a: Advertisement state
 + * @islast: Buffer for indicating whether this is the last message (= 1)
 + * Returns: The new message (caller is responsible for freeing this)
 + *
 + * Note: next_advertisement is shared code with msearchreply_* functions
 + */
 +static struct wpabuf *
 +next_advertisement(struct upnp_wps_device_sm *sm,
 +                 struct advertisement_state_machine *a, int *islast)
 +{
 +      struct wpabuf *msg;
 +      char *NTString = "";
 +      char uuid_string[80];
 +      struct upnp_wps_device_interface *iface;
 +
 +      *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)
- fail:
-       wpabuf_free(msg);
-       return NULL;
++              return NULL;
 +      switch (a->type) {
 +      case ADVERTISE_UP:
 +      case ADVERTISE_DOWN:
 +              NTString = "NT";
 +              wpabuf_put_str(msg, "NOTIFY * HTTP/1.1\r\n");
 +              wpabuf_printf(msg, "HOST: %s:%d\r\n",
 +                            UPNP_MULTICAST_ADDRESS, UPNP_MULTICAST_PORT);
 +              wpabuf_printf(msg, "CACHE-CONTROL: max-age=%d\r\n",
 +                            UPNP_CACHE_SEC);
 +              wpabuf_printf(msg, "NTS: %s\r\n",
 +                            (a->type == ADVERTISE_UP ?
 +                             "ssdp:alive" : "ssdp:byebye"));
 +              break;
 +      case MSEARCH_REPLY:
 +              NTString = "ST";
 +              wpabuf_put_str(msg, "HTTP/1.1 200 OK\r\n");
 +              wpabuf_printf(msg, "CACHE-CONTROL: max-age=%d\r\n",
 +                            UPNP_CACHE_SEC);
 +
 +              wpabuf_put_str(msg, "DATE: ");
 +              format_date(msg);
 +              wpabuf_put_str(msg, "\r\n");
 +
 +              wpabuf_put_str(msg, "EXT:\r\n");
 +              break;
 +      }
 +
 +      if (a->type != ADVERTISE_DOWN) {
 +              /* Where others may get our XML files from */
 +              wpabuf_printf(msg, "LOCATION: http://%s:%d/%s\r\n",
 +                            sm->ip_addr_text, sm->web_port,
 +                            UPNP_WPS_DEVICE_XML_FILE);
 +      }
 +
 +      /* The SERVER line has three comma-separated fields:
 +       *      operating system / version
 +       *      upnp version
 +       *      software package / version
 +       * However, only the UPnP version is really required, the
 +       * others can be place holders... for security reasons
 +       * it is better to NOT provide extra information.
 +       */
 +      wpabuf_put_str(msg, "SERVER: Unspecified, UPnP/1.0, Unspecified\r\n");
 +
 +      switch (a->state / UPNP_ADVERTISE_REPEAT) {
 +      case 0:
 +              wpabuf_printf(msg, "%s: upnp:rootdevice\r\n", NTString);
 +              wpabuf_printf(msg, "USN: uuid:%s::upnp:rootdevice\r\n",
 +                            uuid_string);
 +              break;
 +      case 1:
 +              wpabuf_printf(msg, "%s: uuid:%s\r\n", NTString, uuid_string);
 +              wpabuf_printf(msg, "USN: uuid:%s\r\n", uuid_string);
 +              break;
 +      case 2:
 +              wpabuf_printf(msg, "%s: urn:schemas-wifialliance-org:device:"
 +                            "WFADevice:1\r\n", NTString);
 +              wpabuf_printf(msg, "USN: uuid:%s::urn:schemas-wifialliance-"
 +                            "org:device:WFADevice:1\r\n", uuid_string);
 +              break;
 +      case 3:
 +              wpabuf_printf(msg, "%s: urn:schemas-wifialliance-org:service:"
 +                            "WFAWLANConfig:1\r\n", NTString);
 +              wpabuf_printf(msg, "USN: uuid:%s::urn:schemas-wifialliance-"
 +                            "org:service:WFAWLANConfig:1\r\n", uuid_string);
 +              break;
 +      }
 +      wpabuf_put_str(msg, "\r\n");
 +
 +      if (a->state + 1 >= 4 * UPNP_ADVERTISE_REPEAT)
 +              *islast = 1;
 +
 +      return msg;
-       if (sd < 0)
-               goto fail;
-       if (fcntl(sd, F_SETFL, O_NONBLOCK) != 0)
-               goto fail;
-       if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)))
 +}
 +
 +
 +static void advertisement_state_machine_handler(void *eloop_data,
 +                                              void *user_ctx);
 +
 +
 +/**
 + * advertisement_state_machine_stop - Stop SSDP advertisements
 + * @sm: WPS UPnP state machine from upnp_wps_device_init()
 + * @send_byebye: Send byebye advertisement messages immediately
 + */
 +void advertisement_state_machine_stop(struct upnp_wps_device_sm *sm,
 +                                    int send_byebye)
 +{
 +      struct advertisement_state_machine *a = &sm->advertisement;
 +      int islast = 0;
 +      struct wpabuf *msg;
 +      struct sockaddr_in dest;
 +
 +      eloop_cancel_timeout(advertisement_state_machine_handler, NULL, sm);
 +      if (!send_byebye || sm->multicast_sd < 0)
 +              return;
 +
 +      a->type = ADVERTISE_DOWN;
 +      a->state = 0;
 +
 +      os_memset(&dest, 0, sizeof(dest));
 +      dest.sin_family = AF_INET;
 +      dest.sin_addr.s_addr = inet_addr(UPNP_MULTICAST_ADDRESS);
 +      dest.sin_port = htons(UPNP_MULTICAST_PORT);
 +
 +      while (!islast) {
 +              msg = next_advertisement(sm, a, &islast);
 +              if (msg == NULL)
 +                      break;
 +              if (sendto(sm->multicast_sd, wpabuf_head(msg), wpabuf_len(msg),
 +                         0, (struct sockaddr *) &dest, sizeof(dest)) < 0) {
 +                      wpa_printf(MSG_INFO, "WPS UPnP: Advertisement sendto "
 +                                 "failed: %d (%s)", errno, strerror(errno));
 +              }
 +              wpabuf_free(msg);
 +              a->state++;
 +      }
 +}
 +
 +
 +static void advertisement_state_machine_handler(void *eloop_data,
 +                                              void *user_ctx)
 +{
 +      struct upnp_wps_device_sm *sm = user_ctx;
 +      struct advertisement_state_machine *a = &sm->advertisement;
 +      struct wpabuf *msg;
 +      int next_timeout_msec = 100;
 +      int next_timeout_sec = 0;
 +      struct sockaddr_in dest;
 +      int islast = 0;
 +
 +      /*
 +       * Each is sent twice (in case lost) w/ 100 msec delay between;
 +       * spec says no more than 3 times.
 +       * One pair for rootdevice, one pair for uuid, and a pair each for
 +       * each of the two urns.
 +       * The entire sequence must be repeated before cache control timeout
 +       * (which  is min  1800 seconds),
 +       * recommend random portion of half of the advertised cache control age
 +       * to ensure against loss... perhaps 1800/4 + rand*1800/4 ?
 +       * Delay random interval < 100 msec prior to initial sending.
 +       * TTL of 4
 +       */
 +
 +      wpa_printf(MSG_MSGDUMP, "WPS UPnP: Advertisement state=%d", a->state);
 +      msg = next_advertisement(sm, a, &islast);
 +      if (msg == NULL)
 +              return;
 +
 +      os_memset(&dest, 0, sizeof(dest));
 +      dest.sin_family = AF_INET;
 +      dest.sin_addr.s_addr = inet_addr(UPNP_MULTICAST_ADDRESS);
 +      dest.sin_port = htons(UPNP_MULTICAST_PORT);
 +
 +      if (sendto(sm->multicast_sd, wpabuf_head(msg), wpabuf_len(msg), 0,
 +                 (struct sockaddr *) &dest, sizeof(dest)) == -1) {
 +              wpa_printf(MSG_ERROR, "WPS UPnP: Advertisement sendto failed:"
 +                         "%d (%s)", errno, strerror(errno));
 +              next_timeout_msec = 0;
 +              next_timeout_sec = 10; /* ... later */
 +      } else if (islast) {
 +              a->state = 0; /* wrap around */
 +              if (a->type == ADVERTISE_DOWN) {
 +                      wpa_printf(MSG_DEBUG, "WPS UPnP: ADVERTISE_DOWN->UP");
 +                      a->type = ADVERTISE_UP;
 +                      /* do it all over again right away */
 +              } else {
 +                      u16 r;
 +                      /*
 +                       * Start over again after a long timeout
 +                       * (see notes above)
 +                       */
 +                      next_timeout_msec = 0;
 +                      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++;
 +                      wpa_printf(MSG_DEBUG, "WPS UPnP: ADVERTISE_UP (#%u); "
 +                                 "next in %d sec",
 +                                 sm->advertise_count, next_timeout_sec);
 +              }
 +      } else {
 +              a->state++;
 +      }
 +
 +      wpabuf_free(msg);
 +
 +      eloop_register_timeout(next_timeout_sec, next_timeout_msec,
 +                             advertisement_state_machine_handler, NULL, sm);
 +}
 +
 +
 +/**
 + * advertisement_state_machine_start - Start SSDP advertisements
 + * @sm: WPS UPnP state machine from upnp_wps_device_init()
 + * Returns: 0 on success, -1 on failure
 + */
 +int advertisement_state_machine_start(struct upnp_wps_device_sm *sm)
 +{
 +      struct advertisement_state_machine *a = &sm->advertisement;
 +      int next_timeout_msec;
 +
 +      advertisement_state_machine_stop(sm, 0);
 +
 +      /*
 +       * Start out advertising down, this automatically switches
 +       * to advertising up which signals our restart.
 +       */
 +      a->type = ADVERTISE_DOWN;
 +      a->state = 0;
 +      /* (other fields not used here) */
 +
 +      /* First timeout should be random interval < 100 msec */
 +      next_timeout_msec = (100 * (os_random() & 0xFF)) >> 8;
 +      return eloop_register_timeout(0, next_timeout_msec,
 +                                    advertisement_state_machine_handler,
 +                                    NULL, sm);
 +}
 +
 +
 +/***************************************************************************
 + * M-SEARCH replies
 + * These are very similar to the multicast advertisements, with some
 + * small changes in data content; and they are sent (UDP) to a specific
 + * unicast address instead of multicast.
 + * They are sent in response to a UDP M-SEARCH packet.
 + **************************************************************************/
 +
 +/**
 + * msearchreply_state_machine_stop - Stop M-SEARCH reply state machine
 + * @a: Selected advertisement/reply state
 + */
 +void msearchreply_state_machine_stop(struct advertisement_state_machine *a)
 +{
 +      wpa_printf(MSG_DEBUG, "WPS UPnP: M-SEARCH stop");
 +      dl_list_del(&a->list);
 +      os_free(a);
 +}
 +
 +
 +static void msearchreply_state_machine_handler(void *eloop_data,
 +                                             void *user_ctx)
 +{
 +      struct advertisement_state_machine *a = user_ctx;
 +      struct upnp_wps_device_sm *sm = eloop_data;
 +      struct wpabuf *msg;
 +      int next_timeout_msec = 100;
 +      int next_timeout_sec = 0;
 +      int islast = 0;
 +
 +      /*
 +       * Each response is sent twice (in case lost) w/ 100 msec delay
 +       * between; spec says no more than 3 times.
 +       * One pair for rootdevice, one pair for uuid, and a pair each for
 +       * each of the two urns.
 +       */
 +
 +      /* TODO: should only send the requested response types */
 +
 +      wpa_printf(MSG_MSGDUMP, "WPS UPnP: M-SEARCH reply state=%d (%s:%d)",
 +                 a->state, inet_ntoa(a->client.sin_addr),
 +                 ntohs(a->client.sin_port));
 +      msg = next_advertisement(sm, a, &islast);
 +      if (msg == NULL)
 +              return;
 +
 +      /*
 +       * Send it on the multicast socket to avoid having to set up another
 +       * socket.
 +       */
 +      if (sendto(sm->multicast_sd, wpabuf_head(msg), wpabuf_len(msg), 0,
 +                 (struct sockaddr *) &a->client, sizeof(a->client)) < 0) {
 +              wpa_printf(MSG_DEBUG, "WPS UPnP: M-SEARCH reply sendto "
 +                         "errno %d (%s) for %s:%d",
 +                         errno, strerror(errno),
 +                         inet_ntoa(a->client.sin_addr),
 +                         ntohs(a->client.sin_port));
 +              /* Ignore error and hope for the best */
 +      }
 +      wpabuf_free(msg);
 +      if (islast) {
 +              wpa_printf(MSG_DEBUG, "WPS UPnP: M-SEARCH reply done");
 +              msearchreply_state_machine_stop(a);
 +              return;
 +      }
 +      a->state++;
 +
 +      wpa_printf(MSG_MSGDUMP, "WPS UPnP: M-SEARCH reply in %d.%03d sec",
 +                 next_timeout_sec, next_timeout_msec);
 +      eloop_register_timeout(next_timeout_sec, next_timeout_msec,
 +                             msearchreply_state_machine_handler, sm, a);
 +}
 +
 +
 +/**
 + * msearchreply_state_machine_start - Reply to M-SEARCH discovery request
 + * @sm: WPS UPnP state machine from upnp_wps_device_init()
 + * @client: Client address
 + * @mx: Maximum delay in seconds
 + *
 + * Use TTL of 4 (this was done when socket set up).
 + * A response should be given in randomized portion of min(MX,120) seconds
 + *
 + * UPnP-arch-DeviceArchitecture, 1.2.3:
 + * To be found, a device must send a UDP response to the source IP address and
 + * port that sent the request to the multicast channel. Devices respond if the
 + * ST header of the M-SEARCH request is "ssdp:all", "upnp:rootdevice", "uuid:"
 + * followed by a UUID that exactly matches one advertised by the device.
 + */
 +static void msearchreply_state_machine_start(struct upnp_wps_device_sm *sm,
 +                                           struct sockaddr_in *client,
 +                                           int mx)
 +{
 +      struct advertisement_state_machine *a;
 +      int next_timeout_sec;
 +      int next_timeout_msec;
 +      int replies;
 +
 +      replies = dl_list_len(&sm->msearch_replies);
 +      wpa_printf(MSG_DEBUG, "WPS UPnP: M-SEARCH reply start (%d "
 +                 "outstanding)", replies);
 +      if (replies >= MAX_MSEARCH) {
 +              wpa_printf(MSG_INFO, "WPS UPnP: Too many outstanding "
 +                         "M-SEARCH replies");
 +              return;
 +      }
 +
 +      a = os_zalloc(sizeof(*a));
 +      if (a == NULL)
 +              return;
 +      a->type = MSEARCH_REPLY;
 +      a->state = 0;
 +      os_memcpy(&a->client, client, sizeof(*client));
 +      /* Wait time depending on MX value */
 +      next_timeout_msec = (1000 * mx * (os_random() & 0xFF)) >> 8;
 +      next_timeout_sec = next_timeout_msec / 1000;
 +      next_timeout_msec = next_timeout_msec % 1000;
 +      if (eloop_register_timeout(next_timeout_sec, next_timeout_msec,
 +                                 msearchreply_state_machine_handler, sm,
 +                                 a)) {
 +              /* No way to recover (from malloc failure) */
 +              goto fail;
 +      }
 +      /* Remember for future cleanup */
 +      dl_list_add(&sm->msearch_replies, &a->list);
 +      return;
 +
 +fail:
 +      wpa_printf(MSG_INFO, "WPS UPnP: M-SEARCH reply failure!");
 +      eloop_cancel_timeout(msearchreply_state_machine_handler, sm, a);
 +      os_free(a);
 +}
 +
 +
 +/**
 + * ssdp_parse_msearch - Process a received M-SEARCH
 + * @sm: WPS UPnP state machine from upnp_wps_device_init()
 + * @client: Client address
 + * @data: NULL terminated M-SEARCH message
 + *
 + * Given that we have received a header w/ M-SEARCH, act upon it
 + *
 + * Format of M-SEARCH (case insensitive!):
 + *
 + * First line must be:
 + *      M-SEARCH * HTTP/1.1
 + * Other lines in arbitrary order:
 + *      HOST:239.255.255.250:1900
 + *      ST:<varies -- must match>
 + *      MAN:"ssdp:discover"
 + *      MX:<varies>
 + *
 + * It should be noted that when Microsoft Vista is still learning its IP
 + * address, it sends out host lines like: HOST:[FF02::C]:1900
 + */
 +static void ssdp_parse_msearch(struct upnp_wps_device_sm *sm,
 +                             struct sockaddr_in *client, const char *data)
 +{
 +#ifndef CONFIG_NO_STDOUT_DEBUG
 +      const char *start = data;
 +#endif /* CONFIG_NO_STDOUT_DEBUG */
 +      int got_host = 0;
 +      int got_st = 0, st_match = 0;
 +      int got_man = 0;
 +      int got_mx = 0;
 +      int mx = 0;
 +
 +      /*
 +       * Skip first line M-SEARCH * HTTP/1.1
 +       * (perhaps we should check remainder of the line for syntax)
 +       */
 +      data += line_length(data);
 +
 +      /* Parse remaining lines */
 +      for (; *data != '\0'; data += line_length(data)) {
 +              if (token_eq(data, "host")) {
 +                      /* The host line indicates who the packet
 +                       * is addressed to... but do we really care?
 +                       * Note that Microsoft sometimes does funny
 +                       * stuff with the HOST: line.
 +                       */
 +#if 0   /* could be */
 +                      data += token_length(data);
 +                      data += word_separation_length(data);
 +                      if (*data != ':')
 +                              goto bad;
 +                      data++;
 +                      data += word_separation_length(data);
 +                      /* UPNP_MULTICAST_ADDRESS */
 +                      if (!str_starts(data, "239.255.255.250"))
 +                              goto bad;
 +                      data += os_strlen("239.255.255.250");
 +                      if (*data == ':') {
 +                              if (!str_starts(data, ":1900"))
 +                                      goto bad;
 +                      }
 +#endif  /* could be */
 +                      got_host = 1;
 +                      continue;
 +              } else if (token_eq(data, "st")) {
 +                      /* There are a number of forms; we look
 +                       * for one that matches our case.
 +                       */
 +                      got_st = 1;
 +                      data += token_length(data);
 +                      data += word_separation_length(data);
 +                      if (*data != ':')
 +                              continue;
 +                      data++;
 +                      data += word_separation_length(data);
 +                      if (str_starts(data, "ssdp:all")) {
 +                              st_match = 1;
 +                              continue;
 +                      }
 +                      if (str_starts(data, "upnp:rootdevice")) {
 +                              st_match = 1;
 +                              continue;
 +                      }
 +                      if (str_starts(data, "uuid:")) {
 +                              char uuid_string[80];
 +                              struct upnp_wps_device_interface *iface;
 +                              iface = dl_list_first(
 +                                      &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));
 +                              if (str_starts(data, uuid_string))
 +                                      st_match = 1;
 +                              continue;
 +                      }
 +#if 0
 +                      /* FIX: should we really reply to IGD string? */
 +                      if (str_starts(data, "urn:schemas-upnp-org:device:"
 +                                     "InternetGatewayDevice:1")) {
 +                              st_match = 1;
 +                              continue;
 +                      }
 +#endif
 +                      if (str_starts(data, "urn:schemas-wifialliance-org:"
 +                                     "service:WFAWLANConfig:1")) {
 +                              st_match = 1;
 +                              continue;
 +                      }
 +                      if (str_starts(data, "urn:schemas-wifialliance-org:"
 +                                     "device:WFADevice:1")) {
 +                              st_match = 1;
 +                              continue;
 +                      }
 +                      continue;
 +              } else if (token_eq(data, "man")) {
 +                      data += token_length(data);
 +                      data += word_separation_length(data);
 +                      if (*data != ':')
 +                              continue;
 +                      data++;
 +                      data += word_separation_length(data);
 +                      if (!str_starts(data, "\"ssdp:discover\"")) {
 +                              wpa_printf(MSG_DEBUG, "WPS UPnP: Unexpected "
 +                                         "M-SEARCH man-field");
 +                              goto bad;
 +                      }
 +                      got_man = 1;
 +                      continue;
 +              } else if (token_eq(data, "mx")) {
 +                      data += token_length(data);
 +                      data += word_separation_length(data);
 +                      if (*data != ':')
 +                              continue;
 +                      data++;
 +                      data += word_separation_length(data);
 +                      mx = atol(data);
 +                      got_mx = 1;
 +                      continue;
 +              }
 +              /* ignore anything else */
 +      }
 +      if (!got_host || !got_st || !got_man || !got_mx || mx < 0) {
 +              wpa_printf(MSG_DEBUG, "WPS UPnP: Invalid M-SEARCH: %d %d %d "
 +                         "%d mx=%d", got_host, got_st, got_man, got_mx, mx);
 +              goto bad;
 +      }
 +      if (!st_match) {
 +              wpa_printf(MSG_DEBUG, "WPS UPnP: Ignored M-SEARCH (no ST "
 +                         "match)");
 +              return;
 +      }
 +      if (mx > 120)
 +              mx = 120; /* UPnP-arch-DeviceArchitecture, 1.2.3 */
 +      msearchreply_state_machine_start(sm, client, mx);
 +      return;
 +
 +bad:
 +      wpa_printf(MSG_INFO, "WPS UPnP: Failed to parse M-SEARCH");
 +      wpa_printf(MSG_MSGDUMP, "WPS UPnP: M-SEARCH data:\n%s", start);
 +}
 +
 +
 +/* Listening for (UDP) discovery (M-SEARCH) packets */
 +
 +/**
 + * ssdp_listener_stop - Stop SSDP listered
 + * @sm: WPS UPnP state machine from upnp_wps_device_init()
 + *
 + * This function stops the SSDP listener that was started by calling
 + * ssdp_listener_start().
 + */
 +void ssdp_listener_stop(struct upnp_wps_device_sm *sm)
 +{
 +      if (sm->ssdp_sd_registered) {
 +              eloop_unregister_sock(sm->ssdp_sd, EVENT_TYPE_READ);
 +              sm->ssdp_sd_registered = 0;
 +      }
 +
 +      if (sm->ssdp_sd != -1) {
 +              close(sm->ssdp_sd);
 +              sm->ssdp_sd = -1;
 +      }
 +
 +      eloop_cancel_timeout(msearchreply_state_machine_handler, sm,
 +                           ELOOP_ALL_CTX);
 +}
 +
 +
 +static void ssdp_listener_handler(int sd, void *eloop_ctx, void *sock_ctx)
 +{
 +      struct upnp_wps_device_sm *sm = sock_ctx;
 +      struct sockaddr_in addr; /* client address */
 +      socklen_t addr_len;
 +      int nread;
 +      char buf[MULTICAST_MAX_READ], *pos;
 +
 +      addr_len = sizeof(addr);
 +      nread = recvfrom(sm->ssdp_sd, buf, sizeof(buf) - 1, 0,
 +                       (struct sockaddr *) &addr, &addr_len);
 +      if (nread <= 0)
 +              return;
 +      buf[nread] = '\0'; /* need null termination for algorithm */
 +
 +      if (str_starts(buf, "NOTIFY ")) {
 +              /*
 +               * Silently ignore NOTIFYs to avoid filling debug log with
 +               * unwanted messages.
 +               */
 +              return;
 +      }
 +
 +      pos = os_strchr(buf, '\n');
 +      if (pos)
 +              *pos = '\0';
 +      wpa_printf(MSG_MSGDUMP, "WPS UPnP: Received SSDP packet from %s:%d: "
 +                 "%s", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port), buf);
 +      if (pos)
 +              *pos = '\n';
 +
 +      /* Parse first line */
 +      if (os_strncasecmp(buf, "M-SEARCH", os_strlen("M-SEARCH")) == 0 &&
 +          !isgraph(buf[strlen("M-SEARCH")])) {
 +              ssdp_parse_msearch(sm, &addr, buf);
 +              return;
 +      }
 +
 +      /* Ignore anything else */
 +}
 +
 +
 +int ssdp_listener_open(void)
 +{
 +      struct sockaddr_in addr;
 +      struct ip_mreq mcast_addr;
 +      int on = 1;
 +      /* per UPnP spec, keep IP packet time to live (TTL) small */
 +      unsigned char ttl = 4;
 +      int sd;
 +
 +      sd = socket(AF_INET, SOCK_DGRAM, 0);
-                      (char *) &mcast_addr, sizeof(mcast_addr)))
-               goto fail;
-       if (setsockopt(sd, IPPROTO_IP, IP_MULTICAST_TTL,
++      if (sd < 0 ||
++          fcntl(sd, F_SETFL, O_NONBLOCK) != 0 ||
++          setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)))
 +              goto fail;
 +      os_memset(&addr, 0, sizeof(addr));
 +      addr.sin_family = AF_INET;
 +      addr.sin_addr.s_addr = htonl(INADDR_ANY);
 +      addr.sin_port = htons(UPNP_MULTICAST_PORT);
 +      if (bind(sd, (struct sockaddr *) &addr, sizeof(addr)))
 +              goto fail;
 +      os_memset(&mcast_addr, 0, sizeof(mcast_addr));
 +      mcast_addr.imr_interface.s_addr = htonl(INADDR_ANY);
 +      mcast_addr.imr_multiaddr.s_addr = inet_addr(UPNP_MULTICAST_ADDRESS);
 +      if (setsockopt(sd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
++                     (char *) &mcast_addr, sizeof(mcast_addr)) ||
++          setsockopt(sd, IPPROTO_IP, IP_MULTICAST_TTL,
 +                     &ttl, sizeof(ttl)))
 +              goto fail;
 +
 +      return sd;
 +
 +fail:
 +      if (sd >= 0)
 +              close(sd);
 +      return -1;
 +}
 +
 +
 +/**
 + * ssdp_listener_start - Set up for receiving discovery (UDP) packets
 + * @sm: WPS UPnP state machine from upnp_wps_device_init()
 + * Returns: 0 on success, -1 on failure
 + *
 + * The SSDP listener is stopped by calling ssdp_listener_stop().
 + */
 +int ssdp_listener_start(struct upnp_wps_device_sm *sm)
 +{
 +      sm->ssdp_sd = ssdp_listener_open();
 +
 +      if (eloop_register_sock(sm->ssdp_sd, EVENT_TYPE_READ,
 +                              ssdp_listener_handler, NULL, sm))
 +              goto fail;
 +      sm->ssdp_sd_registered = 1;
 +      return 0;
 +
 +fail:
 +      /* Error */
 +      wpa_printf(MSG_ERROR, "WPS UPnP: ssdp_listener_start failed");
 +      ssdp_listener_stop(sm);
 +      return -1;
 +}
 +
 +
 +/**
 + * add_ssdp_network - Add routing entry for SSDP
 + * @net_if: Selected network interface name
 + * Returns: 0 on success, -1 on failure
 + *
 + * This function assures that the multicast address will be properly
 + * handled by Linux networking code (by a modification to routing tables).
 + * This must be done per network interface. It really only needs to be done
 + * once after booting up, but it does not hurt to call this more frequently
 + * "to be safe".
 + */
 +int add_ssdp_network(const char *net_if)
 +{
 +#ifdef __linux__
 +      int ret = -1;
 +      int sock = -1;
 +      struct rtentry rt;
 +      struct sockaddr_in *sin;
 +
 +      if (!net_if)
 +              goto fail;
 +
 +      os_memset(&rt, 0, sizeof(rt));
 +      sock = socket(AF_INET, SOCK_DGRAM, 0);
 +      if (sock < 0)
 +              goto fail;
 +
 +      rt.rt_dev = (char *) net_if;
 +      sin = aliasing_hide_typecast(&rt.rt_dst, struct sockaddr_in);
 +      sin->sin_family = AF_INET;
 +      sin->sin_port = 0;
 +      sin->sin_addr.s_addr = inet_addr(SSDP_TARGET);
 +      sin = aliasing_hide_typecast(&rt.rt_genmask, struct sockaddr_in);
 +      sin->sin_family = AF_INET;
 +      sin->sin_port = 0;
 +      sin->sin_addr.s_addr = inet_addr(SSDP_NETMASK);
 +      rt.rt_flags = RTF_UP;
 +      if (ioctl(sock, SIOCADDRT, &rt) < 0) {
 +              if (errno == EPERM) {
 +                      wpa_printf(MSG_DEBUG, "add_ssdp_network: No "
 +                                 "permissions to add routing table entry");
 +                      /* Continue to allow testing as non-root */
 +              } else if (errno != EEXIST) {
 +                      wpa_printf(MSG_INFO, "add_ssdp_network() ioctl errno "
 +                                 "%d (%s)", errno, strerror(errno));
 +                      goto fail;
 +              }
 +      }
 +
 +      ret = 0;
 +
 +fail:
 +      if (sock >= 0)
 +              close(sock);
 +
 +      return ret;
 +#else /* __linux__ */
 +      return 0;
 +#endif /* __linux__ */
 +}
 +
 +
 +int ssdp_open_multicast_sock(u32 ip_addr, const char *forced_ifname)
 +{
 +      int sd;
 +       /* per UPnP-arch-DeviceArchitecture, 1. Discovery, keep IP packet
 +        * time to live (TTL) small */
 +      unsigned char ttl = 4;
 +
 +      sd = socket(AF_INET, SOCK_DGRAM, 0);
 +      if (sd < 0)
 +              return -1;
 +
 +      if (forced_ifname) {
 +#ifdef __linux__
 +              struct ifreq req;
 +              os_memset(&req, 0, sizeof(req));
 +              os_strlcpy(req.ifr_name, forced_ifname, sizeof(req.ifr_name));
 +              if (setsockopt(sd, SOL_SOCKET, SO_BINDTODEVICE, &req,
 +                             sizeof(req)) < 0) {
 +                      wpa_printf(MSG_INFO, "WPS UPnP: Failed to bind "
 +                                 "multicast socket to ifname %s: %s",
 +                                 forced_ifname, strerror(errno));
 +                      close(sd);
 +                      return -1;
 +              }
 +#endif /* __linux__ */
 +      }
 +
 +#if 0   /* maybe ok if we sometimes block on writes */
 +      if (fcntl(sd, F_SETFL, O_NONBLOCK) != 0) {
 +              close(sd);
 +              return -1;
 +      }
 +#endif
 +
 +      if (setsockopt(sd, IPPROTO_IP, IP_MULTICAST_IF,
 +                     &ip_addr, sizeof(ip_addr))) {
 +              wpa_printf(MSG_DEBUG, "WPS: setsockopt(IP_MULTICAST_IF) %x: "
 +                         "%d (%s)", ip_addr, errno, strerror(errno));
 +              close(sd);
 +              return -1;
 +      }
 +      if (setsockopt(sd, IPPROTO_IP, IP_MULTICAST_TTL,
 +                     &ttl, sizeof(ttl))) {
 +              wpa_printf(MSG_DEBUG, "WPS: setsockopt(IP_MULTICAST_TTL): "
 +                         "%d (%s)", errno, strerror(errno));
 +              close(sd);
 +              return -1;
 +      }
 +
 +#if 0   /* not needed, because we don't receive using multicast_sd */
 +      {
 +              struct ip_mreq mreq;
 +              mreq.imr_multiaddr.s_addr = inet_addr(UPNP_MULTICAST_ADDRESS);
 +              mreq.imr_interface.s_addr = ip_addr;
 +              wpa_printf(MSG_DEBUG, "WPS UPnP: Multicast addr 0x%x if addr "
 +                         "0x%x",
 +                         mreq.imr_multiaddr.s_addr,
 +                         mreq.imr_interface.s_addr);
 +              if (setsockopt(sd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq,
 +                              sizeof(mreq))) {
 +                      wpa_printf(MSG_ERROR,
 +                                 "WPS UPnP: setsockopt "
 +                                 "IP_ADD_MEMBERSHIP errno %d (%s)",
 +                                 errno, strerror(errno));
 +                      close(sd);
 +                      return -1;
 +              }
 +      }
 +#endif  /* not needed */
 +
 +      /*
 +       * TODO: What about IP_MULTICAST_LOOP? It seems to be on by default?
 +       * which aids debugging I suppose but isn't really necessary?
 +       */
 +
 +      return sd;
 +}
 +
 +
 +/**
 + * ssdp_open_multicast - Open socket for sending multicast SSDP messages
 + * @sm: WPS UPnP state machine from upnp_wps_device_init()
 + * Returns: 0 on success, -1 on failure
 + */
 +int ssdp_open_multicast(struct upnp_wps_device_sm *sm)
 +{
 +      sm->multicast_sd = ssdp_open_multicast_sock(sm->ip_addr, NULL);
 +      if (sm->multicast_sd < 0)
 +              return -1;
 +      return 0;
 +}
index b1cf571d8acb10acf721b2f4440ac514f0ae2a61,0000000000000000000000000000000000000000..d5b0b5b26e9d556b3f1f32fd05471d5929021bab
mode 100644,000000..100644
--- /dev/null
@@@ -1,1341 -1,0 +1,1350 @@@
-                       wpa_printf(MSG_DEBUG, "WPS UPnP: Unsubscribing %p %s",
-                                  s, (sa && sa->domain_and_port) ?
 +/*
 + * UPnP WPS Device - Web connections
 + * Copyright (c) 2000-2003 Intel Corporation
 + * Copyright (c) 2006-2007 Sony Corporation
 + * Copyright (c) 2008-2009 Atheros Communications
 + * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
 + *
 + * See wps_upnp.c for more details on licensing and code history.
 + */
 +
 +#include "includes.h"
 +
 +#include "common.h"
 +#include "base64.h"
 +#include "uuid.h"
 +#include "httpread.h"
 +#include "http_server.h"
 +#include "wps_i.h"
 +#include "wps_upnp.h"
 +#include "wps_upnp_i.h"
 +#include "upnp_xml.h"
 +
 +/***************************************************************************
 + * Web connections (we serve pages of info about ourselves, handle
 + * requests, etc. etc.).
 + **************************************************************************/
 +
 +#define WEB_CONNECTION_TIMEOUT_SEC 30   /* Drop web connection after t.o. */
 +#define WEB_CONNECTION_MAX_READ 8000    /* Max we'll read for TCP request */
 +#define MAX_WEB_CONNECTIONS 10          /* max simultaneous web connects */
 +
 +
 +static const char *urn_wfawlanconfig =
 +      "urn:schemas-wifialliance-org:service:WFAWLANConfig:1";
 +static const char *http_server_hdr =
 +      "Server: unspecified, UPnP/1.0, unspecified\r\n";
 +static const char *http_connection_close =
 +      "Connection: close\r\n";
 +
 +/*
 + * "Files" that we serve via HTTP. The format of these files is given by
 + * WFA WPS specifications. Extra white space has been removed to save space.
 + */
 +
 +static const char wps_scpd_xml[] =
 +"<?xml version=\"1.0\"?>\n"
 +"<scpd xmlns=\"urn:schemas-upnp-org:service-1-0\">\n"
 +"<specVersion><major>1</major><minor>0</minor></specVersion>\n"
 +"<actionList>\n"
 +"<action>\n"
 +"<name>GetDeviceInfo</name>\n"
 +"<argumentList>\n"
 +"<argument>\n"
 +"<name>NewDeviceInfo</name>\n"
 +"<direction>out</direction>\n"
 +"<relatedStateVariable>DeviceInfo</relatedStateVariable>\n"
 +"</argument>\n"
 +"</argumentList>\n"
 +"</action>\n"
 +"<action>\n"
 +"<name>PutMessage</name>\n"
 +"<argumentList>\n"
 +"<argument>\n"
 +"<name>NewInMessage</name>\n"
 +"<direction>in</direction>\n"
 +"<relatedStateVariable>InMessage</relatedStateVariable>\n"
 +"</argument>\n"
 +"<argument>\n"
 +"<name>NewOutMessage</name>\n"
 +"<direction>out</direction>\n"
 +"<relatedStateVariable>OutMessage</relatedStateVariable>\n"
 +"</argument>\n"
 +"</argumentList>\n"
 +"</action>\n"
 +"<action>\n"
 +"<name>PutWLANResponse</name>\n"
 +"<argumentList>\n"
 +"<argument>\n"
 +"<name>NewMessage</name>\n"
 +"<direction>in</direction>\n"
 +"<relatedStateVariable>Message</relatedStateVariable>\n"
 +"</argument>\n"
 +"<argument>\n"
 +"<name>NewWLANEventType</name>\n"
 +"<direction>in</direction>\n"
 +"<relatedStateVariable>WLANEventType</relatedStateVariable>\n"
 +"</argument>\n"
 +"<argument>\n"
 +"<name>NewWLANEventMAC</name>\n"
 +"<direction>in</direction>\n"
 +"<relatedStateVariable>WLANEventMAC</relatedStateVariable>\n"
 +"</argument>\n"
 +"</argumentList>\n"
 +"</action>\n"
 +"<action>\n"
 +"<name>SetSelectedRegistrar</name>\n"
 +"<argumentList>\n"
 +"<argument>\n"
 +"<name>NewMessage</name>\n"
 +"<direction>in</direction>\n"
 +"<relatedStateVariable>Message</relatedStateVariable>\n"
 +"</argument>\n"
 +"</argumentList>\n"
 +"</action>\n"
 +"</actionList>\n"
 +"<serviceStateTable>\n"
 +"<stateVariable sendEvents=\"no\">\n"
 +"<name>Message</name>\n"
 +"<dataType>bin.base64</dataType>\n"
 +"</stateVariable>\n"
 +"<stateVariable sendEvents=\"no\">\n"
 +"<name>InMessage</name>\n"
 +"<dataType>bin.base64</dataType>\n"
 +"</stateVariable>\n"
 +"<stateVariable sendEvents=\"no\">\n"
 +"<name>OutMessage</name>\n"
 +"<dataType>bin.base64</dataType>\n"
 +"</stateVariable>\n"
 +"<stateVariable sendEvents=\"no\">\n"
 +"<name>DeviceInfo</name>\n"
 +"<dataType>bin.base64</dataType>\n"
 +"</stateVariable>\n"
 +"<stateVariable sendEvents=\"yes\">\n"
 +"<name>APStatus</name>\n"
 +"<dataType>ui1</dataType>\n"
 +"</stateVariable>\n"
 +"<stateVariable sendEvents=\"yes\">\n"
 +"<name>STAStatus</name>\n"
 +"<dataType>ui1</dataType>\n"
 +"</stateVariable>\n"
 +"<stateVariable sendEvents=\"yes\">\n"
 +"<name>WLANEvent</name>\n"
 +"<dataType>bin.base64</dataType>\n"
 +"</stateVariable>\n"
 +"<stateVariable sendEvents=\"no\">\n"
 +"<name>WLANEventType</name>\n"
 +"<dataType>ui1</dataType>\n"
 +"</stateVariable>\n"
 +"<stateVariable sendEvents=\"no\">\n"
 +"<name>WLANEventMAC</name>\n"
 +"<dataType>string</dataType>\n"
 +"</stateVariable>\n"
 +"<stateVariable sendEvents=\"no\">\n"
 +"<name>WLANResponse</name>\n"
 +"<dataType>bin.base64</dataType>\n"
 +"</stateVariable>\n"
 +"</serviceStateTable>\n"
 +"</scpd>\n"
 +;
 +
 +
 +static const char *wps_device_xml_prefix =
 +      "<?xml version=\"1.0\"?>\n"
 +      "<root xmlns=\"urn:schemas-upnp-org:device-1-0\">\n"
 +      "<specVersion>\n"
 +      "<major>1</major>\n"
 +      "<minor>0</minor>\n"
 +      "</specVersion>\n"
 +      "<device>\n"
 +      "<deviceType>urn:schemas-wifialliance-org:device:WFADevice:1"
 +      "</deviceType>\n";
 +
 +static const char *wps_device_xml_postfix =
 +      "<serviceList>\n"
 +      "<service>\n"
 +      "<serviceType>urn:schemas-wifialliance-org:service:WFAWLANConfig:1"
 +      "</serviceType>\n"
 +      "<serviceId>urn:wifialliance-org:serviceId:WFAWLANConfig1</serviceId>"
 +      "\n"
 +      "<SCPDURL>" UPNP_WPS_SCPD_XML_FILE "</SCPDURL>\n"
 +      "<controlURL>" UPNP_WPS_DEVICE_CONTROL_FILE "</controlURL>\n"
 +      "<eventSubURL>" UPNP_WPS_DEVICE_EVENT_FILE "</eventSubURL>\n"
 +      "</service>\n"
 +      "</serviceList>\n"
 +      "</device>\n"
 +      "</root>\n";
 +
 +
 +/* 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_interface *iface,
 +                                struct upnp_wps_device_sm *sm,
 +                                struct wpabuf *buf)
 +{
 +      const char *s;
 +      char uuid_string[80];
 +
 +      wpabuf_put_str(buf, wps_device_xml_prefix);
 +
 +      /*
 +       * Add required fields with default values if not configured. Add
 +       * optional and recommended fields only if configured.
 +       */
 +      s = iface->wps->friendly_name;
 +      s = ((s && *s) ? s : "WPS Access Point");
 +      xml_add_tagged_data(buf, "friendlyName", s);
 +
 +      s = iface->wps->dev.manufacturer;
 +      s = ((s && *s) ? s : "");
 +      xml_add_tagged_data(buf, "manufacturer", s);
 +
 +      if (iface->wps->manufacturer_url)
 +              xml_add_tagged_data(buf, "manufacturerURL",
 +                                  iface->wps->manufacturer_url);
 +
 +      if (iface->wps->model_description)
 +              xml_add_tagged_data(buf, "modelDescription",
 +                                  iface->wps->model_description);
 +
 +      s = iface->wps->dev.model_name;
 +      s = ((s && *s) ? s : "");
 +      xml_add_tagged_data(buf, "modelName", s);
 +
 +      if (iface->wps->dev.model_number)
 +              xml_add_tagged_data(buf, "modelNumber",
 +                                  iface->wps->dev.model_number);
 +
 +      if (iface->wps->model_url)
 +              xml_add_tagged_data(buf, "modelURL", iface->wps->model_url);
 +
 +      if (iface->wps->dev.serial_number)
 +              xml_add_tagged_data(buf, "serialNumber",
 +                                  iface->wps->dev.serial_number);
 +
 +      uuid_bin2str(iface->wps->uuid, uuid_string, sizeof(uuid_string));
 +      s = uuid_string;
 +      /* Need "uuid:" prefix, thus we can't use xml_add_tagged_data()
 +       * easily...
 +       */
 +      wpabuf_put_str(buf, "<UDN>uuid:");
 +      xml_data_encode(buf, s, os_strlen(s));
 +      wpabuf_put_str(buf, "</UDN>\n");
 +
 +      if (iface->wps->upc)
 +              xml_add_tagged_data(buf, "UPC", iface->wps->upc);
 +
 +      wpabuf_put_str(buf, wps_device_xml_postfix);
 +}
 +
 +
 +static void http_put_reply_code(struct wpabuf *buf, enum http_reply_code code)
 +{
 +      wpabuf_put_str(buf, "HTTP/1.1 ");
 +      switch (code) {
 +      case HTTP_OK:
 +              wpabuf_put_str(buf, "200 OK\r\n");
 +              break;
 +      case HTTP_BAD_REQUEST:
 +              wpabuf_put_str(buf, "400 Bad request\r\n");
 +              break;
 +      case HTTP_PRECONDITION_FAILED:
 +              wpabuf_put_str(buf, "412 Precondition failed\r\n");
 +              break;
 +      case HTTP_UNIMPLEMENTED:
 +              wpabuf_put_str(buf, "501 Unimplemented\r\n");
 +              break;
 +      case HTTP_INTERNAL_SERVER_ERROR:
 +      default:
 +              wpabuf_put_str(buf, "500 Internal server error\r\n");
 +              break;
 +      }
 +}
 +
 +
 +static void http_put_date(struct wpabuf *buf)
 +{
 +      wpabuf_put_str(buf, "Date: ");
 +      format_date(buf);
 +      wpabuf_put_str(buf, "\r\n");
 +}
 +
 +
 +static void http_put_empty(struct wpabuf *buf, enum http_reply_code code)
 +{
 +      http_put_reply_code(buf, code);
 +      wpabuf_put_str(buf, http_server_hdr);
 +      wpabuf_put_str(buf, http_connection_close);
 +      wpabuf_put_str(buf, "Content-Length: 0\r\n"
 +                     "\r\n");
 +}
 +
 +
 +/* Given that we have received a header w/ GET, act upon it
 + *
 + * Format of GET (case-insensitive):
 + *
 + * First line must be:
 + *      GET /<file> HTTP/1.1
 + * Since we don't do anything fancy we just ignore other lines.
 + *
 + * Our response (if no error) which includes only required lines is:
 + * HTTP/1.1 200 OK
 + * Connection: close
 + * Content-Type: text/xml
 + * Date: <rfc1123-date>
 + *
 + * Header lines must end with \r\n
 + * Per RFC 2616, content-length: is not required but connection:close
 + * would appear to be required (given that we will be closing it!).
 + */
 +static void web_connection_parse_get(struct upnp_wps_device_sm *sm,
 +                                   struct http_request *hreq, char *filename)
 +{
 +      struct wpabuf *buf; /* output buffer, allocated */
 +      char *put_length_here;
 +      char *body_start;
 +      enum {
 +              GET_DEVICE_XML_FILE,
 +              GET_SCPD_XML_FILE
 +      } req;
 +      size_t extra_len = 0;
 +      int body_length;
 +      char len_buf[10];
 +      struct upnp_wps_device_interface *iface;
 +
 +      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 (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;
 +              extra_len = 3000;
 +              if (iface->wps->friendly_name)
 +                      extra_len += os_strlen(iface->wps->friendly_name);
 +              if (iface->wps->manufacturer_url)
 +                      extra_len += os_strlen(iface->wps->manufacturer_url);
 +              if (iface->wps->model_description)
 +                      extra_len += os_strlen(iface->wps->model_description);
 +              if (iface->wps->model_url)
 +                      extra_len += os_strlen(iface->wps->model_url);
 +              if (iface->wps->upc)
 +                      extra_len += os_strlen(iface->wps->upc);
 +      } else if (!os_strcasecmp(filename, UPNP_WPS_SCPD_XML_FILE)) {
 +              wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP GET for SCPD XML");
 +              req = GET_SCPD_XML_FILE;
 +              extra_len = os_strlen(wps_scpd_xml);
 +      } else {
 +              /* File not found */
 +              wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP GET file not found: %s",
 +                         filename);
 +              buf = wpabuf_alloc(200);
 +              if (buf == NULL) {
 +                      http_request_deinit(hreq);
 +                      return;
 +              }
 +              wpabuf_put_str(buf,
 +                             "HTTP/1.1 404 Not Found\r\n"
 +                             "Connection: close\r\n");
 +
 +              http_put_date(buf);
 +
 +              /* terminating empty line */
 +              wpabuf_put_str(buf, "\r\n");
 +
 +              goto send_buf;
 +      }
 +
 +      buf = wpabuf_alloc(1000 + extra_len);
 +      if (buf == NULL) {
 +              http_request_deinit(hreq);
 +              return;
 +      }
 +
 +      wpabuf_put_str(buf,
 +                     "HTTP/1.1 200 OK\r\n"
 +                     "Content-Type: text/xml; charset=\"utf-8\"\r\n");
 +      wpabuf_put_str(buf, "Server: Unspecified, UPnP/1.0, Unspecified\r\n");
 +      wpabuf_put_str(buf, "Connection: close\r\n");
 +      wpabuf_put_str(buf, "Content-Length: ");
 +      /*
 +       * We will paste the length in later, leaving some extra whitespace.
 +       * HTTP code is supposed to be tolerant of extra whitespace.
 +       */
 +      put_length_here = wpabuf_put(buf, 0);
 +      wpabuf_put_str(buf, "        \r\n");
 +
 +      http_put_date(buf);
 +
 +      /* terminating empty line */
 +      wpabuf_put_str(buf, "\r\n");
 +
 +      body_start = wpabuf_put(buf, 0);
 +
 +      switch (req) {
 +      case GET_DEVICE_XML_FILE:
 +              format_wps_device_xml(iface, sm, buf);
 +              break;
 +      case GET_SCPD_XML_FILE:
 +              wpabuf_put_str(buf, wps_scpd_xml);
 +              break;
 +      }
 +
 +      /* Now patch in the content length at the end */
 +      body_length = (char *) wpabuf_put(buf, 0) - body_start;
 +      os_snprintf(len_buf, 10, "%d", body_length);
 +      os_memcpy(put_length_here, len_buf, os_strlen(len_buf));
 +
 +send_buf:
 +      http_request_send_and_deinit(hreq, buf);
 +}
 +
 +
 +static enum http_reply_code
 +web_process_get_device_info(struct upnp_wps_device_sm *sm,
 +                          struct wpabuf **reply, const char **replyname)
 +{
 +      static const char *name = "NewDeviceInfo";
 +      struct wps_config cfg;
 +      struct upnp_wps_device_interface *iface;
 +      struct upnp_wps_peer *peer;
 +
 +      iface = dl_list_first(&sm->interfaces,
 +                            struct upnp_wps_device_interface, list);
 +
 +      wpa_printf(MSG_DEBUG, "WPS UPnP: GetDeviceInfo");
 +
 +      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
 +       * be noted that this is frequently used just to get the device data,
 +       * i.e., there may not be any intent to actually complete the
 +       * registration.
 +       */
 +
 +      if (peer->wps)
 +              wps_deinit(peer->wps);
 +
 +      os_memset(&cfg, 0, sizeof(cfg));
 +      cfg.wps = iface->wps;
 +      cfg.pin = (u8 *) iface->ctx->ap_pin;
 +      cfg.pin_len = os_strlen(iface->ctx->ap_pin);
 +      peer->wps = wps_init(&cfg);
 +      if (peer->wps) {
 +              enum wsc_op_code op_code;
 +              *reply = wps_get_msg(peer->wps, &op_code);
 +              if (*reply == NULL) {
 +                      wps_deinit(peer->wps);
 +                      peer->wps = NULL;
 +              }
 +      } else
 +              *reply = NULL;
 +      if (*reply == NULL) {
 +              wpa_printf(MSG_INFO, "WPS UPnP: Failed to get DeviceInfo");
 +              return HTTP_INTERNAL_SERVER_ERROR;
 +      }
 +      *replyname = name;
 +      return HTTP_OK;
 +}
 +
 +
 +static enum http_reply_code
 +web_process_put_message(struct upnp_wps_device_sm *sm, char *data,
 +                      struct wpabuf **reply, const char **replyname)
 +{
 +      struct wpabuf *msg;
 +      static const char *name = "NewOutMessage";
 +      enum http_reply_code ret;
 +      enum wps_process_res res;
 +      enum wsc_op_code op_code;
 +      struct upnp_wps_device_interface *iface;
 +
 +      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
 +       * operation with the access point itself; as compared with
 +       * PutWLANResponse which is for proxying.
 +       */
 +      wpa_printf(MSG_DEBUG, "WPS UPnP: PutMessage");
 +      msg = xml_get_base64_item(data, "NewInMessage", &ret);
 +      if (msg == NULL)
 +              return ret;
 +      res = wps_process_msg(iface->peer.wps, WSC_UPnP, msg);
 +      if (res == WPS_FAILURE)
 +              *reply = NULL;
 +      else
 +              *reply = wps_get_msg(iface->peer.wps, &op_code);
 +      wpabuf_free(msg);
 +      if (*reply == NULL)
 +              return HTTP_INTERNAL_SERVER_ERROR;
 +      *replyname = name;
 +      return HTTP_OK;
 +}
 +
 +
 +static enum http_reply_code
 +web_process_put_wlan_response(struct upnp_wps_device_sm *sm, char *data,
 +                            struct wpabuf **reply, const char **replyname)
 +{
 +      struct wpabuf *msg;
 +      enum http_reply_code ret;
 +      u8 macaddr[ETH_ALEN];
 +      int ev_type;
 +      int type;
 +      char *val;
 +      struct upnp_wps_device_interface *iface;
 +      int ok = 0;
 +
 +      /*
 +       * External UPnP-based Registrar is passing us a message to be proxied
 +       * over to a Wi-Fi -based client of ours.
 +       */
 +
 +      wpa_printf(MSG_DEBUG, "WPS UPnP: PutWLANResponse");
 +      msg = xml_get_base64_item(data, "NewMessage", &ret);
 +      if (msg == NULL) {
 +              wpa_printf(MSG_DEBUG, "WPS UPnP: Could not extract NewMessage "
 +                         "from PutWLANResponse");
 +              return ret;
 +      }
 +      val = xml_get_first_item(data, "NewWLANEventType");
 +      if (val == NULL) {
 +              wpa_printf(MSG_DEBUG, "WPS UPnP: No NewWLANEventType in "
 +                         "PutWLANResponse");
 +              wpabuf_free(msg);
 +              return UPNP_ARG_VALUE_INVALID;
 +      }
 +      ev_type = atol(val);
 +      os_free(val);
 +      val = xml_get_first_item(data, "NewWLANEventMAC");
 +      if (val == NULL) {
 +              wpa_printf(MSG_DEBUG, "WPS UPnP: No NewWLANEventMAC in "
 +                         "PutWLANResponse");
 +              wpabuf_free(msg);
 +              return UPNP_ARG_VALUE_INVALID;
 +      }
 +      if (hwaddr_aton(val, macaddr)) {
 +              wpa_printf(MSG_DEBUG, "WPS UPnP: Invalid NewWLANEventMAC in "
 +                         "PutWLANResponse: '%s'", val);
 +#ifdef CONFIG_WPS_STRICT
 +              {
 +                      struct wps_parse_attr attr;
 +                      if (wps_parse_msg(msg, &attr) < 0 || attr.version2) {
 +                              wpabuf_free(msg);
 +                              os_free(val);
 +                              return UPNP_ARG_VALUE_INVALID;
 +                      }
 +              }
 +#endif /* CONFIG_WPS_STRICT */
 +              if (hwaddr_aton2(val, macaddr) > 0) {
 +                      /*
 +                       * At least some versions of Intel PROset seem to be
 +                       * using dot-deliminated MAC address format here.
 +                       */
 +                      wpa_printf(MSG_DEBUG, "WPS UPnP: Workaround - allow "
 +                                 "incorrect MAC address format in "
 +                                 "NewWLANEventMAC: %s -> " MACSTR,
 +                                 val, MAC2STR(macaddr));
 +              } else {
 +                      wpabuf_free(msg);
 +                      os_free(val);
 +                      return UPNP_ARG_VALUE_INVALID;
 +              }
 +      }
 +      os_free(val);
 +      if (ev_type == UPNP_WPS_WLANEVENT_TYPE_EAP) {
 +              struct wps_parse_attr attr;
 +              if (wps_parse_msg(msg, &attr) < 0 ||
 +                  attr.msg_type == NULL)
 +                      type = -1;
 +              else
 +                      type = *attr.msg_type;
 +              wpa_printf(MSG_DEBUG, "WPS UPnP: Message Type %d", type);
 +      } else
 +              type = -1;
 +      dl_list_for_each(iface, &sm->interfaces,
 +                       struct upnp_wps_device_interface, list) {
 +              if (iface->ctx->rx_req_put_wlan_response &&
 +                  iface->ctx->rx_req_put_wlan_response(iface->priv, ev_type,
 +                                                       macaddr, msg, type)
 +                  == 0)
 +                      ok = 1;
 +      }
 +
 +      if (!ok) {
 +              wpa_printf(MSG_INFO, "WPS UPnP: Fail: sm->ctx->"
 +                         "rx_req_put_wlan_response");
 +              wpabuf_free(msg);
 +              return HTTP_INTERNAL_SERVER_ERROR;
 +      }
 +      wpabuf_free(msg);
 +      *replyname = NULL;
 +      *reply = NULL;
 +      return HTTP_OK;
 +}
 +
 +
 +static int find_er_addr(struct subscription *s, struct sockaddr_in *cli)
 +{
 +      struct subscr_addr *a;
 +
 +      dl_list_for_each(a, &s->addr_list, struct subscr_addr, list) {
 +              if (cli->sin_addr.s_addr == a->saddr.sin_addr.s_addr)
 +                      return 1;
 +      }
 +      return 0;
 +}
 +
 +
 +static struct subscription * find_er(struct upnp_wps_device_sm *sm,
 +                                   struct sockaddr_in *cli)
 +{
 +      struct subscription *s;
 +      dl_list_for_each(s, &sm->subscriptions, struct subscription, list)
 +              if (find_er_addr(s, cli))
 +                      return s;
 +      return NULL;
 +}
 +
 +
 +static enum http_reply_code
 +web_process_set_selected_registrar(struct upnp_wps_device_sm *sm,
 +                                 struct sockaddr_in *cli, char *data,
 +                                 struct wpabuf **reply,
 +                                 const char **replyname)
 +{
 +      struct wpabuf *msg;
 +      enum http_reply_code ret;
 +      struct subscription *s;
 +      struct upnp_wps_device_interface *iface;
 +      int err = 0;
 +
 +      wpa_printf(MSG_DEBUG, "WPS UPnP: SetSelectedRegistrar");
 +      s = find_er(sm, cli);
 +      if (s == NULL) {
 +              wpa_printf(MSG_DEBUG, "WPS UPnP: Ignore SetSelectedRegistrar "
 +                         "from unknown ER");
 +              return UPNP_ACTION_FAILED;
 +      }
 +      msg = xml_get_base64_item(data, "NewMessage", &ret);
 +      if (msg == NULL)
 +              return ret;
 +      dl_list_for_each(iface, &sm->interfaces,
 +                       struct upnp_wps_device_interface, list) {
 +              if (upnp_er_set_selected_registrar(iface->wps->registrar, s,
 +                                                 msg))
 +                      err = 1;
 +      }
 +      wpabuf_free(msg);
 +      if (err)
 +              return HTTP_INTERNAL_SERVER_ERROR;
 +      *replyname = NULL;
 +      *reply = NULL;
 +      return HTTP_OK;
 +}
 +
 +
 +static const char *soap_prefix =
 +      "<?xml version=\"1.0\"?>\n"
 +      "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" "
 +      "s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">\n"
 +      "<s:Body>\n";
 +static const char *soap_postfix =
 +      "</s:Body>\n</s:Envelope>\n";
 +
 +static const char *soap_error_prefix =
 +      "<s:Fault>\n"
 +      "<faultcode>s:Client</faultcode>\n"
 +      "<faultstring>UPnPError</faultstring>\n"
 +      "<detail>\n"
 +      "<UPnPError xmlns=\"urn:schemas-upnp-org:control-1-0\">\n";
 +static const char *soap_error_postfix =
 +      "<errorDescription>Error</errorDescription>\n"
 +      "</UPnPError>\n"
 +      "</detail>\n"
 +      "</s:Fault>\n";
 +
 +static void web_connection_send_reply(struct http_request *req,
 +                                    enum http_reply_code ret,
 +                                    const char *action, int action_len,
 +                                    const struct wpabuf *reply,
 +                                    const char *replyname)
 +{
 +      struct wpabuf *buf;
 +      char *replydata;
 +      char *put_length_here = NULL;
 +      char *body_start = NULL;
 +
 +      if (reply) {
 +              size_t len;
 +              replydata = (char *) base64_encode(wpabuf_head(reply),
 +                                                 wpabuf_len(reply), &len);
 +      } else
 +              replydata = NULL;
 +
 +      /* Parameters of the response:
 +       *      action(action_len) -- action we are responding to
 +       *      replyname -- a name we need for the reply
 +       *      replydata -- NULL or null-terminated string
 +       */
 +      buf = wpabuf_alloc(1000 + (replydata ? os_strlen(replydata) : 0U) +
 +                         (action_len > 0 ? action_len * 2 : 0));
 +      if (buf == NULL) {
 +              wpa_printf(MSG_INFO, "WPS UPnP: Cannot allocate reply to "
 +                         "POST");
 +              os_free(replydata);
 +              http_request_deinit(req);
 +              return;
 +      }
 +
 +      /*
 +       * Assuming we will be successful, put in the output header first.
 +       * Note: we do not keep connections alive (and httpread does
 +       * not support it)... therefore we must have Connection: close.
 +       */
 +      if (ret == HTTP_OK) {
 +              wpabuf_put_str(buf,
 +                             "HTTP/1.1 200 OK\r\n"
 +                             "Content-Type: text/xml; "
 +                             "charset=\"utf-8\"\r\n");
 +      } else {
 +              wpabuf_printf(buf, "HTTP/1.1 %d Error\r\n", ret);
 +      }
 +      wpabuf_put_str(buf, http_connection_close);
 +
 +      wpabuf_put_str(buf, "Content-Length: ");
 +      /*
 +       * We will paste the length in later, leaving some extra whitespace.
 +       * HTTP code is supposed to be tolerant of extra whitespace.
 +       */
 +      put_length_here = wpabuf_put(buf, 0);
 +      wpabuf_put_str(buf, "        \r\n");
 +
 +      http_put_date(buf);
 +
 +      /* terminating empty line */
 +      wpabuf_put_str(buf, "\r\n");
 +
 +      body_start = wpabuf_put(buf, 0);
 +
 +      if (ret == HTTP_OK) {
 +              wpabuf_put_str(buf, soap_prefix);
 +              wpabuf_put_str(buf, "<u:");
 +              wpabuf_put_data(buf, action, action_len);
 +              wpabuf_put_str(buf, "Response xmlns:u=\"");
 +              wpabuf_put_str(buf, urn_wfawlanconfig);
 +              wpabuf_put_str(buf, "\">\n");
 +              if (replydata && replyname) {
 +                      /* TODO: might possibly need to escape part of reply
 +                       * data? ...
 +                       * probably not, unlikely to have ampersand(&) or left
 +                       * angle bracket (<) in it...
 +                       */
 +                      wpabuf_printf(buf, "<%s>", replyname);
 +                      wpabuf_put_str(buf, replydata);
 +                      wpabuf_printf(buf, "</%s>\n", replyname);
 +              }
 +              wpabuf_put_str(buf, "</u:");
 +              wpabuf_put_data(buf, action, action_len);
 +              wpabuf_put_str(buf, "Response>\n");
 +              wpabuf_put_str(buf, soap_postfix);
 +      } else {
 +              /* Error case */
 +              wpabuf_put_str(buf, soap_prefix);
 +              wpabuf_put_str(buf, soap_error_prefix);
 +              wpabuf_printf(buf, "<errorCode>%d</errorCode>\n", ret);
 +              wpabuf_put_str(buf, soap_error_postfix);
 +              wpabuf_put_str(buf, soap_postfix);
 +      }
 +      os_free(replydata);
 +
 +      /* Now patch in the content length at the end */
 +      if (body_start && put_length_here) {
 +              int body_length = (char *) wpabuf_put(buf, 0) - body_start;
 +              char len_buf[10];
 +              os_snprintf(len_buf, sizeof(len_buf), "%d", body_length);
 +              os_memcpy(put_length_here, len_buf, os_strlen(len_buf));
 +      }
 +
 +      http_request_send_and_deinit(req, buf);
 +}
 +
 +
 +static const char * web_get_action(struct http_request *req,
 +                                 size_t *action_len)
 +{
 +      const char *match;
 +      int match_len;
 +      char *b;
 +      char *action;
 +
 +      *action_len = 0;
 +      /* The SOAPAction line of the header tells us what we want to do */
 +      b = http_request_get_hdr_line(req, "SOAPAction:");
 +      if (b == NULL)
 +              return NULL;
 +      if (*b == '"')
 +              b++;
 +      else
 +              return NULL;
 +      match = urn_wfawlanconfig;
 +      match_len = os_strlen(urn_wfawlanconfig) - 1;
 +      if (os_strncasecmp(b, match, match_len))
 +              return NULL;
 +      b += match_len;
 +      /* skip over version */
 +      while (isgraph(*b) && *b != '#')
 +              b++;
 +      if (*b != '#')
 +              return NULL;
 +      b++;
 +      /* Following the sharp(#) should be the action and a double quote */
 +      action = b;
 +      while (isgraph(*b) && *b != '"')
 +              b++;
 +      if (*b != '"')
 +              return NULL;
 +      *action_len = b - action;
 +      return action;
 +}
 +
 +
 +/* Given that we have received a header w/ POST, act upon it
 + *
 + * Format of POST (case-insensitive):
 + *
 + * First line must be:
 + *      POST /<file> HTTP/1.1
 + * Since we don't do anything fancy we just ignore other lines.
 + *
 + * Our response (if no error) which includes only required lines is:
 + * HTTP/1.1 200 OK
 + * Connection: close
 + * Content-Type: text/xml
 + * Date: <rfc1123-date>
 + *
 + * Header lines must end with \r\n
 + * Per RFC 2616, content-length: is not required but connection:close
 + * would appear to be required (given that we will be closing it!).
 + */
 +static void web_connection_parse_post(struct upnp_wps_device_sm *sm,
 +                                    struct sockaddr_in *cli,
 +                                    struct http_request *req,
 +                                    const char *filename)
 +{
 +      enum http_reply_code ret;
 +      char *data = http_request_get_data(req); /* body of http msg */
 +      const char *action = NULL;
 +      size_t action_len = 0;
 +      const char *replyname = NULL; /* argument name for the reply */
 +      struct wpabuf *reply = NULL; /* data for the reply */
 +
 +      if (os_strcasecmp(filename, UPNP_WPS_DEVICE_CONTROL_FILE)) {
 +              wpa_printf(MSG_INFO, "WPS UPnP: Invalid POST filename %s",
 +                         filename);
 +              ret = HTTP_NOT_FOUND;
 +              goto bad;
 +      }
 +
 +      ret = UPNP_INVALID_ACTION;
 +      action = web_get_action(req, &action_len);
 +      if (action == NULL)
 +              goto bad;
 +
 +      if (!os_strncasecmp("GetDeviceInfo", action, action_len))
 +              ret = web_process_get_device_info(sm, &reply, &replyname);
 +      else if (!os_strncasecmp("PutMessage", action, action_len))
 +              ret = web_process_put_message(sm, data, &reply, &replyname);
 +      else if (!os_strncasecmp("PutWLANResponse", action, action_len))
 +              ret = web_process_put_wlan_response(sm, data, &reply,
 +                                                  &replyname);
 +      else if (!os_strncasecmp("SetSelectedRegistrar", action, action_len))
 +              ret = web_process_set_selected_registrar(sm, cli, data, &reply,
 +                                                       &replyname);
 +      else
 +              wpa_printf(MSG_INFO, "WPS UPnP: Unknown POST type");
 +
 +bad:
 +      if (ret != HTTP_OK)
 +              wpa_printf(MSG_INFO, "WPS UPnP: POST failure ret=%d", ret);
 +      web_connection_send_reply(req, ret, action, action_len, reply,
 +                                replyname);
 +      wpabuf_free(reply);
 +}
 +
 +
 +/* Given that we have received a header w/ SUBSCRIBE, act upon it
 + *
 + * Format of SUBSCRIBE (case-insensitive):
 + *
 + * First line must be:
 + *      SUBSCRIBE /wps_event HTTP/1.1
 + *
 + * Our response (if no error) which includes only required lines is:
 + * HTTP/1.1 200 OK
 + * Server: xx, UPnP/1.0, xx
 + * SID: uuid:xxxxxxxxx
 + * Timeout: Second-<n>
 + * Content-Length: 0
 + * Date: xxxx
 + *
 + * Header lines must end with \r\n
 + * Per RFC 2616, content-length: is not required but connection:close
 + * would appear to be required (given that we will be closing it!).
 + */
 +static void web_connection_parse_subscribe(struct upnp_wps_device_sm *sm,
 +                                         struct http_request *req,
 +                                         const char *filename)
 +{
 +      struct wpabuf *buf;
 +      char *b;
 +      char *hdr = http_request_get_hdr(req);
 +      char *h;
 +      char *match;
 +      int match_len;
 +      char *end;
 +      int len;
 +      int got_nt = 0;
 +      u8 uuid[UUID_LEN];
 +      int got_uuid = 0;
 +      char *callback_urls = NULL;
 +      struct subscription *s = NULL;
 +      enum http_reply_code ret = HTTP_INTERNAL_SERVER_ERROR;
 +
 +      buf = wpabuf_alloc(1000);
 +      if (buf == NULL) {
 +              http_request_deinit(req);
 +              return;
 +      }
 +
 +      wpa_hexdump_ascii(MSG_DEBUG, "WPS UPnP: HTTP SUBSCRIBE",
 +                        (u8 *) hdr, os_strlen(hdr));
 +
 +      /* Parse/validate headers */
 +      h = hdr;
 +      /* First line: SUBSCRIBE /wps_event HTTP/1.1
 +       * has already been parsed.
 +       */
 +      if (os_strcasecmp(filename, UPNP_WPS_DEVICE_EVENT_FILE) != 0) {
 +              ret = HTTP_PRECONDITION_FAILED;
 +              goto error;
 +      }
 +      wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP SUBSCRIBE for event");
 +      end = os_strchr(h, '\n');
 +
 +      while (end) {
 +              /* Option line by option line */
 +              h = end + 1;
 +              end = os_strchr(h, '\n');
 +              if (end == NULL)
 +                      break; /* no unterminated lines allowed */
 +
 +              /* NT assures that it is our type of subscription;
 +               * not used for a renewal.
 +               **/
 +              match = "NT:";
 +              match_len = os_strlen(match);
 +              if (os_strncasecmp(h, match, match_len) == 0) {
 +                      h += match_len;
 +                      while (*h == ' ' || *h == '\t')
 +                              h++;
 +                      match = "upnp:event";
 +                      match_len = os_strlen(match);
 +                      if (os_strncasecmp(h, match, match_len) != 0) {
 +                              ret = HTTP_BAD_REQUEST;
 +                              goto error;
 +                      }
 +                      got_nt = 1;
 +                      continue;
 +              }
 +              /* HOST should refer to us */
 +#if 0
 +              match = "HOST:";
 +              match_len = os_strlen(match);
 +              if (os_strncasecmp(h, match, match_len) == 0) {
 +                      h += match_len;
 +                      while (*h == ' ' || *h == '\t')
 +                              h++;
 +                      .....
 +              }
 +#endif
 +              /* CALLBACK gives one or more URLs for NOTIFYs
 +               * to be sent as a result of the subscription.
 +               * Each URL is enclosed in angle brackets.
 +               */
 +              match = "CALLBACK:";
 +              match_len = os_strlen(match);
 +              if (os_strncasecmp(h, match, match_len) == 0) {
 +                      h += match_len;
 +                      while (*h == ' ' || *h == '\t')
 +                              h++;
 +                      len = end - h;
 +                      os_free(callback_urls);
 +                      callback_urls = dup_binstr(h, len);
 +                      if (callback_urls == NULL) {
 +                              ret = HTTP_INTERNAL_SERVER_ERROR;
 +                              goto error;
 +                      }
++                      if (len > 0 && callback_urls[len - 1] == '\r')
++                              callback_urls[len - 1] = '\0';
 +                      continue;
 +              }
 +              /* SID is only for renewal */
 +              match = "SID:";
 +              match_len = os_strlen(match);
 +              if (os_strncasecmp(h, match, match_len) == 0) {
 +                      h += match_len;
 +                      while (*h == ' ' || *h == '\t')
 +                              h++;
 +                      match = "uuid:";
 +                      match_len = os_strlen(match);
 +                      if (os_strncasecmp(h, match, match_len) != 0) {
 +                              ret = HTTP_BAD_REQUEST;
 +                              goto error;
 +                      }
 +                      h += match_len;
 +                      while (*h == ' ' || *h == '\t')
 +                              h++;
 +                      if (uuid_str2bin(h, uuid)) {
 +                              ret = HTTP_BAD_REQUEST;
 +                              goto error;
 +                      }
 +                      got_uuid = 1;
 +                      continue;
 +              }
 +              /* TIMEOUT is requested timeout, but apparently we can
 +               * just ignore this.
 +               */
 +      }
 +
 +      if (got_uuid) {
 +              /* renewal */
 +              wpa_printf(MSG_DEBUG, "WPS UPnP: Subscription renewal");
 +              if (callback_urls) {
 +                      ret = HTTP_BAD_REQUEST;
 +                      goto error;
 +              }
 +              s = subscription_renew(sm, uuid);
 +              if (s == NULL) {
 +                      char str[80];
 +                      uuid_bin2str(uuid, str, sizeof(str));
 +                      wpa_printf(MSG_DEBUG, "WPS UPnP: Could not find "
 +                                 "SID %s", str);
 +                      ret = HTTP_PRECONDITION_FAILED;
 +                      goto error;
 +              }
 +      } else if (callback_urls) {
 +              wpa_printf(MSG_DEBUG, "WPS UPnP: New subscription");
 +              if (!got_nt) {
 +                      ret = HTTP_PRECONDITION_FAILED;
 +                      goto error;
 +              }
 +              s = subscription_start(sm, callback_urls);
 +              if (s == NULL) {
 +                      ret = HTTP_INTERNAL_SERVER_ERROR;
 +                      goto error;
 +              }
 +      } else {
 +              ret = HTTP_PRECONDITION_FAILED;
 +              goto error;
 +      }
 +
 +      /* success */
 +      http_put_reply_code(buf, HTTP_OK);
 +      wpabuf_put_str(buf, http_server_hdr);
 +      wpabuf_put_str(buf, http_connection_close);
 +      wpabuf_put_str(buf, "Content-Length: 0\r\n");
 +      wpabuf_put_str(buf, "SID: uuid:");
 +      /* subscription id */
 +      b = wpabuf_put(buf, 0);
 +      uuid_bin2str(s->uuid, b, 80);
 +      wpa_printf(MSG_DEBUG, "WPS UPnP: Assigned SID %s", b);
 +      wpabuf_put(buf, os_strlen(b));
 +      wpabuf_put_str(buf, "\r\n");
 +      wpabuf_printf(buf, "Timeout: Second-%d\r\n", UPNP_SUBSCRIBE_SEC);
 +      http_put_date(buf);
 +      /* And empty line to terminate header: */
 +      wpabuf_put_str(buf, "\r\n");
 +
 +      os_free(callback_urls);
 +      http_request_send_and_deinit(req, buf);
 +      return;
 +
 +error:
 +      /* Per UPnP spec:
 +      * Errors
 +      * Incompatible headers
 +      *   400 Bad Request. If SID header and one of NT or CALLBACK headers
 +      *     are present, the publisher must respond with HTTP error
 +      *     400 Bad Request.
 +      * Missing or invalid CALLBACK
 +      *   412 Precondition Failed. If CALLBACK header is missing or does not
 +      *     contain a valid HTTP URL, the publisher must respond with HTTP
 +      *     error 412 Precondition Failed.
 +      * Invalid NT
 +      *   412 Precondition Failed. If NT header does not equal upnp:event,
 +      *     the publisher must respond with HTTP error 412 Precondition
 +      *     Failed.
 +      * [For resubscription, use 412 if unknown uuid].
 +      * Unable to accept subscription
 +      *   5xx. If a publisher is not able to accept a subscription (such as
 +      *     due to insufficient resources), it must respond with a
 +      *     HTTP 500-series error code.
 +      *   599 Too many subscriptions (not a standard HTTP error)
 +      */
 +      wpa_printf(MSG_DEBUG, "WPS UPnP: SUBSCRIBE failed - return %d", ret);
 +      http_put_empty(buf, ret);
 +      http_request_send_and_deinit(req, buf);
 +      os_free(callback_urls);
 +}
 +
 +
 +/* Given that we have received a header w/ UNSUBSCRIBE, act upon it
 + *
 + * Format of UNSUBSCRIBE (case-insensitive):
 + *
 + * First line must be:
 + *      UNSUBSCRIBE /wps_event HTTP/1.1
 + *
 + * Our response (if no error) which includes only required lines is:
 + * HTTP/1.1 200 OK
 + * Content-Length: 0
 + *
 + * Header lines must end with \r\n
 + * Per RFC 2616, content-length: is not required but connection:close
 + * would appear to be required (given that we will be closing it!).
 + */
 +static void web_connection_parse_unsubscribe(struct upnp_wps_device_sm *sm,
 +                                           struct http_request *req,
 +                                           const char *filename)
 +{
 +      struct wpabuf *buf;
 +      char *hdr = http_request_get_hdr(req);
 +      char *h;
 +      char *match;
 +      int match_len;
 +      char *end;
 +      u8 uuid[UUID_LEN];
 +      int got_uuid = 0;
 +      struct subscription *s = NULL;
 +      enum http_reply_code ret = HTTP_INTERNAL_SERVER_ERROR;
 +
 +      /* Parse/validate headers */
 +      h = hdr;
 +      /* First line: UNSUBSCRIBE /wps_event HTTP/1.1
 +       * has already been parsed.
 +       */
 +      if (os_strcasecmp(filename, UPNP_WPS_DEVICE_EVENT_FILE) != 0) {
 +              ret = HTTP_PRECONDITION_FAILED;
 +              goto send_msg;
 +      }
 +      wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP UNSUBSCRIBE for event");
 +      end = os_strchr(h, '\n');
 +
 +      while (end) {
 +              /* Option line by option line */
 +              h = end + 1;
 +              end = os_strchr(h, '\n');
 +              if (end == NULL)
 +                      break; /* no unterminated lines allowed */
 +
 +              /* HOST should refer to us */
 +#if 0
 +              match = "HOST:";
 +              match_len = os_strlen(match);
 +              if (os_strncasecmp(h, match, match_len) == 0) {
 +                      h += match_len;
 +                      while (*h == ' ' || *h == '\t')
 +                              h++;
 +                      .....
 +              }
 +#endif
 +              match = "SID:";
 +              match_len = os_strlen(match);
 +              if (os_strncasecmp(h, match, match_len) == 0) {
 +                      h += match_len;
 +                      while (*h == ' ' || *h == '\t')
 +                              h++;
 +                      match = "uuid:";
 +                      match_len = os_strlen(match);
 +                      if (os_strncasecmp(h, match, match_len) != 0) {
 +                              ret = HTTP_BAD_REQUEST;
 +                              goto send_msg;
 +                      }
 +                      h += match_len;
 +                      while (*h == ' ' || *h == '\t')
 +                              h++;
 +                      if (uuid_str2bin(h, uuid)) {
 +                              ret = HTTP_BAD_REQUEST;
 +                              goto send_msg;
 +                      }
 +                      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) {
++              char str[80];
++
++              uuid_bin2str(uuid, str, sizeof(str));
++
 +              s = subscription_find(sm, uuid);
 +              if (s) {
 +                      struct subscr_addr *sa;
 +                      sa = dl_list_first(&s->addr_list, struct subscr_addr,
 +                                         list);
-                       wpa_printf(MSG_INFO, "WPS UPnP: Could not find matching subscription to unsubscribe");
++                      wpa_printf(MSG_DEBUG,
++                                 "WPS UPnP: Unsubscribing %p (SID %s) %s",
++                                 s, str, (sa && sa->domain_and_port) ?
 +                                 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 (SID %s)",
++                                 str);
 +                      ret = HTTP_PRECONDITION_FAILED;
 +                      goto send_msg;
 +              }
 +      } else {
 +              wpa_printf(MSG_INFO, "WPS UPnP: Unsubscribe fails (not "
 +                         "found)");
 +              ret = HTTP_PRECONDITION_FAILED;
 +              goto send_msg;
 +      }
 +
 +      ret = HTTP_OK;
 +
 +send_msg:
 +      buf = wpabuf_alloc(200);
 +      if (buf == NULL) {
 +              http_request_deinit(req);
 +              return;
 +      }
 +      http_put_empty(buf, ret);
 +      http_request_send_and_deinit(req, buf);
 +}
 +
 +
 +/* Send error in response to unknown requests */
 +static void web_connection_unimplemented(struct http_request *req)
 +{
 +      struct wpabuf *buf;
 +      buf = wpabuf_alloc(200);
 +      if (buf == NULL) {
 +              http_request_deinit(req);
 +              return;
 +      }
 +      http_put_empty(buf, HTTP_UNIMPLEMENTED);
 +      http_request_send_and_deinit(req, buf);
 +}
 +
 +
 +
 +/* Called when we have gotten an apparently valid http request.
 + */
 +static void web_connection_check_data(void *ctx, struct http_request *req)
 +{
 +      struct upnp_wps_device_sm *sm = ctx;
 +      enum httpread_hdr_type htype = http_request_get_type(req);
 +      char *filename = http_request_get_uri(req);
 +      struct sockaddr_in *cli = http_request_get_cli_addr(req);
 +
 +      if (!filename) {
 +              wpa_printf(MSG_INFO, "WPS UPnP: Could not get HTTP URI");
 +              http_request_deinit(req);
 +              return;
 +      }
 +      /* Trim leading slashes from filename */
 +      while (*filename == '/')
 +              filename++;
 +
 +      wpa_printf(MSG_DEBUG, "WPS UPnP: Got HTTP request type %d from %s:%d",
 +                 htype, inet_ntoa(cli->sin_addr), htons(cli->sin_port));
 +
 +      switch (htype) {
 +      case HTTPREAD_HDR_TYPE_GET:
 +              web_connection_parse_get(sm, req, filename);
 +              break;
 +      case HTTPREAD_HDR_TYPE_POST:
 +              web_connection_parse_post(sm, cli, req, filename);
 +              break;
 +      case HTTPREAD_HDR_TYPE_SUBSCRIBE:
 +              web_connection_parse_subscribe(sm, req, filename);
 +              break;
 +      case HTTPREAD_HDR_TYPE_UNSUBSCRIBE:
 +              web_connection_parse_unsubscribe(sm, req, filename);
 +              break;
 +
 +              /* We are not required to support M-POST; just plain
 +               * POST is supposed to work, so we only support that.
 +               * If for some reason we need to support M-POST, it is
 +               * mostly the same as POST, with small differences.
 +               */
 +      default:
 +              /* Send 501 for anything else */
 +              web_connection_unimplemented(req);
 +              break;
 +      }
 +}
 +
 +
 +/*
 + * Listening for web connections
 + * We have a single TCP listening port, and hand off connections as we get
 + * them.
 + */
 +
 +void web_listener_stop(struct upnp_wps_device_sm *sm)
 +{
 +      http_server_deinit(sm->web_srv);
 +      sm->web_srv = NULL;
 +}
 +
 +
 +int web_listener_start(struct upnp_wps_device_sm *sm)
 +{
 +      struct in_addr addr;
 +      addr.s_addr = sm->ip_addr;
 +      sm->web_srv = http_server_init(&addr, -1, web_connection_check_data,
 +                                     sm);
 +      if (sm->web_srv == NULL) {
 +              web_listener_stop(sm);
 +              return -1;
 +      }
 +      sm->web_port = http_server_get_port(sm->web_srv);
 +
 +      return 0;
 +}
index 1c6a14bce4bcd31edbc8e67bee3338d6a1cbdece,0000000000000000000000000000000000000000..267b565e47847d82b6af7eec5996f2cc60ade670
mode 100644,000000..100644
--- /dev/null
@@@ -1,1975 -1,0 +1,1977 @@@
 +/*
 + * Wi-Fi Protected Setup - Strict protocol validation routines
 + * Copyright (c) 2010, Atheros Communications, 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 "wps_i.h"
 +#include "wps.h"
 +
 +
 +#ifndef WPS_STRICT_ALL
 +#define WPS_STRICT_WPS2
 +#endif /* WPS_STRICT_ALL */
 +
 +
 +static int wps_validate_version(const u8 *version, int mandatory)
 +{
 +      if (version == NULL) {
 +              if (mandatory) {
 +                      wpa_printf(MSG_INFO, "WPS-STRICT: Version attribute "
 +                                 "missing");
 +                      return -1;
 +              }
 +              return 0;
 +      }
 +      if (*version != 0x10) {
 +              wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Version attribute "
 +                         "value 0x%x", *version);
 +              return -1;
 +      }
 +      return 0;
 +}
 +
 +
 +static int wps_validate_version2(const u8 *version2, int mandatory)
 +{
 +      if (version2 == NULL) {
 +              if (mandatory) {
 +                      wpa_printf(MSG_INFO, "WPS-STRICT: Version2 attribute "
 +                                 "missing");
 +                      return -1;
 +              }
 +              return 0;
 +      }
 +      if (*version2 < 0x20) {
 +              wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Version2 attribute "
 +                         "value 0x%x", *version2);
 +              return -1;
 +      }
 +      return 0;
 +}
 +
 +
 +static int wps_validate_request_type(const u8 *request_type, int mandatory)
 +{
 +      if (request_type == NULL) {
 +              if (mandatory) {
 +                      wpa_printf(MSG_INFO, "WPS-STRICT: Request Type "
 +                                 "attribute missing");
 +                      return -1;
 +              }
 +              return 0;
 +      }
 +      if (*request_type > 0x03) {
 +              wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Request Type "
 +                         "attribute value 0x%x", *request_type);
 +              return -1;
 +      }
 +      return 0;
 +}
 +
 +
 +static int wps_validate_response_type(const u8 *response_type, int mandatory)
 +{
 +      if (response_type == NULL) {
 +              if (mandatory) {
 +                      wpa_printf(MSG_INFO, "WPS-STRICT: Response Type "
 +                                 "attribute missing");
 +                      return -1;
 +              }
 +              return 0;
 +      }
 +      if (*response_type > 0x03) {
 +              wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Response Type "
 +                         "attribute value 0x%x", *response_type);
 +              return -1;
 +      }
 +      return 0;
 +}
 +
 +
 +static int valid_config_methods(u16 val, int wps2)
 +{
 +      if (wps2) {
 +              if ((val & 0x6000) && !(val & WPS_CONFIG_DISPLAY)) {
 +                      wpa_printf(MSG_INFO, "WPS-STRICT: Physical/Virtual "
 +                                 "Display flag without old Display flag "
 +                                 "set");
 +                      return 0;
 +              }
 +              if (!(val & 0x6000) && (val & WPS_CONFIG_DISPLAY)) {
 +                      wpa_printf(MSG_INFO, "WPS-STRICT: Display flag "
 +                                 "without Physical/Virtual Display flag");
 +                      return 0;
 +              }
 +              if ((val & 0x0600) && !(val & WPS_CONFIG_PUSHBUTTON)) {
 +                      wpa_printf(MSG_INFO, "WPS-STRICT: Physical/Virtual "
 +                                 "PushButton flag without old PushButton "
 +                                 "flag set");
 +                      return 0;
 +              }
 +              if (!(val & 0x0600) && (val & WPS_CONFIG_PUSHBUTTON)) {
 +                      wpa_printf(MSG_INFO, "WPS-STRICT: PushButton flag "
 +                                 "without Physical/Virtual PushButton flag");
 +                      return 0;
 +              }
 +      }
 +
 +      return 1;
 +}
 +
 +
 +static int wps_validate_config_methods(const u8 *config_methods, int wps2,
 +                                     int mandatory)
 +{
 +      u16 val;
 +
 +      if (config_methods == NULL) {
 +              if (mandatory) {
 +                      wpa_printf(MSG_INFO, "WPS-STRICT: Configuration "
 +                                 "Methods attribute missing");
 +                      return -1;
 +              }
 +              return 0;
 +      }
 +
 +      val = WPA_GET_BE16(config_methods);
 +      if (!valid_config_methods(val, wps2)) {
 +              wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Configuration "
 +                         "Methods attribute value 0x%04x", val);
 +              return -1;
 +      }
 +      return 0;
 +}
 +
 +
 +static int wps_validate_ap_config_methods(const u8 *config_methods, int wps2,
 +                                        int mandatory)
 +{
 +      u16 val;
 +
 +      if (wps_validate_config_methods(config_methods, wps2, mandatory) < 0)
 +              return -1;
 +      if (config_methods == NULL)
 +              return 0;
 +      val = WPA_GET_BE16(config_methods);
 +      if (val & WPS_CONFIG_PUSHBUTTON) {
 +              wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Configuration "
 +                         "Methods attribute value 0x%04x in AP info "
 +                         "(PushButton not allowed for registering new ER)",
 +                         val);
 +              return -1;
 +      }
 +      return 0;
 +}
 +
 +
 +static int wps_validate_uuid_e(const u8 *uuid_e, int mandatory)
 +{
 +      if (uuid_e == NULL) {
 +              if (mandatory) {
 +                      wpa_printf(MSG_INFO, "WPS-STRICT: UUID-E "
 +                                 "attribute missing");
 +                      return -1;
 +              }
 +              return 0;
 +      }
 +      return 0;
 +}
 +
 +
 +static int wps_validate_uuid_r(const u8 *uuid_r, int mandatory)
 +{
 +      if (uuid_r == NULL) {
 +              if (mandatory) {
 +                      wpa_printf(MSG_INFO, "WPS-STRICT: UUID-R "
 +                                 "attribute missing");
 +                      return -1;
 +              }
 +              return 0;
 +      }
 +      return 0;
 +}
 +
 +
 +static int wps_validate_primary_dev_type(const u8 *primary_dev_type,
 +                                       int mandatory)
 +{
 +      if (primary_dev_type == NULL) {
 +              if (mandatory) {
 +                      wpa_printf(MSG_INFO, "WPS-STRICT: Primary Device Type "
 +                                 "attribute missing");
 +                      return -1;
 +              }
 +              return 0;
 +      }
 +      return 0;
 +}
 +
 +
 +static int wps_validate_rf_bands(const u8 *rf_bands, int mandatory)
 +{
 +      if (rf_bands == NULL) {
 +              if (mandatory) {
 +                      wpa_printf(MSG_INFO, "WPS-STRICT: RF Bands "
 +                                 "attribute missing");
 +                      return -1;
 +              }
 +              return 0;
 +      }
 +      if (*rf_bands != WPS_RF_24GHZ && *rf_bands != WPS_RF_50GHZ &&
++          *rf_bands != WPS_RF_60GHZ &&
++          *rf_bands != (WPS_RF_24GHZ | WPS_RF_50GHZ | WPS_RF_60GHZ) &&
 +          *rf_bands != (WPS_RF_24GHZ | WPS_RF_50GHZ)) {
 +              wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Rf Bands "
 +                         "attribute value 0x%x", *rf_bands);
 +              return -1;
 +      }
 +      return 0;
 +}
 +
 +
 +static int wps_validate_assoc_state(const u8 *assoc_state, int mandatory)
 +{
 +      u16 val;
 +      if (assoc_state == NULL) {
 +              if (mandatory) {
 +                      wpa_printf(MSG_INFO, "WPS-STRICT: Association State "
 +                                 "attribute missing");
 +                      return -1;
 +              }
 +              return 0;
 +      }
 +      val = WPA_GET_BE16(assoc_state);
 +      if (val > 4) {
 +              wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Association State "
 +                         "attribute value 0x%04x", val);
 +              return -1;
 +      }
 +      return 0;
 +}
 +
 +
 +static int wps_validate_config_error(const u8 *config_error, int mandatory)
 +{
 +      u16 val;
 +
 +      if (config_error == NULL) {
 +              if (mandatory) {
 +                      wpa_printf(MSG_INFO, "WPS-STRICT: Configuration Error "
 +                                 "attribute missing");
 +                      return -1;
 +              }
 +              return 0;
 +      }
 +      val = WPA_GET_BE16(config_error);
 +      if (val > 20) {
 +              wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Configuration Error "
 +                         "attribute value 0x%04x", val);
 +              return -1;
 +      }
 +      return 0;
 +}
 +
 +
 +static int wps_validate_dev_password_id(const u8 *dev_password_id,
 +                                      int mandatory)
 +{
 +      u16 val;
 +
 +      if (dev_password_id == NULL) {
 +              if (mandatory) {
 +                      wpa_printf(MSG_INFO, "WPS-STRICT: Device Password ID "
 +                                 "attribute missing");
 +                      return -1;
 +              }
 +              return 0;
 +      }
 +      val = WPA_GET_BE16(dev_password_id);
 +      if (val >= 0x0008 && val <= 0x000f) {
 +              wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Device Password ID "
 +                         "attribute value 0x%04x", val);
 +              return -1;
 +      }
 +      return 0;
 +}
 +
 +
 +static int wps_validate_manufacturer(const u8 *manufacturer, size_t len,
 +                                   int mandatory)
 +{
 +      if (manufacturer == NULL) {
 +              if (mandatory) {
 +                      wpa_printf(MSG_INFO, "WPS-STRICT: Manufacturer "
 +                                 "attribute missing");
 +                      return -1;
 +              }
 +              return 0;
 +      }
 +      if (len > 0 && manufacturer[len - 1] == 0) {
 +              wpa_hexdump_ascii(MSG_INFO, "WPS-STRICT: Invalid Manufacturer "
 +                         "attribute value", manufacturer, len);
 +              return -1;
 +      }
 +      return 0;
 +}
 +
 +
 +static int wps_validate_model_name(const u8 *model_name, size_t len,
 +                                 int mandatory)
 +{
 +      if (model_name == NULL) {
 +              if (mandatory) {
 +                      wpa_printf(MSG_INFO, "WPS-STRICT: Model Name "
 +                                 "attribute missing");
 +                      return -1;
 +              }
 +              return 0;
 +      }
 +      if (len > 0 && model_name[len - 1] == 0) {
 +              wpa_hexdump_ascii(MSG_INFO, "WPS-STRICT: Invalid Model Name "
 +                         "attribute value", model_name, len);
 +              return -1;
 +      }
 +      return 0;
 +}
 +
 +
 +static int wps_validate_model_number(const u8 *model_number, size_t len,
 +                                   int mandatory)
 +{
 +      if (model_number == NULL) {
 +              if (mandatory) {
 +                      wpa_printf(MSG_INFO, "WPS-STRICT: Model Number "
 +                                 "attribute missing");
 +                      return -1;
 +              }
 +              return 0;
 +      }
 +      if (len > 0 && model_number[len - 1] == 0) {
 +              wpa_hexdump_ascii(MSG_INFO, "WPS-STRICT: Invalid Model Number "
 +                         "attribute value", model_number, len);
 +              return -1;
 +      }
 +      return 0;
 +}
 +
 +
 +static int wps_validate_serial_number(const u8 *serial_number, size_t len,
 +                                    int mandatory)
 +{
 +      if (serial_number == NULL) {
 +              if (mandatory) {
 +                      wpa_printf(MSG_INFO, "WPS-STRICT: Serial Number "
 +                                 "attribute missing");
 +                      return -1;
 +              }
 +              return 0;
 +      }
 +      if (len > 0 && serial_number[len - 1] == 0) {
 +              wpa_hexdump_ascii(MSG_INFO, "WPS-STRICT: Invalid Serial "
 +                                "Number attribute value",
 +                                serial_number, len);
 +              return -1;
 +      }
 +      return 0;
 +}
 +
 +
 +static int wps_validate_dev_name(const u8 *dev_name, size_t len,
 +                               int mandatory)
 +{
 +      if (dev_name == NULL) {
 +              if (mandatory) {
 +                      wpa_printf(MSG_INFO, "WPS-STRICT: Device Name "
 +                                 "attribute missing");
 +                      return -1;
 +              }
 +              return 0;
 +      }
 +      if (len > 0 && dev_name[len - 1] == 0) {
 +              wpa_hexdump_ascii(MSG_INFO, "WPS-STRICT: Invalid Device Name "
 +                         "attribute value", dev_name, len);
 +              return -1;
 +      }
 +      return 0;
 +}
 +
 +
 +static int wps_validate_request_to_enroll(const u8 *request_to_enroll,
 +                                        int mandatory)
 +{
 +      if (request_to_enroll == NULL) {
 +              if (mandatory) {
 +                      wpa_printf(MSG_INFO, "WPS-STRICT: Request to Enroll "
 +                                 "attribute missing");
 +                      return -1;
 +              }
 +              return 0;
 +      }
 +      if (*request_to_enroll > 0x01) {
 +              wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Request to Enroll "
 +                         "attribute value 0x%x", *request_to_enroll);
 +              return -1;
 +      }
 +      return 0;
 +}
 +
 +
 +static int wps_validate_req_dev_type(const u8 *req_dev_type[], size_t num,
 +                                   int mandatory)
 +{
 +      if (num == 0) {
 +              if (mandatory) {
 +                      wpa_printf(MSG_INFO, "WPS-STRICT: Requested Device "
 +                                 "Type attribute missing");
 +                      return -1;
 +              }
 +              return 0;
 +      }
 +      return 0;
 +}
 +
 +
 +static int wps_validate_wps_state(const u8 *wps_state, int mandatory)
 +{
 +      if (wps_state == NULL) {
 +              if (mandatory) {
 +                      wpa_printf(MSG_INFO, "WPS-STRICT: Wi-Fi Protected "
 +                                 "Setup State attribute missing");
 +                      return -1;
 +              }
 +              return 0;
 +      }
 +      if (*wps_state != WPS_STATE_NOT_CONFIGURED &&
 +          *wps_state != WPS_STATE_CONFIGURED) {
 +              wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Wi-Fi Protected "
 +                         "Setup State attribute value 0x%x", *wps_state);
 +              return -1;
 +      }
 +      return 0;
 +}
 +
 +
 +static int wps_validate_ap_setup_locked(const u8 *ap_setup_locked,
 +                                      int mandatory)
 +{
 +      if (ap_setup_locked == NULL) {
 +              if (mandatory) {
 +                      wpa_printf(MSG_INFO, "WPS-STRICT: AP Setup Locked "
 +                                 "attribute missing");
 +                      return -1;
 +              }
 +              return 0;
 +      }
 +      if (*ap_setup_locked > 1) {
 +              wpa_printf(MSG_INFO, "WPS-STRICT: Invalid AP Setup Locked "
 +                         "attribute value 0x%x", *ap_setup_locked);
 +              return -1;
 +      }
 +      return 0;
 +}
 +
 +
 +static int wps_validate_selected_registrar(const u8 *selected_registrar,
 +                                         int mandatory)
 +{
 +      if (selected_registrar == NULL) {
 +              if (mandatory) {
 +                      wpa_printf(MSG_INFO, "WPS-STRICT: Selected Registrar "
 +                                 "attribute missing");
 +                      return -1;
 +              }
 +              return 0;
 +      }
 +      if (*selected_registrar > 1) {
 +              wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Selected Registrar "
 +                         "attribute value 0x%x", *selected_registrar);
 +              return -1;
 +      }
 +      return 0;
 +}
 +
 +
 +static int wps_validate_sel_reg_config_methods(const u8 *config_methods,
 +                                             int wps2, int mandatory)
 +{
 +      u16 val;
 +
 +      if (config_methods == NULL) {
 +              if (mandatory) {
 +                      wpa_printf(MSG_INFO, "WPS-STRICT: Selected Registrar "
 +                                 "Configuration Methods attribute missing");
 +                      return -1;
 +              }
 +              return 0;
 +      }
 +
 +      val = WPA_GET_BE16(config_methods);
 +      if (!valid_config_methods(val, wps2)) {
 +              wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Selected Registrar "
 +                         "Configuration Methods attribute value 0x%04x",
 +                         val);
 +              return -1;
 +      }
 +      return 0;
 +}
 +
 +
 +static int wps_validate_authorized_macs(const u8 *authorized_macs, size_t len,
 +                                      int mandatory)
 +{
 +      if (authorized_macs == NULL) {
 +              if (mandatory) {
 +                      wpa_printf(MSG_INFO, "WPS-STRICT: Authorized MACs "
 +                                 "attribute missing");
 +                      return -1;
 +              }
 +              return 0;
 +      }
 +      if (len > 30 && (len % ETH_ALEN) != 0) {
 +              wpa_hexdump(MSG_INFO, "WPS-STRICT: Invalid Authorized "
 +                          "MACs attribute value", authorized_macs, len);
 +              return -1;
 +      }
 +      return 0;
 +}
 +
 +
 +static int wps_validate_msg_type(const u8 *msg_type, int mandatory)
 +{
 +      if (msg_type == NULL) {
 +              if (mandatory) {
 +                      wpa_printf(MSG_INFO, "WPS-STRICT: Message Type "
 +                                 "attribute missing");
 +                      return -1;
 +              }
 +              return 0;
 +      }
 +      if (*msg_type < WPS_Beacon || *msg_type > WPS_WSC_DONE) {
 +              wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Message Type "
 +                         "attribute value 0x%x", *msg_type);
 +              return -1;
 +      }
 +      return 0;
 +}
 +
 +
 +static int wps_validate_mac_addr(const u8 *mac_addr, int mandatory)
 +{
 +      if (mac_addr == NULL) {
 +              if (mandatory) {
 +                      wpa_printf(MSG_INFO, "WPS-STRICT: MAC Address "
 +                                 "attribute missing");
 +                      return -1;
 +              }
 +              return 0;
 +      }
 +      if (mac_addr[0] & 0x01) {
 +              wpa_printf(MSG_INFO, "WPS-STRICT: Invalid MAC Address "
 +                         "attribute value " MACSTR, MAC2STR(mac_addr));
 +              return -1;
 +      }
 +      return 0;
 +}
 +
 +
 +static int wps_validate_enrollee_nonce(const u8 *enrollee_nonce, int mandatory)
 +{
 +      if (enrollee_nonce == NULL) {
 +              if (mandatory) {
 +                      wpa_printf(MSG_INFO, "WPS-STRICT: Enrollee Nonce "
 +                                 "attribute missing");
 +                      return -1;
 +              }
 +              return 0;
 +      }
 +      return 0;
 +}
 +
 +
 +static int wps_validate_registrar_nonce(const u8 *registrar_nonce,
 +                                      int mandatory)
 +{
 +      if (registrar_nonce == NULL) {
 +              if (mandatory) {
 +                      wpa_printf(MSG_INFO, "WPS-STRICT: Registrar Nonce "
 +                                 "attribute missing");
 +                      return -1;
 +              }
 +              return 0;
 +      }
 +      return 0;
 +}
 +
 +
 +static int wps_validate_public_key(const u8 *public_key, size_t len,
 +                                 int mandatory)
 +{
 +      if (public_key == NULL) {
 +              if (mandatory) {
 +                      wpa_printf(MSG_INFO, "WPS-STRICT: Public Key "
 +                                 "attribute missing");
 +                      return -1;
 +              }
 +              return 0;
 +      }
 +      if (len != 192) {
 +              wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Public Key "
 +                         "attribute length %d", (int) len);
 +              return -1;
 +      }
 +      return 0;
 +}
 +
 +
 +static int num_bits_set(u16 val)
 +{
 +      int c;
 +      for (c = 0; val; c++)
 +              val &= val - 1;
 +      return c;
 +}
 +
 +
 +static int wps_validate_auth_type_flags(const u8 *flags, int mandatory)
 +{
 +      u16 val;
 +
 +      if (flags == NULL) {
 +              if (mandatory) {
 +                      wpa_printf(MSG_INFO, "WPS-STRICT: Authentication Type "
 +                                 "Flags attribute missing");
 +                      return -1;
 +              }
 +              return 0;
 +      }
 +      val = WPA_GET_BE16(flags);
 +      if ((val & ~WPS_AUTH_TYPES) || !(val & WPS_AUTH_WPA2PSK)) {
 +              wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Authentication Type "
 +                         "Flags attribute value 0x%04x", val);
 +              return -1;
 +      }
 +      return 0;
 +}
 +
 +
 +static int wps_validate_auth_type(const u8 *type, int mandatory)
 +{
 +      u16 val;
 +
 +      if (type == NULL) {
 +              if (mandatory) {
 +                      wpa_printf(MSG_INFO, "WPS-STRICT: Authentication Type "
 +                                 "attribute missing");
 +                      return -1;
 +              }
 +              return 0;
 +      }
 +      val = WPA_GET_BE16(type);
 +      if ((val & ~WPS_AUTH_TYPES) || val == 0 ||
 +          (num_bits_set(val) > 1 &&
 +           val != (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK))) {
 +              wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Authentication Type "
 +                         "attribute value 0x%04x", val);
 +              return -1;
 +      }
 +      return 0;
 +}
 +
 +
 +static int wps_validate_encr_type_flags(const u8 *flags, int mandatory)
 +{
 +      u16 val;
 +
 +      if (flags == NULL) {
 +              if (mandatory) {
 +                      wpa_printf(MSG_INFO, "WPS-STRICT: Encryption Type "
 +                                 "Flags attribute missing");
 +                      return -1;
 +              }
 +              return 0;
 +      }
 +      val = WPA_GET_BE16(flags);
 +      if ((val & ~WPS_ENCR_TYPES) || !(val & WPS_ENCR_AES)) {
 +              wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Encryption Type "
 +                         "Flags attribute value 0x%04x", val);
 +              return -1;
 +      }
 +      return 0;
 +}
 +
 +
 +static int wps_validate_encr_type(const u8 *type, int mandatory)
 +{
 +      u16 val;
 +
 +      if (type == NULL) {
 +              if (mandatory) {
 +                      wpa_printf(MSG_INFO, "WPS-STRICT: Encryption Type "
 +                                 "attribute missing");
 +                      return -1;
 +              }
 +              return 0;
 +      }
 +      val = WPA_GET_BE16(type);
 +      if ((val & ~WPS_ENCR_TYPES) || val == 0 ||
 +          (num_bits_set(val) > 1 && val != (WPS_ENCR_TKIP | WPS_ENCR_AES))) {
 +              wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Encryption Type "
 +                         "attribute value 0x%04x", val);
 +              return -1;
 +      }
 +      return 0;
 +}
 +
 +
 +static int wps_validate_conn_type_flags(const u8 *flags, int mandatory)
 +{
 +      if (flags == NULL) {
 +              if (mandatory) {
 +                      wpa_printf(MSG_INFO, "WPS-STRICT: Connection Type "
 +                                 "Flags attribute missing");
 +                      return -1;
 +              }
 +              return 0;
 +      }
 +      if ((*flags & ~(WPS_CONN_ESS | WPS_CONN_IBSS)) ||
 +          !(*flags & WPS_CONN_ESS)) {
 +              wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Connection Type "
 +                         "Flags attribute value 0x%02x", *flags);
 +              return -1;
 +      }
 +      return 0;
 +}
 +
 +
 +static int wps_validate_os_version(const u8 *os_version, int mandatory)
 +{
 +      if (os_version == NULL) {
 +              if (mandatory) {
 +                      wpa_printf(MSG_INFO, "WPS-STRICT: OS Version "
 +                                 "attribute missing");
 +                      return -1;
 +              }
 +              return 0;
 +      }
 +      return 0;
 +}
 +
 +
 +static int wps_validate_authenticator(const u8 *authenticator, int mandatory)
 +{
 +      if (authenticator == NULL) {
 +              if (mandatory) {
 +                      wpa_printf(MSG_INFO, "WPS-STRICT: Authenticator "
 +                                 "attribute missing");
 +                      return -1;
 +              }
 +              return 0;
 +      }
 +      return 0;
 +}
 +
 +
 +static int wps_validate_e_hash1(const u8 *hash, int mandatory)
 +{
 +      if (hash == NULL) {
 +              if (mandatory) {
 +                      wpa_printf(MSG_INFO, "WPS-STRICT: E-Hash1 "
 +                                 "attribute missing");
 +                      return -1;
 +              }
 +              return 0;
 +      }
 +      return 0;
 +}
 +
 +
 +static int wps_validate_e_hash2(const u8 *hash, int mandatory)
 +{
 +      if (hash == NULL) {
 +              if (mandatory) {
 +                      wpa_printf(MSG_INFO, "WPS-STRICT: E-Hash2 "
 +                                 "attribute missing");
 +                      return -1;
 +              }
 +              return 0;
 +      }
 +      return 0;
 +}
 +
 +
 +static int wps_validate_r_hash1(const u8 *hash, int mandatory)
 +{
 +      if (hash == NULL) {
 +              if (mandatory) {
 +                      wpa_printf(MSG_INFO, "WPS-STRICT: R-Hash1 "
 +                                 "attribute missing");
 +                      return -1;
 +              }
 +              return 0;
 +      }
 +      return 0;
 +}
 +
 +
 +static int wps_validate_r_hash2(const u8 *hash, int mandatory)
 +{
 +      if (hash == NULL) {
 +              if (mandatory) {
 +                      wpa_printf(MSG_INFO, "WPS-STRICT: R-Hash2 "
 +                                 "attribute missing");
 +                      return -1;
 +              }
 +              return 0;
 +      }
 +      return 0;
 +}
 +
 +
 +static int wps_validate_encr_settings(const u8 *encr_settings, size_t len,
 +                                 int mandatory)
 +{
 +      if (encr_settings == NULL) {
 +              if (mandatory) {
 +                      wpa_printf(MSG_INFO, "WPS-STRICT: Encrypted Settings "
 +                                 "attribute missing");
 +                      return -1;
 +              }
 +              return 0;
 +      }
 +      if (len < 16) {
 +              wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Encrypted Settings "
 +                         "attribute length %d", (int) len);
 +              return -1;
 +      }
 +      return 0;
 +}
 +
 +
 +static int wps_validate_settings_delay_time(const u8 *delay, int mandatory)
 +{
 +      if (delay == NULL) {
 +              if (mandatory) {
 +                      wpa_printf(MSG_INFO, "WPS-STRICT: Settings Delay Time "
 +                                 "attribute missing");
 +                      return -1;
 +              }
 +              return 0;
 +      }
 +      return 0;
 +}
 +
 +
 +static int wps_validate_r_snonce1(const u8 *nonce, int mandatory)
 +{
 +      if (nonce == NULL) {
 +              if (mandatory) {
 +                      wpa_printf(MSG_INFO, "WPS-STRICT: R-SNonce1 "
 +                                 "attribute missing");
 +                      return -1;
 +              }
 +              return 0;
 +      }
 +      return 0;
 +}
 +
 +
 +static int wps_validate_r_snonce2(const u8 *nonce, int mandatory)
 +{
 +      if (nonce == NULL) {
 +              if (mandatory) {
 +                      wpa_printf(MSG_INFO, "WPS-STRICT: R-SNonce2 "
 +                                 "attribute missing");
 +                      return -1;
 +              }
 +              return 0;
 +      }
 +      return 0;
 +}
 +
 +
 +static int wps_validate_e_snonce1(const u8 *nonce, int mandatory)
 +{
 +      if (nonce == NULL) {
 +              if (mandatory) {
 +                      wpa_printf(MSG_INFO, "WPS-STRICT: E-SNonce1 "
 +                                 "attribute missing");
 +                      return -1;
 +              }
 +              return 0;
 +      }
 +      return 0;
 +}
 +
 +
 +static int wps_validate_e_snonce2(const u8 *nonce, int mandatory)
 +{
 +      if (nonce == NULL) {
 +              if (mandatory) {
 +                      wpa_printf(MSG_INFO, "WPS-STRICT: E-SNonce2 "
 +                                 "attribute missing");
 +                      return -1;
 +              }
 +              return 0;
 +      }
 +      return 0;
 +}
 +
 +
 +static int wps_validate_key_wrap_auth(const u8 *auth, int mandatory)
 +{
 +      if (auth == NULL) {
 +              if (mandatory) {
 +                      wpa_printf(MSG_INFO, "WPS-STRICT: Key Wrap "
 +                                 "Authenticator attribute missing");
 +                      return -1;
 +              }
 +              return 0;
 +      }
 +      return 0;
 +}
 +
 +
 +static int wps_validate_ssid(const u8 *ssid, size_t ssid_len, int mandatory)
 +{
 +      if (ssid == NULL) {
 +              if (mandatory) {
 +                      wpa_printf(MSG_INFO, "WPS-STRICT: SSID "
 +                                 "attribute missing");
 +                      return -1;
 +              }
 +              return 0;
 +      }
 +      if (ssid_len == 0 || ssid[ssid_len - 1] == 0) {
 +              wpa_hexdump_ascii(MSG_INFO, "WPS-STRICT: Invalid SSID "
 +                                "attribute value", ssid, ssid_len);
 +              return -1;
 +      }
 +      return 0;
 +}
 +
 +
 +static int wps_validate_network_key_index(const u8 *idx, int mandatory)
 +{
 +      if (idx == NULL) {
 +              if (mandatory) {
 +                      wpa_printf(MSG_INFO, "WPS-STRICT: Network Key Index "
 +                                 "attribute missing");
 +                      return -1;
 +              }
 +              return 0;
 +      }
 +      return 0;
 +}
 +
 +
 +static int wps_validate_network_idx(const u8 *idx, int mandatory)
 +{
 +      if (idx == NULL) {
 +              if (mandatory) {
 +                      wpa_printf(MSG_INFO, "WPS-STRICT: Network Index "
 +                                 "attribute missing");
 +                      return -1;
 +              }
 +              return 0;
 +      }
 +      return 0;
 +}
 +
 +
 +static int wps_validate_network_key(const u8 *key, size_t key_len,
 +                                  const u8 *encr_type, int mandatory)
 +{
 +      if (key == NULL) {
 +              if (mandatory) {
 +                      wpa_printf(MSG_INFO, "WPS-STRICT: Network Key "
 +                                 "attribute missing");
 +                      return -1;
 +              }
 +              return 0;
 +      }
 +      if (((encr_type == NULL || WPA_GET_BE16(encr_type) != WPS_ENCR_WEP) &&
 +           key_len > 8 && key_len < 64 && key[key_len - 1] == 0) ||
 +          key_len > 64) {
 +              wpa_hexdump_ascii_key(MSG_INFO, "WPS-STRICT: Invalid Network "
 +                                    "Key attribute value", key, key_len);
 +              return -1;
 +      }
 +      return 0;
 +}
 +
 +
 +static int wps_validate_network_key_shareable(const u8 *val, int mandatory)
 +{
 +      if (val == NULL) {
 +              if (mandatory) {
 +                      wpa_printf(MSG_INFO, "WPS-STRICT: Network Key "
 +                                 "Shareable attribute missing");
 +                      return -1;
 +              }
 +              return 0;
 +      }
 +      if (*val > 1) {
 +              wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Network Key "
 +                         "Shareable attribute value 0x%x", *val);
 +              return -1;
 +      }
 +      return 0;
 +}
 +
 +
 +static int wps_validate_cred(const u8 *cred, size_t len)
 +{
 +      struct wps_parse_attr attr;
 +      struct wpabuf buf;
 +
 +      if (cred == NULL)
 +              return -1;
 +      wpabuf_set(&buf, cred, len);
 +      if (wps_parse_msg(&buf, &attr) < 0) {
 +              wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse Credential");
 +              return -1;
 +      }
 +
 +      if (wps_validate_network_idx(attr.network_idx, 1) ||
 +          wps_validate_ssid(attr.ssid, attr.ssid_len, 1) ||
 +          wps_validate_auth_type(attr.auth_type, 1) ||
 +          wps_validate_encr_type(attr.encr_type, 1) ||
 +          wps_validate_network_key_index(attr.network_key_idx, 0) ||
 +          wps_validate_network_key(attr.network_key, attr.network_key_len,
 +                                   attr.encr_type, 1) ||
 +          wps_validate_mac_addr(attr.mac_addr, 1) ||
 +          wps_validate_network_key_shareable(attr.network_key_shareable, 0))
 +      {
 +              wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Credential");
 +              return -1;
 +      }
 +
 +
 +      return 0;
 +}
 +
 +
 +static int wps_validate_credential(const u8 *cred[], size_t len[], size_t num,
 +                                 int mandatory)
 +{
 +      size_t i;
 +
 +      if (num == 0) {
 +              if (mandatory) {
 +                      wpa_printf(MSG_INFO, "WPS-STRICT: Credential "
 +                                 "attribute missing");
 +                      return -1;
 +              }
 +              return 0;
 +      }
 +
 +      for (i = 0; i < num; i++) {
 +              if (wps_validate_cred(cred[i], len[i]) < 0)
 +                      return -1;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +int wps_validate_beacon(const struct wpabuf *wps_ie)
 +{
 +      struct wps_parse_attr attr;
 +      int wps2, sel_reg;
 +
 +      if (wps_ie == NULL) {
 +              wpa_printf(MSG_INFO, "WPS-STRICT: No WPS IE in Beacon frame");
 +              return -1;
 +      }
 +      if (wps_parse_msg(wps_ie, &attr) < 0) {
 +              wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse WPS IE in "
 +                         "Beacon frame");
 +              return -1;
 +      }
 +
 +      wps2 = attr.version2 != NULL;
 +      sel_reg = attr.selected_registrar != NULL &&
 +              *attr.selected_registrar != 0;
 +      if (wps_validate_version(attr.version, 1) ||
 +          wps_validate_wps_state(attr.wps_state, 1) ||
 +          wps_validate_ap_setup_locked(attr.ap_setup_locked, 0) ||
 +          wps_validate_selected_registrar(attr.selected_registrar, 0) ||
 +          wps_validate_dev_password_id(attr.dev_password_id, sel_reg) ||
 +          wps_validate_sel_reg_config_methods(attr.sel_reg_config_methods,
 +                                              wps2, sel_reg) ||
 +          wps_validate_uuid_e(attr.uuid_e, 0) ||
 +          wps_validate_rf_bands(attr.rf_bands, 0) ||
 +          wps_validate_version2(attr.version2, wps2) ||
 +          wps_validate_authorized_macs(attr.authorized_macs,
 +                                       attr.authorized_macs_len, 0)) {
 +              wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Beacon frame");
 +              return -1;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +int wps_validate_beacon_probe_resp(const struct wpabuf *wps_ie, int probe,
 +                                 const u8 *addr)
 +{
 +      struct wps_parse_attr attr;
 +      int wps2, sel_reg;
 +
 +      if (wps_ie == NULL) {
 +              wpa_printf(MSG_INFO, "WPS-STRICT: No WPS IE in "
 +                         "%sProbe Response frame", probe ? "" : "Beacon/");
 +              return -1;
 +      }
 +      if (wps_parse_msg(wps_ie, &attr) < 0) {
 +              wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse WPS IE in "
 +                         "%sProbe Response frame", probe ? "" : "Beacon/");
 +              return -1;
 +      }
 +
 +      wps2 = attr.version2 != NULL;
 +      sel_reg = attr.selected_registrar != NULL &&
 +              *attr.selected_registrar != 0;
 +      if (wps_validate_version(attr.version, 1) ||
 +          wps_validate_wps_state(attr.wps_state, 1) ||
 +          wps_validate_ap_setup_locked(attr.ap_setup_locked, 0) ||
 +          wps_validate_selected_registrar(attr.selected_registrar, 0) ||
 +          wps_validate_dev_password_id(attr.dev_password_id, sel_reg) ||
 +          wps_validate_sel_reg_config_methods(attr.sel_reg_config_methods,
 +                                              wps2, sel_reg) ||
 +          wps_validate_response_type(attr.response_type, probe) ||
 +          wps_validate_uuid_e(attr.uuid_e, probe) ||
 +          wps_validate_manufacturer(attr.manufacturer, attr.manufacturer_len,
 +                                    probe) ||
 +          wps_validate_model_name(attr.model_name, attr.model_name_len,
 +                                  probe) ||
 +          wps_validate_model_number(attr.model_number, attr.model_number_len,
 +                                    probe) ||
 +          wps_validate_serial_number(attr.serial_number,
 +                                     attr.serial_number_len, probe) ||
 +          wps_validate_primary_dev_type(attr.primary_dev_type, probe) ||
 +          wps_validate_dev_name(attr.dev_name, attr.dev_name_len, probe) ||
 +          wps_validate_ap_config_methods(attr.config_methods, wps2, probe) ||
 +          wps_validate_rf_bands(attr.rf_bands, 0) ||
 +          wps_validate_version2(attr.version2, wps2) ||
 +          wps_validate_authorized_macs(attr.authorized_macs,
 +                                       attr.authorized_macs_len, 0)) {
 +              wpa_printf(MSG_INFO, "WPS-STRICT: Invalid %sProbe Response "
 +                         "frame from " MACSTR, probe ? "" : "Beacon/",
 +                         MAC2STR(addr));
 +#ifdef WPS_STRICT_WPS2
 +              if (wps2)
 +                      return -1;
 +#else /* WPS_STRICT_WPS2 */
 +              return -1;
 +#endif /* WPS_STRICT_WPS2 */
 +      }
 +
 +      return 0;
 +}
 +
 +
 +int wps_validate_probe_req(const struct wpabuf *wps_ie, const u8 *addr)
 +{
 +      struct wps_parse_attr attr;
 +      int wps2;
 +
 +      if (wps_ie == NULL) {
 +              wpa_printf(MSG_INFO, "WPS-STRICT: No WPS IE in "
 +                         "Probe Request frame");
 +              return -1;
 +      }
 +      if (wps_parse_msg(wps_ie, &attr) < 0) {
 +              wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse WPS IE in "
 +                         "Probe Request frame");
 +              return -1;
 +      }
 +
 +      wps2 = attr.version2 != NULL;
 +      if (wps_validate_version(attr.version, 1) ||
 +          wps_validate_request_type(attr.request_type, 1) ||
 +          wps_validate_config_methods(attr.config_methods, wps2, 1) ||
 +          wps_validate_uuid_e(attr.uuid_e, attr.uuid_r == NULL) ||
 +          wps_validate_uuid_r(attr.uuid_r, attr.uuid_e == NULL) ||
 +          wps_validate_primary_dev_type(attr.primary_dev_type, 1) ||
 +          wps_validate_rf_bands(attr.rf_bands, 1) ||
 +          wps_validate_assoc_state(attr.assoc_state, 1) ||
 +          wps_validate_config_error(attr.config_error, 1) ||
 +          wps_validate_dev_password_id(attr.dev_password_id, 1) ||
 +          wps_validate_version2(attr.version2, wps2) ||
 +          wps_validate_manufacturer(attr.manufacturer, attr.manufacturer_len,
 +                                    wps2) ||
 +          wps_validate_model_name(attr.model_name, attr.model_name_len,
 +                                  wps2) ||
 +          wps_validate_model_number(attr.model_number, attr.model_number_len,
 +                                    wps2) ||
 +          wps_validate_dev_name(attr.dev_name, attr.dev_name_len, wps2) ||
 +          wps_validate_request_to_enroll(attr.request_to_enroll, 0) ||
 +          wps_validate_req_dev_type(attr.req_dev_type, attr.num_req_dev_type,
 +                                    0)) {
 +              wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Probe Request "
 +                         "frame from " MACSTR, MAC2STR(addr));
 +              return -1;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +int wps_validate_assoc_req(const struct wpabuf *wps_ie)
 +{
 +      struct wps_parse_attr attr;
 +      int wps2;
 +
 +      if (wps_ie == NULL) {
 +              wpa_printf(MSG_INFO, "WPS-STRICT: No WPS IE in "
 +                         "(Re)Association Request frame");
 +              return -1;
 +      }
 +      if (wps_parse_msg(wps_ie, &attr) < 0) {
 +              wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse WPS IE in "
 +                         "(Re)Association Request frame");
 +              return -1;
 +      }
 +
 +      wps2 = attr.version2 != NULL;
 +      if (wps_validate_version(attr.version, 1) ||
 +          wps_validate_request_type(attr.request_type, 1) ||
 +          wps_validate_version2(attr.version2, wps2)) {
 +              wpa_printf(MSG_INFO, "WPS-STRICT: Invalid (Re)Association "
 +                         "Request frame");
 +              return -1;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +int wps_validate_assoc_resp(const struct wpabuf *wps_ie)
 +{
 +      struct wps_parse_attr attr;
 +      int wps2;
 +
 +      if (wps_ie == NULL) {
 +              wpa_printf(MSG_INFO, "WPS-STRICT: No WPS IE in "
 +                         "(Re)Association Response frame");
 +              return -1;
 +      }
 +      if (wps_parse_msg(wps_ie, &attr) < 0) {
 +              wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse WPS IE in "
 +                         "(Re)Association Response frame");
 +              return -1;
 +      }
 +
 +      wps2 = attr.version2 != NULL;
 +      if (wps_validate_version(attr.version, 1) ||
 +          wps_validate_response_type(attr.response_type, 1) ||
 +          wps_validate_version2(attr.version2, wps2)) {
 +              wpa_printf(MSG_INFO, "WPS-STRICT: Invalid (Re)Association "
 +                         "Response frame");
 +              return -1;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +int wps_validate_m1(const struct wpabuf *tlvs)
 +{
 +      struct wps_parse_attr attr;
 +      int wps2;
 +
 +      if (tlvs == NULL) {
 +              wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M1");
 +              return -1;
 +      }
 +      if (wps_parse_msg(tlvs, &attr) < 0) {
 +              wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
 +                         "in M1");
 +              return -1;
 +      }
 +
 +      wps2 = attr.version2 != NULL;
 +      if (wps_validate_version(attr.version, 1) ||
 +          wps_validate_msg_type(attr.msg_type, 1) ||
 +          wps_validate_uuid_e(attr.uuid_e, 1) ||
 +          wps_validate_mac_addr(attr.mac_addr, 1) ||
 +          wps_validate_enrollee_nonce(attr.enrollee_nonce, 1) ||
 +          wps_validate_public_key(attr.public_key, attr.public_key_len, 1) ||
 +          wps_validate_auth_type_flags(attr.auth_type_flags, 1) ||
 +          wps_validate_encr_type_flags(attr.encr_type_flags, 1) ||
 +          wps_validate_conn_type_flags(attr.conn_type_flags, 1) ||
 +          wps_validate_config_methods(attr.config_methods, wps2, 1) ||
 +          wps_validate_wps_state(attr.wps_state, 1) ||
 +          wps_validate_manufacturer(attr.manufacturer, attr.manufacturer_len,
 +                                    1) ||
 +          wps_validate_model_name(attr.model_name, attr.model_name_len, 1) ||
 +          wps_validate_model_number(attr.model_number, attr.model_number_len,
 +                                    1) ||
 +          wps_validate_serial_number(attr.serial_number,
 +                                     attr.serial_number_len, 1) ||
 +          wps_validate_primary_dev_type(attr.primary_dev_type, 1) ||
 +          wps_validate_dev_name(attr.dev_name, attr.dev_name_len, 1) ||
 +          wps_validate_rf_bands(attr.rf_bands, 1) ||
 +          wps_validate_assoc_state(attr.assoc_state, 1) ||
 +          wps_validate_dev_password_id(attr.dev_password_id, 1) ||
 +          wps_validate_config_error(attr.config_error, 1) ||
 +          wps_validate_os_version(attr.os_version, 1) ||
 +          wps_validate_version2(attr.version2, wps2) ||
 +          wps_validate_request_to_enroll(attr.request_to_enroll, 0)) {
 +              wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M1");
 +#ifdef WPS_STRICT_WPS2
 +              if (wps2)
 +                      return -1;
 +#else /* WPS_STRICT_WPS2 */
 +              return -1;
 +#endif /* WPS_STRICT_WPS2 */
 +      }
 +
 +      return 0;
 +}
 +
 +
 +int wps_validate_m2(const struct wpabuf *tlvs)
 +{
 +      struct wps_parse_attr attr;
 +      int wps2;
 +
 +      if (tlvs == NULL) {
 +              wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M2");
 +              return -1;
 +      }
 +      if (wps_parse_msg(tlvs, &attr) < 0) {
 +              wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
 +                         "in M2");
 +              return -1;
 +      }
 +
 +      wps2 = attr.version2 != NULL;
 +      if (wps_validate_version(attr.version, 1) ||
 +          wps_validate_msg_type(attr.msg_type, 1) ||
 +          wps_validate_enrollee_nonce(attr.enrollee_nonce, 1) ||
 +          wps_validate_registrar_nonce(attr.registrar_nonce, 1) ||
 +          wps_validate_uuid_r(attr.uuid_r, 1) ||
 +          wps_validate_public_key(attr.public_key, attr.public_key_len, 1) ||
 +          wps_validate_auth_type_flags(attr.auth_type_flags, 1) ||
 +          wps_validate_encr_type_flags(attr.encr_type_flags, 1) ||
 +          wps_validate_conn_type_flags(attr.conn_type_flags, 1) ||
 +          wps_validate_config_methods(attr.config_methods, wps2, 1) ||
 +          wps_validate_manufacturer(attr.manufacturer, attr.manufacturer_len,
 +                                    1) ||
 +          wps_validate_model_name(attr.model_name, attr.model_name_len, 1) ||
 +          wps_validate_model_number(attr.model_number, attr.model_number_len,
 +                                    1) ||
 +          wps_validate_serial_number(attr.serial_number,
 +                                     attr.serial_number_len, 1) ||
 +          wps_validate_primary_dev_type(attr.primary_dev_type, 1) ||
 +          wps_validate_dev_name(attr.dev_name, attr.dev_name_len, 1) ||
 +          wps_validate_rf_bands(attr.rf_bands, 1) ||
 +          wps_validate_assoc_state(attr.assoc_state, 1) ||
 +          wps_validate_config_error(attr.config_error, 1) ||
 +          wps_validate_dev_password_id(attr.dev_password_id, 1) ||
 +          wps_validate_os_version(attr.os_version, 1) ||
 +          wps_validate_version2(attr.version2, wps2) ||
 +          wps_validate_authenticator(attr.authenticator, 1)) {
 +              wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M2");
 +#ifdef WPS_STRICT_WPS2
 +              if (wps2)
 +                      return -1;
 +#else /* WPS_STRICT_WPS2 */
 +              return -1;
 +#endif /* WPS_STRICT_WPS2 */
 +      }
 +
 +      return 0;
 +}
 +
 +
 +int wps_validate_m2d(const struct wpabuf *tlvs)
 +{
 +      struct wps_parse_attr attr;
 +      int wps2;
 +
 +      if (tlvs == NULL) {
 +              wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M2D");
 +              return -1;
 +      }
 +      if (wps_parse_msg(tlvs, &attr) < 0) {
 +              wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
 +                         "in M2D");
 +              return -1;
 +      }
 +
 +      wps2 = attr.version2 != NULL;
 +      if (wps_validate_version(attr.version, 1) ||
 +          wps_validate_msg_type(attr.msg_type, 1) ||
 +          wps_validate_enrollee_nonce(attr.enrollee_nonce, 1) ||
 +          wps_validate_registrar_nonce(attr.registrar_nonce, 1) ||
 +          wps_validate_uuid_r(attr.uuid_r, 1) ||
 +          wps_validate_auth_type_flags(attr.auth_type_flags, 1) ||
 +          wps_validate_encr_type_flags(attr.encr_type_flags, 1) ||
 +          wps_validate_conn_type_flags(attr.conn_type_flags, 1) ||
 +          wps_validate_config_methods(attr.config_methods, wps2, 1) ||
 +          wps_validate_manufacturer(attr.manufacturer, attr.manufacturer_len,
 +                                    1) ||
 +          wps_validate_model_name(attr.model_name, attr.model_name_len, 1) ||
 +          wps_validate_model_number(attr.model_number, attr.model_number_len,
 +                                    1) ||
 +          wps_validate_serial_number(attr.serial_number,
 +                                     attr.serial_number_len, 1) ||
 +          wps_validate_primary_dev_type(attr.primary_dev_type, 1) ||
 +          wps_validate_dev_name(attr.dev_name, attr.dev_name_len, 1) ||
 +          wps_validate_rf_bands(attr.rf_bands, 1) ||
 +          wps_validate_assoc_state(attr.assoc_state, 1) ||
 +          wps_validate_config_error(attr.config_error, 1) ||
 +          wps_validate_os_version(attr.os_version, 1) ||
 +          wps_validate_version2(attr.version2, wps2)) {
 +              wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M2D");
 +#ifdef WPS_STRICT_WPS2
 +              if (wps2)
 +                      return -1;
 +#else /* WPS_STRICT_WPS2 */
 +              return -1;
 +#endif /* WPS_STRICT_WPS2 */
 +      }
 +
 +      return 0;
 +}
 +
 +
 +int wps_validate_m3(const struct wpabuf *tlvs)
 +{
 +      struct wps_parse_attr attr;
 +      int wps2;
 +
 +      if (tlvs == NULL) {
 +              wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M3");
 +              return -1;
 +      }
 +      if (wps_parse_msg(tlvs, &attr) < 0) {
 +              wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
 +                         "in M3");
 +              return -1;
 +      }
 +
 +      wps2 = attr.version2 != NULL;
 +      if (wps_validate_version(attr.version, 1) ||
 +          wps_validate_msg_type(attr.msg_type, 1) ||
 +          wps_validate_registrar_nonce(attr.registrar_nonce, 1) ||
 +          wps_validate_e_hash1(attr.e_hash1, 1) ||
 +          wps_validate_e_hash2(attr.e_hash2, 1) ||
 +          wps_validate_version2(attr.version2, wps2) ||
 +          wps_validate_authenticator(attr.authenticator, 1)) {
 +              wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M3");
 +#ifdef WPS_STRICT_WPS2
 +              if (wps2)
 +                      return -1;
 +#else /* WPS_STRICT_WPS2 */
 +              return -1;
 +#endif /* WPS_STRICT_WPS2 */
 +      }
 +
 +      return 0;
 +}
 +
 +
 +int wps_validate_m4(const struct wpabuf *tlvs)
 +{
 +      struct wps_parse_attr attr;
 +      int wps2;
 +
 +      if (tlvs == NULL) {
 +              wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M4");
 +              return -1;
 +      }
 +      if (wps_parse_msg(tlvs, &attr) < 0) {
 +              wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
 +                         "in M4");
 +              return -1;
 +      }
 +
 +      wps2 = attr.version2 != NULL;
 +      if (wps_validate_version(attr.version, 1) ||
 +          wps_validate_msg_type(attr.msg_type, 1) ||
 +          wps_validate_enrollee_nonce(attr.enrollee_nonce, 1) ||
 +          wps_validate_r_hash1(attr.r_hash1, 1) ||
 +          wps_validate_r_hash2(attr.r_hash2, 1) ||
 +          wps_validate_encr_settings(attr.encr_settings,
 +                                     attr.encr_settings_len, 1) ||
 +          wps_validate_version2(attr.version2, wps2) ||
 +          wps_validate_authenticator(attr.authenticator, 1)) {
 +              wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M4");
 +#ifdef WPS_STRICT_WPS2
 +              if (wps2)
 +                      return -1;
 +#else /* WPS_STRICT_WPS2 */
 +              return -1;
 +#endif /* WPS_STRICT_WPS2 */
 +      }
 +
 +      return 0;
 +}
 +
 +
 +int wps_validate_m4_encr(const struct wpabuf *tlvs, int wps2)
 +{
 +      struct wps_parse_attr attr;
 +
 +      if (tlvs == NULL) {
 +              wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M4 encrypted "
 +                         "settings");
 +              return -1;
 +      }
 +      if (wps_parse_msg(tlvs, &attr) < 0) {
 +              wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
 +                         "in M4 encrypted settings");
 +              return -1;
 +      }
 +
 +      if (wps_validate_r_snonce1(attr.r_snonce1, 1) ||
 +          wps_validate_key_wrap_auth(attr.key_wrap_auth, 1)) {
 +              wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M4 encrypted "
 +                         "settings");
 +#ifdef WPS_STRICT_WPS2
 +              if (wps2)
 +                      return -1;
 +#else /* WPS_STRICT_WPS2 */
 +              return -1;
 +#endif /* WPS_STRICT_WPS2 */
 +      }
 +
 +      return 0;
 +}
 +
 +
 +int wps_validate_m5(const struct wpabuf *tlvs)
 +{
 +      struct wps_parse_attr attr;
 +      int wps2;
 +
 +      if (tlvs == NULL) {
 +              wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M5");
 +              return -1;
 +      }
 +      if (wps_parse_msg(tlvs, &attr) < 0) {
 +              wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
 +                         "in M5");
 +              return -1;
 +      }
 +
 +      wps2 = attr.version2 != NULL;
 +      if (wps_validate_version(attr.version, 1) ||
 +          wps_validate_msg_type(attr.msg_type, 1) ||
 +          wps_validate_registrar_nonce(attr.registrar_nonce, 1) ||
 +          wps_validate_encr_settings(attr.encr_settings,
 +                                     attr.encr_settings_len, 1) ||
 +          wps_validate_version2(attr.version2, wps2) ||
 +          wps_validate_authenticator(attr.authenticator, 1)) {
 +              wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M5");
 +#ifdef WPS_STRICT_WPS2
 +              if (wps2)
 +                      return -1;
 +#else /* WPS_STRICT_WPS2 */
 +              return -1;
 +#endif /* WPS_STRICT_WPS2 */
 +      }
 +
 +      return 0;
 +}
 +
 +
 +int wps_validate_m5_encr(const struct wpabuf *tlvs, int wps2)
 +{
 +      struct wps_parse_attr attr;
 +
 +      if (tlvs == NULL) {
 +              wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M5 encrypted "
 +                         "settings");
 +              return -1;
 +      }
 +      if (wps_parse_msg(tlvs, &attr) < 0) {
 +              wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
 +                         "in M5 encrypted settings");
 +              return -1;
 +      }
 +
 +      if (wps_validate_e_snonce1(attr.e_snonce1, 1) ||
 +          wps_validate_key_wrap_auth(attr.key_wrap_auth, 1)) {
 +              wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M5 encrypted "
 +                         "settings");
 +#ifdef WPS_STRICT_WPS2
 +              if (wps2)
 +                      return -1;
 +#else /* WPS_STRICT_WPS2 */
 +              return -1;
 +#endif /* WPS_STRICT_WPS2 */
 +      }
 +
 +      return 0;
 +}
 +
 +
 +int wps_validate_m6(const struct wpabuf *tlvs)
 +{
 +      struct wps_parse_attr attr;
 +      int wps2;
 +
 +      if (tlvs == NULL) {
 +              wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M6");
 +              return -1;
 +      }
 +      if (wps_parse_msg(tlvs, &attr) < 0) {
 +              wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
 +                         "in M6");
 +              return -1;
 +      }
 +
 +      wps2 = attr.version2 != NULL;
 +      if (wps_validate_version(attr.version, 1) ||
 +          wps_validate_msg_type(attr.msg_type, 1) ||
 +          wps_validate_enrollee_nonce(attr.enrollee_nonce, 1) ||
 +          wps_validate_encr_settings(attr.encr_settings,
 +                                     attr.encr_settings_len, 1) ||
 +          wps_validate_version2(attr.version2, wps2) ||
 +          wps_validate_authenticator(attr.authenticator, 1)) {
 +              wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M6");
 +#ifdef WPS_STRICT_WPS2
 +              if (wps2)
 +                      return -1;
 +#else /* WPS_STRICT_WPS2 */
 +              return -1;
 +#endif /* WPS_STRICT_WPS2 */
 +      }
 +
 +      return 0;
 +}
 +
 +
 +int wps_validate_m6_encr(const struct wpabuf *tlvs, int wps2)
 +{
 +      struct wps_parse_attr attr;
 +
 +      if (tlvs == NULL) {
 +              wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M6 encrypted "
 +                         "settings");
 +              return -1;
 +      }
 +      if (wps_parse_msg(tlvs, &attr) < 0) {
 +              wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
 +                         "in M6 encrypted settings");
 +              return -1;
 +      }
 +
 +      if (wps_validate_r_snonce2(attr.r_snonce2, 1) ||
 +          wps_validate_key_wrap_auth(attr.key_wrap_auth, 1)) {
 +              wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M6 encrypted "
 +                         "settings");
 +#ifdef WPS_STRICT_WPS2
 +              if (wps2)
 +                      return -1;
 +#else /* WPS_STRICT_WPS2 */
 +              return -1;
 +#endif /* WPS_STRICT_WPS2 */
 +      }
 +
 +      return 0;
 +}
 +
 +
 +int wps_validate_m7(const struct wpabuf *tlvs)
 +{
 +      struct wps_parse_attr attr;
 +      int wps2;
 +
 +      if (tlvs == NULL) {
 +              wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M7");
 +              return -1;
 +      }
 +      if (wps_parse_msg(tlvs, &attr) < 0) {
 +              wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
 +                         "in M7");
 +              return -1;
 +      }
 +
 +      wps2 = attr.version2 != NULL;
 +      if (wps_validate_version(attr.version, 1) ||
 +          wps_validate_msg_type(attr.msg_type, 1) ||
 +          wps_validate_registrar_nonce(attr.registrar_nonce, 1) ||
 +          wps_validate_encr_settings(attr.encr_settings,
 +                                     attr.encr_settings_len, 1) ||
 +          wps_validate_settings_delay_time(attr.settings_delay_time, 0) ||
 +          wps_validate_version2(attr.version2, wps2) ||
 +          wps_validate_authenticator(attr.authenticator, 1)) {
 +              wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M7");
 +#ifdef WPS_STRICT_WPS2
 +              if (wps2)
 +                      return -1;
 +#else /* WPS_STRICT_WPS2 */
 +              return -1;
 +#endif /* WPS_STRICT_WPS2 */
 +      }
 +
 +      return 0;
 +}
 +
 +
 +int wps_validate_m7_encr(const struct wpabuf *tlvs, int ap, int wps2)
 +{
 +      struct wps_parse_attr attr;
 +
 +      if (tlvs == NULL) {
 +              wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M7 encrypted "
 +                         "settings");
 +              return -1;
 +      }
 +      if (wps_parse_msg(tlvs, &attr) < 0) {
 +              wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
 +                         "in M7 encrypted settings");
 +              return -1;
 +      }
 +
 +      if (wps_validate_e_snonce2(attr.e_snonce2, 1) ||
 +          wps_validate_ssid(attr.ssid, attr.ssid_len, !ap) ||
 +          wps_validate_mac_addr(attr.mac_addr, !ap) ||
 +          wps_validate_auth_type(attr.auth_type, !ap) ||
 +          wps_validate_encr_type(attr.encr_type, !ap) ||
 +          wps_validate_network_key_index(attr.network_key_idx, 0) ||
 +          wps_validate_network_key(attr.network_key, attr.network_key_len,
 +                                   attr.encr_type, !ap) ||
 +          wps_validate_key_wrap_auth(attr.key_wrap_auth, 1)) {
 +              wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M7 encrypted "
 +                         "settings");
 +#ifdef WPS_STRICT_WPS2
 +              if (wps2)
 +                      return -1;
 +#else /* WPS_STRICT_WPS2 */
 +              return -1;
 +#endif /* WPS_STRICT_WPS2 */
 +      }
 +
 +      return 0;
 +}
 +
 +
 +int wps_validate_m8(const struct wpabuf *tlvs)
 +{
 +      struct wps_parse_attr attr;
 +      int wps2;
 +
 +      if (tlvs == NULL) {
 +              wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M8");
 +              return -1;
 +      }
 +      if (wps_parse_msg(tlvs, &attr) < 0) {
 +              wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
 +                         "in M8");
 +              return -1;
 +      }
 +
 +      wps2 = attr.version2 != NULL;
 +      if (wps_validate_version(attr.version, 1) ||
 +          wps_validate_msg_type(attr.msg_type, 1) ||
 +          wps_validate_enrollee_nonce(attr.enrollee_nonce, 1) ||
 +          wps_validate_encr_settings(attr.encr_settings,
 +                                     attr.encr_settings_len, 1) ||
 +          wps_validate_version2(attr.version2, wps2) ||
 +          wps_validate_authenticator(attr.authenticator, 1)) {
 +              wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M8");
 +#ifdef WPS_STRICT_WPS2
 +              if (wps2)
 +                      return -1;
 +#else /* WPS_STRICT_WPS2 */
 +              return -1;
 +#endif /* WPS_STRICT_WPS2 */
 +      }
 +
 +      return 0;
 +}
 +
 +
 +int wps_validate_m8_encr(const struct wpabuf *tlvs, int ap, int wps2)
 +{
 +      struct wps_parse_attr attr;
 +
 +      if (tlvs == NULL) {
 +              wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in M8 encrypted "
 +                         "settings");
 +              return -1;
 +      }
 +      if (wps_parse_msg(tlvs, &attr) < 0) {
 +              wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
 +                         "in M8 encrypted settings");
 +              return -1;
 +      }
 +
 +      if (wps_validate_ssid(attr.ssid, attr.ssid_len, ap) ||
 +          wps_validate_auth_type(attr.auth_type, ap) ||
 +          wps_validate_encr_type(attr.encr_type, ap) ||
 +          wps_validate_network_key_index(attr.network_key_idx, 0) ||
 +          wps_validate_mac_addr(attr.mac_addr, ap) ||
 +          wps_validate_credential(attr.cred, attr.cred_len, attr.num_cred,
 +                                  !ap) ||
 +          wps_validate_key_wrap_auth(attr.key_wrap_auth, 1)) {
 +              wpa_printf(MSG_INFO, "WPS-STRICT: Invalid M8 encrypted "
 +                         "settings");
 +#ifdef WPS_STRICT_WPS2
 +              if (wps2)
 +                      return -1;
 +#else /* WPS_STRICT_WPS2 */
 +              return -1;
 +#endif /* WPS_STRICT_WPS2 */
 +      }
 +
 +      return 0;
 +}
 +
 +
 +int wps_validate_wsc_ack(const struct wpabuf *tlvs)
 +{
 +      struct wps_parse_attr attr;
 +      int wps2;
 +
 +      if (tlvs == NULL) {
 +              wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in WSC_ACK");
 +              return -1;
 +      }
 +      if (wps_parse_msg(tlvs, &attr) < 0) {
 +              wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
 +                         "in WSC_ACK");
 +              return -1;
 +      }
 +
 +      wps2 = attr.version2 != NULL;
 +      if (wps_validate_version(attr.version, 1) ||
 +          wps_validate_msg_type(attr.msg_type, 1) ||
 +          wps_validate_enrollee_nonce(attr.enrollee_nonce, 1) ||
 +          wps_validate_registrar_nonce(attr.registrar_nonce, 1) ||
 +          wps_validate_version2(attr.version2, wps2)) {
 +              wpa_printf(MSG_INFO, "WPS-STRICT: Invalid WSC_ACK");
 +#ifdef WPS_STRICT_WPS2
 +              if (wps2)
 +                      return -1;
 +#else /* WPS_STRICT_WPS2 */
 +              return -1;
 +#endif /* WPS_STRICT_WPS2 */
 +      }
 +
 +      return 0;
 +}
 +
 +
 +int wps_validate_wsc_nack(const struct wpabuf *tlvs)
 +{
 +      struct wps_parse_attr attr;
 +      int wps2;
 +
 +      if (tlvs == NULL) {
 +              wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in WSC_NACK");
 +              return -1;
 +      }
 +      if (wps_parse_msg(tlvs, &attr) < 0) {
 +              wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
 +                         "in WSC_NACK");
 +              return -1;
 +      }
 +
 +      wps2 = attr.version2 != NULL;
 +      if (wps_validate_version(attr.version, 1) ||
 +          wps_validate_msg_type(attr.msg_type, 1) ||
 +          wps_validate_enrollee_nonce(attr.enrollee_nonce, 1) ||
 +          wps_validate_registrar_nonce(attr.registrar_nonce, 1) ||
 +          wps_validate_config_error(attr.config_error, 1) ||
 +          wps_validate_version2(attr.version2, wps2)) {
 +              wpa_printf(MSG_INFO, "WPS-STRICT: Invalid WSC_NACK");
 +#ifdef WPS_STRICT_WPS2
 +              if (wps2)
 +                      return -1;
 +#else /* WPS_STRICT_WPS2 */
 +              return -1;
 +#endif /* WPS_STRICT_WPS2 */
 +      }
 +
 +      return 0;
 +}
 +
 +
 +int wps_validate_wsc_done(const struct wpabuf *tlvs)
 +{
 +      struct wps_parse_attr attr;
 +      int wps2;
 +
 +      if (tlvs == NULL) {
 +              wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in WSC_Done");
 +              return -1;
 +      }
 +      if (wps_parse_msg(tlvs, &attr) < 0) {
 +              wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
 +                         "in WSC_Done");
 +              return -1;
 +      }
 +
 +      wps2 = attr.version2 != NULL;
 +      if (wps_validate_version(attr.version, 1) ||
 +          wps_validate_msg_type(attr.msg_type, 1) ||
 +          wps_validate_enrollee_nonce(attr.enrollee_nonce, 1) ||
 +          wps_validate_registrar_nonce(attr.registrar_nonce, 1) ||
 +          wps_validate_version2(attr.version2, wps2)) {
 +              wpa_printf(MSG_INFO, "WPS-STRICT: Invalid WSC_Done");
 +#ifdef WPS_STRICT_WPS2
 +              if (wps2)
 +                      return -1;
 +#else /* WPS_STRICT_WPS2 */
 +              return -1;
 +#endif /* WPS_STRICT_WPS2 */
 +      }
 +
 +      return 0;
 +}
 +
 +
 +int wps_validate_upnp_set_selected_registrar(const struct wpabuf *tlvs)
 +{
 +      struct wps_parse_attr attr;
 +      int wps2;
 +      int sel_reg;
 +
 +      if (tlvs == NULL) {
 +              wpa_printf(MSG_INFO, "WPS-STRICT: No TLVs in "
 +                         "SetSelectedRegistrar");
 +              return -1;
 +      }
 +      if (wps_parse_msg(tlvs, &attr) < 0) {
 +              wpa_printf(MSG_INFO, "WPS-STRICT: Failed to parse attributes "
 +                         "in SetSelectedRegistrar");
 +              return -1;
 +      }
 +
 +      wps2 = attr.version2 != NULL;
 +      sel_reg = attr.selected_registrar != NULL &&
 +              *attr.selected_registrar != 0;
 +      if (wps_validate_version(attr.version, 1) ||
 +          wps_validate_dev_password_id(attr.dev_password_id, sel_reg) ||
 +          wps_validate_sel_reg_config_methods(attr.sel_reg_config_methods,
 +                                              wps2, sel_reg) ||
 +          wps_validate_version2(attr.version2, wps2) ||
 +          wps_validate_authorized_macs(attr.authorized_macs,
 +                                       attr.authorized_macs_len, wps2) ||
 +          wps_validate_uuid_r(attr.uuid_r, wps2)) {
 +              wpa_printf(MSG_INFO, "WPS-STRICT: Invalid "
 +                         "SetSelectedRegistrar");
 +#ifdef WPS_STRICT_WPS2
 +              if (wps2)
 +                      return -1;
 +#else /* WPS_STRICT_WPS2 */
 +              return -1;
 +#endif /* WPS_STRICT_WPS2 */
 +      }
 +
 +      return 0;
 +}
index 1ac79b4ae5f91c6dcf1b96b3259d4797e1dc2801,0000000000000000000000000000000000000000..facd90eea30c1fa3409d9d246d1456fa82f7dbfd
mode 100644,000000..100644
--- /dev/null
@@@ -1,2071 -1,0 +1,2134 @@@
 +ChangeLog for wpa_supplicant
 +
++2015-09-27 - v2.5
++      * fixed P2P validation of SSID element length before copying it
++        [http://w1.fi/security/2015-1/] (CVE-2015-1863)
++      * fixed WPS UPnP vulnerability with HTTP chunked transfer encoding
++        [http://w1.fi/security/2015-2/] (CVE-2015-4141)
++      * fixed WMM Action frame parser (AP mode)
++        [http://w1.fi/security/2015-3/] (CVE-2015-4142)
++      * fixed EAP-pwd peer missing payload length validation
++        [http://w1.fi/security/2015-4/]
++        (CVE-2015-4143, CVE-2015-4144, CVE-2015-4145, CVE-2015-4146)
++      * fixed validation of WPS and P2P NFC NDEF record payload length
++        [http://w1.fi/security/2015-5/]
++      * nl80211:
++        - added VHT configuration for IBSS
++        - fixed vendor command handling to check OUI properly
++        - allow driver-based roaming to change ESS
++      * added AVG_BEACON_RSSI to SIGNAL_POLL output
++      * wpa_cli: added tab completion for number of commands
++      * removed unmaintained and not yet completed SChannel/CryptoAPI support
++      * modified Extended Capabilities element use in Probe Request frames to
++        include all cases if any of the values are non-zero
++      * added support for dynamically creating/removing a virtual interface
++        with interface_add/interface_remove
++      * added support for hashed password (NtHash) in EAP-pwd peer
++      * added support for memory-only PSK/passphrase (mem_only_psk=1 and
++        CTRL-REQ/RSP-PSK_PASSPHRASE)
++      * P2P
++        - optimize scan frequencies list when re-joining a persistent group
++        - fixed number of sequences with nl80211 P2P Device interface
++        - added operating class 125 for P2P use cases (this allows 5 GHz
++          channels 161 and 169 to be used if they are enabled in the current
++          regulatory domain)
++        - number of fixes to P2PS functionality
++        - do not allow 40 MHz co-ex PRI/SEC switch to force MCC
++        - extended support for preferred channel listing
++      * D-Bus:
++        - fixed WPS property of fi.w1.wpa_supplicant1.BSS interface
++        - fixed PresenceRequest to use group interface
++        - added new signals: FindStopped, WPS pbc-overlap,
++          GroupFormationFailure, WPS timeout, InvitationReceived
++        - added new methods: WPS Cancel, P2P Cancel, Reconnect, RemoveClient
++        - added manufacturer info
++      * added EAP-EKE peer support for deriving Session-Id
++      * added wps_priority configuration parameter to set the default priority
++        for all network profiles added by WPS
++      * added support to request a scan with specific SSIDs with the SCAN
++        command (optional "ssid <hexdump>" arguments)
++      * removed support for WEP40/WEP104 as a group cipher with WPA/WPA2
++      * fixed SAE group selection in an error case
++      * modified SAE routines to be more robust and PWE generation to be
++        stronger against timing attacks
++      * added support for Brainpool Elliptic Curves with SAE
++      * added support for CCMP-256 and GCMP-256 as group ciphers with FT
++      * fixed BSS selection based on estimated throughput
++      * added option to disable TLSv1.0 with OpenSSL
++        (phase1="tls_disable_tlsv1_0=1")
++      * added Fast Session Transfer (FST) module
++      * fixed OpenSSL PKCS#12 extra certificate handling
++      * fixed key derivation for Suite B 192-bit AKM (this breaks
++        compatibility with the earlier version)
++      * added RSN IE to Mesh Peering Open/Confirm frames
++      * number of small fixes
++
 +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)
 +      * removed unmaintained driver wrappers broadcom, iphone, osx, ralink,
 +        hostap, madwifi (hostap and madwifi remain available for hostapd;
 +        their wpa_supplicant functionality is obsoleted by wext)
 +      * improved debug logging (human readable event names, interface name
 +        included in more entries)
 +      * changed AP mode behavior to enable WPS only for open and
 +        WPA/WPA2-Personal configuration
 +      * improved P2P concurrency operations
 +        - better coordination of concurrent scan and P2P search operations
 +        - avoid concurrent remain-on-channel operation requests by canceling
 +          previous operations prior to starting a new one
 +        - reject operations that would require multi-channel concurrency if
 +          the driver does not support it
 +        - add parameter to select whether STA or P2P connection is preferred
 +          if the driver cannot support both at the same time
 +        - allow driver to indicate channel changes
 +        - added optional delay=<search delay in milliseconds> parameter for
 +          p2p_find to avoid taking all radio resources
 +        - use 500 ms p2p_find search delay by default during concurrent
 +          operations
 +        - allow all channels in GO Negotiation if the driver supports
 +          multi-channel concurrency
 +      * added number of small changes to make it easier for static analyzers
 +        to understand the implementation
 +      * fixed number of small bugs (see git logs for more details)
 +      * nl80211: number of updates to use new cfg80211/nl80211 functionality
 +        - replace monitor interface with nl80211 commands for AP mode
 +        - additional information for driver-based AP SME
 +        - STA entry authorization in RSN IBSS
 +      * EAP-pwd:
 +        - fixed KDF for group 21 and zero-padding
 +        - added support for fragmentation
 +        - increased maximum number of hunting-and-pecking iterations
 +      * avoid excessive Probe Response retries for broadcast Probe Request
 +        frames (only with drivers using wpa_supplicant AP mode SME/MLME)
 +      * added "GET country" ctrl_iface command
 +      * do not save an invalid network block in wpa_supplicant.conf to avoid
 +        problems reading the file on next start
 +      * send STA connected/disconnected ctrl_iface events to both the P2P
 +        group and parent interfaces
 +      * added preliminary support for using TLS v1.2 (CONFIG_TLSV12=y)
 +      * added "SET pno <1/0>" ctrl_iface command to start/stop preferred
 +        network offload with sched_scan driver command
 +      * merged in number of changes from Android repository for P2P, nl80211,
 +        and build parameters
 +      * changed P2P GO mode configuration to use driver capabilities to
 +        automatically enable HT operations when supported
 +      * added "wpa_cli status wps" command to fetch WPA2-Personal passhrase
 +        for WPS use cases in AP mode
 +      * EAP-AKA: keep pseudonym identity across EAP exchanges to match EAP-SIM
 +        behavior
 +      * improved reassociation behavior in cases where association is rejected
 +        or when an AP disconnects us to handle common load balancing
 +        mechanisms
 +        - try to avoid extra scans when the needed information is available
 +      * added optional "join" argument for p2p_prov_disc ctrl_iface command
 +      * added group ifname to P2P-PROV-DISC-* events
 +      * added P2P Device Address to AP-STA-DISCONNECTED event and use
 +        p2p_dev_addr parameter name with AP-STA-CONNECTED
 +      * added workarounds for WPS PBC overlap detection for some P2P use cases
 +        where deployed stations work incorrectly
 +      * optimize WPS connection speed by disconnecting prior to WPS scan and
 +        by using single channel scans when AP channel is known
 +      * PCSC and SIM/USIM improvements:
 +        - accept 0x67 (Wrong length) as a response to READ RECORD to fix
 +          issues with some USIM cards
 +        - try to read MNC length from SIM/USIM
 +        - build realm according to 3GPP TS 23.003 with identity from the SIM
 +        - allow T1 protocol to be enabled
 +      * added more WPS and P2P information available through D-Bus
 +      * improve P2P negotiation robustness
 +        - extra waits to get ACK frames through
 +        - longer timeouts for cases where deployed devices have been
 +          identified have issues meeting the specification requirements
 +        - more retries for some P2P frames
 +        - handle race conditions in GO Negotiation start by both devices
 +        - ignore unexpected GO Negotiation Response frame
 +      * added support for libnl 3.2 and newer
 +      * added P2P persistent group info to P2P_PEER data
 +      * maintain a list of P2P Clients for persistent group on GO
 +      * AP: increased initial group key handshake retransmit timeout to 500 ms
 +      * added optional dev_id parameter for p2p_find
 +      * added P2P-FIND-STOPPED ctrl_iface event
 +      * fixed issues in WPA/RSN element validation when roaming with ap_scan=1
 +        and driver-based BSS selection
 +      * do not expire P2P peer entries while connected with the peer in a
 +        group
 +      * fixed WSC element inclusion in cases where P2P is disabled
 +      * AP: added a WPS workaround for mixed mode AP Settings with Windows 7
 +      * EAP-SIM: fixed AT_COUNTER_TOO_SMALL use
 +      * EAP-SIM/AKA: append realm to pseudonym identity
 +      * EAP-SIM/AKA: store pseudonym identity in network configuration to
 +        allow it to persist over multiple EAP sessions and wpa_supplicant
 +        restarts
 +      * EAP-AKA': updated to RFC 5448 (username prefixes changed); note: this
 +        breaks interoperability with older versions
 +      * added support for WFA Hotspot 2.0
 +        - GAS/ANQP to fetch network information
 +        - credential configuration and automatic network selections based on
 +          credential match with ANQP information
 +      * limited PMKSA cache entries to be used only with the network context
 +        that was used to create them
 +      * improved PMKSA cache expiration to avoid unnecessary disconnections
 +      * adjusted bgscan_simple fast-scan backoff to avoid too frequent
 +        background scans
 +      * removed ctrl_iface event on P2P PD Response in join-group case
 +      * added option to fetch BSS table entry based on P2P Device Address
 +        ("BSS p2p_dev_addr=<P2P Device Address>")
 +      * added BSS entry age to ctrl_iface BSS command output
 +      * added optional MASK=0xH option for ctrl_iface BSS command to select
 +        which fields are included in the response
 +      * added optional RANGE=ALL|N1-N2 option for ctrl_iface BSS command to
 +        fetch information about several BSSes in one call
 +      * simplified licensing terms by selecting the BSD license as the only
 +        alternative
 +      * added "P2P_SET disallow_freq <freq list>" ctrl_iface command to
 +        disable channels from P2P use
 +      * added p2p_pref_chan configuration parameter to allow preferred P2P
 +        channels to be specified
 +      * added support for advertising immediate availability of a WPS
 +        credential for P2P use cases
 +      * optimized scan operations for P2P use cases (use single channel scan
 +        for a specific SSID when possible)
 +      * EAP-TTLS: fixed peer challenge generation for MSCHAPv2
 +      * SME: do not use reassociation after explicit disconnection request
 +        (local or a notification from an AP)
 +      * added support for sending debug info to Linux tracing (-T on command
 +        line)
 +      * added support for using Deauthentication reason code 3 as an
 +        indication of P2P group termination
 +      * added wps_vendor_ext_m1 configuration parameter to allow vendor
 +        specific attributes to be added to WPS M1
 +      * started using separate TLS library context for tunneled TLS
 +        (EAP-PEAP/TLS, EAP-TTLS/TLS, EAP-FAST/TLS) to support different CA
 +        certificate configuration between Phase 1 and Phase 2
 +      * added optional "auto" parameter for p2p_connect to request automatic
 +        GO Negotiation vs. join-a-group selection
 +      * added disabled_scan_offload parameter to disable automatic scan
 +        offloading (sched_scan)
 +      * added optional persistent=<network id> parameter for p2p_connect to
 +        allow forcing of a specific SSID/passphrase for GO Negotiation
 +      * added support for OBSS scan requests and 20/40 BSS coexistence reports
 +      * reject PD Request for unknown group
 +      * removed scripts and notes related to Windows binary releases (which
 +        have not been used starting from 1.x)
 +      * added initial support for WNM operations
 +        - Keep-alive based on BSS max idle period
 +        - WNM-Sleep Mode
 +        - minimal BSS Transition Management processing
 +      * added autoscan module to control scanning behavior while not connected
 +        - autoscan_periodic and autoscan_exponential modules
 +      * added new WPS NFC ctrl_iface mechanism
 +        - added initial support NFC connection handover
 +        - removed obsoleted WPS_OOB command (including support for deprecated
 +          UFD config_method)
 +      * added optional framework for external password storage ("ext:<name>")
 +      * wpa_cli: added optional support for controlling wpa_supplicant
 +        remotely over UDP (CONFIG_CTRL_IFACE=udp-remote) for testing purposes
 +      * wpa_cli: extended tab completion to more commands
 +      * changed SSID output to use printf-escaped strings instead of masking
 +        of non-ASCII characters
 +        - SSID can now be configured in the same format: ssid=P"abc\x00test"
 +      * removed default ACM=1 from AC_VO and AC_VI
 +      * added optional "ht40" argument for P2P ctrl_iface commands to allow
 +        40 MHz channels to be requested on the 5 GHz band
 +      * added optional parameters for p2p_invite command to specify channel
 +        when reinvoking a persistent group as the GO
 +      * improved FIPS mode builds with OpenSSL
 +        - "make fips" with CONFIG_FIPS=y to build wpa_supplicant with the
 +          OpenSSL FIPS object module
 +        - replace low level OpenSSL AES API calls to use EVP
 +        - use OpenSSL keying material exporter when possible
 +        - do not export TLS keys in FIPS mode
 +        - remove MD5 from CONFIG_FIPS=y builds
 +        - use OpenSSL function for PKBDF2 passphrase-to-PSK
 +        - use OpenSSL HMAC implementation
 +        - mix RAND_bytes() output into random_get_bytes() to force OpenSSL
 +          DRBG to be used in FIPS mode
 +        - use OpenSSL CMAC implementation
 +      * added mechanism to disable TLS Session Ticket extension
 +        - a workaround for servers that do not support TLS extensions that
 +          was enabled by default in recent OpenSSL versions
 +        - tls_disable_session_ticket=1
 +        - automatically disable TLS Session Ticket extension by default when
 +          using EAP-TLS/PEAP/TTLS (i.e., only use it with EAP-FAST)
 +      * changed VENDOR-TEST EAP method to use proper private enterprise number
 +        (this will not interoperate with older versions)
 +      * disable network block temporarily on authentication failures
 +      * improved WPS AP selection during WPS PIN iteration
 +      * added support for configuring GCMP cipher for IEEE 802.11ad
 +      * added support for Wi-Fi Display extensions
 +        - WFD_SUBELEMENT_SET ctrl_iface command to configure WFD subelements
 +        - SET wifi_display <0/1> to disable/enable WFD support
 +        - WFD service discovery
 +        - an external program is needed to manage the audio/video streaming
 +          and codecs
 +      * optimized scan result use for network selection
 +        - use the internal BSS table instead of raw scan results
 +        - allow unnecessary scans to be skipped if fresh information is
 +          available (e.g., after GAS/ANQP round for Interworking)
 +      * added support for 256-bit AES with internal TLS implementation
 +      * allow peer to propose channel in P2P invitation process for a
 +        persistent group
 +      * added disallow_aps parameter to allow BSSIDs/SSIDs to be disallowed
 +        from network selection
 +      * re-enable the networks disabled during WPS operations
 +      * allow P2P functionality to be disabled per interface (p2p_disabled=1)
 +      * added secondary device types into P2P_PEER output
 +      * added an option to disable use of a separate P2P group interface
 +        (p2p_no_group_iface=1)
 +      * fixed P2P Bonjour SD to match entries with both compressed and not
 +        compressed domain name format and support multiple Bonjour PTR matches
 +        for the same key
 +      * use deauthentication instead of disassociation for all disconnection
 +        operations; this removes the now unused disassociate() wpa_driver_ops
 +        callback
 +      * optimized PSK generation on P2P GO by caching results to avoid
 +        multiple PBKDF2 operations
 +      * added okc=1 global configuration parameter to allow OKC to be enabled
 +        by default for all network blocks
 +      * added a workaround for WPS PBC session overlap detection to avoid
 +        interop issues with deployed station implementations that do not
 +        remove active PBC indication from Probe Request frames properly
 +      * added basic support for 60 GHz band
 +      * extend EAPOL frames processing workaround for roaming cases
 +        (postpone processing of unexpected EAPOL frame until association
 +        event to handle reordered events)
 +
 +2012-05-10 - v1.0
 +      * bsd: Add support for setting HT values in IFM_MMASK.
 +      * Delay STA entry removal until Deauth/Disassoc TX status in AP mode.
 +        This allows the driver to use PS buffering of Deauthentication and
 +        Disassociation frames when the STA is in power save sleep. Only
 +        available with drivers that provide TX status events for Deauth/
 +        Disassoc frames (nl80211).
 +      * Drop oldest unknown BSS table entries first. This makes it less
 +        likely to hit connection issues in environments with huge number
 +        of visible APs.
 +      * Add systemd support.
 +      * Add support for setting the syslog facility from the config file
 +        at build time.
 +      * atheros: Add support for IEEE 802.11w configuration.
 +      * AP mode: Allow enable HT20 if driver supports it, by setting the
 +        config parameter ieee80211n.
 +      * Allow AP mode to disconnect STAs based on low ACK condition (when
 +        the data connection is not working properly, e.g., due to the STA
 +        going outside the range of the AP). Disabled by default, enable by
 +        config option disassoc_low_ack.
 +      * nl80211:
 +        - Support GTK rekey offload.
 +        - Support PMKSA candidate events. This adds support for RSN
 +          pre-authentication with nl80211 interface and drivers that handle
 +          roaming internally.
 +      * dbus:
 +        - Add a DBus signal for EAP SM requests, emitted on the Interface
 +          object.
 +        - Export max scan ssids supported by the driver as MaxScanSSID.
 +        - Add signal Certification for information about server certification.
 +        - Add BSSExpireAge and BSSExpireCount interface properties and
 +          support set/get, which allows for setting BSS cache expiration age
 +          and expiration scan count.
 +        - Add ConfigFile to AddInterface properties.
 +        - Add Interface.Country property and support to get/set the value.
 +        - Add DBus property CurrentAuthMode.
 +        - P2P DBus API added.
 +        - Emit property changed events (for property BSSs) when adding/
 +          removing BSSs.
 +        - Treat '' in SSIDs of Interface.Scan as a request for broadcast
 +          scan, instead of ignoring it.
 +        - Add DBus getter/setter for FastReauth.
 +        - Raise PropertiesChanged on org.freedesktop.DBus.Properties.
 +      * wpa_cli:
 +        - Send AP-STA-DISCONNECTED event when an AP disconnects a station
 +          due to inactivity.
 +        - Make second argument to set command optional. This can be used to
 +          indicate a zero length value.
 +        - Add signal_poll command.
 +        - Add bss_expire_age and bss_expire_count commands to set/get BSS
 +          cache expiration age and expiration scan count.
 +        - Add ability to set scan interval (the time in seconds wpa_s waits
 +          before requesting a new scan after failing to find a suitable
 +          network in scan results) using scan_interval command.
 +        - Add event CTRL-EVENT-ASSOC-REJECT for association rejected.
 +        - Add command get version, that returns wpa_supplicant version string.
 +        - Add command sta_autoconnect for disabling automatic reconnection
 +          on receiving disconnection event.
 +        - Setting bssid parameter to an empty string "" or any can now be
 +          used to clear the bssid_set flag in a network block, i.e., to remove
 +          bssid filtering.
 +        - Add tdls_testing command to add a special testing feature for
 +          changing TDLS behavior. Build param CONFIG_TDLS_TESTING must be
 +          enabled as well.
 +        - For interworking, add wpa_cli commands interworking_select,
 +          interworking_connect, anqp_get, fetch_anqp, and stop_fetch_anqp.
 +        - Many P2P commands were added. See README-P2P.
 +        - Many WPS/WPS ER commands - see WPS/WPS ER sections for details.
 +        - Allow set command to change global config parameters.
 +        - Add log_level command, which can be used to display the current
 +          debugging level and to change the log level during run time.
 +        - Add note command, which can be used to insert notes to the debug
 +          log.
 +        - Add internal line edit implementation. CONFIG_WPA_CLI_EDIT=y
 +          can now be used to build wpa_cli with internal implementation of
 +          line editing and history support. This can be used as a replacement
 +          for CONFIG_READLINE=y.
 +      * AP mode: Add max_num_sta config option, which can be used to limit
 +        the number of stations allowed to connect to the AP.
 +      * Add WPA_IGNORE_CONFIG_ERRORS build option to continue in case of bad
 +        config file.
 +      * wext: Increase scan timeout from 5 to 10 seconds.
 +      * Add blacklist command, allowing an external program to
 +        manage the BSS blacklist and display its current contents.
 +      * WPS:
 +        - Add wpa_cli wps_pin get command for generating random PINs. This can
 +          be used in a UI to generate a PIN without starting WPS (or P2P)
 +          operation.
 +        - Set RF bands based on driver capabilities, instead of hardcoding
 +          them.
 +        - Add mechanism for indicating non-standard WPS errors.
 +        - Add CONFIG_WPS_REG_DISABLE_OPEN=y option to disable open networks
 +          by default.
 +        - Add wps_ap_pin cli command for wpa_supplicant AP mode.
 +        - Add wps_check_pin cli command for processing PIN from user input.
 +          UIs can use this command to process a PIN entered by a user and to
 +          validate the checksum digit (if present).
 +        - Cancel WPS operation on PBC session overlap detection.
 +        - New wps_cancel command in wpa_cli will cancel a pending WPS
 +          operation.
 +        - wpa_cli action: Add WPS_EVENT_SUCCESS and WPS_EVENT_FAIL handlers.
 +        - Trigger WPS config update on Manufacturer, Model Name, Model
 +          Number, and Serial Number changes.
 +        - Fragment size is now configurable for EAP-WSC peer. Use
 +          wpa_cli set wps_fragment_size <val>.
 +        - Disable AP PIN after 10 consecutive failures. Slow down attacks on
 +          failures up to 10.
 +        - Allow AP to start in Enrollee mode without AP PIN for probing, to
 +          be compatible with Windows 7.
 +        - Add Config Error into WPS-FAIL events to provide more info to the
 +          user on how to resolve the issue.
 +        - Label and Display config methods are not allowed to be enabled
 +          at the same time, since it is unclear which PIN to use if both
 +          methods are advertised.
 +        - When controlling multiple interfaces:
 +           - apply WPS commands to all interfaces configured to use WPS
 +           - apply WPS config changes to all interfaces that use WPS
 +           - when an attack is detected on any interface, disable AP PIN on
 +             all interfaces
 +      * WPS ER:
 +        - Add special AP Setup Locked mode to allow read only ER.
 +          ap_setup_locked=2 can now be used to enable a special mode where
 +          WPS ER can learn the current AP settings, but cannot change them.
 +        - Show SetSelectedRegistrar events as ctrl_iface events
 +        - Add wps_er_set_config to enroll a network based on a local
 +          network configuration block instead of having to (re-)learn the
 +          current AP settings with wps_er_learn.
 +        - Allow AP filtering based on IP address, add ctrl_iface event for
 +          learned AP settings, add wps_er_config command to configure an AP.
 +      * WPS 2.0: Add support for WPS 2.0 (CONFIG_WPS2)
 +        - Add build option CONFIG_WPS_EXTENSIBILITY_TESTING to enable tool
 +          for testing protocol extensibility.
 +        - Add build option CONFIG_WPS_STRICT to allow disabling of WPS
 +          workarounds.
 +        - Add support for AuthorizedMACs attribute.
 +      * TDLS:
 +        - Propogate TDLS related nl80211 capability flags from kernel and
 +          add them as driver capability flags. If the driver doesn't support
 +          capabilities, assume TDLS is supported internally. When TDLS is
 +          explicitly not supported, disable all user facing TDLS operations.
 +        - Allow TDLS to be disabled at runtime (mostly for testing).
 +          Use set tdls_disabled.
 +        - Honor AP TDLS settings that prohibit/allow TDLS.
 +        - Add a special testing feature for changing TDLS behavior. Use
 +          CONFIG_TDLS_TESTING build param to enable. Configure at runtime
 +          with tdls_testing cli command.
 +        - Add support for TDLS 802.11z.
 +      * wlantest: Add a tool wlantest for IEEE802.11 protocol testing.
 +        wlantest can be used to capture frames from a monitor interface
 +        for realtime capturing or from pcap files for offline analysis.
 +      * Interworking: Support added for 802.11u. Enable in .config with
 +        CONFIG_INTERWORKING. See wpa_supplicant.conf for config parameters
 +        for interworking. wpa_cli commands added to support this are
 +        interworking_select, interworking_connect, anqp_get, fetch_anqp,
 +        and stop_fetch_anqp.
 +      * Android: Add build and runtime support for Android wpa_supplicant.
 +      * bgscan learn: Add new bgscan that learns BSS information based on
 +        previous scans, and uses that information to dynamically generate
 +        the list of channels for background scans.
 +      * Add a new debug message level for excessive information. Use
 +        -ddd to enable.
 +      * TLS: Add support for tls_disable_time_checks=1 in client mode.
 +      * Internal TLS:
 +        - Add support for TLS v1.1 (RFC 4346). Enable with build parameter
 +          CONFIG_TLSV11.
 +        - Add domainComponent parser for X.509 names.
 +      * Linux: Add RFKill support by adding an interface state "disabled".
 +      * Reorder some IEs to get closer to IEEE 802.11 standard. Move
 +        WMM into end of Beacon, Probe Resp and (Re)Assoc Resp frames.
 +        Move HT IEs to be later in (Re)Assoc Resp.
 +      * Solaris: Add support for wired 802.1X client.
 +      * Wi-Fi Direct support. See README-P2P for more information.
 +      * Many bugfixes.
 +
 +2010-04-18 - v0.7.2
 +      * nl80211: fixed number of issues with roaming
 +      * avoid unnecessary roaming if multiple APs with similar signal
 +        strength are present in scan results
 +      * add TLS client events and server probing to ease design of
 +        automatic detection of EAP parameters
 +      * add option for server certificate matching (SHA256 hash of the
 +        certificate) instead of trusted CA certificate configuration
 +      * bsd: Cleaned up driver wrapper and added various low-level
 +        configuration options
 +      * wpa_gui-qt4: do not show too frequent WPS AP available events as
 +        tray messages
 +      * TNC: fixed issues with fragmentation
 +      * EAP-TNC: add Flags field into fragment acknowledgement (needed to
 +        interoperate with other implementations; may potentially breaks
 +        compatibility with older wpa_supplicant/hostapd versions)
 +      * wpa_cli: added option for using a separate process to receive event
 +        messages to reduce latency in showing these
 +        (CFLAGS += -DCONFIG_WPA_CLI_FORK=y in .config to enable this)
 +      * maximum BSS table size can now be configured (bss_max_count)
 +      * BSSes to be included in the BSS table can be filtered based on
 +        configured SSIDs to save memory (filter_ssids)
 +      * fix number of issues with IEEE 802.11r/FT; this version is not
 +        backwards compatible with old versions
 +      * nl80211: add support for IEEE 802.11r/FT protocol (both over-the-air
 +        and over-the-DS)
 +      * add freq_list network configuration parameter to allow the AP
 +        selection to filter out entries based on the operating channel
 +      * add signal strength change events for bgscan; this allows more
 +        dynamic changes to background scanning interval based on changes in
 +        the signal strength with the current AP; this improves roaming within
 +        ESS quite a bit, e.g., with bgscan="simple:30:-45:300" in the network
 +        configuration block to request background scans less frequently when
 +        signal strength remains good and to automatically trigger background
 +        scans whenever signal strength drops noticeably
 +        (this is currently only available with nl80211)
 +      * add BSSID and reason code (if available) to disconnect event messages
 +      * wpa_gui-qt4: more complete support for translating the GUI with
 +        linguist and add German translation
 +      * fix DH padding with internal crypto code (mainly, for WPS)
 +      * do not trigger initial scan automatically anymore if there are no
 +        enabled networks
 +
 +2010-01-16 - v0.7.1
 +      * cleaned up driver wrapper API (struct wpa_driver_ops); the new API
 +        is not fully backwards compatible, so out-of-tree driver wrappers
 +        will need modifications
 +      * cleaned up various module interfaces
 +      * merge hostapd and wpa_supplicant developers' documentation into a
 +        single document
 +      * nl80211: use explicit deauthentication to clear cfg80211 state to
 +        avoid issues when roaming between APs
 +      * dbus: major design changes in the new D-Bus API
 +        (fi.w1.wpa_supplicant1)
 +      * nl80211: added support for IBSS networks
 +      * added internal debugging mechanism with backtrace support and memory
 +        allocation/freeing validation, etc. tests (CONFIG_WPA_TRACE=y)
 +      * added WPS ER unsubscription command to more cleanly unregister from
 +        receiving UPnP events when ER is terminated
 +      * cleaned up AP mode operations to avoid need for virtual driver_ops
 +        wrapper
 +      * added BSS table to maintain more complete scan result information
 +        over multiple scans (that may include only partial results)
 +      * wpa_gui-qt4: update Peers dialog information more dynamically while
 +        the dialog is kept open
 +      * fixed PKCS#12 use with OpenSSL 1.0.0
 +      * driver_wext: Added cfg80211-specific optimization to avoid some
 +        unnecessary scans and to speed up association
 +
 +2009-11-21 - v0.7.0
 +      * increased wpa_cli ping interval to 5 seconds and made this
 +        configurable with a new command line options (-G<seconds>)
 +      * fixed scan buffer processing with WEXT to handle up to 65535
 +        byte result buffer (previously, limited to 32768 bytes)
 +      * allow multiple driver wrappers to be specified on command line
 +        (e.g., -Dnl80211,wext); the first one that is able to initialize the
 +        interface will be used
 +      * added support for multiple SSIDs per scan request to optimize
 +        scan_ssid=1 operations in ap_scan=1 mode (i.e., search for hidden
 +        SSIDs); this requires driver support and can currently be used only
 +        with nl80211
 +      * added support for WPS USBA out-of-band mechanism with USB Flash
 +        Drives (UFD) (CONFIG_WPS_UFD=y)
 +      * driver_ndis: add PAE group address to the multicast address list to
 +        fix wired IEEE 802.1X authentication
 +      * fixed IEEE 802.11r key derivation function to match with the standard
 +        (note: this breaks interoperability with previous version) [Bug 303]
 +      * added better support for drivers that allow separate authentication
 +        and association commands (e.g., mac80211-based Linux drivers with
 +        nl80211; SME in wpa_supplicant); this allows over-the-air FT protocol
 +        to be used (IEEE 802.11r)
 +      * fixed SHA-256 based key derivation function to match with the
 +        standard when using CCMP (for IEEE 802.11r and IEEE 802.11w)
 +        (note: this breaks interoperability with previous version) [Bug 307]
 +      * use shared driver wrapper files with hostapd
 +      * added AP mode functionality (CONFIG_AP=y) with mode=2 in the network
 +        block; this can be used for open and WPA2-Personal networks
 +        (optionally, with WPS); this links in parts of hostapd functionality
 +        into wpa_supplicant
 +      * wpa_gui-qt4: added new Peers dialog to show information about peers
 +        (other devices, including APs and stations, etc. in the neighborhood)
 +      * added support for WPS External Registrar functionality (configure APs
 +        and enroll new devices); can be used with wpa_gui-qt4 Peers dialog
 +        and wpa_cli commands wps_er_start, wps_er_stop, wps_er_pin,
 +        wps_er_pbc, wps_er_learn
 +        (this can also be used with a new 'none' driver wrapper if no
 +        wireless device or IEEE 802.1X on wired is needed)
 +      * driver_nl80211: multiple updates to provide support for new Linux
 +        nl80211/mac80211 functionality
 +      * updated management frame protection to use IEEE Std 802.11w-2009
 +      * fixed number of small WPS issues and added workarounds to
 +        interoperate with common deployed broken implementations
 +      * added support for NFC out-of-band mechanism with WPS
 +      * driver_ndis: fixed wired IEEE 802.1X authentication with PAE group
 +        address frames
 +      * added preliminary support for IEEE 802.11r RIC processing
 +      * added support for specifying subset of enabled frequencies to scan
 +        (scan_freq option in the network configuration block); this can speed
 +        up scanning process considerably if it is known that only a small
 +        subset of channels is actually used in the network (this is currently
 +        supported only with -Dnl80211)
 +      * added a workaround for race condition between receiving the
 +        association event and the following EAPOL-Key
 +      * added background scan and roaming infrastructure to allow
 +        network-specific optimizations to be used to improve roaming within
 +        an ESS (same SSID)
 +      * added new DBus interface (fi.w1.wpa_supplicant1)
 +
 +2009-01-06 - v0.6.7
 +      * added support for Wi-Fi Protected Setup (WPS)
 +        (wpa_supplicant can now be configured to act as a WPS Enrollee to
 +        enroll credentials for a network using PIN and PBC methods; in
 +        addition, wpa_supplicant can act as a wireless WPS Registrar to
 +        configure an AP); WPS support can be enabled by adding CONFIG_WPS=y
 +        into .config and setting the runtime configuration variables in
 +        wpa_supplicant.conf (see WPS section in the example configuration
 +        file); new wpa_cli commands wps_pin, wps_pbc, and wps_reg are used to
 +        manage WPS negotiation; see README-WPS for more details
 +      * added support for EAP-AKA' (draft-arkko-eap-aka-kdf)
 +      * added support for using driver_test over UDP socket
 +      * fixed PEAPv0 Cryptobinding interoperability issue with Windows Server
 +        2008 NPS; optional cryptobinding is now enabled (again) by default
 +      * fixed PSK editing in wpa_gui
 +      * changed EAP-GPSK to use the IANA assigned EAP method type 51
 +      * added a Windows installer that includes WinPcap and all the needed
 +        DLLs; in addition, it set up the registry automatically so that user
 +        will only need start wpa_gui to get prompted to start the wpasvc
 +        servide and add a new interface if needed through wpa_gui dialog
 +      * updated management frame protection to use IEEE 802.11w/D7.0
 +
 +2008-11-23 - v0.6.6
 +      * added Milenage SIM/USIM emulator for EAP-SIM/EAP-AKA
 +        (can be used to simulate test SIM/USIM card with a known private key;
 +        enable with CONFIG_SIM_SIMULATOR=y/CONFIG_USIM_SIMULATOR=y in .config
 +        and password="Ki:OPc"/password="Ki:OPc:SQN" in network configuration)
 +      * added a new network configuration option, wpa_ptk_rekey, that can be
 +        used to enforce frequent PTK rekeying, e.g., to mitigate some attacks
 +        against TKIP deficiencies
 +      * added an optional mitigation mechanism for certain attacks against
 +        TKIP by delaying Michael MIC error reports by a random amount of time
 +        between 0 and 60 seconds; this can be enabled with a build option
 +        CONFIG_DELAYED_MIC_ERROR_REPORT=y in .config
 +      * fixed EAP-AKA to use RES Length field in AT_RES as length in bits,
 +        not bytes
 +      * updated OpenSSL code for EAP-FAST to use an updated version of the
 +        session ticket overriding API that was included into the upstream
 +        OpenSSL 0.9.9 tree on 2008-11-15 (no additional OpenSSL patch is
 +        needed with that version anymore)
 +      * updated userspace MLME instructions to match with the current Linux
 +        mac80211 implementation; please also note that this can only be used
 +        with driver_nl80211.c (the old code from driver_wext.c was removed)
 +      * added support (Linux only) for RoboSwitch chipsets (often found in
 +        consumer grade routers); driver interface 'roboswitch'
 +      * fixed canceling of PMKSA caching when using drivers that generate
 +        RSN IE and refuse to drop PMKIDs that wpa_supplicant does not know
 +        about
 +
 +2008-11-01 - v0.6.5
 +      * added support for SHA-256 as X.509 certificate digest when using the
 +        internal X.509/TLSv1 implementation
 +      * updated management frame protection to use IEEE 802.11w/D6.0
 +      * added support for using SHA256-based stronger key derivation for WPA2
 +        (IEEE 802.11w)
 +      * fixed FT (IEEE 802.11r) authentication after a failed association to
 +        use correct FTIE
 +      * added support for configuring Phase 2 (inner/tunneled) authentication
 +        method with wpa_gui-qt4
 +
 +2008-08-10 - v0.6.4
 +      * added support for EAP Sequences in EAP-FAST Phase 2
 +      * added support for using TNC with EAP-FAST
 +      * added driver_ps3 for the PS3 Linux wireless driver
 +      * added support for optional cryptobinding with PEAPv0
 +      * fixed the OpenSSL patches (0.9.8g and 0.9.9) for EAP-FAST to
 +        allow fallback to full handshake if server rejects PAC-Opaque
 +      * added fragmentation support for EAP-TNC
 +      * added support for parsing PKCS #8 formatted private keys into the
 +        internal TLS implementation (both PKCS #1 RSA key and PKCS #8
 +        encapsulated RSA key can now be used)
 +      * added option of using faster, but larger, routines in the internal
 +        LibTomMath (for internal TLS implementation) to speed up DH and RSA
 +        calculations (CONFIG_INTERNAL_LIBTOMMATH_FAST=y)
 +      * fixed race condition between disassociation event and group key
 +        handshake to avoid getting stuck in incorrect state [Bug 261]
 +      * fixed opportunistic key caching (proactive_key_caching)
 +
 +2008-02-22 - v0.6.3
 +      * removed 'nai' and 'eappsk' network configuration variables that were
 +        previously used for configuring user identity and key for EAP-PSK,
 +        EAP-PAX, EAP-SAKE, and EAP-GPSK. 'identity' field is now used as the
 +        replacement for 'nai' (if old configuration used a separate
 +        'identity' value, that would now be configured as
 +        'anonymous_identity'). 'password' field is now used as the
 +        replacement for 'eappsk' (it can also be set using hexstring to
 +        present random binary data)
 +      * removed '-w' command line parameter (wait for interface to be added,
 +        if needed); cleaner way of handling this functionality is to use an
 +        external mechanism (e.g., hotplug scripts) that start wpa_supplicant
 +        when an interface is added
 +      * updated FT support to use the latest draft, IEEE 802.11r/D9.0
 +      * added ctrl_iface monitor event (CTRL-EVENT-SCAN-RESULTS) for
 +        indicating when new scan results become available
 +      * added new ctrl_iface command, BSS, to allow scan results to be
 +        fetched without hitting the message size limits (this command
 +        can be used to iterate through the scan results one BSS at the time)
 +      * fixed EAP-SIM not to include AT_NONCE_MT and AT_SELECTED_VERSION
 +        attributes in EAP-SIM Start/Response when using fast reauthentication
 +      * fixed EAPOL not to end up in infinite loop when processing dynamic
 +        WEP keys with IEEE 802.1X
 +      * fixed problems in getting NDIS events from WMI on Windows 2000
 +
 +2008-01-01 - v0.6.2
 +      * added support for Makefile builds to include debug-log-to-a-file
 +        functionality (CONFIG_DEBUG_FILE=y and -f<path> on command line)
 +      * fixed EAP-SIM and EAP-AKA message parser to validate attribute
 +        lengths properly to avoid potential crash caused by invalid messages
 +      * added data structure for storing allocated buffers (struct wpabuf);
 +        this does not affect wpa_supplicant usage, but many of the APIs
 +        changed and various interfaces (e.g., EAP) is not compatible with old
 +        versions
 +      * added support for protecting EAP-AKA/Identity messages with
 +        AT_CHECKCODE (optional feature in RFC 4187)
 +      * added support for protected result indication with AT_RESULT_IND for
 +        EAP-SIM and EAP-AKA (phase1="result_ind=1")
 +      * added driver_wext workaround for race condition between scanning and
 +        association with drivers that take very long time to scan all
 +        channels (e.g., madwifi with dual-band cards); wpa_supplicant is now
 +        using a longer hardcoded timeout for the scan if the driver supports
 +        notifications for scan completion (SIOCGIWSCAN event); this helps,
 +        e.g., in cases where wpa_supplicant and madwifi driver ended up in
 +        loop where the driver did not even try to associate
 +      * stop EAPOL timer tick when no timers are in use in order to reduce
 +        power consumption (no need to wake up the process once per second)
 +        [Bug 237]
 +      * added support for privilege separation (run only minimal part of
 +        wpa_supplicant functionality as root and rest as unprivileged,
 +        non-root process); see 'Privilege separation' in README for details;
 +        this is disabled by default and can be enabled with CONFIG_PRIVSEP=y
 +        in .config
 +      * changed scan results data structure to include all information
 +        elements to make it easier to support new IEs; old get_scan_result()
 +        driver_ops is still supported for backwards compatibility (results
 +        are converted internally to the new format), but all drivers should
 +        start using the new get_scan_results2() to make them more likely to
 +        work with new features
 +      * Qt4 version of wpa_gui (wpa_gui-qt4 subdirectory) is now native Qt4
 +        application, i.e., it does not require Qt3Support anymore; Windows
 +        binary of wpa_gui.exe is now from this directory and only requires
 +        QtCore4.dll and QtGui4.dll libraries
 +      * updated Windows binary build to use Qt 4.3.3 and made Qt DLLs
 +        available as a separate package to make wpa_gui installation easier:
 +        http://w1.fi/wpa_supplicant/qt4/wpa_gui-qt433-windows-dll.zip
 +      * added support for EAP-IKEv2 (draft-tschofenig-eap-ikev2-15.txt);
 +        only shared key/password authentication is supported in this version
 +
 +2007-11-24 - v0.6.1
 +      * added support for configuring password as NtPasswordHash
 +        (16-byte MD4 hash of password) in hash:<32 hex digits> format
 +      * added support for fallback from abbreviated TLS handshake to
 +        full handshake when using EAP-FAST (e.g., due to an expired
 +        PAC-Opaque)
 +      * updated EAP Generalized Pre-Shared Key (EAP-GPSK) to use the latest
 +        draft (draft-ietf-emu-eap-gpsk-07.txt)
 +      * added support for drivers that take care of RSN 4-way handshake
 +        internally (WPA_DRIVER_FLAGS_4WAY_HANDSHAKE in get_capa flags and
 +        WPA_ALG_PMK in set_key)
 +      * added an experimental port for Mac OS X (CONFIG_DRIVER_OSX=y in
 +        .config); this version supports only ap_scan=2 mode and allow the
 +        driver to take care of the 4-way handshake
 +      * fixed a buffer overflow in parsing TSF from scan results when using
 +        driver_wext.c with a driver that includes the TSF (e.g., iwl4965)
 +        [Bug 232]
 +      * updated FT support to use the latest draft, IEEE 802.11r/D8.0
 +      * fixed an integer overflow issue in the ASN.1 parser used by the
 +        (experimental) internal TLS implementation to avoid a potential
 +        buffer read overflow
 +      * fixed a race condition with -W option (wait for a control interface
 +        monitor before starting) that could have caused the first messages to
 +        be lost
 +      * added support for processing TNCC-TNCS-Messages to report
 +        recommendation (allow/none/isolate) when using TNC [Bug 243]
 +
 +2007-05-28 - v0.6.0
 +      * added network configuration parameter 'frequency' for setting
 +        initial channel for IBSS (adhoc) networks
 +      * added experimental IEEE 802.11r/D6.0 support
 +      * updated EAP-SAKE to RFC 4763 and the IANA-allocated EAP type 48
 +      * updated EAP-PSK to use the IANA-allocated EAP type 47
 +      * fixed EAP-PAX key derivation
 +      * fixed EAP-PSK bit ordering of the Flags field
 +      * fixed EAP-PEAP/TTLS/FAST to use the correct EAP identifier in
 +        tunnelled identity request (previously, the identifier from the outer
 +        method was used, not the tunnelled identifier which could be
 +        different)
 +      * added support for fragmentation of outer TLS packets during Phase 2
 +        of EAP-PEAP/TTLS/FAST
 +      * fixed EAP-TTLS AVP parser processing for too short AVP lengths
 +      * added support for EAP-FAST authentication with inner methods that
 +        generate MSK (e.g., EAP-MSCHAPv2 that was previously only supported
 +        for PAC provisioning)
 +      * added support for authenticated EAP-FAST provisioning
 +      * added support for configuring maximum number of EAP-FAST PACs to
 +        store in a PAC list (fast_max_pac_list_len=<max> in phase1 string)
 +      * added support for storing EAP-FAST PACs in binary format
 +        (fast_pac_format=binary in phase1 string)
 +      * fixed dbus ctrl_iface to validate message interface before
 +        dispatching to avoid a possible segfault [Bug 190]
 +      * fixed PeerKey key derivation to use the correct PRF label
 +      * updated Windows binary build to link against OpenSSL 0.9.8d and
 +        added support for EAP-FAST
 +      * updated EAP Generalized Pre-Shared Key (EAP-GPSK) to use the latest
 +        draft (draft-ietf-emu-eap-gpsk-04.txt)
 +      * fixed EAP-AKA Notification processing to allow Notification to be
 +        processed after AKA Challenge response has been sent
 +      * updated to use IEEE 802.11w/D2.0 for management frame protection
 +        (still experimental)
 +      * fixed EAP-TTLS implementation not to crash on use of freed memory
 +        if TLS library initialization fails
 +      * added support for EAP-TNC (Trusted Network Connect)
 +        (this version implements the EAP-TNC method and EAP-TTLS changes
 +        needed to run two methods in sequence (IF-T) and the IF-IMC and
 +        IF-TNCCS interfaces from TNCC)
 +
 +2006-11-24 - v0.5.6
 +      * added experimental, integrated TLSv1 client implementation with the
 +        needed X.509/ASN.1/RSA/bignum processing (this can be enabled by
 +        setting CONFIG_TLS=internal and CONFIG_INTERNAL_LIBTOMMATH=y in
 +        .config); this can be useful, e.g., if the target system does not
 +        have a suitable TLS library and a minimal code size is required
 +        (total size of this internal TLS/crypto code is bit under 50 kB on
 +        x86 and the crypto code is shared by rest of the supplicant so some
 +        of it was already required; TLSv1/X.509/ASN.1/RSA added about 25 kB)
 +      * removed STAKey handshake since PeerKey handshake has replaced it in
 +        IEEE 802.11ma and there are no known deployments of STAKey
 +      * updated EAP Generalized Pre-Shared Key (EAP-GPSK) to use the latest
 +        draft (draft-ietf-emu-eap-gpsk-01.txt)
 +      * added preliminary implementation of IEEE 802.11w/D1.0 (management
 +        frame protection)
 +        (Note: this requires driver support to work properly.)
 +        (Note2: IEEE 802.11w is an unapproved draft and subject to change.)
 +      * fixed Windows named pipes ctrl_iface to not stop listening for
 +        commands if client program opens a named pipe and closes it
 +        immediately without sending a command
 +      * fixed USIM PIN status determination for the case that PIN is not
 +        needed (this allows EAP-AKA to be used with USIM cards that do not
 +        use PIN)
 +      * added support for reading 3G USIM AID from EF_DIR to allow EAP-AKA to
 +        be used with cards that do not support file selection based on
 +        partial AID
 +      * added support for matching the subjectAltName of the authentication
 +        server certificate against multiple name components (e.g.,
 +        altsubject_match="DNS:server.example.com;DNS:server2.example.com")
 +      * fixed EAP-SIM/AKA key derivation for re-authentication case (only
 +        affects IEEE 802.1X with dynamic WEP keys)
 +      * changed ctrl_iface network configuration 'get' operations to not
 +        return password/key material; if these fields are requested, "*"
 +        will be returned if the password/key is set, but the value of the
 +        parameter is not exposed
 +
 +2006-08-27 - v0.5.5
 +      * added support for building Windows version with UNICODE defined
 +        (wide-char functions)
 +      * driver_ndis: fixed static WEP configuration to avoid race condition
 +        issues with some NDIS drivers between association and setting WEP
 +        keys
 +      * driver_ndis: added validation for IELength value in scan results to
 +        avoid crashes when using buggy NDIS drivers [Bug 165]
 +      * fixed Release|Win32 target in the Visual Studio project files
 +        (previously, only Debug|Win32 target was set properly)
 +      * changed control interface API call wpa_ctrl_pending() to allow it to
 +        return -1 on error (e.g., connection lost); control interface clients
 +        will need to make sure that they verify that the value is indeed >0
 +        when determining whether there are pending messages
 +      * added an alternative control interface backend for Windows targets:
 +        Named Pipe (CONFIG_CTRL_IFACE=named_pipe); this is now the default
 +        control interface mechanism for Windows builds (previously, UDP to
 +        localhost was used)
 +      * changed ctrl_interface configuration for UNIX domain sockets:
 +        - deprecated ctrl_interface_group variable (it may be removed in
 +          future versions)
 +        - allow both directory and group be configured with ctrl_interface
 +          in following format: DIR=/var/run/wpa_supplicant GROUP=wheel
 +        - ctrl_interface=/var/run/wpa_supplicant is still supported for the
 +          case when group is not changed
 +      * added support for controlling more than one interface per process in
 +        Windows version
 +      * added a workaround for a case where the AP is using unknown address
 +        (e.g., MAC address of the wired interface) as the source address for
 +        EAPOL-Key frames; previously, that source address was used as the
 +        destination for EAPOL-Key frames and in key derivation; now, BSSID is
 +        used even if the source address does not match with it
 +        (this resolves an interoperability issue with Thomson SpeedTouch 580)
 +      * added a workaround for UDP-based control interface (which was used in
 +        Windows builds before this release) to prevent packets with forged
 +        addresses from being accepted as local control requests
 +      * removed ndis_events.cpp and possibility of using external
 +        ndis_events.exe; C version (ndis_events.c) is fully functional and
 +        there is no desire to maintain two separate versions of this
 +        implementation
 +      * ndis_events: Changed NDIS event notification design to use WMI to
 +        learn the adapter description through Win32_PnPEntity class; this
 +        should fix some cases where the adapter name was not recognized
 +        correctly (e.g., with some USB WLAN adapters, e.g., Ralink RT2500
 +        USB) [Bug 113]
 +      * fixed selection of the first network in ap_scan=2 mode; previously,
 +        wpa_supplicant could get stuck in SCANNING state when only the first
 +        network for enabled (e.g., after 'wpa_cli select_network 0')
 +      * winsvc: added support for configuring ctrl_interface parameters in
 +        registry (ctrl_interface string value in
 +        HKLM\SOFTWARE\wpa_supplicant\interfaces\0000 key); this new value is
 +        required to enable control interface (previously, this was hardcoded
 +        to be enabled)
 +      * allow wpa_gui subdirectory to be built with both Qt3 and Qt4
 +      * converted wpa_gui-qt4 subdirectory to use Qt4 specific project format
 +
 +2006-06-20 - v0.5.4
 +      * fixed build with CONFIG_STAKEY=y [Bug 143]
 +      * added support for doing MLME (IEEE 802.11 management frame
 +        processing) in wpa_supplicant when using Devicescape IEEE 802.11
 +        stack (wireless-dev.git tree)
 +      * added a new network block configuration option, fragment_size, to
 +        configure the maximum EAP fragment size
 +      * driver_ndis: Disable WZC automatically for the selected interface to
 +        avoid conflicts with two programs trying to control the radio; WZC
 +        will be re-enabled (if it was enabled originally) when wpa_supplicant
 +        is terminated
 +      * added an experimental TLSv1 client implementation
 +        (CONFIG_TLS=internal) that can be used instead of an external TLS
 +        library, e.g., to reduce total size requirement on systems that do
 +        not include any TLS library by default (this is not yet complete;
 +        basic functionality is there, but certificate validation is not yet
 +        included)
 +      * added PeerKey handshake implementation for IEEE 802.11e
 +        direct link setup (DLS) to replace STAKey handshake
 +      * fixed WPA PSK update through ctrl_iface for the case where the old
 +        PSK was derived from an ASCII passphrase and the new PSK is set as
 +        a raw PSK (hex string)
 +      * added new configuration option for identifying which network block
 +        was used (id_str in wpa_supplicant.conf; included on
 +        WPA_EVENT_CONNECT monitor event and as WPA_ID_STR environmental
 +        variable in wpa_cli action scripts; in addition WPA_ID variable is
 +        set to the current unique identifier that wpa_supplicant assigned
 +        automatically for the network and that can be used with
 +        GET_NETWORK/SET_NETWORK ctrl_iface commands)
 +      * wpa_cli action script is now called only when the connect/disconnect
 +        status changes or when associating with a different network
 +      * fixed configuration parser not to remove CCMP from group cipher list
 +        if WPA-None (adhoc) is used (pairwise=NONE in that case)
 +      * fixed integrated NDIS events processing not to hang the process due
 +        to a missed change in eloop_win.c API in v0.5.3 [Bug 155]
 +      * added support for EAP Generalized Pre-Shared Key (EAP-GPSK,
 +        draft-clancy-emu-eap-shared-secret-00.txt)
 +      * added Microsoft Visual Studio 2005 solution and project files for
 +        build wpa_supplicant for Windows (see vs2005 subdirectory)
 +      * eloop_win: fixed unregistration of Windows events
 +      * l2_packet_winpcap: fixed a deadlock in deinitializing l2_packet
 +        at the end of RSN pre-authentication and added unregistration of
 +        a Windows event to avoid getting eloop_win stuck with an invalid
 +        handle
 +      * driver_ndis: added support for selecting AP based on BSSID
 +      * added new environmental variable for wpa_cli action scripts:
 +        WPA_CTRL_DIR is the current control interface directory
 +      * driver_ndis: added support for using NDISUIO instead of WinPcap for
 +        OID set/query operations (CONFIG_USE_NDISUIO=y in .config); with new
 +        l2_packet_ndis (CONFIG_L2_PACKET=ndis), this can be used to build
 +        wpa_supplicant without requiring WinPcap; note that using NDISUIO
 +        requires that WZC is disabled (net stop wzcsvc) since NDISUIO allows
 +        only one application to open the device
 +      * changed NDIS driver naming to only include device GUID, e.g.,
 +        {7EE3EFE5-C165-472F-986D-F6FBEDFE8C8D}, instead of including WinPcap
 +        specific \Device\NPF_ prefix before the GUID; the prefix is still
 +        allowed for backwards compatibility, but it is not required anymore
 +        when specifying the interface
 +      * driver_ndis: re-initialize driver interface is the adapter is removed
 +        and re-inserted [Bug 159]
 +      * driver_madwifi: fixed TKIP and CCMP sequence number configuration on
 +        big endian hosts [Bug 146]
 +
 +2006-04-27 - v0.5.3
 +      * fixed EAP-GTC response to include correct user identity when run as
 +        phase 2 method of EAP-FAST (i.e., EAP-FAST did not work in v0.5.2)
 +      * driver_ndis: Fixed encryption mode configuration for unencrypted
 +        networks (some NDIS drivers ignored this, but others, e.g., Broadcom,
 +        refused to associate with open networks) [Bug 106]
 +      * driver_ndis: use BSSID OID polling to detect when IBSS network is
 +        formed even when ndis_events code is included since some NDIS drivers
 +        do not generate media connect events in IBSS mode
 +      * config_winreg: allow global ctrl_interface parameter to be configured
 +        in Windows registry
 +      * config_winreg: added support for saving configuration data into
 +        Windows registry
 +      * added support for controlling network device operational state
 +        (dormant/up) for Linux 2.6.17 to improve DHCP processing (see
 +        http://www.flamewarmaster.de/software/dhcpclient/ for a DHCP client
 +        that can use this information)
 +      * driver_wext: added support for WE-21 change to SSID configuration
 +      * driver_wext: fixed privacy configuration for static WEP keys mode
 +        [Bug 140]
 +      * added an optional driver_ops callback for MLME-SETPROTECTION.request
 +        primitive
 +      * added support for EAP-SAKE (no EAP method number allocated yet, so
 +        this is using the same experimental type 255 as EAP-PSK)
 +      * added support for dynamically loading EAP methods (.so files) instead
 +        of requiring them to be statically linked in; this is disabled by
 +        default (see CONFIG_DYNAMIC_EAP_METHODS in defconfig for information
 +        on how to use this)
 +
 +2006-03-19 - v0.5.2
 +      * do not try to use USIM APDUs when initializing PC/SC for SIM card
 +        access for a network that has not enabled EAP-AKA
 +      * fixed EAP phase 2 Nak for EAP-{PEAP,TTLS,FAST} (this was broken in
 +        v0.5.1 due to the new support for expanded EAP types)
 +      * added support for generating EAP Expanded Nak
 +      * try to fetch scan results once before requesting new scan when
 +        starting up in ap_scan=1 mode (this can speed up initial association
 +        a lot with, e.g., madwifi-ng driver)
 +      * added support for receiving EAPOL frames from a Linux bridge
 +        interface (-bbr0 on command line)
 +      * fixed EAPOL re-authentication for sessions that used PMKSA caching
 +      * changed EAP method registration to use a dynamic list of methods
 +        instead of a static list generated at build time
 +      * fixed PMKSA cache deinitialization not to use freed memory when
 +        removing PMKSA entries
 +      * fixed a memory leak in EAP-TTLS re-authentication
 +      * reject WPA/WPA2 message 3/4 if it does not include any valid
 +        WPA/RSN IE
 +      * driver_wext: added fallback to use SIOCSIWENCODE for setting auth_alg
 +        if the driver does not support SIOCSIWAUTH
 +
 +2006-01-29 - v0.5.1
 +      * driver_test: added better support for multiple APs and STAs by using
 +        a directory with sockets that include MAC address for each device in
 +        the name (driver_param=test_dir=/tmp/test)
 +      * added support for EAP expanded type (vendor specific EAP methods)
 +      * added AP_SCAN command into ctrl_iface so that ap_scan configuration
 +        option can be changed if needed
 +      * wpa_cli/wpa_gui: skip non-socket files in control directory when
 +        using UNIX domain sockets; this avoids selecting an incorrect
 +        interface (e.g., a PID file could be in this directory, even though
 +        use of this directory for something else than socket files is not
 +        recommended)
 +      * fixed TLS library deinitialization after RSN pre-authentication not
 +        to disable TLS library for normal authentication
 +      * driver_wext: Remove null-termination from SSID length if the driver
 +        used it; some Linux drivers do this and they were causing problems in
 +        wpa_supplicant not finding matching configuration block. This change
 +        would break a case where the SSID actually ends in '\0', but that is
 +        not likely to happen in real use.
 +      * fixed PMKSA cache processing not to trigger deauthentication if the
 +        current PMKSA cache entry is replaced with a valid new entry
 +      * fixed PC/SC initialization for ap_scan != 1 modes (this fixes
 +        EAP-SIM and EAP-AKA with real SIM/USIM card when using ap_scan=0 or
 +        ap_scan=2)
 +
 +2005-12-18 - v0.5.0 (beginning of 0.5.x development releases)
 +      * added experimental STAKey handshake implementation for IEEE 802.11e
 +        direct link setup (DLS); note: this is disabled by default in both
 +        build and runtime configuration (can be enabled with CONFIG_STAKEY=y
 +        and stakey=1)
 +      * fixed EAP-SIM and EAP-AKA pseudonym and fast re-authentication to
 +        decrypt AT_ENCR_DATA attributes correctly
 +      * fixed EAP-AKA to allow resynchronization within the same session
 +      * made code closer to ANSI C89 standard to make it easier to port to
 +        other C libraries and compilers
 +      * started moving operating system or C library specific functions into
 +        wrapper functions defined in os.h and implemented in os_*.c to make
 +        code more portable
 +      * wpa_supplicant can now be built with Microsoft Visual C++
 +        (e.g., with the freely available Toolkit 2003 version or Visual
 +        C++ 2005 Express Edition and Platform SDK); see nmake.mak for an
 +        example makefile for nmake
 +      * added support for using Windows registry for command line parameters
 +        (CONFIG_MAIN=main_winsvc) and configuration data
 +        (CONFIG_BACKEND=winreg); see win_example.reg for an example registry
 +        contents; this version can be run both as a Windows service and as a
 +        normal application; 'wpasvc.exe app' to start as applicant,
 +        'wpasvc.exe reg <full path to wpasvc.exe>' to register a service,
 +        'net start wpasvc' to start the service, 'wpasvc.exe unreg' to
 +        unregister a service
 +      * made it possible to link ndis_events.exe functionality into
 +        wpa_supplicant.exe by defining CONFIG_NDIS_EVENTS_INTEGRATED
 +      * added better support for multiple control interface backends
 +        (CONFIG_CTRL_IFACE option); currently, 'unix' and 'udp' are supported
 +      * fixed PC/SC code to use correct length for GSM AUTH command buffer
 +        and to not use pioRecvPci with SCardTransmit() calls; these were not
 +        causing visible problems with pcsc-lite, but Windows Winscard.dll
 +        refused the previously used parameters; this fixes EAP-SIM and
 +        EAP-AKA authentication using SIM/USIM card under Windows
 +      * added new event loop implementation for Windows using
 +        WaitForMultipleObject() instead of select() in order to allow waiting
 +        for non-socket objects; this can be selected with
 +        CONFIG_ELOOP=eloop_win in .config
 +      * added support for selecting l2_packet implementation in .config
 +        (CONFIG_L2_PACKET; following options are available now: linux, pcap,
 +        winpcap, freebsd, none)
 +      * added new l2_packet implementation for WinPcap
 +        (CONFIG_L2_PACKET=winpcap) that uses a separate receive thread to
 +        reduce latency in EAPOL receive processing from about 100 ms to about
 +        3 ms
 +      * added support for EAP-FAST key derivation using other ciphers than
 +        RC4-128-SHA for authentication and AES128-SHA for provisioning
 +      * added support for configuring CA certificate as DER file and as a
 +        configuration blob
 +      * fixed private key configuration as configuration blob and added
 +        support for using PKCS#12 as a blob
 +      * tls_gnutls: added support for using PKCS#12 files; added support for
 +        session resumption
 +      * added support for loading trusted CA certificates from Windows
 +        certificate store: ca_cert="cert_store://<name>", where <name> is
 +        likely CA (Intermediate CA certificates) or ROOT (root certificates)
 +      * added C version of ndis_events.cpp and made it possible to build this
 +        with MinGW so that CONFIG_NDIS_EVENTS_INTEGRATED can be used more
 +        easily on cross-compilation builds
 +      * added wpasvc.exe into Windows binary release; this is an alternative
 +        version of wpa_supplicant.exe with configuration backend using
 +        Windows registry and with the entry point designed to run as a
 +        Windows service
 +      * integrated ndis_events.exe functionality into wpa_supplicant.exe and
 +        wpasvc.exe and removed this additional tool from the Windows binary
 +        release since it is not needed anymore
 +      * load winscard.dll functions dynamically when building with MinGW
 +        since MinGW does not yet include winscard library
 +
 +2005-11-20 - v0.4.7 (beginning of 0.4.x stable releases)
 +      * l2_packet_pcap: fixed wired IEEE 802.1X authentication with libpcap
 +        and WinPcap to receive frames sent to PAE group address
 +      * disable EAP state machine when IEEE 802.1X authentication is not used
 +        in order to get rid of bogus "EAP failed" messages
 +      * fixed OpenSSL error reporting to go through all pending errors to
 +        avoid confusing reports of old errors being reported at later point
 +        during handshake
 +      * fixed configuration file updating to not write empty variables
 +        (e.g., proto or key_mgmt) that the file parser would not accept
 +      * fixed ADD_NETWORK ctrl_iface command to use the same default values
 +        for variables as empty network definitions read from config file
 +        would get
 +      * fixed EAP state machine to not discard EAP-Failure messages in many
 +        cases (e.g., during TLS handshake)
 +      * fixed a infinite loop in private key reading if the configured file
 +        cannot be parsed successfully
 +      * driver_madwifi: added support for madwifi-ng
 +      * wpa_gui: do not display password/PSK field contents
 +      * wpa_gui: added CA certificate configuration
 +      * driver_ndis: fixed scan request in ap_scan=2 mode not to change SSID
 +      * driver_ndis: include Beacon IEs in AssocInfo in order to notice if
 +        the new AP is using different WPA/RSN IE
 +      * use longer timeout for IEEE 802.11 association to avoid problems with
 +        drivers that may take more than five second to associate
 +
 +2005-10-27 - v0.4.6
 +      * allow fallback to WPA, if mixed WPA+WPA2 networks have mismatch in
 +        RSN IE, but WPA IE would match with wpa_supplicant configuration
 +      * added support for named configuration blobs in order to avoid having
 +        to use file system for external files (e.g., certificates);
 +        variables can be set to "blob://<blob name>" instead of file path to
 +        use a named blob; supported fields: pac_file, client_cert,
 +        private_key
 +      * fixed RSN pre-authentication (it was broken in the clean up of WPA
 +        state machine interface in v0.4.5)
 +      * driver_madwifi: set IEEE80211_KEY_GROUP flag for group keys to make
 +        sure the driver configures broadcast decryption correctly
 +      * added ca_path (and ca_path2) configuration variables that can be used
 +        to configure OpenSSL CA path, e.g., /etc/ssl/certs, for using the
 +        system-wide trusted CA list
 +      * added support for starting wpa_supplicant without a configuration
 +        file (-C argument must be used to set ctrl_interface parameter for
 +        this case; in addition, -p argument can be used to provide
 +        driver_param; these new arguments can also be used with a
 +        configuration to override the values from the configuration)
 +      * added global control interface that can be optionally used for adding
 +        and removing network interfaces dynamically (-g command line argument
 +        for both wpa_supplicant and wpa_cli) without having to restart
 +        wpa_supplicant process
 +      * wpa_gui:
 +        - try to save configuration whenever something is modified
 +        - added WEP key configuration
 +        - added possibility to edit the current network configuration
 +      * driver_ndis: fixed driver polling not to increase frequency on each
 +        received EAPOL frame due to incorrectly cancelled timeout
 +      * added simple configuration file examples (in examples subdirectory)
 +      * fixed driver_wext.c to filter wireless events based on ifindex to
 +        avoid interfaces receiving events from other interfaces
 +      * delay sending initial EAPOL-Start couple of seconds to speed up
 +        authentication for the most common case of Authenticator starting
 +        EAP authentication immediately after association
 +
 +2005-09-25 - v0.4.5
 +      * added a workaround for clearing keys with ndiswrapper to allow
 +        roaming from WPA enabled AP to plaintext one
 +      * added docbook documentation (doc/docbook) that can be used to
 +        generate, e.g., man pages
 +      * l2_packet_linux: use socket type SOCK_DGRAM instead of SOCK_RAW for
 +        PF_PACKET in order to prepare for network devices that do not use
 +        Ethernet headers (e.g., network stack with native IEEE 802.11 frames)
 +      * use receipt of EAPOL-Key frame as a lower layer success indication
 +        for EAP state machine to allow recovery from dropped EAP-Success
 +        frame
 +      * cleaned up internal EAPOL frame processing by not including link
 +        layer (Ethernet) header during WPA and EAPOL/EAP processing; this
 +        header is added only when transmitted the frame; this makes it easier
 +        to use wpa_supplicant on link layers that use different header than
 +        Ethernet
 +      * updated EAP-PSK to use draft 9 by default since this can now be
 +        tested with hostapd; removed support for draft 3, including
 +        server_nai configuration option from network blocks
 +      * driver_wired: add PAE address to the multicast address list in order
 +        to be able to receive EAPOL frames with drivers that do not include
 +        these multicast addresses by default
 +      * driver_wext: add support for WE-19
 +      * added support for multiple configuration backends (CONFIG_BACKEND
 +        option); currently, only 'file' is supported (i.e., the format used
 +        in wpa_supplicant.conf)
 +      * added support for updating configuration ('wpa_cli save_config');
 +        this is disabled by default and can be enabled with global
 +        update_config=1 variable in wpa_supplicant.conf; this allows wpa_cli
 +        and wpa_gui to store the configuration changes in a permanent store
 +      * added GET_NETWORK ctrl_iface command
 +        (e.g., 'wpa_cli get_network 0 ssid')
 +
 +2005-08-21 - v0.4.4
 +      * replaced OpenSSL patch for EAP-FAST support
 +        (openssl-tls-extensions.patch) with a more generic and correct
 +        patch (the new patch is not compatible with the previous one, so the
 +        OpenSSL library will need to be patched with the new patch in order
 +        to be able to build wpa_supplicant with EAP-FAST support)
 +      * added support for using Windows certificate store (through CryptoAPI)
 +        for client certificate and private key operations (EAP-TLS)
 +        (see wpa_supplicant.conf for more information on how to configure
 +        this with private_key)
 +      * ported wpa_gui to Windows
 +      * added Qt4 version of wpa_gui (wpa_gui-qt4 directory); this can be
 +        built with the open source version of the Qt4 for Windows
 +      * allow non-WPA modes (e.g., IEEE 802.1X with dynamic WEP) to be used
 +        with drivers that do not support WPA
 +      * ndis_events: fixed Windows 2000 support
 +      * added support for enabling/disabling networks from the list of all
 +        configured networks ('wpa_cli enable_network <network id>' and
 +        'wpa_cli disable_network <network id>')
 +      * added support for adding and removing network from the current
 +        configuration ('wpa_cli add_network' and 'wpa_cli remove_network
 +        <network id>'); added networks are disabled by default and they can
 +        be enabled with enable_network command once the configuration is done
 +        for the new network; note: configuration file is not yet updated, so
 +        these new networks are lost when wpa_supplicant is restarted
 +      * added support for setting network configuration parameters through
 +        the control interface, for example:
 +        wpa_cli set_network 0 ssid "\"my network\""
 +      * fixed parsing of strings that include both " and # within double
 +        quoted area (e.g., "start"#end")
 +      * added EAP workaround for PEAP session resumption: allow outer,
 +        i.e., not tunneled, EAP-Success to terminate session since; this can
 +        be disabled with eap_workaround=0
 +        (this was allowed for PEAPv1 before, but now it is also allowed for
 +        PEAPv0 since at least one RADIUS authentication server seems to be
 +        doing this for PEAPv0, too)
 +      * wpa_gui: added preliminary support for adding new networks to the
 +        wpa_supplicant configuration (double click on the scan results to
 +        open network configuration)
 +
 +2005-06-26 - v0.4.3
 +      * removed interface for external EAPOL/EAP supplicant (e.g.,
 +        Xsupplicant), (CONFIG_XSUPPLICANT_IFACE) since it is not required
 +        anymore and is unlikely to be used by anyone
 +      * driver_ndis: fixed WinPcap 3.0 support
 +      * fixed build with CONFIG_DNET_PCAP=y on Linux
 +      * l2_packet: moved different implementations into separate files
 +        (l2_packet_*.c)
 +
 +2005-06-12 - v0.4.2
 +      * driver_ipw: updated driver structures to match with ipw2200-1.0.4
 +        (note: ipw2100-1.1.0 is likely to require an update to work with
 +        this)
 +      * added support for using ap_scan=2 mode with multiple network blocks;
 +        wpa_supplicant will go through the networks one by one until the
 +        driver reports a successful association; this uses the same order for
 +        networks as scan_ssid=1 scans, i.e., the priority field is ignored
 +        and the network block order in the file is used instead
 +      * fixed a potential issue in RSN pre-authentication ending up using
 +        freed memory if pre-authentication times out
 +      * added support for matching alternative subject name extensions of the
 +        authentication server certificate; new configuration variables
 +        altsubject_match and altsubject_match2
 +      * driver_ndis: added support for IEEE 802.1X authentication with wired
 +        NDIS drivers
 +      * added support for querying private key password (EAP-TLS) through the
 +        control interface (wpa_cli/wpa_gui) if one is not included in the
 +        configuration file
 +      * driver_broadcom: fixed couple of memory leaks in scan result
 +        processing
 +      * EAP-PAX is now registered as EAP type 46
 +      * fixed EAP-PAX MAC calculation
 +      * fixed EAP-PAX CK and ICK key derivation
 +      * added support for using password with EAP-PAX (as an alternative to
 +        entering key with eappsk); SHA-1 hash of the password will be used as
 +        the key in this case
 +      * added support for arbitrary driver interface parameters through the
 +        configuration file with a new driver_param field; this adds a new
 +        driver_ops function set_param()
 +      * added possibility to override l2_packet module with driver interface
 +        API (new send_eapol handler); this can be used to implement driver
 +        specific TX/RX functions for EAPOL frames
 +      * fixed ctrl_interface_group processing for the case where gid is
 +        entered as a number, not group name
 +      * driver_test: added support for testing hostapd with wpa_supplicant
 +        by using test driver interface without any kernel drivers or network
 +        cards
 +
 +2005-05-22 - v0.4.1
 +      * driver_madwifi: fixed WPA/WPA2 mode configuration to allow EAPOL
 +        packets to be encrypted; this was apparently broken by the changed
 +        ioctl order in v0.4.0
 +      * driver_madwifi: added preliminary support for compiling against 'BSD'
 +        branch of madwifi CVS tree
 +      * added support for EAP-MSCHAPv2 password retries within the same EAP
 +        authentication session
 +      * added support for password changes with EAP-MSCHAPv2 (used when the
 +        password has expired)
 +      * added support for reading additional certificates from PKCS#12 files
 +        and adding them to the certificate chain
 +      * fixed association with IEEE 802.1X (no WPA) when dynamic WEP keys
 +        were used
 +      * fixed a possible double free in EAP-TTLS fast-reauthentication when
 +        identity or password is entered through control interface
 +      * display EAP Notification messages to user through control interface
 +        with "CTRL-EVENT-EAP-NOTIFICATION" prefix
 +      * added GUI version of wpa_cli, wpa_gui; this is not build
 +        automatically with 'make'; use 'make wpa_gui' to build (this requires
 +        Qt development tools)
 +      * added 'disconnect' command to control interface for setting
 +        wpa_supplicant in state where it will not associate before
 +        'reassociate' command has been used
 +      * added support for selecting a network from the list of all configured
 +        networks ('wpa_cli select_network <network id>'; this disabled all
 +        other networks; to re-enable, 'wpa_cli select_network any')
 +      * added support for getting scan results through control interface
 +      * added EAP workaround for PEAPv1 session resumption: allow outer,
 +        i.e., not tunneled, EAP-Success to terminate session since; this can
 +        be disabled with eap_workaround=0
 +
 +2005-04-25 - v0.4.0 (beginning of 0.4.x development releases)
 +      * added a new build time option, CONFIG_NO_STDOUT_DEBUG, that can be
 +        used to reduce the size of the wpa_supplicant considerably if
 +        debugging code is not needed
 +      * fixed EAPOL-Key validation to drop packets with invalid Key Data
 +        Length; such frames could have crashed wpa_supplicant due to buffer
 +        overflow
 +      * added support for wired authentication (IEEE 802.1X on wired
 +        Ethernet); driver interface 'wired'
 +      * obsoleted set_wpa() handler in the driver interface API (it can be
 +        replaced by moving enable/disable functionality into init()/deinit())
 +        (calls to set_wpa() are still present for backwards compatibility,
 +        but they may be removed in the future)
 +      * driver_madwifi: fixed association in plaintext mode
 +      * modified the EAP workaround that accepts EAP-Success with incorrect
 +        Identifier to be even less strict about verification in order to
 +        interoperate with some authentication servers
 +      * added support for sending TLS alerts
 +      * added support for 'any' SSID wildcard; if ssid is not configured or
 +        is set to an empty string, any SSID will be accepted for non-WPA AP
 +      * added support for asking PIN (for SIM) from frontends (e.g.,
 +        wpa_cli); if a PIN is needed, but not included in the configuration
 +        file, a control interface request is sent and EAP processing is
 +        delayed until the PIN is available
 +      * added support for using external devices (e.g., a smartcard) for
 +        private key operations in EAP-TLS (CONFIG_SMARTCARD=y in .config);
 +        new wpa_supplicant.conf variables:
 +        - global: opensc_engine_path, pkcs11_engine_path, pkcs11_module_path
 +        - network: engine, engine_id, key_id
 +      * added experimental support for EAP-PAX
 +      * added monitor mode for wpa_cli (-a<path to a program to run>) that
 +        allows external commands (e.g., shell scripts) to be run based on
 +        wpa_supplicant events, e.g., when authentication has been completed
 +        and data connection is ready; other related wpa_cli arguments:
 +        -B (run in background), -P (write PID file); wpa_supplicant has a new
 +        command line argument (-W) that can be used to make it wait until a
 +        control interface command is received in order to avoid missing
 +        events
 +      * added support for opportunistic WPA2 PMKSA key caching (disabled by
 +        default, can be enabled with proactive_key_caching=1)
 +      * fixed RSN IE in 4-Way Handshake message 2/4 for the case where
 +        Authenticator rejects PMKSA caching attempt and the driver is not
 +        using assoc_info events
 +      * added -P<pid file> argument for wpa_supplicant to write the current
 +        process id into a file
 +
 +2005-02-12 - v0.3.7 (beginning of 0.3.x stable releases)
 +      * added new phase1 option parameter, include_tls_length=1, to force
 +        wpa_supplicant to add TLS Message Length field to all TLS messages
 +        even if the packet is not fragmented; this may be needed with some
 +        authentication servers
 +      * fixed WPA/RSN IE verification in message 3 of 4-Way Handshake when
 +        using drivers that take care of AP selection (e.g., when using
 +        ap_scan=2)
 +      * fixed reprocessing of pending request after ctrl_iface requests for
 +        identity/password/otp
 +      * fixed ctrl_iface requests for identity/password/otp in Phase 2 of
 +        EAP-PEAP and EAP-TTLS
 +      * all drivers using driver_wext: set interface up and select Managed
 +        mode when starting wpa_supplicant; set interface down when exiting
 +      * renamed driver_ipw2100.c to driver_ipw.c since it now supports both
 +        ipw2100 and ipw2200; please note that this also changed the
 +        configuration variable in .config to CONFIG_DRIVER_IPW
 +
 +2005-01-24 - v0.3.6
 +      * fixed a busy loop introduced in v0.3.5 for scan result processing
 +        when no matching AP is found
 +
 +2005-01-23 - v0.3.5
 +      * added a workaround for an interoperability issue with a Cisco AP
 +        when using WPA2-PSK
 +      * fixed non-WPA IEEE 802.1X to use the same authentication timeout as
 +        WPA with IEEE 802.1X (i.e., timeout 10 -> 70 sec to allow
 +        retransmission of dropped frames)
 +      * fixed issues with 64-bit CPUs and SHA1 cleanup in previous version
 +        (e.g., segfault when processing EAPOL-Key frames)
 +      * fixed EAP workaround and fast reauthentication configuration for
 +        RSN pre-authentication; previously these were disabled and
 +        pre-authentication would fail if the used authentication server
 +        requires EAP workarounds
 +      * added support for blacklisting APs that fail or timeout
 +        authentication in ap_scan=1 mode so that all APs are tried in cases
 +        where the ones with strongest signal level are failing authentication
 +      * fixed CA certificate loading after a failed EAP-TLS/PEAP/TTLS
 +        authentication attempt
 +      * allow EAP-PEAP/TTLS fast reauthentication only if Phase 2 succeeded
 +        in the previous authentication (previously, only Phase 1 success was
 +        verified)
 +
 +2005-01-09 - v0.3.4
 +      * added preliminary support for IBSS (ad-hoc) mode configuration
 +        (mode=1 in network block); this included a new key_mgmt mode
 +        WPA-NONE, i.e., TKIP or CCMP with a fixed key (based on psk) and no
 +        key management; see wpa_supplicant.conf for more details and an
 +        example on how to configure this (note: this is currently implemented
 +        only for driver_hostapd.c, but the changes should be trivial to add
 +        in associate() handler for other drivers, too (assuming the driver
 +        supports WPA-None)
 +      * added preliminary port for native Windows (i.e., no cygwin) using
 +        mingw
 +
 +2005-01-02 - v0.3.3
 +      * added optional support for GNU Readline and History Libraries for
 +        wpa_cli (CONFIG_READLINE)
 +      * cleaned up EAP state machine <-> method interface and number of
 +        small problems with error case processing not terminating on
 +        EAP-Failure but waiting for timeout
 +      * added couple of workarounds for interoperability issues with a
 +        Cisco AP when using WPA2
 +      * added support for EAP-FAST (draft-cam-winget-eap-fast-00.txt);
 +        Note: This requires a patch for openssl to add support for TLS
 +        extensions and number of workarounds for operations without
 +        certificates. Proof of concept type of experimental patch is
 +        included in openssl-tls-extensions.patch.
 +
 +2004-12-19 - v0.3.2
 +      * fixed private key loading for cases where passphrase is not set
 +      * fixed Windows/cygwin L2 packet handler freeing; previous version
 +        could cause a segfault when RSN pre-authentication was completed
 +      * added support for PMKSA caching with drivers that generate RSN IEs
 +        (e.g., NDIS); currently, this is only implemented in driver_ndis.c,
 +        but similar code can be easily added to driver_ndiswrapper.c once
 +        ndiswrapper gets full support for RSN PMKSA caching
 +      * improved recovery from PMKID mismatches by requesting full EAP
 +        authentication in case of failed PMKSA caching attempt
 +      * driver_ndis: added support for NDIS NdisMIncidateStatus() events
 +        (this requires that ndis_events is ran while wpa_supplicant is
 +        running)
 +      * driver_ndis: use ADD_WEP/REMOVE_WEP when configuring WEP keys
 +      * added support for driver interfaces to replace the interface name
 +        based on driver/OS specific mapping, e.g., in case of driver_ndis,
 +        this allows the beginning of the adapter description to be used as
 +        the interface name
 +      * added support for CR+LF (Windows-style) line ends in configuration
 +        file
 +      * driver_ndis: enable radio before starting scanning, disable radio
 +        when exiting
 +      * modified association event handler to set portEnabled = FALSE before
 +        clearing port Valid in order to reset EAP state machine and avoid
 +        problems with new authentication getting ignored because of state
 +        machines ending up in AUTHENTICATED/SUCCESS state based on old
 +        information
 +      * added support for driver events to add PMKID candidates in order to
 +        allow drivers to give priority to most likely roaming candidates
 +      * driver_hostap: moved PrivacyInvoked configuration to associate()
 +        function so that this will not be set for plaintext connections
 +      * added KEY_MGMT_802_1X_NO_WPA as a new key_mgmt type so that driver
 +        interface can distinguish plaintext and IEEE 802.1X (no WPA)
 +        authentication
 +      * fixed static WEP key configuration to use broadcast/default type for
 +        all keys (previously, the default TX key was configured as pairwise/
 +        unicast key)
 +      * driver_ndis: added legacy WPA capability detection for non-WPA2
 +        drivers
 +      * added support for setting static WEP keys for IEEE 802.1X without
 +        dynamic WEP keying (eapol_flags=0)
 +
 +2004-12-12 - v0.3.1
 +      * added support for reading PKCS#12 (PFX) files (as a replacement for
 +        PEM/DER) to get certificate and private key (CONFIG_PKCS12)
 +      * fixed compilation with CONFIG_PCSC=y
 +      * added new ap_scan mode, ap_scan=2, for drivers that take care of
 +        association, but need to be configured with security policy and SSID,
 +        e.g., ndiswrapper and NDIS driver; this mode should allow such
 +        drivers to work with hidden SSIDs and optimized roaming; when
 +        ap_scan=2 is used, only the first network block in the configuration
 +        file is used and this configuration should have explicit security
 +        policy (i.e., only one option in the lists) for key_mgmt, pairwise,
 +        group, proto variables
 +      * added experimental port of wpa_supplicant for Windows
 +        - driver_ndis.c driver interface (NDIS OIDs)
 +        - currently, this requires cygwin and WinPcap
 +        - small utility, win_if_list, can be used to get interface name
 +      * control interface can now be removed at build time; add
 +        CONFIG_CTRL_IFACE=y to .config to maintain old functionality
 +      * optional Xsupplicant interface can now be removed at build time;
 +        (CONFIG_XSUPPLICANT_IFACE=y in .config to bring it back)
 +      * added auth_alg to driver interface associate() parameters to make it
 +        easier for drivers to configure authentication algorithm as part of
 +        the association
 +
 +2004-12-05 - v0.3.0 (beginning of 0.3.x development releases)
 +      * driver_broadcom: added new driver interface for Broadcom wl.o driver
 +        (a generic driver for Broadcom IEEE 802.11a/g cards)
 +      * wpa_cli: fixed parsing of -p <path> command line argument
 +      * PEAPv1: fixed tunneled EAP-Success reply handling to reply with TLS
 +        ACK, not tunneled EAP-Success (of which only the first byte was
 +        actually send due to a bug in previous code); this seems to
 +        interoperate with most RADIUS servers that implements PEAPv1
 +      * PEAPv1: added support for terminating PEAP authentication on tunneled
 +        EAP-Success message; this can be configured by adding
 +        peap_outer_success=0 on phase1 parameters in wpa_supplicant.conf
 +        (some RADIUS servers require this whereas others require a tunneled
 +        reply
 +      * PEAPv1: changed phase1 option peaplabel to use default to 0, i.e., to
 +        the old label for key derivation; previously, the default was 1,
 +        but it looks like most existing PEAPv1 implementations use the old
 +        label which is thus more suitable default option
 +      * added support for EAP-PSK (draft-bersani-eap-psk-03.txt)
 +      * fixed parsing of wep_tx_keyidx
 +      * added support for configuring list of allowed Phase 2 EAP types
 +        (for both EAP-PEAP and EAP-TTLS) instead of only one type
 +      * added support for configuring IEEE 802.11 authentication algorithm
 +        (auth_alg; mainly for using Shared Key authentication with static
 +        WEP keys)
 +      * added support for EAP-AKA (with UMTS SIM)
 +      * fixed couple of errors in PCSC handling that could have caused
 +        random-looking errors for EAP-SIM
 +      * added support for EAP-SIM pseudonyms and fast re-authentication
 +      * added support for EAP-TLS/PEAP/TTLS fast re-authentication (TLS
 +        session resumption)
 +      * added support for EAP-SIM with two challanges
 +        (phase1="sim_min_num_chal=3" can be used to require three challenges)
 +      * added support for configuring DH/DSA parameters for an ephemeral DH
 +        key exchange (EAP-TLS/PEAP/TTLS) using new configuration parameters
 +        dh_file and dh_file2 (phase 2); this adds support for using DSA keys
 +        and optional DH key exchange to achieve forward secracy with RSA keys
 +      * added support for matching subject of the authentication server
 +        certificate with a substring when using EAP-TLS/PEAP/TTLS; new
 +        configuration variables subject_match and subject_match2
 +      * changed SSID configuration in driver_wext.c (used by many driver
 +        interfaces) to use ssid_len+1 as the length for SSID since some Linux
 +        drivers expect this
 +      * fixed couple of unaligned reads in scan result parsing to fix WPA
 +        connection on some platforms (e.g., ARM)
 +      * added driver interface for Intel ipw2100 driver
 +      * added support for LEAP with WPA
 +      * added support for larger scan results report (old limit was 4 kB of
 +        data, i.e., about 35 or so APs) when using Linux wireless extensions
 +        v17 or newer
 +      * fixed a bug in PMKSA cache processing: skip sending of EAPOL-Start
 +        only if there is a PMKSA cache entry for the current AP
 +      * fixed error handling for case where reading of scan results fails:
 +        must schedule a new scan or wpa_supplicant will remain waiting
 +        forever
 +      * changed debug output to remove shared password/key material by
 +        default; all key information can be included with -K command line
 +        argument to match the previous behavior
 +      * added support for timestamping debug log messages (disabled by
 +        default, can be enabled with -t command line argument)
 +      * set pairwise/group cipher suite for non-WPA IEEE 802.1X to WEP-104
 +        if keys are not configured to be used; this fixes IEEE 802.1X mode
 +        with drivers that use this information to configure whether Privacy
 +        bit can be in Beacon frames (e.g., ndiswrapper)
 +      * avoid clearing driver keys if no keys have been configured since last
 +        key clear request; this seems to improve reliability of group key
 +        handshake for ndiswrapper & NDIS driver which seems to be suffering
 +        of some kind of timing issue when the keys are cleared again after
 +        association
 +      * changed driver interface API:
 +        - WPA_SUPPLICANT_DRIVER_VERSION define can be used to determine which
 +          version is being used (now, this is set to 2; previously, it was
 +          not defined)
 +        - pass pointer to private data structure to all calls
 +        - the new API is not backwards compatible; all in-tree driver
 +          interfaces has been converted to the new API
 +      * added support for controlling multiple interfaces (radios) per
 +        wpa_supplicant process; each interface needs to be listed on the
 +        command line (-c, -i, -D arguments) with -N as a separator
 +        (-cwpa1.conf -iwlan0 -Dhostap -N -cwpa2.conf -iath0 -Dmadwifi)
 +      * added a workaround for EAP servers that incorrectly use same Id for
 +        sequential EAP packets
 +      * changed libpcap/libdnet configuration to use .config variable,
 +        CONFIG_DNET_PCAP, instead of requiring Makefile modification
 +      * improved downgrade attack detection in IE verification of msg 3/4:
 +        verify both WPA and RSN IEs, if present, not only the selected one;
 +        reject the AP if an RSN IE is found in msg 3/4, but not in Beacon or
 +        Probe Response frame, and RSN is enabled in wpa_supplicant
 +        configuration
 +      * fixed WPA msg 3/4 processing to allow Key Data field contain other
 +        IEs than just one WPA IE
 +      * added support for FreeBSD and driver interface for the BSD net80211
 +        layer (CONFIG_DRIVER_BSD=y in .config); please note that some of the
 +        required kernel mods have not yet been committed
 +      * made EAP workarounds configurable; enabled by default, can be
 +        disabled with network block option eap_workaround=0
 +
 +2004-07-17 - v0.2.4 (beginning of 0.2.x stable releases)
 +      * resolved couple of interoperability issues with EAP-PEAPv1 and
 +        Phase 2 (inner EAP) fragment reassembly
 +      * driver_madwifi: fixed WEP key configuration for IEEE 802.1X when the
 +        AP is using non-zero key index for the unicast key and key index zero
 +        for the broadcast key
 +      * driver_hostap: fixed IEEE 802.1X WEP key updates and
 +        re-authentication by allowing unencrypted EAPOL frames when not using
 +        WPA
 +      * added a new driver interface, 'wext', which uses only standard,
 +        driver independent functionality in Linux wireless extensions;
 +        currently, this can be used only for non-WPA IEEE 802.1X mode, but
 +        eventually, this is to be extended to support full WPA/WPA2 once
 +        Linux wireless extensions get support for this
 +      * added support for mode in which the driver is responsible for AP
 +        scanning and selection; this is disabled by default and can be
 +        enabled with global ap_scan=0 variable in wpa_supplicant.conf;
 +        this mode can be used, e.g., with generic 'wext' driver interface to
 +        use wpa_supplicant as IEEE 802.1X Supplicant with any Linux driver
 +        supporting wireless extensions.
 +      * driver_madwifi: fixed WPA2 configuration and scan_ssid=1 (e.g.,
 +        operation with an AP that does not include SSID in the Beacon frames)
 +      * added support for new EAP authentication methods:
 +        EAP-TTLS/EAP-OTP, EAP-PEAPv0/OTP, EAP-PEAPv1/OTP, EAP-OTP
 +      * added support for asking one-time-passwords from frontends (e.g.,
 +        wpa_cli); this 'otp' command works otherwise like 'password' command,
 +        but the password is used only once and the frontend will be asked for
 +        a new password whenever a request from authenticator requires a
 +        password; this can be used with both EAP-OTP and EAP-GTC
 +      * changed wpa_cli to automatically re-establish connection so that it
 +        does not need to be re-started when wpa_supplicant is terminated and
 +        started again
 +      * improved user data (identity/password/otp) requests through
 +        frontends: process pending EAPOL packets after getting new
 +        information so that full authentication does not need to be
 +        restarted; in addition, send pending requests again whenever a new
 +        frontend is attached
 +      * changed control frontends to use a new directory for socket files to
 +        make it easier for wpa_cli to automatically select between interfaces
 +        and to provide access control for the control interface;
 +        wpa_supplicant.conf: ctrl_interface is now a path
 +        (/var/run/wpa_supplicant is the recommended path) and
 +        ctrl_interface_group can be used to select which group gets access to
 +        the control interface;
 +        wpa_cli: by default, try to connect to the first interface available
 +        in /var/run/wpa_supplicant; this path can be overriden with -p option
 +        and an interface can be selected with -i option (i.e., in most common
 +        cases, wpa_cli does not need to get any arguments)
 +      * added support for LEAP
 +      * added driver interface for Linux ndiswrapper
 +      * added priority option for network blocks in the configuration file;
 +        this allows networks to be grouped based on priority (the scan
 +        results are searched for matches with network blocks in this order)
 +
 +2004-06-20 - v0.2.3
 +      * sort scan results to improve AP selection
 +      * fixed control interface socket removal for some error cases
 +      * improved scan requesting and authentication timeout
 +      * small improvements/bug fixes for EAP-MSCHAPv2, EAP-PEAP, and
 +        TLS processing
 +      * PEAP version can now be forced with phase1="peapver=<ver>"
 +        (mostly for testing; by default, the highest version supported by
 +        both the Supplicant and Authentication Server is selected
 +        automatically)
 +      * added support for madwifi driver (Atheros ar521x)
 +      * added a workaround for cases where AP sets Install Tx/Rx bit for
 +        WPA Group Key messages when pairwise keys are used (without this,
 +        the Group Key would be used for Tx and the AP would drop frames
 +        from the station)
 +      * added GSM SIM/USIM interface for GSM authentication algorithm for
 +        EAP-SIM; this requires pcsc-lite
 +      * added support for ATMEL AT76C5XXx driver
 +      * fixed IEEE 802.1X WEP key derivation in the case where Authenticator
 +        does not include key data in the EAPOL-Key frame (i.e., part of
 +        EAP keying material is used as data encryption key)
 +      * added support for using plaintext and static WEP networks
 +        (key_mgmt=NONE)
 +
 +2004-05-31 - v0.2.2
 +      * added support for new EAP authentication methods:
 +        EAP-TTLS/EAP-MD5-Challenge
 +        EAP-TTLS/EAP-GTC
 +        EAP-TTLS/EAP-MSCHAPv2
 +        EAP-TTLS/EAP-TLS
 +        EAP-TTLS/MSCHAPv2
 +        EAP-TTLS/MSCHAP
 +        EAP-TTLS/PAP
 +        EAP-TTLS/CHAP
 +        EAP-PEAP/TLS
 +        EAP-PEAP/GTC
 +        EAP-PEAP/MD5-Challenge
 +        EAP-GTC
 +        EAP-SIM (not yet complete; needs GSM/SIM authentication interface)
 +      * added support for anonymous identity (to be used when identity is
 +        sent in plaintext; real identity will be used within TLS protected
 +        tunnel (e.g., with EAP-TTLS)
 +      * added event messages from wpa_supplicant to frontends, e.g., wpa_cli
 +      * added support for requesting identity and password information using
 +        control interface; in other words, the password for EAP-PEAP or
 +        EAP-TTLS does not need to be included in the configuration file since
 +        a frontand (e.g., wpa_cli) can ask it from the user
 +      * improved RSN pre-authentication to use a candidate list and process
 +        all candidates from each scan; not only one per scan
 +      * fixed RSN IE and WPA IE capabilities field parsing
 +      * ignore Tx bit in GTK IE when Pairwise keys are used
 +      * avoid making new scan requests during IEEE 802.1X negotiation
 +      * use openssl/libcrypto for MD5 and SHA-1 when compiling wpa_supplicant
 +        with TLS support (this replaces the included implementation with
 +        library code to save about 8 kB since the library code is needed
 +        anyway for TLS)
 +      * fixed WPA-PSK only mode when compiled without IEEE 802.1X support
 +        (i.e., without CONFIG_IEEE8021X_EAPOL=y in .config)
 +
 +2004-05-06 - v0.2.1
 +      * added support for internal IEEE 802.1X (actually, IEEE 802.1aa/D6.1)
 +        Supplicant
 +        - EAPOL state machines for Supplicant [IEEE 802.1aa/D6.1]
 +        - EAP peer state machine [draft-ietf-eap-statemachine-02.pdf]
 +        - EAP-MD5 (cannot be used with WPA-RADIUS)
 +          [draft-ietf-eap-rfc2284bis-09.txt]
 +        - EAP-TLS [RFC 2716]
 +        - EAP-MSCHAPv2 (currently used only with EAP-PEAP)
 +        - EAP-PEAP/MSCHAPv2 [draft-josefsson-pppext-eap-tls-eap-07.txt]
 +          [draft-kamath-pppext-eap-mschapv2-00.txt]
 +          (PEAP version 0, 1, and parts of 2; only 0 and 1 are enabled by
 +          default; tested with FreeRADIUS, Microsoft IAS, and Funk Odyssey)
 +        - new configuration file options: eap, identity, password, ca_cert,
 +          client_cert, privatekey, private_key_passwd
 +        - Xsupplicant is not required anymore, but it can be used by
 +          disabling the internal IEEE 802.1X Supplicant with -e command line
 +          option
 +        - this code is not included in the default build; Makefile need to
 +          be edited for this (uncomment lines for selected functionality)
 +        - EAP-TLS and EAP-PEAP require openssl libraries
 +      * use module prefix in debug messages (WPA, EAP, EAP-TLS, ..)
 +      * added support for non-WPA IEEE 802.1X mode with dynamic WEP keys
 +        (i.e., complete IEEE 802.1X/EAP authentication and use IEEE 802.1X
 +         EAPOL-Key frames instead of WPA key handshakes)
 +      * added support for IEEE 802.11i/RSN (WPA2)
 +        - improved PTK Key Handshake
 +        - PMKSA caching, pre-authentication
 +      * fixed wpa_supplicant to ignore possible extra data after WPA
 +        EAPOL-Key packets (this fixes 'Invalid EAPOL-Key MIC when using
 +        TPTK' error from message 3 of 4-Way Handshake in case the AP
 +        includes extra data after the EAPOL-Key)
 +      * added interface for external programs (frontends) to control
 +        wpa_supplicant
 +        - CLI example (wpa_cli) with interactive mode and command line
 +          mode
 +        - replaced SIGUSR1 status/statistics with the new control interface
 +      * made some feature compile time configurable
 +        - .config file for make
 +        - driver interfaces (hostap, hermes, ..)
 +        - EAPOL/EAP functions
 +
 +2004-02-15 - v0.2.0
 +      * Initial version of wpa_supplicant
index 7ecf7a85c30861b6da733f7cd428ea30e6928a61,0000000000000000000000000000000000000000..7a4f4cf4fbe90a559962b12e9132417d359f1c40
mode 100644,000000..100644
--- /dev/null
@@@ -1,1339 -1,0 +1,1381 @@@
-                                    ssi_signal);
 +/*
 + * WPA Supplicant - Basic AP mode support routines
 + * Copyright (c) 2003-2009, Jouni Malinen <j@w1.fi>
 + * Copyright (c) 2009, Atheros Communications
 + *
 + * This software may be distributed under the terms of the BSD license.
 + * See README for more details.
 + */
 +
 +#include "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 "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"
 +#ifdef NEED_AP_MLME
 +#include "ap/ieee802_11.h"
 +#endif /* NEED_AP_MLME */
 +#include "ap/beacon.h"
 +#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"
 +#include "config.h"
 +#include "wpa_supplicant_i.h"
 +#include "driver_i.h"
 +#include "p2p_supplicant.h"
 +#include "ap.h"
 +#include "ap/sta_info.h"
 +#include "notify.h"
 +
 +
 +#ifdef CONFIG_WPS
 +static void wpas_wps_ap_pin_timeout(void *eloop_data, void *user_ctx);
 +#endif /* CONFIG_WPS */
 +
 +
 +#ifdef CONFIG_IEEE80211N
 +static void wpas_conf_ap_vht(struct wpa_supplicant *wpa_s,
 +                           struct hostapd_config *conf,
 +                           struct hostapd_hw_modes *mode)
 +{
 +#ifdef CONFIG_P2P
 +      u8 center_chan = 0;
 +      u8 channel = conf->channel;
 +
 +      if (!conf->secondary_channel)
 +              goto no_vht;
 +
 +      center_chan = wpas_p2p_get_vht80_center(wpa_s, mode, channel);
 +      if (!center_chan)
 +              goto no_vht;
 +
 +      /* 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 */
 +
 +#ifdef CONFIG_IEEE80211N
 +      /*
 +       * Enable HT20 if the driver supports it, by setting conf->ieee80211n
 +       * and a mask of allowed capabilities within conf->ht_capab.
 +       * Using default config settings for: conf->ht_op_mode_fixed,
 +       * conf->secondary_channel, conf->require_ht
 +       */
 +      if (wpa_s->hw.modes) {
 +              struct hostapd_hw_modes *mode = NULL;
 +              int i, no_ht = 0;
 +              for (i = 0; i < wpa_s->hw.num_modes; i++) {
 +                      if (wpa_s->hw.modes[i].mode == conf->hw_mode) {
 +                              mode = &wpa_s->hw.modes[i];
 +                              break;
 +                      }
 +              }
 +
 +#ifdef CONFIG_HT_OVERRIDES
 +              if (ssid->disable_ht) {
 +                      conf->ieee80211n = 0;
 +                      conf->ht_capab = 0;
 +                      no_ht = 1;
 +              }
 +#endif /* CONFIG_HT_OVERRIDES */
 +
 +              if (!no_ht && mode && mode->ht_capab) {
 +                      conf->ieee80211n = 1;
 +#ifdef CONFIG_P2P
 +                      if (conf->hw_mode == HOSTAPD_MODE_IEEE80211A &&
 +                          (mode->ht_capab &
 +                           HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) &&
 +                          ssid->ht40)
 +                              conf->secondary_channel =
 +                                      wpas_p2p_get_ht40_mode(wpa_s, mode,
 +                                                             conf->channel);
 +                      if (conf->secondary_channel)
 +                              conf->ht_capab |=
 +                                      HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET;
 +#endif /* CONFIG_P2P */
 +
 +                      /*
 +                       * white-list capabilities that won't cause issues
 +                       * to connecting stations, while leaving the current
 +                       * capabilities intact (currently disabled SMPS).
 +                       */
 +                      conf->ht_capab |= mode->ht_capab &
 +                              (HT_CAP_INFO_GREEN_FIELD |
 +                               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);
 +                      }
 +              }
 +      }
++
++      if (conf->secondary_channel) {
++              struct wpa_supplicant *iface;
++
++              for (iface = wpa_s->global->ifaces; iface; iface = iface->next)
++              {
++                      if (iface == wpa_s ||
++                          iface->wpa_state < WPA_AUTHENTICATING ||
++                          (int) iface->assoc_freq != ssid->frequency)
++                              continue;
++
++                      /*
++                       * Do not allow 40 MHz co-ex PRI/SEC switch to force us
++                       * to change our PRI channel since we have an existing,
++                       * concurrent connection on that channel and doing
++                       * multi-channel concurrency is likely to cause more
++                       * harm than using different PRI/SEC selection in
++                       * environment with multiple BSSes on these two channels
++                       * with mixed 20 MHz or PRI channel selection.
++                       */
++                      conf->no_pri_sec_switch = 1;
++              }
++      }
 +#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 &&
 +          (ssid->mode == WPAS_MODE_P2P_GO ||
 +           ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION)) {
 +              /* Remove 802.11b rates from supported and basic rate sets */
 +              int *list = os_malloc(4 * sizeof(int));
 +              if (list) {
 +                      list[0] = 60;
 +                      list[1] = 120;
 +                      list[2] = 240;
 +                      list[3] = -1;
 +              }
 +              conf->basic_rates = list;
 +
 +              list = os_malloc(9 * sizeof(int));
 +              if (list) {
 +                      list[0] = 60;
 +                      list[1] = 90;
 +                      list[2] = 120;
 +                      list[3] = 180;
 +                      list[4] = 240;
 +                      list[5] = 360;
 +                      list[6] = 480;
 +                      list[7] = 540;
 +                      list[8] = -1;
 +              }
 +              conf->supported_rates = list;
 +      }
 +
 +      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) {
 +              wpa_printf(MSG_ERROR, "No SSID configured for AP mode");
 +              return -1;
 +      }
 +      os_memcpy(bss->ssid.ssid, ssid->ssid, ssid->ssid_len);
 +      bss->ssid.ssid_len = ssid->ssid_len;
 +      bss->ssid.ssid_set = 1;
 +
 +      bss->ignore_broadcast_ssid = ssid->ignore_broadcast_ssid;
 +
 +      if (ssid->auth_alg)
 +              bss->auth_algs = ssid->auth_alg;
 +
 +      if (wpa_key_mgmt_wpa_psk(ssid->key_mgmt))
 +              bss->wpa = ssid->proto;
 +      bss->wpa_key_mgmt = ssid->key_mgmt;
 +      bss->wpa_pairwise = ssid->pairwise_cipher;
 +      if (ssid->psk_set) {
 +              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;
 +              os_memcpy(bss->ssid.wpa_psk->psk, ssid->psk, PMK_LEN);
 +              bss->ssid.wpa_psk->group = 1;
 +      } else if (ssid->passphrase) {
 +              bss->ssid.wpa_passphrase = os_strdup(ssid->passphrase);
 +      } else if (ssid->wep_key_len[0] || ssid->wep_key_len[1] ||
 +                 ssid->wep_key_len[2] || ssid->wep_key_len[3]) {
 +              struct hostapd_wep_keys *wep = &bss->ssid.wep;
 +              int i;
 +              for (i = 0; i < NUM_WEP_KEYS; i++) {
 +                      if (ssid->wep_key_len[i] == 0)
 +                              continue;
 +                      wep->key[i] = os_malloc(ssid->wep_key_len[i]);
 +                      if (wep->key[i] == NULL)
 +                              return -1;
 +                      os_memcpy(wep->key[i], ssid->wep_key[i],
 +                                ssid->wep_key_len[i]);
 +                      wep->len[i] = ssid->wep_key_len[i];
 +              }
 +              wep->idx = ssid->wep_tx_keyidx;
 +              wep->keys_set = 1;
 +      }
 +
 +      if (ssid->ap_max_inactivity)
 +              bss->ap_max_inactivity = ssid->ap_max_inactivity;
 +
 +      if (ssid->dtim_period)
 +              bss->dtim_period = ssid->dtim_period;
 +      else if (wpa_s->conf->dtim_period)
 +              bss->dtim_period = wpa_s->conf->dtim_period;
 +
 +      if (ssid->beacon_int)
 +              conf->beacon_int = ssid->beacon_int;
 +      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,
 +                                                  bss->rsn_pairwise);
 +
 +      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;
 +      } 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 (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_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.
 +               */
 +              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
 +       * require user interaction to actually use it. Only the internal
 +       * Registrar is supported.
 +       */
 +      if (bss->ssid.security_policy != SECURITY_WPA_PSK &&
 +          bss->ssid.security_policy != SECURITY_PLAINTEXT)
 +              goto no_wps;
 +      if (bss->ssid.security_policy == SECURITY_WPA_PSK &&
 +          (!(bss->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP)) ||
 +           !(bss->wpa & 2)))
 +              goto no_wps; /* WPS2 does not allow WPA/TKIP-only
 +                            * configuration */
 +      bss->eap_server = 1;
 +
 +      if (!ssid->ignore_broadcast_ssid)
 +              bss->wps_state = 2;
 +
 +      bss->ap_setup_locked = 2;
 +      if (wpa_s->conf->config_methods)
 +              bss->config_methods = os_strdup(wpa_s->conf->config_methods);
 +      os_memcpy(bss->device_type, wpa_s->conf->device_type,
 +                WPS_DEV_TYPE_LEN);
 +      if (wpa_s->conf->device_name) {
 +              bss->device_name = os_strdup(wpa_s->conf->device_name);
 +              bss->friendly_name = os_strdup(wpa_s->conf->device_name);
 +      }
 +      if (wpa_s->conf->manufacturer)
 +              bss->manufacturer = os_strdup(wpa_s->conf->manufacturer);
 +      if (wpa_s->conf->model_name)
 +              bss->model_name = os_strdup(wpa_s->conf->model_name);
 +      if (wpa_s->conf->model_number)
 +              bss->model_number = os_strdup(wpa_s->conf->model_number);
 +      if (wpa_s->conf->serial_number)
 +              bss->serial_number = os_strdup(wpa_s->conf->serial_number);
 +      if (is_nil_uuid(wpa_s->conf->uuid))
 +              os_memcpy(bss->uuid, wpa_s->wps->uuid, WPS_UUID_LEN);
 +      else
 +              os_memcpy(bss->uuid, wpa_s->conf->uuid, WPS_UUID_LEN);
 +      os_memcpy(bss->os_version, wpa_s->conf->os_version, 4);
 +      bss->pbc_in_m1 = wpa_s->conf->pbc_in_m1;
 +no_wps:
 +#endif /* CONFIG_WPS */
 +
 +      if (wpa_s->max_stations &&
 +          wpa_s->max_stations < wpa_s->conf->max_num_sta)
 +              bss->max_num_sta = wpa_s->max_stations;
 +      else
 +              bss->max_num_sta = wpa_s->conf->max_num_sta;
 +
 +      bss->disassoc_low_ack = wpa_s->conf->disassoc_low_ack;
 +
 +      if (wpa_s->conf->ap_vendor_elements) {
 +              bss->vendor_elements =
 +                      wpabuf_dup(wpa_s->conf->ap_vendor_elements);
 +      }
 +
 +      return 0;
 +}
 +
 +
 +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;
 +
 +      mgmt = (const struct ieee80211_mgmt *) buf;
 +      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,
 +                         buf + IEEE80211_HDRLEN + 1,
 +                         len - IEEE80211_HDRLEN - 1, freq);
 +#endif /* CONFIG_P2P */
 +}
 +
 +
 +static void ap_wps_event_cb(void *ctx, enum wps_event event,
 +                          union wps_event_data *data)
 +{
 +#ifdef CONFIG_P2P
 +      struct wpa_supplicant *wpa_s = ctx;
 +
 +      if (event == WPS_EV_FAIL) {
 +              struct wps_event_fail *fail = &data->fail;
 +
 +              if (wpa_s->parent && wpa_s->parent != wpa_s &&
 +                  wpa_s == wpa_s->global->p2p_group_formation) {
 +                      /*
 +                       * src/ap/wps_hostapd.c has already sent this on the
 +                       * main interface, so only send on the parent interface
 +                       * here if needed.
 +                       */
 +                      wpa_msg(wpa_s->parent, MSG_INFO, WPS_EVENT_FAIL
 +                              "msg=%d config_error=%d",
 +                              fail->msg, fail->config_error);
 +              }
 +              wpas_p2p_wps_failed(wpa_s, fail);
 +      }
 +#endif /* CONFIG_P2P */
 +}
 +
 +
 +static void ap_sta_authorized_cb(void *ctx, const u8 *mac_addr,
 +                               int authorized, const u8 *p2p_dev_addr)
 +{
 +      wpas_notify_sta_authorized(ctx, mac_addr, authorized, p2p_dev_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;
 +
 +      mgmt = (const struct ieee80211_mgmt *) buf;
 +      if (len < IEEE80211_HDRLEN + 1)
 +              return -1;
 +      wpas_p2p_rx_action(wpa_s, mgmt->da, mgmt->sa, mgmt->bssid,
 +                         mgmt->u.action.category,
 +                         buf + IEEE80211_HDRLEN + 1,
 +                         len - IEEE80211_HDRLEN - 1, freq);
 +#endif /* CONFIG_P2P */
 +      return 0;
 +}
 +
 +
 +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)
 +{
 +      struct wpa_supplicant *wpa_s = ctx;
++      unsigned int freq = 0;
++
++      if (wpa_s->ap_iface)
++              freq = wpa_s->ap_iface->freq;
++
 +      return wpas_p2p_probe_req_rx(wpa_s, sa, da, bssid, ie, ie_len,
-       hostapd_event_ch_switch(wpa_s->ap_iface->bss[0], freq, ht, offset, width, cf1, cf1);
++                                   freq, ssi_signal);
 +}
 +
 +
 +static void ap_wps_reg_success_cb(void *ctx, const u8 *mac_addr,
 +                                const u8 *uuid_e)
 +{
 +      struct wpa_supplicant *wpa_s = ctx;
 +      wpas_p2p_wps_success(wpa_s, mac_addr, 1);
 +}
 +
 +
 +static void wpas_ap_configured_cb(void *ctx)
 +{
 +      struct wpa_supplicant *wpa_s = ctx;
 +
 +      wpa_supplicant_set_state(wpa_s, WPA_COMPLETED);
 +
 +      if (wpa_s->ap_configured_cb)
 +              wpa_s->ap_configured_cb(wpa_s->ap_configured_cb_ctx,
 +                                      wpa_s->ap_configured_cb_data);
 +}
 +
 +
 +int wpa_supplicant_create_ap(struct wpa_supplicant *wpa_s,
 +                           struct wpa_ssid *ssid)
 +{
 +      struct wpa_driver_associate_params params;
 +      struct hostapd_iface *hapd_iface;
 +      struct hostapd_config *conf;
 +      size_t i;
 +
 +      if (ssid->ssid == NULL || ssid->ssid_len == 0) {
 +              wpa_printf(MSG_ERROR, "No SSID configured for AP mode");
 +              return -1;
 +      }
 +
 +      wpa_supplicant_ap_deinit(wpa_s);
 +
 +      wpa_printf(MSG_DEBUG, "Setting up AP (SSID='%s')",
 +                 wpa_ssid_txt(ssid->ssid, ssid->ssid_len));
 +
 +      os_memset(&params, 0, sizeof(params));
 +      params.ssid = ssid->ssid;
 +      params.ssid_len = ssid->ssid_len;
 +      switch (ssid->mode) {
 +      case WPAS_MODE_AP:
 +      case WPAS_MODE_P2P_GO:
 +      case WPAS_MODE_P2P_GROUP_FORMATION:
 +              params.mode = IEEE80211_MODE_AP;
 +              break;
 +      default:
 +              return -1;
 +      }
 +      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 = wpa_s->key_mgmt;
 +
 +      wpa_s->pairwise_cipher = wpa_pick_pairwise_cipher(ssid->pairwise_cipher,
 +                                                        1);
 +      if (wpa_s->pairwise_cipher < 0) {
 +              wpa_printf(MSG_WARNING, "WPA: Failed to select pairwise "
 +                         "cipher.");
 +              return -1;
 +      }
 +      params.pairwise_suite = wpa_s->pairwise_cipher;
 +      params.group_suite = params.pairwise_suite;
 +
 +#ifdef CONFIG_P2P
 +      if (ssid->mode == WPAS_MODE_P2P_GO ||
 +          ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION)
 +              params.p2p = 1;
 +#endif /* CONFIG_P2P */
 +
 +      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;
 +      }
 +
 +      wpa_s->ap_iface = hapd_iface = os_zalloc(sizeof(*wpa_s->ap_iface));
 +      if (hapd_iface == NULL)
 +              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;
 +      hapd_iface->extended_capa_len = wpa_s->extended_capa_len;
 +
 +      wpa_s->ap_iface->conf = conf = hostapd_config_defaults();
 +      if (conf == NULL) {
 +              wpa_supplicant_ap_deinit(wpa_s);
 +              return -1;
 +      }
 +
 +      os_memcpy(wpa_s->ap_iface->conf->wmm_ac_params,
 +                wpa_s->conf->wmm_ac_params,
 +                sizeof(wpa_s->conf->wmm_ac_params));
 +
 +      if (params.uapsd > 0) {
 +              conf->bss[0]->wmm_enabled = 1;
 +              conf->bss[0]->wmm_uapsd = 1;
 +      }
 +
 +      if (wpa_supplicant_conf_ap(wpa_s, ssid, conf)) {
 +              wpa_printf(MSG_ERROR, "Failed to create AP configuration");
 +              wpa_supplicant_ap_deinit(wpa_s);
 +              return -1;
 +      }
 +
 +#ifdef CONFIG_P2P
 +      if (ssid->mode == WPAS_MODE_P2P_GO)
 +              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 |
 +                      P2P_GROUP_FORMATION;
 +#endif /* CONFIG_P2P */
 +
 +      hapd_iface->num_bss = conf->num_bss;
 +      hapd_iface->bss = os_calloc(conf->num_bss,
 +                                  sizeof(struct hostapd_data *));
 +      if (hapd_iface->bss == NULL) {
 +              wpa_supplicant_ap_deinit(wpa_s);
 +              return -1;
 +      }
 +
 +      for (i = 0; i < conf->num_bss; i++) {
 +              hapd_iface->bss[i] =
 +                      hostapd_alloc_bss_data(hapd_iface, conf,
 +                                             conf->bss[i]);
 +              if (hapd_iface->bss[i] == NULL) {
 +                      wpa_supplicant_ap_deinit(wpa_s);
 +                      return -1;
 +              }
 +
 +              hapd_iface->bss[i]->msg_ctx = wpa_s;
 +              hapd_iface->bss[i]->msg_ctx_parent = wpa_s->parent;
 +              hapd_iface->bss[i]->public_action_cb = ap_public_action_rx;
 +              hapd_iface->bss[i]->public_action_cb_ctx = wpa_s;
 +              hapd_iface->bss[i]->vendor_action_cb = ap_vendor_action_rx;
 +              hapd_iface->bss[i]->vendor_action_cb_ctx = wpa_s;
 +              hostapd_register_probereq_cb(hapd_iface->bss[i],
 +                                           ap_probe_req_rx, wpa_s);
 +              hapd_iface->bss[i]->wps_reg_success_cb = ap_wps_reg_success_cb;
 +              hapd_iface->bss[i]->wps_reg_success_cb_ctx = wpa_s;
 +              hapd_iface->bss[i]->wps_event_cb = ap_wps_event_cb;
 +              hapd_iface->bss[i]->wps_event_cb_ctx = 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);
 +      hapd_iface->bss[0]->driver = wpa_s->driver;
 +      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;
 +
 +      if (hostapd_setup_interface(wpa_s->ap_iface)) {
 +              wpa_printf(MSG_ERROR, "Failed to initialize AP interface");
 +              wpa_supplicant_ap_deinit(wpa_s);
 +              return -1;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +void wpa_supplicant_ap_deinit(struct wpa_supplicant *wpa_s)
 +{
 +#ifdef CONFIG_WPS
 +      eloop_cancel_timeout(wpas_wps_ap_pin_timeout, wpa_s, NULL);
 +#endif /* CONFIG_WPS */
 +
 +      if (wpa_s->ap_iface == NULL)
 +              return;
 +
 +      wpa_s->current_ssid = NULL;
 +      eapol_sm_notify_config(wpa_s->eapol, NULL, NULL);
 +      wpa_s->assoc_freq = 0;
 +      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);
 +}
 +
 +
 +void ap_tx_status(void *ctx, const u8 *addr,
 +                const u8 *buf, size_t len, int ack)
 +{
 +#ifdef NEED_AP_MLME
 +      struct wpa_supplicant *wpa_s = ctx;
 +      hostapd_tx_status(wpa_s->ap_iface->bss[0], addr, buf, len, ack);
 +#endif /* NEED_AP_MLME */
 +}
 +
 +
 +void ap_eapol_tx_status(void *ctx, const u8 *dst,
 +                      const u8 *data, size_t len, int ack)
 +{
 +#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 */
 +}
 +
 +
 +void ap_client_poll_ok(void *ctx, const u8 *addr)
 +{
 +#ifdef NEED_AP_MLME
 +      struct wpa_supplicant *wpa_s = ctx;
 +      if (wpa_s->ap_iface)
 +              hostapd_client_poll_ok(wpa_s->ap_iface->bss[0], addr);
 +#endif /* NEED_AP_MLME */
 +}
 +
 +
 +void ap_rx_from_unknown_sta(void *ctx, const u8 *addr, int wds)
 +{
 +#ifdef NEED_AP_MLME
 +      struct wpa_supplicant *wpa_s = ctx;
 +      ieee802_11_rx_from_unknown(wpa_s->ap_iface->bss[0], addr, wds);
 +#endif /* NEED_AP_MLME */
 +}
 +
 +
 +void ap_mgmt_rx(void *ctx, struct rx_mgmt *rx_mgmt)
 +{
 +#ifdef NEED_AP_MLME
 +      struct wpa_supplicant *wpa_s = ctx;
 +      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->ap_iface->bss[0], rx_mgmt->frame,
 +                      rx_mgmt->frame_len, &fi);
 +#endif /* NEED_AP_MLME */
 +}
 +
 +
 +void ap_mgmt_tx_cb(void *ctx, const u8 *buf, size_t len, u16 stype, int ok)
 +{
 +#ifdef NEED_AP_MLME
 +      struct wpa_supplicant *wpa_s = ctx;
 +      ieee802_11_mgmt_cb(wpa_s->ap_iface->bss[0], buf, len, stype, ok);
 +#endif /* NEED_AP_MLME */
 +}
 +
 +
 +void wpa_supplicant_ap_rx_eapol(struct wpa_supplicant *wpa_s,
 +                              const u8 *src_addr, const u8 *buf, size_t len)
 +{
 +      ieee802_1x_receive(wpa_s->ap_iface->bss[0], src_addr, buf, len);
 +}
 +
 +
 +#ifdef CONFIG_WPS
 +
 +int wpa_supplicant_ap_wps_pbc(struct wpa_supplicant *wpa_s, const u8 *bssid,
 +                            const u8 *p2p_dev_addr)
 +{
 +      if (!wpa_s->ap_iface)
 +              return -1;
 +      return hostapd_wps_button_pushed(wpa_s->ap_iface->bss[0],
 +                                       p2p_dev_addr);
 +}
 +
 +
 +int wpa_supplicant_ap_wps_cancel(struct wpa_supplicant *wpa_s)
 +{
 +      struct wps_registrar *reg;
 +      int reg_sel = 0, wps_sta = 0;
 +
 +      if (!wpa_s->ap_iface || !wpa_s->ap_iface->bss[0]->wps)
 +              return -1;
 +
 +      reg = wpa_s->ap_iface->bss[0]->wps->registrar;
 +      reg_sel = wps_registrar_wps_cancel(reg);
 +      wps_sta = ap_for_each_sta(wpa_s->ap_iface->bss[0],
 +                                ap_sta_wps_cancel, NULL);
 +
 +      if (!reg_sel && !wps_sta) {
 +              wpa_printf(MSG_DEBUG, "No WPS operation in progress at this "
 +                         "time");
 +              return -1;
 +      }
 +
 +      /*
 +       * There are 2 cases to return wps cancel as success:
 +       * 1. When wps cancel was initiated but no connection has been
 +       *    established with client yet.
 +       * 2. Client is in the middle of exchanging WPS messages.
 +       */
 +
 +      return 0;
 +}
 +
 +
 +int wpa_supplicant_ap_wps_pin(struct wpa_supplicant *wpa_s, const u8 *bssid,
 +                            const char *pin, char *buf, size_t buflen,
 +                            int timeout)
 +{
 +      int ret, ret_len = 0;
 +
 +      if (!wpa_s->ap_iface)
 +              return -1;
 +
 +      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 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);
 +      if (ret)
 +              return -1;
 +      return ret_len;
 +}
 +
 +
 +static void wpas_wps_ap_pin_timeout(void *eloop_data, void *user_ctx)
 +{
 +      struct wpa_supplicant *wpa_s = eloop_data;
 +      wpa_printf(MSG_DEBUG, "WPS: AP PIN timed out");
 +      wpas_wps_ap_pin_disable(wpa_s);
 +}
 +
 +
 +static void wpas_wps_ap_pin_enable(struct wpa_supplicant *wpa_s, int timeout)
 +{
 +      struct hostapd_data *hapd;
 +
 +      if (wpa_s->ap_iface == NULL)
 +              return;
 +      hapd = wpa_s->ap_iface->bss[0];
 +      wpa_printf(MSG_DEBUG, "WPS: Enabling AP PIN (timeout=%d)", timeout);
 +      hapd->ap_pin_failures = 0;
 +      eloop_cancel_timeout(wpas_wps_ap_pin_timeout, wpa_s, NULL);
 +      if (timeout > 0)
 +              eloop_register_timeout(timeout, 0,
 +                                     wpas_wps_ap_pin_timeout, wpa_s, NULL);
 +}
 +
 +
 +void wpas_wps_ap_pin_disable(struct wpa_supplicant *wpa_s)
 +{
 +      struct hostapd_data *hapd;
 +
 +      if (wpa_s->ap_iface == NULL)
 +              return;
 +      wpa_printf(MSG_DEBUG, "WPS: Disabling AP PIN");
 +      hapd = wpa_s->ap_iface->bss[0];
 +      os_free(hapd->conf->ap_pin);
 +      hapd->conf->ap_pin = NULL;
 +      eloop_cancel_timeout(wpas_wps_ap_pin_timeout, wpa_s, NULL);
 +}
 +
 +
 +const char * wpas_wps_ap_pin_random(struct wpa_supplicant *wpa_s, int timeout)
 +{
 +      struct hostapd_data *hapd;
 +      unsigned int pin;
 +      char pin_txt[9];
 +
 +      if (wpa_s->ap_iface == NULL)
 +              return NULL;
 +      hapd = wpa_s->ap_iface->bss[0];
 +      pin = wps_generate_pin();
 +      os_snprintf(pin_txt, sizeof(pin_txt), "%08u", pin);
 +      os_free(hapd->conf->ap_pin);
 +      hapd->conf->ap_pin = os_strdup(pin_txt);
 +      if (hapd->conf->ap_pin == NULL)
 +              return NULL;
 +      wpas_wps_ap_pin_enable(wpa_s, timeout);
 +
 +      return hapd->conf->ap_pin;
 +}
 +
 +
 +const char * wpas_wps_ap_pin_get(struct wpa_supplicant *wpa_s)
 +{
 +      struct hostapd_data *hapd;
 +      if (wpa_s->ap_iface == NULL)
 +              return NULL;
 +      hapd = wpa_s->ap_iface->bss[0];
 +      return hapd->conf->ap_pin;
 +}
 +
 +
 +int wpas_wps_ap_pin_set(struct wpa_supplicant *wpa_s, const char *pin,
 +                      int timeout)
 +{
 +      struct hostapd_data *hapd;
 +      char pin_txt[9];
 +      int ret;
 +
 +      if (wpa_s->ap_iface == NULL)
 +              return -1;
 +      hapd = wpa_s->ap_iface->bss[0];
 +      ret = os_snprintf(pin_txt, sizeof(pin_txt), "%s", pin);
 +      if (os_snprintf_error(sizeof(pin_txt), ret))
 +              return -1;
 +      os_free(hapd->conf->ap_pin);
 +      hapd->conf->ap_pin = os_strdup(pin_txt);
 +      if (hapd->conf->ap_pin == NULL)
 +              return -1;
 +      wpas_wps_ap_pin_enable(wpa_s, timeout);
 +
 +      return 0;
 +}
 +
 +
 +void wpa_supplicant_ap_pwd_auth_fail(struct wpa_supplicant *wpa_s)
 +{
 +      struct hostapd_data *hapd;
 +
 +      if (wpa_s->ap_iface == NULL)
 +              return;
 +      hapd = wpa_s->ap_iface->bss[0];
 +
 +      /*
 +       * Registrar failed to prove its knowledge of the AP PIN. Disable AP
 +       * PIN if this happens multiple times to slow down brute force attacks.
 +       */
 +      hapd->ap_pin_failures++;
 +      wpa_printf(MSG_DEBUG, "WPS: AP PIN authentication failure number %u",
 +                 hapd->ap_pin_failures);
 +      if (hapd->ap_pin_failures < 3)
 +              return;
 +
 +      wpa_printf(MSG_DEBUG, "WPS: Disable AP PIN");
 +      hapd->ap_pin_failures = 0;
 +      os_free(hapd->conf->ap_pin);
 +      hapd->conf->ap_pin = NULL;
 +}
 +
 +
 +#ifdef CONFIG_WPS_NFC
 +
 +struct wpabuf * wpas_ap_wps_nfc_config_token(struct wpa_supplicant *wpa_s,
 +                                           int ndef)
 +{
 +      struct hostapd_data *hapd;
 +
 +      if (wpa_s->ap_iface == NULL)
 +              return NULL;
 +      hapd = wpa_s->ap_iface->bss[0];
 +      return hostapd_wps_nfc_config_token(hapd, ndef);
 +}
 +
 +
 +struct wpabuf * wpas_ap_wps_nfc_handover_sel(struct wpa_supplicant *wpa_s,
 +                                           int ndef)
 +{
 +      struct hostapd_data *hapd;
 +
 +      if (wpa_s->ap_iface == NULL)
 +              return NULL;
 +      hapd = wpa_s->ap_iface->bss[0];
 +      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 */
 +
 +
 +#ifdef CONFIG_CTRL_IFACE
 +
 +int ap_ctrl_iface_sta_first(struct wpa_supplicant *wpa_s,
 +                          char *buf, size_t buflen)
 +{
 +      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(hapd, buf, buflen);
 +}
 +
 +
 +int ap_ctrl_iface_sta(struct wpa_supplicant *wpa_s, const char *txtaddr,
 +                    char *buf, size_t buflen)
 +{
 +      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(hapd, txtaddr, buf, buflen);
 +}
 +
 +
 +int ap_ctrl_iface_sta_next(struct wpa_supplicant *wpa_s, const char *txtaddr,
 +                         char *buf, size_t buflen)
 +{
 +      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(hapd, txtaddr, buf, buflen);
 +}
 +
 +
 +int ap_ctrl_iface_sta_disassociate(struct wpa_supplicant *wpa_s,
 +                                 const char *txtaddr)
 +{
 +      if (wpa_s->ap_iface == NULL)
 +              return -1;
 +      return hostapd_ctrl_iface_disassociate(wpa_s->ap_iface->bss[0],
 +                                             txtaddr);
 +}
 +
 +
 +int ap_ctrl_iface_sta_deauthenticate(struct wpa_supplicant *wpa_s,
 +                                   const char *txtaddr)
 +{
 +      if (wpa_s->ap_iface == NULL)
 +              return -1;
 +      return hostapd_ctrl_iface_deauthenticate(wpa_s->ap_iface->bss[0],
 +                                               txtaddr);
 +}
 +
 +
 +int ap_ctrl_iface_wpa_get_status(struct wpa_supplicant *wpa_s, char *buf,
 +                               size_t buflen, int verbose)
 +{
 +      char *pos = buf, *end = buf + buflen;
 +      int ret;
 +      struct hostapd_bss_config *conf;
 +
 +      if (wpa_s->ap_iface == NULL)
 +              return -1;
 +
 +      conf = wpa_s->ap_iface->bss[0]->conf;
 +      if (conf->wpa == 0)
 +              return 0;
 +
 +      ret = os_snprintf(pos, end - pos,
 +                        "pairwise_cipher=%s\n"
 +                        "group_cipher=%s\n"
 +                        "key_mgmt=%s\n",
 +                        wpa_cipher_txt(conf->rsn_pairwise),
 +                        wpa_cipher_txt(conf->wpa_group),
 +                        wpa_key_mgmt_txt(conf->wpa_key_mgmt,
 +                                         conf->wpa));
 +      if (os_snprintf_error(end - pos, ret))
 +              return pos - buf;
 +      pos += ret;
 +      return pos - buf;
 +}
 +
 +#endif /* CONFIG_CTRL_IFACE */
 +
 +
 +int wpa_supplicant_ap_update_beacon(struct wpa_supplicant *wpa_s)
 +{
 +      struct hostapd_iface *iface = wpa_s->ap_iface;
 +      struct wpa_ssid *ssid = wpa_s->current_ssid;
 +      struct hostapd_data *hapd;
 +
 +      if (ssid == NULL || wpa_s->ap_iface == NULL ||
 +          ssid->mode == WPAS_MODE_INFRA ||
 +          ssid->mode == WPAS_MODE_IBSS)
 +              return -1;
 +
 +#ifdef CONFIG_P2P
 +      if (ssid->mode == WPAS_MODE_P2P_GO)
 +              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 |
 +                      P2P_GROUP_FORMATION;
 +#endif /* CONFIG_P2P */
 +
 +      hapd = iface->bss[0];
 +      if (hapd->drv_priv == NULL)
 +              return -1;
 +      ieee802_11_set_beacons(iface);
 +      hostapd_set_ap_wps_ie(hapd);
 +
 +      return 0;
 +}
 +
 +
 +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 */
 +}
 +
 +
++#ifdef CONFIG_CTRL_IFACE
 +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);
 +}
++#endif /* CONFIG_CTRL_IFACE */
 +
 +
 +void wpas_ap_ch_switch(struct wpa_supplicant *wpa_s, int freq, int ht,
 +                     int offset, int width, int cf1, int cf2)
 +{
 +      if (!wpa_s->ap_iface)
 +              return;
 +
 +      wpa_s->assoc_freq = freq;
++      if (wpa_s->current_ssid)
++              wpa_s->current_ssid->frequency = freq;
++      hostapd_event_ch_switch(wpa_s->ap_iface->bss[0], freq, ht,
++                              offset, width, cf1, cf2);
 +}
 +
 +
 +int wpa_supplicant_ap_mac_addr_filter(struct wpa_supplicant *wpa_s,
 +                                    const u8 *addr)
 +{
 +      struct hostapd_data *hapd;
 +      struct hostapd_bss_config *conf;
 +
 +      if (!wpa_s->ap_iface)
 +              return -1;
 +
 +      if (addr)
 +              wpa_printf(MSG_DEBUG, "AP: Set MAC address filter: " MACSTR,
 +                         MAC2STR(addr));
 +      else
 +              wpa_printf(MSG_DEBUG, "AP: Clear MAC address filter");
 +
 +      hapd = wpa_s->ap_iface->bss[0];
 +      conf = hapd->conf;
 +
 +      os_free(conf->accept_mac);
 +      conf->accept_mac = NULL;
 +      conf->num_accept_mac = 0;
 +      os_free(conf->deny_mac);
 +      conf->deny_mac = NULL;
 +      conf->num_deny_mac = 0;
 +
 +      if (addr == NULL) {
 +              conf->macaddr_acl = ACCEPT_UNLESS_DENIED;
 +              return 0;
 +      }
 +
 +      conf->macaddr_acl = DENY_UNLESS_ACCEPTED;
 +      conf->accept_mac = os_zalloc(sizeof(struct mac_acl_entry));
 +      if (conf->accept_mac == NULL)
 +              return -1;
 +      os_memcpy(conf->accept_mac[0].addr, addr, ETH_ALEN);
 +      conf->num_accept_mac = 1;
 +
 +      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 */
 +
 +
++#ifdef CONFIG_CTRL_IFACE
 +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);
 +}
++#endif /* CONFIG_CTRL_IFACE */
 +
 +
 +#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 */
++
++
++void ap_periodic(struct wpa_supplicant *wpa_s)
++{
++      if (wpa_s->ap_iface)
++              hostapd_periodic_iface(wpa_s->ap_iface);
++}
index 3f4151d8cb946f841b57d22e323370f52a18f05e,0000000000000000000000000000000000000000..594168cf1e03b4b147db2ce91fdd0e1afded3d36
mode 100644,000000..100644
--- /dev/null
@@@ -1,96 -1,0 +1,98 @@@
 +/*
 + * WPA Supplicant - Basic AP mode support routines
 + * Copyright (c) 2003-2009, Jouni Malinen <j@w1.fi>
 + * Copyright (c) 2009, Atheros Communications
 + *
 + * This software may be distributed under the terms of the BSD license.
 + * See README for more details.
 + */
 +
 +#ifndef AP_H
 +#define AP_H
 +
 +int wpa_supplicant_create_ap(struct wpa_supplicant *wpa_s,
 +                           struct wpa_ssid *ssid);
 +void wpa_supplicant_ap_deinit(struct wpa_supplicant *wpa_s);
 +void wpa_supplicant_ap_rx_eapol(struct wpa_supplicant *wpa_s,
 +                              const u8 *src_addr, const u8 *buf, size_t len);
 +int wpa_supplicant_ap_wps_pbc(struct wpa_supplicant *wpa_s, const u8 *bssid,
 +                            const u8 *p2p_dev_addr);
 +int wpa_supplicant_ap_wps_pin(struct wpa_supplicant *wpa_s, const u8 *bssid,
 +                            const char *pin, char *buf, size_t buflen,
 +                            int timeout);
 +int wpa_supplicant_ap_wps_cancel(struct wpa_supplicant *wpa_s);
 +void wpas_wps_ap_pin_disable(struct wpa_supplicant *wpa_s);
 +const char * wpas_wps_ap_pin_random(struct wpa_supplicant *wpa_s, int timeout);
 +const char * wpas_wps_ap_pin_get(struct wpa_supplicant *wpa_s);
 +int wpas_wps_ap_pin_set(struct wpa_supplicant *wpa_s, const char *pin,
 +                      int timeout);
 +int ap_ctrl_iface_sta_first(struct wpa_supplicant *wpa_s,
 +                          char *buf, size_t buflen);
 +int ap_ctrl_iface_sta(struct wpa_supplicant *wpa_s, const char *txtaddr,
 +                    char *buf, size_t buflen);
 +int ap_ctrl_iface_sta_next(struct wpa_supplicant *wpa_s, const char *txtaddr,
 +                         char *buf, size_t buflen);
 +int ap_ctrl_iface_sta_deauthenticate(struct wpa_supplicant *wpa_s,
 +                                   const char *txtaddr);
 +int ap_ctrl_iface_sta_disassociate(struct wpa_supplicant *wpa_s,
 +                                 const char *txtaddr);
 +int ap_ctrl_iface_wpa_get_status(struct wpa_supplicant *wpa_s, char *buf,
 +                               size_t buflen, int verbose);
 +void ap_tx_status(void *ctx, const u8 *addr,
 +                const u8 *buf, size_t len, int ack);
 +void ap_eapol_tx_status(void *ctx, const u8 *dst,
 +                      const u8 *data, size_t len, int ack);
 +void ap_client_poll_ok(void *ctx, const u8 *addr);
 +void ap_rx_from_unknown_sta(void *ctx, const u8 *addr, int wds);
 +void ap_mgmt_rx(void *ctx, struct rx_mgmt *rx_mgmt);
 +void ap_mgmt_tx_cb(void *ctx, const u8 *buf, size_t len, u16 stype, int ok);
 +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 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);
 +
++void ap_periodic(struct wpa_supplicant *wpa_s);
++
 +#endif /* AP_H */
index b4c47e21051d40957089054b2bc263369cd7f73e,0000000000000000000000000000000000000000..1051ee3a4c550482995c187887e4120184ea1ae2
mode 100644,000000..100644
--- /dev/null
@@@ -1,1242 -1,0 +1,1236 @@@
- /**
-  * WPA_BSS_EXPIRATION_PERIOD - Period of expiration run in seconds
-  */
- #define WPA_BSS_EXPIRATION_PERIOD 10
 +/*
 + * BSS table
 + * 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 "utils/includes.h"
 +
 +#include "utils/common.h"
 +#include "utils/eloop.h"
 +#include "common/ieee802_11_defs.h"
 +#include "drivers/driver.h"
 +#include "wpa_supplicant_i.h"
 +#include "config.h"
 +#include "notify.h"
 +#include "scan.h"
 +#include "bss.h"
 +
 +
-       return bss == wpa_s->current_bss ||
-               (!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));
 +#define WPA_BSS_FREQ_CHANGED_FLAG     BIT(0)
 +#define WPA_BSS_SIGNAL_CHANGED_FLAG   BIT(1)
 +#define WPA_BSS_PRIVACY_CHANGED_FLAG  BIT(2)
 +#define WPA_BSS_MODE_CHANGED_FLAG     BIT(3)
 +#define WPA_BSS_WPAIE_CHANGED_FLAG    BIT(4)
 +#define WPA_BSS_RSNIE_CHANGED_FLAG    BIT(5)
 +#define WPA_BSS_WPS_CHANGED_FLAG      BIT(6)
 +#define WPA_BSS_RATES_CHANGED_FLAG    BIT(7)
 +#define WPA_BSS_IES_CHANGED_FLAG      BIT(8)
 +
 +
 +static void wpa_bss_set_hessid(struct wpa_bss *bss)
 +{
 +#ifdef CONFIG_INTERWORKING
 +      const u8 *ie = wpa_bss_get_ie(bss, WLAN_EID_INTERWORKING);
 +      if (ie == NULL || (ie[1] != 7 && ie[1] != 9)) {
 +              os_memset(bss->hessid, 0, ETH_ALEN);
 +              return;
 +      }
 +      if (ie[1] == 7)
 +              os_memcpy(bss->hessid, ie + 3, ETH_ALEN);
 +      else
 +              os_memcpy(bss->hessid, ie + 5, ETH_ALEN);
 +#endif /* CONFIG_INTERWORKING */
 +}
 +
 +
 +/**
 + * wpa_bss_anqp_alloc - Allocate ANQP data structure for a BSS entry
 + * Returns: Allocated ANQP data structure or %NULL on failure
 + *
 + * The allocated ANQP data structure has its users count set to 1. It may be
 + * shared by multiple BSS entries and each shared entry is freed with
 + * wpa_bss_anqp_free().
 + */
 +struct wpa_bss_anqp * wpa_bss_anqp_alloc(void)
 +{
 +      struct wpa_bss_anqp *anqp;
 +      anqp = os_zalloc(sizeof(*anqp));
 +      if (anqp == NULL)
 +              return NULL;
 +      anqp->users = 1;
 +      return anqp;
 +}
 +
 +
 +/**
 + * wpa_bss_anqp_clone - Clone an ANQP data structure
 + * @anqp: ANQP data structure from wpa_bss_anqp_alloc()
 + * Returns: Cloned ANQP data structure or %NULL on failure
 + */
 +static struct wpa_bss_anqp * wpa_bss_anqp_clone(struct wpa_bss_anqp *anqp)
 +{
 +      struct wpa_bss_anqp *n;
 +
 +      n = os_zalloc(sizeof(*n));
 +      if (n == NULL)
 +              return NULL;
 +
 +#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);
 +      ANQP_DUP(ip_addr_type_availability);
 +      ANQP_DUP(nai_realm);
 +      ANQP_DUP(anqp_3gpp);
 +      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
 +
 +      return n;
 +}
 +
 +
 +/**
 + * wpa_bss_anqp_unshare_alloc - Unshare ANQP data (if shared) in a BSS entry
 + * @bss: BSS entry
 + * Returns: 0 on success, -1 on failure
 + *
 + * This function ensures the specific BSS entry has an ANQP data structure that
 + * is not shared with any other BSS entry.
 + */
 +int wpa_bss_anqp_unshare_alloc(struct wpa_bss *bss)
 +{
 +      struct wpa_bss_anqp *anqp;
 +
 +      if (bss->anqp && bss->anqp->users > 1) {
 +              /* allocated, but shared - clone an unshared copy */
 +              anqp = wpa_bss_anqp_clone(bss->anqp);
 +              if (anqp == NULL)
 +                      return -1;
 +              anqp->users = 1;
 +              bss->anqp->users--;
 +              bss->anqp = anqp;
 +              return 0;
 +      }
 +
 +      if (bss->anqp)
 +              return 0; /* already allocated and not shared */
 +
 +      /* not allocated - allocate a new storage area */
 +      bss->anqp = wpa_bss_anqp_alloc();
 +      return bss->anqp ? 0 : -1;
 +}
 +
 +
 +/**
 + * wpa_bss_anqp_free - Free an ANQP data structure
 + * @anqp: ANQP data structure from wpa_bss_anqp_alloc() or wpa_bss_anqp_clone()
 + */
 +static void wpa_bss_anqp_free(struct wpa_bss_anqp *anqp)
 +{
 +      if (anqp == NULL)
 +              return;
 +
 +      anqp->users--;
 +      if (anqp->users > 0) {
 +              /* Another BSS entry holds a pointer to this ANQP info */
 +              return;
 +      }
 +
 +#ifdef CONFIG_INTERWORKING
 +      wpabuf_free(anqp->capability_list);
 +      wpabuf_free(anqp->venue_name);
 +      wpabuf_free(anqp->network_auth_type);
 +      wpabuf_free(anqp->roaming_consortium);
 +      wpabuf_free(anqp->ip_addr_type_availability);
 +      wpabuf_free(anqp->nai_realm);
 +      wpabuf_free(anqp->anqp_3gpp);
 +      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)
 +{
 +      if (wpa_s->last_scan_res) {
 +              unsigned int i;
 +              for (i = 0; i < wpa_s->last_scan_res_used; i++) {
 +                      if (wpa_s->last_scan_res[i] == bss) {
 +                              os_memmove(&wpa_s->last_scan_res[i],
 +                                         &wpa_s->last_scan_res[i + 1],
 +                                         (wpa_s->last_scan_res_used - i - 1)
 +                                         * sizeof(struct wpa_bss *));
 +                              wpa_s->last_scan_res_used--;
 +                              break;
 +                      }
 +              }
 +      }
 +      wpa_bss_update_pending_connect(wpa_s, bss, NULL);
 +      dl_list_del(&bss->list);
 +      dl_list_del(&bss->list_id);
 +      wpa_s->num_bss--;
 +      wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Remove id %u BSSID " MACSTR
 +              " SSID '%s' due to %s", bss->id, MAC2STR(bss->bssid),
 +              wpa_ssid_txt(bss->ssid, bss->ssid_len), reason);
 +      wpas_notify_bss_removed(wpa_s, bss->bssid, bss->id);
 +      wpa_bss_anqp_free(bss->anqp);
 +      os_free(bss);
 +}
 +
 +
 +/**
 + * wpa_bss_get - Fetch a BSS table entry based on BSSID and SSID
 + * @wpa_s: Pointer to wpa_supplicant data
 + * @bssid: BSSID
 + * @ssid: SSID
 + * @ssid_len: Length of @ssid
 + * Returns: Pointer to the BSS entry or %NULL if not found
 + */
 +struct wpa_bss * wpa_bss_get(struct wpa_supplicant *wpa_s, const u8 *bssid,
 +                           const u8 *ssid, size_t ssid_len)
 +{
 +      struct wpa_bss *bss;
 +      if (!wpa_supplicant_filter_bssid_match(wpa_s, bssid))
 +              return NULL;
 +      dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
 +              if (os_memcmp(bss->bssid, bssid, ETH_ALEN) == 0 &&
 +                  bss->ssid_len == ssid_len &&
 +                  os_memcmp(bss->ssid, ssid, ssid_len) == 0)
 +                      return bss;
 +      }
 +      return NULL;
 +}
 +
 +
 +static void calculate_update_time(const struct os_reltime *fetch_time,
 +                                unsigned int age_ms,
 +                                struct os_reltime *update_time)
 +{
 +      os_time_t usec;
 +
 +      update_time->sec = fetch_time->sec;
 +      update_time->usec = fetch_time->usec;
 +      update_time->sec -= age_ms / 1000;
 +      usec = (age_ms % 1000) * 1000;
 +      if (update_time->usec < usec) {
 +              update_time->sec--;
 +              update_time->usec += 1000000;
 +      }
 +      update_time->usec -= usec;
 +}
 +
 +
 +static void wpa_bss_copy_res(struct wpa_bss *dst, struct wpa_scan_res *src,
 +                           struct os_reltime *fetch_time)
 +{
 +      dst->flags = src->flags;
 +      os_memcpy(dst->bssid, src->bssid, ETH_ALEN);
 +      dst->freq = src->freq;
 +      dst->beacon_int = src->beacon_int;
 +      dst->caps = src->caps;
 +      dst->qual = src->qual;
 +      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);
 +}
 +
 +
 +static int wpa_bss_known(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
 +{
 +      struct wpa_ssid *ssid;
 +
 +      for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
 +              if (ssid->ssid == NULL || ssid->ssid_len == 0)
 +                      continue;
 +              if (ssid->ssid_len == bss->ssid_len &&
 +                  os_memcmp(ssid->ssid, bss->ssid, ssid->ssid_len) == 0)
 +                      return 1;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +static int wpa_bss_in_use(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
 +{
-               " SSID '%s'",
-               bss->id, MAC2STR(bss->bssid), wpa_ssid_txt(ssid, ssid_len));
++      if (bss == wpa_s->current_bss)
++              return 1;
++
++      if (wpa_s->current_bss &&
++          (bss->ssid_len != wpa_s->current_bss->ssid_len ||
++           os_memcmp(bss->ssid, wpa_s->current_bss->ssid,
++                     bss->ssid_len) != 0))
++              return 0; /* SSID has changed */
++
++      return !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);
 +}
 +
 +
 +static int wpa_bss_remove_oldest_unknown(struct wpa_supplicant *wpa_s)
 +{
 +      struct wpa_bss *bss;
 +
 +      dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
 +              if (!wpa_bss_known(wpa_s, bss)) {
 +                      wpa_bss_remove(wpa_s, bss, __func__);
 +                      return 0;
 +              }
 +      }
 +
 +      return -1;
 +}
 +
 +
 +static int wpa_bss_remove_oldest(struct wpa_supplicant *wpa_s)
 +{
 +      struct wpa_bss *bss;
 +
 +      /*
 +       * Remove the oldest entry that does not match with any configured
 +       * network.
 +       */
 +      if (wpa_bss_remove_oldest_unknown(wpa_s) == 0)
 +              return 0;
 +
 +      /*
 +       * Remove the oldest entry that isn't currently in use.
 +       */
 +      dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
 +              if (!wpa_bss_in_use(wpa_s, bss)) {
 +                      wpa_bss_remove(wpa_s, bss, __func__);
 +                      return 0;
 +              }
 +      }
 +
 +      return -1;
 +}
 +
 +
 +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_reltime *fetch_time)
 +{
 +      struct wpa_bss *bss;
 +
 +      bss = os_zalloc(sizeof(*bss) + res->ie_len + res->beacon_ie_len);
 +      if (bss == NULL)
 +              return NULL;
 +      bss->id = wpa_s->bss_next_id++;
 +      bss->last_update_idx = wpa_s->bss_update_idx;
 +      wpa_bss_copy_res(bss, res, fetch_time);
 +      os_memcpy(bss->ssid, ssid, ssid_len);
 +      bss->ssid_len = ssid_len;
 +      bss->ie_len = res->ie_len;
 +      bss->beacon_ie_len = res->beacon_ie_len;
 +      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;
 +      }
 +
 +      dl_list_add_tail(&wpa_s->bss, &bss->list);
 +      dl_list_add_tail(&wpa_s->bss_id, &bss->list_id);
 +      wpa_s->num_bss++;
 +      wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Add new id %u BSSID " MACSTR
-                        const struct wpa_scan_res *new, u32 ie)
++              " SSID '%s' freq %d",
++              bss->id, MAC2STR(bss->bssid), wpa_ssid_txt(ssid, ssid_len),
++              bss->freq);
 +      wpas_notify_bss_added(wpa_s, bss->bssid, bss->id);
 +      return bss;
 +}
 +
 +
 +static int are_ies_equal(const struct wpa_bss *old,
-               new_ie = wpa_scan_get_vendor_ie(new, ie);
++                       const struct wpa_scan_res *new_res, u32 ie)
 +{
 +      const u8 *old_ie, *new_ie;
 +      struct wpabuf *old_ie_buff = NULL;
 +      struct wpabuf *new_ie_buff = NULL;
 +      int new_ie_len, old_ie_len, ret, is_multi;
 +
 +      switch (ie) {
 +      case WPA_IE_VENDOR_TYPE:
 +              old_ie = wpa_bss_get_vendor_ie(old, ie);
-               new_ie_buff = wpa_scan_get_vendor_ie_multi(new, ie);
++              new_ie = wpa_scan_get_vendor_ie(new_res, ie);
 +              is_multi = 0;
 +              break;
 +      case WPS_IE_VENDOR_TYPE:
 +              old_ie_buff = wpa_bss_get_vendor_ie_multi(old, ie);
-               new_ie = wpa_scan_get_ie(new, ie);
++              new_ie_buff = wpa_scan_get_vendor_ie_multi(new_res, ie);
 +              is_multi = 1;
 +              break;
 +      case WLAN_EID_RSN:
 +      case WLAN_EID_SUPP_RATES:
 +      case WLAN_EID_EXT_SUPP_RATES:
 +              old_ie = wpa_bss_get_ie(old, ie);
-                              const struct wpa_scan_res *new)
++              new_ie = wpa_scan_get_ie(new_res, ie);
 +              is_multi = 0;
 +              break;
 +      default:
 +              wpa_printf(MSG_DEBUG, "bss: %s: cannot compare IEs", __func__);
 +              return 0;
 +      }
 +
 +      if (is_multi) {
 +              /* in case of multiple IEs stored in buffer */
 +              old_ie = old_ie_buff ? wpabuf_head_u8(old_ie_buff) : NULL;
 +              new_ie = new_ie_buff ? wpabuf_head_u8(new_ie_buff) : NULL;
 +              old_ie_len = old_ie_buff ? wpabuf_len(old_ie_buff) : 0;
 +              new_ie_len = new_ie_buff ? wpabuf_len(new_ie_buff) : 0;
 +      } else {
 +              /* in case of single IE */
 +              old_ie_len = old_ie ? old_ie[1] + 2 : 0;
 +              new_ie_len = new_ie ? new_ie[1] + 2 : 0;
 +      }
 +
 +      if (!old_ie || !new_ie)
 +              ret = !old_ie && !new_ie;
 +      else
 +              ret = (old_ie_len == new_ie_len &&
 +                     os_memcmp(old_ie, new_ie, old_ie_len) == 0);
 +
 +      wpabuf_free(old_ie_buff);
 +      wpabuf_free(new_ie_buff);
 +
 +      return ret;
 +}
 +
 +
 +static u32 wpa_bss_compare_res(const struct wpa_bss *old,
-       int caps_diff = old->caps ^ new->caps;
++                             const struct wpa_scan_res *new_res)
 +{
 +      u32 changes = 0;
-       if (old->freq != new->freq)
++      int caps_diff = old->caps ^ new_res->caps;
 +
-       if (old->level != new->level)
++      if (old->freq != new_res->freq)
 +              changes |= WPA_BSS_FREQ_CHANGED_FLAG;
 +
-       if (old->ie_len == new->ie_len &&
-           os_memcmp(old + 1, new + 1, old->ie_len) == 0)
++      if (old->level != new_res->level)
 +              changes |= WPA_BSS_SIGNAL_CHANGED_FLAG;
 +
 +      if (caps_diff & IEEE80211_CAP_PRIVACY)
 +              changes |= WPA_BSS_PRIVACY_CHANGED_FLAG;
 +
 +      if (caps_diff & IEEE80211_CAP_IBSS)
 +              changes |= WPA_BSS_MODE_CHANGED_FLAG;
 +
-       if (!are_ies_equal(old, new, WPA_IE_VENDOR_TYPE))
++      if (old->ie_len == new_res->ie_len &&
++          os_memcmp(old + 1, new_res + 1, old->ie_len) == 0)
 +              return changes;
 +      changes |= WPA_BSS_IES_CHANGED_FLAG;
 +
-       if (!are_ies_equal(old, new, WLAN_EID_RSN))
++      if (!are_ies_equal(old, new_res, WPA_IE_VENDOR_TYPE))
 +              changes |= WPA_BSS_WPAIE_CHANGED_FLAG;
 +
-       if (!are_ies_equal(old, new, WPS_IE_VENDOR_TYPE))
++      if (!are_ies_equal(old, new_res, WLAN_EID_RSN))
 +              changes |= WPA_BSS_RSNIE_CHANGED_FLAG;
 +
-       if (!are_ies_equal(old, new, WLAN_EID_SUPP_RATES) ||
-           !are_ies_equal(old, new, WLAN_EID_EXT_SUPP_RATES))
++      if (!are_ies_equal(old, new_res, WPS_IE_VENDOR_TYPE))
 +              changes |= WPA_BSS_WPS_CHANGED_FLAG;
 +
-       if (ssid[1] > 32) {
++      if (!are_ies_equal(old, new_res, WLAN_EID_SUPP_RATES) ||
++          !are_ies_equal(old, new_res, WLAN_EID_EXT_SUPP_RATES))
 +              changes |= WPA_BSS_RATES_CHANGED_FLAG;
 +
 +      return changes;
 +}
 +
 +
 +static void notify_bss_changes(struct wpa_supplicant *wpa_s, u32 changes,
 +                             const struct wpa_bss *bss)
 +{
 +      if (changes & WPA_BSS_FREQ_CHANGED_FLAG)
 +              wpas_notify_bss_freq_changed(wpa_s, bss->id);
 +
 +      if (changes & WPA_BSS_SIGNAL_CHANGED_FLAG)
 +              wpas_notify_bss_signal_changed(wpa_s, bss->id);
 +
 +      if (changes & WPA_BSS_PRIVACY_CHANGED_FLAG)
 +              wpas_notify_bss_privacy_changed(wpa_s, bss->id);
 +
 +      if (changes & WPA_BSS_MODE_CHANGED_FLAG)
 +              wpas_notify_bss_mode_changed(wpa_s, bss->id);
 +
 +      if (changes & WPA_BSS_WPAIE_CHANGED_FLAG)
 +              wpas_notify_bss_wpaie_changed(wpa_s, bss->id);
 +
 +      if (changes & WPA_BSS_RSNIE_CHANGED_FLAG)
 +              wpas_notify_bss_rsnie_changed(wpa_s, bss->id);
 +
 +      if (changes & WPA_BSS_WPS_CHANGED_FLAG)
 +              wpas_notify_bss_wps_changed(wpa_s, bss->id);
 +
 +      if (changes & WPA_BSS_IES_CHANGED_FLAG)
 +              wpas_notify_bss_ies_changed(wpa_s, bss->id);
 +
 +      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_reltime *fetch_time)
 +{
 +      u32 changes;
 +
 +      changes = wpa_bss_compare_res(bss, res);
++      if (changes & WPA_BSS_FREQ_CHANGED_FLAG)
++              wpa_printf(MSG_DEBUG, "BSS: " MACSTR " changed freq %d --> %d",
++                         MAC2STR(bss->bssid), bss->freq, res->freq);
 +      bss->scan_miss_count = 0;
 +      bss->last_update_idx = wpa_s->bss_update_idx;
 +      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);
 +              bss->ie_len = res->ie_len;
 +              bss->beacon_ie_len = res->beacon_ie_len;
 +      } else {
 +              struct wpa_bss *nbss;
 +              struct dl_list *prev = bss->list_id.prev;
 +              dl_list_del(&bss->list_id);
 +              nbss = os_realloc(bss, sizeof(*bss) + res->ie_len +
 +                                res->beacon_ie_len);
 +              if (nbss) {
 +                      unsigned int i;
 +                      for (i = 0; i < wpa_s->last_scan_res_used; i++) {
 +                              if (wpa_s->last_scan_res[i] == bss) {
 +                                      wpa_s->last_scan_res[i] = nbss;
 +                                      break;
 +                              }
 +                      }
 +                      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);
 +                      bss->ie_len = res->ie_len;
 +                      bss->beacon_ie_len = res->beacon_ie_len;
 +              }
 +              dl_list_add(prev, &bss->list_id);
 +      }
 +      if (changes & WPA_BSS_IES_CHANGED_FLAG)
 +              wpa_bss_set_hessid(bss);
 +      dl_list_add_tail(&wpa_s->bss, &bss->list);
 +
 +      notify_bss_changes(wpa_s, changes, bss);
 +
 +      return bss;
 +}
 +
 +
 +/**
 + * wpa_bss_update_start - Start a BSS table update from scan results
 + * @wpa_s: Pointer to wpa_supplicant data
 + *
 + * This function is called at the start of each BSS table update round for new
 + * scan results. The actual scan result entries are indicated with calls to
 + * wpa_bss_update_scan_res() and the update round is finished with a call to
 + * wpa_bss_update_end().
 + */
 +void wpa_bss_update_start(struct wpa_supplicant *wpa_s)
 +{
 +      wpa_s->bss_update_idx++;
 +      wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Start scan result update %u",
 +              wpa_s->bss_update_idx);
 +      wpa_s->last_scan_res_used = 0;
 +}
 +
 +
 +/**
 + * wpa_bss_update_scan_res - Update a BSS table entry based on a scan result
 + * @wpa_s: Pointer to wpa_supplicant data
 + * @res: Scan result
 + * @fetch_time: Time when the result was fetched from the driver
 + *
 + * This function updates a BSS table entry (or adds one) based on a scan result.
 + * This is called separately for each scan result between the calls to
 + * wpa_bss_update_start() and wpa_bss_update_end().
 + */
 +void wpa_bss_update_scan_res(struct wpa_supplicant *wpa_s,
 +                           struct wpa_scan_res *res,
 +                           struct os_reltime *fetch_time)
 +{
 +      const u8 *ssid, *p2p, *mesh;
 +      struct wpa_bss *bss;
 +
 +      if (wpa_s->conf->ignore_old_scan_res) {
 +              struct os_reltime update;
 +              calculate_update_time(fetch_time, res->age, &update);
 +              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",
 +                              (unsigned int) age.sec,
 +                              (unsigned int) age.usec);
 +                      return;
 +              }
 +      }
 +
 +      ssid = wpa_scan_get_ie(res, WLAN_EID_SSID);
 +      if (ssid == NULL) {
 +              wpa_dbg(wpa_s, MSG_DEBUG, "BSS: No SSID IE included for "
 +                      MACSTR, MAC2STR(res->bssid));
 +              return;
 +      }
-       if (mesh && mesh[1] <= 32)
++      if (ssid[1] > SSID_MAX_LEN) {
 +              wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Too long SSID IE included for "
 +                      MACSTR, MAC2STR(res->bssid));
 +              return;
 +      }
 +
 +      p2p = wpa_scan_get_vendor_ie(res, P2P_IE_VENDOR_TYPE);
 +#ifdef CONFIG_P2P
 +      if (p2p == NULL &&
 +          wpa_s->p2p_group_interface != NOT_P2P_GROUP_INTERFACE) {
 +              /*
 +               * If it's a P2P specific interface, then don't update
 +               * the scan result without a P2P IE.
 +               */
 +              wpa_printf(MSG_DEBUG, "BSS: No P2P IE - skipping BSS " MACSTR
 +                         " update for P2P interface", MAC2STR(res->bssid));
 +              return;
 +      }
 +#endif /* CONFIG_P2P */
 +      if (p2p && ssid[1] == P2P_WILDCARD_SSID_LEN &&
 +          os_memcmp(ssid + 2, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN) == 0)
 +              return; /* Skip P2P listen discovery results here */
 +
 +      /* TODO: add option for ignoring BSSes we are not interested in
 +       * (to save memory) */
 +
 +      mesh = wpa_scan_get_ie(res, WLAN_EID_MESH_ID);
- static void wpa_bss_timeout(void *eloop_ctx, void *timeout_ctx)
- {
-       struct wpa_supplicant *wpa_s = eloop_ctx;
-       wpa_bss_flush_by_age(wpa_s, wpa_s->conf->bss_expiration_age);
-       eloop_register_timeout(WPA_BSS_EXPIRATION_PERIOD, 0,
-                              wpa_bss_timeout, wpa_s, NULL);
- }
++      if (mesh && mesh[1] <= SSID_MAX_LEN)
 +              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 {
 +              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;
 +      if (wpa_s->last_scan_res_used >= wpa_s->last_scan_res_size) {
 +              struct wpa_bss **n;
 +              unsigned int siz;
 +              if (wpa_s->last_scan_res_size == 0)
 +                      siz = 32;
 +              else
 +                      siz = wpa_s->last_scan_res_size * 2;
 +              n = os_realloc_array(wpa_s->last_scan_res, siz,
 +                                   sizeof(struct wpa_bss *));
 +              if (n == NULL)
 +                      return;
 +              wpa_s->last_scan_res = n;
 +              wpa_s->last_scan_res_size = siz;
 +      }
 +
 +      if (wpa_s->last_scan_res)
 +              wpa_s->last_scan_res[wpa_s->last_scan_res_used++] = bss;
 +}
 +
 +
 +static int wpa_bss_included_in_scan(const struct wpa_bss *bss,
 +                                  const struct scan_info *info)
 +{
 +      int found;
 +      size_t i;
 +
 +      if (info == NULL)
 +              return 1;
 +
 +      if (info->num_freqs) {
 +              found = 0;
 +              for (i = 0; i < info->num_freqs; i++) {
 +                      if (bss->freq == info->freqs[i]) {
 +                              found = 1;
 +                              break;
 +                      }
 +              }
 +              if (!found)
 +                      return 0;
 +      }
 +
 +      if (info->num_ssids) {
 +              found = 0;
 +              for (i = 0; i < info->num_ssids; i++) {
 +                      const struct wpa_driver_scan_ssid *s = &info->ssids[i];
 +                      if ((s->ssid == NULL || s->ssid_len == 0) ||
 +                          (s->ssid_len == bss->ssid_len &&
 +                           os_memcmp(s->ssid, bss->ssid, bss->ssid_len) ==
 +                           0)) {
 +                              found = 1;
 +                              break;
 +                      }
 +              }
 +              if (!found)
 +                      return 0;
 +      }
 +
 +      return 1;
 +}
 +
 +
 +/**
 + * wpa_bss_update_end - End a BSS table update from scan results
 + * @wpa_s: Pointer to wpa_supplicant data
 + * @info: Information about scan parameters
 + * @new_scan: Whether this update round was based on a new scan
 + *
 + * This function is called at the end of each BSS table update round for new
 + * scan results. The start of the update was indicated with a call to
 + * wpa_bss_update_start().
 + */
 +void wpa_bss_update_end(struct wpa_supplicant *wpa_s, struct scan_info *info,
 +                      int new_scan)
 +{
 +      struct wpa_bss *bss, *n;
 +
 +      os_get_reltime(&wpa_s->last_scan);
 +      if (!new_scan)
 +              return; /* do not expire entries without new scan */
 +
 +      dl_list_for_each_safe(bss, n, &wpa_s->bss, struct wpa_bss, list) {
 +              if (wpa_bss_in_use(wpa_s, bss))
 +                      continue;
 +              if (!wpa_bss_included_in_scan(bss, info))
 +                      continue; /* expire only BSSes that were scanned */
 +              if (bss->last_update_idx < wpa_s->bss_update_idx)
 +                      bss->scan_miss_count++;
 +              if (bss->scan_miss_count >=
 +                  wpa_s->conf->bss_expiration_scan_count) {
 +                      wpa_bss_remove(wpa_s, bss, "no match in scan");
 +              }
 +      }
 +
 +      wpa_printf(MSG_DEBUG, "BSS: last_scan_res_used=%u/%u",
 +                 wpa_s->last_scan_res_used, wpa_s->last_scan_res_size);
 +}
 +
 +
 +/**
 + * wpa_bss_flush_by_age - Flush old BSS entries
 + * @wpa_s: Pointer to wpa_supplicant data
 + * @age: Maximum entry age in seconds
 + *
 + * Remove BSS entries that have not been updated during the last @age seconds.
 + */
 +void wpa_bss_flush_by_age(struct wpa_supplicant *wpa_s, int age)
 +{
 +      struct wpa_bss *bss, *n;
 +      struct os_reltime t;
 +
 +      if (dl_list_empty(&wpa_s->bss))
 +              return;
 +
 +      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_reltime_before(&bss->last_update, &t)) {
 +                      wpa_bss_remove(wpa_s, bss, __func__);
 +              } else
 +                      break;
 +      }
 +}
 +
 +
-       eloop_register_timeout(WPA_BSS_EXPIRATION_PERIOD, 0,
-                              wpa_bss_timeout, wpa_s, NULL);
 +/**
 + * wpa_bss_init - Initialize BSS table
 + * @wpa_s: Pointer to wpa_supplicant data
 + * Returns: 0 on success, -1 on failure
 + *
 + * This prepares BSS table lists and timer for periodic updates. The BSS table
 + * is deinitialized with wpa_bss_deinit() once not needed anymore.
 + */
 +int wpa_bss_init(struct wpa_supplicant *wpa_s)
 +{
 +      dl_list_init(&wpa_s->bss);
 +      dl_list_init(&wpa_s->bss_id);
-       eloop_cancel_timeout(wpa_bss_timeout, wpa_s, NULL);
 +      return 0;
 +}
 +
 +
 +/**
 + * wpa_bss_flush - Flush all unused BSS entries
 + * @wpa_s: Pointer to wpa_supplicant data
 + */
 +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 */
 +
 +      dl_list_for_each_safe(bss, n, &wpa_s->bss, struct wpa_bss, list) {
 +              if (wpa_bss_in_use(wpa_s, bss))
 +                      continue;
 +              wpa_bss_remove(wpa_s, bss, __func__);
 +      }
 +}
 +
 +
 +/**
 + * wpa_bss_deinit - Deinitialize BSS table
 + * @wpa_s: Pointer to wpa_supplicant data
 + */
 +void wpa_bss_deinit(struct wpa_supplicant *wpa_s)
 +{
 +      wpa_bss_flush(wpa_s);
 +}
 +
 +
 +/**
 + * wpa_bss_get_bssid - Fetch a BSS table entry based on BSSID
 + * @wpa_s: Pointer to wpa_supplicant data
 + * @bssid: BSSID
 + * Returns: Pointer to the BSS entry or %NULL if not found
 + */
 +struct wpa_bss * wpa_bss_get_bssid(struct wpa_supplicant *wpa_s,
 +                                 const u8 *bssid)
 +{
 +      struct wpa_bss *bss;
 +      if (!wpa_supplicant_filter_bssid_match(wpa_s, bssid))
 +              return NULL;
 +      dl_list_for_each_reverse(bss, &wpa_s->bss, struct wpa_bss, list) {
 +              if (os_memcmp(bss->bssid, bssid, ETH_ALEN) == 0)
 +                      return bss;
 +      }
 +      return NULL;
 +}
 +
 +
 +/**
 + * wpa_bss_get_bssid_latest - Fetch the latest BSS table entry based on BSSID
 + * @wpa_s: Pointer to wpa_supplicant data
 + * @bssid: BSSID
 + * Returns: Pointer to the BSS entry or %NULL if not found
 + *
 + * This function is like wpa_bss_get_bssid(), but full BSS table is iterated to
 + * find the entry that has the most recent update. This can help in finding the
 + * correct entry in cases where the SSID of the AP may have changed recently
 + * (e.g., in WPS reconfiguration cases).
 + */
 +struct wpa_bss * wpa_bss_get_bssid_latest(struct wpa_supplicant *wpa_s,
 +                                        const u8 *bssid)
 +{
 +      struct wpa_bss *bss, *found = NULL;
 +      if (!wpa_supplicant_filter_bssid_match(wpa_s, bssid))
 +              return NULL;
 +      dl_list_for_each_reverse(bss, &wpa_s->bss, struct wpa_bss, list) {
 +              if (os_memcmp(bss->bssid, bssid, ETH_ALEN) != 0)
 +                      continue;
 +              if (found == NULL ||
 +                  os_reltime_before(&found->last_update, &bss->last_update))
 +                      found = bss;
 +      }
 +      return found;
 +}
 +
 +
 +#ifdef CONFIG_P2P
 +/**
 + * wpa_bss_get_p2p_dev_addr - Fetch a BSS table entry based on P2P Device Addr
 + * @wpa_s: Pointer to wpa_supplicant data
 + * @dev_addr: P2P Device Address of the GO
 + * Returns: Pointer to the BSS entry or %NULL if not found
 + */
 +struct wpa_bss * wpa_bss_get_p2p_dev_addr(struct wpa_supplicant *wpa_s,
 +                                        const u8 *dev_addr)
 +{
 +      struct wpa_bss *bss;
 +      dl_list_for_each_reverse(bss, &wpa_s->bss, struct wpa_bss, list) {
 +              u8 addr[ETH_ALEN];
 +              if (p2p_parse_dev_addr((const u8 *) (bss + 1), bss->ie_len,
 +                                     addr) == 0 &&
 +                  os_memcmp(addr, dev_addr, ETH_ALEN) == 0)
 +                      return bss;
 +      }
 +      return NULL;
 +}
 +#endif /* CONFIG_P2P */
 +
 +
 +/**
 + * wpa_bss_get_id - Fetch a BSS table entry based on identifier
 + * @wpa_s: Pointer to wpa_supplicant data
 + * @id: Unique identifier (struct wpa_bss::id) assigned for the entry
 + * Returns: Pointer to the BSS entry or %NULL if not found
 + */
 +struct wpa_bss * wpa_bss_get_id(struct wpa_supplicant *wpa_s, unsigned int id)
 +{
 +      struct wpa_bss *bss;
 +      dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
 +              if (bss->id == id)
 +                      return bss;
 +      }
 +      return NULL;
 +}
 +
 +
 +/**
 + * wpa_bss_get_id_range - Fetch a BSS table entry based on identifier range
 + * @wpa_s: Pointer to wpa_supplicant data
 + * @idf: Smallest allowed identifier assigned for the entry
 + * @idf: Largest allowed identifier assigned for the entry
 + * Returns: Pointer to the BSS entry or %NULL if not found
 + *
 + * This function is similar to wpa_bss_get_id() but allows a BSS entry with the
 + * smallest id value to be fetched within the specified range without the
 + * caller having to know the exact id.
 + */
 +struct wpa_bss * wpa_bss_get_id_range(struct wpa_supplicant *wpa_s,
 +                                    unsigned int idf, unsigned int idl)
 +{
 +      struct wpa_bss *bss;
 +      dl_list_for_each(bss, &wpa_s->bss_id, struct wpa_bss, list_id) {
 +              if (bss->id >= idf && bss->id <= idl)
 +                      return bss;
 +      }
 +      return NULL;
 +}
 +
 +
 +/**
 + * wpa_bss_get_ie - Fetch a specified information element from a BSS entry
 + * @bss: BSS table entry
 + * @ie: Information element identitifier (WLAN_EID_*)
 + * 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.
 + */
 +const u8 * wpa_bss_get_ie(const struct wpa_bss *bss, u8 ie)
 +{
 +      const u8 *end, *pos;
 +
 +      pos = (const u8 *) (bss + 1);
 +      end = pos + bss->ie_len;
 +
 +      while (pos + 1 < end) {
 +              if (pos + 2 + pos[1] > end)
 +                      break;
 +              if (pos[0] == ie)
 +                      return pos;
 +              pos += 2 + pos[1];
 +      }
 +
 +      return NULL;
 +}
 +
 +
 +/**
 + * wpa_bss_get_vendor_ie - Fetch a vendor information element 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.
 + */
 +const u8 * wpa_bss_get_vendor_ie(const struct wpa_bss *bss, u32 vendor_type)
 +{
 +      const u8 *end, *pos;
 +
 +      pos = (const u8 *) (bss + 1);
 +      end = pos + bss->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_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)
 + * Returns: Pointer to the information element payload or %NULL if not found
 + *
 + * This function returns concatenated payload of possibly fragmented vendor
 + * specific information elements in the BSS entry. The caller is responsible for
 + * freeing the returned buffer.
 + */
 +struct wpabuf * wpa_bss_get_vendor_ie_multi(const struct wpa_bss *bss,
 +                                          u32 vendor_type)
 +{
 +      struct wpabuf *buf;
 +      const u8 *end, *pos;
 +
 +      buf = wpabuf_alloc(bss->ie_len);
 +      if (buf == NULL)
 +              return NULL;
 +
 +      pos = (const u8 *) (bss + 1);
 +      end = pos + bss->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]))
 +                      wpabuf_put_data(buf, pos + 2 + 4, pos[1] - 4);
 +              pos += 2 + pos[1];
 +      }
 +
 +      if (wpabuf_len(buf) == 0) {
 +              wpabuf_free(buf);
 +              buf = NULL;
 +      }
 +
 +      return buf;
 +}
 +
 +
 +/**
 + * wpa_bss_get_vendor_ie_multi_beacon - Fetch vendor IE data from a BSS entry
 + * @bss: BSS table entry
 + * @vendor_type: Vendor type (four octets starting the IE payload)
 + * Returns: Pointer to the information element payload or %NULL if not found
 + *
 + * This function returns concatenated payload of possibly fragmented vendor
 + * specific information elements in the BSS entry. The caller is responsible for
 + * freeing the returned buffer.
 + *
 + * This function is like wpa_bss_get_vendor_ie_multi(), but uses IE buffer only
 + * from Beacon frames instead of either Beacon or Probe Response frames.
 + */
 +struct wpabuf * wpa_bss_get_vendor_ie_multi_beacon(const struct wpa_bss *bss,
 +                                                 u32 vendor_type)
 +{
 +      struct wpabuf *buf;
 +      const u8 *end, *pos;
 +
 +      buf = wpabuf_alloc(bss->beacon_ie_len);
 +      if (buf == NULL)
 +              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]))
 +                      wpabuf_put_data(buf, pos + 2 + 4, pos[1] - 4);
 +              pos += 2 + pos[1];
 +      }
 +
 +      if (wpabuf_len(buf) == 0) {
 +              wpabuf_free(buf);
 +              buf = NULL;
 +      }
 +
 +      return buf;
 +}
 +
 +
 +/**
 + * wpa_bss_get_max_rate - Get maximum legacy TX rate supported in a BSS
 + * @bss: BSS table entry
 + * Returns: Maximum legacy rate in units of 500 kbps
 + */
 +int wpa_bss_get_max_rate(const struct wpa_bss *bss)
 +{
 +      int rate = 0;
 +      const u8 *ie;
 +      int i;
 +
 +      ie = wpa_bss_get_ie(bss, WLAN_EID_SUPP_RATES);
 +      for (i = 0; ie && i < ie[1]; i++) {
 +              if ((ie[i + 2] & 0x7f) > rate)
 +                      rate = ie[i + 2] & 0x7f;
 +      }
 +
 +      ie = wpa_bss_get_ie(bss, WLAN_EID_EXT_SUPP_RATES);
 +      for (i = 0; ie && i < ie[1]; i++) {
 +              if ((ie[i + 2] & 0x7f) > rate)
 +                      rate = ie[i + 2] & 0x7f;
 +      }
 +
 +      return rate;
 +}
 +
 +
 +/**
 + * wpa_bss_get_bit_rates - Get legacy TX rates supported in a BSS
 + * @bss: BSS table entry
 + * @rates: Buffer for returning a pointer to the rates list (units of 500 kbps)
 + * Returns: number of legacy TX rates or -1 on failure
 + *
 + * The caller is responsible for freeing the returned buffer with os_free() in
 + * case of success.
 + */
 +int wpa_bss_get_bit_rates(const struct wpa_bss *bss, u8 **rates)
 +{
 +      const u8 *ie, *ie2;
 +      int i, j;
 +      unsigned int len;
 +      u8 *r;
 +
 +      ie = wpa_bss_get_ie(bss, WLAN_EID_SUPP_RATES);
 +      ie2 = wpa_bss_get_ie(bss, WLAN_EID_EXT_SUPP_RATES);
 +
 +      len = (ie ? ie[1] : 0) + (ie2 ? ie2[1] : 0);
 +
 +      r = os_malloc(len);
 +      if (!r)
 +              return -1;
 +
 +      for (i = 0; ie && i < ie[1]; i++)
 +              r[i] = ie[i + 2] & 0x7f;
 +
 +      for (j = 0; ie2 && j < ie2[1]; j++)
 +              r[i + j] = ie2[j + 2] & 0x7f;
 +
 +      *rates = r;
 +      return len;
 +}
index 634aa3cc06d09ff2458bedd8162b0ac6b63e3397,0000000000000000000000000000000000000000..b215380eeb15251582c07d5d57994b90786b30f6
mode 100644,000000..100644
--- /dev/null
@@@ -1,150 -1,0 +1,150 @@@
-       u8 ssid[32];
 +/*
 + * BSS table
 + * 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.
 + */
 +
 +#ifndef BSS_H
 +#define BSS_H
 +
 +struct wpa_scan_res;
 +
 +#define WPA_BSS_QUAL_INVALID          BIT(0)
 +#define WPA_BSS_NOISE_INVALID         BIT(1)
 +#define WPA_BSS_LEVEL_INVALID         BIT(2)
 +#define WPA_BSS_LEVEL_DBM             BIT(3)
 +#define WPA_BSS_AUTHENTICATED         BIT(4)
 +#define WPA_BSS_ASSOCIATED            BIT(5)
 +#define WPA_BSS_ANQP_FETCH_TRIED      BIT(6)
 +
 +/**
 + * struct wpa_bss_anqp - ANQP data for a BSS entry (struct wpa_bss)
 + */
 +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;
 +      struct wpabuf *ip_addr_type_availability;
 +      struct wpabuf *nai_realm;
 +      struct wpabuf *anqp_3gpp;
 +      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 */
 +};
 +
 +/**
 + * struct wpa_bss - BSS table
 + *
 + * This structure is used to store information about neighboring BSSes in
 + * generic format. It is mainly updated based on scan results from the driver.
 + */
 +struct wpa_bss {
 +      /** List entry for struct wpa_supplicant::bss */
 +      struct dl_list list;
 +      /** List entry for struct wpa_supplicant::bss_id */
 +      struct dl_list list_id;
 +      /** Unique identifier for this BSS entry */
 +      unsigned int id;
 +      /** Number of counts without seeing this BSS */
 +      unsigned int scan_miss_count;
 +      /** Index of the last scan update */
 +      unsigned int last_update_idx;
 +      /** Information flags about the BSS/IBSS (WPA_BSS_*) */
 +      unsigned int flags;
 +      /** BSSID */
 +      u8 bssid[ETH_ALEN];
 +      /** HESSID */
 +      u8 hessid[ETH_ALEN];
 +      /** SSID */
++      u8 ssid[SSID_MAX_LEN];
 +      /** Length of SSID */
 +      size_t ssid_len;
 +      /** Frequency of the channel in MHz (e.g., 2412 = channel 1) */
 +      int freq;
 +      /** Beacon interval in TUs (host byte order) */
 +      u16 beacon_int;
 +      /** Capability information field in host byte order */
 +      u16 caps;
 +      /** Signal quality */
 +      int qual;
 +      /** Noise level */
 +      int noise;
 +      /** Signal level */
 +      int level;
 +      /** Timestamp of last Beacon/Probe Response frame */
 +      u64 tsf;
 +      /** Time of the last update (i.e., Beacon or Probe Response RX) */
 +      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) */
 +      size_t ie_len;
 +      /** Length of the following Beacon IE field in octets */
 +      size_t beacon_ie_len;
 +      /* followed by ie_len octets of IEs */
 +      /* followed by beacon_ie_len octets of IEs */
 +};
 +
 +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_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);
 +void wpa_bss_deinit(struct wpa_supplicant *wpa_s);
 +void wpa_bss_flush(struct wpa_supplicant *wpa_s);
 +void wpa_bss_flush_by_age(struct wpa_supplicant *wpa_s, int age);
 +struct wpa_bss * wpa_bss_get(struct wpa_supplicant *wpa_s, const u8 *bssid,
 +                           const u8 *ssid, size_t ssid_len);
 +struct wpa_bss * wpa_bss_get_bssid(struct wpa_supplicant *wpa_s,
 +                                 const u8 *bssid);
 +struct wpa_bss * wpa_bss_get_bssid_latest(struct wpa_supplicant *wpa_s,
 +                                        const u8 *bssid);
 +struct wpa_bss * wpa_bss_get_p2p_dev_addr(struct wpa_supplicant *wpa_s,
 +                                        const u8 *dev_addr);
 +struct wpa_bss * wpa_bss_get_id(struct wpa_supplicant *wpa_s, unsigned int id);
 +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,
 +                                                 u32 vendor_type);
 +int wpa_bss_get_max_rate(const struct wpa_bss *bss);
 +int wpa_bss_get_bit_rates(const struct wpa_bss *bss, u8 **rates);
 +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 8e6cd2006b0eada173ef6c28cc2b6c9b17eaf594,0000000000000000000000000000000000000000..b1adab77bbe0c5511ca1fa31842f45a33751b283
mode 100644,000000..100644
--- /dev/null
@@@ -1,4300 -1,0 +1,4357 @@@
-       { STR_RANGE(ssid, 0, MAX_SSID_LEN) },
 +/*
 + * WPA Supplicant / Configuration parser and common functions
 + * 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 "includes.h"
 +
 +#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"
 +#include "p2p/p2p.h"
++#include "fst/fst.h"
 +#include "config.h"
 +
 +
 +#if !defined(CONFIG_CTRL_IFACE) && defined(CONFIG_NO_CONFIG_WRITE)
 +#define NO_CONFIG_WRITE
 +#endif
 +
 +/*
 + * Structure for network configuration parsing. This data is used to implement
 + * a generic parser for each network block variable. The table of configuration
 + * variables is defined below in this file (ssid_fields[]).
 + */
 +struct parse_data {
 +      /* Configuration variable name */
 +      char *name;
 +
 +      /* Parser function for this variable */
 +      int (*parser)(const struct parse_data *data, struct wpa_ssid *ssid,
 +                    int line, const char *value);
 +
 +#ifndef NO_CONFIG_WRITE
 +      /* Writer function (i.e., to get the variable in text format from
 +       * internal presentation). */
 +      char * (*writer)(const struct parse_data *data, struct wpa_ssid *ssid);
 +#endif /* NO_CONFIG_WRITE */
 +
 +      /* Variable specific parameters for the parser. */
 +      void *param1, *param2, *param3, *param4;
 +
 +      /* 0 = this variable can be included in debug output and ctrl_iface
 +       * 1 = this variable contains key/private data and it must not be
 +       *     included in debug output unless explicitly requested. In
 +       *     addition, this variable will not be readable through the
 +       *     ctrl_iface.
 +       */
 +      int key_data;
 +};
 +
 +
 +static int wpa_config_parse_str(const struct parse_data *data,
 +                              struct wpa_ssid *ssid,
 +                              int line, const char *value)
 +{
 +      size_t res_len, *dst_len;
 +      char **dst, *tmp;
 +
 +      if (os_strcmp(value, "NULL") == 0) {
 +              wpa_printf(MSG_DEBUG, "Unset configuration string '%s'",
 +                         data->name);
 +              tmp = NULL;
 +              res_len = 0;
 +              goto set;
 +      }
 +
 +      tmp = wpa_config_parse_string(value, &res_len);
 +      if (tmp == NULL) {
 +              wpa_printf(MSG_ERROR, "Line %d: failed to parse %s '%s'.",
 +                         line, data->name,
 +                         data->key_data ? "[KEY DATA REMOVED]" : value);
 +              return -1;
 +      }
 +
 +      if (data->key_data) {
 +              wpa_hexdump_ascii_key(MSG_MSGDUMP, data->name,
 +                                    (u8 *) tmp, res_len);
 +      } else {
 +              wpa_hexdump_ascii(MSG_MSGDUMP, data->name,
 +                                (u8 *) tmp, res_len);
 +      }
 +
 +      if (data->param3 && res_len < (size_t) data->param3) {
 +              wpa_printf(MSG_ERROR, "Line %d: too short %s (len=%lu "
 +                         "min_len=%ld)", line, data->name,
 +                         (unsigned long) res_len, (long) data->param3);
 +              os_free(tmp);
 +              return -1;
 +      }
 +
 +      if (data->param4 && res_len > (size_t) data->param4) {
 +              wpa_printf(MSG_ERROR, "Line %d: too long %s (len=%lu "
 +                         "max_len=%ld)", line, data->name,
 +                         (unsigned long) res_len, (long) data->param4);
 +              os_free(tmp);
 +              return -1;
 +      }
 +
 +set:
 +      dst = (char **) (((u8 *) ssid) + (long) data->param1);
 +      dst_len = (size_t *) (((u8 *) ssid) + (long) data->param2);
 +      os_free(*dst);
 +      *dst = tmp;
 +      if (data->param2)
 +              *dst_len = res_len;
 +
 +      return 0;
 +}
 +
 +
 +#ifndef NO_CONFIG_WRITE
 +static char * wpa_config_write_string_ascii(const u8 *value, size_t len)
 +{
 +      char *buf;
 +
 +      buf = os_malloc(len + 3);
 +      if (buf == NULL)
 +              return NULL;
 +      buf[0] = '"';
 +      os_memcpy(buf + 1, value, len);
 +      buf[len + 1] = '"';
 +      buf[len + 2] = '\0';
 +
 +      return buf;
 +}
 +
 +
 +static char * wpa_config_write_string_hex(const u8 *value, size_t len)
 +{
 +      char *buf;
 +
 +      buf = os_zalloc(2 * len + 1);
 +      if (buf == NULL)
 +              return NULL;
 +      wpa_snprintf_hex(buf, 2 * len + 1, value, len);
 +
 +      return buf;
 +}
 +
 +
 +static char * wpa_config_write_string(const u8 *value, size_t len)
 +{
 +      if (value == NULL)
 +              return NULL;
 +
 +      if (is_hex(value, len))
 +              return wpa_config_write_string_hex(value, len);
 +      else
 +              return wpa_config_write_string_ascii(value, len);
 +}
 +
 +
 +static char * wpa_config_write_str(const struct parse_data *data,
 +                                 struct wpa_ssid *ssid)
 +{
 +      size_t len;
 +      char **src;
 +
 +      src = (char **) (((u8 *) ssid) + (long) data->param1);
 +      if (*src == NULL)
 +              return NULL;
 +
 +      if (data->param2)
 +              len = *((size_t *) (((u8 *) ssid) + (long) data->param2));
 +      else
 +              len = os_strlen(*src);
 +
 +      return wpa_config_write_string((const u8 *) *src, len);
 +}
 +#endif /* NO_CONFIG_WRITE */
 +
 +
 +static int wpa_config_parse_int(const struct parse_data *data,
 +                              struct wpa_ssid *ssid,
 +                              int line, const char *value)
 +{
 +      int val, *dst;
 +      char *end;
 +
 +      dst = (int *) (((u8 *) ssid) + (long) data->param1);
 +      val = strtol(value, &end, 0);
 +      if (*end) {
 +              wpa_printf(MSG_ERROR, "Line %d: invalid number \"%s\"",
 +                         line, value);
 +              return -1;
 +      }
 +      *dst = val;
 +      wpa_printf(MSG_MSGDUMP, "%s=%d (0x%x)", data->name, *dst, *dst);
 +
 +      if (data->param3 && *dst < (long) data->param3) {
 +              wpa_printf(MSG_ERROR, "Line %d: too small %s (value=%d "
 +                         "min_value=%ld)", line, data->name, *dst,
 +                         (long) data->param3);
 +              *dst = (long) data->param3;
 +              return -1;
 +      }
 +
 +      if (data->param4 && *dst > (long) data->param4) {
 +              wpa_printf(MSG_ERROR, "Line %d: too large %s (value=%d "
 +                         "max_value=%ld)", line, data->name, *dst,
 +                         (long) data->param4);
 +              *dst = (long) data->param4;
 +              return -1;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +#ifndef NO_CONFIG_WRITE
 +static char * wpa_config_write_int(const struct parse_data *data,
 +                                 struct wpa_ssid *ssid)
 +{
 +      int *src, res;
 +      char *value;
 +
 +      src = (int *) (((u8 *) ssid) + (long) data->param1);
 +
 +      value = os_malloc(20);
 +      if (value == NULL)
 +              return NULL;
 +      res = os_snprintf(value, 20, "%d", *src);
 +      if (os_snprintf_error(20, res)) {
 +              os_free(value);
 +              return NULL;
 +      }
 +      value[20 - 1] = '\0';
 +      return value;
 +}
 +#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)
 +{
 +      if (value[0] == '\0' || os_strcmp(value, "\"\"") == 0 ||
 +          os_strcmp(value, "any") == 0) {
 +              ssid->bssid_set = 0;
 +              wpa_printf(MSG_MSGDUMP, "BSSID any");
 +              return 0;
 +      }
 +      if (hwaddr_aton(value, ssid->bssid)) {
 +              wpa_printf(MSG_ERROR, "Line %d: Invalid BSSID '%s'.",
 +                         line, value);
 +              return -1;
 +      }
 +      ssid->bssid_set = 1;
 +      wpa_hexdump(MSG_MSGDUMP, "BSSID", ssid->bssid, ETH_ALEN);
 +      return 0;
 +}
 +
 +
 +#ifndef NO_CONFIG_WRITE
 +static char * wpa_config_write_bssid(const struct parse_data *data,
 +                                   struct wpa_ssid *ssid)
 +{
 +      char *value;
 +      int res;
 +
 +      if (!ssid->bssid_set)
 +              return NULL;
 +
 +      value = os_malloc(20);
 +      if (value == NULL)
 +              return NULL;
 +      res = os_snprintf(value, 20, MACSTR, MAC2STR(ssid->bssid));
 +      if (os_snprintf_error(20, res)) {
 +              os_free(value);
 +              return NULL;
 +      }
 +      value[20 - 1] = '\0';
 +      return value;
 +}
 +#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) {
 +              str_clear_free(ssid->passphrase);
 +              ssid->passphrase = NULL;
 +              ssid->psk_set = 0;
 +              os_free(ssid->ext_psk);
 +              ssid->ext_psk = os_strdup(value + 4);
 +              if (ssid->ext_psk == NULL)
 +                      return -1;
 +              wpa_printf(MSG_DEBUG, "PSK: External password '%s'",
 +                         ssid->ext_psk);
 +              return 0;
 +      }
 +#endif /* CONFIG_EXT_PASSWORD */
 +
 +      if (*value == '"') {
 +#ifndef CONFIG_NO_PBKDF2
 +              const char *pos;
 +              size_t len;
 +
 +              value++;
 +              pos = os_strrchr(value, '"');
 +              if (pos)
 +                      len = pos - value;
 +              else
 +                      len = os_strlen(value);
 +              if (len < 8 || len > 63) {
 +                      wpa_printf(MSG_ERROR, "Line %d: Invalid passphrase "
 +                                 "length %lu (expected: 8..63) '%s'.",
 +                                 line, (unsigned long) len, value);
 +                      return -1;
 +              }
 +              wpa_hexdump_ascii_key(MSG_MSGDUMP, "PSK (ASCII passphrase)",
 +                                    (u8 *) value, len);
 +              if (ssid->passphrase && os_strlen(ssid->passphrase) == len &&
 +                  os_memcmp(ssid->passphrase, value, len) == 0)
 +                      return 0;
 +              ssid->psk_set = 0;
 +              str_clear_free(ssid->passphrase);
 +              ssid->passphrase = dup_binstr(value, len);
 +              if (ssid->passphrase == NULL)
 +                      return -1;
 +              return 0;
 +#else /* CONFIG_NO_PBKDF2 */
 +              wpa_printf(MSG_ERROR, "Line %d: ASCII passphrase not "
 +                         "supported.", line);
 +              return -1;
 +#endif /* CONFIG_NO_PBKDF2 */
 +      }
 +
 +      if (hexstr2bin(value, ssid->psk, PMK_LEN) ||
 +          value[PMK_LEN * 2] != '\0') {
 +              wpa_printf(MSG_ERROR, "Line %d: Invalid PSK '%s'.",
 +                         line, value);
 +              return -1;
 +      }
 +
 +      str_clear_free(ssid->passphrase);
 +      ssid->passphrase = NULL;
 +
 +      ssid->psk_set = 1;
 +      wpa_hexdump_key(MSG_MSGDUMP, "PSK", ssid->psk, PMK_LEN);
 +      return 0;
 +}
 +
 +
 +#ifndef NO_CONFIG_WRITE
 +static char * wpa_config_write_psk(const struct parse_data *data,
 +                                 struct wpa_ssid *ssid)
 +{
 +#ifdef CONFIG_EXT_PASSWORD
 +      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;
 +              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 */
 +
 +      if (ssid->passphrase)
 +              return wpa_config_write_string_ascii(
 +                      (const u8 *) ssid->passphrase,
 +                      os_strlen(ssid->passphrase));
 +
 +      if (ssid->psk_set)
 +              return wpa_config_write_string_hex(ssid->psk, PMK_LEN);
 +
 +      return NULL;
 +}
 +#endif /* NO_CONFIG_WRITE */
 +
 +
 +static int wpa_config_parse_proto(const struct parse_data *data,
 +                                struct wpa_ssid *ssid, int line,
 +                                const char *value)
 +{
 +      int val = 0, last, errors = 0;
 +      char *start, *end, *buf;
 +
 +      buf = os_strdup(value);
 +      if (buf == NULL)
 +              return -1;
 +      start = buf;
 +
 +      while (*start != '\0') {
 +              while (*start == ' ' || *start == '\t')
 +                      start++;
 +              if (*start == '\0')
 +                      break;
 +              end = start;
 +              while (*end != ' ' && *end != '\t' && *end != '\0')
 +                      end++;
 +              last = *end == '\0';
 +              *end = '\0';
 +              if (os_strcmp(start, "WPA") == 0)
 +                      val |= WPA_PROTO_WPA;
 +              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);
 +                      errors++;
 +              }
 +
 +              if (last)
 +                      break;
 +              start = end + 1;
 +      }
 +      os_free(buf);
 +
 +      if (val == 0) {
 +              wpa_printf(MSG_ERROR,
 +                         "Line %d: no proto values configured.", line);
 +              errors++;
 +      }
 +
 +      wpa_printf(MSG_MSGDUMP, "proto: 0x%x", val);
 +      ssid->proto = val;
 +      return errors ? -1 : 0;
 +}
 +
 +
 +#ifndef NO_CONFIG_WRITE
 +static char * wpa_config_write_proto(const struct parse_data *data,
 +                                   struct wpa_ssid *ssid)
 +{
 +      int ret;
 +      char *buf, *pos, *end;
 +
 +      pos = buf = os_zalloc(20);
 +      if (buf == NULL)
 +              return NULL;
 +      end = buf + 20;
 +
 +      if (ssid->proto & WPA_PROTO_WPA) {
 +              ret = os_snprintf(pos, end - pos, "%sWPA",
 +                                pos == buf ? "" : " ");
 +              if (os_snprintf_error(end - pos, ret))
 +                      return buf;
 +              pos += ret;
 +      }
 +
 +      if (ssid->proto & WPA_PROTO_RSN) {
 +              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;
 +      }
 +
 +      if (pos == buf) {
 +              os_free(buf);
 +              buf = NULL;
 +      }
 +
 +      return buf;
 +}
 +#endif /* NO_CONFIG_WRITE */
 +
 +
 +static int wpa_config_parse_key_mgmt(const struct parse_data *data,
 +                                   struct wpa_ssid *ssid, int line,
 +                                   const char *value)
 +{
 +      int val = 0, last, errors = 0;
 +      char *start, *end, *buf;
 +
 +      buf = os_strdup(value);
 +      if (buf == NULL)
 +              return -1;
 +      start = buf;
 +
 +      while (*start != '\0') {
 +              while (*start == ' ' || *start == '\t')
 +                      start++;
 +              if (*start == '\0')
 +                      break;
 +              end = start;
 +              while (*end != ' ' && *end != '\t' && *end != '\0')
 +                      end++;
 +              last = *end == '\0';
 +              *end = '\0';
 +              if (os_strcmp(start, "WPA-PSK") == 0)
 +                      val |= WPA_KEY_MGMT_PSK;
 +              else if (os_strcmp(start, "WPA-EAP") == 0)
 +                      val |= WPA_KEY_MGMT_IEEE8021X;
 +              else if (os_strcmp(start, "IEEE8021X") == 0)
 +                      val |= WPA_KEY_MGMT_IEEE8021X_NO_WPA;
 +              else if (os_strcmp(start, "NONE") == 0)
 +                      val |= WPA_KEY_MGMT_NONE;
 +              else if (os_strcmp(start, "WPA-NONE") == 0)
 +                      val |= WPA_KEY_MGMT_WPA_NONE;
 +#ifdef CONFIG_IEEE80211R
 +              else if (os_strcmp(start, "FT-PSK") == 0)
 +                      val |= WPA_KEY_MGMT_FT_PSK;
 +              else if (os_strcmp(start, "FT-EAP") == 0)
 +                      val |= WPA_KEY_MGMT_FT_IEEE8021X;
 +#endif /* CONFIG_IEEE80211R */
 +#ifdef CONFIG_IEEE80211W
 +              else if (os_strcmp(start, "WPA-PSK-SHA256") == 0)
 +                      val |= WPA_KEY_MGMT_PSK_SHA256;
 +              else if (os_strcmp(start, "WPA-EAP-SHA256") == 0)
 +                      val |= WPA_KEY_MGMT_IEEE8021X_SHA256;
 +#endif /* CONFIG_IEEE80211W */
 +#ifdef CONFIG_WPS
 +              else if (os_strcmp(start, "WPS") == 0)
 +                      val |= WPA_KEY_MGMT_WPS;
 +#endif /* CONFIG_WPS */
 +#ifdef CONFIG_SAE
 +              else if (os_strcmp(start, "SAE") == 0)
 +                      val |= WPA_KEY_MGMT_SAE;
 +              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);
 +                      errors++;
 +              }
 +
 +              if (last)
 +                      break;
 +              start = end + 1;
 +      }
 +      os_free(buf);
 +
 +      if (val == 0) {
 +              wpa_printf(MSG_ERROR,
 +                         "Line %d: no key_mgmt values configured.", line);
 +              errors++;
 +      }
 +
 +      wpa_printf(MSG_MSGDUMP, "key_mgmt: 0x%x", val);
 +      ssid->key_mgmt = val;
 +      return errors ? -1 : 0;
 +}
 +
 +
 +#ifndef NO_CONFIG_WRITE
 +static char * wpa_config_write_key_mgmt(const struct parse_data *data,
 +                                      struct wpa_ssid *ssid)
 +{
 +      char *buf, *pos, *end;
 +      int ret;
 +
 +      pos = buf = os_zalloc(100);
 +      if (buf == NULL)
 +              return NULL;
 +      end = buf + 100;
 +
 +      if (ssid->key_mgmt & WPA_KEY_MGMT_PSK) {
 +              ret = os_snprintf(pos, end - pos, "%sWPA-PSK",
 +                                pos == buf ? "" : " ");
 +              if (os_snprintf_error(end - pos, ret)) {
 +                      end[-1] = '\0';
 +                      return buf;
 +              }
 +              pos += ret;
 +      }
 +
 +      if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X) {
 +              ret = os_snprintf(pos, end - pos, "%sWPA-EAP",
 +                                pos == buf ? "" : " ");
 +              if (os_snprintf_error(end - pos, ret)) {
 +                      end[-1] = '\0';
 +                      return buf;
 +              }
 +              pos += ret;
 +      }
 +
 +      if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) {
 +              ret = os_snprintf(pos, end - pos, "%sIEEE8021X",
 +                                pos == buf ? "" : " ");
 +              if (os_snprintf_error(end - pos, ret)) {
 +                      end[-1] = '\0';
 +                      return buf;
 +              }
 +              pos += ret;
 +      }
 +
 +      if (ssid->key_mgmt & WPA_KEY_MGMT_NONE) {
 +              ret = os_snprintf(pos, end - pos, "%sNONE",
 +                                pos == buf ? "" : " ");
 +              if (os_snprintf_error(end - pos, ret)) {
 +                      end[-1] = '\0';
 +                      return buf;
 +              }
 +              pos += ret;
 +      }
 +
 +      if (ssid->key_mgmt & WPA_KEY_MGMT_WPA_NONE) {
 +              ret = os_snprintf(pos, end - pos, "%sWPA-NONE",
 +                                pos == buf ? "" : " ");
 +              if (os_snprintf_error(end - pos, ret)) {
 +                      end[-1] = '\0';
 +                      return buf;
 +              }
 +              pos += ret;
 +      }
 +
 +#ifdef CONFIG_IEEE80211R
 +      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) {
 +              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) {
 +              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) {
 +              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) {
 +              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 */
 +
 +
 +static int wpa_config_parse_cipher(int line, const char *value)
 +{
 +      int val = wpa_parse_cipher(value);
 +      if (val < 0) {
 +              wpa_printf(MSG_ERROR, "Line %d: invalid cipher '%s'.",
 +                         line, value);
 +              return -1;
 +      }
 +      if (val == 0) {
 +              wpa_printf(MSG_ERROR, "Line %d: no cipher values configured.",
 +                         line);
 +              return -1;
 +      }
 +      return val;
 +}
 +
 +
 +#ifndef NO_CONFIG_WRITE
 +static char * wpa_config_write_cipher(int cipher)
 +{
 +      char *buf = os_zalloc(50);
 +      if (buf == NULL)
 +              return NULL;
 +
 +      if (wpa_write_ciphers(buf, buf + 50, cipher, " ") < 0) {
 +              os_free(buf);
 +              return NULL;
 +      }
 +
 +      return buf;
 +}
 +#endif /* NO_CONFIG_WRITE */
 +
 +
 +static int wpa_config_parse_pairwise(const struct parse_data *data,
 +                                   struct wpa_ssid *ssid, int line,
 +                                   const char *value)
 +{
 +      int val;
 +      val = wpa_config_parse_cipher(line, value);
 +      if (val == -1)
 +              return -1;
 +      if (val & ~WPA_ALLOWED_PAIRWISE_CIPHERS) {
 +              wpa_printf(MSG_ERROR, "Line %d: not allowed pairwise cipher "
 +                         "(0x%x).", line, val);
 +              return -1;
 +      }
 +
 +      wpa_printf(MSG_MSGDUMP, "pairwise: 0x%x", val);
 +      ssid->pairwise_cipher = val;
 +      return 0;
 +}
 +
 +
 +#ifndef NO_CONFIG_WRITE
 +static char * wpa_config_write_pairwise(const struct parse_data *data,
 +                                      struct wpa_ssid *ssid)
 +{
 +      return wpa_config_write_cipher(ssid->pairwise_cipher);
 +}
 +#endif /* NO_CONFIG_WRITE */
 +
 +
 +static int wpa_config_parse_group(const struct parse_data *data,
 +                                struct wpa_ssid *ssid, int line,
 +                                const char *value)
 +{
 +      int val;
 +      val = wpa_config_parse_cipher(line, value);
 +      if (val == -1)
 +              return -1;
++
++      /*
++       * Backwards compatibility - filter out WEP ciphers that were previously
++       * allowed.
++       */
++      val &= ~(WPA_CIPHER_WEP104 | WPA_CIPHER_WEP40);
++
 +      if (val & ~WPA_ALLOWED_GROUP_CIPHERS) {
 +              wpa_printf(MSG_ERROR, "Line %d: not allowed group cipher "
 +                         "(0x%x).", line, val);
 +              return -1;
 +      }
 +
 +      wpa_printf(MSG_MSGDUMP, "group: 0x%x", val);
 +      ssid->group_cipher = val;
 +      return 0;
 +}
 +
 +
 +#ifndef NO_CONFIG_WRITE
 +static char * wpa_config_write_group(const struct parse_data *data,
 +                                   struct wpa_ssid *ssid)
 +{
 +      return wpa_config_write_cipher(ssid->group_cipher);
 +}
 +#endif /* NO_CONFIG_WRITE */
 +
 +
 +static int wpa_config_parse_auth_alg(const struct parse_data *data,
 +                                   struct wpa_ssid *ssid, int line,
 +                                   const char *value)
 +{
 +      int val = 0, last, errors = 0;
 +      char *start, *end, *buf;
 +
 +      buf = os_strdup(value);
 +      if (buf == NULL)
 +              return -1;
 +      start = buf;
 +
 +      while (*start != '\0') {
 +              while (*start == ' ' || *start == '\t')
 +                      start++;
 +              if (*start == '\0')
 +                      break;
 +              end = start;
 +              while (*end != ' ' && *end != '\t' && *end != '\0')
 +                      end++;
 +              last = *end == '\0';
 +              *end = '\0';
 +              if (os_strcmp(start, "OPEN") == 0)
 +                      val |= WPA_AUTH_ALG_OPEN;
 +              else if (os_strcmp(start, "SHARED") == 0)
 +                      val |= WPA_AUTH_ALG_SHARED;
 +              else if (os_strcmp(start, "LEAP") == 0)
 +                      val |= WPA_AUTH_ALG_LEAP;
 +              else {
 +                      wpa_printf(MSG_ERROR, "Line %d: invalid auth_alg '%s'",
 +                                 line, start);
 +                      errors++;
 +              }
 +
 +              if (last)
 +                      break;
 +              start = end + 1;
 +      }
 +      os_free(buf);
 +
 +      if (val == 0) {
 +              wpa_printf(MSG_ERROR,
 +                         "Line %d: no auth_alg values configured.", line);
 +              errors++;
 +      }
 +
 +      wpa_printf(MSG_MSGDUMP, "auth_alg: 0x%x", val);
 +      ssid->auth_alg = val;
 +      return errors ? -1 : 0;
 +}
 +
 +
 +#ifndef NO_CONFIG_WRITE
 +static char * wpa_config_write_auth_alg(const struct parse_data *data,
 +                                      struct wpa_ssid *ssid)
 +{
 +      char *buf, *pos, *end;
 +      int ret;
 +
 +      pos = buf = os_zalloc(30);
 +      if (buf == NULL)
 +              return NULL;
 +      end = buf + 30;
 +
 +      if (ssid->auth_alg & WPA_AUTH_ALG_OPEN) {
 +              ret = os_snprintf(pos, end - pos, "%sOPEN",
 +                                pos == buf ? "" : " ");
 +              if (os_snprintf_error(end - pos, ret)) {
 +                      end[-1] = '\0';
 +                      return buf;
 +              }
 +              pos += ret;
 +      }
 +
 +      if (ssid->auth_alg & WPA_AUTH_ALG_SHARED) {
 +              ret = os_snprintf(pos, end - pos, "%sSHARED",
 +                                pos == buf ? "" : " ");
 +              if (os_snprintf_error(end - pos, ret)) {
 +                      end[-1] = '\0';
 +                      return buf;
 +              }
 +              pos += ret;
 +      }
 +
 +      if (ssid->auth_alg & WPA_AUTH_ALG_LEAP) {
 +              ret = os_snprintf(pos, end - pos, "%sLEAP",
 +                                pos == buf ? "" : " ");
 +              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 */
 +
 +
 +static int * wpa_config_parse_int_array(const char *value)
 +{
 +      int *freqs;
 +      size_t used, len;
 +      const char *pos;
 +
 +      used = 0;
 +      len = 10;
 +      freqs = os_calloc(len + 1, sizeof(int));
 +      if (freqs == NULL)
 +              return NULL;
 +
 +      pos = value;
 +      while (pos) {
 +              while (*pos == ' ')
 +                      pos++;
 +              if (used == len) {
 +                      int *n;
 +                      size_t i;
 +                      n = os_realloc_array(freqs, len * 2 + 1, sizeof(int));
 +                      if (n == NULL) {
 +                              os_free(freqs);
 +                              return NULL;
 +                      }
 +                      for (i = len; i <= len * 2; i++)
 +                              n[i] = 0;
 +                      freqs = n;
 +                      len *= 2;
 +              }
 +
 +              freqs[used] = atoi(pos);
 +              if (freqs[used] == 0)
 +                      break;
 +              used++;
 +              pos = os_strchr(pos + 1, ' ');
 +      }
 +
 +      return freqs;
 +}
 +
 +
 +static int wpa_config_parse_scan_freq(const struct parse_data *data,
 +                                    struct wpa_ssid *ssid, int line,
 +                                    const char *value)
 +{
 +      int *freqs;
 +
 +      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;
 +
 +      return 0;
 +}
 +
 +
 +static int wpa_config_parse_freq_list(const struct parse_data *data,
 +                                    struct wpa_ssid *ssid, int line,
 +                                    const char *value)
 +{
 +      int *freqs;
 +
 +      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;
 +
 +      return 0;
 +}
 +
 +
 +#ifndef NO_CONFIG_WRITE
 +static char * wpa_config_write_freqs(const struct parse_data *data,
 +                                   const int *freqs)
 +{
 +      char *buf, *pos, *end;
 +      int i, ret;
 +      size_t count;
 +
 +      if (freqs == NULL)
 +              return NULL;
 +
 +      count = 0;
 +      for (i = 0; freqs[i]; i++)
 +              count++;
 +
 +      pos = buf = os_zalloc(10 * count + 1);
 +      if (buf == NULL)
 +              return NULL;
 +      end = buf + 10 * count + 1;
 +
 +      for (i = 0; freqs[i]; i++) {
 +              ret = os_snprintf(pos, end - pos, "%s%u",
 +                                i == 0 ? "" : " ", freqs[i]);
 +              if (os_snprintf_error(end - pos, ret)) {
 +                      end[-1] = '\0';
 +                      return buf;
 +              }
 +              pos += ret;
 +      }
 +
 +      return buf;
 +}
 +
 +
 +static char * wpa_config_write_scan_freq(const struct parse_data *data,
 +                                       struct wpa_ssid *ssid)
 +{
 +      return wpa_config_write_freqs(data, ssid->scan_freq);
 +}
 +
 +
 +static char * wpa_config_write_freq_list(const struct parse_data *data,
 +                                       struct wpa_ssid *ssid)
 +{
 +      return wpa_config_write_freqs(data, ssid->freq_list);
 +}
 +#endif /* NO_CONFIG_WRITE */
 +
 +
 +#ifdef IEEE8021X_EAPOL
 +static int wpa_config_parse_eap(const struct parse_data *data,
 +                              struct wpa_ssid *ssid, int line,
 +                              const char *value)
 +{
 +      int last, errors = 0;
 +      char *start, *end, *buf;
 +      struct eap_method_type *methods = NULL, *tmp;
 +      size_t num_methods = 0;
 +
 +      buf = os_strdup(value);
 +      if (buf == NULL)
 +              return -1;
 +      start = buf;
 +
 +      while (*start != '\0') {
 +              while (*start == ' ' || *start == '\t')
 +                      start++;
 +              if (*start == '\0')
 +                      break;
 +              end = start;
 +              while (*end != ' ' && *end != '\t' && *end != '\0')
 +                      end++;
 +              last = *end == '\0';
 +              *end = '\0';
 +              tmp = methods;
 +              methods = os_realloc_array(methods, num_methods + 1,
 +                                         sizeof(*methods));
 +              if (methods == NULL) {
 +                      os_free(tmp);
 +                      os_free(buf);
 +                      return -1;
 +              }
 +              methods[num_methods].method = eap_peer_get_type(
 +                      start, &methods[num_methods].vendor);
 +              if (methods[num_methods].vendor == EAP_VENDOR_IETF &&
 +                  methods[num_methods].method == EAP_TYPE_NONE) {
 +                      wpa_printf(MSG_ERROR, "Line %d: unknown EAP method "
 +                                 "'%s'", line, start);
 +                      wpa_printf(MSG_ERROR, "You may need to add support for"
 +                                 " this EAP method during wpa_supplicant\n"
 +                                 "build time configuration.\n"
 +                                 "See README for more information.");
 +                      errors++;
 +              } else if (methods[num_methods].vendor == EAP_VENDOR_IETF &&
 +                         methods[num_methods].method == EAP_TYPE_LEAP)
 +                      ssid->leap++;
 +              else
 +                      ssid->non_leap++;
 +              num_methods++;
 +              if (last)
 +                      break;
 +              start = end + 1;
 +      }
 +      os_free(buf);
 +
 +      tmp = methods;
 +      methods = os_realloc_array(methods, num_methods + 1, sizeof(*methods));
 +      if (methods == NULL) {
 +              os_free(tmp);
 +              return -1;
 +      }
 +      methods[num_methods].vendor = EAP_VENDOR_IETF;
 +      methods[num_methods].method = EAP_TYPE_NONE;
 +      num_methods++;
 +
 +      wpa_hexdump(MSG_MSGDUMP, "eap methods",
 +                  (u8 *) methods, num_methods * sizeof(*methods));
 +      os_free(ssid->eap.eap_methods);
 +      ssid->eap.eap_methods = methods;
 +      return errors ? -1 : 0;
 +}
 +
 +
++#ifndef NO_CONFIG_WRITE
 +static char * wpa_config_write_eap(const struct parse_data *data,
 +                                 struct wpa_ssid *ssid)
 +{
 +      int i, ret;
 +      char *buf, *pos, *end;
 +      const struct eap_method_type *eap_methods = ssid->eap.eap_methods;
 +      const char *name;
 +
 +      if (eap_methods == NULL)
 +              return NULL;
 +
 +      pos = buf = os_zalloc(100);
 +      if (buf == NULL)
 +              return NULL;
 +      end = buf + 100;
 +
 +      for (i = 0; eap_methods[i].vendor != EAP_VENDOR_IETF ||
 +                   eap_methods[i].method != EAP_TYPE_NONE; i++) {
 +              name = eap_get_name(eap_methods[i].vendor,
 +                                  eap_methods[i].method);
 +              if (name) {
 +                      ret = os_snprintf(pos, end - pos, "%s%s",
 +                                        pos == buf ? "" : " ", name);
 +                      if (os_snprintf_error(end - pos, ret))
 +                              break;
 +                      pos += ret;
 +              }
 +      }
 +
 +      end[-1] = '\0';
 +
 +      return buf;
 +}
++#endif /* NO_CONFIG_WRITE */
 +
 +
 +static int wpa_config_parse_password(const struct parse_data *data,
 +                                   struct wpa_ssid *ssid, int line,
 +                                   const char *value)
 +{
 +      u8 *hash;
 +
 +      if (os_strcmp(value, "NULL") == 0) {
 +              wpa_printf(MSG_DEBUG, "Unset configuration string 'password'");
 +              bin_clear_free(ssid->eap.password, ssid->eap.password_len);
 +              ssid->eap.password = NULL;
 +              ssid->eap.password_len = 0;
 +              return 0;
 +      }
 +
 +#ifdef CONFIG_EXT_PASSWORD
 +      if (os_strncmp(value, "ext:", 4) == 0) {
 +              char *name = os_strdup(value + 4);
 +              if (name == NULL)
 +                      return -1;
 +              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;
 +              ssid->eap.flags |= EAP_CONFIG_FLAGS_EXT_PASSWORD;
 +              return 0;
 +      }
 +#endif /* CONFIG_EXT_PASSWORD */
 +
 +      if (os_strncmp(value, "hash:", 5) != 0) {
 +              char *tmp;
 +              size_t res_len;
 +
 +              tmp = wpa_config_parse_string(value, &res_len);
 +              if (tmp == NULL) {
 +                      wpa_printf(MSG_ERROR, "Line %d: failed to parse "
 +                                 "password.", line);
 +                      return -1;
 +              }
 +              wpa_hexdump_ascii_key(MSG_MSGDUMP, data->name,
 +                                    (u8 *) tmp, res_len);
 +
 +              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;
 +              ssid->eap.flags &= ~EAP_CONFIG_FLAGS_EXT_PASSWORD;
 +
 +              return 0;
 +      }
 +
 +
 +      /* NtPasswordHash: hash:<32 hex digits> */
 +      if (os_strlen(value + 5) != 2 * 16) {
 +              wpa_printf(MSG_ERROR, "Line %d: Invalid password hash length "
 +                         "(expected 32 hex digits)", line);
 +              return -1;
 +      }
 +
 +      hash = os_malloc(16);
 +      if (hash == NULL)
 +              return -1;
 +
 +      if (hexstr2bin(value + 5, hash, 16)) {
 +              os_free(hash);
 +              wpa_printf(MSG_ERROR, "Line %d: Invalid password hash", line);
 +              return -1;
 +      }
 +
 +      wpa_hexdump_key(MSG_MSGDUMP, data->name, hash, 16);
 +
 +      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;
 +      ssid->eap.flags &= ~EAP_CONFIG_FLAGS_EXT_PASSWORD;
 +
 +      return 0;
 +}
 +
 +
++#ifndef NO_CONFIG_WRITE
 +static char * wpa_config_write_password(const struct parse_data *data,
 +                                      struct wpa_ssid *ssid)
 +{
 +      char *buf;
 +
 +      if (ssid->eap.password == NULL)
 +              return NULL;
 +
 +#ifdef CONFIG_EXT_PASSWORD
 +      if (ssid->eap.flags & EAP_CONFIG_FLAGS_EXT_PASSWORD) {
 +              buf = os_zalloc(4 + ssid->eap.password_len + 1);
 +              if (buf == NULL)
 +                      return NULL;
 +              os_memcpy(buf, "ext:", 4);
 +              os_memcpy(buf + 4, ssid->eap.password, ssid->eap.password_len);
 +              return buf;
 +      }
 +#endif /* CONFIG_EXT_PASSWORD */
 +
 +      if (!(ssid->eap.flags & EAP_CONFIG_FLAGS_PASSWORD_NTHASH)) {
 +              return wpa_config_write_string(
 +                      ssid->eap.password, ssid->eap.password_len);
 +      }
 +
 +      buf = os_malloc(5 + 32 + 1);
 +      if (buf == NULL)
 +              return NULL;
 +
 +      os_memcpy(buf, "hash:", 5);
 +      wpa_snprintf_hex(buf + 5, 32 + 1, ssid->eap.password, 16);
 +
 +      return buf;
 +}
++#endif /* NO_CONFIG_WRITE */
 +#endif /* IEEE8021X_EAPOL */
 +
 +
 +static int wpa_config_parse_wep_key(u8 *key, size_t *len, int line,
 +                                  const char *value, int idx)
 +{
 +      char *buf, title[20];
 +      int res;
 +
 +      buf = wpa_config_parse_string(value, len);
 +      if (buf == NULL) {
 +              wpa_printf(MSG_ERROR, "Line %d: Invalid WEP key %d '%s'.",
 +                         line, idx, value);
 +              return -1;
 +      }
 +      if (*len > MAX_WEP_KEY_LEN) {
 +              wpa_printf(MSG_ERROR, "Line %d: Too long WEP key %d '%s'.",
 +                         line, idx, value);
 +              os_free(buf);
 +              return -1;
 +      }
 +      if (*len && *len != 5 && *len != 13 && *len != 16) {
 +              wpa_printf(MSG_ERROR, "Line %d: Invalid WEP key length %u - "
 +                         "this network block will be ignored",
 +                         line, (unsigned int) *len);
 +      }
 +      os_memcpy(key, buf, *len);
 +      str_clear_free(buf);
 +      res = os_snprintf(title, sizeof(title), "wep_key%d", idx);
 +      if (!os_snprintf_error(sizeof(title), res))
 +              wpa_hexdump_key(MSG_MSGDUMP, title, key, *len);
 +      return 0;
 +}
 +
 +
 +static int wpa_config_parse_wep_key0(const struct parse_data *data,
 +                                   struct wpa_ssid *ssid, int line,
 +                                   const char *value)
 +{
 +      return wpa_config_parse_wep_key(ssid->wep_key[0],
 +                                      &ssid->wep_key_len[0], line,
 +                                      value, 0);
 +}
 +
 +
 +static int wpa_config_parse_wep_key1(const struct parse_data *data,
 +                                   struct wpa_ssid *ssid, int line,
 +                                   const char *value)
 +{
 +      return wpa_config_parse_wep_key(ssid->wep_key[1],
 +                                      &ssid->wep_key_len[1], line,
 +                                      value, 1);
 +}
 +
 +
 +static int wpa_config_parse_wep_key2(const struct parse_data *data,
 +                                   struct wpa_ssid *ssid, int line,
 +                                   const char *value)
 +{
 +      return wpa_config_parse_wep_key(ssid->wep_key[2],
 +                                      &ssid->wep_key_len[2], line,
 +                                      value, 2);
 +}
 +
 +
 +static int wpa_config_parse_wep_key3(const struct parse_data *data,
 +                                   struct wpa_ssid *ssid, int line,
 +                                   const char *value)
 +{
 +      return wpa_config_parse_wep_key(ssid->wep_key[3],
 +                                      &ssid->wep_key_len[3], line,
 +                                      value, 3);
 +}
 +
 +
 +#ifndef NO_CONFIG_WRITE
 +static char * wpa_config_write_wep_key(struct wpa_ssid *ssid, int idx)
 +{
 +      if (ssid->wep_key_len[idx] == 0)
 +              return NULL;
 +      return wpa_config_write_string(ssid->wep_key[idx],
 +                                     ssid->wep_key_len[idx]);
 +}
 +
 +
 +static char * wpa_config_write_wep_key0(const struct parse_data *data,
 +                                      struct wpa_ssid *ssid)
 +{
 +      return wpa_config_write_wep_key(ssid, 0);
 +}
 +
 +
 +static char * wpa_config_write_wep_key1(const struct parse_data *data,
 +                                      struct wpa_ssid *ssid)
 +{
 +      return wpa_config_write_wep_key(ssid, 1);
 +}
 +
 +
 +static char * wpa_config_write_wep_key2(const struct parse_data *data,
 +                                      struct wpa_ssid *ssid)
 +{
 +      return wpa_config_write_wep_key(ssid, 2);
 +}
 +
 +
 +static char * wpa_config_write_wep_key3(const struct parse_data *data,
 +                                      struct wpa_ssid *ssid)
 +{
 +      return wpa_config_write_wep_key(ssid, 3);
 +}
 +#endif /* NO_CONFIG_WRITE */
 +
 +
 +#ifdef CONFIG_P2P
 +
 +static int wpa_config_parse_go_p2p_dev_addr(const struct parse_data *data,
 +                                          struct wpa_ssid *ssid, int line,
 +                                          const char *value)
 +{
 +      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;
 +      }
 +      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_go_p2p_dev_addr(const struct parse_data *data,
 +                                             struct wpa_ssid *ssid)
 +{
 +      char *value;
 +      int res;
 +
 +      if (is_zero_ether_addr(ssid->go_p2p_dev_addr))
 +              return NULL;
 +
 +      value = os_malloc(20);
 +      if (value == NULL)
 +              return NULL;
 +      res = os_snprintf(value, 20, MACSTR, MAC2STR(ssid->go_p2p_dev_addr));
 +      if (os_snprintf_error(20, res)) {
 +              os_free(value);
 +              return NULL;
 +      }
 +      value[20 - 1] = '\0';
 +      return value;
 +}
 +#endif /* NO_CONFIG_WRITE */
 +
 +
 +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);
 +}
 +
 +
 +#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
 +#define STR_KEY(f) _STR(f), NULL, NULL, NULL, 1
 +#define STR_KEYe(f) _STRe(f), NULL, NULL, NULL, 1
 +
 +/* STR_LEN: Define a string variable with a separate variable for storing the
 + * data length. Unlike STR(), this can be used to store arbitrary binary data
 + * (i.e., even nul termination character). */
 +#define _STR_LEN(f) _STR(f), OFFSET(f ## _len)
 +#define _STR_LENe(f) _STRe(f), OFFSET(eap.f ## _len)
 +#define STR_LEN(f) _STR_LEN(f), NULL, NULL, 0
 +#define STR_LENe(f) _STR_LENe(f), NULL, NULL, 0
 +#define STR_LEN_KEY(f) _STR_LEN(f), NULL, NULL, 1
 +
 +/* STR_RANGE: Like STR_LEN(), but with minimum and maximum allowed length
 + * explicitly specified. */
 +#define _STR_RANGE(f, min, max) _STR_LEN(f), (void *) (min), (void *) (max)
 +#define STR_RANGE(f, min, max) _STR_RANGE(f, min, max), 0
 +#define STR_RANGE_KEY(f, min, max) _STR_RANGE(f, min, max), 1
 +
 +#ifdef NO_CONFIG_WRITE
 +#define _INT(f) #f, wpa_config_parse_int, OFFSET(f), (void *) 0
 +#define _INTe(f) #f, wpa_config_parse_int, OFFSET(eap.f), (void *) 0
 +#else /* NO_CONFIG_WRITE */
 +#define _INT(f) #f, wpa_config_parse_int, wpa_config_write_int, \
 +      OFFSET(f), (void *) 0
 +#define _INTe(f) #f, wpa_config_parse_int, wpa_config_write_int, \
 +      OFFSET(eap.f), (void *) 0
 +#endif /* NO_CONFIG_WRITE */
 +
 +/* INT: Define an integer variable */
 +#define INT(f) _INT(f), NULL, NULL, 0
 +#define INTe(f) _INTe(f), NULL, NULL, 0
 +
 +/* INT_RANGE: Define an integer variable with allowed value range */
 +#define INT_RANGE(f, min, max) _INT(f), (void *) (min), (void *) (max), 0
 +
 +/* FUNC: Define a configuration variable that uses a custom function for
 + * parsing and writing the value. */
 +#ifdef NO_CONFIG_WRITE
 +#define _FUNC(f) #f, wpa_config_parse_ ## f, NULL, NULL, NULL, NULL
 +#else /* NO_CONFIG_WRITE */
 +#define _FUNC(f) #f, wpa_config_parse_ ## f, wpa_config_write_ ## f, \
 +      NULL, NULL, NULL, NULL
 +#endif /* NO_CONFIG_WRITE */
 +#define FUNC(f) _FUNC(f), 0
 +#define FUNC_KEY(f) _FUNC(f), 1
 +
 +/*
 + * Table of network configuration variables. This table is used to parse each
 + * network configuration variable, e.g., each line in wpa_supplicant.conf file
 + * that is inside a network block.
 + *
 + * This table is generated using the helper macros defined above and with
 + * generous help from the C pre-processor. The field name is stored as a string
 + * into .name and for STR and INT types, the offset of the target buffer within
 + * struct wpa_ssid is stored in .param1. .param2 (if not NULL) is similar
 + * offset to the field containing the length of the configuration variable.
 + * .param3 and .param4 can be used to mark the allowed range (length for STR
 + * and value for INT).
 + *
 + * For each configuration line in wpa_supplicant.conf, the parser goes through
 + * this table and select the entry that matches with the field name. The parser
 + * function (.parser) is then called to parse the actual value of the field.
 + *
 + * This kind of mechanism makes it easy to add new configuration parameters,
 + * since only one line needs to be added into this table and into the
 + * struct wpa_ssid definition if the new variable is either a string or
 + * integer. More complex types will need to use their own parser and writer
 + * functions.
 + */
 +static const struct parse_data ssid_fields[] = {
-               if (len > MAX_SSID_LEN) {
++      { STR_RANGE(ssid, 0, SSID_MAX_LEN) },
 +      { INT_RANGE(scan_ssid, 0, 1) },
 +      { FUNC(bssid) },
 +      { FUNC(bssid_blacklist) },
 +      { FUNC(bssid_whitelist) },
 +      { FUNC_KEY(psk) },
++      { INT(mem_only_psk) },
 +      { FUNC(proto) },
 +      { FUNC(key_mgmt) },
 +      { INT(bg_scan_period) },
 +      { FUNC(pairwise) },
 +      { FUNC(group) },
 +      { FUNC(auth_alg) },
 +      { FUNC(scan_freq) },
 +      { FUNC(freq_list) },
 +#ifdef IEEE8021X_EAPOL
 +      { FUNC(eap) },
 +      { STR_LENe(identity) },
 +      { STR_LENe(anonymous_identity) },
 +      { FUNC_KEY(password) },
 +      { STRe(ca_cert) },
 +      { STRe(ca_path) },
 +      { STRe(client_cert) },
 +      { STRe(private_key) },
 +      { STR_KEYe(private_key_passwd) },
 +      { 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) },
 +      { STRe(private_key2) },
 +      { STR_KEYe(private_key2_passwd) },
 +      { STRe(dh_file2) },
 +      { STRe(subject_match2) },
 +      { STRe(altsubject_match2) },
 +      { STRe(domain_suffix_match2) },
 +      { STRe(domain_match2) },
 +      { STRe(phase1) },
 +      { STRe(phase2) },
 +      { STRe(pcsc) },
 +      { STR_KEYe(pin) },
 +      { STRe(engine_id) },
 +      { STRe(key_id) },
 +      { STRe(cert_id) },
 +      { STRe(ca_cert_id) },
 +      { STR_KEYe(pin2) },
 +      { STRe(engine2_id) },
 +      { STRe(key2_id) },
 +      { STRe(cert2_id) },
 +      { STRe(ca_cert2_id) },
 +      { 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) },
 +      { FUNC_KEY(wep_key2) },
 +      { FUNC_KEY(wep_key3) },
 +      { INT(wep_tx_keyidx) },
 +      { INT(priority) },
 +#ifdef IEEE8021X_EAPOL
 +      { INT(eap_workaround) },
 +      { STRe(pac_file) },
 +      { 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) },
 +#ifdef CONFIG_IEEE80211W
 +      { INT_RANGE(ieee80211w, 0, 2) },
 +#endif /* CONFIG_IEEE80211W */
 +      { 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) },
 +      { STR(ht_mcs) },
 +#endif /* CONFIG_HT_OVERRIDES */
 +#ifdef CONFIG_VHT_OVERRIDES
 +      { INT_RANGE(disable_vht, 0, 1) },
 +      { INT(vht_capa) },
 +      { INT(vht_capa_mask) },
 +      { INT_RANGE(vht_rx_mcs_nss_1, -1, 3) },
 +      { INT_RANGE(vht_rx_mcs_nss_2, -1, 3) },
 +      { INT_RANGE(vht_rx_mcs_nss_3, -1, 3) },
 +      { INT_RANGE(vht_rx_mcs_nss_4, -1, 3) },
 +      { INT_RANGE(vht_rx_mcs_nss_5, -1, 3) },
 +      { INT_RANGE(vht_rx_mcs_nss_6, -1, 3) },
 +      { INT_RANGE(vht_rx_mcs_nss_7, -1, 3) },
 +      { INT_RANGE(vht_rx_mcs_nss_8, -1, 3) },
 +      { INT_RANGE(vht_tx_mcs_nss_1, -1, 3) },
 +      { INT_RANGE(vht_tx_mcs_nss_2, -1, 3) },
 +      { INT_RANGE(vht_tx_mcs_nss_3, -1, 3) },
 +      { INT_RANGE(vht_tx_mcs_nss_4, -1, 3) },
 +      { INT_RANGE(vht_tx_mcs_nss_5, -1, 3) },
 +      { INT_RANGE(vht_tx_mcs_nss_6, -1, 3) },
 +      { INT_RANGE(vht_tx_mcs_nss_7, -1, 3) },
 +      { INT_RANGE(vht_tx_mcs_nss_8, -1, 3) },
 +#endif /* CONFIG_VHT_OVERRIDES */
 +      { 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
 +#undef _STR
 +#undef STR
 +#undef STR_KEY
 +#undef _STR_LEN
 +#undef STR_LEN
 +#undef STR_LEN_KEY
 +#undef _STR_RANGE
 +#undef STR_RANGE
 +#undef STR_RANGE_KEY
 +#undef _INT
 +#undef INT
 +#undef INT_RANGE
 +#undef _FUNC
 +#undef FUNC
 +#undef FUNC_KEY
 +#define NUM_SSID_FIELDS ARRAY_SIZE(ssid_fields)
 +
 +
 +/**
 + * wpa_config_add_prio_network - Add a network to priority lists
 + * @config: Configuration data from wpa_config_read()
 + * @ssid: Pointer to the network configuration to be added to the list
 + * Returns: 0 on success, -1 on failure
 + *
 + * This function is used to add a network block to the priority list of
 + * networks. This must be called for each network when reading in the full
 + * configuration. In addition, this can be used indirectly when updating
 + * priorities by calling wpa_config_update_prio_list().
 + */
 +int wpa_config_add_prio_network(struct wpa_config *config,
 +                              struct wpa_ssid *ssid)
 +{
 +      int prio;
 +      struct wpa_ssid *prev, **nlist;
 +
 +      /*
 +       * Add to an existing priority list if one is available for the
 +       * configured priority level for this network.
 +       */
 +      for (prio = 0; prio < config->num_prio; prio++) {
 +              prev = config->pssid[prio];
 +              if (prev->priority == ssid->priority) {
 +                      while (prev->pnext)
 +                              prev = prev->pnext;
 +                      prev->pnext = ssid;
 +                      return 0;
 +              }
 +      }
 +
 +      /* First network for this priority - add a new priority list */
 +      nlist = os_realloc_array(config->pssid, config->num_prio + 1,
 +                               sizeof(struct wpa_ssid *));
 +      if (nlist == NULL)
 +              return -1;
 +
 +      for (prio = 0; prio < config->num_prio; prio++) {
 +              if (nlist[prio]->priority < ssid->priority) {
 +                      os_memmove(&nlist[prio + 1], &nlist[prio],
 +                                 (config->num_prio - prio) *
 +                                 sizeof(struct wpa_ssid *));
 +                      break;
 +              }
 +      }
 +
 +      nlist[prio] = ssid;
 +      config->num_prio++;
 +      config->pssid = nlist;
 +
 +      return 0;
 +}
 +
 +
 +/**
 + * wpa_config_update_prio_list - Update network priority list
 + * @config: Configuration data from wpa_config_read()
 + * Returns: 0 on success, -1 on failure
 + *
 + * This function is called to update the priority list of networks in the
 + * configuration when a network is being added or removed. This is also called
 + * if a priority for a network is changed.
 + */
 +int wpa_config_update_prio_list(struct wpa_config *config)
 +{
 +      struct wpa_ssid *ssid;
 +      int ret = 0;
 +
 +      os_free(config->pssid);
 +      config->pssid = NULL;
 +      config->num_prio = 0;
 +
 +      ssid = config->ssid;
 +      while (ssid) {
 +              ssid->pnext = NULL;
 +              if (wpa_config_add_prio_network(config, ssid) < 0)
 +                      ret = -1;
 +              ssid = ssid->next;
 +      }
 +
 +      return ret;
 +}
 +
 +
 +#ifdef IEEE8021X_EAPOL
 +static void eap_peer_config_free(struct eap_peer_config *eap)
 +{
 +      os_free(eap->eap_methods);
 +      bin_clear_free(eap->identity, eap->identity_len);
 +      os_free(eap->anonymous_identity);
 +      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);
 +      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);
 +      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);
 +      str_clear_free(eap->pin);
 +      os_free(eap->engine_id);
 +      os_free(eap->key_id);
 +      os_free(eap->cert_id);
 +      os_free(eap->ca_cert_id);
 +      os_free(eap->key2_id);
 +      os_free(eap->cert2_id);
 +      os_free(eap->ca_cert2_id);
 +      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);
 +      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 */
 +
 +
 +/**
 + * wpa_config_free_ssid - Free network/ssid configuration data
 + * @ssid: Configuration data for the network
 + *
 + * This function frees all resources allocated for the network configuration
 + * data.
 + */
 +void wpa_config_free_ssid(struct wpa_ssid *ssid)
 +{
 +      struct psk_list_entry *psk;
 +
 +      os_free(ssid->ssid);
 +      str_clear_free(ssid->passphrase);
 +      os_free(ssid->ext_psk);
 +#ifdef IEEE8021X_EAPOL
 +      eap_peer_config_free(&ssid->eap);
 +#endif /* IEEE8021X_EAPOL */
 +      os_free(ssid->id_str);
 +      os_free(ssid->scan_freq);
 +      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 */
 +#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);
 +      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);
 +      str_clear_free(cred->private_key_passwd);
 +      os_free(cred->imsi);
 +      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()
 + *
 + * This function frees all resources allocated for the configuration data by
 + * wpa_config_read().
 + */
 +void wpa_config_free(struct wpa_config *config)
 +{
 +      struct wpa_ssid *ssid, *prev = NULL;
 +      struct wpa_cred *cred, *cprev;
 +      int i;
 +
 +      ssid = config->ssid;
 +      while (ssid) {
 +              prev = ssid;
 +              ssid = ssid->next;
 +              wpa_config_free_ssid(prev);
 +      }
 +
 +      cred = config->cred;
 +      while (cred) {
 +              cprev = cred;
 +              cred = cred->next;
 +              wpa_config_free_cred(cprev);
 +      }
 +
 +      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);
 +      str_clear_free(config->pcsc_pin);
 +      os_free(config->driver_param);
 +      os_free(config->device_name);
 +      os_free(config->manufacturer);
 +      os_free(config->model_name);
 +      os_free(config->model_number);
 +      os_free(config->serial_number);
 +      os_free(config->config_methods);
 +      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);
 +      wpabuf_free(config->wps_nfc_dh_privkey);
 +      wpabuf_free(config->wps_nfc_dev_pw);
 +      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->fst_group_id);
 +      os_free(config);
 +}
 +
 +
 +/**
 + * wpa_config_foreach_network - Iterate over each configured network
 + * @config: Configuration data from wpa_config_read()
 + * @func: Callback function to process each network
 + * @arg: Opaque argument to pass to callback function
 + *
 + * Iterate over the set of configured networks calling the specified
 + * function for each item. We guard against callbacks removing the
 + * supplied network.
 + */
 +void wpa_config_foreach_network(struct wpa_config *config,
 +                              void (*func)(void *, struct wpa_ssid *),
 +                              void *arg)
 +{
 +      struct wpa_ssid *ssid, *next;
 +
 +      ssid = config->ssid;
 +      while (ssid) {
 +              next = ssid->next;
 +              func(arg, ssid);
 +              ssid = next;
 +      }
 +}
 +
 +
 +/**
 + * wpa_config_get_network - Get configured network based on id
 + * @config: Configuration data from wpa_config_read()
 + * @id: Unique network id to search for
 + * Returns: Network configuration or %NULL if not found
 + */
 +struct wpa_ssid * wpa_config_get_network(struct wpa_config *config, int id)
 +{
 +      struct wpa_ssid *ssid;
 +
 +      ssid = config->ssid;
 +      while (ssid) {
 +              if (id == ssid->id)
 +                      break;
 +              ssid = ssid->next;
 +      }
 +
 +      return ssid;
 +}
 +
 +
 +/**
 + * wpa_config_add_network - Add a new network with empty configuration
 + * @config: Configuration data from wpa_config_read()
 + * Returns: The new network configuration or %NULL if operation failed
 + */
 +struct wpa_ssid * wpa_config_add_network(struct wpa_config *config)
 +{
 +      int id;
 +      struct wpa_ssid *ssid, *last = NULL;
 +
 +      id = -1;
 +      ssid = config->ssid;
 +      while (ssid) {
 +              if (ssid->id > id)
 +                      id = ssid->id;
 +              last = ssid;
 +              ssid = ssid->next;
 +      }
 +      id++;
 +
 +      ssid = os_zalloc(sizeof(*ssid));
 +      if (ssid == NULL)
 +              return NULL;
 +      ssid->id = id;
 +      dl_list_init(&ssid->psk_list);
 +      if (last)
 +              last->next = ssid;
 +      else
 +              config->ssid = ssid;
 +
 +      wpa_config_update_prio_list(config);
 +
 +      return ssid;
 +}
 +
 +
 +/**
 + * wpa_config_remove_network - Remove a configured network based on id
 + * @config: Configuration data from wpa_config_read()
 + * @id: Unique network id to search for
 + * Returns: 0 on success, or -1 if the network was not found
 + */
 +int wpa_config_remove_network(struct wpa_config *config, int id)
 +{
 +      struct wpa_ssid *ssid, *prev = NULL;
 +
 +      ssid = config->ssid;
 +      while (ssid) {
 +              if (id == ssid->id)
 +                      break;
 +              prev = ssid;
 +              ssid = ssid->next;
 +      }
 +
 +      if (ssid == NULL)
 +              return -1;
 +
 +      if (prev)
 +              prev->next = ssid->next;
 +      else
 +              config->ssid = ssid->next;
 +
 +      wpa_config_update_prio_list(config);
 +      wpa_config_free_ssid(ssid);
 +      return 0;
 +}
 +
 +
 +/**
 + * wpa_config_set_network_defaults - Set network default values
 + * @ssid: Pointer to network configuration data
 + */
 +void wpa_config_set_network_defaults(struct wpa_ssid *ssid)
 +{
 +      ssid->proto = DEFAULT_PROTO;
 +      ssid->pairwise_cipher = DEFAULT_PAIRWISE;
 +      ssid->group_cipher = DEFAULT_GROUP;
 +      ssid->key_mgmt = DEFAULT_KEY_MGMT;
 +      ssid->bg_scan_period = DEFAULT_BG_SCAN_PERIOD;
 +#ifdef IEEE8021X_EAPOL
 +      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;
 +#endif /* CONFIG_HT_OVERRIDES */
 +#ifdef CONFIG_VHT_OVERRIDES
 +      ssid->vht_rx_mcs_nss_1 = -1;
 +      ssid->vht_rx_mcs_nss_2 = -1;
 +      ssid->vht_rx_mcs_nss_3 = -1;
 +      ssid->vht_rx_mcs_nss_4 = -1;
 +      ssid->vht_rx_mcs_nss_5 = -1;
 +      ssid->vht_rx_mcs_nss_6 = -1;
 +      ssid->vht_rx_mcs_nss_7 = -1;
 +      ssid->vht_rx_mcs_nss_8 = -1;
 +      ssid->vht_tx_mcs_nss_1 = -1;
 +      ssid->vht_tx_mcs_nss_2 = -1;
 +      ssid->vht_tx_mcs_nss_3 = -1;
 +      ssid->vht_tx_mcs_nss_4 = -1;
 +      ssid->vht_tx_mcs_nss_5 = -1;
 +      ssid->vht_tx_mcs_nss_6 = -1;
 +      ssid->vht_tx_mcs_nss_7 = -1;
 +      ssid->vht_tx_mcs_nss_8 = -1;
 +#endif /* CONFIG_VHT_OVERRIDES */
 +      ssid->proactive_key_caching = -1;
 +#ifdef CONFIG_IEEE80211W
 +      ssid->ieee80211w = MGMT_FRAME_PROTECTION_DEFAULT;
 +#endif /* CONFIG_IEEE80211W */
 +      ssid->mac_addr = -1;
 +}
 +
 +
 +/**
 + * wpa_config_set - Set a variable in network configuration
 + * @ssid: Pointer to network configuration data
 + * @var: Variable name, e.g., "ssid"
 + * @value: Variable value
 + * @line: Line number in configuration file or 0 if not used
 + * Returns: 0 on success, -1 on failure
 + *
 + * This function can be used to set network configuration variables based on
 + * both the configuration file and management interface input. The value
 + * parameter must be in the same format as the text-based configuration file is
 + * using. For example, strings are using double quotation marks.
 + */
 +int wpa_config_set(struct wpa_ssid *ssid, const char *var, const char *value,
 +                 int line)
 +{
 +      size_t i;
 +      int ret = 0;
 +
 +      if (ssid == NULL || var == NULL || value == NULL)
 +              return -1;
 +
 +      for (i = 0; i < NUM_SSID_FIELDS; i++) {
 +              const struct parse_data *field = &ssid_fields[i];
 +              if (os_strcmp(var, field->name) != 0)
 +                      continue;
 +
 +              if (field->parser(field, ssid, line, value)) {
 +                      if (line) {
 +                              wpa_printf(MSG_ERROR, "Line %d: failed to "
 +                                         "parse %s '%s'.", line, var, value);
 +                      }
 +                      ret = -1;
 +              }
 +              break;
 +      }
 +      if (i == NUM_SSID_FIELDS) {
 +              if (line) {
 +                      wpa_printf(MSG_ERROR, "Line %d: unknown network field "
 +                                 "'%s'.", line, var);
 +              }
 +              ret = -1;
 +      }
 +
 +      return ret;
 +}
 +
 +
 +int wpa_config_set_quoted(struct wpa_ssid *ssid, const char *var,
 +                        const char *value)
 +{
 +      size_t len;
 +      char *buf;
 +      int ret;
 +
 +      len = os_strlen(value);
 +      buf = os_malloc(len + 3);
 +      if (buf == NULL)
 +              return -1;
 +      buf[0] = '"';
 +      os_memcpy(buf + 1, value, len);
 +      buf[len + 1] = '"';
 +      buf[len + 2] = '\0';
 +      ret = wpa_config_set(ssid, var, buf, 0);
 +      os_free(buf);
 +      return ret;
 +}
 +
 +
 +/**
 + * wpa_config_get_all - Get all options from network configuration
 + * @ssid: Pointer to network configuration data
 + * @get_keys: Determines if keys/passwords will be included in returned list
 + *    (if they may be exported)
 + * Returns: %NULL terminated list of all set keys and their values in the form
 + * of [key1, val1, key2, val2, ... , NULL]
 + *
 + * This function can be used to get list of all configured network properties.
 + * The caller is responsible for freeing the returned list and all its
 + * elements.
 + */
 +char ** wpa_config_get_all(struct wpa_ssid *ssid, int get_keys)
 +{
++#ifdef NO_CONFIG_WRITE
++      return NULL;
++#else /* NO_CONFIG_WRITE */
 +      const struct parse_data *field;
 +      char *key, *value;
 +      size_t i;
 +      char **props;
 +      int fields_num;
 +
 +      get_keys = get_keys && ssid->export_keys;
 +
 +      props = os_calloc(2 * NUM_SSID_FIELDS + 1, sizeof(char *));
 +      if (!props)
 +              return NULL;
 +
 +      fields_num = 0;
 +      for (i = 0; i < NUM_SSID_FIELDS; i++) {
 +              field = &ssid_fields[i];
 +              if (field->key_data && !get_keys)
 +                      continue;
 +              value = field->writer(field, ssid);
 +              if (value == NULL)
 +                      continue;
 +              if (os_strlen(value) == 0) {
 +                      os_free(value);
 +                      continue;
 +              }
 +
 +              key = os_strdup(field->name);
 +              if (key == NULL) {
 +                      os_free(value);
 +                      goto err;
 +              }
 +
 +              props[fields_num * 2] = key;
 +              props[fields_num * 2 + 1] = value;
 +
 +              fields_num++;
 +      }
 +
 +      return props;
 +
 +err:
 +      value = *props;
 +      while (value)
 +              os_free(value++);
 +      os_free(props);
 +      return NULL;
++#endif /* NO_CONFIG_WRITE */
 +}
 +
 +
 +#ifndef NO_CONFIG_WRITE
 +/**
 + * wpa_config_get - Get a variable in network configuration
 + * @ssid: Pointer to network configuration data
 + * @var: Variable name, e.g., "ssid"
 + * Returns: Value of the variable or %NULL on failure
 + *
 + * This function can be used to get network configuration variables. The
 + * returned value is a copy of the configuration variable in text format, i.e,.
 + * the same format that the text-based configuration file and wpa_config_set()
 + * are using for the value. The caller is responsible for freeing the returned
 + * value.
 + */
 +char * wpa_config_get(struct wpa_ssid *ssid, const char *var)
 +{
 +      size_t i;
 +
 +      if (ssid == NULL || var == NULL)
 +              return NULL;
 +
 +      for (i = 0; i < NUM_SSID_FIELDS; i++) {
 +              const struct parse_data *field = &ssid_fields[i];
 +              if (os_strcmp(var, field->name) == 0)
 +                      return field->writer(field, ssid);
 +      }
 +
 +      return NULL;
 +}
 +
 +
 +/**
 + * wpa_config_get_no_key - Get a variable in network configuration (no keys)
 + * @ssid: Pointer to network configuration data
 + * @var: Variable name, e.g., "ssid"
 + * Returns: Value of the variable or %NULL on failure
 + *
 + * This function can be used to get network configuration variable like
 + * wpa_config_get(). The only difference is that this functions does not expose
 + * key/password material from the configuration. In case a key/password field
 + * is requested, the returned value is an empty string or %NULL if the variable
 + * is not set or "*" if the variable is set (regardless of its value). The
 + * returned value is a copy of the configuration variable in text format, i.e,.
 + * the same format that the text-based configuration file and wpa_config_set()
 + * are using for the value. The caller is responsible for freeing the returned
 + * value.
 + */
 +char * wpa_config_get_no_key(struct wpa_ssid *ssid, const char *var)
 +{
 +      size_t i;
 +
 +      if (ssid == NULL || var == NULL)
 +              return NULL;
 +
 +      for (i = 0; i < NUM_SSID_FIELDS; i++) {
 +              const struct parse_data *field = &ssid_fields[i];
 +              if (os_strcmp(var, field->name) == 0) {
 +                      char *res = field->writer(field, ssid);
 +                      if (field->key_data) {
 +                              if (res && res[0]) {
 +                                      wpa_printf(MSG_DEBUG, "Do not allow "
 +                                                 "key_data field to be "
 +                                                 "exposed");
 +                                      str_clear_free(res);
 +                                      return os_strdup("*");
 +                              }
 +
 +                              os_free(res);
 +                              return NULL;
 +                      }
 +                      return res;
 +              }
 +      }
 +
 +      return NULL;
 +}
 +#endif /* NO_CONFIG_WRITE */
 +
 +
 +/**
 + * wpa_config_update_psk - Update WPA PSK based on passphrase and SSID
 + * @ssid: Pointer to network configuration data
 + *
 + * This function must be called to update WPA PSK when either SSID or the
 + * passphrase has changed for the network configuration.
 + */
 +void wpa_config_update_psk(struct wpa_ssid *ssid)
 +{
 +#ifndef CONFIG_NO_PBKDF2
 +      pbkdf2_sha1(ssid->passphrase, ssid->ssid, ssid->ssid_len, 4096,
 +                  ssid->psk, PMK_LEN);
 +      wpa_hexdump_key(MSG_MSGDUMP, "PSK (from passphrase)",
 +                      ssid->psk, PMK_LEN);
 +      ssid->psk_set = 1;
 +#endif /* CONFIG_NO_PBKDF2 */
 +}
 +
 +
 +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;
 +      }
 +
 +      if (os_strcmp(var, "eap") == 0) {
 +              struct eap_method_type method;
 +              method.method = eap_peer_get_type(value, &method.vendor);
 +              if (method.vendor == EAP_VENDOR_IETF &&
 +                  method.method == EAP_TYPE_NONE) {
 +                      wpa_printf(MSG_ERROR, "Line %d: unknown EAP type '%s' "
 +                                 "for a credential", line, value);
 +                      return -1;
 +              }
 +              os_free(cred->eap_method);
 +              cred->eap_method = os_malloc(sizeof(*cred->eap_method));
 +              if (cred->eap_method == NULL)
 +                      return -1;
 +              os_memcpy(cred->eap_method, &method, sizeof(method));
 +              return 0;
 +      }
 +
 +      if (os_strcmp(var, "password") == 0 &&
 +          os_strncmp(value, "ext:", 4) == 0) {
 +              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 "
 +                         "value '%s'.", line, var, value);
 +              return -1;
 +      }
 +
 +      if (os_strcmp(var, "realm") == 0) {
 +              os_free(cred->realm);
 +              cred->realm = val;
 +              return 0;
 +      }
 +
 +      if (os_strcmp(var, "username") == 0) {
 +              str_clear_free(cred->username);
 +              cred->username = val;
 +              return 0;
 +      }
 +
 +      if (os_strcmp(var, "password") == 0) {
 +              str_clear_free(cred->password);
 +              cred->password = val;
 +              cred->ext_password = 0;
 +              return 0;
 +      }
 +
 +      if (os_strcmp(var, "ca_cert") == 0) {
 +              os_free(cred->ca_cert);
 +              cred->ca_cert = val;
 +              return 0;
 +      }
 +
 +      if (os_strcmp(var, "client_cert") == 0) {
 +              os_free(cred->client_cert);
 +              cred->client_cert = val;
 +              return 0;
 +      }
 +
 +      if (os_strcmp(var, "private_key") == 0) {
 +              os_free(cred->private_key);
 +              cred->private_key = val;
 +              return 0;
 +      }
 +
 +      if (os_strcmp(var, "private_key_passwd") == 0) {
 +              str_clear_free(cred->private_key_passwd);
 +              cred->private_key_passwd = val;
 +              return 0;
 +      }
 +
 +      if (os_strcmp(var, "imsi") == 0) {
 +              os_free(cred->imsi);
 +              cred->imsi = val;
 +              return 0;
 +      }
 +
 +      if (os_strcmp(var, "milenage") == 0) {
 +              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) {
 +              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;
 +      }
 +
 +      if (os_strcmp(var, "phase1") == 0) {
 +              os_free(cred->phase1);
 +              cred->phase1 = val;
 +              return 0;
 +      }
 +
 +      if (os_strcmp(var, "phase2") == 0) {
 +              os_free(cred->phase2);
 +              cred->phase2 = val;
 +              return 0;
 +      }
 +
 +      if (os_strcmp(var, "roaming_consortium") == 0) {
 +              if (len < 3 || len > sizeof(cred->roaming_consortium)) {
 +                      wpa_printf(MSG_ERROR, "Line %d: invalid "
 +                                 "roaming_consortium length %d (3..15 "
 +                                 "expected)", line, (int) len);
 +                      os_free(val);
 +                      return -1;
 +              }
 +              os_memcpy(cred->roaming_consortium, val, len);
 +              cred->roaming_consortium_len = len;
 +              os_free(val);
 +              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;
 +
- #define IPV4(f) #f, wpa_global_config_parse_ipv4, NULL, OFFSET(f), NULL, NULL
++              if (len > SSID_MAX_LEN) {
 +                      wpa_printf(MSG_ERROR, "Line %d: invalid "
 +                                 "excluded_ssid length %d", line, (int) len);
 +                      os_free(val);
 +                      return -1;
 +              }
 +
 +              e = os_realloc_array(cred->excluded_ssid,
 +                                   cred->num_excluded_ssid + 1,
 +                                   sizeof(struct excluded_ssid));
 +              if (e == NULL) {
 +                      os_free(val);
 +                      return -1;
 +              }
 +              cred->excluded_ssid = e;
 +
 +              e = &cred->excluded_ssid[cred->num_excluded_ssid++];
 +              os_memcpy(e->ssid, val, len);
 +              e->ssid_len = len;
 +
 +              os_free(val);
 +
 +              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);
 +      }
 +
 +      os_free(val);
 +
 +      return -1;
 +}
 +
 +
 +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;
 +
 +      cred = config->cred;
 +      while (cred) {
 +              if (id == cred->id)
 +                      break;
 +              cred = cred->next;
 +      }
 +
 +      return cred;
 +}
 +
 +
 +struct wpa_cred * wpa_config_add_cred(struct wpa_config *config)
 +{
 +      int id;
 +      struct wpa_cred *cred, *last = NULL;
 +
 +      id = -1;
 +      cred = config->cred;
 +      while (cred) {
 +              if (cred->id > id)
 +                      id = cred->id;
 +              last = cred;
 +              cred = cred->next;
 +      }
 +      id++;
 +
 +      cred = os_zalloc(sizeof(*cred));
 +      if (cred == NULL)
 +              return NULL;
 +      cred->id = id;
 +      cred->sim_num = DEFAULT_USER_SELECTED_SIM;
 +      if (last)
 +              last->next = cred;
 +      else
 +              config->cred = cred;
 +
 +      return cred;
 +}
 +
 +
 +int wpa_config_remove_cred(struct wpa_config *config, int id)
 +{
 +      struct wpa_cred *cred, *prev = NULL;
 +
 +      cred = config->cred;
 +      while (cred) {
 +              if (id == cred->id)
 +                      break;
 +              prev = cred;
 +              cred = cred->next;
 +      }
 +
 +      if (cred == NULL)
 +              return -1;
 +
 +      if (prev)
 +              prev->next = cred->next;
 +      else
 +              config->cred = cred->next;
 +
 +      wpa_config_free_cred(cred);
 +      return 0;
 +}
 +
 +
 +#ifndef CONFIG_NO_CONFIG_BLOBS
 +/**
 + * wpa_config_get_blob - Get a named configuration blob
 + * @config: Configuration data from wpa_config_read()
 + * @name: Name of the blob
 + * Returns: Pointer to blob data or %NULL if not found
 + */
 +const struct wpa_config_blob * wpa_config_get_blob(struct wpa_config *config,
 +                                                 const char *name)
 +{
 +      struct wpa_config_blob *blob = config->blobs;
 +
 +      while (blob) {
 +              if (os_strcmp(blob->name, name) == 0)
 +                      return blob;
 +              blob = blob->next;
 +      }
 +      return NULL;
 +}
 +
 +
 +/**
 + * wpa_config_set_blob - Set or add a named configuration blob
 + * @config: Configuration data from wpa_config_read()
 + * @blob: New value for the blob
 + *
 + * Adds a new configuration blob or replaces the current value of an existing
 + * blob.
 + */
 +void wpa_config_set_blob(struct wpa_config *config,
 +                       struct wpa_config_blob *blob)
 +{
 +      wpa_config_remove_blob(config, blob->name);
 +      blob->next = config->blobs;
 +      config->blobs = blob;
 +}
 +
 +
 +/**
 + * wpa_config_free_blob - Free blob data
 + * @blob: Pointer to blob to be freed
 + */
 +void wpa_config_free_blob(struct wpa_config_blob *blob)
 +{
 +      if (blob) {
 +              os_free(blob->name);
 +              bin_clear_free(blob->data, blob->len);
 +              os_free(blob);
 +      }
 +}
 +
 +
 +/**
 + * wpa_config_remove_blob - Remove a named configuration blob
 + * @config: Configuration data from wpa_config_read()
 + * @name: Name of the blob to remove
 + * Returns: 0 if blob was removed or -1 if blob was not found
 + */
 +int wpa_config_remove_blob(struct wpa_config *config, const char *name)
 +{
 +      struct wpa_config_blob *pos = config->blobs, *prev = NULL;
 +
 +      while (pos) {
 +              if (os_strcmp(pos->name, name) == 0) {
 +                      if (prev)
 +                              prev->next = pos->next;
 +                      else
 +                              config->blobs = pos->next;
 +                      wpa_config_free_blob(pos);
 +                      return 0;
 +              }
 +              prev = pos;
 +              pos = pos->next;
 +      }
 +
 +      return -1;
 +}
 +#endif /* CONFIG_NO_CONFIG_BLOBS */
 +
 +
 +/**
 + * wpa_config_alloc_empty - Allocate an empty configuration
 + * @ctrl_interface: Control interface parameters, e.g., path to UNIX domain
 + * socket
 + * @driver_param: Driver parameters
 + * Returns: Pointer to allocated configuration data or %NULL on failure
 + */
 +struct wpa_config * wpa_config_alloc_empty(const char *ctrl_interface,
 +                                         const char *driver_param)
 +{
 +      struct wpa_config *config;
 +      const int aCWmin = 4, aCWmax = 10;
 +      const struct hostapd_wmm_ac_params ac_bk =
 +              { aCWmin, aCWmax, 7, 0, 0 }; /* background traffic */
 +      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 };
 +      const struct hostapd_wmm_ac_params ac_vo = /* voice traffic */
 +              { aCWmin - 2, aCWmin - 1, 2, 1500 / 32, 0 };
 +
 +      config = os_zalloc(sizeof(*config));
 +      if (config == NULL)
 +              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->dot11RSNASAERetransPeriod =
++              DEFAULT_DOT11_RSNA_SAE_RETRANS_PERIOD;
 +      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_freq_change_policy = DEFAULT_P2P_GO_FREQ_MOVE;
 +      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;
 +      config->max_num_sta = DEFAULT_MAX_NUM_STA;
 +      config->access_network_type = DEFAULT_ACCESS_NETWORK_TYPE;
 +      config->scan_cur_freq = DEFAULT_SCAN_CUR_FREQ;
 +      config->wmm_ac_params[0] = ac_be;
 +      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);
 +      if (driver_param)
 +              config->driver_param = os_strdup(driver_param);
 +
 +      return config;
 +}
 +
 +
 +#ifndef CONFIG_NO_STDOUT_DEBUG
 +/**
 + * wpa_config_debug_dump_networks - Debug dump of configured networks
 + * @config: Configuration data from wpa_config_read()
 + */
 +void wpa_config_debug_dump_networks(struct wpa_config *config)
 +{
 +      int prio;
 +      struct wpa_ssid *ssid;
 +
 +      for (prio = 0; prio < config->num_prio; prio++) {
 +              ssid = config->pssid[prio];
 +              wpa_printf(MSG_DEBUG, "Priority group %d",
 +                         ssid->priority);
 +              while (ssid) {
 +                      wpa_printf(MSG_DEBUG, "   id=%d ssid='%s'",
 +                                 ssid->id,
 +                                 wpa_ssid_txt(ssid->ssid, ssid->ssid_len));
 +                      ssid = ssid->pnext;
 +              }
 +      }
 +}
 +#endif /* CONFIG_NO_STDOUT_DEBUG */
 +
 +
 +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;
 +};
 +
 +
 +static int wpa_global_config_parse_int(const struct global_parse_data *data,
 +                                     struct wpa_config *config, int line,
 +                                     const char *pos)
 +{
 +      int val, *dst;
 +      char *end;
 +
 +      dst = (int *) (((u8 *) config) + (long) data->param1);
 +      val = strtol(pos, &end, 0);
 +      if (*end) {
 +              wpa_printf(MSG_ERROR, "Line %d: invalid number \"%s\"",
 +                         line, pos);
 +              return -1;
 +      }
 +      *dst = val;
 +
 +      wpa_printf(MSG_DEBUG, "%s=%d", data->name, *dst);
 +
 +      if (data->param2 && *dst < (long) data->param2) {
 +              wpa_printf(MSG_ERROR, "Line %d: too small %s (value=%d "
 +                         "min_value=%ld)", line, data->name, *dst,
 +                         (long) data->param2);
 +              *dst = (long) data->param2;
 +              return -1;
 +      }
 +
 +      if (data->param3 && *dst > (long) data->param3) {
 +              wpa_printf(MSG_ERROR, "Line %d: too large %s (value=%d "
 +                         "max_value=%ld)", line, data->name, *dst,
 +                         (long) data->param3);
 +              *dst = (long) data->param3;
 +              return -1;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +static int wpa_global_config_parse_str(const struct global_parse_data *data,
 +                                     struct wpa_config *config, int line,
 +                                     const char *pos)
 +{
 +      size_t len;
 +      char **dst, *tmp;
 +
 +      len = os_strlen(pos);
 +      if (data->param2 && len < (size_t) data->param2) {
 +              wpa_printf(MSG_ERROR, "Line %d: too short %s (len=%lu "
 +                         "min_len=%ld)", line, data->name,
 +                         (unsigned long) len, (long) data->param2);
 +              return -1;
 +      }
 +
 +      if (data->param3 && len > (size_t) data->param3) {
 +              wpa_printf(MSG_ERROR, "Line %d: too long %s (len=%lu "
 +                         "max_len=%ld)", line, data->name,
 +                         (unsigned long) len, (long) data->param3);
 +              return -1;
 +      }
 +
 +      tmp = os_strdup(pos);
 +      if (tmp == NULL)
 +              return -1;
 +
 +      dst = (char **) (((u8 *) config) + (long) data->param1);
 +      os_free(*dst);
 +      *dst = tmp;
 +      wpa_printf(MSG_DEBUG, "%s='%s'", data->name, *dst);
 +
 +      return 0;
 +}
 +
 +
 +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)
 +{
 +      size_t len;
 +      struct wpabuf **dst, *tmp;
 +
 +      len = os_strlen(pos);
 +      if (len & 0x01)
 +              return -1;
 +
 +      tmp = wpabuf_alloc(len / 2);
 +      if (tmp == NULL)
 +              return -1;
 +
 +      if (hexstr2bin(pos, wpabuf_put(tmp, len / 2), len / 2)) {
 +              wpabuf_free(tmp);
 +              return -1;
 +      }
 +
 +      dst = (struct wpabuf **) (((u8 *) config) + (long) data->param1);
 +      wpabuf_free(*dst);
 +      *dst = tmp;
 +      wpa_printf(MSG_DEBUG, "%s", data->name);
 +
 +      return 0;
 +}
 +
 +
 +static int wpa_config_process_freq_list(const struct global_parse_data *data,
 +                                      struct wpa_config *config, int line,
 +                                      const char *value)
 +{
 +      int *freqs;
 +
 +      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)
 +{
 +      if (!pos[0] || !pos[1]) {
 +              wpa_printf(MSG_DEBUG, "Invalid country set");
 +              return -1;
 +      }
 +      config->country[0] = pos[0];
 +      config->country[1] = pos[1];
 +      wpa_printf(MSG_DEBUG, "country='%c%c'",
 +                 config->country[0], config->country[1]);
 +      return 0;
 +}
 +
 +
 +static int wpa_config_process_load_dynamic_eap(
 +      const struct global_parse_data *data, struct wpa_config *config,
 +      int line, const char *so)
 +{
 +      int ret;
 +      wpa_printf(MSG_DEBUG, "load_dynamic_eap=%s", so);
 +      ret = eap_peer_method_load(so);
 +      if (ret == -2) {
 +              wpa_printf(MSG_DEBUG, "This EAP type was already loaded - not "
 +                         "reloading.");
 +      } else if (ret) {
 +              wpa_printf(MSG_ERROR, "Line %d: Failed to load dynamic EAP "
 +                         "method '%s'.", line, so);
 +              return -1;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +#ifdef CONFIG_WPS
 +
 +static int wpa_config_process_uuid(const struct global_parse_data *data,
 +                                 struct wpa_config *config, int line,
 +                                 const char *pos)
 +{
 +      char buf[40];
 +      if (uuid_str2bin(pos, config->uuid)) {
 +              wpa_printf(MSG_ERROR, "Line %d: invalid UUID", line);
 +              return -1;
 +      }
 +      uuid_bin2str(config->uuid, buf, sizeof(buf));
 +      wpa_printf(MSG_DEBUG, "uuid=%s", buf);
 +      return 0;
 +}
 +
 +
 +static int wpa_config_process_device_type(
 +      const struct global_parse_data *data,
 +      struct wpa_config *config, int line, const char *pos)
 +{
 +      return wps_dev_type_str2bin(pos, config->device_type);
 +}
 +
 +
 +static int wpa_config_process_os_version(const struct global_parse_data *data,
 +                                       struct wpa_config *config, int line,
 +                                       const char *pos)
 +{
 +      if (hexstr2bin(pos, config->os_version, 4)) {
 +              wpa_printf(MSG_ERROR, "Line %d: invalid os_version", line);
 +              return -1;
 +      }
 +      wpa_printf(MSG_DEBUG, "os_version=%08x",
 +                 WPA_GET_BE32(config->os_version));
 +      return 0;
 +}
 +
 +
 +static int wpa_config_process_wps_vendor_ext_m1(
 +      const struct global_parse_data *data,
 +      struct wpa_config *config, int line, const char *pos)
 +{
 +      struct wpabuf *tmp;
 +      int len = os_strlen(pos) / 2;
 +      u8 *p;
 +
 +      if (!len) {
 +              wpa_printf(MSG_ERROR, "Line %d: "
 +                         "invalid wps_vendor_ext_m1", line);
 +              return -1;
 +      }
 +
 +      tmp = wpabuf_alloc(len);
 +      if (tmp) {
 +              p = wpabuf_put(tmp, len);
 +
 +              if (hexstr2bin(pos, p, len)) {
 +                      wpa_printf(MSG_ERROR, "Line %d: "
 +                                 "invalid wps_vendor_ext_m1", line);
 +                      wpabuf_free(tmp);
 +                      return -1;
 +              }
 +
 +              wpabuf_free(config->wps_vendor_ext_m1);
 +              config->wps_vendor_ext_m1 = tmp;
 +      } else {
 +              wpa_printf(MSG_ERROR, "Can not allocate "
 +                         "memory for wps_vendor_ext_m1");
 +              return -1;
 +      }
 +
 +      return 0;
 +}
 +
 +#endif /* CONFIG_WPS */
 +
 +#ifdef CONFIG_P2P
 +static int wpa_config_process_sec_device_type(
 +      const struct global_parse_data *data,
 +      struct wpa_config *config, int line, const char *pos)
 +{
 +      int idx;
 +
 +      if (config->num_sec_device_types >= MAX_SEC_DEVICE_TYPES) {
 +              wpa_printf(MSG_ERROR, "Line %d: too many sec_device_type "
 +                         "items", line);
 +              return -1;
 +      }
 +
 +      idx = config->num_sec_device_types;
 +
 +      if (wps_dev_type_str2bin(pos, config->sec_device_type[idx]))
 +              return -1;
 +
 +      config->num_sec_device_types++;
 +      return 0;
 +}
 +
 +
 +static int wpa_config_process_p2p_pref_chan(
 +      const struct global_parse_data *data,
 +      struct wpa_config *config, int line, const char *pos)
 +{
 +      struct p2p_channel *pref = NULL, *n;
 +      unsigned int num = 0;
 +      const char *pos2;
 +      u8 op_class, chan;
 +
 +      /* format: class:chan,class:chan,... */
 +
 +      while (*pos) {
 +              op_class = atoi(pos);
 +              pos2 = os_strchr(pos, ':');
 +              if (pos2 == NULL)
 +                      goto fail;
 +              pos2++;
 +              chan = atoi(pos2);
 +
 +              n = os_realloc_array(pref, num + 1,
 +                                   sizeof(struct p2p_channel));
 +              if (n == NULL)
 +                      goto fail;
 +              pref = n;
 +              pref[num].op_class = op_class;
 +              pref[num].chan = chan;
 +              num++;
 +
 +              pos = os_strchr(pos2, ',');
 +              if (pos == NULL)
 +                      break;
 +              pos++;
 +      }
 +
 +      os_free(config->p2p_pref_chan);
 +      config->p2p_pref_chan = pref;
 +      config->num_p2p_pref_chan = num;
 +      wpa_hexdump(MSG_DEBUG, "P2P: Preferred class/channel pairs",
 +                  (u8 *) config->p2p_pref_chan,
 +                  config->num_p2p_pref_chan * sizeof(struct p2p_channel));
 +
 +      return 0;
 +
 +fail:
 +      os_free(pref);
 +      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 */
 +
 +
 +static int wpa_config_process_hessid(
 +      const struct global_parse_data *data,
 +      struct wpa_config *config, int line, const char *pos)
 +{
 +      if (hwaddr_aton2(pos, config->hessid) < 0) {
 +              wpa_printf(MSG_ERROR, "Line %d: Invalid hessid '%s'",
 +                         line, pos);
 +              return -1;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +static int wpa_config_process_sae_groups(
 +      const struct global_parse_data *data,
 +      struct wpa_config *config, int line, const char *pos)
 +{
 +      int *groups = wpa_config_parse_int_array(pos);
 +      if (groups == NULL) {
 +              wpa_printf(MSG_ERROR, "Line %d: Invalid sae_groups '%s'",
 +                         line, pos);
 +              return -1;
 +      }
 +
 +      os_free(config->sae_groups);
 +      config->sae_groups = groups;
 +
 +      return 0;
 +}
 +
 +
 +static int wpa_config_process_ap_vendor_elements(
 +      const struct global_parse_data *data,
 +      struct wpa_config *config, int line, const char *pos)
 +{
 +      struct wpabuf *tmp;
 +      int len = os_strlen(pos) / 2;
 +      u8 *p;
 +
 +      if (!len) {
 +              wpa_printf(MSG_ERROR, "Line %d: invalid ap_vendor_elements",
 +                         line);
 +              return -1;
 +      }
 +
 +      tmp = wpabuf_alloc(len);
 +      if (tmp) {
 +              p = wpabuf_put(tmp, len);
 +
 +              if (hexstr2bin(pos, p, len)) {
 +                      wpa_printf(MSG_ERROR, "Line %d: invalid "
 +                                 "ap_vendor_elements", line);
 +                      wpabuf_free(tmp);
 +                      return -1;
 +              }
 +
 +              wpabuf_free(config->ap_vendor_elements);
 +              config->ap_vendor_elements = tmp;
 +      } else {
 +              wpa_printf(MSG_ERROR, "Cannot allocate memory for "
 +                         "ap_vendor_elements");
 +              return -1;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +#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 CONFIG_P2P
++static int wpa_config_get_ipv4(const char *name, struct wpa_config *config,
++                             long offset, char *buf, size_t buflen,
++                             int pretty_print)
++{
++      void *val = ((u8 *) config) + (long) offset;
++      int res;
++      char addr[INET_ADDRSTRLEN];
++
++      if (!val || !inet_ntop(AF_INET, val, addr, sizeof(addr)))
++              return -1;
++
++      if (pretty_print)
++              res = os_snprintf(buf, buflen, "%s=%s\n", name, addr);
++      else
++              res = os_snprintf(buf, buflen, "%s", addr);
++
++      if (os_snprintf_error(buflen, res))
++              res = -1;
++
++      return res;
++}
++#endif /* CONFIG_P2P */
++
++
 +#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, 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, 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, NULL, OFFSET(f), NULL, NULL
-       { STR_RANGE(device_name, 0, 32), CFG_CHANGED_DEVICE_NAME },
++#define IPV4(f) #f, wpa_global_config_parse_ipv4, wpa_config_get_ipv4,  \
++      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 },
++      { INT(dot11RSNASAERetransPeriod), 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 },
 +      { INT(dot11RSNAConfigSATimeout), 0 },
 +#ifndef CONFIG_NO_CONFIG_WRITE
 +      { INT(update_config), 0 },
 +#endif /* CONFIG_NO_CONFIG_WRITE */
 +      { FUNC_NO_VAR(load_dynamic_eap), 0 },
 +#ifdef CONFIG_WPS
 +      { FUNC(uuid), CFG_CHANGED_UUID },
-       { INT(p2p_listen_reg_class), 0 },
-       { INT(p2p_listen_channel), 0 },
++      { STR_RANGE(device_name, 0, WPS_DEV_NAME_MAX_LEN),
++        CFG_CHANGED_DEVICE_NAME },
 +      { STR_RANGE(manufacturer, 0, 64), CFG_CHANGED_WPS_STRING },
 +      { STR_RANGE(model_name, 0, 32), CFG_CHANGED_WPS_STRING },
 +      { STR_RANGE(model_number, 0, 32), CFG_CHANGED_WPS_STRING },
 +      { STR_RANGE(serial_number, 0, 32), CFG_CHANGED_WPS_STRING },
 +      { FUNC(device_type), CFG_CHANGED_DEVICE_TYPE },
 +      { FUNC(os_version), CFG_CHANGED_OS_VERSION },
 +      { STR(config_methods), CFG_CHANGED_CONFIG_METHODS },
 +      { INT_RANGE(wps_cred_processing, 0, 2), 0 },
 +      { FUNC(wps_vendor_ext_m1), CFG_CHANGED_VENDOR_EXTENSION },
 +#endif /* CONFIG_WPS */
 +#ifdef CONFIG_P2P
 +      { FUNC(sec_device_type), CFG_CHANGED_SEC_DEVICE_TYPE },
++      { INT(p2p_listen_reg_class), CFG_CHANGED_P2P_LISTEN_CHANNEL },
++      { INT(p2p_listen_channel), CFG_CHANGED_P2P_LISTEN_CHANNEL },
 +      { 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_go_freq_change_policy, 0, P2P_GO_FREQ_MOVE_MAX), 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 },
++      { INT_RANGE(p2p_cli_probe, 0, 1), 0 },
 +#endif /* CONFIG_P2P */
 +      { FUNC(country), CFG_CHANGED_COUNTRY },
 +      { INT(bss_max_count), 0 },
 +      { INT(bss_expiration_age), 0 },
 +      { INT(bss_expiration_scan_count), 0 },
 +      { INT_RANGE(filter_ssids, 0, 1), 0 },
 +      { INT_RANGE(filter_rssi, -100, 0), 0 },
 +      { INT(max_num_sta), 0 },
 +      { INT_RANGE(disassoc_low_ack, 0, 1), 0 },
 +#ifdef CONFIG_HS20
 +      { INT_RANGE(hs20, 0, 1), 0 },
 +#endif /* CONFIG_HS20 */
 +      { INT_RANGE(interworking, 0, 1), 0 },
 +      { FUNC(hessid), 0 },
 +      { INT_RANGE(access_network_type, 0, 15), 0 },
 +      { INT_RANGE(pbc_in_m1, 0, 1), 0 },
 +      { STR(autoscan), 0 },
 +      { INT_RANGE(wps_nfc_dev_pw_id, 0x10, 0xffff),
 +        CFG_CHANGED_NFC_PASSWORD_TOKEN },
 +      { BIN(wps_nfc_dh_pubkey), CFG_CHANGED_NFC_PASSWORD_TOKEN },
 +      { BIN(wps_nfc_dh_privkey), CFG_CHANGED_NFC_PASSWORD_TOKEN },
 +      { BIN(wps_nfc_dev_pw), CFG_CHANGED_NFC_PASSWORD_TOKEN },
 +      { STR(ext_password_backend), CFG_CHANGED_EXT_PW_BACKEND },
 +      { INT(p2p_go_max_inactivity), 0 },
 +      { INT_RANGE(auto_interworking, 0, 1), 0 },
 +      { INT(okc), 0 },
 +      { INT(pmf), 0 },
 +      { FUNC(sae_groups), 0 },
 +      { INT(dtim_period), 0 },
 +      { INT(beacon_int), 0 },
 +      { FUNC(ap_vendor_elements), 0 },
 +      { INT_RANGE(ignore_old_scan_res, 0, 1), 0 },
 +      { 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 },
++      { INT(wps_priority), 0},
++#ifdef CONFIG_FST
++      { STR_RANGE(fst_group_id, 1, FST_MAX_GROUP_ID_LEN), 0 },
++      { INT_RANGE(fst_priority, 1, FST_MAX_PRIO_VALUE), 0 },
++      { INT_RANGE(fst_llt, 1, FST_MAX_LLT_MS), 0 },
++#endif /* CONFIG_FST */
 +};
 +
 +#undef FUNC
 +#undef _INT
 +#undef INT
 +#undef INT_RANGE
 +#undef _STR
 +#undef STR
 +#undef STR_RANGE
 +#undef BIN
 +#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)
 +{
 +      size_t i;
 +      int ret = 0;
 +
 +      for (i = 0; i < NUM_GLOBAL_FIELDS; i++) {
 +              const struct global_parse_data *field = &global_fields[i];
 +              size_t flen = os_strlen(field->name);
 +              if (os_strncmp(pos, field->name, flen) != 0 ||
 +                  pos[flen] != '=')
 +                      continue;
 +
 +              if (field->parser(field, config, line, pos + flen + 1)) {
 +                      wpa_printf(MSG_ERROR, "Line %d: failed to "
 +                                 "parse '%s'.", line, pos);
 +                      ret = -1;
 +              }
 +              if (field->changed_flag == CFG_CHANGED_NFC_PASSWORD_TOKEN)
 +                      config->wps_nfc_pw_from_config = 1;
 +              config->changed_parameters |= field->changed_flag;
 +              break;
 +      }
 +      if (i == NUM_GLOBAL_FIELDS) {
 +#ifdef CONFIG_AP
 +              if (os_strncmp(pos, "wmm_ac_", 7) == 0) {
 +                      char *tmp = os_strchr(pos, '=');
 +                      if (tmp == NULL) {
 +                              if (line < 0)
 +                                      return -1;
 +                              wpa_printf(MSG_ERROR, "Line %d: invalid line "
 +                                         "'%s'", line, pos);
 +                              return -1;
 +                      }
 +                      *tmp++ = '\0';
 +                      if (hostapd_config_wmm_ac(config->wmm_ac_params, pos,
 +                                                tmp)) {
 +                              wpa_printf(MSG_ERROR, "Line %d: invalid WMM "
 +                                         "AC item", line);
 +                              return -1;
 +                      }
 +              }
 +#endif /* CONFIG_AP */
 +              if (line < 0)
 +                      return -1;
 +              wpa_printf(MSG_ERROR, "Line %d: unknown global field '%s'.",
 +                         line, pos);
 +              ret = -1;
 +      }
 +
 +      return ret;
 +}
index 34b754e09c532d3b8f5cdb388617f640fd863204,0000000000000000000000000000000000000000..627f38b6e005617f5800853586b0141f986a380b
mode 100644,000000..100644
--- /dev/null
@@@ -1,1258 -1,0 +1,1342 @@@
-               u8 ssid[MAX_SSID_LEN];
 +/*
 + * WPA Supplicant / Configuration file structures
 + * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi>
 + *
 + * This software may be distributed under the terms of the BSD license.
 + * See README for more details.
 + */
 +
 +#ifndef CONFIG_H
 +#define CONFIG_H
 +
 +#define DEFAULT_EAPOL_VERSION 1
 +#ifdef CONFIG_NO_SCAN_PROCESSING
 +#define DEFAULT_AP_SCAN 2
 +#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
++/*
++ * The default dot11RSNASAERetransPeriod is defined as 40 ms in the standard,
++ * but use 1000 ms in practice to avoid issues on low power CPUs.
++ */
++#define DEFAULT_DOT11_RSNA_SAE_RETRANS_PERIOD 1000
 +#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"
++#include "common/ieee802_11_defs.h"
 +#include "common/ieee802_11_common.h"
 +
 +
 +struct wpa_cred {
 +      /**
 +       * next - Next credential in the list
 +       *
 +       * This pointer can be used to iterate over all credentials. The head
 +       * of this list is stored in the cred field of struct wpa_config.
 +       */
 +      struct wpa_cred *next;
 +
 +      /**
 +       * id - Unique id for the credential
 +       *
 +       * This identifier is used as a unique identifier for each credential
 +       * block when using the control interface. Each credential is allocated
 +       * an id when it is being created, either when reading the
 +       * configuration file or when a new credential is added through the
 +       * control interface.
 +       */
 +      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
 +       * (0). This field can be used to give higher priority for credentials
 +       * (and similarly in struct wpa_ssid for network blocks) to change the
 +       * Interworking automatic networking selection behavior. The matching
 +       * network (based on either an enabled network block or a credential)
 +       * with the highest priority value will be selected.
 +       */
 +      int priority;
 +
 +      /**
 +       * pcsc - Use PC/SC and SIM/USIM card
 +       */
 +      int pcsc;
 +
 +      /**
 +       * realm - Home Realm for Interworking
 +       */
 +      char *realm;
 +
 +      /**
 +       * username - Username for Interworking network selection
 +       */
 +      char *username;
 +
 +      /**
 +       * password - Password for Interworking network selection
 +       */
 +      char *password;
 +
 +      /**
 +       * ext_password - Whether password is a name for external storage
 +       */
 +      int ext_password;
 +
 +      /**
 +       * ca_cert - CA certificate for Interworking network selection
 +       */
 +      char *ca_cert;
 +
 +      /**
 +       * client_cert - File path to client certificate file (PEM/DER)
 +       *
 +       * This field is used with Interworking networking selection for a case
 +       * where client certificate/private key is used for authentication
 +       * (EAP-TLS). Full path to the file should be used since working
 +       * directory may change when wpa_supplicant is run in the background.
 +       *
 +       * Alternatively, a named configuration blob can be used by setting
 +       * this to blob://blob_name.
 +       */
 +      char *client_cert;
 +
 +      /**
 +       * private_key - File path to client private key file (PEM/DER/PFX)
 +       *
 +       * When PKCS#12/PFX file (.p12/.pfx) is used, client_cert should be
 +       * commented out. Both the private key and certificate will be read
 +       * from the PKCS#12 file in this case. Full path to the file should be
 +       * used since working directory may change when wpa_supplicant is run
 +       * in the background.
 +       *
 +       * Windows certificate store can be used by leaving client_cert out and
 +       * configuring private_key in one of the following formats:
 +       *
 +       * cert://substring_to_match
 +       *
 +       * hash://certificate_thumbprint_in_hex
 +       *
 +       * For example: private_key="hash://63093aa9c47f56ae88334c7b65a4"
 +       *
 +       * Note that when running wpa_supplicant as an application, the user
 +       * certificate store (My user account) is used, whereas computer store
 +       * (Computer account) is used when running wpasvc as a service.
 +       *
 +       * Alternatively, a named configuration blob can be used by setting
 +       * this to blob://blob_name.
 +       */
 +      char *private_key;
 +
 +      /**
 +       * private_key_passwd - Password for private key file
 +       */
 +      char *private_key_passwd;
 +
 +      /**
 +       * imsi - IMSI in <MCC> | <MNC> | '-' | <MSIN> format
 +       */
 +      char *imsi;
 +
 +      /**
 +       * milenage - Milenage parameters for SIM/USIM simulator in
 +       *      <Ki>:<OPc>:<SQN> format
 +       */
 +      char *milenage;
 +
 +      /**
 +       * 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. Multiple domain entries
 +       * can be used to configure alternative FQDNs that will be considered
 +       * home networks.
 +       */
 +      char **domain;
 +
 +      /**
 +       * num_domain - Number of FQDNs in the domain array
 +       */
 +      size_t num_domain;
 +
 +      /**
 +       * roaming_consortium - Roaming Consortium OI
 +       *
 +       * If roaming_consortium_len is non-zero, this field contains the
 +       * Roaming Consortium OI that can be used to determine which access
 +       * points support authentication with this credential. This is an
 +       * alternative to the use of the realm parameter. When using Roaming
 +       * Consortium to match the network, the EAP parameters need to be
 +       * pre-configured with the credential since the NAI Realm information
 +       * may not be available or fetched.
 +       */
 +      u8 roaming_consortium[15];
 +
 +      /**
 +       * roaming_consortium_len - Length of roaming_consortium
 +       */
 +      size_t roaming_consortium_len;
 +
 +      u8 required_roaming_consortium[15];
 +      size_t required_roaming_consortium_len;
 +
 +      /**
 +       * eap_method - EAP method to use
 +       *
 +       * Pre-configured EAP method to use with this credential or %NULL to
 +       * indicate no EAP method is selected, i.e., the method will be
 +       * selected automatically based on ANQP information.
 +       */
 +      struct eap_method_type *eap_method;
 +
 +      /**
 +       * phase1 - Phase 1 (outer authentication) parameters
 +       *
 +       * Pre-configured EAP parameters or %NULL.
 +       */
 +      char *phase1;
 +
 +      /**
 +       * phase2 - Phase 2 (inner authentication) parameters
 +       *
 +       * Pre-configured EAP parameters or %NULL.
 +       */
 +      char *phase2;
 +
 +      struct excluded_ssid {
++              u8 ssid[SSID_MAX_LEN];
 +              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;
 +};
 +
 +
 +#define CFG_CHANGED_DEVICE_NAME BIT(0)
 +#define CFG_CHANGED_CONFIG_METHODS BIT(1)
 +#define CFG_CHANGED_DEVICE_TYPE BIT(2)
 +#define CFG_CHANGED_OS_VERSION BIT(3)
 +#define CFG_CHANGED_UUID BIT(4)
 +#define CFG_CHANGED_COUNTRY BIT(5)
 +#define CFG_CHANGED_SEC_DEVICE_TYPE BIT(6)
 +#define CFG_CHANGED_P2P_SSID_POSTFIX BIT(7)
 +#define CFG_CHANGED_WPS_STRING BIT(8)
 +#define CFG_CHANGED_P2P_INTRA_BSS BIT(9)
 +#define CFG_CHANGED_VENDOR_EXTENSION BIT(10)
 +#define CFG_CHANGED_P2P_LISTEN_CHANNEL BIT(11)
 +#define CFG_CHANGED_P2P_OPER_CHANNEL BIT(12)
 +#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
 + *
 + * This data structure is presents the per-interface (radio) configuration
 + * data. In many cases, there is only one struct wpa_config instance, but if
 + * more than one network interface is being controlled, one instance is used
 + * for each.
 + */
 +struct wpa_config {
 +      /**
 +       * ssid - Head of the global network list
 +       *
 +       * This is the head for the list of all the configured networks.
 +       */
 +      struct wpa_ssid *ssid;
 +
 +      /**
 +       * pssid - Per-priority network lists (in priority order)
 +       */
 +      struct wpa_ssid **pssid;
 +
 +      /**
 +       * num_prio - Number of different priorities used in the pssid lists
 +       *
 +       * This indicates how many per-priority network lists are included in
 +       * pssid.
 +       */
 +      int num_prio;
 +
 +      /**
 +       * cred - Head of the credential list
 +       *
 +       * This is the head for the list of all the configured credentials.
 +       */
 +      struct wpa_cred *cred;
 +
 +      /**
 +       * eapol_version - IEEE 802.1X/EAPOL version number
 +       *
 +       * wpa_supplicant is implemented based on IEEE Std 802.1X-2004 which
 +       * defines EAPOL version 2. However, there are many APs that do not
 +       * handle the new version number correctly (they seem to drop the
 +       * frames completely). In order 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).
 +       */
 +      int eapol_version;
 +
 +      /**
 +       * ap_scan - AP scanning/selection
 +       *
 +       * By default, wpa_supplicant requests driver to perform AP
 +       * scanning and then uses the scan results to select a
 +       * suitable AP. Another alternative is to allow the driver to
 +       * take care of AP scanning and selection and use
 +       * wpa_supplicant just to process EAPOL frames based on IEEE
 +       * 802.11 association information from the driver.
 +       *
 +       * 1: wpa_supplicant initiates scanning and AP selection (default).
 +       *
 +       * 0: Driver takes care of scanning, AP selection, and IEEE 802.11
 +       * association parameters (e.g., WPA IE generation); this mode can
 +       * also be used with 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.
 +       *
 +       * 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, the network blocks in the configuration are tried
 +       * one by one until the driver reports successful association; each
 +       * network block should have explicit security policy (i.e., only one
 +       * option in the lists) for key_mgmt, pairwise, group, proto variables.
++       *
++       * Note: ap_scan=2 should not be used with the nl80211 driver interface
++       * (the current Linux interface). ap_scan=1 is optimized work working
++       * with nl80211. For finding networks using hidden SSID, scan_ssid=1 in
++       * the network block can be used with nl80211.
 +       */
 +      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
 +       * indicates support for this (sched_scan). This configuration
 +       * parameter can be used to disable this offloading mechanism.
 +       */
 +      int disable_scan_offload;
 +
 +      /**
 +       * ctrl_interface - Parameters for the control interface
 +       *
 +       * If this is specified, %wpa_supplicant will open a control interface
 +       * that is available for external programs to manage %wpa_supplicant.
 +       * The meaning of this string depends on which control interface
 +       * mechanism is used. For all cases, the existence of this parameter
 +       * in configuration is used to determine whether the control interface
 +       * is enabled.
 +       *
 +       * For UNIX domain sockets (default on Linux and BSD): This is a
 +       * directory that will be created for UNIX domain sockets for listening
 +       * to requests from external programs (CLI/GUI, etc.) for status
 +       * information and configuration. The socket file will be named based
 +       * on the interface name, so multiple %wpa_supplicant processes can be
 +       * run at the same time if more than one interface is used.
 +       * /var/run/wpa_supplicant is the recommended directory for sockets and
 +       * by default, wpa_cli will use it when trying to connect with
 +       * %wpa_supplicant.
 +       *
 +       * Access control for the control interface can be configured
 +       * by setting the directory to allow only members of a group
 +       * to use sockets. This way, it is possible to run
 +       * %wpa_supplicant as root (since it needs to change network
 +       * configuration and open raw sockets) and still allow GUI/CLI
 +       * components to be run as non-root users. However, since the
 +       * control interface can be used to change the network
 +       * configuration, this access needs to be protected in many
 +       * cases. By default, %wpa_supplicant is configured to use gid
 +       * 0 (root). If you want to allow non-root users to use the
 +       * control interface, add a new group and change this value to
 +       * match with that group. Add users that should have control
 +       * interface access to this group.
 +       *
 +       * When configuring both the directory and group, use following format:
 +       * DIR=/var/run/wpa_supplicant GROUP=wheel
 +       * DIR=/var/run/wpa_supplicant GROUP=0
 +       * (group can be either group name or gid)
 +       *
 +       * For UDP connections (default on Windows): The value will be ignored.
 +       * This variable is just used to select that the control interface is
 +       * to be created. The value can be set to, e.g., udp
 +       * (ctrl_interface=udp).
 +       *
 +       * For Windows Named Pipe: This value can be used to set the security
 +       * descriptor for controlling access to the control interface. Security
 +       * descriptor can be set using Security Descriptor String Format (see
 +       * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/secauthz/security/security_descriptor_string_format.asp).
 +       * The descriptor string needs to be prefixed with SDDL=. For example,
 +       * ctrl_interface=SDDL=D: would set an empty DACL (which will reject
 +       * all connections).
 +       */
 +      char *ctrl_interface;
 +
 +      /**
 +       * ctrl_interface_group - Control interface group (DEPRECATED)
 +       *
 +       * This variable is only used for backwards compatibility. Group for
 +       * UNIX domain sockets should now be specified using GROUP=group in
 +       * ctrl_interface variable.
 +       */
 +      char *ctrl_interface_group;
 +
 +      /**
 +       * fast_reauth - EAP fast re-authentication (session resumption)
 +       *
 +       * By default, fast re-authentication is enabled for all EAP methods
 +       * that support it. This variable can be used to disable fast
 +       * re-authentication (by setting fast_reauth=0). Normally, there is no
 +       * need to disable fast re-authentication.
 +       */
 +      int fast_reauth;
 +
 +      /**
 +       * opensc_engine_path - Path to the OpenSSL engine for opensc
 +       *
 +       * This is an OpenSSL specific configuration option for loading OpenSC
 +       * engine (engine_opensc.so); if %NULL, this engine is not loaded.
 +       */
 +      char *opensc_engine_path;
 +
 +      /**
 +       * pkcs11_engine_path - Path to the OpenSSL engine for PKCS#11
 +       *
 +       * This is an OpenSSL specific configuration option for loading PKCS#11
 +       * engine (engine_pkcs11.so); if %NULL, this engine is not loaded.
 +       */
 +      char *pkcs11_engine_path;
 +
 +      /**
 +       * pkcs11_module_path - Path to the OpenSSL OpenSC/PKCS#11 module
 +       *
 +       * This is an OpenSSL specific configuration option for configuring
 +       * path to OpenSC/PKCS#11 engine (opensc-pkcs11.so); if %NULL, this
 +       * module is not loaded.
 +       */
 +      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
 +       * initialized for SIM/USIM access. Empty string can be used to match
 +       * the first available reader.
 +       */
 +      char *pcsc_reader;
 +
 +      /**
 +       * pcsc_pin - PIN for USIM, GSM SIM, and smartcards
 +       *
 +       * This field is used to configure PIN for SIM/USIM for EAP-SIM and
 +       * EAP-AKA. If left out, this will be asked through control interface.
 +       */
 +      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
 +       * optional struct wpa_driver_ops::set_param() handler. This can be
 +       * used to configure driver specific options without having to add new
 +       * driver interface functionality.
 +       */
 +      char *driver_param;
 +
 +      /**
 +       * dot11RSNAConfigPMKLifetime - Maximum lifetime of a PMK
 +       *
 +       * dot11 MIB variable for the maximum lifetime of a PMK in the PMK
 +       * cache (unit: seconds).
 +       */
 +      unsigned int dot11RSNAConfigPMKLifetime;
 +
 +      /**
 +       * dot11RSNAConfigPMKReauthThreshold - PMK re-authentication threshold
 +       *
 +       * dot11 MIB variable for the percentage of the PMK lifetime
 +       * that should expire before an IEEE 802.1X reauthentication occurs.
 +       */
 +      unsigned int dot11RSNAConfigPMKReauthThreshold;
 +
 +      /**
 +       * dot11RSNAConfigSATimeout - Security association timeout
 +       *
 +       * dot11 MIB variable for the maximum time a security association
 +       * shall take to set up (unit: seconds).
 +       */
 +      unsigned int dot11RSNAConfigSATimeout;
 +
 +      /**
 +       * update_config - Is wpa_supplicant allowed to update configuration
 +       *
 +       * This variable control whether wpa_supplicant is allow to re-write
 +       * its configuration with wpa_config_write(). If this is zero,
 +       * configuration data is only changed in memory and the external data
 +       * is not overriden. If this is non-zero, wpa_supplicant will update
 +       * the configuration data (e.g., a file) whenever configuration is
 +       * changed. This update may replace the old configuration which can
 +       * remove comments from it in case of a text file configuration.
 +       */
 +      int update_config;
 +
 +      /**
 +       * blobs - Configuration blobs
 +       */
 +      struct wpa_config_blob *blobs;
 +
 +      /**
 +       * uuid - Universally Unique IDentifier (UUID; see RFC 4122) for WPS
 +       */
 +      u8 uuid[16];
 +
 +      /**
 +       * device_name - Device Name (WPS)
 +       * User-friendly description of device; up to 32 octets encoded in
 +       * UTF-8
 +       */
 +      char *device_name;
 +
 +      /**
 +       * manufacturer - Manufacturer (WPS)
 +       * The manufacturer of the device (up to 64 ASCII characters)
 +       */
 +      char *manufacturer;
 +
 +      /**
 +       * model_name - Model Name (WPS)
 +       * Model of the device (up to 32 ASCII characters)
 +       */
 +      char *model_name;
 +
 +      /**
 +       * model_number - Model Number (WPS)
 +       * Additional device description (up to 32 ASCII characters)
 +       */
 +      char *model_number;
 +
 +      /**
 +       * serial_number - Serial Number (WPS)
 +       * Serial number of the device (up to 32 characters)
 +       */
 +      char *serial_number;
 +
 +      /**
 +       * device_type - Primary Device Type (WPS)
 +       */
 +      u8 device_type[WPS_DEV_TYPE_LEN];
 +
 +      /**
 +       * config_methods - Config Methods
 +       *
 +       * This is a space-separated list of supported WPS configuration
 +       * methods. For example, "label virtual_display virtual_push_button
 +       * keypad".
 +       * 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.
 +       */
 +      char *config_methods;
 +
 +      /**
 +       * os_version - OS Version (WPS)
 +       * 4-octet operating system version number
 +       */
 +      u8 os_version[4];
 +
 +      /**
 +       * country - Country code
 +       *
 +       * This is the ISO/IEC alpha2 country code for which we are operating
 +       * in
 +       */
 +      char country[2];
 +
 +      /**
 +       * wps_cred_processing - Credential processing
 +       *
 +       *   0 = process received credentials internally
 +       *   1 = do not process received credentials; just pass them over
 +       *      ctrl_iface to external program(s)
 +       *   2 = process received credentials internally and pass them over
 +       *      ctrl_iface to external program(s)
 +       */
 +      int wps_cred_processing;
 +
 +#define MAX_SEC_DEVICE_TYPES 5
 +      /**
 +       * sec_device_types - Secondary Device Types (P2P)
 +       */
 +      u8 sec_device_type[MAX_SEC_DEVICE_TYPES][WPS_DEV_TYPE_LEN];
 +      int num_sec_device_types;
 +
 +      int p2p_listen_reg_class;
 +      int p2p_listen_channel;
 +      int p2p_oper_reg_class;
 +      int p2p_oper_channel;
 +      int p2p_go_intent;
 +      char *p2p_ssid_postfix;
 +      int persistent_reconnect;
 +      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;
 +
 +#define MAX_WPS_VENDOR_EXT 10
 +      /**
 +       * wps_vendor_ext - Vendor extension attributes in WPS
 +       */
 +      struct wpabuf *wps_vendor_ext[MAX_WPS_VENDOR_EXT];
 +
 +      /**
 +       * p2p_group_idle - Maximum idle time in seconds for P2P group
 +       *
 +       * This value controls how long a P2P group is maintained after there
 +       * is no other members in the group. As a GO, this means no associated
 +       * stations in the group. As a P2P client, this means no GO seen in
 +       * scan results. The maximum idle time is specified in seconds with 0
 +       * indicating no time limit, i.e., the P2P group remains in active
 +       * state indefinitely until explicitly removed. As a P2P client, the
 +       * maximum idle time of P2P_MAX_CLIENT_IDLE seconds is enforced, i.e.,
 +       * this parameter is mainly meant for GO use and for P2P client, it can
 +       * only be used to reduce the default timeout to smaller value. A
 +       * special value -1 can be used to configure immediate removal of the
 +       * group for P2P client role on any disconnection after the data
 +       * connection has been established.
 +       */
 +      int p2p_group_idle;
 +
++      /**
++       * p2p_go_freq_change_policy - The GO frequency change policy
++       *
++       * This controls the behavior of the GO when there is a change in the
++       * map of the currently used frequencies in case more than one channel
++       * is supported.
++       *
++       * @P2P_GO_FREQ_MOVE_SCM: Prefer working in a single channel mode if
++       * possible. In case the GO is the only interface using its frequency
++       * and there are other station interfaces on other frequencies, the GO
++       * will migrate to one of these frequencies.
++       *
++       * @P2P_GO_FREQ_MOVE_SCM_PEER_SUPPORTS: Same as P2P_GO_FREQ_MOVE_SCM,
++       * but a transition is possible only in case one of the other used
++       * frequencies is one of the frequencies in the intersection of the
++       * frequency list of the local device and the peer device.
++       *
++       * @P2P_GO_FREQ_MOVE_STAY: Prefer to stay on the current frequency.
++       */
++      enum {
++              P2P_GO_FREQ_MOVE_SCM = 0,
++              P2P_GO_FREQ_MOVE_SCM_PEER_SUPPORTS = 1,
++              P2P_GO_FREQ_MOVE_STAY = 2,
++              P2P_GO_FREQ_MOVE_MAX = P2P_GO_FREQ_MOVE_STAY,
++      } p2p_go_freq_change_policy;
++
++#define DEFAULT_P2P_GO_FREQ_MOVE P2P_GO_FREQ_MOVE_STAY
++
 +      /**
 +       * 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;
 +
 +      /**
 +       * bss_expiration_age - BSS entry age after which it can be expired
 +       *
 +       * This value controls the time in seconds after which a BSS entry
 +       * gets removed if it has not been updated or is not in use.
 +       */
 +      unsigned int bss_expiration_age;
 +
 +      /**
 +       * bss_expiration_scan_count - Expire BSS after number of scans
 +       *
 +       * If the BSS entry has not been seen in this many scans, it will be
 +       * removed. A value of 1 means that entry is removed after the first
 +       * scan in which the BSSID is not seen. Larger values can be used
 +       * to avoid BSS entries disappearing if they are not visible in
 +       * every scan (e.g., low signal quality or interference).
 +       */
 +      unsigned int bss_expiration_scan_count;
 +
 +      /**
 +       * filter_ssids - SSID-based scan result filtering
 +       *
 +       *   0 = do not filter scan results
 +       *   1 = only include configured SSIDs in scan results/BSS table
 +       */
 +      int filter_ssids;
 +
 +      /**
 +       * filter_rssi - RSSI-based scan result filtering
 +       *
 +       * 0 = do not filter scan results
 +       * -n = filter scan results below -n dBm
 +       */
 +      int filter_rssi;
 +
 +      /**
 +       * max_num_sta - Maximum number of STAs in an AP/P2P GO
 +       */
 +      unsigned int max_num_sta;
 +
 +      /**
 +       * freq_list - Array of allowed scan frequencies or %NULL for all
 +       *
 +       * This is an optional zero-terminated array of frequencies in
 +       * megahertz (MHz) to allow for narrowing scanning range.
 +       */
 +      int *freq_list;
 +
 +      /**
 +       * scan_cur_freq - Whether to scan only the current channel
 +       *
 +       * If true, attempt to scan only the current channel if any other
 +       * VIFs on this radio are already associated on a particular channel.
 +       */
 +      int scan_cur_freq;
 +
 +      /**
 +       * changed_parameters - Bitmap of changed parameters since last update
 +       */
 +      unsigned int changed_parameters;
 +
 +      /**
 +       * disassoc_low_ack - Disassocicate stations with massive packet loss
 +       */
 +      int disassoc_low_ack;
 +
 +      /**
 +       * interworking - Whether Interworking (IEEE 802.11u) is enabled
 +       */
 +      int interworking;
 +
 +      /**
 +       * access_network_type - Access Network Type
 +       *
 +       * When Interworking is enabled, scans will be limited to APs that
 +       * advertise the specified Access Network Type (0..15; with 15
 +       * indicating wildcard match).
 +       */
 +      int access_network_type;
 +
 +      /**
 +       * hessid - Homogenous ESS identifier
 +       *
 +       * If this is set (any octet is non-zero), scans will be used to
 +       * request response only from BSSes belonging to the specified
 +       * Homogeneous ESS. This is used only if interworking is enabled.
 +       */
 +      u8 hessid[ETH_ALEN];
 +
 +      /**
 +       * hs20 - Hotspot 2.0
 +       */
 +      int hs20;
 +
 +      /**
 +       * pbc_in_m1 - AP mode WPS probing workaround for PBC with Windows 7
 +       *
 +       * Windows 7 uses incorrect way of figuring out AP's WPS capabilities
 +       * by acting as a Registrar and using M1 from the AP. The config
 +       * methods attribute in that message is supposed to indicate only the
 +       * configuration method supported by the AP in Enrollee role, i.e., to
 +       * add an external Registrar. For that case, PBC shall not be used and
 +       * as such, the PushButton config method is removed from M1 by default.
 +       * If pbc_in_m1=1 is included in the configuration file, the PushButton
 +       * config method is left in M1 (if included in config_methods
 +       * parameter) to allow Windows 7 to use PBC instead of PIN (e.g., from
 +       * a label in the AP).
 +       */
 +      int pbc_in_m1;
 +
 +      /**
 +       * autoscan - Automatic scan parameters or %NULL if none
 +       *
 +       * This is an optional set of parameters for automatic scanning
 +       * within an interface in following format:
 +       * <autoscan module name>:<module parameters>
 +       */
 +      char *autoscan;
 +
 +      /**
 +       * wps_nfc_pw_from_config - NFC Device Password was read from config
 +       *
 +       * This parameter can be determined whether the NFC Device Password was
 +       * included in the configuration (1) or generated dynamically (0). Only
 +       * the former case is re-written back to the configuration file.
 +       */
 +      int wps_nfc_pw_from_config;
 +
 +      /**
 +       * wps_nfc_dev_pw_id - NFC Device Password ID for password token
 +       */
 +      int wps_nfc_dev_pw_id;
 +
 +      /**
 +       * wps_nfc_dh_pubkey - NFC DH Public Key for password token
 +       */
 +      struct wpabuf *wps_nfc_dh_pubkey;
 +
 +      /**
 +       * wps_nfc_dh_privkey - NFC DH Private Key for password token
 +       */
 +      struct wpabuf *wps_nfc_dh_privkey;
 +
 +      /**
 +       * wps_nfc_dev_pw - NFC Device Password for password token
 +       */
 +      struct wpabuf *wps_nfc_dev_pw;
 +
 +      /**
 +       * ext_password_backend - External password backend or %NULL if none
 +       *
 +       * format: <backend name>[:<optional backend parameters>]
 +       */
 +      char *ext_password_backend;
 +
 +      /*
 +       * p2p_go_max_inactivity - Timeout in seconds to detect STA inactivity
 +       *
 +       * This timeout value is used in P2P GO mode to clean up
 +       * inactive stations.
 +       * By default: 300 seconds.
 +       */
 +      int p2p_go_max_inactivity;
 +
 +      struct hostapd_wmm_ac_params wmm_ac_params[4];
 +
 +      /**
 +       * auto_interworking - Whether to use network selection automatically
 +       *
 +       * 0 = do not automatically go through Interworking network selection
 +       *     (i.e., require explicit interworking_select command for this)
 +       * 1 = perform Interworking network selection if one or more
 +       *     credentials have been configured and scan did not find a
 +       *     matching network block
 +       */
 +      int auto_interworking;
 +
 +      /**
 +       * p2p_go_ht40 - Default mode for HT40 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_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;
 +
 +      /**
 +       * p2p_no_group_iface - Whether group interfaces can be used
 +       *
 +       * By default, wpa_supplicant will create a separate interface for P2P
 +       * group operations if the driver supports this. This functionality can
 +       * be disabled by setting this parameter to 1. In that case, the same
 +       * interface that was used for the P2P management operations is used
 +       * also for the group operation.
 +       */
 +      int p2p_no_group_iface;
 +
++      /**
++       * p2p_cli_probe - Enable/disable P2P CLI probe request handling
++       *
++       * If this parameter is set to 1, a connected P2P Client will receive
++       * and handle Probe Request frames. Setting this parameter to 0
++       * disables this option. Default value: 0.
++       *
++       * Note: Setting this property at run time takes effect on the following
++       * interface state transition to/from the WPA_COMPLETED state.
++       */
++      int p2p_cli_probe;
++
 +      /**
 +       * okc - Whether to enable opportunistic key caching by default
 +       *
 +       * By default, OKC is disabled unless enabled by the per-network
 +       * proactive_key_caching=1 parameter. okc=1 can be used to change this
 +       * default behavior.
 +       */
 +      int okc;
 +
 +      /**
 +       * pmf - Whether to enable/require PMF by default
 +       *
 +       * By default, PMF is disabled unless enabled by the per-network
 +       * ieee80211w=1 or ieee80211w=2 parameter. pmf=1/2 can be used to change
 +       * this default behavior.
 +       */
 +      enum mfp_options pmf;
 +
 +      /**
 +       * sae_groups - Preference list of enabled groups for SAE
 +       *
 +       * By default (if this parameter is not set), the mandatory group 19
 +       * (ECC group defined over a 256-bit prime order field) is preferred,
 +       * but other groups are also enabled. If this parameter is set, the
 +       * groups will be tried in the indicated order.
 +       */
 +      int *sae_groups;
 +
 +      /**
 +       * dtim_period - Default DTIM period in Beacon intervals
 +       *
 +       * This parameter can be used to set the default value for network
 +       * blocks that do not specify dtim_period.
 +       */
 +      int dtim_period;
 +
 +      /**
 +       * beacon_int - Default Beacon interval in TU
 +       *
 +       * This parameter can be used to set the default value for network
 +       * blocks that do not specify beacon_int.
 +       */
 +      int beacon_int;
 +
 +      /**
 +       * ap_vendor_elements: Vendor specific elements for Beacon/ProbeResp
 +       *
 +       * This parameter can be used to define additional vendor specific
 +       * elements for Beacon and Probe Response frames in AP/P2P GO mode. The
 +       * format for these element(s) is a hexdump of the raw information
 +       * elements (id+len+payload for one or more elements).
 +       */
 +      struct wpabuf *ap_vendor_elements;
 +
 +      /**
 +       * ignore_old_scan_res - Ignore scan results older than request
 +       *
 +       * The driver may have a cache of scan results that makes it return
 +       * information that is older than our scan trigger. This parameter can
 +       * be used to configure such old information to be ignored instead of
 +       * allowing it to update the internal BSS table.
 +       */
 +      int ignore_old_scan_res;
 +
 +      /**
 +       * 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;
 +
++      /**
++       * dot11RSNASAERetransPeriod - Timeout to retransmit SAE Auth frame
++       *
++       * This timeout value is used in mesh STA to retransmit
++       * SAE Authentication frame.
++       * By default: 1000 milliseconds.
++       */
++      int dot11RSNASAERetransPeriod;
++
 +      /**
 +       * 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;
++
++      /**
++       * wps_priority - Priority for the networks added through WPS
++       *
++       * This priority value will be set to each network profile that is added
++       * by executing the WPS protocol.
++       */
++      int wps_priority;
++
++      /**
++       * fst_group_id - FST group ID
++       */
++      char *fst_group_id;
++
++      /**
++       * fst_priority - priority of the interface within the FST group
++       */
++      int fst_priority;
++
++      /**
++       * fst_llt - default FST LLT (Link-Lost Timeout) to be used for the
++       * interface.
++       */
++      int fst_llt;
 +};
 +
 +
 +/* Prototypes for common functions from config.c */
 +
 +void wpa_config_free(struct wpa_config *ssid);
 +void wpa_config_free_ssid(struct wpa_ssid *ssid);
 +void wpa_config_foreach_network(struct wpa_config *config,
 +                              void (*func)(void *, struct wpa_ssid *),
 +                              void *arg);
 +struct wpa_ssid * wpa_config_get_network(struct wpa_config *config, int id);
 +struct wpa_ssid * wpa_config_add_network(struct wpa_config *config);
 +int wpa_config_remove_network(struct wpa_config *config, int id);
 +void wpa_config_set_network_defaults(struct wpa_ssid *ssid);
 +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);
 +void wpa_config_update_psk(struct wpa_ssid *ssid);
 +int wpa_config_add_prio_network(struct wpa_config *config,
 +                              struct wpa_ssid *ssid);
 +int wpa_config_update_prio_list(struct wpa_config *config);
 +const struct wpa_config_blob * wpa_config_get_blob(struct wpa_config *config,
 +                                                 const char *name);
 +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);
 +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);
 +#ifndef CONFIG_NO_STDOUT_DEBUG
 +void wpa_config_debug_dump_networks(struct wpa_config *config);
 +#else /* CONFIG_NO_STDOUT_DEBUG */
 +#define wpa_config_debug_dump_networks(c) do { } while (0)
 +#endif /* CONFIG_NO_STDOUT_DEBUG */
 +
 +
 +/* Prototypes for common functions from config.c */
 +int wpa_config_process_global(struct wpa_config *config, char *pos, int line);
 +
 +
 +/* Prototypes for backend specific functions from the selected config_*.c */
 +
 +/**
 + * wpa_config_read - Read and parse configuration database
 + * @name: Name of the configuration (e.g., path and file name for the
 + * configuration file)
 + * @cfgp: Pointer to previously allocated configuration data or %NULL if none
 + * Returns: Pointer to allocated configuration data or %NULL on failure
 + *
 + * This function reads configuration data, parses its contents, and allocates
 + * data structures needed for storing configuration information. The allocated
 + * data can be freed with wpa_config_free().
 + *
 + * Each configuration backend needs to implement this function.
 + */
 +struct wpa_config * wpa_config_read(const char *name, struct wpa_config *cfgp);
 +
 +/**
 + * wpa_config_write - Write or update configuration data
 + * @name: Name of the configuration (e.g., path and file name for the
 + * configuration file)
 + * @config: Configuration data from wpa_config_read()
 + * Returns: 0 on success, -1 on failure
 + *
 + * This function write all configuration data into an external database (e.g.,
 + * a text file) in a format that can be read with wpa_config_read(). This can
 + * be used to allow wpa_supplicant to update its configuration, e.g., when a
 + * new network is added or a password is changed.
 + *
 + * Each configuration backend needs to implement this function.
 + */
 +int wpa_config_write(const char *name, struct wpa_config *config);
 +
 +#endif /* CONFIG_H */
index 3d3a6e404fd8a9f47448b554d90661d7cb9f6619,0000000000000000000000000000000000000000..fb438ea43e13f07b110b7004632f49e16f522a92
mode 100644,000000..100644
--- /dev/null
@@@ -1,1366 -1,0 +1,1387 @@@
-       char *value = wpa_config_get(ssid, "psk");
 +/*
 + * WPA Supplicant / Configuration backend: text file
 + * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi>
 + *
 + * This software may be distributed under the terms of the BSD license.
 + * See README for more details.
 + *
 + * This file implements a configuration backend for text files. All the
 + * configuration information is stored in a text file that uses a format
 + * described in the sample configuration file, wpa_supplicant.conf.
 + */
 +
 +#include "includes.h"
 +#ifdef ANDROID
 +#include <sys/stat.h>
 +#endif /* ANDROID */
 +
 +#include "common.h"
 +#include "config.h"
 +#include "base64.h"
 +#include "uuid.h"
 +#include "p2p/p2p.h"
 +#include "eap_peer/eap_methods.h"
 +#include "eap_peer/eap.h"
 +
 +
 +static int newline_terminated(const char *buf, size_t buflen)
 +{
 +      size_t len = os_strlen(buf);
 +      if (len == 0)
 +              return 0;
 +      if (len == buflen - 1 && buf[buflen - 1] != '\r' &&
 +          buf[len - 1] != '\n')
 +              return 0;
 +      return 1;
 +}
 +
 +
 +static void skip_line_end(FILE *stream)
 +{
 +      char buf[100];
 +      while (fgets(buf, sizeof(buf), stream)) {
 +              buf[sizeof(buf) - 1] = '\0';
 +              if (newline_terminated(buf, sizeof(buf)))
 +                      return;
 +      }
 +}
 +
 +
 +/**
 + * wpa_config_get_line - Read the next configuration file line
 + * @s: Buffer for the line
 + * @size: The buffer length
 + * @stream: File stream to read from
 + * @line: Pointer to a variable storing the file line number
 + * @_pos: Buffer for the pointer to the beginning of data on the text line or
 + * %NULL if not needed (returned value used instead)
 + * Returns: Pointer to the beginning of data on the text line or %NULL if no
 + * more text lines are available.
 + *
 + * This function reads the next non-empty line from the configuration file and
 + * removes comments. The returned string is guaranteed to be null-terminated.
 + */
 +static char * wpa_config_get_line(char *s, int size, FILE *stream, int *line,
 +                                char **_pos)
 +{
 +      char *pos, *end, *sstart;
 +
 +      while (fgets(s, size, stream)) {
 +              (*line)++;
 +              s[size - 1] = '\0';
 +              if (!newline_terminated(s, size)) {
 +                      /*
 +                       * The line was truncated - skip rest of it to avoid
 +                       * confusing error messages.
 +                       */
 +                      wpa_printf(MSG_INFO, "Long line in configuration file "
 +                                 "truncated");
 +                      skip_line_end(stream);
 +              }
 +              pos = s;
 +
 +              /* Skip white space from the beginning of line. */
 +              while (*pos == ' ' || *pos == '\t' || *pos == '\r')
 +                      pos++;
 +
 +              /* Skip comment lines and empty lines */
 +              if (*pos == '#' || *pos == '\n' || *pos == '\0')
 +                      continue;
 +
 +              /*
 +               * Remove # comments unless they are within a double quoted
 +               * string.
 +               */
 +              sstart = os_strchr(pos, '"');
 +              if (sstart)
 +                      sstart = os_strrchr(sstart + 1, '"');
 +              if (!sstart)
 +                      sstart = pos;
 +              end = os_strchr(sstart, '#');
 +              if (end)
 +                      *end-- = '\0';
 +              else
 +                      end = pos + os_strlen(pos) - 1;
 +
 +              /* Remove trailing white space. */
 +              while (end > pos &&
 +                     (*end == '\n' || *end == ' ' || *end == '\t' ||
 +                      *end == '\r'))
 +                      *end-- = '\0';
 +
 +              if (*pos == '\0')
 +                      continue;
 +
 +              if (_pos)
 +                      *_pos = pos;
 +              return pos;
 +      }
 +
 +      if (_pos)
 +              *_pos = NULL;
 +      return NULL;
 +}
 +
 +
 +static int wpa_config_validate_network(struct wpa_ssid *ssid, int line)
 +{
 +      int errors = 0;
 +
 +      if (ssid->passphrase) {
 +              if (ssid->psk_set) {
 +                      wpa_printf(MSG_ERROR, "Line %d: both PSK and "
 +                                 "passphrase configured.", line);
 +                      errors++;
 +              }
 +              wpa_config_update_psk(ssid);
 +      }
 +
 +      if ((ssid->group_cipher & WPA_CIPHER_CCMP) &&
 +          !(ssid->pairwise_cipher & WPA_CIPHER_CCMP) &&
 +          !(ssid->pairwise_cipher & WPA_CIPHER_NONE)) {
 +              /* Group cipher cannot be stronger than the pairwise cipher. */
 +              wpa_printf(MSG_DEBUG, "Line %d: removed CCMP from group cipher"
 +                         " list since it was not allowed for pairwise "
 +                         "cipher", 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;
 +}
 +
 +
 +static struct wpa_ssid * wpa_config_read_network(FILE *f, int *line, int id)
 +{
 +      struct wpa_ssid *ssid;
 +      int errors = 0, end = 0;
 +      char buf[2000], *pos, *pos2;
 +
 +      wpa_printf(MSG_MSGDUMP, "Line: %d - start of a new network block",
 +                 *line);
 +      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);
 +
 +      while (wpa_config_get_line(buf, sizeof(buf), f, line, &pos)) {
 +              if (os_strcmp(pos, "}") == 0) {
 +                      end = 1;
 +                      break;
 +              }
 +
 +              pos2 = os_strchr(pos, '=');
 +              if (pos2 == NULL) {
 +                      wpa_printf(MSG_ERROR, "Line %d: Invalid SSID line "
 +                                 "'%s'.", *line, pos);
 +                      errors++;
 +                      continue;
 +              }
 +
 +              *pos2++ = '\0';
 +              if (*pos2 == '"') {
 +                      if (os_strchr(pos2 + 1, '"') == NULL) {
 +                              wpa_printf(MSG_ERROR, "Line %d: invalid "
 +                                         "quotation '%s'.", *line, pos2);
 +                              errors++;
 +                              continue;
 +                      }
 +              }
 +
 +              if (wpa_config_set(ssid, pos, pos2, *line) < 0)
 +                      errors++;
 +      }
 +
 +      if (!end) {
 +              wpa_printf(MSG_ERROR, "Line %d: network block was not "
 +                         "terminated properly.", *line);
 +              errors++;
 +      }
 +
 +      errors += wpa_config_validate_network(ssid, *line);
 +
 +      if (errors) {
 +              wpa_config_free_ssid(ssid);
 +              ssid = NULL;
 +      }
 +
 +      return ssid;
 +}
 +
 +
 +static struct wpa_cred * wpa_config_read_cred(FILE *f, int *line, int id)
 +{
 +      struct wpa_cred *cred;
 +      int errors = 0, end = 0;
 +      char buf[256], *pos, *pos2;
 +
 +      wpa_printf(MSG_MSGDUMP, "Line: %d - start of a new cred block", *line);
 +      cred = os_zalloc(sizeof(*cred));
 +      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) {
 +                      end = 1;
 +                      break;
 +              }
 +
 +              pos2 = os_strchr(pos, '=');
 +              if (pos2 == NULL) {
 +                      wpa_printf(MSG_ERROR, "Line %d: Invalid cred line "
 +                                 "'%s'.", *line, pos);
 +                      errors++;
 +                      continue;
 +              }
 +
 +              *pos2++ = '\0';
 +              if (*pos2 == '"') {
 +                      if (os_strchr(pos2 + 1, '"') == NULL) {
 +                              wpa_printf(MSG_ERROR, "Line %d: invalid "
 +                                         "quotation '%s'.", *line, pos2);
 +                              errors++;
 +                              continue;
 +                      }
 +              }
 +
 +              if (wpa_config_set_cred(cred, pos, pos2, *line) < 0)
 +                      errors++;
 +      }
 +
 +      if (!end) {
 +              wpa_printf(MSG_ERROR, "Line %d: cred block was not "
 +                         "terminated properly.", *line);
 +              errors++;
 +      }
 +
 +      if (errors) {
 +              wpa_config_free_cred(cred);
 +              cred = NULL;
 +      }
 +
 +      return cred;
 +}
 +
 +
 +#ifndef CONFIG_NO_CONFIG_BLOBS
 +static struct wpa_config_blob * wpa_config_read_blob(FILE *f, int *line,
 +                                                   const char *name)
 +{
 +      struct wpa_config_blob *blob;
 +      char buf[256], *pos;
 +      unsigned char *encoded = NULL, *nencoded;
 +      int end = 0;
 +      size_t encoded_len = 0, len;
 +
 +      wpa_printf(MSG_MSGDUMP, "Line: %d - start of a new named blob '%s'",
 +                 *line, name);
 +
 +      while (wpa_config_get_line(buf, sizeof(buf), f, line, &pos)) {
 +              if (os_strcmp(pos, "}") == 0) {
 +                      end = 1;
 +                      break;
 +              }
 +
 +              len = os_strlen(pos);
 +              nencoded = os_realloc(encoded, encoded_len + len);
 +              if (nencoded == NULL) {
 +                      wpa_printf(MSG_ERROR, "Line %d: not enough memory for "
 +                                 "blob", *line);
 +                      os_free(encoded);
 +                      return NULL;
 +              }
 +              encoded = nencoded;
 +              os_memcpy(encoded + encoded_len, pos, len);
 +              encoded_len += len;
 +      }
 +
 +      if (!end) {
 +              wpa_printf(MSG_ERROR, "Line %d: blob was not terminated "
 +                         "properly", *line);
 +              os_free(encoded);
 +              return NULL;
 +      }
 +
 +      blob = os_zalloc(sizeof(*blob));
 +      if (blob == NULL) {
 +              os_free(encoded);
 +              return NULL;
 +      }
 +      blob->name = os_strdup(name);
 +      blob->data = base64_decode(encoded, encoded_len, &blob->len);
 +      os_free(encoded);
 +
 +      if (blob->name == NULL || blob->data == NULL) {
 +              wpa_config_free_blob(blob);
 +              return NULL;
 +      }
 +
 +      return blob;
 +}
 +
 +
 +static int wpa_config_process_blob(struct wpa_config *config, FILE *f,
 +                                 int *line, char *bname)
 +{
 +      char *name_end;
 +      struct wpa_config_blob *blob;
 +
 +      name_end = os_strchr(bname, '=');
 +      if (name_end == NULL) {
 +              wpa_printf(MSG_ERROR, "Line %d: no blob name terminator",
 +                         *line);
 +              return -1;
 +      }
 +      *name_end = '\0';
 +
 +      blob = wpa_config_read_blob(f, line, bname);
 +      if (blob == NULL) {
 +              wpa_printf(MSG_ERROR, "Line %d: failed to read blob %s",
 +                         *line, bname);
 +              return -1;
 +      }
 +      wpa_config_set_blob(config, blob);
 +      return 0;
 +}
 +#endif /* CONFIG_NO_CONFIG_BLOBS */
 +
 +
 +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, *head;
 +      struct wpa_cred *cred, *cred_tail, *cred_head;
 +      struct wpa_config *config;
 +      int id = 0;
 +      int cred_id = 0;
 +
 +      if (name == NULL)
 +              return NULL;
 +      if (cfgp)
 +              config = cfgp;
 +      else
 +              config = wpa_config_alloc_empty(NULL, NULL);
 +      if (config == NULL) {
 +              wpa_printf(MSG_ERROR, "Failed to allocate config file "
 +                         "structure");
 +              return NULL;
 +      }
 +      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");
 +      if (f == NULL) {
 +              wpa_printf(MSG_ERROR, "Failed to open config file '%s', "
 +                         "error: %s", name, strerror(errno));
 +              os_free(config);
 +              return NULL;
 +      }
 +
 +      while (wpa_config_get_line(buf, sizeof(buf), f, &line, &pos)) {
 +              if (os_strcmp(pos, "network={") == 0) {
 +                      ssid = wpa_config_read_network(f, &line, id++);
 +                      if (ssid == NULL) {
 +                              wpa_printf(MSG_ERROR, "Line %d: failed to "
 +                                         "parse network block.", line);
 +                              errors++;
 +                              continue;
 +                      }
 +                      if (head == NULL) {
 +                              head = tail = ssid;
 +                      } else {
 +                              tail->next = ssid;
 +                              tail = ssid;
 +                      }
 +                      if (wpa_config_add_prio_network(config, ssid)) {
 +                              wpa_printf(MSG_ERROR, "Line %d: failed to add "
 +                                         "network block to priority list.",
 +                                         line);
 +                              errors++;
 +                              continue;
 +                      }
 +              } else if (os_strcmp(pos, "cred={") == 0) {
 +                      cred = wpa_config_read_cred(f, &line, cred_id++);
 +                      if (cred == NULL) {
 +                              wpa_printf(MSG_ERROR, "Line %d: failed to "
 +                                         "parse cred block.", line);
 +                              errors++;
 +                              continue;
 +                      }
 +                      if (cred_head == NULL) {
 +                              cred_head = cred_tail = cred;
 +                      } else {
 +                              cred_tail->next = cred;
 +                              cred_tail = cred;
 +                      }
 +#ifndef CONFIG_NO_CONFIG_BLOBS
 +              } else if (os_strncmp(pos, "blob-base64-", 12) == 0) {
 +                      if (wpa_config_process_blob(config, f, &line, pos + 12)
 +                          < 0) {
 +                              wpa_printf(MSG_ERROR, "Line %d: failed to "
 +                                         "process blob.", line);
 +                              errors++;
 +                              continue;
 +                      }
 +#endif /* CONFIG_NO_CONFIG_BLOBS */
 +              } else if (wpa_config_process_global(config, pos, line) < 0) {
 +                      wpa_printf(MSG_ERROR, "Line %d: Invalid configuration "
 +                                 "line '%s'.", line, pos);
 +                      errors++;
 +                      continue;
 +              }
 +      }
 +
 +      fclose(f);
 +
 +      config->ssid = head;
 +      wpa_config_debug_dump_networks(config);
 +      config->cred = cred_head;
 +
 +#ifndef WPA_IGNORE_CONFIG_ERRORS
 +      if (errors) {
 +              wpa_config_free(config);
 +              config = NULL;
 +              head = NULL;
 +      }
 +#endif /* WPA_IGNORE_CONFIG_ERRORS */
 +
 +      return config;
 +}
 +
 +
 +#ifndef CONFIG_NO_CONFIG_WRITE
 +
 +static void write_str(FILE *f, const char *field, struct wpa_ssid *ssid)
 +{
 +      char *value = wpa_config_get(ssid, field);
 +      if (value == NULL)
 +              return;
 +      fprintf(f, "\t%s=%s\n", field, value);
 +      os_free(value);
 +}
 +
 +
 +static void write_int(FILE *f, const char *field, int value, int def)
 +{
 +      if (value == def)
 +              return;
 +      fprintf(f, "\t%s=%d\n", field, value);
 +}
 +
 +
 +static void write_bssid(FILE *f, struct wpa_ssid *ssid)
 +{
 +      char *value = wpa_config_get(ssid, "bssid");
 +      if (value == NULL)
 +              return;
 +      fprintf(f, "\tbssid=%s\n", value);
 +      os_free(value);
 +}
 +
 +
 +static void write_psk(FILE *f, struct wpa_ssid *ssid)
 +{
-               fprintf(f, "dot11RSNAConfigPMKLifetime=%d\n",
++      char *value;
++
++      if (ssid->mem_only_psk)
++              return;
++
++      value = wpa_config_get(ssid, "psk");
 +      if (value == NULL)
 +              return;
 +      fprintf(f, "\tpsk=%s\n", value);
 +      os_free(value);
 +}
 +
 +
 +static void write_proto(FILE *f, struct wpa_ssid *ssid)
 +{
 +      char *value;
 +
 +      if (ssid->proto == DEFAULT_PROTO)
 +              return;
 +
 +      value = wpa_config_get(ssid, "proto");
 +      if (value == NULL)
 +              return;
 +      if (value[0])
 +              fprintf(f, "\tproto=%s\n", value);
 +      os_free(value);
 +}
 +
 +
 +static void write_key_mgmt(FILE *f, struct wpa_ssid *ssid)
 +{
 +      char *value;
 +
 +      if (ssid->key_mgmt == DEFAULT_KEY_MGMT)
 +              return;
 +
 +      value = wpa_config_get(ssid, "key_mgmt");
 +      if (value == NULL)
 +              return;
 +      if (value[0])
 +              fprintf(f, "\tkey_mgmt=%s\n", value);
 +      os_free(value);
 +}
 +
 +
 +static void write_pairwise(FILE *f, struct wpa_ssid *ssid)
 +{
 +      char *value;
 +
 +      if (ssid->pairwise_cipher == DEFAULT_PAIRWISE)
 +              return;
 +
 +      value = wpa_config_get(ssid, "pairwise");
 +      if (value == NULL)
 +              return;
 +      if (value[0])
 +              fprintf(f, "\tpairwise=%s\n", value);
 +      os_free(value);
 +}
 +
 +
 +static void write_group(FILE *f, struct wpa_ssid *ssid)
 +{
 +      char *value;
 +
 +      if (ssid->group_cipher == DEFAULT_GROUP)
 +              return;
 +
 +      value = wpa_config_get(ssid, "group");
 +      if (value == NULL)
 +              return;
 +      if (value[0])
 +              fprintf(f, "\tgroup=%s\n", value);
 +      os_free(value);
 +}
 +
 +
 +static void write_auth_alg(FILE *f, struct wpa_ssid *ssid)
 +{
 +      char *value;
 +
 +      if (ssid->auth_alg == 0)
 +              return;
 +
 +      value = wpa_config_get(ssid, "auth_alg");
 +      if (value == NULL)
 +              return;
 +      if (value[0])
 +              fprintf(f, "\tauth_alg=%s\n", value);
 +      os_free(value);
 +}
 +
 +
 +#ifdef IEEE8021X_EAPOL
 +static void write_eap(FILE *f, struct wpa_ssid *ssid)
 +{
 +      char *value;
 +
 +      value = wpa_config_get(ssid, "eap");
 +      if (value == NULL)
 +              return;
 +
 +      if (value[0])
 +              fprintf(f, "\teap=%s\n", value);
 +      os_free(value);
 +}
 +#endif /* IEEE8021X_EAPOL */
 +
 +
 +static void write_wep_key(FILE *f, int idx, struct wpa_ssid *ssid)
 +{
 +      char field[20], *value;
 +      int res;
 +
 +      res = os_snprintf(field, sizeof(field), "wep_key%d", idx);
 +      if (os_snprintf_error(sizeof(field), res))
 +              return;
 +      value = wpa_config_get(ssid, field);
 +      if (value) {
 +              fprintf(f, "\t%s=%s\n", field, value);
 +              os_free(value);
 +      }
 +}
 +
 +
 +#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");
 +      if (value == NULL)
 +              return;
 +      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 */
 +
 +
 +static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid)
 +{
 +      int i;
 +
 +#define STR(t) write_str(f, #t, ssid)
 +#define INT(t) write_int(f, #t, ssid->t, 0)
 +#define INTe(t) write_int(f, #t, ssid->eap.t, 0)
 +#define INT_DEF(t, def) write_int(f, #t, ssid->t, def)
 +#define INT_DEFe(t, def) write_int(f, #t, ssid->eap.t, def)
 +
 +      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);
++      INT(mem_only_psk);
 +      write_proto(f, ssid);
 +      write_key_mgmt(f, ssid);
 +      INT_DEF(bg_scan_period, DEFAULT_BG_SCAN_PERIOD);
 +      write_pairwise(f, ssid);
 +      write_group(f, ssid);
 +      write_auth_alg(f, ssid);
 +      STR(bgscan);
 +      STR(autoscan);
 +      STR(scan_freq);
 +#ifdef IEEE8021X_EAPOL
 +      write_eap(f, ssid);
 +      STR(identity);
 +      STR(anonymous_identity);
 +      STR(password);
 +      STR(ca_cert);
 +      STR(ca_path);
 +      STR(client_cert);
 +      STR(private_key);
 +      STR(private_key_passwd);
 +      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);
 +      STR(private_key2);
 +      STR(private_key2_passwd);
 +      STR(dh_file2);
 +      STR(subject_match2);
 +      STR(altsubject_match2);
 +      STR(domain_suffix_match2);
 +      STR(domain_match2);
 +      STR(phase1);
 +      STR(phase2);
 +      STR(pcsc);
 +      STR(pin);
 +      STR(engine_id);
 +      STR(key_id);
 +      STR(cert_id);
 +      STR(ca_cert_id);
 +      STR(key2_id);
 +      STR(pin2);
 +      STR(engine2_id);
 +      STR(cert2_id);
 +      STR(ca_cert2_id);
 +      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);
 +      INT(wep_tx_keyidx);
 +      INT(priority);
 +#ifdef IEEE8021X_EAPOL
 +      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
 +#undef INT_DEF
 +}
 +
 +
 +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)
 +              fprintf(f, "\tpcsc=%d\n", cred->pcsc);
 +      if (cred->realm)
 +              fprintf(f, "\trealm=\"%s\"\n", cred->realm);
 +      if (cred->username)
 +              fprintf(f, "\tusername=\"%s\"\n", cred->username);
 +      if (cred->password && cred->ext_password)
 +              fprintf(f, "\tpassword=ext:%s\n", cred->password);
 +      else if (cred->password)
 +              fprintf(f, "\tpassword=\"%s\"\n", cred->password);
 +      if (cred->ca_cert)
 +              fprintf(f, "\tca_cert=\"%s\"\n", cred->ca_cert);
 +      if (cred->client_cert)
 +              fprintf(f, "\tclient_cert=\"%s\"\n", cred->client_cert);
 +      if (cred->private_key)
 +              fprintf(f, "\tprivate_key=\"%s\"\n", cred->private_key);
 +      if (cred->private_key_passwd)
 +              fprintf(f, "\tprivate_key_passwd=\"%s\"\n",
 +                      cred->private_key_passwd);
 +      if (cred->imsi)
 +              fprintf(f, "\timsi=\"%s\"\n", cred->imsi);
 +      if (cred->milenage)
 +              fprintf(f, "\tmilenage=\"%s\"\n", cred->milenage);
 +      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) {
 +              fprintf(f, "\troaming_consortium=");
 +              for (i = 0; i < cred->roaming_consortium_len; i++)
 +                      fprintf(f, "%02x", cred->roaming_consortium[i]);
 +              fprintf(f, "\n");
 +      }
 +      if (cred->eap_method) {
 +              const char *name;
 +              name = eap_get_name(cred->eap_method[0].vendor,
 +                                  cred->eap_method[0].method);
 +              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 j;
 +              for (i = 0; i < cred->num_excluded_ssid; i++) {
 +                      struct excluded_ssid *e = &cred->excluded_ssid[i];
 +                      fprintf(f, "\texcluded_ssid=");
 +                      for (j = 0; j < e->ssid_len; j++)
 +                              fprintf(f, "%02x", e->ssid[j]);
 +                      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);
 +}
 +
 +
 +#ifndef CONFIG_NO_CONFIG_BLOBS
 +static int wpa_config_write_blob(FILE *f, struct wpa_config_blob *blob)
 +{
 +      unsigned char *encoded;
 +
 +      encoded = base64_encode(blob->data, blob->len, NULL);
 +      if (encoded == NULL)
 +              return -1;
 +
 +      fprintf(f, "\nblob-base64-%s={\n%s}\n", blob->name, encoded);
 +      os_free(encoded);
 +      return 0;
 +}
 +#endif /* CONFIG_NO_CONFIG_BLOBS */
 +
 +
 +static void write_global_bin(FILE *f, const char *field,
 +                           const struct wpabuf *val)
 +{
 +      size_t i;
 +      const u8 *pos;
 +
 +      if (val == NULL)
 +              return;
 +
 +      fprintf(f, "%s=", field);
 +      pos = wpabuf_head(val);
 +      for (i = 0; i < wpabuf_len(val); i++)
 +              fprintf(f, "%02X", *pos++);
 +      fprintf(f, "\n");
 +}
 +
 +
 +static void wpa_config_write_global(FILE *f, struct wpa_config *config)
 +{
 +#ifdef CONFIG_CTRL_IFACE
 +      if (config->ctrl_interface)
 +              fprintf(f, "ctrl_interface=%s\n", config->ctrl_interface);
 +      if (config->ctrl_interface_group)
 +              fprintf(f, "ctrl_interface_group=%s\n",
 +                      config->ctrl_interface_group);
 +#endif /* CONFIG_CTRL_IFACE */
 +      if (config->eapol_version != DEFAULT_EAPOL_VERSION)
 +              fprintf(f, "eapol_version=%d\n", config->eapol_version);
 +      if (config->ap_scan != DEFAULT_AP_SCAN)
 +              fprintf(f, "ap_scan=%d\n", config->ap_scan);
 +      if (config->disable_scan_offload)
 +              fprintf(f, "disable_scan_offload=%d\n",
 +                      config->disable_scan_offload);
 +      if (config->fast_reauth != DEFAULT_FAST_REAUTH)
 +              fprintf(f, "fast_reauth=%d\n", config->fast_reauth);
 +      if (config->opensc_engine_path)
 +              fprintf(f, "opensc_engine_path=%s\n",
 +                      config->opensc_engine_path);
 +      if (config->pkcs11_engine_path)
 +              fprintf(f, "pkcs11_engine_path=%s\n",
 +                      config->pkcs11_engine_path);
 +      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)
 +              fprintf(f, "pcsc_pin=%s\n", config->pcsc_pin);
 +      if (config->driver_param)
 +              fprintf(f, "driver_param=%s\n", config->driver_param);
 +      if (config->dot11RSNAConfigPMKLifetime)
-               fprintf(f, "dot11RSNAConfigPMKReauthThreshold=%d\n",
++              fprintf(f, "dot11RSNAConfigPMKLifetime=%u\n",
 +                      config->dot11RSNAConfigPMKLifetime);
 +      if (config->dot11RSNAConfigPMKReauthThreshold)
-               fprintf(f, "dot11RSNAConfigSATimeout=%d\n",
++              fprintf(f, "dot11RSNAConfigPMKReauthThreshold=%u\n",
 +                      config->dot11RSNAConfigPMKReauthThreshold);
 +      if (config->dot11RSNAConfigSATimeout)
-               fprintf(f, "p2p_listen_reg_class=%u\n",
++              fprintf(f, "dot11RSNAConfigSATimeout=%u\n",
 +                      config->dot11RSNAConfigSATimeout);
 +      if (config->update_config)
 +              fprintf(f, "update_config=%d\n", config->update_config);
 +#ifdef CONFIG_WPS
 +      if (!is_nil_uuid(config->uuid)) {
 +              char buf[40];
 +              uuid_bin2str(config->uuid, buf, sizeof(buf));
 +              fprintf(f, "uuid=%s\n", buf);
 +      }
 +      if (config->device_name)
 +              fprintf(f, "device_name=%s\n", config->device_name);
 +      if (config->manufacturer)
 +              fprintf(f, "manufacturer=%s\n", config->manufacturer);
 +      if (config->model_name)
 +              fprintf(f, "model_name=%s\n", config->model_name);
 +      if (config->model_number)
 +              fprintf(f, "model_number=%s\n", config->model_number);
 +      if (config->serial_number)
 +              fprintf(f, "serial_number=%s\n", config->serial_number);
 +      {
 +              char _buf[WPS_DEV_TYPE_BUFSIZE], *buf;
 +              buf = wps_dev_type_bin2str(config->device_type,
 +                                         _buf, sizeof(_buf));
 +              if (os_strcmp(buf, "0-00000000-0") != 0)
 +                      fprintf(f, "device_type=%s\n", buf);
 +      }
 +      if (WPA_GET_BE32(config->os_version))
 +              fprintf(f, "os_version=%08x\n",
 +                      WPA_GET_BE32(config->os_version));
 +      if (config->config_methods)
 +              fprintf(f, "config_methods=%s\n", config->config_methods);
 +      if (config->wps_cred_processing)
 +              fprintf(f, "wps_cred_processing=%d\n",
 +                      config->wps_cred_processing);
 +      if (config->wps_vendor_ext_m1) {
 +              int i, len = wpabuf_len(config->wps_vendor_ext_m1);
 +              const u8 *p = wpabuf_head_u8(config->wps_vendor_ext_m1);
 +              if (len > 0) {
 +                      fprintf(f, "wps_vendor_ext_m1=");
 +                      for (i = 0; i < len; i++)
 +                              fprintf(f, "%02x", *p++);
 +                      fprintf(f, "\n");
 +              }
 +      }
 +#endif /* CONFIG_WPS */
 +#ifdef CONFIG_P2P
 +      if (config->p2p_listen_reg_class)
-               fprintf(f, "p2p_listen_channel=%u\n",
++              fprintf(f, "p2p_listen_reg_class=%d\n",
 +                      config->p2p_listen_reg_class);
 +      if (config->p2p_listen_channel)
-               fprintf(f, "p2p_oper_reg_class=%u\n",
++              fprintf(f, "p2p_listen_channel=%d\n",
 +                      config->p2p_listen_channel);
 +      if (config->p2p_oper_reg_class)
-               fprintf(f, "p2p_oper_channel=%u\n", config->p2p_oper_channel);
++              fprintf(f, "p2p_oper_reg_class=%d\n",
 +                      config->p2p_oper_reg_class);
 +      if (config->p2p_oper_channel)
-               fprintf(f, "p2p_go_intent=%u\n", config->p2p_go_intent);
++              fprintf(f, "p2p_oper_channel=%d\n", config->p2p_oper_channel);
 +      if (config->p2p_go_intent != DEFAULT_P2P_GO_INTENT)
-               fprintf(f, "persistent_reconnect=%u\n",
++              fprintf(f, "p2p_go_intent=%d\n", config->p2p_go_intent);
 +      if (config->p2p_ssid_postfix)
 +              fprintf(f, "p2p_ssid_postfix=%s\n", config->p2p_ssid_postfix);
 +      if (config->persistent_reconnect)
-               fprintf(f, "p2p_intra_bss=%u\n", config->p2p_intra_bss);
++              fprintf(f, "persistent_reconnect=%d\n",
 +                      config->persistent_reconnect);
 +      if (config->p2p_intra_bss != DEFAULT_P2P_INTRA_BSS)
-               fprintf(f, "p2p_group_idle=%u\n", config->p2p_group_idle);
++              fprintf(f, "p2p_intra_bss=%d\n", config->p2p_intra_bss);
 +      if (config->p2p_group_idle)
-               fprintf(f, "p2p_go_ht40=%u\n", config->p2p_go_ht40);
++              fprintf(f, "p2p_group_idle=%d\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=");
 +              for (i = 0; i < config->num_p2p_pref_chan; i++) {
 +                      fprintf(f, "%s%u:%u", i > 0 ? "," : "",
 +                              config->p2p_pref_chan[i].op_class,
 +                              config->p2p_pref_chan[i].chan);
 +              }
 +              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_vht=%u\n", config->p2p_go_vht);
++              fprintf(f, "p2p_go_ht40=%d\n", config->p2p_go_ht40);
 +      if (config->p2p_go_vht)
-               fprintf(f, "p2p_go_ctwindow=%u\n", config->p2p_go_ctwindow);
++              fprintf(f, "p2p_go_vht=%d\n", config->p2p_go_vht);
 +      if (config->p2p_go_ctwindow != DEFAULT_P2P_GO_CTWINDOW)
-               fprintf(f, "p2p_disabled=%u\n", config->p2p_disabled);
++              fprintf(f, "p2p_go_ctwindow=%d\n", config->p2p_go_ctwindow);
 +      if (config->p2p_disabled)
-               fprintf(f, "p2p_no_group_iface=%u\n",
++              fprintf(f, "p2p_disabled=%d\n", config->p2p_disabled);
 +      if (config->p2p_no_group_iface)
-               fprintf(f, "p2p_ignore_shared_freq=%u\n",
++              fprintf(f, "p2p_no_group_iface=%d\n",
 +                      config->p2p_no_group_iface);
 +      if (config->p2p_ignore_shared_freq)
-               fprintf(f, "disassoc_low_ack=%u\n", config->disassoc_low_ack);
++              fprintf(f, "p2p_ignore_shared_freq=%d\n",
 +                      config->p2p_ignore_shared_freq);
++      if (config->p2p_cli_probe)
++              fprintf(f, "p2p_cli_probe=%d\n", config->p2p_cli_probe);
++      if (config->p2p_go_freq_change_policy != DEFAULT_P2P_GO_FREQ_MOVE)
++              fprintf(f, "p2p_go_freq_change_policy=%u\n",
++                      config->p2p_go_freq_change_policy);
 +#endif /* CONFIG_P2P */
 +      if (config->country[0] && config->country[1]) {
 +              fprintf(f, "country=%c%c\n",
 +                      config->country[0], config->country[1]);
 +      }
 +      if (config->bss_max_count != DEFAULT_BSS_MAX_COUNT)
 +              fprintf(f, "bss_max_count=%u\n", config->bss_max_count);
 +      if (config->bss_expiration_age != DEFAULT_BSS_EXPIRATION_AGE)
 +              fprintf(f, "bss_expiration_age=%u\n",
 +                      config->bss_expiration_age);
 +      if (config->bss_expiration_scan_count !=
 +          DEFAULT_BSS_EXPIRATION_SCAN_COUNT)
 +              fprintf(f, "bss_expiration_scan_count=%u\n",
 +                      config->bss_expiration_scan_count);
 +      if (config->filter_ssids)
 +              fprintf(f, "filter_ssids=%d\n", config->filter_ssids);
 +      if (config->max_num_sta != DEFAULT_MAX_NUM_STA)
 +              fprintf(f, "max_num_sta=%u\n", config->max_num_sta);
 +      if (config->disassoc_low_ack)
-               fprintf(f, "interworking=%u\n", config->interworking);
++              fprintf(f, "disassoc_low_ack=%d\n", config->disassoc_low_ack);
 +#ifdef CONFIG_HS20
 +      if (config->hs20)
 +              fprintf(f, "hs20=1\n");
 +#endif /* CONFIG_HS20 */
 +#ifdef CONFIG_INTERWORKING
 +      if (config->interworking)
-               fprintf(f, "pbc_in_m1=%u\n", config->pbc_in_m1);
++              fprintf(f, "interworking=%d\n", config->interworking);
 +      if (!is_zero_ether_addr(config->hessid))
 +              fprintf(f, "hessid=" MACSTR "\n", MAC2STR(config->hessid));
 +      if (config->access_network_type != DEFAULT_ACCESS_NETWORK_TYPE)
 +              fprintf(f, "access_network_type=%d\n",
 +                      config->access_network_type);
 +#endif /* CONFIG_INTERWORKING */
 +      if (config->pbc_in_m1)
-                       fprintf(f, "%s%u", i > 0 ? " " : "",
++              fprintf(f, "pbc_in_m1=%d\n", config->pbc_in_m1);
 +      if (config->wps_nfc_pw_from_config) {
 +              if (config->wps_nfc_dev_pw_id)
 +                      fprintf(f, "wps_nfc_dev_pw_id=%d\n",
 +                              config->wps_nfc_dev_pw_id);
 +              write_global_bin(f, "wps_nfc_dh_pubkey",
 +                               config->wps_nfc_dh_pubkey);
 +              write_global_bin(f, "wps_nfc_dh_privkey",
 +                               config->wps_nfc_dh_privkey);
 +              write_global_bin(f, "wps_nfc_dev_pw", config->wps_nfc_dev_pw);
 +      }
 +
 +      if (config->ext_password_backend)
 +              fprintf(f, "ext_password_backend=%s\n",
 +                      config->ext_password_backend);
 +      if (config->p2p_go_max_inactivity != DEFAULT_P2P_GO_MAX_INACTIVITY)
 +              fprintf(f, "p2p_go_max_inactivity=%d\n",
 +                      config->p2p_go_max_inactivity);
 +      if (config->auto_interworking)
 +              fprintf(f, "auto_interworking=%d\n",
 +                      config->auto_interworking);
 +      if (config->okc)
 +              fprintf(f, "okc=%d\n", config->okc);
 +      if (config->pmf)
 +              fprintf(f, "pmf=%d\n", config->pmf);
 +      if (config->dtim_period)
 +              fprintf(f, "dtim_period=%d\n", config->dtim_period);
 +      if (config->beacon_int)
 +              fprintf(f, "beacon_int=%d\n", config->beacon_int);
 +
 +      if (config->sae_groups) {
 +              int i;
 +              fprintf(f, "sae_groups=");
 +              for (i = 0; config->sae_groups[i] >= 0; i++) {
 +                      fprintf(f, "%s%d", i > 0 ? " " : "",
 +                              config->sae_groups[i]);
 +              }
 +              fprintf(f, "\n");
 +      }
 +
 +      if (config->ap_vendor_elements) {
 +              int i, len = wpabuf_len(config->ap_vendor_elements);
 +              const u8 *p = wpabuf_head_u8(config->ap_vendor_elements);
 +              if (len > 0) {
 +                      fprintf(f, "ap_vendor_elements=");
 +                      for (i = 0; i < len; i++)
 +                              fprintf(f, "%02x", *p++);
 +                      fprintf(f, "\n");
 +              }
 +      }
 +
 +      if (config->ignore_old_scan_res)
 +              fprintf(f, "ignore_old_scan_res=%d\n",
 +                      config->ignore_old_scan_res);
 +
 +      if (config->freq_list && config->freq_list[0]) {
 +              int i;
 +              fprintf(f, "freq_list=");
 +              for (i = 0; config->freq_list[i]; i++) {
-               fprintf(f, "key_mgmt_offload=%u\n", config->key_mgmt_offload);
++                      fprintf(f, "%s%d", i > 0 ? " " : "",
 +                              config->freq_list[i]);
 +              }
 +              fprintf(f, "\n");
 +      }
 +      if (config->scan_cur_freq != DEFAULT_SCAN_CUR_FREQ)
 +              fprintf(f, "scan_cur_freq=%d\n", config->scan_cur_freq);
 +
 +      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=%d\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->dot11RSNASAERetransPeriod !=
++          DEFAULT_DOT11_RSNA_SAE_RETRANS_PERIOD)
++              fprintf(f, "dot11RSNASAERetransPeriod=%d\n",
++                      config->dot11RSNASAERetransPeriod);
++
 +      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);
++
++      if (config->wps_priority)
++              fprintf(f, "wps_priority=%d\n", config->wps_priority);
 +}
 +
 +#endif /* CONFIG_NO_CONFIG_WRITE */
 +
 +
 +int wpa_config_write(const char *name, struct wpa_config *config)
 +{
 +#ifndef CONFIG_NO_CONFIG_WRITE
 +      FILE *f;
 +      struct wpa_ssid *ssid;
 +      struct wpa_cred *cred;
 +#ifndef CONFIG_NO_CONFIG_BLOBS
 +      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");
 +      }
 +
 +      for (ssid = config->ssid; ssid; ssid = ssid->next) {
 +              if (ssid->key_mgmt == WPA_KEY_MGMT_WPS || ssid->temporary)
 +                      continue; /* do not save temporary networks */
 +              if (wpa_key_mgmt_wpa_psk(ssid->key_mgmt) && !ssid->psk_set &&
 +                  !ssid->passphrase)
 +                      continue; /* do not save invalid network */
 +              fprintf(f, "\nnetwork={\n");
 +              wpa_config_write_network(f, ssid);
 +              fprintf(f, "}\n");
 +      }
 +
 +#ifndef CONFIG_NO_CONFIG_BLOBS
 +      for (blob = config->blobs; blob; blob = blob->next) {
 +              ret = wpa_config_write_blob(f, blob);
 +              if (ret)
 +                      break;
 +      }
 +#endif /* CONFIG_NO_CONFIG_BLOBS */
 +
++      os_fdatasync(f);
++
 +      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",
 +                 orig_name, ret ? "un" : "");
 +      return ret;
 +#else /* CONFIG_NO_CONFIG_WRITE */
 +      return -1;
 +#endif /* CONFIG_NO_CONFIG_WRITE */
 +}
index 7c826cfd983f817682648a7f4a875087c18e6124,0000000000000000000000000000000000000000..7ef326cfbed634c8f34024861b13c17e6106b7bc
mode 100644,000000..100644
--- /dev/null
@@@ -1,717 -1,0 +1,724 @@@
- #define MAX_SSID_LEN 32
 +/*
 + * WPA Supplicant / Network configuration structures
 + * 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.
 + */
 +
 +#ifndef CONFIG_SSID_H
 +#define CONFIG_SSID_H
 +
 +#include "common/defs.h"
 +#include "utils/list.h"
 +#include "eap_peer/eap_config.h"
 +
- #define DEFAULT_GROUP (WPA_CIPHER_CCMP | WPA_CIPHER_TKIP | \
-                      WPA_CIPHER_WEP104 | WPA_CIPHER_WEP40)
 +
 +#define DEFAULT_EAP_WORKAROUND ((unsigned int) -1)
 +#define DEFAULT_EAPOL_FLAGS (EAPOL_FLAG_REQUIRE_KEY_UNICAST | \
 +                           EAPOL_FLAG_REQUIRE_KEY_BROADCAST)
 +#define DEFAULT_PROTO (WPA_PROTO_WPA | WPA_PROTO_RSN)
 +#define DEFAULT_KEY_MGMT (WPA_KEY_MGMT_PSK | WPA_KEY_MGMT_IEEE8021X)
 +#define DEFAULT_PAIRWISE (WPA_CIPHER_CCMP | WPA_CIPHER_TKIP)
-        * such drivers to use hidden SSIDs.
++#define DEFAULT_GROUP (WPA_CIPHER_CCMP | WPA_CIPHER_TKIP)
 +#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
 + *
 + * This structure includes all the configuration variables for a network. This
 + * data is included in the per-interface configuration data as an element of
 + * the network list, struct wpa_config::ssid. Each network block in the
 + * configuration is mapped to a struct wpa_ssid instance.
 + */
 +struct wpa_ssid {
 +      /**
 +       * next - Next network in global list
 +       *
 +       * This pointer can be used to iterate over all networks. The head of
 +       * this list is stored in the ssid field of struct wpa_config.
 +       */
 +      struct wpa_ssid *next;
 +
 +      /**
 +       * pnext - Next network in per-priority list
 +       *
 +       * This pointer can be used to iterate over all networks in the same
 +       * priority class. The heads of these list are stored in the pssid
 +       * fields of struct wpa_config.
 +       */
 +      struct wpa_ssid *pnext;
 +
 +      /**
 +       * id - Unique id for the network
 +       *
 +       * This identifier is used as a unique identifier for each network
 +       * block when using the control interface. Each network is allocated an
 +       * id when it is being created, either when reading the configuration
 +       * file or when a new network is added through the control interface.
 +       */
 +      int id;
 +
 +      /**
 +       * priority - Priority group
 +       *
 +       * By default, all networks will get same priority group (0). If some
 +       * of the networks are more desirable, this field can be used to change
 +       * the order in which wpa_supplicant goes through the networks when
 +       * selecting a BSS. The priority groups will be iterated in decreasing
 +       * priority (i.e., the larger the priority value, the sooner the
 +       * network is matched against the scan results). Within each priority
 +       * group, networks will be selected based on security policy, signal
 +       * strength, etc.
 +       *
 +       * Please note that AP scanning with scan_ssid=1 and ap_scan=2 mode are
 +       * not using this priority to select the order for scanning. Instead,
 +       * they try the networks in the order that used in the configuration
 +       * file.
 +       */
 +      int priority;
 +
 +      /**
 +       * ssid - Service set identifier (network name)
 +       *
 +       * This is the SSID for the network. For wireless interfaces, this is
 +       * used to select which network will be used. If set to %NULL (or
 +       * ssid_len=0), any SSID can be used. For wired interfaces, this must
 +       * be set to %NULL. Note: SSID may contain any characters, even nul
 +       * (ASCII 0) and as such, this should not be assumed to be a nul
 +       * terminated string. ssid_len defines how many characters are valid
 +       * and the ssid field is not guaranteed to be nul terminated.
 +       */
 +      u8 *ssid;
 +
 +      /**
 +       * ssid_len - Length of the SSID
 +       */
 +      size_t ssid_len;
 +
 +      /**
 +       * bssid - BSSID
 +       *
 +       * If set, this network block is used only when associating with the AP
 +       * using the configured BSSID
 +       *
 +       * If this is a persistent P2P group (disabled == 2), this is the GO
 +       * Device Address.
 +       */
 +      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];
 +
 +      /**
 +       * psk_set - Whether PSK field is configured
 +       */
 +      int psk_set;
 +
 +      /**
 +       * passphrase - WPA ASCII passphrase
 +       *
 +       * If this is set, psk will be generated using the SSID and passphrase
 +       * configured for the network. ASCII passphrase must be between 8 and
 +       * 63 characters (inclusive).
 +       */
 +      char *passphrase;
 +
 +      /**
 +       * ext_psk - PSK/passphrase name in external storage
 +       *
 +       * If this is set, PSK/passphrase will be fetched from external storage
 +       * when requesting association with the network.
 +       */
 +      char *ext_psk;
 +
++      /**
++       * mem_only_psk - Whether to keep PSK/passphrase only in memory
++       *
++       * 0 = allow psk/passphrase to be stored to the configuration file
++       * 1 = do not store psk/passphrase to the configuration file
++       */
++      int mem_only_psk;
++
 +      /**
 +       * pairwise_cipher - Bitfield of allowed pairwise ciphers, WPA_CIPHER_*
 +       */
 +      int pairwise_cipher;
 +
 +      /**
 +       * group_cipher - Bitfield of allowed group ciphers, WPA_CIPHER_*
 +       */
 +      int group_cipher;
 +
 +      /**
 +       * key_mgmt - Bitfield of allowed key management protocols
 +       *
 +       * WPA_KEY_MGMT_*
 +       */
 +      int key_mgmt;
 +
 +      /**
 +       * bg_scan_period - Background scan period in seconds, 0 to disable, or
 +       * -1 to indicate no change to default driver configuration
 +       */
 +      int bg_scan_period;
 +
 +      /**
 +       * proto - Bitfield of allowed protocols, WPA_PROTO_*
 +       */
 +      int proto;
 +
 +      /**
 +       * auth_alg -  Bitfield of allowed authentication algorithms
 +       *
 +       * WPA_AUTH_ALG_*
 +       */
 +      int auth_alg;
 +
 +      /**
 +       * scan_ssid - Scan this SSID with Probe Requests
 +       *
 +       * scan_ssid can be used to scan for APs using hidden SSIDs.
 +       * Note: Many drivers do not support this. ap_mode=2 can be used with
++       * such drivers to use hidden SSIDs. Note2: Most nl80211-based drivers
++       * do support scan_ssid=1 and that should be used with them instead of
++       * ap_scan=2.
 +       */
 +      int scan_ssid;
 +
 +#ifdef IEEE8021X_EAPOL
 +#define EAPOL_FLAG_REQUIRE_KEY_UNICAST BIT(0)
 +#define EAPOL_FLAG_REQUIRE_KEY_BROADCAST BIT(1)
 +      /**
 +       * eapol_flags - Bit field of IEEE 802.1X/EAPOL options (EAPOL_FLAG_*)
 +       */
 +      int eapol_flags;
 +
 +      /**
 +       * eap - EAP peer configuration for this network
 +       */
 +      struct eap_peer_config eap;
 +#endif /* IEEE8021X_EAPOL */
 +
 +#define NUM_WEP_KEYS 4
 +#define MAX_WEP_KEY_LEN 16
 +      /**
 +       * wep_key - WEP keys
 +       */
 +      u8 wep_key[NUM_WEP_KEYS][MAX_WEP_KEY_LEN];
 +
 +      /**
 +       * wep_key_len - WEP key lengths
 +       */
 +      size_t wep_key_len[NUM_WEP_KEYS];
 +
 +      /**
 +       * wep_tx_keyidx - Default key index for TX frames using WEP
 +       */
 +      int wep_tx_keyidx;
 +
 +      /**
 +       * proactive_key_caching - Enable proactive key caching
 +       *
 +       * This field can be used to enable proactive key caching which is also
 +       * known as opportunistic PMKSA caching for WPA2. This is disabled (0)
 +       * by default unless default value is changed with the global okc=1
 +       * parameter. Enable by setting this to 1.
 +       *
 +       * Proactive key caching is used to make supplicant assume that the APs
 +       * are using the same PMK and generate PMKSA cache entries without
 +       * doing RSN pre-authentication. This requires support from the AP side
 +       * and is normally used with wireless switches that co-locate the
 +       * authenticator.
 +       *
 +       * Internally, special value -1 is used to indicate that the parameter
 +       * was not specified in the configuration (i.e., default behavior is
 +       * followed).
 +       */
 +      int proactive_key_caching;
 +
 +      /**
 +       * mixed_cell - Whether mixed cells are allowed
 +       *
 +       * This option can be used to configure whether so called mixed cells,
 +       * i.e., networks that use both plaintext and encryption in the same
 +       * SSID, are allowed. This is disabled (0) by default. Enable by
 +       * setting this to 1.
 +       */
 +      int mixed_cell;
 +
 +#ifdef IEEE8021X_EAPOL
 +
 +      /**
 +       * leap - Number of EAP methods using LEAP
 +       *
 +       * This field should be set to 1 if LEAP is enabled. This is used to
 +       * select IEEE 802.11 authentication algorithm.
 +       */
 +      int leap;
 +
 +      /**
 +       * non_leap - Number of EAP methods not using LEAP
 +       *
 +       * This field should be set to >0 if any EAP method other than LEAP is
 +       * enabled. This is used to select IEEE 802.11 authentication
 +       * algorithm.
 +       */
 +      int non_leap;
 +
 +      /**
 +       * eap_workaround - EAP workarounds enabled
 +       *
 +       * wpa_supplicant supports number of "EAP workarounds" to work around
 +       * interoperability issues with incorrectly behaving authentication
 +       * servers. This is recommended to be enabled by default because some
 +       * of the issues are present in large number of authentication servers.
 +       *
 +       * Strict EAP conformance mode can be configured by disabling
 +       * workarounds with eap_workaround = 0.
 +       */
 +      unsigned int eap_workaround;
 +
 +#endif /* IEEE8021X_EAPOL */
 +
 +      /**
 +       * mode - IEEE 802.11 operation mode (Infrastucture/IBSS)
 +       *
 +       * 0 = infrastructure (Managed) mode, i.e., associate with an AP.
 +       *
 +       * 1 = IBSS (ad-hoc, peer-to-peer)
 +       *
 +       * 2 = AP (access point)
 +       *
 +       * 3 = P2P Group Owner (can be set in the configuration file)
 +       *
 +       * 4 = P2P Group Formation (used internally; not in configuration
 +       * files)
 +       *
 +       * 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,
 +              WPAS_MODE_IBSS = 1,
 +              WPAS_MODE_AP = 2,
 +              WPAS_MODE_P2P_GO = 3,
 +              WPAS_MODE_P2P_GROUP_FORMATION = 4,
 +              WPAS_MODE_MESH = 5,
 +      } mode;
 +
 +      /**
 +       * disabled - Whether this network is currently disabled
 +       *
 +       * 0 = this network can be used (default).
 +       * 1 = this network block is disabled (can be enabled through
 +       * ctrl_iface, e.g., with wpa_cli or wpa_gui).
 +       * 2 = this network block includes parameters for a persistent P2P
 +       * group (can be used with P2P ctrl_iface commands)
 +       */
 +      int disabled;
 +
 +      /**
 +       * disabled_for_connect - Whether this network was temporarily disabled
 +       *
 +       * This flag is used to reenable all the temporarily disabled networks
 +       * after either the success or failure of a WPS connection.
 +       */
 +      int disabled_for_connect;
 +
 +      /**
 +       * peerkey -  Whether PeerKey handshake for direct links is allowed
 +       *
 +       * This is only used when both RSN/WPA2 and IEEE 802.11e (QoS) are
 +       * enabled.
 +       *
 +       * 0 = disabled (default)
 +       * 1 = enabled
 +       */
 +      int peerkey;
 +
 +      /**
 +       * id_str - Network identifier string for external scripts
 +       *
 +       * This value is passed to external ctrl_iface monitors in
 +       * WPA_EVENT_CONNECTED event and wpa_cli sets this as WPA_ID_STR
 +       * environment variable for action scripts.
 +       */
 +      char *id_str;
 +
 +#ifdef CONFIG_IEEE80211W
 +      /**
 +       * ieee80211w - Whether management frame protection is enabled
 +       *
 +       * This value is used to configure policy for management frame
 +       * protection (IEEE 802.11w). 0 = disabled, 1 = optional, 2 = required.
 +       * This is disabled by default unless the default value has been changed
 +       * with the global pmf=1/2 parameter.
 +       *
 +       * Internally, special value 3 is used to indicate that the parameter
 +       * was not specified in the configuration (i.e., default behavior is
 +       * followed).
 +       */
 +      enum mfp_options ieee80211w;
 +#endif /* CONFIG_IEEE80211W */
 +
 +      /**
 +       * frequency - Channel frequency in megahertz (MHz) for IBSS
 +       *
 +       * This value is used to configure the initial channel for IBSS (adhoc)
 +       * networks, e.g., 2412 = IEEE 802.11b/g channel 1. It is ignored in
 +       * the infrastructure mode. In addition, this value is only used by the
 +       * station that creates the IBSS. If an IBSS network with the
 +       * configured SSID is already present, the frequency of the network
 +       * will be used instead of this configured value.
 +       */
 +      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
 +       *
 +       * This value can be used to enforce rekeying of PTK to mitigate some
 +       * attacks against TKIP deficiencies.
 +       */
 +      int wpa_ptk_rekey;
 +
 +      /**
 +       * scan_freq - Array of frequencies to scan or %NULL for all
 +       *
 +       * This is an optional zero-terminated array of frequencies in
 +       * megahertz (MHz) to include in scan requests when searching for this
 +       * network. This can be used to speed up scanning when the network is
 +       * known to not use all possible channels.
 +       */
 +      int *scan_freq;
 +
 +      /**
 +       * 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) in following format:
 +       * <bgscan module name>:<module parameters>
 +       */
 +      char *bgscan;
 +
 +      /**
 +       * ignore_broadcast_ssid - Hide SSID in AP mode
 +       *
 +       * Send empty SSID in beacons and ignore probe request frames that do
 +       * not specify full SSID, i.e., require stations to know SSID.
 +       * default: disabled (0)
 +       * 1 = send empty (length=0) SSID in beacon and ignore probe request
 +       * for broadcast SSID
 +       * 2 = clear SSID (ASCII 0), but keep the original length (this may be
 +       * required with some clients that do not support empty SSID) and
 +       * ignore probe requests for broadcast SSID
 +       */
 +      int ignore_broadcast_ssid;
 +
 +      /**
 +       * freq_list - Array of allowed frequencies or %NULL for all
 +       *
 +       * This is an optional zero-terminated array of frequencies in
 +       * megahertz (MHz) to allow for selecting the BSS. If set, scan results
 +       * that do not match any of the specified frequencies are not
 +       * considered when selecting a BSS.
 +       */
 +      int *freq_list;
 +
 +      /**
 +       * p2p_client_list - List of P2P Clients in a persistent group (GO)
 +       *
 +       * This is a list of P2P Clients (P2P Device Address) that have joined
 +       * the persistent group. This is maintained on the GO for persistent
 +       * group entries (disabled == 2).
 +       */
 +      u8 *p2p_client_list;
 +
 +      /**
 +       * num_p2p_clients - Number of entries in p2p_client_list
 +       */
 +      size_t num_p2p_clients;
 +
 +#ifndef P2P_MAX_STORED_CLIENTS
 +#define P2P_MAX_STORED_CLIENTS 100
 +#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;
 +
 +      /**
 +       * p2p_persistent_group - Whether this is a persistent group
 +       */
 +      int p2p_persistent_group;
 +
 +      /**
 +       * temporary - Whether this network is temporary and not to be saved
 +       */
 +      int temporary;
 +
 +      /**
 +       * export_keys - Whether keys may be exported
 +       *
 +       * This attribute will be set when keys are determined through
 +       * WPS or similar so that they may be exported.
 +       */
 +      int export_keys;
 +
 +#ifdef CONFIG_HT_OVERRIDES
 +      /**
 +       * disable_ht - Disable HT (IEEE 802.11n) for this network
 +       *
 +       * By default, use it if it is available, but this can be configured
 +       * to 1 to have it disabled.
 +       */
 +      int disable_ht;
 +
 +      /**
 +       * disable_ht40 - Disable HT40 for this network
 +       *
 +       * By default, use it if it is available, but this can be configured
 +       * to 1 to have it disabled.
 +       */
 +      int disable_ht40;
 +
 +      /**
 +       * disable_sgi - Disable SGI (Short Guard Interval) for this network
 +       *
 +       * By default, use it if it is available, but this can be configured
 +       * to 1 to have it disabled.
 +       */
 +      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
 +       * when enabled (assuming it is otherwise supported)
 +       * -1 (default) means do not apply any settings to the kernel.
 +       */
 +      int disable_max_amsdu;
 +
 +      /**
 +       * ampdu_factor - Maximum A-MPDU Length Exponent
 +       *
 +       * Value: 0-3, see 7.3.2.56.3 in IEEE Std 802.11n-2009.
 +       */
 +      int ampdu_factor;
 +
 +      /**
 +       * ampdu_density - Minimum A-MPDU Start Spacing
 +       *
 +       * Value: 0-7, see 7.3.2.56.3 in IEEE Std 802.11n-2009.
 +       */
 +      int ampdu_density;
 +
 +      /**
 +       * ht_mcs - Allowed HT-MCS rates, in ASCII hex: ffff0000...
 +       *
 +       * By default (empty string): Use whatever the OS has configured.
 +       */
 +      char *ht_mcs;
 +#endif /* CONFIG_HT_OVERRIDES */
 +
 +#ifdef CONFIG_VHT_OVERRIDES
 +      /**
 +       * disable_vht - Disable VHT (IEEE 802.11ac) for this network
 +       *
 +       * By default, use it if it is available, but this can be configured
 +       * to 1 to have it disabled.
 +       */
 +      int disable_vht;
 +
 +      /**
 +       * vht_capa - VHT capabilities to use
 +       */
 +      unsigned int vht_capa;
 +
 +      /**
 +       * vht_capa_mask - mask for VHT capabilities
 +       */
 +      unsigned int vht_capa_mask;
 +
 +      int vht_rx_mcs_nss_1, vht_rx_mcs_nss_2,
 +          vht_rx_mcs_nss_3, vht_rx_mcs_nss_4,
 +          vht_rx_mcs_nss_5, vht_rx_mcs_nss_6,
 +          vht_rx_mcs_nss_7, vht_rx_mcs_nss_8;
 +      int vht_tx_mcs_nss_1, vht_tx_mcs_nss_2,
 +          vht_tx_mcs_nss_3, vht_tx_mcs_nss_4,
 +          vht_tx_mcs_nss_5, vht_tx_mcs_nss_6,
 +          vht_tx_mcs_nss_7, vht_tx_mcs_nss_8;
 +#endif /* CONFIG_VHT_OVERRIDES */
 +
 +      /**
 +       * ap_max_inactivity - Timeout in seconds to detect STA's inactivity
 +       *
 +       * This timeout value is used in AP mode to clean up inactive stations.
 +       * By default: 300 seconds.
 +       */
 +      int ap_max_inactivity;
 +
 +      /**
 +       * dtim_period - DTIM period in Beacon intervals
 +       * By default: 2
 +       */
 +      int dtim_period;
 +
 +      /**
 +       * beacon_int - Beacon interval (default: 100 TU)
 +       */
 +      int beacon_int;
 +
 +      /**
 +       * auth_failures - Number of consecutive authentication failures
 +       */
 +      unsigned int auth_failures;
 +
 +      /**
 +       * disabled_until - Network block disabled until this time if non-zero
 +       */
 +      struct os_reltime disabled_until;
 +
 +      /**
 +       * parent_cred - Pointer to parent wpa_cred entry
 +       *
 +       * This pointer can be used to delete temporary networks when a wpa_cred
 +       * that was used to create them is removed. This pointer should not be
 +       * 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 b4aefb65ec25482be6ad0dd7bda61fc235b7c3ef,0000000000000000000000000000000000000000..3b97806d871dfe069d646057dcb926d7e4251500
mode 100644,000000..100644
--- /dev/null
@@@ -1,8991 -1,0 +1,9478 @@@
-                       if ((end - pos) & 0x01 || end - pos > 2 * 32 ||
 +/*
 + * WPA Supplicant / Control interface (shared code for all backends)
 + * 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 "rsn_supp/preauth.h"
 +#include "rsn_supp/pmksa_cache.h"
 +#include "l2_packet/l2_packet.h"
 +#include "wps/wps.h"
++#include "fst/fst.h"
++#include "fst/fst_ctrl_iface.h"
 +#include "config.h"
 +#include "wpa_supplicant_i.h"
 +#include "driver_i.h"
 +#include "wps_supplicant.h"
 +#include "ibss_rsn.h"
 +#include "ap.h"
 +#include "p2p_supplicant.h"
 +#include "p2p/p2p.h"
 +#include "hs20_supplicant.h"
 +#include "wifi_display.h"
 +#include "notify.h"
 +#include "bss.h"
 +#include "scan.h"
 +#include "ctrl_iface.h"
 +#include "interworking.h"
 +#include "blacklist.h"
 +#include "autoscan.h"
 +#include "wnm_sta.h"
 +#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 * freq_range_to_channel_list(struct wpa_supplicant *wpa_s,
 +                                      char *val);
 +
 +static int set_bssid_filter(struct wpa_supplicant *wpa_s, char *val)
 +{
 +      char *pos;
 +      u8 addr[ETH_ALEN], *filter = NULL, *n;
 +      size_t count = 0;
 +
 +      pos = val;
 +      while (pos) {
 +              if (*pos == '\0')
 +                      break;
 +              if (hwaddr_aton(pos, addr)) {
 +                      os_free(filter);
 +                      return -1;
 +              }
 +              n = os_realloc_array(filter, count + 1, ETH_ALEN);
 +              if (n == NULL) {
 +                      os_free(filter);
 +                      return -1;
 +              }
 +              filter = n;
 +              os_memcpy(filter + count * ETH_ALEN, addr, ETH_ALEN);
 +              count++;
 +
 +              pos = os_strchr(pos, ' ');
 +              if (pos)
 +                      pos++;
 +      }
 +
 +      wpa_hexdump(MSG_DEBUG, "bssid_filter", filter, count * ETH_ALEN);
 +      os_free(wpa_s->bssid_filter);
 +      wpa_s->bssid_filter = filter;
 +      wpa_s->bssid_filter_count = count;
 +
 +      return 0;
 +}
 +
 +
 +static int set_disallow_aps(struct wpa_supplicant *wpa_s, char *val)
 +{
 +      char *pos;
 +      u8 addr[ETH_ALEN], *bssid = NULL, *n;
 +      struct wpa_ssid_value *ssid = NULL, *ns;
 +      size_t count = 0, ssid_count = 0;
 +      struct wpa_ssid *c;
 +
 +      /*
 +       * disallow_list ::= <ssid_spec> | <bssid_spec> | <disallow_list> | ""
 +       * SSID_SPEC ::= ssid <SSID_HEX>
 +       * BSSID_SPEC ::= bssid <BSSID_HEX>
 +       */
 +
 +      pos = val;
 +      while (pos) {
 +              if (*pos == '\0')
 +                      break;
 +              if (os_strncmp(pos, "bssid ", 6) == 0) {
 +                      int res;
 +                      pos += 6;
 +                      res = hwaddr_aton2(pos, addr);
 +                      if (res < 0) {
 +                              os_free(ssid);
 +                              os_free(bssid);
 +                              wpa_printf(MSG_DEBUG, "Invalid disallow_aps "
 +                                         "BSSID value '%s'", pos);
 +                              return -1;
 +                      }
 +                      pos += res;
 +                      n = os_realloc_array(bssid, count + 1, ETH_ALEN);
 +                      if (n == NULL) {
 +                              os_free(ssid);
 +                              os_free(bssid);
 +                              return -1;
 +                      }
 +                      bssid = n;
 +                      os_memcpy(bssid + count * ETH_ALEN, addr, ETH_ALEN);
 +                      count++;
 +              } else if (os_strncmp(pos, "ssid ", 5) == 0) {
 +                      char *end;
 +                      pos += 5;
 +
 +                      end = pos;
 +                      while (*end) {
 +                              if (*end == '\0' || *end == ' ')
 +                                      break;
 +                              end++;
 +                      }
 +
 +                      ns = os_realloc_array(ssid, ssid_count + 1,
 +                                            sizeof(struct wpa_ssid_value));
 +                      if (ns == NULL) {
 +                              os_free(ssid);
 +                              os_free(bssid);
 +                              return -1;
 +                      }
 +                      ssid = ns;
 +
-               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;
++                      if ((end - pos) & 0x01 ||
++                          end - pos > 2 * SSID_MAX_LEN ||
 +                          hexstr2bin(pos, ssid[ssid_count].ssid,
 +                                     (end - pos) / 2) < 0) {
 +                              os_free(ssid);
 +                              os_free(bssid);
 +                              wpa_printf(MSG_DEBUG, "Invalid disallow_aps "
 +                                         "SSID value '%s'", pos);
 +                              return -1;
 +                      }
 +                      ssid[ssid_count].ssid_len = (end - pos) / 2;
 +                      wpa_hexdump_ascii(MSG_DEBUG, "disallow_aps SSID",
 +                                        ssid[ssid_count].ssid,
 +                                        ssid[ssid_count].ssid_len);
 +                      ssid_count++;
 +                      pos = end;
 +              } else {
 +                      wpa_printf(MSG_DEBUG, "Unexpected disallow_aps value "
 +                                 "'%s'", pos);
 +                      os_free(ssid);
 +                      os_free(bssid);
 +                      return -1;
 +              }
 +
 +              pos = os_strchr(pos, ' ');
 +              if (pos)
 +                      pos++;
 +      }
 +
 +      wpa_hexdump(MSG_DEBUG, "disallow_aps_bssid", bssid, count * ETH_ALEN);
 +      os_free(wpa_s->disallow_aps_bssid);
 +      wpa_s->disallow_aps_bssid = bssid;
 +      wpa_s->disallow_aps_bssid_count = count;
 +
 +      wpa_printf(MSG_DEBUG, "disallow_aps_ssid_count %d", (int) ssid_count);
 +      os_free(wpa_s->disallow_aps_ssid);
 +      wpa_s->disallow_aps_ssid = ssid;
 +      wpa_s->disallow_aps_ssid_count = ssid_count;
 +
 +      if (!wpa_s->current_ssid || wpa_s->wpa_state < WPA_AUTHENTICATING)
 +              return 0;
 +
 +      c = wpa_s->current_ssid;
 +      if (c->mode != WPAS_MODE_INFRA && c->mode != WPAS_MODE_IBSS)
 +              return 0;
 +
 +      if (!disallowed_bssid(wpa_s, wpa_s->bssid) &&
 +          !disallowed_ssid(wpa_s, c->ssid, c->ssid_len))
 +              return 0;
 +
 +      wpa_printf(MSG_DEBUG, "Disconnect and try to find another network "
 +                 "because current AP was marked disallowed");
 +
 +#ifdef CONFIG_SME
 +      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);
 +
 +      return 0;
 +}
 +
 +
 +#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 wpas_ctrl_set_band(struct wpa_supplicant *wpa_s, char *band)
++{
++      union wpa_event_data event;
++
++      if (os_strcmp(band, "AUTO") == 0)
++              wpa_s->setband = WPA_SETBAND_AUTO;
++      else if (os_strcmp(band, "5G") == 0)
++              wpa_s->setband = WPA_SETBAND_5G;
++      else if (os_strcmp(band, "2G") == 0)
++              wpa_s->setband = WPA_SETBAND_2G;
++      else
++              return -1;
++
++      if (wpa_drv_setband(wpa_s, wpa_s->setband) == 0) {
++              os_memset(&event, 0, sizeof(event));
++              event.channel_list_changed.initiator = REGDOM_SET_BY_USER;
++              event.channel_list_changed.type = REGDOM_TYPE_UNKNOWN;
++              wpa_supplicant_event(wpa_s, EVENT_CHANNEL_LIST_CHANGED, &event);
++      }
++
++      return 0;
++}
++
++
 +static int wpa_supplicant_ctrl_iface_set(struct wpa_supplicant *wpa_s,
 +                                       char *cmd)
 +{
 +      char *value;
 +      int ret = 0;
 +
 +      value = os_strchr(cmd, ' ');
 +      if (value == NULL)
 +              return -1;
 +      *value++ = '\0';
 +
 +      wpa_printf(MSG_DEBUG, "CTRL_IFACE SET '%s'='%s'", cmd, value);
 +      if (os_strcasecmp(cmd, "EAPOL::heldPeriod") == 0) {
 +              eapol_sm_configure(wpa_s->eapol,
 +                                 atoi(value), -1, -1, -1);
 +      } else if (os_strcasecmp(cmd, "EAPOL::authPeriod") == 0) {
 +              eapol_sm_configure(wpa_s->eapol,
 +                                 -1, atoi(value), -1, -1);
 +      } else if (os_strcasecmp(cmd, "EAPOL::startPeriod") == 0) {
 +              eapol_sm_configure(wpa_s->eapol,
 +                                 -1, -1, atoi(value), -1);
 +      } else if (os_strcasecmp(cmd, "EAPOL::maxStart") == 0) {
 +              eapol_sm_configure(wpa_s->eapol,
 +                                 -1, -1, -1, atoi(value));
 +      } else if (os_strcasecmp(cmd, "dot11RSNAConfigPMKLifetime") == 0) {
 +              if (wpa_sm_set_param(wpa_s->wpa, RSNA_PMK_LIFETIME,
 +                                   atoi(value)))
 +                      ret = -1;
 +      } else if (os_strcasecmp(cmd, "dot11RSNAConfigPMKReauthThreshold") ==
 +                 0) {
 +              if (wpa_sm_set_param(wpa_s->wpa, RSNA_PMK_REAUTH_THRESHOLD,
 +                                   atoi(value)))
 +                      ret = -1;
 +      } else if (os_strcasecmp(cmd, "dot11RSNAConfigSATimeout") == 0) {
 +              if (wpa_sm_set_param(wpa_s->wpa, RSNA_SA_TIMEOUT, atoi(value)))
 +                      ret = -1;
 +      } else if (os_strcasecmp(cmd, "wps_fragment_size") == 0) {
 +              wpa_s->wps_fragment_size = atoi(value);
 +#ifdef CONFIG_WPS_TESTING
 +      } else if (os_strcasecmp(cmd, "wps_version_number") == 0) {
 +              long int val;
 +              val = strtol(value, NULL, 0);
 +              if (val < 0 || val > 0xff) {
 +                      ret = -1;
 +                      wpa_printf(MSG_DEBUG, "WPS: Invalid "
 +                                 "wps_version_number %ld", val);
 +              } else {
 +                      wps_version_number = val;
 +                      wpa_printf(MSG_DEBUG, "WPS: Testing - force WPS "
 +                                 "version %u.%u",
 +                                 (wps_version_number & 0xf0) >> 4,
 +                                 wps_version_number & 0x0f);
 +              }
 +      } else if (os_strcasecmp(cmd, "wps_testing_dummy_cred") == 0) {
 +              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 */
 +      } else if (os_strcasecmp(cmd, "tdls_disabled") == 0) {
 +              int disabled = atoi(value);
 +              wpa_printf(MSG_DEBUG, "TDLS: tdls_disabled=%d", disabled);
 +              if (disabled) {
 +                      if (wpa_drv_tdls_oper(wpa_s, TDLS_DISABLE, NULL) < 0)
 +                              ret = -1;
 +              } else if (wpa_drv_tdls_oper(wpa_s, TDLS_ENABLE, NULL) < 0)
 +                      ret = -1;
 +              wpa_tdls_enable(wpa_s->wpa, !disabled);
 +#endif /* CONFIG_TDLS */
 +      } else if (os_strcasecmp(cmd, "pno") == 0) {
 +              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)
 +                      ret = -1;
 +              else if (disabled)
 +                      wpa_supplicant_set_state(wpa_s, WPA_INACTIVE);
 +      } else if (os_strcasecmp(cmd, "uapsd") == 0) {
 +              if (os_strcmp(value, "disable") == 0)
 +                      wpa_s->set_sta_uapsd = 0;
 +              else {
 +                      int be, bk, vi, vo;
 +                      char *pos;
 +                      /* format: BE,BK,VI,VO;max SP Length */
 +                      be = atoi(value);
 +                      pos = os_strchr(value, ',');
 +                      if (pos == NULL)
 +                              return -1;
 +                      pos++;
 +                      bk = atoi(pos);
 +                      pos = os_strchr(pos, ',');
 +                      if (pos == NULL)
 +                              return -1;
 +                      pos++;
 +                      vi = atoi(pos);
 +                      pos = os_strchr(pos, ',');
 +                      if (pos == NULL)
 +                              return -1;
 +                      pos++;
 +                      vo = atoi(pos);
 +                      /* ignore max SP Length for now */
 +
 +                      wpa_s->set_sta_uapsd = 1;
 +                      wpa_s->sta_uapsd = 0;
 +                      if (be)
 +                              wpa_s->sta_uapsd |= BIT(0);
 +                      if (bk)
 +                              wpa_s->sta_uapsd |= BIT(1);
 +                      if (vi)
 +                              wpa_s->sta_uapsd |= BIT(2);
 +                      if (vo)
 +                              wpa_s->sta_uapsd |= BIT(3);
 +              }
 +      } else if (os_strcasecmp(cmd, "ps") == 0) {
 +              ret = wpa_drv_set_p2p_powersave(wpa_s, atoi(value), -1, -1);
 +#ifdef CONFIG_WIFI_DISPLAY
 +      } else if (os_strcasecmp(cmd, "wifi_display") == 0) {
 +              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);
 +      } else if (os_strcasecmp(cmd, "disallow_aps") == 0) {
 +              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) {
-                       u8 ssid_buf[MAX_SSID_LEN];
++              ret = wpas_ctrl_set_band(wpa_s, value);
 +      } else {
 +              value[-1] = '=';
 +              ret = wpa_config_process_global(wpa_s->conf, cmd, -1);
 +              if (ret == 0)
 +                      wpa_supplicant_update_config(wpa_s);
 +      }
 +
 +      return ret;
 +}
 +
 +
 +static int wpa_supplicant_ctrl_iface_get(struct wpa_supplicant *wpa_s,
 +                                       char *cmd, char *buf, size_t buflen)
 +{
 +      int res = -1;
 +
 +      wpa_printf(MSG_DEBUG, "CTRL_IFACE GET '%s'", cmd);
 +
 +      if (os_strcmp(cmd, "version") == 0) {
 +              res = os_snprintf(buf, buflen, "%s", VERSION_STR);
 +      } else if (os_strcasecmp(cmd, "country") == 0) {
 +              if (wpa_s->conf->country[0] && wpa_s->conf->country[1])
 +                      res = os_snprintf(buf, buflen, "%c%c",
 +                                        wpa_s->conf->country[0],
 +                                        wpa_s->conf->country[1]);
 +#ifdef CONFIG_WIFI_DISPLAY
 +      } else if (os_strcasecmp(cmd, "wifi_display") == 0) {
 +              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) {
 +              if (wpa_s->last_gtk_len == 0)
 +                      return -1;
 +              res = wpa_snprintf_hex(buf, buflen, wpa_s->last_gtk,
 +                                     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 (os_snprintf_error(buflen, res))
 +              return -1;
 +      return res;
 +}
 +
 +
 +#ifdef IEEE8021X_EAPOL
 +static int wpa_supplicant_ctrl_iface_preauth(struct wpa_supplicant *wpa_s,
 +                                           char *addr)
 +{
 +      u8 bssid[ETH_ALEN];
 +      struct wpa_ssid *ssid = wpa_s->current_ssid;
 +
 +      if (hwaddr_aton(addr, bssid)) {
 +              wpa_printf(MSG_DEBUG, "CTRL_IFACE PREAUTH: invalid address "
 +                         "'%s'", addr);
 +              return -1;
 +      }
 +
 +      wpa_printf(MSG_DEBUG, "CTRL_IFACE PREAUTH " MACSTR, MAC2STR(bssid));
 +      rsn_preauth_deinit(wpa_s->wpa);
 +      if (rsn_preauth_init(wpa_s->wpa, bssid, ssid ? &ssid->eap : NULL))
 +              return -1;
 +
 +      return 0;
 +}
 +#endif /* IEEE8021X_EAPOL */
 +
 +
 +#ifdef CONFIG_PEERKEY
 +/* MLME-STKSTART.request(peer) */
 +static int wpa_supplicant_ctrl_iface_stkstart(
 +      struct wpa_supplicant *wpa_s, char *addr)
 +{
 +      u8 peer[ETH_ALEN];
 +
 +      if (hwaddr_aton(addr, peer)) {
 +              wpa_printf(MSG_DEBUG, "CTRL_IFACE STKSTART: invalid "
 +                         "address '%s'", addr);
 +              return -1;
 +      }
 +
 +      wpa_printf(MSG_DEBUG, "CTRL_IFACE STKSTART " MACSTR,
 +                 MAC2STR(peer));
 +
 +      return wpa_sm_stkstart(wpa_s->wpa, peer);
 +}
 +#endif /* CONFIG_PEERKEY */
 +
 +
 +#ifdef CONFIG_TDLS
 +
 +static int wpa_supplicant_ctrl_iface_tdls_discover(
 +      struct wpa_supplicant *wpa_s, char *addr)
 +{
 +      u8 peer[ETH_ALEN];
 +      int ret;
 +
 +      if (hwaddr_aton(addr, peer)) {
 +              wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_DISCOVER: invalid "
 +                         "address '%s'", addr);
 +              return -1;
 +      }
 +
 +      wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_DISCOVER " MACSTR,
 +                 MAC2STR(peer));
 +
 +      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);
 +
 +      return ret;
 +}
 +
 +
 +static int wpa_supplicant_ctrl_iface_tdls_setup(
 +      struct wpa_supplicant *wpa_s, char *addr)
 +{
 +      u8 peer[ETH_ALEN];
 +      int ret;
 +
 +      if (hwaddr_aton(addr, peer)) {
 +              wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_SETUP: invalid "
 +                         "address '%s'", addr);
 +              return -1;
 +      }
 +
 +      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))
 +              ret = wpa_tdls_start(wpa_s->wpa, peer);
 +      else
 +              ret = wpa_drv_tdls_oper(wpa_s, TDLS_SETUP, peer);
 +
 +      return ret;
 +}
 +
 +
 +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 "
 +                         "address '%s'", addr);
 +              return -1;
 +      }
 +
 +      wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_TEARDOWN " 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_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);
 +}
 +
++
++static int wpa_supplicant_ctrl_iface_tdls_link_status(
++      struct wpa_supplicant *wpa_s, const char *addr,
++      char *buf, size_t buflen)
++{
++      u8 peer[ETH_ALEN];
++      const char *tdls_status;
++      int ret;
++
++      if (hwaddr_aton(addr, peer)) {
++              wpa_printf(MSG_DEBUG,
++                         "CTRL_IFACE TDLS_LINK_STATUS: Invalid address '%s'",
++                         addr);
++              return -1;
++      }
++      wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_LINK_STATUS " MACSTR,
++                 MAC2STR(peer));
++
++      tdls_status = wpa_tdls_get_link_status(wpa_s->wpa, peer);
++      wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_LINK_STATUS: %s", tdls_status);
++      ret = os_snprintf(buf, buflen, "TDLS link status: %s\n", tdls_status);
++      if (os_snprintf_error(buflen, ret))
++              return -1;
++
++      return ret;
++}
++
 +#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)
 +{
 +      u8 target_ap[ETH_ALEN];
 +      struct wpa_bss *bss;
 +      const u8 *mdie;
 +
 +      if (hwaddr_aton(addr, target_ap)) {
 +              wpa_printf(MSG_DEBUG, "CTRL_IFACE FT_DS: invalid "
 +                         "address '%s'", addr);
 +              return -1;
 +      }
 +
 +      wpa_printf(MSG_DEBUG, "CTRL_IFACE FT_DS " MACSTR, MAC2STR(target_ap));
 +
 +      bss = wpa_bss_get_bssid(wpa_s, target_ap);
 +      if (bss)
 +              mdie = wpa_bss_get_ie(bss, WLAN_EID_MOBILITY_DOMAIN);
 +      else
 +              mdie = NULL;
 +
 +      return wpa_ft_start_over_ds(wpa_s->wpa, target_ap, mdie);
 +}
 +#endif /* CONFIG_IEEE80211R */
 +
 +
 +#ifdef CONFIG_WPS
 +static int wpa_supplicant_ctrl_iface_wps_pbc(struct wpa_supplicant *wpa_s,
 +                                           char *cmd)
 +{
 +      u8 bssid[ETH_ALEN], *_bssid = bssid;
 +#ifdef CONFIG_P2P
 +      u8 p2p_dev_addr[ETH_ALEN];
 +#endif /* CONFIG_P2P */
 +#ifdef CONFIG_AP
 +      u8 *_p2p_dev_addr = NULL;
 +#endif /* CONFIG_AP */
 +
 +      if (cmd == NULL || os_strcmp(cmd, "any") == 0) {
 +              _bssid = NULL;
 +#ifdef CONFIG_P2P
 +      } else if (os_strncmp(cmd, "p2p_dev_addr=", 13) == 0) {
 +              if (hwaddr_aton(cmd + 13, p2p_dev_addr)) {
 +                      wpa_printf(MSG_DEBUG, "CTRL_IFACE WPS_PBC: invalid "
 +                                 "P2P Device Address '%s'",
 +                                 cmd + 13);
 +                      return -1;
 +              }
 +              _p2p_dev_addr = p2p_dev_addr;
 +#endif /* CONFIG_P2P */
 +      } else if (hwaddr_aton(cmd, bssid)) {
 +              wpa_printf(MSG_DEBUG, "CTRL_IFACE WPS_PBC: invalid BSSID '%s'",
 +                         cmd);
 +              return -1;
 +      }
 +
 +#ifdef CONFIG_AP
 +      if (wpa_s->ap_iface)
 +              return wpa_supplicant_ap_wps_pbc(wpa_s, _bssid, _p2p_dev_addr);
 +#endif /* CONFIG_AP */
 +
 +      return wpas_wps_start_pbc(wpa_s, _bssid, 0);
 +}
 +
 +
 +static int wpa_supplicant_ctrl_iface_wps_pin(struct wpa_supplicant *wpa_s,
 +                                           char *cmd, char *buf,
 +                                           size_t buflen)
 +{
 +      u8 bssid[ETH_ALEN], *_bssid = bssid;
 +      char *pin;
 +      int ret;
 +
 +      pin = os_strchr(cmd, ' ');
 +      if (pin)
 +              *pin++ = '\0';
 +
 +      if (os_strcmp(cmd, "any") == 0)
 +              _bssid = NULL;
 +      else if (os_strcmp(cmd, "get") == 0) {
 +              ret = wps_generate_pin();
 +              goto done;
 +      } else if (hwaddr_aton(cmd, bssid)) {
 +              wpa_printf(MSG_DEBUG, "CTRL_IFACE WPS_PIN: invalid BSSID '%s'",
 +                         cmd);
 +              return -1;
 +      }
 +
 +#ifdef CONFIG_AP
 +      if (wpa_s->ap_iface) {
 +              int timeout = 0;
 +              char *pos;
 +
 +              if (pin) {
 +                      pos = os_strchr(pin, ' ');
 +                      if (pos) {
 +                              *pos++ = '\0';
 +                              timeout = atoi(pos);
 +                      }
 +              }
 +
 +              return wpa_supplicant_ap_wps_pin(wpa_s, _bssid, pin,
 +                                               buf, buflen, timeout);
 +      }
 +#endif /* CONFIG_AP */
 +
 +      if (pin) {
 +              ret = wpas_wps_start_pin(wpa_s, _bssid, pin, 0,
 +                                       DEV_PW_DEFAULT);
 +              if (ret < 0)
 +                      return -1;
 +              ret = os_snprintf(buf, buflen, "%s", pin);
 +              if (os_snprintf_error(buflen, ret))
 +                      return -1;
 +              return ret;
 +      }
 +
 +      ret = wpas_wps_start_pin(wpa_s, _bssid, NULL, 0, DEV_PW_DEFAULT);
 +      if (ret < 0)
 +              return -1;
 +
 +done:
 +      /* Return the generated PIN */
 +      ret = os_snprintf(buf, buflen, "%08d", ret);
 +      if (os_snprintf_error(buflen, ret))
 +              return -1;
 +      return ret;
 +}
 +
 +
 +static int wpa_supplicant_ctrl_iface_wps_check_pin(
 +      struct wpa_supplicant *wpa_s, char *cmd, char *buf, size_t buflen)
 +{
 +      char pin[9];
 +      size_t len;
 +      char *pos;
 +      int ret;
 +
 +      wpa_hexdump_ascii_key(MSG_DEBUG, "WPS_CHECK_PIN",
 +                            (u8 *) cmd, os_strlen(cmd));
 +      for (pos = cmd, len = 0; *pos != '\0'; pos++) {
 +              if (*pos < '0' || *pos > '9')
 +                      continue;
 +              pin[len++] = *pos;
 +              if (len == 9) {
 +                      wpa_printf(MSG_DEBUG, "WPS: Too long PIN");
 +                      return -1;
 +              }
 +      }
 +      if (len != 4 && len != 8) {
 +              wpa_printf(MSG_DEBUG, "WPS: Invalid PIN length %d", (int) len);
 +              return -1;
 +      }
 +      pin[len] = '\0';
 +
 +      if (len == 8) {
 +              unsigned int pin_val;
 +              pin_val = atoi(pin);
 +              if (!wps_pin_valid(pin_val)) {
 +                      wpa_printf(MSG_DEBUG, "WPS: Invalid checksum digit");
 +                      ret = os_snprintf(buf, buflen, "FAIL-CHECKSUM\n");
 +                      if (os_snprintf_error(buflen, ret))
 +                              return -1;
 +                      return ret;
 +              }
 +      }
 +
 +      ret = os_snprintf(buf, buflen, "%s", pin);
 +      if (os_snprintf_error(buflen, ret))
 +              return -1;
 +
 +      return ret;
 +}
 +
 +
 +#ifdef CONFIG_WPS_NFC
 +
 +static int wpa_supplicant_ctrl_iface_wps_nfc(struct wpa_supplicant *wpa_s,
 +                                           char *cmd)
 +{
 +      u8 bssid[ETH_ALEN], *_bssid = bssid;
 +
 +      if (cmd == NULL || cmd[0] == '\0')
 +              _bssid = NULL;
 +      else if (hwaddr_aton(cmd, bssid))
 +              return -1;
 +
 +      return wpas_wps_start_nfc(wpa_s, NULL, _bssid, NULL, 0, 0, NULL, NULL,
 +                                0, 0);
 +}
 +
 +
 +static int wpa_supplicant_ctrl_iface_wps_nfc_config_token(
 +      struct wpa_supplicant *wpa_s, char *cmd, char *reply, size_t max_len)
 +{
 +      int ndef;
 +      struct wpabuf *buf;
 +      int res;
 +      char *pos;
 +
 +      pos = os_strchr(cmd, ' ');
 +      if (pos)
 +              *pos++ = '\0';
 +      if (os_strcmp(cmd, "WPS") == 0)
 +              ndef = 0;
 +      else if (os_strcmp(cmd, "NDEF") == 0)
 +              ndef = 1;
 +      else
 +              return -1;
 +
 +      buf = wpas_wps_nfc_config_token(wpa_s, ndef, pos);
 +      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;
 +}
 +
 +
 +static int wpa_supplicant_ctrl_iface_wps_nfc_token(
 +      struct wpa_supplicant *wpa_s, char *cmd, char *reply, size_t max_len)
 +{
 +      int ndef;
 +      struct wpabuf *buf;
 +      int res;
 +
 +      if (os_strcmp(cmd, "WPS") == 0)
 +              ndef = 0;
 +      else if (os_strcmp(cmd, "NDEF") == 0)
 +              ndef = 1;
 +      else
 +              return -1;
 +
 +      buf = wpas_wps_nfc_token(wpa_s, ndef);
 +      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;
 +}
 +
 +
 +static int wpa_supplicant_ctrl_iface_wps_nfc_tag_read(
 +      struct wpa_supplicant *wpa_s, char *pos)
 +{
 +      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)
 +              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;
 +      }
 +
 +      ret = wpas_wps_nfc_tag_read(wpa_s, buf, forced_freq);
 +      wpabuf_free(buf);
 +
 +      return ret;
 +}
 +
 +
 +static int wpas_ctrl_nfc_get_handover_req_wps(struct wpa_supplicant *wpa_s,
 +                                            char *reply, size_t max_len,
 +                                            int ndef)
 +{
 +      struct wpabuf *buf;
 +      int res;
 +
 +      buf = wpas_wps_nfc_handover_req(wpa_s, ndef);
 +      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;
 +}
 +
 +
 +#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, "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, 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;
 +}
 +
 +
 +static int wpas_ctrl_nfc_get_handover_sel_wps(struct wpa_supplicant *wpa_s,
 +                                            char *reply, size_t max_len,
 +                                            int ndef, int cr, char *uuid)
 +{
 +      struct wpabuf *buf;
 +      int res;
 +
 +      buf = wpas_wps_nfc_handover_sel(wpa_s, ndef, cr, uuid);
 +      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;
 +}
 +
 +
 +#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)
 +{
 +      char *pos, *pos2;
 +      int ndef;
 +
 +      pos = os_strchr(cmd, ' ');
 +      if (pos == NULL)
 +              return -1;
 +      *pos++ = '\0';
 +
 +      if (os_strcmp(cmd, "WPS") == 0)
 +              ndef = 0;
 +      else if (os_strcmp(cmd, "NDEF") == 0)
 +              ndef = 1;
 +      else
 +              return -1;
 +
 +      pos2 = os_strchr(pos, ' ');
 +      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_report_handover(struct wpa_supplicant *wpa_s,
 +                                       char *cmd)
 +{
 +      size_t len;
 +      struct wpabuf *req, *sel;
 +      int ret;
 +      char *pos, *role, *type, *pos2;
 +#ifdef CONFIG_P2P
 +      char *freq;
 +      int forced_freq = 0;
 +
 +      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) {
 +              wpa_printf(MSG_DEBUG, "NFC: Missing type in handover report");
 +              return -1;
 +      }
 +      *pos++ = '\0';
 +
 +      type = pos;
 +      pos = os_strchr(type, ' ');
 +      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) {
 +              wpa_printf(MSG_DEBUG, "NFC: Missing select message in handover report");
 +              return -1;
 +      }
 +      *pos2++ = '\0';
 +
 +      len = os_strlen(pos);
 +      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) {
 +              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;
 +      }
 +      len /= 2;
 +
 +      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);
 +              ret = -1;
 +      }
 +      wpabuf_free(req);
 +      wpabuf_free(sel);
 +
 +      if (ret)
 +              wpa_printf(MSG_DEBUG, "NFC: Failed to process reported handover messages");
 +
 +      return ret;
 +}
 +
 +#endif /* CONFIG_WPS_NFC */
 +
 +
 +static int wpa_supplicant_ctrl_iface_wps_reg(struct wpa_supplicant *wpa_s,
 +                                           char *cmd)
 +{
 +      u8 bssid[ETH_ALEN];
 +      char *pin;
 +      char *new_ssid;
 +      char *new_auth;
 +      char *new_encr;
 +      char *new_key;
 +      struct wps_new_ap_settings ap;
 +
 +      pin = os_strchr(cmd, ' ');
 +      if (pin == NULL)
 +              return -1;
 +      *pin++ = '\0';
 +
 +      if (hwaddr_aton(cmd, bssid)) {
 +              wpa_printf(MSG_DEBUG, "CTRL_IFACE WPS_REG: invalid BSSID '%s'",
 +                         cmd);
 +              return -1;
 +      }
 +
 +      new_ssid = os_strchr(pin, ' ');
 +      if (new_ssid == NULL)
 +              return wpas_wps_start_reg(wpa_s, bssid, pin, NULL);
 +      *new_ssid++ = '\0';
 +
 +      new_auth = os_strchr(new_ssid, ' ');
 +      if (new_auth == NULL)
 +              return -1;
 +      *new_auth++ = '\0';
 +
 +      new_encr = os_strchr(new_auth, ' ');
 +      if (new_encr == NULL)
 +              return -1;
 +      *new_encr++ = '\0';
 +
 +      new_key = os_strchr(new_encr, ' ');
 +      if (new_key == NULL)
 +              return -1;
 +      *new_key++ = '\0';
 +
 +      os_memset(&ap, 0, sizeof(ap));
 +      ap.ssid_hex = new_ssid;
 +      ap.auth = new_auth;
 +      ap.encr = new_encr;
 +      ap.key_hex = new_key;
 +      return wpas_wps_start_reg(wpa_s, bssid, pin, &ap);
 +}
 +
 +
 +#ifdef CONFIG_AP
 +static int wpa_supplicant_ctrl_iface_wps_ap_pin(struct wpa_supplicant *wpa_s,
 +                                              char *cmd, char *buf,
 +                                              size_t buflen)
 +{
 +      int timeout = 300;
 +      char *pos;
 +      const char *pin_txt;
 +
 +      if (!wpa_s->ap_iface)
 +              return -1;
 +
 +      pos = os_strchr(cmd, ' ');
 +      if (pos)
 +              *pos++ = '\0';
 +
 +      if (os_strcmp(cmd, "disable") == 0) {
 +              wpas_wps_ap_pin_disable(wpa_s);
 +              return os_snprintf(buf, buflen, "OK\n");
 +      }
 +
 +      if (os_strcmp(cmd, "random") == 0) {
 +              if (pos)
 +                      timeout = atoi(pos);
 +              pin_txt = wpas_wps_ap_pin_random(wpa_s, timeout);
 +              if (pin_txt == NULL)
 +                      return -1;
 +              return os_snprintf(buf, buflen, "%s", pin_txt);
 +      }
 +
 +      if (os_strcmp(cmd, "get") == 0) {
 +              pin_txt = wpas_wps_ap_pin_get(wpa_s);
 +              if (pin_txt == NULL)
 +                      return -1;
 +              return os_snprintf(buf, buflen, "%s", pin_txt);
 +      }
 +
 +      if (os_strcmp(cmd, "set") == 0) {
 +              char *pin;
 +              if (pos == NULL)
 +                      return -1;
 +              pin = pos;
 +              pos = os_strchr(pos, ' ');
 +              if (pos) {
 +                      *pos++ = '\0';
 +                      timeout = atoi(pos);
 +              }
 +              if (os_strlen(pin) > buflen)
 +                      return -1;
 +              if (wpas_wps_ap_pin_set(wpa_s, pin, timeout) < 0)
 +                      return -1;
 +              return os_snprintf(buf, buflen, "%s", pin);
 +      }
 +
 +      return -1;
 +}
 +#endif /* CONFIG_AP */
 +
 +
 +#ifdef CONFIG_WPS_ER
 +static int wpa_supplicant_ctrl_iface_wps_er_pin(struct wpa_supplicant *wpa_s,
 +                                              char *cmd)
 +{
 +      char *uuid = cmd, *pin, *pos;
 +      u8 addr_buf[ETH_ALEN], *addr = NULL;
 +      pin = os_strchr(uuid, ' ');
 +      if (pin == NULL)
 +              return -1;
 +      *pin++ = '\0';
 +      pos = os_strchr(pin, ' ');
 +      if (pos) {
 +              *pos++ = '\0';
 +              if (hwaddr_aton(pos, addr_buf) == 0)
 +                      addr = addr_buf;
 +      }
 +      return wpas_wps_er_add_pin(wpa_s, addr, uuid, pin);
 +}
 +
 +
 +static int wpa_supplicant_ctrl_iface_wps_er_learn(struct wpa_supplicant *wpa_s,
 +                                                char *cmd)
 +{
 +      char *uuid = cmd, *pin;
 +      pin = os_strchr(uuid, ' ');
 +      if (pin == NULL)
 +              return -1;
 +      *pin++ = '\0';
 +      return wpas_wps_er_learn(wpa_s, uuid, pin);
 +}
 +
 +
 +static int wpa_supplicant_ctrl_iface_wps_er_set_config(
 +      struct wpa_supplicant *wpa_s, char *cmd)
 +{
 +      char *uuid = cmd, *id;
 +      id = os_strchr(uuid, ' ');
 +      if (id == NULL)
 +              return -1;
 +      *id++ = '\0';
 +      return wpas_wps_er_set_config(wpa_s, uuid, atoi(id));
 +}
 +
 +
 +static int wpa_supplicant_ctrl_iface_wps_er_config(
 +      struct wpa_supplicant *wpa_s, char *cmd)
 +{
 +      char *pin;
 +      char *new_ssid;
 +      char *new_auth;
 +      char *new_encr;
 +      char *new_key;
 +      struct wps_new_ap_settings ap;
 +
 +      pin = os_strchr(cmd, ' ');
 +      if (pin == NULL)
 +              return -1;
 +      *pin++ = '\0';
 +
 +      new_ssid = os_strchr(pin, ' ');
 +      if (new_ssid == NULL)
 +              return -1;
 +      *new_ssid++ = '\0';
 +
 +      new_auth = os_strchr(new_ssid, ' ');
 +      if (new_auth == NULL)
 +              return -1;
 +      *new_auth++ = '\0';
 +
 +      new_encr = os_strchr(new_auth, ' ');
 +      if (new_encr == NULL)
 +              return -1;
 +      *new_encr++ = '\0';
 +
 +      new_key = os_strchr(new_encr, ' ');
 +      if (new_key == NULL)
 +              return -1;
 +      *new_key++ = '\0';
 +
 +      os_memset(&ap, 0, sizeof(ap));
 +      ap.ssid_hex = new_ssid;
 +      ap.auth = new_auth;
 +      ap.encr = new_encr;
 +      ap.key_hex = new_key;
 +      return wpas_wps_er_config(wpa_s, cmd, pin, &ap);
 +}
 +
 +
 +#ifdef CONFIG_WPS_NFC
 +static int wpa_supplicant_ctrl_iface_wps_er_nfc_config_token(
 +      struct wpa_supplicant *wpa_s, char *cmd, char *reply, size_t max_len)
 +{
 +      int ndef;
 +      struct wpabuf *buf;
 +      int res;
 +      char *uuid;
 +
 +      uuid = os_strchr(cmd, ' ');
 +      if (uuid == NULL)
 +              return -1;
 +      *uuid++ = '\0';
 +
 +      if (os_strcmp(cmd, "WPS") == 0)
 +              ndef = 0;
 +      else if (os_strcmp(cmd, "NDEF") == 0)
 +              ndef = 1;
 +      else
 +              return -1;
 +
 +      buf = wpas_wps_er_nfc_config_token(wpa_s, ndef, uuid);
 +      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_WPS_NFC */
 +#endif /* CONFIG_WPS_ER */
 +
 +#endif /* CONFIG_WPS */
 +
 +
 +#ifdef CONFIG_IBSS_RSN
 +static int wpa_supplicant_ctrl_iface_ibss_rsn(
 +      struct wpa_supplicant *wpa_s, char *addr)
 +{
 +      u8 peer[ETH_ALEN];
 +
 +      if (hwaddr_aton(addr, peer)) {
 +              wpa_printf(MSG_DEBUG, "CTRL_IFACE IBSS_RSN: invalid "
 +                         "address '%s'", addr);
 +              return -1;
 +      }
 +
 +      wpa_printf(MSG_DEBUG, "CTRL_IFACE IBSS_RSN " MACSTR,
 +                 MAC2STR(peer));
 +
 +      return ibss_rsn_start(wpa_s->ibss_rsn, peer);
 +}
 +#endif /* CONFIG_IBSS_RSN */
 +
 +
 +static int wpa_supplicant_ctrl_iface_ctrl_rsp(struct wpa_supplicant *wpa_s,
 +                                            char *rsp)
 +{
 +#ifdef IEEE8021X_EAPOL
 +      char *pos, *id_pos;
 +      int id;
 +      struct wpa_ssid *ssid;
 +
 +      pos = os_strchr(rsp, '-');
 +      if (pos == NULL)
 +              return -1;
 +      *pos++ = '\0';
 +      id_pos = pos;
 +      pos = os_strchr(pos, ':');
 +      if (pos == NULL)
 +              return -1;
 +      *pos++ = '\0';
 +      id = atoi(id_pos);
 +      wpa_printf(MSG_DEBUG, "CTRL_IFACE: field=%s id=%d", rsp, id);
 +      wpa_hexdump_ascii_key(MSG_DEBUG, "CTRL_IFACE: value",
 +                            (u8 *) pos, os_strlen(pos));
 +
 +      ssid = wpa_config_get_network(wpa_s->conf, id);
 +      if (ssid == NULL) {
 +              wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find SSID id=%d "
 +                         "to update", id);
 +              return -1;
 +      }
 +
 +      return wpa_supplicant_ctrl_iface_ctrl_rsp_handle(wpa_s, ssid, rsp,
 +                                                       pos);
 +#else /* IEEE8021X_EAPOL */
 +      wpa_printf(MSG_DEBUG, "CTRL_IFACE: 802.1X not included");
 +      return -1;
 +#endif /* IEEE8021X_EAPOL */
 +}
 +
 +
 +static int wpa_supplicant_ctrl_iface_status(struct wpa_supplicant *wpa_s,
 +                                          const char *params,
 +                                          char *buf, size_t buflen)
 +{
 +      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;
 +      end = buf + buflen;
 +      if (wpa_s->wpa_state >= WPA_ASSOCIATED) {
 +              struct wpa_ssid *ssid = wpa_s->current_ssid;
 +              ret = os_snprintf(pos, end - pos, "bssid=" MACSTR "\n",
 +                                MAC2STR(wpa_s->bssid));
 +              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) {
 +                      u8 *_ssid = ssid->ssid;
 +                      size_t ssid_len = ssid->ssid_len;
- static const char * debug_level_str(int level)
- {
-       switch (level) {
-       case MSG_EXCESSIVE:
-               return "EXCESSIVE";
-       case MSG_MSGDUMP:
-               return "MSGDUMP";
-       case MSG_DEBUG:
-               return "DEBUG";
-       case MSG_INFO:
-               return "INFO";
-       case MSG_WARNING:
-               return "WARNING";
-       case MSG_ERROR:
-               return "ERROR";
-       default:
-               return "?";
-       }
- }
- static int str_to_debug_level(const char *s)
- {
-       if (os_strcasecmp(s, "EXCESSIVE") == 0)
-               return MSG_EXCESSIVE;
-       if (os_strcasecmp(s, "MSGDUMP") == 0)
-               return MSG_MSGDUMP;
-       if (os_strcasecmp(s, "DEBUG") == 0)
-               return MSG_DEBUG;
-       if (os_strcasecmp(s, "INFO") == 0)
-               return MSG_INFO;
-       if (os_strcasecmp(s, "WARNING") == 0)
-               return MSG_WARNING;
-       if (os_strcasecmp(s, "ERROR") == 0)
-               return MSG_ERROR;
-       return -1;
- }
++                      u8 ssid_buf[SSID_MAX_LEN];
 +                      if (ssid_len == 0) {
 +                              int _res = wpa_drv_get_ssid(wpa_s, ssid_buf);
 +                              if (_res < 0)
 +                                      ssid_len = 0;
 +                              else
 +                                      ssid_len = _res;
 +                              _ssid = ssid_buf;
 +                      }
 +                      ret = os_snprintf(pos, end - pos, "ssid=%s\nid=%d\n",
 +                                        wpa_ssid_txt(_ssid, ssid_len),
 +                                        ssid->id);
 +                      if (os_snprintf_error(end - pos, ret))
 +                              return pos - buf;
 +                      pos += ret;
 +
 +                      if (wps && ssid->passphrase &&
 +                          wpa_key_mgmt_wpa_psk(ssid->key_mgmt) &&
 +                          (ssid->mode == WPAS_MODE_AP ||
 +                           ssid->mode == WPAS_MODE_P2P_GO)) {
 +                              ret = os_snprintf(pos, end - pos,
 +                                                "passphrase=%s\n",
 +                                                ssid->passphrase);
 +                              if (os_snprintf_error(end - pos, ret))
 +                                      return pos - buf;
 +                              pos += ret;
 +                      }
 +                      if (ssid->id_str) {
 +                              ret = os_snprintf(pos, end - pos,
 +                                                "id_str=%s\n",
 +                                                ssid->id_str);
 +                              if (os_snprintf_error(end - pos, ret))
 +                                      return pos - buf;
 +                              pos += ret;
 +                      }
 +
 +                      switch (ssid->mode) {
 +                      case WPAS_MODE_INFRA:
 +                              ret = os_snprintf(pos, end - pos,
 +                                                "mode=station\n");
 +                              break;
 +                      case WPAS_MODE_IBSS:
 +                              ret = os_snprintf(pos, end - pos,
 +                                                "mode=IBSS\n");
 +                              break;
 +                      case WPAS_MODE_AP:
 +                              ret = os_snprintf(pos, end - pos,
 +                                                "mode=AP\n");
 +                              break;
 +                      case WPAS_MODE_P2P_GO:
 +                              ret = os_snprintf(pos, end - pos,
 +                                                "mode=P2P GO\n");
 +                              break;
 +                      case WPAS_MODE_P2P_GROUP_FORMATION:
 +                              ret = os_snprintf(pos, end - pos,
 +                                                "mode=P2P GO - group "
 +                                                "formation\n");
 +                              break;
 +                      default:
 +                              ret = 0;
 +                              break;
 +                      }
 +                      if (os_snprintf_error(end - pos, ret))
 +                              return pos - buf;
 +                      pos += ret;
 +              }
 +
 +#ifdef CONFIG_AP
 +              if (wpa_s->ap_iface) {
 +                      pos += ap_ctrl_iface_wpa_get_status(wpa_s, pos,
 +                                                          end - pos,
 +                                                          verbose);
 +              } else
 +#endif /* CONFIG_AP */
 +              pos += wpa_sm_get_status(wpa_s->wpa, pos, end - pos, verbose);
 +      }
 +#ifdef CONFIG_SAE
 +      if (wpa_s->wpa_state >= WPA_ASSOCIATED &&
 +#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 (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 (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 (os_snprintf_error(end - pos, ret))
 +                      return pos - buf;
 +              pos += ret;
 +      }
 +
 +#ifdef CONFIG_P2P
 +      if (wpa_s->global->p2p) {
 +              ret = os_snprintf(pos, end - pos, "p2p_device_address=" MACSTR
 +                                "\n", MAC2STR(wpa_s->global->p2p_dev_addr));
 +              if (os_snprintf_error(end - pos, ret))
 +                      return pos - buf;
 +              pos += ret;
 +      }
 +#endif /* CONFIG_P2P */
 +
 +      ret = os_snprintf(pos, end - pos, "address=" MACSTR "\n",
 +                        MAC2STR(wpa_s->own_addr));
 +      if (os_snprintf_error(end - pos, ret))
 +              return pos - buf;
 +      pos += ret;
 +
 +#ifdef CONFIG_HS20
 +      if (wpa_s->current_bss &&
 +          (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)) {
 +              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;
 +      }
 +
 +      if (wpa_s->current_ssid) {
 +              struct wpa_cred *cred;
 +              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->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[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;
 +                      else
 +                              res = interworking_home_sp_cred(
 +                                      wpa_s, cred,
 +                                      wpa_s->current_bss->anqp->domain_name);
 +                      if (res > 0)
 +                              type = "home";
 +                      else if (res == 0)
 +                              type = "roaming";
 +                      else
 +                              type = "unknown";
 +
 +                      ret = os_snprintf(pos, end - pos, "sp_type=%s\n", type);
 +                      if (os_snprintf_error(end - pos, ret))
 +                              return pos - buf;
 +                      pos += ret;
 +
 +                      break;
 +              }
 +      }
 +#endif /* CONFIG_HS20 */
 +
 +      if (wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt) ||
 +          wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) {
 +              res = eapol_sm_get_status(wpa_s->eapol, pos, end - pos,
 +                                        verbose);
 +              if (res >= 0)
 +                      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;
 +}
 +
 +
 +static int wpa_supplicant_ctrl_iface_bssid(struct wpa_supplicant *wpa_s,
 +                                         char *cmd)
 +{
 +      char *pos;
 +      int id;
 +      struct wpa_ssid *ssid;
 +      u8 bssid[ETH_ALEN];
 +
 +      /* cmd: "<network id> <BSSID>" */
 +      pos = os_strchr(cmd, ' ');
 +      if (pos == NULL)
 +              return -1;
 +      *pos++ = '\0';
 +      id = atoi(cmd);
 +      wpa_printf(MSG_DEBUG, "CTRL_IFACE: id=%d bssid='%s'", id, pos);
 +      if (hwaddr_aton(pos, bssid)) {
 +              wpa_printf(MSG_DEBUG ,"CTRL_IFACE: invalid BSSID '%s'", pos);
 +              return -1;
 +      }
 +
 +      ssid = wpa_config_get_network(wpa_s->conf, id);
 +      if (ssid == NULL) {
 +              wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find SSID id=%d "
 +                         "to update", id);
 +              return -1;
 +      }
 +
 +      os_memcpy(ssid->bssid, bssid, ETH_ALEN);
 +      ssid->bssid_set = !is_zero_ether_addr(bssid);
 +
 +      return 0;
 +}
 +
 +
 +static int wpa_supplicant_ctrl_iface_blacklist(struct wpa_supplicant *wpa_s,
 +                                             char *cmd, char *buf,
 +                                             size_t buflen)
 +{
 +      u8 bssid[ETH_ALEN];
 +      struct wpa_blacklist *e;
 +      char *pos, *end;
 +      int ret;
 +
 +      /* cmd: "BLACKLIST [<BSSID>]" */
 +      if (*cmd == '\0') {
 +              pos = buf;
 +              end = buf + buflen;
 +              e = wpa_s->blacklist;
 +              while (e) {
 +                      ret = os_snprintf(pos, end - pos, MACSTR "\n",
 +                                        MAC2STR(e->bssid));
 +                      if (os_snprintf_error(end - pos, ret))
 +                              return pos - buf;
 +                      pos += ret;
 +                      e = e->next;
 +              }
 +              return pos - buf;
 +      }
 +
 +      cmd++;
 +      if (os_strncmp(cmd, "clear", 5) == 0) {
 +              wpa_blacklist_clear(wpa_s);
 +              os_memcpy(buf, "OK\n", 3);
 +              return 3;
 +      }
 +
 +      wpa_printf(MSG_DEBUG, "CTRL_IFACE: BLACKLIST bssid='%s'", cmd);
 +      if (hwaddr_aton(cmd, bssid)) {
 +              wpa_printf(MSG_DEBUG, "CTRL_IFACE: invalid BSSID '%s'", cmd);
 +              return -1;
 +      }
 +
 +      /*
 +       * Add the BSSID twice, so its count will be 2, causing it to be
 +       * skipped when processing scan results.
 +       */
 +      ret = wpa_blacklist_add(wpa_s, bssid);
 +      if (ret < 0)
 +              return -1;
 +      ret = wpa_blacklist_add(wpa_s, bssid);
 +      if (ret < 0)
 +              return -1;
 +      os_memcpy(buf, "OK\n", 3);
 +      return 3;
 +}
 +
 +
-       if (cmd && os_strlen(cmd)) {
 +static int wpa_supplicant_ctrl_iface_log_level(struct wpa_supplicant *wpa_s,
 +                                             char *cmd, char *buf,
 +                                             size_t buflen)
 +{
 +      char *pos, *end, *stamp;
 +      int ret;
 +
 +      /* cmd: "LOG_LEVEL [<level>]" */
 +      if (*cmd == '\0') {
 +              pos = buf;
 +              end = buf + buflen;
 +              ret = os_snprintf(pos, end - pos, "Current level: %s\n"
 +                                "Timestamp: %d\n",
 +                                debug_level_str(wpa_debug_level),
 +                                wpa_debug_timestamp);
 +              if (os_snprintf_error(end - pos, ret))
 +                      ret = 0;
 +
 +              return ret;
 +      }
 +
 +      while (*cmd == ' ')
 +              cmd++;
 +
 +      stamp = os_strchr(cmd, ' ');
 +      if (stamp) {
 +              *stamp++ = '\0';
 +              while (*stamp == ' ') {
 +                      stamp++;
 +              }
 +      }
 +
-       const u8 *ie, *ie2, *p2p, *mesh;
++      if (os_strlen(cmd)) {
 +              int level = str_to_debug_level(cmd);
 +              if (level < 0)
 +                      return -1;
 +              wpa_debug_level = level;
 +      }
 +
 +      if (stamp && os_strlen(stamp))
 +              wpa_debug_timestamp = atoi(stamp);
 +
 +      os_memcpy(buf, "OK\n", 3);
 +      return 3;
 +}
 +
 +
 +static int wpa_supplicant_ctrl_iface_list_networks(
 +      struct wpa_supplicant *wpa_s, char *cmd, char *buf, size_t buflen)
 +{
 +      char *pos, *end, *prev;
 +      struct wpa_ssid *ssid;
 +      int ret;
 +
 +      pos = buf;
 +      end = buf + buflen;
 +      ret = os_snprintf(pos, end - pos,
 +                        "network id / ssid / bssid / flags\n");
 +      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 (os_snprintf_error(end - pos, ret))
 +                      return prev - buf;
 +              pos += ret;
 +              if (ssid->bssid_set) {
 +                      ret = os_snprintf(pos, end - pos, "\t" MACSTR,
 +                                        MAC2STR(ssid->bssid));
 +              } else {
 +                      ret = os_snprintf(pos, end - pos, "\tany");
 +              }
 +              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 ?
 +                                "[CURRENT]" : "",
 +                                ssid->disabled ? "[DISABLED]" : "",
 +                                ssid->disabled_until.sec ?
 +                                "[TEMP-DISABLED]" : "",
 +                                ssid->disabled == 2 ? "[P2P-PERSISTENT]" :
 +                                "");
 +              if (os_snprintf_error(end - pos, ret))
 +                      return prev - buf;
 +              pos += ret;
 +              ret = os_snprintf(pos, end - pos, "\n");
 +              if (os_snprintf_error(end - pos, ret))
 +                      return prev - buf;
 +              pos += ret;
 +
 +              ssid = ssid->next;
 +      }
 +
 +      return pos - buf;
 +}
 +
 +
 +static char * wpa_supplicant_cipher_txt(char *pos, char *end, int cipher)
 +{
 +      int ret;
 +      ret = os_snprintf(pos, end - pos, "-");
 +      if (os_snprintf_error(end - pos, ret))
 +              return pos;
 +      pos += ret;
 +      ret = wpa_write_ciphers(pos, end, cipher, "+");
 +      if (ret < 0)
 +              return pos;
 +      pos += ret;
 +      return pos;
 +}
 +
 +
 +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;
 +      char *start;
 +      int ret;
 +
 +      ret = os_snprintf(pos, end - pos, "[%s-", proto);
 +      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 (os_snprintf_error(end - pos, ret))
 +                      return pos;
 +              pos += ret;
 +              return pos;
 +      }
 +
 +      start = pos;
 +      if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X) {
 +              ret = os_snprintf(pos, end - pos, "%sEAP",
 +                                pos == start ? "" : "+");
 +              if (os_snprintf_error(end - pos, ret))
 +                      return pos;
 +              pos += ret;
 +      }
 +      if (data.key_mgmt & WPA_KEY_MGMT_PSK) {
 +              ret = os_snprintf(pos, end - pos, "%sPSK",
 +                                pos == start ? "" : "+");
 +              if (os_snprintf_error(end - pos, ret))
 +                      return pos;
 +              pos += ret;
 +      }
 +      if (data.key_mgmt & WPA_KEY_MGMT_WPA_NONE) {
 +              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;
 +      }
 +#ifdef CONFIG_IEEE80211R
 +      if (data.key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) {
 +              ret = os_snprintf(pos, end - pos, "%sFT/EAP",
 +                                pos == start ? "" : "+");
 +              if (os_snprintf_error(end - pos, ret))
 +                      return pos;
 +              pos += ret;
 +      }
 +      if (data.key_mgmt & WPA_KEY_MGMT_FT_PSK) {
 +              ret = os_snprintf(pos, end - pos, "%sFT/PSK",
 +                                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;
 +      }
 +#endif /* CONFIG_IEEE80211R */
 +#ifdef CONFIG_IEEE80211W
 +      if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) {
 +              ret = os_snprintf(pos, end - pos, "%sEAP-SHA256",
 +                                pos == start ? "" : "+");
 +              if (os_snprintf_error(end - pos, ret))
 +                      return pos;
 +              pos += ret;
 +      }
 +      if (data.key_mgmt & WPA_KEY_MGMT_PSK_SHA256) {
 +              ret = os_snprintf(pos, end - pos, "%sPSK-SHA256",
 +                                pos == start ? "" : "+");
 +              if (os_snprintf_error(end - pos, ret))
 +                      return pos;
 +              pos += ret;
 +      }
 +#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 */
 +
++      if (data.key_mgmt & WPA_KEY_MGMT_OSEN) {
++              ret = os_snprintf(pos, end - pos, "%sOSEN",
++                                pos == start ? "" : "+");
++              if (os_snprintf_error(end - pos, ret))
++                      return pos;
++              pos += ret;
++      }
++
 +      pos = wpa_supplicant_cipher_txt(pos, end, data.pairwise_cipher);
 +
 +      if (data.capabilities & WPA_CAPABILITY_PREAUTH) {
 +              ret = os_snprintf(pos, end - pos, "-preauth");
 +              if (os_snprintf_error(end - pos, ret))
 +                      return pos;
 +              pos += ret;
 +      }
 +
 +      ret = os_snprintf(pos, end - pos, "]");
 +      if (os_snprintf_error(end - pos, ret))
 +              return pos;
 +      pos += ret;
 +
 +      return pos;
 +}
 +
 +
 +#ifdef CONFIG_WPS
 +static char * wpa_supplicant_wps_ie_txt_buf(struct wpa_supplicant *wpa_s,
 +                                          char *pos, char *end,
 +                                          struct wpabuf *wps_ie)
 +{
 +      int ret;
 +      const char *txt;
 +
 +      if (wps_ie == NULL)
 +              return pos;
 +      if (wps_is_selected_pbc_registrar(wps_ie))
 +              txt = "[WPS-PBC]";
 +      else if (wps_is_addr_authorized(wps_ie, wpa_s->own_addr, 0))
 +              txt = "[WPS-AUTH]";
 +      else if (wps_is_selected_pin_registrar(wps_ie))
 +              txt = "[WPS-PIN]";
 +      else
 +              txt = "[WPS]";
 +
 +      ret = os_snprintf(pos, end - pos, "%s", txt);
 +      if (!os_snprintf_error(end - pos, ret))
 +              pos += ret;
 +      wpabuf_free(wps_ie);
 +      return pos;
 +}
 +#endif /* CONFIG_WPS */
 +
 +
 +static char * wpa_supplicant_wps_ie_txt(struct wpa_supplicant *wpa_s,
 +                                      char *pos, char *end,
 +                                      const struct wpa_bss *bss)
 +{
 +#ifdef CONFIG_WPS
 +      struct wpabuf *wps_ie;
 +      wps_ie = wpa_bss_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE);
 +      return wpa_supplicant_wps_ie_txt_buf(wpa_s, pos, end, wps_ie);
 +#else /* CONFIG_WPS */
 +      return pos;
 +#endif /* CONFIG_WPS */
 +}
 +
 +
 +/* Format one result on one text line into a buffer. */
 +static int wpa_supplicant_ctrl_iface_scan_result(
 +      struct wpa_supplicant *wpa_s,
 +      const struct wpa_bss *bss, char *buf, size_t buflen)
 +{
 +      char *pos, *end;
 +      int ret;
-       if (!ie && !ie2 && bss->caps & IEEE80211_CAP_PRIVACY) {
++      const u8 *ie, *ie2, *osen_ie, *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)
 +              return 0; /* Do not show P2P listen discovery results here */
 +
 +      pos = buf;
 +      end = buf + buflen;
 +
 +      ret = os_snprintf(pos, end - pos, MACSTR "\t%d\t%d\t",
 +                        MAC2STR(bss->bssid), bss->freq, bss->level);
 +      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, mesh ? "RSN" : "WPA2",
 +                                          ie2, 2 + ie2[1]);
 +      }
++      osen_ie = wpa_bss_get_vendor_ie(bss, OSEN_IE_VENDOR_TYPE);
++      if (osen_ie)
++              pos = wpa_supplicant_ie_txt(pos, end, "OSEN",
++                                          osen_ie, 2 + osen_ie[1]);
 +      pos = wpa_supplicant_wps_ie_txt(wpa_s, pos, end, bss);
-                       wpa_s->own_disconnect_req = 1;
++      if (!ie && !ie2 && !osen_ie && (bss->caps & IEEE80211_CAP_PRIVACY)) {
 +              ret = os_snprintf(pos, end - pos, "[WEP]");
 +              if (os_snprintf_error(end - pos, ret))
 +                      return -1;
 +              pos += ret;
 +      }
 +      if (mesh) {
 +              ret = os_snprintf(pos, end - pos, "[MESH]");
 +              if (os_snprintf_error(end - pos, ret))
 +                      return -1;
 +              pos += ret;
 +      }
 +      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 (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 (os_snprintf_error(end - pos, ret))
 +                      return -1;
 +              pos += ret;
 +      }
 +#endif /* CONFIG_HS20 */
++#ifdef CONFIG_FST
++      if (wpa_bss_get_ie(bss, WLAN_EID_MULTI_BAND)) {
++              ret = os_snprintf(pos, end - pos, "[FST]");
++              if (os_snprintf_error(end - pos, ret))
++                      return -1;
++              pos += ret;
++      }
++#endif /* CONFIG_FST */
 +
 +      ret = os_snprintf(pos, end - pos, "\t%s",
 +                        wpa_ssid_txt(bss->ssid, bss->ssid_len));
 +      if (os_snprintf_error(end - pos, ret))
 +              return -1;
 +      pos += ret;
 +
 +      ret = os_snprintf(pos, end - pos, "\n");
 +      if (os_snprintf_error(end - pos, ret))
 +              return -1;
 +      pos += ret;
 +
 +      return pos - buf;
 +}
 +
 +
 +static int wpa_supplicant_ctrl_iface_scan_results(
 +      struct wpa_supplicant *wpa_s, char *buf, size_t buflen)
 +{
 +      char *pos, *end;
 +      struct wpa_bss *bss;
 +      int ret;
 +
 +      pos = buf;
 +      end = buf + buflen;
 +      ret = os_snprintf(pos, end - pos, "bssid / frequency / signal level / "
 +                        "flags / ssid\n");
 +      if (os_snprintf_error(end - pos, ret))
 +              return pos - buf;
 +      pos += ret;
 +
 +      dl_list_for_each(bss, &wpa_s->bss_id, struct wpa_bss, list_id) {
 +              ret = wpa_supplicant_ctrl_iface_scan_result(wpa_s, bss, pos,
 +                                                          end - pos);
 +              if (ret < 0 || ret >= end - pos)
 +                      return pos - buf;
 +              pos += ret;
 +      }
 +
 +      return pos - buf;
 +}
 +
 +
 +#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_strncmp(cmd, "any", 3) == 0) {
 +              wpa_printf(MSG_DEBUG, "CTRL_IFACE: SELECT_NETWORK any");
 +              ssid = NULL;
 +      } else {
 +              id = atoi(cmd);
 +              wpa_printf(MSG_DEBUG, "CTRL_IFACE: SELECT_NETWORK 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->disabled == 2) {
 +                      wpa_printf(MSG_DEBUG, "CTRL_IFACE: Cannot use "
 +                                 "SELECT_NETWORK with persistent P2P group");
 +                      return -1;
 +              }
 +      }
 +
 +      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_s->scan_min_time.sec = 0;
++      wpa_s->scan_min_time.usec = 0;
 +      wpa_supplicant_select_network(wpa_s, ssid);
 +
 +      return 0;
 +}
 +
 +
 +static int wpa_supplicant_ctrl_iface_enable_network(
 +      struct wpa_supplicant *wpa_s, char *cmd)
 +{
 +      int id;
 +      struct wpa_ssid *ssid;
 +
 +      /* cmd: "<network id>" or "all" */
 +      if (os_strcmp(cmd, "all") == 0) {
 +              wpa_printf(MSG_DEBUG, "CTRL_IFACE: ENABLE_NETWORK all");
 +              ssid = NULL;
 +      } else {
 +              id = atoi(cmd);
 +              wpa_printf(MSG_DEBUG, "CTRL_IFACE: ENABLE_NETWORK 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->disabled == 2) {
 +                      wpa_printf(MSG_DEBUG, "CTRL_IFACE: Cannot use "
 +                                 "ENABLE_NETWORK with persistent P2P group");
 +                      return -1;
 +              }
 +
 +              if (os_strstr(cmd, " no-connect")) {
 +                      ssid->disabled = 0;
 +                      return 0;
 +              }
 +      }
++      wpa_s->scan_min_time.sec = 0;
++      wpa_s->scan_min_time.usec = 0;
 +      wpa_supplicant_enable_network(wpa_s, ssid);
 +
 +      return 0;
 +}
 +
 +
 +static int wpa_supplicant_ctrl_iface_disable_network(
 +      struct wpa_supplicant *wpa_s, char *cmd)
 +{
 +      int id;
 +      struct wpa_ssid *ssid;
 +
 +      /* cmd: "<network id>" or "all" */
 +      if (os_strcmp(cmd, "all") == 0) {
 +              wpa_printf(MSG_DEBUG, "CTRL_IFACE: DISABLE_NETWORK all");
 +              ssid = NULL;
 +      } else {
 +              id = atoi(cmd);
 +              wpa_printf(MSG_DEBUG, "CTRL_IFACE: DISABLE_NETWORK 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->disabled == 2) {
 +                      wpa_printf(MSG_DEBUG, "CTRL_IFACE: Cannot use "
 +                                 "DISABLE_NETWORK with persistent P2P "
 +                                 "group");
 +                      return -1;
 +              }
 +      }
 +      wpa_supplicant_disable_network(wpa_s, ssid);
 +
 +      return 0;
 +}
 +
 +
 +static int wpa_supplicant_ctrl_iface_add_network(
 +      struct wpa_supplicant *wpa_s, char *buf, size_t buflen)
 +{
 +      struct wpa_ssid *ssid;
 +      int ret;
 +
 +      wpa_printf(MSG_DEBUG, "CTRL_IFACE: ADD_NETWORK");
 +
 +      ssid = wpa_config_add_network(wpa_s->conf);
 +      if (ssid == NULL)
 +              return -1;
 +
 +      wpas_notify_network_added(wpa_s, ssid);
 +
 +      ssid->disabled = 1;
 +      wpa_config_set_network_defaults(ssid);
 +
 +      ret = os_snprintf(buf, buflen, "%d\n", ssid->id);
 +      if (os_snprintf_error(buflen, ret))
 +              return -1;
 +      return ret;
 +}
 +
 +
 +static int wpa_supplicant_ctrl_iface_remove_network(
 +      struct wpa_supplicant *wpa_s, char *cmd)
 +{
 +      int id;
 +      struct wpa_ssid *ssid;
 +      int was_disabled;
 +
 +      /* cmd: "<network id>" or "all" */
 +      if (os_strcmp(cmd, "all") == 0) {
 +              wpa_printf(MSG_DEBUG, "CTRL_IFACE: REMOVE_NETWORK all");
 +              if (wpa_s->sched_scanning)
 +                      wpa_supplicant_cancel_sched_scan(wpa_s);
 +
 +              eapol_sm_invalidate_cached_session(wpa_s->eapol);
 +              if (wpa_s->current_ssid) {
 +#ifdef CONFIG_SME
 +                      wpa_s->sme.prev_bssid_set = 0;
 +#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;
++                      if (wpa_s->wpa_state >= WPA_AUTHENTICATING)
++                              wpa_s->own_disconnect_req = 1;
 +                      wpa_supplicant_deauthenticate(
 +                              wpa_s, WLAN_REASON_DEAUTH_LEAVING);
 +              }
 +              ssid = wpa_s->conf->ssid;
 +              while (ssid) {
 +                      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);
 +              }
 +              return 0;
 +      }
 +
 +      id = atoi(cmd);
 +      wpa_printf(MSG_DEBUG, "CTRL_IFACE: REMOVE_NETWORK id=%d", id);
 +
 +      ssid = wpa_config_get_network(wpa_s->conf, id);
 +      if (ssid)
 +              wpas_notify_network_removed(wpa_s, ssid);
 +      if (ssid == NULL) {
 +              wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find network "
 +                         "id=%d", id);
 +              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;
 +#endif /* CONFIG_SME */
 +              /*
 +               * Invalidate the EAP session cache if the current or
 +               * previously used network is removed.
 +               */
 +              eapol_sm_invalidate_cached_session(wpa_s->eapol);
 +      }
 +
 +      if (ssid == wpa_s->current_ssid) {
 +              wpa_sm_set_config(wpa_s->wpa, NULL);
 +              eapol_sm_notify_config(wpa_s->eapol, NULL, NULL);
 +
-       wpa_printf(MSG_DEBUG, "CTRL_IFACE: GET_NETWORK id=%d name='%s'",
++              if (wpa_s->wpa_state >= WPA_AUTHENTICATING)
++                      wpa_s->own_disconnect_req = 1;
 +              wpa_supplicant_deauthenticate(wpa_s,
 +                                            WLAN_REASON_DEAUTH_LEAVING);
 +      }
 +
 +      was_disabled = ssid->disabled;
 +
 +      if (wpa_config_remove_network(wpa_s->conf, id) < 0) {
 +              wpa_printf(MSG_DEBUG, "CTRL_IFACE: Not able to remove the "
 +                         "network id=%d", id);
 +              return -1;
 +      }
 +
 +      if (!was_disabled && wpa_s->sched_scanning) {
 +              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);
 +      }
 +
 +      return 0;
 +}
 +
 +
 +static int wpa_supplicant_ctrl_iface_update_network(
 +      struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
 +      char *name, char *value)
 +{
 +      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, ' ');
 +      if (name == NULL)
 +              return -1;
 +      *name++ = '\0';
 +
 +      value = os_strchr(name, ' ');
 +      if (value == NULL)
 +              return -1;
 +      *value++ = '\0';
 +
 +      id = atoi(cmd);
 +      wpa_printf(MSG_DEBUG, "CTRL_IFACE: SET_NETWORK id=%d name='%s'",
 +                 id, name);
 +      wpa_hexdump_ascii_key(MSG_DEBUG, "CTRL_IFACE: value",
 +                            (u8 *) value, os_strlen(value));
 +
 +      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;
 +      }
 +
 +      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 (prev_disabled != ssid->disabled &&
 +          (prev_disabled == 2 || ssid->disabled == 2))
 +              wpas_notify_network_type_changed(wpa_s, ssid);
 +
 +      return ret;
 +}
 +
 +
 +static int wpa_supplicant_ctrl_iface_get_network(
 +      struct wpa_supplicant *wpa_s, char *cmd, char *buf, size_t buflen)
 +{
 +      int id;
 +      size_t res;
 +      struct wpa_ssid *ssid;
 +      char *name, *value;
 +
 +      /* cmd: "<network id> <variable name>" */
 +      name = os_strchr(cmd, ' ');
 +      if (name == NULL || buflen == 0)
 +              return -1;
 +      *name++ = '\0';
 +
 +      id = atoi(cmd);
-               wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find network "
++      wpa_printf(MSG_EXCESSIVE, "CTRL_IFACE: GET_NETWORK id=%d name='%s'",
 +                 id, name);
 +
 +      ssid = wpa_config_get_network(wpa_s->conf, id);
 +      if (ssid == NULL) {
-               wpa_printf(MSG_DEBUG, "CTRL_IFACE: Failed to get network "
++              wpa_printf(MSG_EXCESSIVE, "CTRL_IFACE: Could not find network "
 +                         "id=%d", id);
 +              return -1;
 +      }
 +
 +      value = wpa_config_get_no_key(ssid, name);
 +      if (value == NULL) {
-       struct wpa_supplicant *wpa_s, char *cmd)
++              wpa_printf(MSG_EXCESSIVE, "CTRL_IFACE: Failed to get network "
 +                         "variable '%s'", name);
 +              return -1;
 +      }
 +
 +      res = os_strlcpy(buf, value, buflen);
 +      if (res >= buflen) {
 +              os_free(value);
 +              return -1;
 +      }
 +
 +      os_free(value);
 +
 +      return res;
 +}
 +
 +
 +static int wpa_supplicant_ctrl_iface_dup_network(
-       wpa_printf(MSG_DEBUG, "CTRL_IFACE: DUP_NETWORK id=%d -> %d name='%s'",
-                  id_s, id_d, name);
++      struct wpa_supplicant *wpa_s, char *cmd,
++      struct wpa_supplicant *dst_wpa_s)
 +{
 +      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);
-       ssid_d = wpa_config_get_network(wpa_s->conf, id_d);
++
++      wpa_printf(MSG_DEBUG,
++                 "CTRL_IFACE: DUP_NETWORK ifname=%s->%s id=%d->%d name='%s'",
++                 wpa_s->ifname, dst_wpa_s->ifname, 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;
 +      }
 +
-       ret = wpa_supplicant_ctrl_iface_update_network(wpa_s, ssid_d, name,
++      ssid_d = wpa_config_get_network(dst_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;
 +      }
 +
-       const u8 *ie, *ie2;
++      ret = wpa_supplicant_ctrl_iface_update_network(dst_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)
 +{
 +      char *pos, *end;
 +      struct wpa_cred *cred;
 +      int ret;
 +
 +      pos = buf;
 +      end = buf + buflen;
 +      ret = os_snprintf(pos, end - pos,
 +                        "cred id / realm / username / domain / imsi\n");
 +      if (os_snprintf_error(end - pos, ret))
 +              return pos - buf;
 +      pos += ret;
 +
 +      cred = wpa_s->conf->cred;
 +      while (cred) {
 +              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[0] : "",
 +                                cred->imsi ? cred->imsi : "");
 +              if (os_snprintf_error(end - pos, ret))
 +                      return pos - buf;
 +              pos += ret;
 +
 +              cred = cred->next;
 +      }
 +
 +      return pos - buf;
 +}
 +
 +
 +static int wpa_supplicant_ctrl_iface_add_cred(struct wpa_supplicant *wpa_s,
 +                                            char *buf, size_t buflen)
 +{
 +      struct wpa_cred *cred;
 +      int ret;
 +
 +      wpa_printf(MSG_DEBUG, "CTRL_IFACE: ADD_CRED");
 +
 +      cred = wpa_config_add_cred(wpa_s->conf);
 +      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 (os_snprintf_error(buflen, ret))
 +              return -1;
 +      return ret;
 +}
 +
 +
 +static int wpas_ctrl_remove_cred(struct wpa_supplicant *wpa_s,
 +                               struct wpa_cred *cred)
 +{
 +      struct wpa_ssid *ssid;
 +      char str[20];
 +      int id;
 +
 +      if (cred == NULL) {
 +              wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find cred");
 +              return -1;
 +      }
 +
 +      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);
 +                      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
 +                      ssid = ssid->next;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +static int wpa_supplicant_ctrl_iface_remove_cred(struct wpa_supplicant *wpa_s,
 +                                               char *cmd)
 +{
 +      int id;
 +      struct wpa_cred *cred, *prev;
 +
 +      /* 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;
 +              while (cred) {
 +                      prev = cred;
 +                      cred = cred->next;
 +                      wpas_ctrl_remove_cred(wpa_s, prev);
 +              }
 +              return 0;
 +      }
 +
 +      if (os_strncmp(cmd, "sp_fqdn=", 8) == 0) {
 +              wpa_printf(MSG_DEBUG, "CTRL_IFACE: REMOVE_CRED SP FQDN '%s'",
 +                         cmd + 8);
 +              cred = wpa_s->conf->cred;
 +              while (cred) {
 +                      prev = cred;
 +                      cred = cred->next;
 +                      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;
 +      }
 +
 +      id = atoi(cmd);
 +      wpa_printf(MSG_DEBUG, "CTRL_IFACE: REMOVE_CRED id=%d", id);
 +
 +      cred = wpa_config_get_cred(wpa_s->conf, id);
 +      return wpas_ctrl_remove_cred(wpa_s, cred);
 +}
 +
 +
 +static int wpa_supplicant_ctrl_iface_set_cred(struct wpa_supplicant *wpa_s,
 +                                            char *cmd)
 +{
 +      int id;
 +      struct wpa_cred *cred;
 +      char *name, *value;
 +
 +      /* cmd: "<cred id> <variable name> <value>" */
 +      name = os_strchr(cmd, ' ');
 +      if (name == NULL)
 +              return -1;
 +      *name++ = '\0';
 +
 +      value = os_strchr(name, ' ');
 +      if (value == NULL)
 +              return -1;
 +      *value++ = '\0';
 +
 +      id = atoi(cmd);
 +      wpa_printf(MSG_DEBUG, "CTRL_IFACE: SET_CRED id=%d name='%s'",
 +                 id, name);
 +      wpa_hexdump_ascii_key(MSG_DEBUG, "CTRL_IFACE: value",
 +                            (u8 *) value, os_strlen(value));
 +
 +      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;
 +      }
 +
 +      if (wpa_config_set_cred(cred, name, value, 0) < 0) {
 +              wpa_printf(MSG_DEBUG, "CTRL_IFACE: Failed to set cred "
 +                         "variable '%s'", name);
 +              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)
 +{
 +      int ret;
 +
 +      if (!wpa_s->conf->update_config) {
 +              wpa_printf(MSG_DEBUG, "CTRL_IFACE: SAVE_CONFIG - Not allowed "
 +                         "to update configuration (update_config=0)");
 +              return -1;
 +      }
 +
 +      ret = wpa_config_write(wpa_s->confname, wpa_s->conf);
 +      if (ret) {
 +              wpa_printf(MSG_DEBUG, "CTRL_IFACE: SAVE_CONFIG - Failed to "
 +                         "update configuration");
 +      } else {
 +              wpa_printf(MSG_DEBUG, "CTRL_IFACE: SAVE_CONFIG - Configuration"
 +                         " updated");
 +      }
 +
 +      return ret;
 +}
 +#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;
 +      char *pos, *end;
 +      size_t len;
 +      unsigned int i;
 +
 +      pos = buf;
 +      end = pos + buflen;
 +
 +      if (res < 0) {
 +              if (strict)
 +                      return 0;
 +              len = os_strlcpy(buf, "CCMP TKIP NONE", buflen);
 +              if (len >= buflen)
 +                      return -1;
 +              return len;
 +      }
 +
 +      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;
 +}
 +
 +
 +static int ctrl_iface_get_capability_group(int res, char *strict,
 +                                         struct wpa_driver_capa *capa,
 +                                         char *buf, size_t buflen)
 +{
 +      int ret;
 +      char *pos, *end;
 +      size_t len;
 +      unsigned int i;
 +
 +      pos = buf;
 +      end = pos + buflen;
 +
 +      if (res < 0) {
 +              if (strict)
 +                      return 0;
 +              len = os_strlcpy(buf, "CCMP TKIP WEP104 WEP40", buflen);
 +              if (len >= buflen)
 +                      return -1;
 +              return len;
 +      }
 +
 +      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;
 +              }
 +      }
 +
 +      return pos - buf;
 +}
 +
 +
 +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;
 +
 +      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;
 +}
 +
 +
 +static int ctrl_iface_get_capability_key_mgmt(int res, char *strict,
 +                                            struct wpa_driver_capa *capa,
 +                                            char *buf, size_t buflen)
 +{
 +      int ret;
 +      char *pos, *end;
 +      size_t len;
 +
 +      pos = buf;
 +      end = pos + buflen;
 +
 +      if (res < 0) {
 +              if (strict)
 +                      return 0;
 +              len = os_strlcpy(buf, "WPA-PSK WPA-EAP IEEE8021X WPA-NONE "
 +                               "NONE", buflen);
 +              if (len >= buflen)
 +                      return -1;
 +              return len;
 +      }
 +
 +      ret = os_snprintf(pos, end - pos, "NONE IEEE8021X");
 +      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 (os_snprintf_error(end - pos, ret))
 +                      return pos - buf;
 +              pos += ret;
 +      }
 +
 +      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 (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 (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;
 +}
 +
 +
 +static int ctrl_iface_get_capability_proto(int res, char *strict,
 +                                         struct wpa_driver_capa *capa,
 +                                         char *buf, size_t buflen)
 +{
 +      int ret;
 +      char *pos, *end;
 +      size_t len;
 +
 +      pos = buf;
 +      end = pos + buflen;
 +
 +      if (res < 0) {
 +              if (strict)
 +                      return 0;
 +              len = os_strlcpy(buf, "RSN WPA", buflen);
 +              if (len >= buflen)
 +                      return -1;
 +              return len;
 +      }
 +
 +      if (capa->key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA2 |
 +                            WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) {
 +              ret = os_snprintf(pos, end - pos, "%sRSN",
 +                                pos == buf ? "" : " ");
 +              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_WPA_PSK)) {
 +              ret = os_snprintf(pos, end - pos, "%sWPA",
 +                                pos == buf ? "" : " ");
 +              if (os_snprintf_error(end - pos, ret))
 +                      return pos - buf;
 +              pos += ret;
 +      }
 +
 +      return pos - buf;
 +}
 +
 +
 +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;
 +      char *pos, *end;
 +      size_t len;
 +
 +      pos = buf;
 +      end = pos + buflen;
 +
 +      if (res < 0) {
 +              if (strict)
 +                      return 0;
 +              len = os_strlcpy(buf, "OPEN SHARED LEAP", buflen);
 +              if (len >= buflen)
 +                      return -1;
 +              return len;
 +      }
 +
 +      if (capa->auth & (WPA_DRIVER_AUTH_OPEN)) {
 +              ret = os_snprintf(pos, end - pos, "%sOPEN",
 +                                pos == buf ? "" : " ");
 +              if (os_snprintf_error(end - pos, ret))
 +                      return pos - buf;
 +              pos += ret;
 +      }
 +
 +      if (capa->auth & (WPA_DRIVER_AUTH_SHARED)) {
 +              ret = os_snprintf(pos, end - pos, "%sSHARED",
 +                                pos == buf ? "" : " ");
 +              if (os_snprintf_error(end - pos, ret))
 +                      return pos - buf;
 +              pos += ret;
 +      }
 +
 +      if (capa->auth & (WPA_DRIVER_AUTH_LEAP)) {
 +              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;
 +      }
 +#endif /* CONFIG_SAE */
 +
 +      return pos - buf;
 +}
 +
 +
 +static int ctrl_iface_get_capability_modes(int res, char *strict,
 +                                         struct wpa_driver_capa *capa,
 +                                         char *buf, size_t buflen)
 +{
 +      int ret;
 +      char *pos, *end;
 +      size_t len;
 +
 +      pos = buf;
 +      end = pos + buflen;
 +
 +      if (res < 0) {
 +              if (strict)
 +                      return 0;
 +              len = os_strlcpy(buf, "IBSS AP", buflen);
 +              if (len >= buflen)
 +                      return -1;
 +              return len;
 +      }
 +
 +      if (capa->flags & WPA_DRIVER_FLAGS_IBSS) {
 +              ret = os_snprintf(pos, end - pos, "%sIBSS",
 +                                pos == buf ? "" : " ");
 +              if (os_snprintf_error(end - pos, ret))
 +                      return pos - buf;
 +              pos += ret;
 +      }
 +
 +      if (capa->flags & WPA_DRIVER_FLAGS_AP) {
 +              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;
 +      }
 +#endif /* CONFIG_MESH */
 +
 +      return pos - buf;
 +}
 +
 +
 +static int ctrl_iface_get_capability_channels(struct wpa_supplicant *wpa_s,
 +                                            char *buf, size_t buflen)
 +{
 +      struct hostapd_channel_data *chnl;
 +      int ret, i, j;
 +      char *pos, *end, *hmode;
 +
 +      pos = buf;
 +      end = pos + buflen;
 +
 +      for (j = 0; j < wpa_s->hw.num_modes; j++) {
 +              switch (wpa_s->hw.modes[j].mode) {
 +              case HOSTAPD_MODE_IEEE80211B:
 +                      hmode = "B";
 +                      break;
 +              case HOSTAPD_MODE_IEEE80211G:
 +                      hmode = "G";
 +                      break;
 +              case HOSTAPD_MODE_IEEE80211A:
 +                      hmode = "A";
 +                      break;
 +              case HOSTAPD_MODE_IEEE80211AD:
 +                      hmode = "AD";
 +                      break;
 +              default:
 +                      continue;
 +              }
 +              ret = os_snprintf(pos, end - pos, "Mode[%s] Channels:", hmode);
 +              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", chnl[i].chan);
 +                      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;
 +      }
 +
 +      return pos - buf;
 +}
 +
 +
 +static int ctrl_iface_get_capability_freq(struct wpa_supplicant *wpa_s,
 +                                        char *buf, size_t buflen)
 +{
 +      struct hostapd_channel_data *chnl;
 +      int ret, i, j;
 +      char *pos, *end, *hmode;
 +
 +      pos = buf;
 +      end = pos + buflen;
 +
 +      for (j = 0; j < wpa_s->hw.num_modes; j++) {
 +              switch (wpa_s->hw.modes[j].mode) {
 +              case HOSTAPD_MODE_IEEE80211B:
 +                      hmode = "B";
 +                      break;
 +              case HOSTAPD_MODE_IEEE80211G:
 +                      hmode = "G";
 +                      break;
 +              case HOSTAPD_MODE_IEEE80211A:
 +                      hmode = "A";
 +                      break;
 +              case HOSTAPD_MODE_IEEE80211AD:
 +                      hmode = "AD";
 +                      break;
 +              default:
 +                      continue;
 +              }
 +              ret = os_snprintf(pos, end - pos, "Mode[%s] Channels:\n",
 +                                hmode);
 +              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%s\n",
 +                                        chnl[i].chan, chnl[i].freq,
 +                                        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 (os_snprintf_error(end - pos, ret))
 +                      return pos - buf;
 +              pos += ret;
 +      }
 +
 +      return pos - buf;
 +}
 +
 +
 +static int wpa_supplicant_ctrl_iface_get_capability(
 +      struct wpa_supplicant *wpa_s, const char *_field, char *buf,
 +      size_t buflen)
 +{
 +      struct wpa_driver_capa capa;
 +      int res;
 +      char *strict;
 +      char field[30];
 +      size_t len;
 +
 +      /* Determine whether or not strict checking was requested */
 +      len = os_strlcpy(field, _field, sizeof(field));
 +      if (len >= sizeof(field))
 +              return -1;
 +      strict = os_strchr(field, ' ');
 +      if (strict != NULL) {
 +              *strict++ = '\0';
 +              if (os_strcmp(strict, "strict") != 0)
 +                      return -1;
 +      }
 +
 +      wpa_printf(MSG_DEBUG, "CTRL_IFACE: GET_CAPABILITY '%s' %s",
 +              field, strict ? strict : "");
 +
 +      if (os_strcmp(field, "eap") == 0) {
 +              return eap_get_names(buf, buflen);
 +      }
 +
 +      res = wpa_drv_get_capa(wpa_s, &capa);
 +
 +      if (os_strcmp(field, "pairwise") == 0)
 +              return ctrl_iface_get_capability_pairwise(res, strict, &capa,
 +                                                        buf, buflen);
 +
 +      if (os_strcmp(field, "group") == 0)
 +              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);
 +
 +      if (os_strcmp(field, "proto") == 0)
 +              return ctrl_iface_get_capability_proto(res, strict, &capa,
 +                                                     buf, buflen);
 +
 +      if (os_strcmp(field, "auth_alg") == 0)
 +              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,
 +                                                     buf, buflen);
 +
 +      if (os_strcmp(field, "channels") == 0)
 +              return ctrl_iface_get_capability_channels(wpa_s, buf, buflen);
 +
 +      if (os_strcmp(field, "freq") == 0)
 +              return ctrl_iface_get_capability_freq(wpa_s, buf, buflen);
 +
 +#ifdef CONFIG_TDLS
 +      if (os_strcmp(field, "tdls") == 0)
 +              return ctrl_iface_get_capability_tdls(wpa_s, buf, buflen);
 +#endif /* CONFIG_TDLS */
 +
 +#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 */
 +
++#ifdef CONFIG_FIPS
++      if (os_strcmp(field, "fips") == 0) {
++              res = os_snprintf(buf, buflen, "FIPS");
++              if (os_snprintf_error(buflen, res))
++                      return -1;
++              return res;
++      }
++#endif /* CONFIG_FIPS */
++
 +      wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown GET_CAPABILITY field '%s'",
 +                 field);
 +
 +      return -1;
 +}
 +
 +
 +#ifdef CONFIG_INTERWORKING
 +static char * anqp_add_hex(char *pos, char *end, const char *title,
 +                         struct wpabuf *data)
 +{
 +      char *start = pos;
 +      size_t i;
 +      int ret;
 +      const u8 *d;
 +
 +      if (data == NULL)
 +              return start;
 +
 +      ret = os_snprintf(pos, end - pos, "%s=", title);
 +      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 (os_snprintf_error(end - pos, ret))
 +                      return start;
 +              pos += ret;
 +      }
 +
 +      ret = os_snprintf(pos, end - pos, "\n");
 +      if (os_snprintf_error(end - pos, ret))
 +              return start;
 +      pos += ret;
 +
 +      return pos;
 +}
 +#endif /* CONFIG_INTERWORKING */
 +
 +
 +static int print_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
 +                        unsigned long mask, char *buf, size_t buflen)
 +{
 +      size_t i;
 +      int ret;
 +      char *pos, *end;
-               if (!ie && !ie2 && bss->caps & IEEE80211_CAP_PRIVACY) {
++      const u8 *ie, *ie2, *osen_ie;
 +
 +      pos = buf;
 +      end = buf + buflen;
 +
 +      if (mask & WPA_BSS_MASK_ID) {
 +              ret = os_snprintf(pos, end - pos, "id=%u\n", bss->id);
 +              if (os_snprintf_error(end - pos, ret))
 +                      return 0;
 +              pos += ret;
 +      }
 +
 +      if (mask & WPA_BSS_MASK_BSSID) {
 +              ret = os_snprintf(pos, end - pos, "bssid=" MACSTR "\n",
 +                                MAC2STR(bss->bssid));
 +              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 (os_snprintf_error(end - pos, ret))
 +                      return 0;
 +              pos += ret;
 +      }
 +
 +      if (mask & WPA_BSS_MASK_BEACON_INT) {
 +              ret = os_snprintf(pos, end - pos, "beacon_int=%d\n",
 +                                bss->beacon_int);
 +              if (os_snprintf_error(end - pos, ret))
 +                      return 0;
 +              pos += ret;
 +      }
 +
 +      if (mask & WPA_BSS_MASK_CAPABILITIES) {
 +              ret = os_snprintf(pos, end - pos, "capabilities=0x%04x\n",
 +                                bss->caps);
 +              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 (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 (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 (os_snprintf_error(end - pos, ret))
 +                      return 0;
 +              pos += ret;
 +      }
 +
 +      if (mask & WPA_BSS_MASK_TSF) {
 +              ret = os_snprintf(pos, end - pos, "tsf=%016llu\n",
 +                                (unsigned long long) bss->tsf);
 +              if (os_snprintf_error(end - pos, ret))
 +                      return 0;
 +              pos += ret;
 +      }
 +
 +      if (mask & WPA_BSS_MASK_AGE) {
 +              struct os_reltime now;
 +
 +              os_get_reltime(&now);
 +              ret = os_snprintf(pos, end - pos, "age=%d\n",
 +                                (int) (now.sec - bss->last_update.sec));
 +              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 (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 (os_snprintf_error(end - pos, ret))
 +                              return 0;
 +                      pos += ret;
 +              }
 +
 +              ret = os_snprintf(pos, end - pos, "\n");
 +              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 (os_snprintf_error(end - pos, ret))
 +                      return 0;
 +              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]);
++              osen_ie = wpa_bss_get_vendor_ie(bss, OSEN_IE_VENDOR_TYPE);
++              if (osen_ie)
++                      pos = wpa_supplicant_ie_txt(pos, end, "OSEN",
++                                                  osen_ie, 2 + osen_ie[1]);
 +              pos = wpa_supplicant_wps_ie_txt(wpa_s, pos, end, bss);
-               if (ret < 0 || ret >= end - pos)
++              if (!ie && !ie2 && !osen_ie &&
++                  (bss->caps & IEEE80211_CAP_PRIVACY)) {
 +                      ret = os_snprintf(pos, end - pos, "[WEP]");
 +                      if (os_snprintf_error(end - pos, ret))
 +                              return 0;
 +                      pos += ret;
 +              }
 +              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;
 +                      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) ||
 +                  wpa_bss_get_vendor_ie_beacon(bss, P2P_IE_VENDOR_TYPE)) {
 +                      ret = os_snprintf(pos, end - pos, "[P2P]");
 +                      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 (os_snprintf_error(end - pos, ret))
 +                              return 0;
 +                      pos += ret;
 +              }
 +#endif /* CONFIG_HS20 */
 +
 +              ret = os_snprintf(pos, end - pos, "\n");
 +              if (os_snprintf_error(end - pos, ret))
 +                      return 0;
 +              pos += ret;
 +      }
 +
 +      if (mask & WPA_BSS_MASK_SSID) {
 +              ret = os_snprintf(pos, end - pos, "ssid=%s\n",
 +                                wpa_ssid_txt(bss->ssid, bss->ssid_len));
 +              if (os_snprintf_error(end - pos, ret))
 +                      return 0;
 +              pos += ret;
 +      }
 +
 +#ifdef CONFIG_WPS
 +      if (mask & WPA_BSS_MASK_WPS_SCAN) {
 +              ie = (const u8 *) (bss + 1);
 +              ret = wpas_wps_scan_result_text(ie, bss->ie_len, pos, end);
-               pos += ret;
++              if (ret >= end - pos)
 +                      return 0;
-               term = os_strchr(pos + 1, ' ');
-               _seek[seek_count++] = pos + 6;
++              if (ret > 0)
++                      pos += ret;
 +      }
 +#endif /* CONFIG_WPS */
 +
 +#ifdef CONFIG_P2P
 +      if (mask & WPA_BSS_MASK_P2P_SCAN) {
 +              ie = (const u8 *) (bss + 1);
 +              ret = wpas_p2p_scan_result_text(ie, bss->ie_len, pos, end);
 +              if (ret < 0 || ret >= end - pos)
 +                      return 0;
 +              pos += ret;
 +      }
 +#endif /* CONFIG_P2P */
 +
 +#ifdef CONFIG_WIFI_DISPLAY
 +      if (mask & WPA_BSS_MASK_WIFI_DISPLAY) {
 +              struct wpabuf *wfd;
 +              ie = (const u8 *) (bss + 1);
 +              wfd = ieee802_11_vendor_ie_concat(ie, bss->ie_len,
 +                                                WFD_IE_VENDOR_TYPE);
 +              if (wfd) {
 +                      ret = os_snprintf(pos, end - pos, "wfd_subelems=");
 +                      if (os_snprintf_error(end - pos, ret)) {
 +                              wpabuf_free(wfd);
 +                              return 0;
 +                      }
 +                      pos += ret;
 +
 +                      pos += wpa_snprintf_hex(pos, end - pos,
 +                                              wpabuf_head(wfd),
 +                                              wpabuf_len(wfd));
 +                      wpabuf_free(wfd);
 +
 +                      ret = os_snprintf(pos, end - pos, "\n");
 +                      if (os_snprintf_error(end - pos, ret))
 +                              return 0;
 +                      pos += ret;
 +              }
 +      }
 +#endif /* CONFIG_WIFI_DISPLAY */
 +
 +#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",
 +                                 anqp->network_auth_type);
 +              pos = anqp_add_hex(pos, end, "anqp_roaming_consortium",
 +                                 anqp->roaming_consortium);
 +              pos = anqp_add_hex(pos, end, "anqp_ip_addr_type_availability",
 +                                 anqp->ip_addr_type_availability);
 +              pos = anqp_add_hex(pos, end, "anqp_nai_realm",
 +                                 anqp->nai_realm);
 +              pos = anqp_add_hex(pos, end, "anqp_3gpp", anqp->anqp_3gpp);
 +              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;
 +      }
 +
++#ifdef CONFIG_FST
++      if (mask & WPA_BSS_MASK_FST) {
++              ret = fst_ctrl_iface_mb_info(bss->bssid, pos, end - pos);
++              if (ret < 0 || ret >= end - pos)
++                      return 0;
++              pos += ret;
++      }
++#endif /* CONFIG_FST */
++
 +      if (mask & WPA_BSS_MASK_DELIM) {
 +              ret = os_snprintf(pos, end - pos, "====\n");
 +              if (os_snprintf_error(end - pos, ret))
 +                      return 0;
 +              pos += ret;
 +      }
 +
 +      return pos - buf;
 +}
 +
 +
 +static int wpa_supplicant_ctrl_iface_bss(struct wpa_supplicant *wpa_s,
 +                                       const char *cmd, char *buf,
 +                                       size_t buflen)
 +{
 +      u8 bssid[ETH_ALEN];
 +      size_t i;
 +      struct wpa_bss *bss;
 +      struct wpa_bss *bsslast = NULL;
 +      struct dl_list *next;
 +      int ret = 0;
 +      int len;
 +      char *ctmp, *end = buf + buflen;
 +      unsigned long mask = WPA_BSS_MASK_ALL;
 +
 +      if (os_strncmp(cmd, "RANGE=", 6) == 0) {
 +              if (os_strncmp(cmd + 6, "ALL", 3) == 0) {
 +                      bss = dl_list_first(&wpa_s->bss_id, struct wpa_bss,
 +                                          list_id);
 +                      bsslast = dl_list_last(&wpa_s->bss_id, struct wpa_bss,
 +                                             list_id);
 +              } else { /* N1-N2 */
 +                      unsigned int id1, id2;
 +
 +                      if ((ctmp = os_strchr(cmd + 6, '-')) == NULL) {
 +                              wpa_printf(MSG_INFO, "Wrong BSS range "
 +                                         "format");
 +                              return 0;
 +                      }
 +
 +                      if (*(cmd + 6) == '-')
 +                              id1 = 0;
 +                      else
 +                              id1 = atoi(cmd + 6);
 +                      ctmp++;
 +                      if (*ctmp >= '0' && *ctmp <= '9')
 +                              id2 = atoi(ctmp);
 +                      else
 +                              id2 = (unsigned int) -1;
 +                      bss = wpa_bss_get_id_range(wpa_s, id1, id2);
 +                      if (id2 == (unsigned int) -1)
 +                              bsslast = dl_list_last(&wpa_s->bss_id,
 +                                                     struct wpa_bss,
 +                                                     list_id);
 +                      else {
 +                              bsslast = wpa_bss_get_id(wpa_s, id2);
 +                              if (bsslast == NULL && bss && id2 > id1) {
 +                                      struct wpa_bss *tmp = bss;
 +                                      for (;;) {
 +                                              next = tmp->list_id.next;
 +                                              if (next == &wpa_s->bss_id)
 +                                                      break;
 +                                              tmp = dl_list_entry(
 +                                                      next, struct wpa_bss,
 +                                                      list_id);
 +                                              if (tmp->id > id2)
 +                                                      break;
 +                                              bsslast = tmp;
 +                                      }
 +                              }
 +                      }
 +              }
 +      } else if (os_strncmp(cmd, "FIRST", 5) == 0)
 +              bss = dl_list_first(&wpa_s->bss_id, struct wpa_bss, list_id);
 +      else if (os_strncmp(cmd, "LAST", 4) == 0)
 +              bss = dl_list_last(&wpa_s->bss_id, struct wpa_bss, list_id);
 +      else if (os_strncmp(cmd, "ID-", 3) == 0) {
 +              i = atoi(cmd + 3);
 +              bss = wpa_bss_get_id(wpa_s, i);
 +      } else if (os_strncmp(cmd, "NEXT-", 5) == 0) {
 +              i = atoi(cmd + 5);
 +              bss = wpa_bss_get_id(wpa_s, i);
 +              if (bss) {
 +                      next = bss->list_id.next;
 +                      if (next == &wpa_s->bss_id)
 +                              bss = NULL;
 +                      else
 +                              bss = dl_list_entry(next, struct wpa_bss,
 +                                                  list_id);
 +              }
 +#ifdef CONFIG_P2P
 +      } else if (os_strncmp(cmd, "p2p_dev_addr=", 13) == 0) {
 +              if (hwaddr_aton(cmd + 13, bssid) == 0)
 +                      bss = wpa_bss_get_p2p_dev_addr(wpa_s, bssid);
 +              else
 +                      bss = NULL;
 +#endif /* CONFIG_P2P */
 +      } else if (hwaddr_aton(cmd, bssid) == 0)
 +              bss = wpa_bss_get_bssid(wpa_s, bssid);
 +      else {
 +              struct wpa_bss *tmp;
 +              i = atoi(cmd);
 +              bss = NULL;
 +              dl_list_for_each(tmp, &wpa_s->bss_id, struct wpa_bss, list_id)
 +              {
 +                      if (i-- == 0) {
 +                              bss = tmp;
 +                              break;
 +                      }
 +              }
 +      }
 +
 +      if ((ctmp = os_strstr(cmd, "MASK=")) != NULL) {
 +              mask = strtoul(ctmp + 5, NULL, 0x10);
 +              if (mask == 0)
 +                      mask = WPA_BSS_MASK_ALL;
 +      }
 +
 +      if (bss == NULL)
 +              return 0;
 +
 +      if (bsslast == NULL)
 +              bsslast = bss;
 +      do {
 +              len = print_bss_info(wpa_s, bss, mask, buf, buflen);
 +              ret += len;
 +              buf += len;
 +              buflen -= len;
 +              if (bss == bsslast) {
 +                      if ((mask & WPA_BSS_MASK_DELIM) && len &&
 +                          (bss == dl_list_last(&wpa_s->bss_id,
 +                                               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;
 +              if (next == &wpa_s->bss_id)
 +                      break;
 +              bss = dl_list_entry(next, struct wpa_bss, list_id);
 +      } while (bss && len);
 +
 +      return ret;
 +}
 +
 +
 +static int wpa_supplicant_ctrl_iface_ap_scan(
 +      struct wpa_supplicant *wpa_s, char *cmd)
 +{
 +      int ap_scan = atoi(cmd);
 +      return wpa_supplicant_set_ap_scan(wpa_s, ap_scan);
 +}
 +
 +
 +static int wpa_supplicant_ctrl_iface_scan_interval(
 +      struct wpa_supplicant *wpa_s, char *cmd)
 +{
 +      int scan_int = atoi(cmd);
 +      return wpa_supplicant_set_scan_interval(wpa_s, scan_int);
 +}
 +
 +
 +static int wpa_supplicant_ctrl_iface_bss_expire_age(
 +      struct wpa_supplicant *wpa_s, char *cmd)
 +{
 +      int expire_age = atoi(cmd);
 +      return wpa_supplicant_set_bss_expiration_age(wpa_s, expire_age);
 +}
 +
 +
 +static int wpa_supplicant_ctrl_iface_bss_expire_count(
 +      struct wpa_supplicant *wpa_s, char *cmd)
 +{
 +      int expire_count = atoi(cmd);
 +      return wpa_supplicant_set_bss_expiration_count(wpa_s, expire_count);
 +}
 +
 +
 +static void wpa_supplicant_ctrl_iface_bss_flush(
 +      struct wpa_supplicant *wpa_s, char *cmd)
 +{
 +      int flush_age = atoi(cmd);
 +
 +      if (flush_age == 0)
 +              wpa_bss_flush(wpa_s);
 +      else
 +              wpa_bss_flush_by_age(wpa_s, flush_age);
 +}
 +
 +
 +#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");
 +      /* 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);
 +#endif /* CONFIG_IEEE80211W */
 +
 +      wpa_drv_set_key(wpa_s, WPA_ALG_NONE, wpa_s->bssid, 0, 0, NULL, 0, NULL,
 +                      0);
 +      /* MLME-SETPROTECTION.request(None) */
 +      wpa_drv_mlme_setprotection(wpa_s, wpa_s->bssid,
 +                                 MLME_SETPROTECTION_PROTECT_TYPE_NONE,
 +                                 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,
 +                                        char *addr)
 +{
 +#ifdef CONFIG_NO_SCAN_PROCESSING
 +      return -1;
 +#else /* CONFIG_NO_SCAN_PROCESSING */
 +      u8 bssid[ETH_ALEN];
 +      struct wpa_bss *bss;
 +      struct wpa_ssid *ssid = wpa_s->current_ssid;
 +
 +      if (hwaddr_aton(addr, bssid)) {
 +              wpa_printf(MSG_DEBUG, "CTRL_IFACE ROAM: invalid "
 +                         "address '%s'", addr);
 +              return -1;
 +      }
 +
 +      wpa_printf(MSG_DEBUG, "CTRL_IFACE ROAM " MACSTR, MAC2STR(bssid));
 +
 +      if (!ssid) {
 +              wpa_printf(MSG_DEBUG, "CTRL_IFACE ROAM: No network "
 +                         "configuration known for the target AP");
 +              return -1;
 +      }
 +
 +      bss = wpa_bss_get(wpa_s, bssid, ssid->ssid, ssid->ssid_len);
 +      if (!bss) {
 +              wpa_printf(MSG_DEBUG, "CTRL_IFACE ROAM: Target AP not found "
 +                         "from BSS table");
 +              return -1;
 +      }
 +
 +      /*
 +       * TODO: Find best network configuration block from configuration to
 +       * allow roaming to other networks
 +       */
 +
 +      wpa_s->reassociate = 1;
 +      wpa_supplicant_connect(wpa_s, bss, ssid);
 +
 +      return 0;
 +#endif /* CONFIG_NO_SCAN_PROCESSING */
 +}
 +
 +
 +#ifdef CONFIG_P2P
 +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"))
 +              type = P2P_FIND_PROGRESSIVE;
 +
 +      pos = os_strstr(cmd, "dev_id=");
 +      if (pos) {
 +              pos += 7;
 +              if (hwaddr_aton(pos, dev_id))
 +                      return -1;
 +              _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;
 +              search_delay = atoi(pos);
 +      } else
 +              search_delay = wpas_p2p_search_delay(wpa_s);
 +
++      pos = os_strstr(cmd, "freq=");
++      if (pos) {
++              pos += 5;
++              freq = atoi(pos);
++              if (freq <= 0)
++                      return -1;
++      }
++
 +      /* Must be searched for last, because it adds nul termination */
 +      pos = os_strstr(cmd, " seek=");
++      if (pos)
++              pos += 6;
 +      while (pos && seek_count < P2P_MAX_QUERY_HASH + 1) {
 +              char *term;
 +
-               pos = os_strstr(pos + 6, " seek=");
-               if (term)
-                       *term = '\0';
++              _seek[seek_count++] = pos;
 +              seek = _seek;
-       pos = os_strstr(cmd, "freq=");
-       if (pos) {
-               pos += 5;
-               freq = atoi(pos);
-               if (freq <= 0)
-                       return -1;
-       }
++              term = os_strchr(pos, ' ');
++              if (!term)
++                      break;
++              *term = '\0';
++              pos = os_strstr(term + 1, "seek=");
++              if (pos)
++                      pos += 5;
 +      }
 +      if (seek_count > P2P_MAX_QUERY_HASH) {
 +              seek[0] = NULL;
 +              seek_count = 1;
 +      }
 +
-                                       svc_info);
 +      return wpas_p2p_find(wpa_s, timeout, type, _dev_type != NULL, _dev_type,
 +                           _dev_id, search_delay, seek_count, seek, freq);
 +}
 +
 +
++static int p2ps_ctrl_parse_cpt_priority(const char *pos, u8 *cpt)
++{
++      const char *last = NULL;
++      const char *token;
++      long int token_len;
++      unsigned int i;
++
++      /* Expected predefined CPT names delimited by ':' */
++      for (i = 0; (token = cstr_token(pos, ": \t", &last)); i++) {
++              if (i >= P2PS_FEATURE_CAPAB_CPT_MAX) {
++                      wpa_printf(MSG_ERROR,
++                                 "P2PS: CPT name list is too long, expected up to %d names",
++                                 P2PS_FEATURE_CAPAB_CPT_MAX);
++                      cpt[0] = 0;
++                      return -1;
++              }
++
++              token_len = last - token;
++
++              if (token_len  == 3 &&
++                  os_memcmp(token, "UDP", token_len) == 0) {
++                      cpt[i] = P2PS_FEATURE_CAPAB_UDP_TRANSPORT;
++              } else if (token_len == 3 &&
++                         os_memcmp(token, "MAC", token_len) == 0) {
++                      cpt[i] = P2PS_FEATURE_CAPAB_MAC_TRANSPORT;
++              } else {
++                      wpa_printf(MSG_ERROR,
++                                 "P2PS: Unsupported CPT name '%s'", token);
++                      cpt[0] = 0;
++                      return -1;
++              }
++
++              if (isblank(*last)) {
++                      i++;
++                      break;
++              }
++      }
++      cpt[i] = 0;
++      return 0;
++}
++
++
 +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;
++      int i;
 +
 +      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;
 +
++      pos = os_strstr(cmd, "cpt=");
++      if (pos) {
++              if (p2ps_ctrl_parse_cpt_priority(pos + 4,
++                                               p2ps_prov->cpt_priority))
++                      goto invalid_args;
++      } else {
++              p2ps_prov->cpt_priority[0] = P2PS_FEATURE_CAPAB_UDP_TRANSPORT;
++      }
++
++      for (i = 0; p2ps_prov->cpt_priority[i]; i++)
++              p2ps_prov->cpt_mask |= p2ps_prov->cpt_priority[i];
++
 +      /* 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;
 +
++      p2ps_prov->pd_seeker = 1;
++
 +      return wpas_p2p_prov_disc(wpa_s, addr, NULL, WPAS_P2P_PD_FOR_ASP,
 +                                p2ps_prov);
 +}
 +
 +
 +static int p2p_ctrl_connect(struct wpa_supplicant *wpa_s, char *cmd,
 +                          char *buf, size_t buflen)
 +{
 +      u8 addr[ETH_ALEN];
 +      char *pos, *pos2;
 +      char *pin = NULL;
 +      enum p2p_wps_method wps_method;
 +      int new_pin;
 +      int ret;
 +      int persistent_group, persistent_id = -1;
 +      int join;
 +      int auth;
 +      int automatic;
 +      int go_intent = -1;
 +      int freq = 0;
 +      int pd;
 +      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|p2ps]
 +       * [persistent|persistent=<network id>]
 +       * [join] [auth] [go_intent=<0..15>] [freq=<in MHz>] [provdisc]
 +       * [ht40] [vht] [auto] */
 +
 +      if (hwaddr_aton(cmd, addr))
 +              return -1;
 +
 +      pos = cmd + 17;
 +      if (*pos != ' ')
 +              return -1;
 +      pos++;
 +
 +      persistent_group = os_strstr(pos, " persistent") != NULL;
 +      pos2 = os_strstr(pos, " persistent=");
 +      if (pos2) {
 +              struct wpa_ssid *ssid;
 +              persistent_id = atoi(pos2 + 12);
 +              ssid = wpa_config_get_network(wpa_s->conf, persistent_id);
 +              if (ssid == NULL || ssid->disabled != 2 ||
 +                  ssid->mode != WPAS_MODE_P2P_GO) {
 +                      wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find "
 +                                 "SSID id=%d for persistent P2P group (GO)",
 +                                 persistent_id);
 +                      return -1;
 +              }
 +      }
 +      join = os_strstr(pos, " join") != NULL;
 +      auth = os_strstr(pos, " auth") != NULL;
 +      automatic = os_strstr(pos, " auto") != NULL;
 +      pd = os_strstr(pos, " provdisc") != NULL;
 +      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) {
 +              pos2 += 11;
 +              go_intent = atoi(pos2);
 +              if (go_intent < 0 || go_intent > 15)
 +                      return -1;
 +      }
 +
 +      pos2 = os_strstr(pos, " freq=");
 +      if (pos2) {
 +              pos2 += 6;
 +              freq = atoi(pos2);
 +              if (freq <= 0)
 +                      return -1;
 +      }
 +
 +      if (os_strncmp(pos, "pin", 3) == 0) {
 +              /* Request random PIN (to be displayed) and enable the PIN */
 +              wps_method = WPS_PIN_DISPLAY;
 +      } else if (os_strncmp(pos, "pbc", 3) == 0) {
 +              wps_method = WPS_PBC;
 +      } else {
 +              pin = pos;
 +              pos = os_strchr(pin, ' ');
 +              wps_method = WPS_PIN_KEYPAD;
 +              if (pos) {
 +                      *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);
 +                      return 17;
 +              }
 +      }
 +
 +      new_pin = wpas_p2p_connect(wpa_s, addr, pin, wps_method,
 +                                 persistent_group, automatic, join,
 +                                 auth, go_intent, freq, persistent_id, pd,
 +                                 ht40, vht);
 +      if (new_pin == -2) {
 +              os_memcpy(buf, "FAIL-CHANNEL-UNAVAILABLE\n", 25);
 +              return 25;
 +      }
 +      if (new_pin == -3) {
 +              os_memcpy(buf, "FAIL-CHANNEL-UNSUPPORTED\n", 25);
 +              return 25;
 +      }
 +      if (new_pin < 0)
 +              return -1;
 +      if (wps_method == WPS_PIN_DISPLAY && pin == NULL) {
 +              ret = os_snprintf(buf, buflen, "%08d", new_pin);
 +              if (os_snprintf_error(buflen, ret))
 +                      return -1;
 +              return ret;
 +      }
 +
 +      os_memcpy(buf, "OK\n", 3);
 +      return 3;
 +}
 +
 +
 +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);
 +}
 +
 +
 +static int p2p_ctrl_prov_disc(struct wpa_supplicant *wpa_s, char *cmd)
 +{
 +      u8 addr[ETH_ALEN];
 +      char *pos;
 +      enum wpas_p2p_prov_disc_use use = WPAS_P2P_PD_FOR_GO_NEG;
 +
 +      /* <addr> <config method> [join|auto] */
 +
 +      if (hwaddr_aton(cmd, addr))
 +              return -1;
 +
 +      pos = cmd + 17;
 +      if (*pos != ' ')
 +              return -1;
 +      pos++;
 +
 +      if (os_strstr(pos, " join") != NULL)
 +              use = WPAS_P2P_PD_FOR_JOIN;
 +      else if (os_strstr(pos, " auto") != NULL)
 +              use = WPAS_P2P_PD_AUTO;
 +
 +      return wpas_p2p_prov_disc(wpa_s, addr, pos, use, NULL);
 +}
 +
 +
 +static int p2p_get_passphrase(struct wpa_supplicant *wpa_s, char *buf,
 +                            size_t buflen)
 +{
 +      struct wpa_ssid *ssid = wpa_s->current_ssid;
 +
 +      if (ssid == NULL || ssid->mode != WPAS_MODE_P2P_GO ||
 +          ssid->passphrase == NULL)
 +              return -1;
 +
 +      os_strlcpy(buf, ssid->passphrase, buflen);
 +      return os_strlen(buf);
 +}
 +
 +
 +static int p2p_ctrl_serv_disc_req(struct wpa_supplicant *wpa_s, char *cmd,
 +                                char *buf, size_t buflen)
 +{
 +      u64 ref;
 +      int res;
 +      u8 dst_buf[ETH_ALEN], *dst;
 +      struct wpabuf *tlvs;
 +      char *pos;
 +      size_t len;
 +
 +      if (hwaddr_aton(cmd, dst_buf))
 +              return -1;
 +      dst = dst_buf;
 +      if (dst[0] == 0 && dst[1] == 0 && dst[2] == 0 &&
 +          dst[3] == 0 && dst[4] == 0 && dst[5] == 0)
 +              dst = NULL;
 +      pos = cmd + 17;
 +      if (*pos != ' ')
 +              return -1;
 +      pos++;
 +
 +      if (os_strncmp(pos, "upnp ", 5) == 0) {
 +              u8 version;
 +              pos += 5;
 +              if (hexstr2bin(pos, &version, 1) < 0)
 +                      return -1;
 +              pos += 2;
 +              if (*pos != ' ')
 +                      return -1;
 +              pos++;
 +              ref = wpas_p2p_sd_request_upnp(wpa_s, dst, version, pos);
 +#ifdef CONFIG_WIFI_DISPLAY
 +      } 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)
 +                      return -1;
 +              len /= 2;
 +              tlvs = wpabuf_alloc(len);
 +              if (tlvs == NULL)
 +                      return -1;
 +              if (hexstr2bin(pos, wpabuf_put(tlvs, len), len) < 0) {
 +                      wpabuf_free(tlvs);
 +                      return -1;
 +              }
 +
 +              ref = wpas_p2p_sd_request(wpa_s, dst, tlvs);
 +              wpabuf_free(tlvs);
 +      }
 +      if (ref == 0)
 +              return -1;
 +      res = os_snprintf(buf, buflen, "%llx", (long long unsigned) ref);
 +      if (os_snprintf_error(buflen, res))
 +              return -1;
 +      return res;
 +}
 +
 +
 +static int p2p_ctrl_serv_disc_cancel_req(struct wpa_supplicant *wpa_s,
 +                                       char *cmd)
 +{
 +      long long unsigned val;
 +      u64 req;
 +      if (sscanf(cmd, "%llx", &val) != 1)
 +              return -1;
 +      req = val;
 +      return wpas_p2p_sd_cancel_request(wpa_s, req);
 +}
 +
 +
 +static int p2p_ctrl_serv_disc_resp(struct wpa_supplicant *wpa_s, char *cmd)
 +{
 +      int freq;
 +      u8 dst[ETH_ALEN];
 +      u8 dialog_token;
 +      struct wpabuf *resp_tlvs;
 +      char *pos, *pos2;
 +      size_t len;
 +
 +      pos = os_strchr(cmd, ' ');
 +      if (pos == NULL)
 +              return -1;
 +      *pos++ = '\0';
 +      freq = atoi(cmd);
 +      if (freq == 0)
 +              return -1;
 +
 +      if (hwaddr_aton(pos, dst))
 +              return -1;
 +      pos += 17;
 +      if (*pos != ' ')
 +              return -1;
 +      pos++;
 +
 +      pos2 = os_strchr(pos, ' ');
 +      if (pos2 == NULL)
 +              return -1;
 +      *pos2++ = '\0';
 +      dialog_token = atoi(pos);
 +
 +      len = os_strlen(pos2);
 +      if (len & 1)
 +              return -1;
 +      len /= 2;
 +      resp_tlvs = wpabuf_alloc(len);
 +      if (resp_tlvs == NULL)
 +              return -1;
 +      if (hexstr2bin(pos2, wpabuf_put(resp_tlvs, len), len) < 0) {
 +              wpabuf_free(resp_tlvs);
 +              return -1;
 +      }
 +
 +      wpas_p2p_sd_response(wpa_s, freq, dst, dialog_token, resp_tlvs);
 +      wpabuf_free(resp_tlvs);
 +      return 0;
 +}
 +
 +
 +static int p2p_ctrl_serv_disc_external(struct wpa_supplicant *wpa_s,
 +                                     char *cmd)
 +{
 +      if (os_strcmp(cmd, "0") && os_strcmp(cmd, "1"))
 +              return -1;
 +      wpa_s->p2p_sd_over_ctrl_iface = atoi(cmd);
 +      return 0;
 +}
 +
 +
 +static int p2p_ctrl_service_add_bonjour(struct wpa_supplicant *wpa_s,
 +                                      char *cmd)
 +{
 +      char *pos;
 +      size_t len;
 +      struct wpabuf *query, *resp;
 +
 +      pos = os_strchr(cmd, ' ');
 +      if (pos == NULL)
 +              return -1;
 +      *pos++ = '\0';
 +
 +      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;
 +      }
 +
 +      len = os_strlen(pos);
 +      if (len & 1) {
 +              wpabuf_free(query);
 +              return -1;
 +      }
 +      len /= 2;
 +      resp = wpabuf_alloc(len);
 +      if (resp == NULL) {
 +              wpabuf_free(query);
 +              return -1;
 +      }
 +      if (hexstr2bin(pos, wpabuf_put(resp, len), len) < 0) {
 +              wpabuf_free(query);
 +              wpabuf_free(resp);
 +              return -1;
 +      }
 +
 +      if (wpas_p2p_service_add_bonjour(wpa_s, query, resp) < 0) {
 +              wpabuf_free(query);
 +              wpabuf_free(resp);
 +              return -1;
 +      }
 +      return 0;
 +}
 +
 +
 +static int p2p_ctrl_service_add_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_add_upnp(wpa_s, version, pos);
 +}
 +
 +
 +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;
++      char *cpt_prio_str;
++      u8 cpt_prio[P2PS_FEATURE_CAPAB_CPT_MAX + 1];
 +
 +      pos = os_strchr(cmd, ' ');
 +      if (pos == NULL)
 +              return -1;
 +      *pos++ = '\0';
 +
 +      /* 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;
 +      }
 +
 +      /* Advertisement ID is mandatory */
 +      cmd = pos;
 +      pos = os_strchr(cmd, ' ');
 +      if (pos == NULL)
 +              return -1;
 +      *pos++ = '\0';
 +
 +      /* Handle Adv_ID == 0 (wildcard "org.wi-fi.wfds") internally. */
 +      if (sscanf(cmd, "%x", &adv_id) != 1 || adv_id == 0)
 +              return -1;
 +
 +      /* 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;
 +
 +      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';
 +
++      cpt_prio_str = (pos && pos[0]) ? os_strstr(pos, "cpt=") : NULL;
++      if (cpt_prio_str) {
++              pos = os_strchr(pos, ' ');
++              if (pos != NULL)
++                      *pos++ = '\0';
++
++              if (p2ps_ctrl_parse_cpt_priority(cpt_prio_str + 4, cpt_prio))
++                      return -1;
++      } else {
++              cpt_prio[0] = P2PS_FEATURE_CAPAB_UDP_TRANSPORT;
++              cpt_prio[1] = 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,
-                                        char *cmd, int freq, int ht40,
-                                        int vht)
++                                      svc_info, cpt_prio);
 +}
 +
 +
 +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 (os_strcmp(cmd, "all") == 0) {
++              wpas_p2p_service_flush_asp(wpa_s);
++              return 0;
++      }
++
 +      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)
 +              return -1;
 +      *pos++ = '\0';
 +
 +      if (os_strcmp(cmd, "bonjour") == 0)
 +              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;
 +}
 +
 +
 +static int p2p_ctrl_reject(struct wpa_supplicant *wpa_s, char *cmd)
 +{
 +      u8 addr[ETH_ALEN];
 +
 +      /* <addr> */
 +
 +      if (hwaddr_aton(cmd, addr))
 +              return -1;
 +
 +      return wpas_p2p_reject(wpa_s, addr);
 +}
 +
 +
 +static int p2p_ctrl_invite_persistent(struct wpa_supplicant *wpa_s, char *cmd)
 +{
 +      char *pos;
 +      int id;
 +      struct wpa_ssid *ssid;
 +      u8 *_peer = NULL, peer[ETH_ALEN];
 +      int freq = 0, pref_freq = 0;
 +      int ht40, vht;
 +
 +      id = atoi(cmd);
 +      pos = os_strstr(cmd, " peer=");
 +      if (pos) {
 +              pos += 6;
 +              if (hwaddr_aton(pos, peer))
 +                      return -1;
 +              _peer = peer;
 +      }
 +      ssid = wpa_config_get_network(wpa_s->conf, id);
 +      if (ssid == NULL || ssid->disabled != 2) {
 +              wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find SSID id=%d "
 +                         "for persistent P2P group",
 +                         id);
 +              return -1;
 +      }
 +
 +      pos = os_strstr(cmd, " freq=");
 +      if (pos) {
 +              pos += 6;
 +              freq = atoi(pos);
 +              if (freq <= 0)
 +                      return -1;
 +      }
 +
 +      pos = os_strstr(cmd, " pref=");
 +      if (pos) {
 +              pos += 6;
 +              pref_freq = atoi(pos);
 +              if (pref_freq <= 0)
 +                      return -1;
 +      }
 +
 +      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, vht,
 +                             pref_freq);
 +}
 +
 +
 +static int p2p_ctrl_invite_group(struct wpa_supplicant *wpa_s, char *cmd)
 +{
 +      char *pos;
 +      u8 peer[ETH_ALEN], go_dev_addr[ETH_ALEN], *go_dev = NULL;
 +
 +      pos = os_strstr(cmd, " peer=");
 +      if (!pos)
 +              return -1;
 +
 +      *pos = '\0';
 +      pos += 6;
 +      if (hwaddr_aton(pos, peer)) {
 +              wpa_printf(MSG_DEBUG, "P2P: Invalid MAC address '%s'", pos);
 +              return -1;
 +      }
 +
 +      pos = os_strstr(pos, " go_dev_addr=");
 +      if (pos) {
 +              pos += 13;
 +              if (hwaddr_aton(pos, go_dev_addr)) {
 +                      wpa_printf(MSG_DEBUG, "P2P: Invalid MAC address '%s'",
 +                                 pos);
 +                      return -1;
 +              }
 +              go_dev = go_dev_addr;
 +      }
 +
 +      return wpas_p2p_invite_group(wpa_s, cmd, peer, go_dev);
 +}
 +
 +
 +static int p2p_ctrl_invite(struct wpa_supplicant *wpa_s, char *cmd)
 +{
 +      if (os_strncmp(cmd, "persistent=", 11) == 0)
 +              return p2p_ctrl_invite_persistent(wpa_s, cmd + 11);
 +      if (os_strncmp(cmd, "group=", 6) == 0)
 +              return p2p_ctrl_invite_group(wpa_s, cmd + 6);
 +
 +      return -1;
 +}
 +
 +
 +static int p2p_ctrl_group_add_persistent(struct wpa_supplicant *wpa_s,
-       int id;
++                                       int id, int freq, int ht40, int vht)
 +{
-       id = atoi(cmd);
 +      struct wpa_ssid *ssid;
 +
-                                            NULL, 0);
 +      ssid = wpa_config_get_network(wpa_s->conf, id);
 +      if (ssid == NULL || ssid->disabled != 2) {
 +              wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find SSID id=%d "
 +                         "for persistent P2P group",
 +                         id);
 +              return -1;
 +      }
 +
 +      return wpas_p2p_group_add_persistent(wpa_s, ssid, 0, freq, 0, ht40, vht,
-       int freq = 0, ht40, vht;
-       char *pos;
++                                           NULL, 0, 0);
 +}
 +
 +
 +static int p2p_ctrl_group_add(struct wpa_supplicant *wpa_s, char *cmd)
 +{
-       pos = os_strstr(cmd, "freq=");
-       if (pos)
-               freq = atoi(pos + 5);
++      int freq = 0, persistent = 0, group_id = -1;
++      int vht = wpa_s->conf->p2p_go_vht;
++      int ht40 = wpa_s->conf->p2p_go_ht40 || vht;
++      char *token, *context = NULL;
 +
-       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;
++      while ((token = str_token(cmd, " ", &context))) {
++              if (sscanf(token, "freq=%d", &freq) == 1 ||
++                  sscanf(token, "persistent=%d", &group_id) == 1) {
++                      continue;
++              } else if (os_strcmp(token, "ht40") == 0) {
++                      ht40 = 1;
++              } else if (os_strcmp(token, "vht") == 0) {
++                      vht = 1;
++                      ht40 = 1;
++              } else if (os_strcmp(token, "persistent") == 0) {
++                      persistent = 1;
++              } else {
++                      wpa_printf(MSG_DEBUG,
++                                 "CTRL: Invalid P2P_GROUP_ADD parameter: '%s'",
++                                 token);
++                      return -1;
++              }
++      }
 +
-       if (os_strncmp(cmd, "persistent=", 11) == 0)
-               return p2p_ctrl_group_add_persistent(wpa_s, cmd + 11, freq,
-                                                    ht40, vht);
-       if (os_strcmp(cmd, "persistent") == 0 ||
-           os_strncmp(cmd, "persistent ", 11) == 0)
-               return wpas_p2p_group_add(wpa_s, 1, freq, ht40, vht);
-       if (os_strncmp(cmd, "freq=", 5) == 0)
-               return wpas_p2p_group_add(wpa_s, 0, freq, ht40, vht);
-       if (ht40)
-               return wpas_p2p_group_add(wpa_s, 0, freq, ht40, vht);
-       wpa_printf(MSG_DEBUG, "CTRL: Invalid P2P_GROUP_ADD parameters '%s'",
-                  cmd);
-       return -1;
++      if (group_id >= 0)
++              return p2p_ctrl_group_add_persistent(wpa_s, group_id,
++                                                   freq, ht40, vht);
 +
-       wpas_p2p_update_channel_list(wpa_s);
++      return wpas_p2p_group_add(wpa_s, persistent, freq, ht40, vht);
 +}
 +
 +
 +static int p2p_ctrl_peer(struct wpa_supplicant *wpa_s, char *cmd,
 +                       char *buf, size_t buflen)
 +{
 +      u8 addr[ETH_ALEN], *addr_ptr;
 +      int next, res;
 +      const struct p2p_peer_info *info;
 +      char *pos, *end;
 +      char devtype[WPS_DEV_TYPE_BUFSIZE];
 +      struct wpa_ssid *ssid;
 +      size_t i;
 +
 +      if (!wpa_s->global->p2p)
 +              return -1;
 +
 +      if (os_strcmp(cmd, "FIRST") == 0) {
 +              addr_ptr = NULL;
 +              next = 0;
 +      } else if (os_strncmp(cmd, "NEXT-", 5) == 0) {
 +              if (hwaddr_aton(cmd + 5, addr) < 0)
 +                      return -1;
 +              addr_ptr = addr;
 +              next = 1;
 +      } else {
 +              if (hwaddr_aton(cmd, addr) < 0)
 +                      return -1;
 +              addr_ptr = addr;
 +              next = 0;
 +      }
 +
 +      info = p2p_get_peer_info(wpa_s->global->p2p, addr_ptr, next);
 +      if (info == NULL)
 +              return -1;
 +
 +      pos = buf;
 +      end = buf + buflen;
 +
 +      res = os_snprintf(pos, end - pos, MACSTR "\n"
 +                        "pri_dev_type=%s\n"
 +                        "device_name=%s\n"
 +                        "manufacturer=%s\n"
 +                        "model_name=%s\n"
 +                        "model_number=%s\n"
 +                        "serial_number=%s\n"
 +                        "config_methods=0x%x\n"
 +                        "dev_capab=0x%x\n"
 +                        "group_capab=0x%x\n"
 +                        "level=%d\n",
 +                        MAC2STR(info->p2p_device_addr),
 +                        wps_dev_type_bin2str(info->pri_dev_type,
 +                                             devtype, sizeof(devtype)),
 +                        info->device_name,
 +                        info->manufacturer,
 +                        info->model_name,
 +                        info->model_number,
 +                        info->serial_number,
 +                        info->config_methods,
 +                        info->dev_capab,
 +                        info->group_capab,
 +                        info->level);
 +      if (os_snprintf_error(end - pos, res))
 +              return pos - buf;
 +      pos += res;
 +
 +      for (i = 0; i < info->wps_sec_dev_type_list_len / WPS_DEV_TYPE_LEN; i++)
 +      {
 +              const u8 *t;
 +              t = &info->wps_sec_dev_type_list[i * WPS_DEV_TYPE_LEN];
 +              res = os_snprintf(pos, end - pos, "sec_dev_type=%s\n",
 +                                wps_dev_type_bin2str(t, devtype,
 +                                                     sizeof(devtype)));
 +              if (os_snprintf_error(end - pos, res))
 +                      return pos - buf;
 +              pos += res;
 +      }
 +
 +      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 (os_snprintf_error(end - pos, res))
 +                      return pos - buf;
 +              pos += res;
 +      }
 +
 +      res = p2p_get_peer_info_txt(info, pos, end - pos);
 +      if (res < 0)
 +              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;
 +}
 +
 +
 +static int p2p_ctrl_disallow_freq(struct wpa_supplicant *wpa_s,
 +                                const char *param)
 +{
 +      unsigned int i;
 +
 +      if (wpa_s->global->p2p == NULL)
 +              return -1;
 +
 +      if (freq_range_list_parse(&wpa_s->global->p2p_disallow_freq, param) < 0)
 +              return -1;
 +
 +      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->min, freq->max);
 +      }
 +
-       const struct iphdr *ip;
++      wpas_p2p_update_channel_list(wpa_s, WPAS_P2P_CHANNEL_UPDATE_DISALLOW);
 +      return 0;
 +}
 +
 +
 +static int p2p_ctrl_set(struct wpa_supplicant *wpa_s, char *cmd)
 +{
 +      char *param;
 +
 +      if (wpa_s->global->p2p == NULL)
 +              return -1;
 +
 +      param = os_strchr(cmd, ' ');
 +      if (param == NULL)
 +              return -1;
 +      *param++ = '\0';
 +
 +      if (os_strcmp(cmd, "discoverability") == 0) {
 +              p2p_set_client_discoverability(wpa_s->global->p2p,
 +                                             atoi(param));
 +              return 0;
 +      }
 +
 +      if (os_strcmp(cmd, "managed") == 0) {
 +              p2p_set_managed_oper(wpa_s->global->p2p, atoi(param));
 +              return 0;
 +      }
 +
 +      if (os_strcmp(cmd, "listen_channel") == 0) {
 +              return p2p_set_listen_channel(wpa_s->global->p2p, 81,
 +                                            atoi(param), 1);
 +      }
 +
 +      if (os_strcmp(cmd, "ssid_postfix") == 0) {
 +              return p2p_set_ssid_postfix(wpa_s->global->p2p, (u8 *) param,
 +                                          os_strlen(param));
 +      }
 +
 +      if (os_strcmp(cmd, "noa") == 0) {
 +              char *pos;
 +              int count, start, duration;
 +              /* GO NoA parameters: count,start_offset(ms),duration(ms) */
 +              count = atoi(param);
 +              pos = os_strchr(param, ',');
 +              if (pos == NULL)
 +                      return -1;
 +              pos++;
 +              start = atoi(pos);
 +              pos = os_strchr(pos, ',');
 +              if (pos == NULL)
 +                      return -1;
 +              pos++;
 +              duration = atoi(pos);
 +              if (count < 0 || count > 255 || start < 0 || duration < 0)
 +                      return -1;
 +              if (count == 0 && duration > 0)
 +                      return -1;
 +              wpa_printf(MSG_DEBUG, "CTRL_IFACE: P2P_SET GO NoA: count=%d "
 +                         "start=%d duration=%d", count, start, duration);
 +              return wpas_p2p_set_noa(wpa_s, count, start, duration);
 +      }
 +
 +      if (os_strcmp(cmd, "ps") == 0)
 +              return wpa_drv_set_p2p_powersave(wpa_s, atoi(param), -1, -1);
 +
 +      if (os_strcmp(cmd, "oppps") == 0)
 +              return wpa_drv_set_p2p_powersave(wpa_s, -1, atoi(param), -1);
 +
 +      if (os_strcmp(cmd, "ctwindow") == 0)
 +              return wpa_drv_set_p2p_powersave(wpa_s, -1, -1, atoi(param));
 +
 +      if (os_strcmp(cmd, "disabled") == 0) {
 +              wpa_s->global->p2p_disabled = atoi(param);
 +              wpa_printf(MSG_DEBUG, "P2P functionality %s",
 +                         wpa_s->global->p2p_disabled ?
 +                         "disabled" : "enabled");
 +              if (wpa_s->global->p2p_disabled) {
 +                      wpas_p2p_stop_find(wpa_s);
 +                      os_memset(wpa_s->p2p_auth_invite, 0, ETH_ALEN);
 +                      p2p_flush(wpa_s->global->p2p);
 +              }
 +              return 0;
 +      }
 +
 +      if (os_strcmp(cmd, "conc_pref") == 0) {
 +              if (os_strcmp(param, "sta") == 0)
 +                      wpa_s->global->conc_pref = WPA_CONC_PREF_STA;
 +              else if (os_strcmp(param, "p2p") == 0)
 +                      wpa_s->global->conc_pref = WPA_CONC_PREF_P2P;
 +              else {
 +                      wpa_printf(MSG_INFO, "Invalid conc_pref value");
 +                      return -1;
 +              }
 +              wpa_printf(MSG_DEBUG, "Single channel concurrency preference: "
 +                         "%s", param);
 +              return 0;
 +      }
 +
 +      if (os_strcmp(cmd, "force_long_sd") == 0) {
 +              wpa_s->force_long_sd = atoi(param);
 +              return 0;
 +      }
 +
 +      if (os_strcmp(cmd, "peer_filter") == 0) {
 +              u8 addr[ETH_ALEN];
 +              if (hwaddr_aton(param, addr))
 +                      return -1;
 +              p2p_set_peer_filter(wpa_s->global->p2p, addr);
 +              return 0;
 +      }
 +
 +      if (os_strcmp(cmd, "cross_connect") == 0)
 +              return wpas_p2p_set_cross_connect(wpa_s, atoi(param));
 +
 +      if (os_strcmp(cmd, "go_apsd") == 0) {
 +              if (os_strcmp(param, "disable") == 0)
 +                      wpa_s->set_ap_uapsd = 0;
 +              else {
 +                      wpa_s->set_ap_uapsd = 1;
 +                      wpa_s->ap_uapsd = atoi(param);
 +              }
 +              return 0;
 +      }
 +
 +      if (os_strcmp(cmd, "client_apsd") == 0) {
 +              if (os_strcmp(param, "disable") == 0)
 +                      wpa_s->set_sta_uapsd = 0;
 +              else {
 +                      int be, bk, vi, vo;
 +                      char *pos;
 +                      /* format: BE,BK,VI,VO;max SP Length */
 +                      be = atoi(param);
 +                      pos = os_strchr(param, ',');
 +                      if (pos == NULL)
 +                              return -1;
 +                      pos++;
 +                      bk = atoi(pos);
 +                      pos = os_strchr(pos, ',');
 +                      if (pos == NULL)
 +                              return -1;
 +                      pos++;
 +                      vi = atoi(pos);
 +                      pos = os_strchr(pos, ',');
 +                      if (pos == NULL)
 +                              return -1;
 +                      pos++;
 +                      vo = atoi(pos);
 +                      /* ignore max SP Length for now */
 +
 +                      wpa_s->set_sta_uapsd = 1;
 +                      wpa_s->sta_uapsd = 0;
 +                      if (be)
 +                              wpa_s->sta_uapsd |= BIT(0);
 +                      if (bk)
 +                              wpa_s->sta_uapsd |= BIT(1);
 +                      if (vi)
 +                              wpa_s->sta_uapsd |= BIT(2);
 +                      if (vo)
 +                              wpa_s->sta_uapsd |= BIT(3);
 +              }
 +              return 0;
 +      }
 +
 +      if (os_strcmp(cmd, "disallow_freq") == 0)
 +              return p2p_ctrl_disallow_freq(wpa_s, param);
 +
 +      if (os_strcmp(cmd, "disc_int") == 0) {
 +              int min_disc_int, max_disc_int, max_disc_tu;
 +              char *pos;
 +
 +              pos = param;
 +
 +              min_disc_int = atoi(pos);
 +              pos = os_strchr(pos, ' ');
 +              if (pos == NULL)
 +                      return -1;
 +              *pos++ = '\0';
 +
 +              max_disc_int = atoi(pos);
 +              pos = os_strchr(pos, ' ');
 +              if (pos == NULL)
 +                      return -1;
 +              *pos++ = '\0';
 +
 +              max_disc_tu = atoi(pos);
 +
 +              return p2p_set_disc_int(wpa_s->global->p2p, min_disc_int,
 +                                      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);
 +
 +      return -1;
 +}
 +
 +
 +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);
++      wpa_s->parent->p2ps_method_config_any = 0;
 +      if (wpa_s->global->p2p)
 +              p2p_flush(wpa_s->global->p2p);
 +}
 +
 +
 +static int p2p_ctrl_presence_req(struct wpa_supplicant *wpa_s, char *cmd)
 +{
 +      char *pos, *pos2;
 +      unsigned int dur1 = 0, int1 = 0, dur2 = 0, int2 = 0;
 +
 +      if (cmd[0]) {
 +              pos = os_strchr(cmd, ' ');
 +              if (pos == NULL)
 +                      return -1;
 +              *pos++ = '\0';
 +              dur1 = atoi(cmd);
 +
 +              pos2 = os_strchr(pos, ' ');
 +              if (pos2)
 +                      *pos2++ = '\0';
 +              int1 = atoi(pos);
 +      } else
 +              pos2 = NULL;
 +
 +      if (pos2) {
 +              pos = os_strchr(pos2, ' ');
 +              if (pos == NULL)
 +                      return -1;
 +              *pos++ = '\0';
 +              dur2 = atoi(pos2);
 +              int2 = atoi(pos);
 +      }
 +
 +      return wpas_p2p_presence_req(wpa_s, dur1, int1, dur2, int2);
 +}
 +
 +
 +static int p2p_ctrl_ext_listen(struct wpa_supplicant *wpa_s, char *cmd)
 +{
 +      char *pos;
 +      unsigned int period = 0, interval = 0;
 +
 +      if (cmd[0]) {
 +              pos = os_strchr(cmd, ' ');
 +              if (pos == NULL)
 +                      return -1;
 +              *pos++ = '\0';
 +              period = atoi(cmd);
 +              interval = atoi(pos);
 +      }
 +
 +      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_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;
 +
 +      if (hwaddr_aton(dst, bssid)) {
 +              wpa_printf(MSG_DEBUG, "Invalid BSSID '%s'", dst);
 +              return -1;
 +      }
 +
 +      bss = wpa_bss_get_bssid(wpa_s, bssid);
 +      if (bss == NULL) {
 +              wpa_printf(MSG_DEBUG, "Could not find BSS " MACSTR,
 +                         MAC2STR(bssid));
 +              return -1;
 +      }
 +
 +      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);
 +}
 +
 +
 +static int get_anqp(struct wpa_supplicant *wpa_s, char *dst)
 +{
 +      u8 dst_addr[ETH_ALEN];
 +      int used;
 +      char *pos;
 +#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) {
 +              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;
 +              pos++;
 +      }
 +
 +      if (num_id == 0)
 +              return -1;
 +
 +      return anqp_send_req(wpa_s, dst_addr, id, num_id, subtypes);
 +}
 +
 +
 +static int gas_request(struct wpa_supplicant *wpa_s, char *cmd)
 +{
 +      u8 dst_addr[ETH_ALEN];
 +      struct wpabuf *advproto, *query = NULL;
 +      int used, ret = -1;
 +      char *pos, *end;
 +      size_t len;
 +
 +      used = hwaddr_aton2(cmd, dst_addr);
 +      if (used < 0)
 +              return -1;
 +
 +      pos = cmd + used;
 +      while (*pos == ' ')
 +              pos++;
 +
 +      /* Advertisement Protocol ID */
 +      end = os_strchr(pos, ' ');
 +      if (end)
 +              len = end - pos;
 +      else
 +              len = os_strlen(pos);
 +      if (len & 0x01)
 +              return -1;
 +      len /= 2;
 +      if (len == 0)
 +              return -1;
 +      advproto = wpabuf_alloc(len);
 +      if (advproto == NULL)
 +              return -1;
 +      if (hexstr2bin(pos, wpabuf_put(advproto, len), len) < 0)
 +              goto fail;
 +
 +      if (end) {
 +              /* Optional Query Request */
 +              pos = end + 1;
 +              while (*pos == ' ')
 +                      pos++;
 +
 +              len = os_strlen(pos);
 +              if (len) {
 +                      if (len & 0x01)
 +                              goto fail;
 +                      len /= 2;
 +                      if (len == 0)
 +                              goto fail;
 +                      query = wpabuf_alloc(len);
 +                      if (query == NULL)
 +                              goto fail;
 +                      if (hexstr2bin(pos, wpabuf_put(query, len), len) < 0)
 +                              goto fail;
 +              }
 +      }
 +
 +      ret = gas_send_request(wpa_s, dst_addr, advproto, query);
 +
 +fail:
 +      wpabuf_free(advproto);
 +      wpabuf_free(query);
 +
 +      return ret;
 +}
 +
 +
 +static int gas_response_get(struct wpa_supplicant *wpa_s, char *cmd, char *buf,
 +                          size_t buflen)
 +{
 +      u8 addr[ETH_ALEN];
 +      int dialog_token;
 +      int used;
 +      char *pos;
 +      size_t resp_len, start, requested_len;
 +      struct wpabuf *resp;
 +      int ret;
 +
 +      used = hwaddr_aton2(cmd, addr);
 +      if (used < 0)
 +              return -1;
 +
 +      pos = cmd + used;
 +      while (*pos == ' ')
 +              pos++;
 +      dialog_token = atoi(pos);
 +
 +      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(resp);
 +      start = 0;
 +      requested_len = resp_len;
 +
 +      pos = os_strchr(pos, ' ');
 +      if (pos) {
 +              start = atoi(pos);
 +              if (start > resp_len)
 +                      return os_snprintf(buf, buflen, "FAIL-Invalid range");
 +              pos = os_strchr(pos, ',');
 +              if (pos == NULL)
 +                      return -1;
 +              pos++;
 +              requested_len = atoi(pos);
 +              if (start + requested_len > resp_len)
 +                      return os_snprintf(buf, buflen, "FAIL-Invalid range");
 +      }
 +
 +      if (requested_len * 2 + 1 > buflen)
 +              return os_snprintf(buf, buflen, "FAIL-Too long response");
 +
 +      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 */
 +
 +
 +#ifdef CONFIG_HS20
 +
 +static int get_hs20_anqp(struct wpa_supplicant *wpa_s, char *dst)
 +{
 +      u8 dst_addr[ETH_ALEN];
 +      int used;
 +      char *pos;
 +      u32 subtypes = 0;
 +
 +      used = hwaddr_aton2(dst, dst_addr);
 +      if (used < 0)
 +              return -1;
 +      pos = dst + used;
 +      if (*pos == ' ')
 +              pos++;
 +      for (;;) {
 +              int num = atoi(pos);
 +              if (num <= 0 || num > 31)
 +                      return -1;
 +              subtypes |= BIT(num);
 +              pos = os_strchr(pos + 1, ',');
 +              if (pos == NULL)
 +                      break;
 +              pos++;
 +      }
 +
 +      if (subtypes == 0)
 +              return -1;
 +
 +      return hs20_anqp_send_req(wpa_s, dst_addr, subtypes, NULL, 0);
 +}
 +
 +
 +static int hs20_nai_home_realm_list(struct wpa_supplicant *wpa_s,
 +                                  const u8 *addr, const char *realm)
 +{
 +      u8 *buf;
 +      size_t rlen, len;
 +      int ret;
 +
 +      rlen = os_strlen(realm);
 +      len = 3 + rlen;
 +      buf = os_malloc(len);
 +      if (buf == NULL)
 +              return -1;
 +      buf[0] = 1; /* NAI Home Realm Count */
 +      buf[1] = 0; /* Formatted in accordance with RFC 4282 */
 +      buf[2] = rlen;
 +      os_memcpy(buf + 3, realm, rlen);
 +
 +      ret = hs20_anqp_send_req(wpa_s, addr,
 +                               BIT(HS20_STYPE_NAI_HOME_REALM_QUERY),
 +                               buf, len);
 +
 +      os_free(buf);
 +
 +      return ret;
 +}
 +
 +
 +static int hs20_get_nai_home_realm_list(struct wpa_supplicant *wpa_s,
 +                                      char *dst)
 +{
 +      struct wpa_cred *cred = wpa_s->conf->cred;
 +      u8 dst_addr[ETH_ALEN];
 +      int used;
 +      u8 *buf;
 +      size_t len;
 +      int ret;
 +
 +      used = hwaddr_aton2(dst, dst_addr);
 +      if (used < 0)
 +              return -1;
 +
 +      while (dst[used] == ' ')
 +              used++;
 +      if (os_strncmp(dst + used, "realm=", 6) == 0)
 +              return hs20_nai_home_realm_list(wpa_s, dst_addr,
 +                                              dst + used + 6);
 +
 +      len = os_strlen(dst + used);
 +
 +      if (len == 0 && cred && cred->realm)
 +              return hs20_nai_home_realm_list(wpa_s, dst_addr, cred->realm);
 +
 +      if (len & 1)
 +              return -1;
 +      len /= 2;
 +      buf = os_malloc(len);
 +      if (buf == NULL)
 +              return -1;
 +      if (hexstr2bin(dst + used, buf, len) < 0) {
 +              os_free(buf);
 +              return -1;
 +      }
 +
 +      ret = hs20_anqp_send_req(wpa_s, dst_addr,
 +                               BIT(HS20_STYPE_NAI_HOME_REALM_QUERY),
 +                               buf, len);
 +      os_free(buf);
 +
 +      return ret;
 +}
 +
 +
 +static int hs20_icon_request(struct wpa_supplicant *wpa_s, char *cmd)
 +{
 +      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
 +
 +static int wpa_supplicant_ctrl_iface_autoscan(struct wpa_supplicant *wpa_s,
 +                                            char *cmd)
 +{
 +      enum wpa_states state = wpa_s->wpa_state;
 +      char *new_params = NULL;
 +
 +      if (os_strlen(cmd) > 0) {
 +              new_params = os_strdup(cmd);
 +              if (new_params == NULL)
 +                      return -1;
 +      }
 +
 +      os_free(wpa_s->conf->autoscan);
 +      wpa_s->conf->autoscan = new_params;
 +
 +      if (wpa_s->conf->autoscan == NULL)
 +              autoscan_deinit(wpa_s);
 +      else if (state == WPA_DISCONNECTED || state == WPA_INACTIVE)
 +              autoscan_init(wpa_s, 1);
 +      else if (state == WPA_SCANNING)
 +              wpa_supplicant_reinit_autoscan(wpa_s);
 +
 +      return 0;
 +}
 +
 +#endif /* CONFIG_AUTOSCAN */
 +
 +
 +#ifdef CONFIG_WNM
 +
 +static int wpas_ctrl_iface_wnm_sleep(struct wpa_supplicant *wpa_s, char *cmd)
 +{
 +      int enter;
 +      int intval = 0;
 +      char *pos;
 +      int ret;
 +      struct wpabuf *tfs_req = NULL;
 +
 +      if (os_strncmp(cmd, "enter", 5) == 0)
 +              enter = 1;
 +      else if (os_strncmp(cmd, "exit", 4) == 0)
 +              enter = 0;
 +      else
 +              return -1;
 +
 +      pos = os_strstr(cmd, " interval=");
 +      if (pos)
 +              intval = atoi(pos + 10);
 +
 +      pos = os_strstr(cmd, " tfs_req=");
 +      if (pos) {
 +              char *end;
 +              size_t len;
 +              pos += 9;
 +              end = os_strchr(pos, ' ');
 +              if (end)
 +                      len = end - pos;
 +              else
 +                      len = os_strlen(pos);
 +              if (len & 1)
 +                      return -1;
 +              len /= 2;
 +              tfs_req = wpabuf_alloc(len);
 +              if (tfs_req == NULL)
 +                      return -1;
 +              if (hexstr2bin(pos, wpabuf_put(tfs_req, len), len) < 0) {
 +                      wpabuf_free(tfs_req);
 +                      return -1;
 +              }
 +      }
 +
 +      ret = ieee802_11_send_wnmsleep_req(wpa_s, enter ? WNM_SLEEP_MODE_ENTER :
 +                                         WNM_SLEEP_MODE_EXIT, intval,
 +                                         tfs_req);
 +      wpabuf_free(tfs_req);
 +
 +      return ret;
 +}
 +
 +
 +static int wpas_ctrl_iface_wnm_bss_query(struct wpa_supplicant *wpa_s, char *cmd)
 +{
 +      int query_reason;
 +
 +      query_reason = atoi(cmd);
 +
 +      wpa_printf(MSG_DEBUG, "CTRL_IFACE: WNM_BSS_QUERY query_reason=%d",
 +                 query_reason);
 +
 +      return wnm_send_bss_transition_mgmt_query(wpa_s, query_reason);
 +}
 +
 +#endif /* CONFIG_WNM */
 +
 +
 +static int wpa_supplicant_signal_poll(struct wpa_supplicant *wpa_s, char *buf,
 +                                    size_t buflen)
 +{
 +      struct wpa_signal_info si;
 +      int ret;
 +      char *pos, *end;
 +
 +      ret = wpa_drv_signal_poll(wpa_s, &si);
 +      if (ret)
 +              return -1;
 +
 +      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 (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;
 +      }
 +
++      if (si.avg_beacon_signal) {
++              ret = os_snprintf(pos, end - pos,
++                                "AVG_BEACON_RSSI=%d\n", si.avg_beacon_signal);
++              if (os_snprintf_error(end - pos, ret))
++                      return -1;
++              pos += ret;
++      }
++
++      return pos - buf;
++}
++
++
++static int wpas_ctrl_iface_get_pref_freq_list(
++      struct wpa_supplicant *wpa_s, char *cmd, char *buf, size_t buflen)
++{
++      unsigned int freq_list[100], num = 100, i;
++      int ret;
++      enum wpa_driver_if_type iface_type;
++      char *pos, *end;
++
++      pos = buf;
++      end = buf + buflen;
++
++      /* buf: "<interface_type>" */
++      if (os_strcmp(cmd, "STATION") == 0)
++              iface_type = WPA_IF_STATION;
++      else if (os_strcmp(cmd, "AP") == 0)
++              iface_type = WPA_IF_AP_BSS;
++      else if (os_strcmp(cmd, "P2P_GO") == 0)
++              iface_type = WPA_IF_P2P_GO;
++      else if (os_strcmp(cmd, "P2P_CLIENT") == 0)
++              iface_type = WPA_IF_P2P_CLIENT;
++      else if (os_strcmp(cmd, "IBSS") == 0)
++              iface_type = WPA_IF_IBSS;
++      else if (os_strcmp(cmd, "TDLS") == 0)
++              iface_type = WPA_IF_TDLS;
++      else
++              return -1;
++
++      wpa_printf(MSG_DEBUG,
++                 "CTRL_IFACE: GET_PREF_FREQ_LIST iface_type=%d (%s)",
++                 iface_type, buf);
++
++      ret = wpa_drv_get_pref_freq_list(wpa_s, iface_type, &num, freq_list);
++      if (ret)
++              return -1;
++
++      for (i = 0; i < num; i++) {
++              ret = os_snprintf(pos, end - pos, "%s%u",
++                                i > 0 ? "," : "", freq_list[i]);
++              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->p2p_go_avoid_freq.num = 0;
 +      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));
 +      }
++
++      eloop_cancel_timeout(wpas_network_reenabled, wpa_s, NULL);
 +}
 +
 +
 +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;
++      struct wpa_ssid_value *ssid = NULL, *ns;
++      unsigned int ssid_count = 0;
 +
 +      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;
 +      }
 +
++#ifdef CONFIG_INTERWORKING
++      if (wpa_s->fetch_anqp_in_progress || wpa_s->network_select) {
++              wpa_printf(MSG_DEBUG,
++                         "Interworking select in progress - reject new scan");
++              *reply_len = os_snprintf(reply, reply_size, "FAIL-BUSY\n");
++              return;
++      }
++#endif /* CONFIG_INTERWORKING */
++
 +      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;
 +              }
++
++              pos = params;
++              while (pos && *pos != '\0') {
++                      if (os_strncmp(pos, "ssid ", 5) == 0) {
++                              char *end;
++
++                              pos += 5;
++                              end = pos;
++                              while (*end) {
++                                      if (*end == '\0' || *end == ' ')
++                                              break;
++                                      end++;
++                              }
++
++                              ns = os_realloc_array(
++                                      ssid, ssid_count + 1,
++                                      sizeof(struct wpa_ssid_value));
++                              if (ns == NULL) {
++                                      *reply_len = -1;
++                                      goto done;
++                              }
++                              ssid = ns;
++
++                              if ((end - pos) & 0x01 ||
++                                  end - pos > 2 * SSID_MAX_LEN ||
++                                  hexstr2bin(pos, ssid[ssid_count].ssid,
++                                             (end - pos) / 2) < 0) {
++                                      wpa_printf(MSG_DEBUG,
++                                                 "Invalid SSID value '%s'",
++                                                 pos);
++                                      *reply_len = -1;
++                                      goto done;
++                              }
++                              ssid[ssid_count].ssid_len = (end - pos) / 2;
++                              wpa_hexdump_ascii(MSG_DEBUG, "scan SSID",
++                                                ssid[ssid_count].ssid,
++                                                ssid[ssid_count].ssid_len);
++                              ssid_count++;
++                              pos = end;
++                      }
++
++                      pos = os_strchr(pos, ' ');
++                      if (pos)
++                              pos++;
++              }
++      }
++
++      wpa_s->num_ssids_from_scan_req = ssid_count;
++      os_free(wpa_s->ssids_from_scan_req);
++      if (ssid_count) {
++              wpa_s->ssids_from_scan_req = ssid;
++              ssid = NULL;
++      } else {
++              wpa_s->ssids_from_scan_req = NULL;
 +      }
 +
 +      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);
++      os_free(ssid);
 +}
 +
 +
 +#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;
-       ip = (const struct iphdr *) (eth + 1);
-       pos = (const u8 *) (ip + 1);
++      struct iphdr ip;
 +      const u8 *pos;
 +      unsigned int i;
 +
 +      if (len != HWSIM_PACKETLEN)
 +              return;
 +
 +      eth = (const struct ether_header *) buf;
-       if (ip->ihl != 5 || ip->version != 4 ||
-           ntohs(ip->tot_len) != HWSIM_IP_LEN)
++      os_memcpy(&ip, eth + 1, sizeof(ip));
++      pos = &buf[sizeof(*eth) + sizeof(ip)];
 +
-       for (i = 0; i < HWSIM_IP_LEN - sizeof(*ip); i++) {
++      if (ip.ihl != 5 || ip.version != 4 ||
++          ntohs(ip.tot_len) != HWSIM_IP_LEN)
 +              return;
 +
-       u8 buf[HWSIM_PACKETLEN];
++      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;
-       eth = (struct ether_header *) buf;
++      u8 buf[2 + 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;
 +
-       ip->saddr = htonl(192 << 24 | 168 << 16 | 1 << 8 | 1);
-       ip->daddr = htonl(192 << 24 | 168 << 16 | 1 << 8 | 2);
++      eth = (struct ether_header *) &buf[2];
 +      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;
-       if (l2_packet_send(wpa_s->l2_test, dst, ETHERTYPE_IP, buf,
++      ip->saddr = htonl(192U << 24 | 168 << 16 | 1 << 8 | 1);
++      ip->daddr = htonl(192U << 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 (ssid.ssid_len > 32)
++      if (l2_packet_send(wpa_s->l2_test, dst, ETHERTYPE_IP, &buf[2],
 +                         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 */
 +}
 +
++
++static int wpas_ctrl_test_fail(struct wpa_supplicant *wpa_s, char *cmd)
++{
++#ifdef WPA_TRACE_BFD
++      extern char wpa_trace_test_fail_func[256];
++      extern unsigned int wpa_trace_test_fail_after;
++      char *pos;
++
++      wpa_trace_test_fail_after = atoi(cmd);
++      pos = os_strchr(cmd, ':');
++      if (pos) {
++              pos++;
++              os_strlcpy(wpa_trace_test_fail_func, pos,
++                         sizeof(wpa_trace_test_fail_func));
++      } else {
++              wpa_trace_test_fail_after = 0;
++      }
++      return 0;
++#else /* WPA_TRACE_BFD */
++      return -1;
++#endif /* WPA_TRACE_BFD */
++}
++
++
++static int wpas_ctrl_get_fail(struct wpa_supplicant *wpa_s,
++                                  char *buf, size_t buflen)
++{
++#ifdef WPA_TRACE_BFD
++      extern char wpa_trace_test_fail_func[256];
++      extern unsigned int wpa_trace_test_fail_after;
++
++      return os_snprintf(buf, buflen, "%u:%s", wpa_trace_test_fail_after,
++                         wpa_trace_test_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 (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);
 +      }
 +}
 +
 +
 +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);
-               int level = MSG_DEBUG;
-               if (os_strcmp(buf, "PING") == 0)
-                       level = MSG_EXCESSIVE;
++              if (ssid.ssid_len > SSID_MAX_LEN)
 +                      return -1;
 +              ssid.ssid = (u8 *) (cmd + 6);
 +              ssid_p = &ssid;
 +      }
 +
 +      ret = wpas_rrm_send_neighbor_rep_request(wpa_s, ssid_p,
 +                                               wpas_ctrl_neighbor_rep_cb,
 +                                               wpa_s);
 +
 +      return ret;
 +}
 +
 +
 +static int wpas_ctrl_iface_erp_flush(struct wpa_supplicant *wpa_s)
 +{
 +      eapol_sm_erp_flush(wpa_s->eapol);
 +      return 0;
 +}
 +
 +
 +static int wpas_ctrl_iface_mac_rand_scan(struct wpa_supplicant *wpa_s,
 +                                       char *cmd)
 +{
 +      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;
 +              }
 +      }
 +
 +      if (!type) {
 +              wpa_printf(MSG_INFO, "CTRL: MAC_RAND_SCAN no type specified");
 +              return -1;
 +      }
 +
 +      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;
 +      }
 +
 +      if (enable > 1) {
 +              wpa_printf(MSG_INFO,
 +                         "CTRL: MAC_RAND_SCAN enable=<0/1> not specified");
 +              return -1;
 +      }
 +
 +      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;
 +      }
 +
 +      if ((addr && !mask) || (!addr && mask)) {
 +              wpa_printf(MSG_INFO,
 +                         "CTRL: MAC_RAND_SCAN invalid addr/mask combination");
 +              return -1;
 +      }
 +
 +      if (addr && mask && (!(mask[0] & 0x01) || (addr[0] & 0x01))) {
 +              wpa_printf(MSG_INFO,
 +                         "CTRL: MAC_RAND_SCAN cannot allow multicast address");
 +              return -1;
 +      }
 +
 +      if (type & MAC_ADDR_RAND_SCAN) {
 +              wpas_mac_addr_rand_scan_set(wpa_s, MAC_ADDR_RAND_SCAN,
 +                                          addr, mask);
 +      }
 +
 +      if (type & MAC_ADDR_RAND_SCHED_SCAN) {
 +              wpas_mac_addr_rand_scan_set(wpa_s, MAC_ADDR_RAND_SCHED_SCAN,
 +                                          addr, mask);
 +
 +              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;
 +}
 +
 +
++static int wpas_ctrl_cmd_debug_level(const char *cmd)
++{
++      if (os_strcmp(cmd, "PING") == 0 ||
++          os_strncmp(cmd, "BSS ", 4) == 0 ||
++          os_strncmp(cmd, "GET_NETWORK ", 12) == 0 ||
++          os_strncmp(cmd, "STATUS", 6) == 0 ||
++          os_strncmp(cmd, "STA ", 4) == 0 ||
++          os_strncmp(cmd, "STA-", 4) == 0)
++              return MSG_EXCESSIVE;
++      return MSG_DEBUG;
++}
++
++
 +char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
 +                                       char *buf, size_t *resp_len)
 +{
 +      char *reply;
 +      const int reply_size = 4096;
 +      int reply_len;
 +
 +      if (os_strncmp(buf, WPA_CTRL_RSP, os_strlen(WPA_CTRL_RSP)) == 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 {
-               if (wpas_p2p_group_add(wpa_s, 0, 0, 0, 0))
++              int level = wpas_ctrl_cmd_debug_level(buf);
 +              wpa_dbg(wpa_s, level, "Control interface command '%s'", buf);
 +      }
 +
 +      reply = os_malloc(reply_size);
 +      if (reply == NULL) {
 +              *resp_len = 1;
 +              return NULL;
 +      }
 +
 +      os_memcpy(reply, "OK\n", 3);
 +      reply_len = 3;
 +
 +      if (os_strcmp(buf, "PING") == 0) {
 +              os_memcpy(reply, "PONG\n", 5);
 +              reply_len = 5;
 +      } else if (os_strcmp(buf, "IFNAME") == 0) {
 +              reply_len = os_strlen(wpa_s->ifname);
 +              os_memcpy(reply, wpa_s->ifname, reply_len);
 +      } else if (os_strncmp(buf, "RELOG", 5) == 0) {
 +              if (wpa_debug_reopen_file() < 0)
 +                      reply_len = -1;
 +      } else if (os_strncmp(buf, "NOTE ", 5) == 0) {
 +              wpa_printf(MSG_INFO, "NOTE: %s", buf + 5);
 +      } else if (os_strcmp(buf, "MIB") == 0) {
 +              reply_len = wpa_sm_get_mib(wpa_s->wpa, reply, reply_size);
 +              if (reply_len >= 0) {
 +                      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(
 +                      wpa_s, buf + 6, reply, reply_size);
 +      } 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);
 +      } else if (os_strcmp(buf, "LOGON") == 0) {
 +              eapol_sm_notify_logoff(wpa_s->eapol, FALSE);
 +      } else if (os_strcmp(buf, "LOGOFF") == 0) {
 +              eapol_sm_notify_logoff(wpa_s->eapol, TRUE);
 +      } else if (os_strcmp(buf, "REASSOCIATE") == 0) {
 +              if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED)
 +                      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;
 +              else if (wpa_s->disconnected)
 +                      wpas_request_connection(wpa_s);
 +#ifdef IEEE8021X_EAPOL
 +      } else if (os_strncmp(buf, "PREAUTH ", 8) == 0) {
 +              if (wpa_supplicant_ctrl_iface_preauth(wpa_s, buf + 8))
 +                      reply_len = -1;
 +#endif /* IEEE8021X_EAPOL */
 +#ifdef CONFIG_PEERKEY
 +      } else if (os_strncmp(buf, "STKSTART ", 9) == 0) {
 +              if (wpa_supplicant_ctrl_iface_stkstart(wpa_s, buf + 9))
 +                      reply_len = -1;
 +#endif /* CONFIG_PEERKEY */
 +#ifdef CONFIG_IEEE80211R
 +      } else if (os_strncmp(buf, "FT_DS ", 6) == 0) {
 +              if (wpa_supplicant_ctrl_iface_ft_ds(wpa_s, buf + 6))
 +                      reply_len = -1;
 +#endif /* CONFIG_IEEE80211R */
 +#ifdef CONFIG_WPS
 +      } else if (os_strcmp(buf, "WPS_PBC") == 0) {
 +              int res = wpa_supplicant_ctrl_iface_wps_pbc(wpa_s, NULL);
 +              if (res == -2) {
 +                      os_memcpy(reply, "FAIL-PBC-OVERLAP\n", 17);
 +                      reply_len = 17;
 +              } else if (res)
 +                      reply_len = -1;
 +      } else if (os_strncmp(buf, "WPS_PBC ", 8) == 0) {
 +              int res = wpa_supplicant_ctrl_iface_wps_pbc(wpa_s, buf + 8);
 +              if (res == -2) {
 +                      os_memcpy(reply, "FAIL-PBC-OVERLAP\n", 17);
 +                      reply_len = 17;
 +              } else if (res)
 +                      reply_len = -1;
 +      } else if (os_strncmp(buf, "WPS_PIN ", 8) == 0) {
 +              reply_len = wpa_supplicant_ctrl_iface_wps_pin(wpa_s, buf + 8,
 +                                                            reply,
 +                                                            reply_size);
 +      } else if (os_strncmp(buf, "WPS_CHECK_PIN ", 14) == 0) {
 +              reply_len = wpa_supplicant_ctrl_iface_wps_check_pin(
 +                      wpa_s, buf + 14, reply, reply_size);
 +      } else if (os_strcmp(buf, "WPS_CANCEL") == 0) {
 +              if (wpas_wps_cancel(wpa_s))
 +                      reply_len = -1;
 +#ifdef CONFIG_WPS_NFC
 +      } else if (os_strcmp(buf, "WPS_NFC") == 0) {
 +              if (wpa_supplicant_ctrl_iface_wps_nfc(wpa_s, NULL))
 +                      reply_len = -1;
 +      } else if (os_strncmp(buf, "WPS_NFC ", 8) == 0) {
 +              if (wpa_supplicant_ctrl_iface_wps_nfc(wpa_s, buf + 8))
 +                      reply_len = -1;
 +      } else if (os_strncmp(buf, "WPS_NFC_CONFIG_TOKEN ", 21) == 0) {
 +              reply_len = wpa_supplicant_ctrl_iface_wps_nfc_config_token(
 +                      wpa_s, buf + 21, reply, reply_size);
 +      } else if (os_strncmp(buf, "WPS_NFC_TOKEN ", 14) == 0) {
 +              reply_len = wpa_supplicant_ctrl_iface_wps_nfc_token(
 +                      wpa_s, buf + 14, reply, reply_size);
 +      } else if (os_strncmp(buf, "WPS_NFC_TAG_READ ", 17) == 0) {
 +              if (wpa_supplicant_ctrl_iface_wps_nfc_tag_read(wpa_s,
 +                                                             buf + 17))
 +                      reply_len = -1;
 +      } else if (os_strncmp(buf, "NFC_GET_HANDOVER_REQ ", 21) == 0) {
 +              reply_len = wpas_ctrl_nfc_get_handover_req(
 +                      wpa_s, buf + 21, reply, reply_size);
 +      } 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_REPORT_HANDOVER ", 20) == 0) {
 +              if (wpas_ctrl_nfc_report_handover(wpa_s, buf + 20))
 +                      reply_len = -1;
 +#endif /* CONFIG_WPS_NFC */
 +      } else if (os_strncmp(buf, "WPS_REG ", 8) == 0) {
 +              if (wpa_supplicant_ctrl_iface_wps_reg(wpa_s, buf + 8))
 +                      reply_len = -1;
 +#ifdef CONFIG_AP
 +      } else if (os_strncmp(buf, "WPS_AP_PIN ", 11) == 0) {
 +              reply_len = wpa_supplicant_ctrl_iface_wps_ap_pin(
 +                      wpa_s, buf + 11, reply, reply_size);
 +#endif /* CONFIG_AP */
 +#ifdef CONFIG_WPS_ER
 +      } else if (os_strcmp(buf, "WPS_ER_START") == 0) {
 +              if (wpas_wps_er_start(wpa_s, NULL))
 +                      reply_len = -1;
 +      } else if (os_strncmp(buf, "WPS_ER_START ", 13) == 0) {
 +              if (wpas_wps_er_start(wpa_s, buf + 13))
 +                      reply_len = -1;
 +      } else if (os_strcmp(buf, "WPS_ER_STOP") == 0) {
 +              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;
 +      } else if (os_strncmp(buf, "WPS_ER_PBC ", 11) == 0) {
 +              int ret = wpas_wps_er_pbc(wpa_s, buf + 11);
 +              if (ret == -2) {
 +                      os_memcpy(reply, "FAIL-PBC-OVERLAP\n", 17);
 +                      reply_len = 17;
 +              } else if (ret == -3) {
 +                      os_memcpy(reply, "FAIL-UNKNOWN-UUID\n", 18);
 +                      reply_len = 18;
 +              } else if (ret == -4) {
 +                      os_memcpy(reply, "FAIL-NO-AP-SETTINGS\n", 20);
 +                      reply_len = 20;
 +              } else if (ret)
 +                      reply_len = -1;
 +      } else if (os_strncmp(buf, "WPS_ER_LEARN ", 13) == 0) {
 +              if (wpa_supplicant_ctrl_iface_wps_er_learn(wpa_s, buf + 13))
 +                      reply_len = -1;
 +      } else if (os_strncmp(buf, "WPS_ER_SET_CONFIG ", 18) == 0) {
 +              if (wpa_supplicant_ctrl_iface_wps_er_set_config(wpa_s,
 +                                                              buf + 18))
 +                      reply_len = -1;
 +      } else if (os_strncmp(buf, "WPS_ER_CONFIG ", 14) == 0) {
 +              if (wpa_supplicant_ctrl_iface_wps_er_config(wpa_s, buf + 14))
 +                      reply_len = -1;
 +#ifdef CONFIG_WPS_NFC
 +      } else if (os_strncmp(buf, "WPS_ER_NFC_CONFIG_TOKEN ", 24) == 0) {
 +              reply_len = wpa_supplicant_ctrl_iface_wps_er_nfc_config_token(
 +                      wpa_s, buf + 24, reply, reply_size);
 +#endif /* CONFIG_WPS_NFC */
 +#endif /* CONFIG_WPS_ER */
 +#endif /* CONFIG_WPS */
 +#ifdef CONFIG_IBSS_RSN
 +      } else if (os_strncmp(buf, "IBSS_RSN ", 9) == 0) {
 +              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 + 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);
 +      } else if (os_strncmp(buf, "P2P_LISTEN ", 11) == 0) {
 +              if (p2p_ctrl_listen(wpa_s, buf + 11))
 +                      reply_len = -1;
 +      } else if (os_strcmp(buf, "P2P_LISTEN") == 0) {
 +              if (p2p_ctrl_listen(wpa_s, ""))
 +                      reply_len = -1;
 +      } else if (os_strncmp(buf, "P2P_GROUP_REMOVE ", 17) == 0) {
 +              if (wpas_p2p_group_remove(wpa_s, buf + 17))
 +                      reply_len = -1;
 +      } else if (os_strcmp(buf, "P2P_GROUP_ADD") == 0) {
-               if (wpa_supplicant_ctrl_iface_dup_network(wpa_s, buf + 12))
++              if (p2p_ctrl_group_add(wpa_s, ""))
 +                      reply_len = -1;
 +      } else if (os_strncmp(buf, "P2P_GROUP_ADD ", 14) == 0) {
 +              if (p2p_ctrl_group_add(wpa_s, buf + 14))
 +                      reply_len = -1;
 +      } else if (os_strncmp(buf, "P2P_PROV_DISC ", 14) == 0) {
 +              if (p2p_ctrl_prov_disc(wpa_s, buf + 14))
 +                      reply_len = -1;
 +      } else if (os_strcmp(buf, "P2P_GET_PASSPHRASE") == 0) {
 +              reply_len = p2p_get_passphrase(wpa_s, reply, reply_size);
 +      } else if (os_strncmp(buf, "P2P_SERV_DISC_REQ ", 18) == 0) {
 +              reply_len = p2p_ctrl_serv_disc_req(wpa_s, buf + 18, reply,
 +                                                 reply_size);
 +      } else if (os_strncmp(buf, "P2P_SERV_DISC_CANCEL_REQ ", 25) == 0) {
 +              if (p2p_ctrl_serv_disc_cancel_req(wpa_s, buf + 25) < 0)
 +                      reply_len = -1;
 +      } else if (os_strncmp(buf, "P2P_SERV_DISC_RESP ", 19) == 0) {
 +              if (p2p_ctrl_serv_disc_resp(wpa_s, buf + 19) < 0)
 +                      reply_len = -1;
 +      } else if (os_strcmp(buf, "P2P_SERVICE_UPDATE") == 0) {
 +              wpas_p2p_sd_service_update(wpa_s);
 +      } else if (os_strncmp(buf, "P2P_SERV_DISC_EXTERNAL ", 23) == 0) {
 +              if (p2p_ctrl_serv_disc_external(wpa_s, buf + 23) < 0)
 +                      reply_len = -1;
 +      } else if (os_strcmp(buf, "P2P_SERVICE_FLUSH") == 0) {
 +              wpas_p2p_service_flush(wpa_s);
 +      } else if (os_strncmp(buf, "P2P_SERVICE_ADD ", 16) == 0) {
 +              if (p2p_ctrl_service_add(wpa_s, buf + 16) < 0)
 +                      reply_len = -1;
 +      } 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;
 +      } else if (os_strncmp(buf, "P2P_INVITE ", 11) == 0) {
 +              if (p2p_ctrl_invite(wpa_s, buf + 11) < 0)
 +                      reply_len = -1;
 +      } else if (os_strncmp(buf, "P2P_PEER ", 9) == 0) {
 +              reply_len = p2p_ctrl_peer(wpa_s, buf + 9, reply,
 +                                            reply_size);
 +      } else if (os_strncmp(buf, "P2P_SET ", 8) == 0) {
 +              if (p2p_ctrl_set(wpa_s, buf + 8) < 0)
 +                      reply_len = -1;
 +      } else if (os_strcmp(buf, "P2P_FLUSH") == 0) {
 +              p2p_ctrl_flush(wpa_s);
 +      } else if (os_strncmp(buf, "P2P_UNAUTHORIZE ", 16) == 0) {
 +              if (wpas_p2p_unauthorize(wpa_s, buf + 16) < 0)
 +                      reply_len = -1;
 +      } else if (os_strcmp(buf, "P2P_CANCEL") == 0) {
 +              if (wpas_p2p_cancel(wpa_s))
 +                      reply_len = -1;
 +      } else if (os_strncmp(buf, "P2P_PRESENCE_REQ ", 17) == 0) {
 +              if (p2p_ctrl_presence_req(wpa_s, buf + 17) < 0)
 +                      reply_len = -1;
 +      } else if (os_strcmp(buf, "P2P_PRESENCE_REQ") == 0) {
 +              if (p2p_ctrl_presence_req(wpa_s, "") < 0)
 +                      reply_len = -1;
 +      } else if (os_strncmp(buf, "P2P_EXT_LISTEN ", 15) == 0) {
 +              if (p2p_ctrl_ext_listen(wpa_s, buf + 15) < 0)
 +                      reply_len = -1;
 +      } 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) {
 +              if (wifi_display_subelem_set(wpa_s->global, buf + 16) < 0)
 +                      reply_len = -1;
 +      } else if (os_strncmp(buf, "WFD_SUBELEM_GET ", 16) == 0) {
 +              reply_len = wifi_display_subelem_get(wpa_s->global, buf + 16,
 +                                                   reply, reply_size);
 +#endif /* CONFIG_WIFI_DISPLAY */
 +#ifdef CONFIG_INTERWORKING
 +      } else if (os_strcmp(buf, "FETCH_ANQP") == 0) {
 +              if (interworking_fetch_anqp(wpa_s) < 0)
 +                      reply_len = -1;
 +      } else if (os_strcmp(buf, "STOP_FETCH_ANQP") == 0) {
 +              interworking_stop_fetch_anqp(wpa_s);
 +      } 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) < 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;
 +      } else if (os_strncmp(buf, "GAS_REQUEST ", 12) == 0) {
 +              if (gas_request(wpa_s, buf + 12) < 0)
 +                      reply_len = -1;
 +      } else if (os_strncmp(buf, "GAS_RESPONSE_GET ", 17) == 0) {
 +              reply_len = gas_response_get(wpa_s, buf + 17, reply,
 +                                           reply_size);
 +#endif /* CONFIG_INTERWORKING */
 +#ifdef CONFIG_HS20
 +      } else if (os_strncmp(buf, "HS20_ANQP_GET ", 14) == 0) {
 +              if (get_hs20_anqp(wpa_s, buf + 14) < 0)
 +                      reply_len = -1;
 +      } 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 {
 +                      /*
 +                       * 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;
 +      } else if (os_strcmp(buf, "TERMINATE") == 0) {
 +              wpa_supplicant_terminate_proc(wpa_s->global);
 +      } else if (os_strncmp(buf, "BSSID ", 6) == 0) {
 +              if (wpa_supplicant_ctrl_iface_bssid(wpa_s, buf + 6))
 +                      reply_len = -1;
 +      } else if (os_strncmp(buf, "BLACKLIST", 9) == 0) {
 +              reply_len = wpa_supplicant_ctrl_iface_blacklist(
 +                      wpa_s, buf + 9, reply, reply_size);
 +      } 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, NULL, reply, reply_size);
 +      } else if (os_strcmp(buf, "DISCONNECT") == 0) {
 +#ifdef CONFIG_SME
 +              wpa_s->sme.prev_bssid_set = 0;
 +#endif /* CONFIG_SME */
 +              wpa_s->reassociate = 0;
 +              wpa_s->disconnected = 1;
 +              wpa_supplicant_cancel_sched_scan(wpa_s);
 +              wpa_supplicant_cancel_scan(wpa_s);
 +              wpa_supplicant_deauthenticate(wpa_s,
 +                                            WLAN_REASON_DEAUTH_LEAVING);
++              eloop_cancel_timeout(wpas_network_reenabled, wpa_s, NULL);
 +      } 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);
 +      } else if (os_strncmp(buf, "SELECT_NETWORK ", 15) == 0) {
 +              if (wpa_supplicant_ctrl_iface_select_network(wpa_s, buf + 15))
 +                      reply_len = -1;
 +      } else if (os_strncmp(buf, "ENABLE_NETWORK ", 15) == 0) {
 +              if (wpa_supplicant_ctrl_iface_enable_network(wpa_s, buf + 15))
 +                      reply_len = -1;
 +      } else if (os_strncmp(buf, "DISABLE_NETWORK ", 16) == 0) {
 +              if (wpa_supplicant_ctrl_iface_disable_network(wpa_s, buf + 16))
 +                      reply_len = -1;
 +      } else if (os_strcmp(buf, "ADD_NETWORK") == 0) {
 +              reply_len = wpa_supplicant_ctrl_iface_add_network(
 +                      wpa_s, reply, reply_size);
 +      } else if (os_strncmp(buf, "REMOVE_NETWORK ", 15) == 0) {
 +              if (wpa_supplicant_ctrl_iface_remove_network(wpa_s, buf + 15))
 +                      reply_len = -1;
 +      } else if (os_strncmp(buf, "SET_NETWORK ", 12) == 0) {
 +              if (wpa_supplicant_ctrl_iface_set_network(wpa_s, buf + 12))
 +                      reply_len = -1;
 +      } 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) {
-       char *pos;
++              if (wpa_supplicant_ctrl_iface_dup_network(wpa_s, buf + 12,
++                                                        wpa_s))
 +                      reply_len = -1;
 +      } else if (os_strcmp(buf, "LIST_CREDS") == 0) {
 +              reply_len = wpa_supplicant_ctrl_iface_list_creds(
 +                      wpa_s, reply, reply_size);
 +      } else if (os_strcmp(buf, "ADD_CRED") == 0) {
 +              reply_len = wpa_supplicant_ctrl_iface_add_cred(
 +                      wpa_s, reply, reply_size);
 +      } else if (os_strncmp(buf, "REMOVE_CRED ", 12) == 0) {
 +              if (wpa_supplicant_ctrl_iface_remove_cred(wpa_s, buf + 12))
 +                      reply_len = -1;
 +      } 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))
 +                      reply_len = -1;
 +#endif /* CONFIG_NO_CONFIG_WRITE */
 +      } else if (os_strncmp(buf, "GET_CAPABILITY ", 15) == 0) {
 +              reply_len = wpa_supplicant_ctrl_iface_get_capability(
 +                      wpa_s, buf + 15, reply, reply_size);
 +      } else if (os_strncmp(buf, "AP_SCAN ", 8) == 0) {
 +              if (wpa_supplicant_ctrl_iface_ap_scan(wpa_s, buf + 8))
 +                      reply_len = -1;
 +      } else if (os_strncmp(buf, "SCAN_INTERVAL ", 14) == 0) {
 +              if (wpa_supplicant_ctrl_iface_scan_interval(wpa_s, buf + 14))
 +                      reply_len = -1;
 +      } else if (os_strcmp(buf, "INTERFACE_LIST") == 0) {
 +              reply_len = wpa_supplicant_global_iface_list(
 +                      wpa_s->global, reply, reply_size);
 +      } else if (os_strcmp(buf, "INTERFACES") == 0) {
 +              reply_len = wpa_supplicant_global_iface_interfaces(
 +                      wpa_s->global, reply, reply_size);
 +      } else if (os_strncmp(buf, "BSS ", 4) == 0) {
 +              reply_len = wpa_supplicant_ctrl_iface_bss(
 +                      wpa_s, buf + 4, reply, reply_size);
 +#ifdef CONFIG_AP
 +      } else if (os_strcmp(buf, "STA-FIRST") == 0) {
 +              reply_len = ap_ctrl_iface_sta_first(wpa_s, reply, reply_size);
 +      } else if (os_strncmp(buf, "STA ", 4) == 0) {
 +              reply_len = ap_ctrl_iface_sta(wpa_s, buf + 4, reply,
 +                                            reply_size);
 +      } else if (os_strncmp(buf, "STA-NEXT ", 9) == 0) {
 +              reply_len = ap_ctrl_iface_sta_next(wpa_s, buf + 9, reply,
 +                                                 reply_size);
 +      } else if (os_strncmp(buf, "DEAUTHENTICATE ", 15) == 0) {
 +              if (ap_ctrl_iface_sta_deauthenticate(wpa_s, buf + 15))
 +                      reply_len = -1;
 +      } 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) {
 +              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;
 +      } else if (os_strncmp(buf, "BSS_EXPIRE_COUNT ", 17) == 0) {
 +              if (wpa_supplicant_ctrl_iface_bss_expire_count(wpa_s,
 +                                                             buf + 17))
 +                      reply_len = -1;
 +      } else if (os_strncmp(buf, "BSS_FLUSH ", 10) == 0) {
 +              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))
 +                      reply_len = -1;
 +      } else if (os_strncmp(buf, "TDLS_SETUP ", 11) == 0) {
 +              if (wpa_supplicant_ctrl_iface_tdls_setup(wpa_s, buf + 11))
 +                      reply_len = -1;
 +      } 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;
++      } else if (os_strncmp(buf, "TDLS_LINK_STATUS ", 17) == 0) {
++              reply_len = wpa_supplicant_ctrl_iface_tdls_link_status(
++                      wpa_s, buf + 17, reply, reply_size);
 +#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);
 +      } else if (os_strncmp(buf, "PKTCNT_POLL", 11) == 0) {
 +              reply_len = wpa_supplicant_pktcnt_poll(wpa_s, reply,
 +                                                     reply_size);
 +#ifdef CONFIG_AUTOSCAN
 +      } else if (os_strncmp(buf, "AUTOSCAN ", 9) == 0) {
 +              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);
 +#ifdef CONFIG_WNM
 +      } 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 ", 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);
++      } else if (os_strncmp(buf, "TEST_FAIL ", 10) == 0) {
++              if (wpas_ctrl_test_fail(wpa_s, buf + 10) < 0)
++                      reply_len = -1;
++      } else if (os_strcmp(buf, "GET_FAIL") == 0) {
++              reply_len = wpas_ctrl_get_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 if (os_strncmp(buf, "GET_PREF_FREQ_LIST ", 19) == 0) {
++              reply_len = wpas_ctrl_iface_get_pref_freq_list(
++                      wpa_s, buf + 19, reply, reply_size);
 +      } else {
 +              os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
 +              reply_len = 16;
 +      }
 +
 +      if (reply_len < 0) {
 +              os_memcpy(reply, "FAIL\n", 5);
 +              reply_len = 5;
 +      }
 +
 +      *resp_len = reply_len;
 +      return reply;
 +}
 +
 +
 +static int wpa_supplicant_global_iface_add(struct wpa_global *global,
 +                                         char *cmd)
 +{
 +      struct wpa_interface iface;
-        * TAB<bridge_ifname>
++      char *pos, *extra;
++      struct wpa_supplicant *wpa_s;
++      unsigned int create_iface = 0;
++      u8 mac_addr[ETH_ALEN];
 +
 +      /*
 +       * <ifname>TAB<confname>TAB<driver>TAB<ctrl_interface>TAB<driver_param>
-               return -1;
++       * TAB<bridge_ifname>[TAB<create>]
 +       */
 +      wpa_printf(MSG_DEBUG, "CTRL_IFACE GLOBAL INTERFACE_ADD '%s'", cmd);
 +
 +      os_memset(&iface, 0, sizeof(iface));
 +
 +      do {
 +              iface.ifname = pos = cmd;
 +              pos = os_strchr(pos, '\t');
 +              if (pos)
 +                      *pos++ = '\0';
 +              if (iface.ifname[0] == '\0')
 +                      return -1;
 +              if (pos == NULL)
 +                      break;
 +
 +              iface.confname = pos;
 +              pos = os_strchr(pos, '\t');
 +              if (pos)
 +                      *pos++ = '\0';
 +              if (iface.confname[0] == '\0')
 +                      iface.confname = NULL;
 +              if (pos == NULL)
 +                      break;
 +
 +              iface.driver = pos;
 +              pos = os_strchr(pos, '\t');
 +              if (pos)
 +                      *pos++ = '\0';
 +              if (iface.driver[0] == '\0')
 +                      iface.driver = NULL;
 +              if (pos == NULL)
 +                      break;
 +
 +              iface.ctrl_interface = pos;
 +              pos = os_strchr(pos, '\t');
 +              if (pos)
 +                      *pos++ = '\0';
 +              if (iface.ctrl_interface[0] == '\0')
 +                      iface.ctrl_interface = NULL;
 +              if (pos == NULL)
 +                      break;
 +
 +              iface.driver_param = pos;
 +              pos = os_strchr(pos, '\t');
 +              if (pos)
 +                      *pos++ = '\0';
 +              if (iface.driver_param[0] == '\0')
 +                      iface.driver_param = NULL;
 +              if (pos == NULL)
 +                      break;
 +
 +              iface.bridge_ifname = pos;
 +              pos = os_strchr(pos, '\t');
 +              if (pos)
 +                      *pos++ = '\0';
 +              if (iface.bridge_ifname[0] == '\0')
 +                      iface.bridge_ifname = NULL;
 +              if (pos == NULL)
 +                      break;
++
++              extra = pos;
++              pos = os_strchr(pos, '\t');
++              if (pos)
++                      *pos++ = '\0';
++              if (!extra[0])
++                      break;
++
++              if (os_strcmp(extra, "create") == 0)
++                      create_iface = 1;
++              else {
++                      wpa_printf(MSG_DEBUG,
++                                 "INTERFACE_ADD unsupported extra parameter: '%s'",
++                                 extra);
++                      return -1;
++              }
 +      } while (0);
 +
++      if (create_iface) {
++              wpa_printf(MSG_DEBUG, "CTRL_IFACE creating interface '%s'",
++                         iface.ifname);
++              if (!global->ifaces)
++                      return -1;
++              if (wpa_drv_if_add(global->ifaces, WPA_IF_STATION, iface.ifname,
++                                 NULL, NULL, NULL, mac_addr, NULL) < 0) {
++                      wpa_printf(MSG_ERROR,
++                                 "CTRL_IFACE interface creation failed");
++                      return -1;
++              }
++
++              wpa_printf(MSG_DEBUG,
++                         "CTRL_IFACE interface '%s' created with MAC addr: "
++                         MACSTR, iface.ifname, MAC2STR(mac_addr));
++      }
++
 +      if (wpa_supplicant_get_iface(global, iface.ifname))
-       return wpa_supplicant_add_iface(global, &iface, NULL) ? 0 : -1;
++              goto fail;
++
++      wpa_s = wpa_supplicant_add_iface(global, &iface, NULL);
++      if (!wpa_s)
++              goto fail;
++      wpa_s->added_vif = create_iface;
++      return 0;
 +
-       return wpa_supplicant_remove_iface(global, wpa_s, 0);
++fail:
++      if (create_iface)
++              wpa_drv_if_remove(global->ifaces, WPA_IF_STATION, iface.ifname);
++      return -1;
 +}
 +
 +
 +static int wpa_supplicant_global_iface_remove(struct wpa_global *global,
 +                                            char *cmd)
 +{
 +      struct wpa_supplicant *wpa_s;
++      int ret;
++      unsigned int delete_iface;
 +
 +      wpa_printf(MSG_DEBUG, "CTRL_IFACE GLOBAL INTERFACE_REMOVE '%s'", cmd);
 +
 +      wpa_s = wpa_supplicant_get_iface(global, cmd);
 +      if (wpa_s == NULL)
 +              return -1;
-               struct wpa_driver_ops *drv = wpa_drivers[i];
++      delete_iface = wpa_s->added_vif;
++      ret = wpa_supplicant_remove_iface(global, wpa_s, 0);
++      if (!ret && delete_iface) {
++              wpa_printf(MSG_DEBUG, "CTRL_IFACE deleting the interface '%s'",
++                         cmd);
++              ret = wpa_drv_if_remove(global->ifaces, WPA_IF_STATION, cmd);
++      }
++      return ret;
 +}
 +
 +
 +static void wpa_free_iface_info(struct wpa_interface_info *iface)
 +{
 +      struct wpa_interface_info *prev;
 +
 +      while (iface) {
 +              prev = iface;
 +              iface = iface->next;
 +
 +              os_free(prev->ifname);
 +              os_free(prev->desc);
 +              os_free(prev);
 +      }
 +}
 +
 +
 +static int wpa_supplicant_global_iface_list(struct wpa_global *global,
 +                                          char *buf, int len)
 +{
 +      int i, res;
 +      struct wpa_interface_info *iface = NULL, *last = NULL, *tmp;
 +      char *pos, *end;
 +
 +      for (i = 0; wpa_drivers[i]; i++) {
++              const struct wpa_driver_ops *drv = wpa_drivers[i];
 +              if (drv->get_interfaces == NULL)
 +                      continue;
 +              tmp = drv->get_interfaces(global->drv_priv[i]);
 +              if (tmp == NULL)
 +                      continue;
 +
 +              if (last == NULL)
 +                      iface = last = tmp;
 +              else
 +                      last->next = tmp;
 +              while (last->next)
 +                      last = last->next;
 +      }
 +
 +      pos = buf;
 +      end = buf + len;
 +      for (tmp = iface; tmp; tmp = tmp->next) {
 +              res = os_snprintf(pos, end - pos, "%s\t%s\t%s\n",
 +                                tmp->drv_name, tmp->ifname,
 +                                tmp->desc ? tmp->desc : "");
 +              if (os_snprintf_error(end - pos, res)) {
 +                      *pos = '\0';
 +                      break;
 +              }
 +              pos += res;
 +      }
 +
 +      wpa_free_iface_info(iface);
 +
 +      return pos - buf;
 +}
 +
 +
 +static int wpa_supplicant_global_iface_interfaces(struct wpa_global *global,
 +                                                char *buf, int len)
 +{
 +      int res;
 +      char *pos, *end;
 +      struct wpa_supplicant *wpa_s;
 +
 +      wpa_s = global->ifaces;
 +      pos = buf;
 +      end = buf + len;
 +
 +      while (wpa_s) {
 +              res = os_snprintf(pos, end - pos, "%s\n", wpa_s->ifname);
 +              if (os_snprintf_error(end - pos, res)) {
 +                      *pos = '\0';
 +                      break;
 +              }
 +              pos += res;
 +              wpa_s = wpa_s->next;
 +      }
 +      return pos - buf;
 +}
 +
 +
 +static char * wpas_global_ctrl_iface_ifname(struct wpa_global *global,
 +                                          const char *ifname,
 +                                          char *cmd, size_t *resp_len)
 +{
 +      struct wpa_supplicant *wpa_s;
 +
 +      for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
 +              if (os_strcmp(ifname, wpa_s->ifname) == 0)
 +                      break;
 +      }
 +
 +      if (wpa_s == NULL) {
 +              char *resp = os_strdup("FAIL-NO-IFNAME-MATCH\n");
 +              if (resp)
 +                      *resp_len = os_strlen(resp);
 +              else
 +                      *resp_len = 1;
 +              return resp;
 +      }
 +
 +      return wpa_supplicant_ctrl_iface_process(wpa_s, cmd, resp_len);
 +}
 +
 +
 +static char * wpas_global_ctrl_iface_redir_p2p(struct wpa_global *global,
 +                                             char *buf, size_t *resp_len)
 +{
 +#ifdef CONFIG_P2P
 +      static const char * cmd[] = {
 +              "LIST_NETWORKS",
 +              "P2P_FIND",
 +              "P2P_STOP_FIND",
 +              "P2P_LISTEN",
 +              "P2P_GROUP_ADD",
 +              "P2P_GET_PASSPHRASE",
 +              "P2P_SERVICE_UPDATE",
 +              "P2P_SERVICE_FLUSH",
 +              "P2P_FLUSH",
 +              "P2P_CANCEL",
 +              "P2P_PRESENCE_REQ",
 +              "P2P_EXT_LISTEN",
 +              NULL
 +      };
 +      static const char * prefix[] = {
 +#ifdef ANDROID
 +              "DRIVER ",
 +#endif /* ANDROID */
 +              "GET_NETWORK ",
 +              "REMOVE_NETWORK ",
 +              "P2P_FIND ",
 +              "P2P_CONNECT ",
 +              "P2P_LISTEN ",
 +              "P2P_GROUP_REMOVE ",
 +              "P2P_GROUP_ADD ",
 +              "P2P_PROV_DISC ",
 +              "P2P_SERV_DISC_REQ ",
 +              "P2P_SERV_DISC_CANCEL_REQ ",
 +              "P2P_SERV_DISC_RESP ",
 +              "P2P_SERV_DISC_EXTERNAL ",
 +              "P2P_SERVICE_ADD ",
 +              "P2P_SERVICE_DEL ",
 +              "P2P_SERVICE_REP ",
 +              "P2P_REJECT ",
 +              "P2P_INVITE ",
 +              "P2P_PEER ",
 +              "P2P_SET ",
 +              "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;
 +      int i;
 +
 +      if (global->p2p_init_wpa_s == NULL)
 +              return NULL;
 +
 +      for (i = 0; !found && cmd[i]; i++) {
 +              if (os_strcmp(buf, cmd[i]) == 0)
 +                      found = 1;
 +      }
 +
 +      for (i = 0; !found && prefix[i]; i++) {
 +              if (os_strncmp(buf, prefix[i], os_strlen(prefix[i])) == 0)
 +                      found = 1;
 +      }
 +
 +      if (found)
 +              return wpa_supplicant_ctrl_iface_process(global->p2p_init_wpa_s,
 +                                                       buf, resp_len);
 +#endif /* CONFIG_P2P */
 +      return NULL;
 +}
 +
 +
 +static char * wpas_global_ctrl_iface_redir_wfd(struct wpa_global *global,
 +                                             char *buf, size_t *resp_len)
 +{
 +#ifdef CONFIG_WIFI_DISPLAY
 +      if (global->p2p_init_wpa_s == NULL)
 +              return NULL;
 +      if (os_strncmp(buf, "WFD_SUBELEM_SET ", 16) == 0 ||
 +          os_strncmp(buf, "WFD_SUBELEM_GET ", 16) == 0)
 +              return wpa_supplicant_ctrl_iface_process(global->p2p_init_wpa_s,
 +                                                       buf, resp_len);
 +#endif /* CONFIG_WIFI_DISPLAY */
 +      return NULL;
 +}
 +
 +
 +static char * wpas_global_ctrl_iface_redir(struct wpa_global *global,
 +                                         char *buf, size_t *resp_len)
 +{
 +      char *ret;
 +
 +      ret = wpas_global_ctrl_iface_redir_p2p(global, buf, resp_len);
 +      if (ret)
 +              return ret;
 +
 +      ret = wpas_global_ctrl_iface_redir_wfd(global, buf, resp_len);
 +      if (ret)
 +              return ret;
 +
 +      return NULL;
 +}
 +
 +
 +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;
 +}
 +
 +
++static int wpas_global_ctrl_iface_dup_network(struct wpa_global *global,
++                                            char *cmd)
++{
++      struct wpa_supplicant *wpa_s[2]; /* src, dst */
++      char *p;
++      unsigned int i;
++
++      /* cmd: "<src ifname> <dst ifname> <src network id> <dst network id>
++       * <variable name> */
++
++      for (i = 0; i < ARRAY_SIZE(wpa_s) ; i++) {
++              p = os_strchr(cmd, ' ');
++              if (p == NULL)
++                      return -1;
++              *p = '\0';
++
++              wpa_s[i] = global->ifaces;
++              for (; wpa_s[i]; wpa_s[i] = wpa_s[i]->next) {
++                      if (os_strcmp(cmd, wpa_s[i]->ifname) == 0)
++                              break;
++              }
++
++              if (!wpa_s[i]) {
++                      wpa_printf(MSG_DEBUG,
++                                 "CTRL_IFACE: Could not find iface=%s", cmd);
++                      return -1;
++              }
++
++              cmd = p + 1;
++      }
++
++      return wpa_supplicant_ctrl_iface_dup_network(wpa_s[0], cmd, wpa_s[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;
 +}
 +
 +
++#ifdef CONFIG_FST
++
++static int wpas_global_ctrl_iface_fst_attach(struct wpa_global *global,
++                                           char *cmd, char *buf,
++                                           size_t reply_size)
++{
++      char ifname[IFNAMSIZ + 1];
++      struct fst_iface_cfg cfg;
++      struct wpa_supplicant *wpa_s;
++      struct fst_wpa_obj iface_obj;
++
++      if (!fst_parse_attach_command(cmd, ifname, sizeof(ifname), &cfg)) {
++              wpa_s = wpa_supplicant_get_iface(global, ifname);
++              if (wpa_s) {
++                      if (wpa_s->fst) {
++                              wpa_printf(MSG_INFO, "FST: Already attached");
++                              return -1;
++                      }
++                      fst_wpa_supplicant_fill_iface_obj(wpa_s, &iface_obj);
++                      wpa_s->fst = fst_attach(ifname, wpa_s->own_addr,
++                                              &iface_obj, &cfg);
++                      if (wpa_s->fst)
++                              return os_snprintf(buf, reply_size, "OK\n");
++              }
++      }
++
++      return -1;
++}
++
++
++static int wpas_global_ctrl_iface_fst_detach(struct wpa_global *global,
++                                           char *cmd, char *buf,
++                                           size_t reply_size)
++{
++      char ifname[IFNAMSIZ + 1];
++      struct wpa_supplicant *wpa_s;
++
++      if (!fst_parse_detach_command(cmd, ifname, sizeof(ifname))) {
++              wpa_s = wpa_supplicant_get_iface(global, ifname);
++              if (wpa_s) {
++                      if (!fst_iface_detach(ifname)) {
++                              wpa_s->fst = NULL;
++                              return os_snprintf(buf, reply_size, "OK\n");
++                      }
++              }
++      }
++
++      return -1;
++}
++
++#endif /* CONFIG_FST */
++
++
 +char * wpa_supplicant_global_ctrl_iface_process(struct wpa_global *global,
 +                                              char *buf, size_t *resp_len)
 +{
 +      char *reply;
 +      const int reply_size = 2048;
 +      int reply_len;
 +      int level = MSG_DEBUG;
 +
 +      if (os_strncmp(buf, "IFNAME=", 7) == 0) {
 +              char *pos = os_strchr(buf + 7, ' ');
 +              if (pos) {
 +                      *pos++ = '\0';
 +                      return wpas_global_ctrl_iface_ifname(global,
 +                                                           buf + 7, pos,
 +                                                           resp_len);
 +              }
 +      }
 +
 +      reply = wpas_global_ctrl_iface_redir(global, buf, resp_len);
 +      if (reply)
 +              return reply;
 +
 +      if (os_strcmp(buf, "PING") == 0)
 +              level = MSG_EXCESSIVE;
 +      wpa_hexdump_ascii(level, "RX global ctrl_iface",
 +                        (const u8 *) buf, os_strlen(buf));
 +
 +      reply = os_malloc(reply_size);
 +      if (reply == NULL) {
 +              *resp_len = 1;
 +              return NULL;
 +      }
 +
 +      os_memcpy(reply, "OK\n", 3);
 +      reply_len = 3;
 +
 +      if (os_strcmp(buf, "PING") == 0) {
 +              os_memcpy(reply, "PONG\n", 5);
 +              reply_len = 5;
 +      } else if (os_strncmp(buf, "INTERFACE_ADD ", 14) == 0) {
 +              if (wpa_supplicant_global_iface_add(global, buf + 14))
 +                      reply_len = -1;
 +      } else if (os_strncmp(buf, "INTERFACE_REMOVE ", 17) == 0) {
 +              if (wpa_supplicant_global_iface_remove(global, buf + 17))
 +                      reply_len = -1;
 +      } else if (os_strcmp(buf, "INTERFACE_LIST") == 0) {
 +              reply_len = wpa_supplicant_global_iface_list(
 +                      global, reply, reply_size);
 +      } else if (os_strcmp(buf, "INTERFACES") == 0) {
 +              reply_len = wpa_supplicant_global_iface_interfaces(
 +                      global, reply, reply_size);
++#ifdef CONFIG_FST
++      } else if (os_strncmp(buf, "FST-ATTACH ", 11) == 0) {
++              reply_len = wpas_global_ctrl_iface_fst_attach(global, buf + 11,
++                                                            reply,
++                                                            reply_size);
++      } else if (os_strncmp(buf, "FST-DETACH ", 11) == 0) {
++              reply_len = wpas_global_ctrl_iface_fst_detach(global, buf + 11,
++                                                            reply,
++                                                            reply_size);
++      } else if (os_strncmp(buf, "FST-MANAGER ", 12) == 0) {
++              reply_len = fst_ctrl_iface_receive(buf + 12, reply, reply_size);
++#endif /* CONFIG_FST */
 +      } else if (os_strcmp(buf, "TERMINATE") == 0) {
 +              wpa_supplicant_terminate_proc(global);
 +      } else if (os_strcmp(buf, "SUSPEND") == 0) {
 +              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;
 +              }
++      } else if (os_strncmp(buf, "DUP_NETWORK ", 12) == 0) {
++              if (wpas_global_ctrl_iface_dup_network(global, buf + 12))
++                      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;
 +      }
 +
 +      if (reply_len < 0) {
 +              os_memcpy(reply, "FAIL\n", 5);
 +              reply_len = 5;
 +      }
 +
 +      *resp_len = reply_len;
 +      return reply;
 +}
index dc02db213a486c0c6bdf57ecc20f9b6512e6ec1c,0000000000000000000000000000000000000000..54e0e2fac58384c40fd72a5ba00ef3ca520ceed6
mode 100644,000000..100644
--- /dev/null
@@@ -1,829 -1,0 +1,830 @@@
- static void wpa_supplicant_ctrl_iface_msg_cb(void *ctx, int level, int global,
 +/*
 + * WPA Supplicant / Windows Named Pipe -based control interface
 + * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi>
 + *
 + * This software may be distributed under the terms of the BSD license.
 + * See README for more details.
 + */
 +
 +#include "includes.h"
 +
 +#include "common.h"
 +#include "eloop.h"
 +#include "config.h"
 +#include "eapol_supp/eapol_supp_sm.h"
 +#include "wpa_supplicant_i.h"
 +#include "ctrl_iface.h"
 +#include "common/wpa_ctrl.h"
 +
 +#ifdef __MINGW32_VERSION
 +/* mingw-w32api v3.1 does not yet include sddl.h, so define needed parts here
 + */
 +#define SDDL_REVISION_1 1
 +BOOL WINAPI ConvertStringSecurityDescriptorToSecurityDescriptorA(
 +      LPCSTR, DWORD, PSECURITY_DESCRIPTOR *, PULONG);
 +BOOL WINAPI ConvertStringSecurityDescriptorToSecurityDescriptorW(
 +      LPCWSTR, DWORD, PSECURITY_DESCRIPTOR *, PULONG);
 +#ifdef UNICODE
 +#define ConvertStringSecurityDescriptorToSecurityDescriptor \
 +ConvertStringSecurityDescriptorToSecurityDescriptorW
 +#else
 +#define ConvertStringSecurityDescriptorToSecurityDescriptor \
 +ConvertStringSecurityDescriptorToSecurityDescriptorA
 +#endif
 +#else /* __MINGW32_VERSION */
 +#ifndef _WIN32_WINNT
 +#define _WIN32_WINNT 0x0500
 +#endif
 +#include <sddl.h>
 +#endif /* __MINGW32_VERSION */
 +
 +#ifndef WPA_SUPPLICANT_NAMED_PIPE
 +#define WPA_SUPPLICANT_NAMED_PIPE "WpaSupplicant"
 +#endif
 +#define NAMED_PIPE_PREFIX TEXT("\\\\.\\pipe\\") TEXT(WPA_SUPPLICANT_NAMED_PIPE)
 +
 +/* Per-interface ctrl_iface */
 +
 +#define REQUEST_BUFSIZE 256
 +#define REPLY_BUFSIZE 4096
 +
 +struct ctrl_iface_priv;
 +
 +/**
 + * struct wpa_ctrl_dst - Internal data structure of control interface clients
 + *
 + * This structure is used to store information about registered control
 + * interface monitors into struct wpa_supplicant. This data is private to
 + * ctrl_iface_named_pipe.c and should not be touched directly from other files.
 + */
 +struct wpa_ctrl_dst {
 +      /* Note: OVERLAPPED must be the first member of struct wpa_ctrl_dst */
 +      OVERLAPPED overlap;
 +      struct wpa_ctrl_dst *next, *prev;
 +      struct ctrl_iface_priv *priv;
 +      HANDLE pipe;
 +      int attached;
 +      int debug_level;
 +      int errors;
 +      char req_buf[REQUEST_BUFSIZE];
 +      char *rsp_buf;
 +      int used;
 +};
 +
 +
 +struct ctrl_iface_priv {
 +      struct wpa_supplicant *wpa_s;
 +      struct wpa_ctrl_dst *ctrl_dst;
 +      SECURITY_ATTRIBUTES attr;
 +      int sec_attr_set;
 +};
 +
 +
 +static void wpa_supplicant_ctrl_iface_send(struct ctrl_iface_priv *priv,
 +                                         int level, const char *buf,
 +                                         size_t len);
 +
 +static void ctrl_close_pipe(struct wpa_ctrl_dst *dst);
 +static void wpa_supplicant_ctrl_iface_receive(void *, void *);
 +static VOID WINAPI ctrl_iface_read_completed(DWORD err, DWORD bytes,
 +                                           LPOVERLAPPED overlap);
 +
 +struct wpa_global_dst;
 +static void global_close_pipe(struct wpa_global_dst *dst);
 +static void wpa_supplicant_global_iface_receive(void *eloop_data,
 +                                              void *user_ctx);
 +static VOID WINAPI global_iface_read_completed(DWORD err, DWORD bytes,
 +                                             LPOVERLAPPED overlap);
 +
 +
 +static int ctrl_broken_pipe(HANDLE pipe, int used)
 +{
 +      DWORD err;
 +
 +      if (PeekNamedPipe(pipe, NULL, 0, NULL, NULL, NULL))
 +              return 0;
 +
 +      err = GetLastError();
 +      if (err == ERROR_BROKEN_PIPE || (err == ERROR_BAD_PIPE && used))
 +              return 1;
 +      return 0;
 +}
 +
 +
 +static void ctrl_flush_broken_pipes(struct ctrl_iface_priv *priv)
 +{
 +      struct wpa_ctrl_dst *dst, *next;
 +
 +      dst = priv->ctrl_dst;
 +
 +      while (dst) {
 +              next = dst->next;
 +              if (ctrl_broken_pipe(dst->pipe, dst->used)) {
 +                      wpa_printf(MSG_DEBUG, "CTRL: closing broken pipe %p",
 +                                 dst);
 +                      ctrl_close_pipe(dst);
 +              }
 +              dst = next;
 +      }
 +}
 +
 +
 +static int ctrl_open_pipe(struct ctrl_iface_priv *priv)
 +{
 +      struct wpa_ctrl_dst *dst;
 +      DWORD err;
 +      TCHAR name[256];
 +
 +      dst = os_zalloc(sizeof(*dst));
 +      if (dst == NULL)
 +              return -1;
 +      wpa_printf(MSG_DEBUG, "CTRL: Open pipe %p", dst);
 +
 +      dst->priv = priv;
 +      dst->debug_level = MSG_INFO;
 +      dst->pipe = INVALID_HANDLE_VALUE;
 +
 +      dst->overlap.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
 +      if (dst->overlap.hEvent == NULL) {
 +              wpa_printf(MSG_ERROR, "CTRL: CreateEvent failed: %d",
 +                         (int) GetLastError());
 +              goto fail;
 +      }
 +
 +      eloop_register_event(dst->overlap.hEvent,
 +                           sizeof(dst->overlap.hEvent),
 +                           wpa_supplicant_ctrl_iface_receive, dst, NULL);
 +
 +#ifdef UNICODE
 +      _snwprintf(name, 256, NAMED_PIPE_PREFIX TEXT("-%S"),
 +                 priv->wpa_s->ifname);
 +#else /* UNICODE */
 +      os_snprintf(name, 256, NAMED_PIPE_PREFIX "-%s",
 +                  priv->wpa_s->ifname);
 +#endif /* UNICODE */
 +
 +      /* TODO: add support for configuring access list for the pipe */
 +      dst->pipe = CreateNamedPipe(name,
 +                                  PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
 +                                  PIPE_TYPE_MESSAGE |
 +                                  PIPE_READMODE_MESSAGE |
 +                                  PIPE_WAIT,
 +                                  15, REPLY_BUFSIZE, REQUEST_BUFSIZE,
 +                                  1000,
 +                                  priv->sec_attr_set ? &priv->attr : NULL);
 +      if (dst->pipe == INVALID_HANDLE_VALUE) {
 +              wpa_printf(MSG_ERROR, "CTRL: CreateNamedPipe failed: %d",
 +                         (int) GetLastError());
 +              goto fail;
 +      }
 +
 +      if (ConnectNamedPipe(dst->pipe, &dst->overlap)) {
 +              wpa_printf(MSG_ERROR, "CTRL: ConnectNamedPipe failed: %d",
 +                         (int) GetLastError());
 +              CloseHandle(dst->pipe);
 +              os_free(dst);
 +              return -1;
 +      }
 +
 +      err = GetLastError();
 +      switch (err) {
 +      case ERROR_IO_PENDING:
 +              wpa_printf(MSG_DEBUG, "CTRL: ConnectNamedPipe: connection in "
 +                         "progress");
 +              break;
 +      case ERROR_PIPE_CONNECTED:
 +              wpa_printf(MSG_DEBUG, "CTRL: ConnectNamedPipe: already "
 +                         "connected");
 +              if (SetEvent(dst->overlap.hEvent))
 +                      break;
 +              /* fall through */
 +      default:
 +              wpa_printf(MSG_DEBUG, "CTRL: ConnectNamedPipe error: %d",
 +                         (int) err);
 +              CloseHandle(dst->pipe);
 +              os_free(dst);
 +              return -1;
 +      }
 +
 +      dst->next = priv->ctrl_dst;
 +      if (dst->next)
 +              dst->next->prev = dst;
 +      priv->ctrl_dst = dst;
 +
 +      return 0;
 +
 +fail:
 +      ctrl_close_pipe(dst);
 +      return -1;
 +}
 +
 +
 +static void ctrl_close_pipe(struct wpa_ctrl_dst *dst)
 +{
 +      wpa_printf(MSG_DEBUG, "CTRL: close pipe %p", dst);
 +
 +      if (dst->overlap.hEvent) {
 +              eloop_unregister_event(dst->overlap.hEvent,
 +                                     sizeof(dst->overlap.hEvent));
 +              CloseHandle(dst->overlap.hEvent);
 +      }
 +
 +      if (dst->pipe != INVALID_HANDLE_VALUE) {
 +              /*
 +               * Could use FlushFileBuffers() here to guarantee that all data
 +               * gets delivered to the client, but that can block, so let's
 +               * not do this for now.
 +               * FlushFileBuffers(dst->pipe);
 +               */
 +              CloseHandle(dst->pipe);
 +      }
 +
 +      if (dst->prev)
 +              dst->prev->next = dst->next;
 +      else
 +              dst->priv->ctrl_dst = dst->next;
 +      if (dst->next)
 +              dst->next->prev = dst->prev;
 +
 +      os_free(dst->rsp_buf);
 +      os_free(dst);
 +}
 +
 +
 +static VOID WINAPI ctrl_iface_write_completed(DWORD err, DWORD bytes,
 +                                            LPOVERLAPPED overlap)
 +{
 +      struct wpa_ctrl_dst *dst = (struct wpa_ctrl_dst *) overlap;
 +      wpa_printf(MSG_DEBUG, "CTRL: Overlapped write completed: dst=%p "
 +                 "err=%d bytes=%d", dst, (int) err, (int) bytes);
 +      if (err) {
 +              ctrl_close_pipe(dst);
 +              return;
 +      }
 +
 +      os_free(dst->rsp_buf);
 +      dst->rsp_buf = NULL;
 +
 +      if (!ReadFileEx(dst->pipe, dst->req_buf, sizeof(dst->req_buf),
 +                      &dst->overlap, ctrl_iface_read_completed)) {
 +              wpa_printf(MSG_DEBUG, "CTRL: ReadFileEx failed: %d",
 +                         (int) GetLastError());
 +              ctrl_close_pipe(dst);
 +              return;
 +      }
 +      wpa_printf(MSG_DEBUG, "CTRL: Overlapped read started for %p", dst);
 +}
 +
 +
 +static void wpa_supplicant_ctrl_iface_rx(struct wpa_ctrl_dst *dst, size_t len)
 +{
 +      struct wpa_supplicant *wpa_s = dst->priv->wpa_s;
 +      char *reply = NULL, *send_buf;
 +      size_t reply_len = 0, send_len;
 +      int new_attached = 0;
 +      char *buf = dst->req_buf;
 +
 +      dst->used = 1;
 +      if (len >= REQUEST_BUFSIZE)
 +              len = REQUEST_BUFSIZE - 1;
 +      buf[len] = '\0';
 +
 +      if (os_strcmp(buf, "ATTACH") == 0) {
 +              dst->attached = 1;
 +              wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor attached");
 +              new_attached = 1;
 +              reply_len = 2;
 +      } else if (os_strcmp(buf, "DETACH") == 0) {
 +              dst->attached = 0;
 +              wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor detached");
 +              reply_len = 2;
 +      } else if (os_strncmp(buf, "LEVEL ", 6) == 0) {
 +              wpa_printf(MSG_DEBUG, "CTRL_IFACE LEVEL %s", buf + 6);
 +              dst->debug_level = atoi(buf + 6);
 +              reply_len = 2;
 +      } else {
 +              reply = wpa_supplicant_ctrl_iface_process(wpa_s, buf,
 +                                                        &reply_len);
 +      }
 +
 +      if (reply) {
 +              send_buf = reply;
 +              send_len = reply_len;
 +      } else if (reply_len == 2) {
 +              send_buf = "OK\n";
 +              send_len = 3;
 +      } else {
 +              send_buf = "FAIL\n";
 +              send_len = 5;
 +      }
 +
 +      os_free(dst->rsp_buf);
 +      dst->rsp_buf = os_malloc(send_len);
 +      if (dst->rsp_buf == NULL) {
 +              ctrl_close_pipe(dst);
 +              os_free(reply);
 +              return;
 +      }
 +      os_memcpy(dst->rsp_buf, send_buf, send_len);
 +      os_free(reply);
 +
 +      if (!WriteFileEx(dst->pipe, dst->rsp_buf, send_len, &dst->overlap,
 +                       ctrl_iface_write_completed)) {
 +              wpa_printf(MSG_DEBUG, "CTRL: WriteFileEx failed: %d",
 +                         (int) GetLastError());
 +              ctrl_close_pipe(dst);
 +      } else {
 +              wpa_printf(MSG_DEBUG, "CTRL: Overlapped write started for %p",
 +                         dst);
 +      }
 +
 +      if (new_attached)
 +              eapol_sm_notify_ctrl_attached(wpa_s->eapol);
 +}
 +
 +
 +static VOID WINAPI ctrl_iface_read_completed(DWORD err, DWORD bytes,
 +                                           LPOVERLAPPED overlap)
 +{
 +      struct wpa_ctrl_dst *dst = (struct wpa_ctrl_dst *) overlap;
 +      wpa_printf(MSG_DEBUG, "CTRL: Overlapped read completed: dst=%p err=%d "
 +                 "bytes=%d", dst, (int) err, (int) bytes);
 +      if (err == 0 && bytes > 0)
 +              wpa_supplicant_ctrl_iface_rx(dst, bytes);
 +}
 +
 +
 +static void wpa_supplicant_ctrl_iface_receive(void *eloop_data, void *user_ctx)
 +{
 +      struct wpa_ctrl_dst *dst = eloop_data;
 +      struct ctrl_iface_priv *priv = dst->priv;
 +      DWORD bytes;
 +
 +      wpa_printf(MSG_DEBUG, "CTRL: wpa_supplicant_ctrl_iface_receive");
 +      ResetEvent(dst->overlap.hEvent);
 +
 +      if (!GetOverlappedResult(dst->pipe, &dst->overlap, &bytes, FALSE)) {
 +              wpa_printf(MSG_DEBUG, "CTRL: GetOverlappedResult failed: %d",
 +                         (int) GetLastError());
 +              return;
 +      }
 +      wpa_printf(MSG_DEBUG, "CTRL: GetOverlappedResult: New client "
 +                 "connected");
 +
 +      /* Open a new named pipe for the next client. */
 +      ctrl_open_pipe(priv);
 +
 +      /* Use write completion function to start reading a command */
 +      ctrl_iface_write_completed(0, 0, &dst->overlap);
 +
 +      ctrl_flush_broken_pipes(priv);
 +}
 +
 +
 +static int ctrl_iface_parse(struct ctrl_iface_priv *priv, const char *params)
 +{
 +      const char *sddl = NULL;
 +      TCHAR *t_sddl;
 +
 +      if (os_strncmp(params, "SDDL=", 5) == 0)
 +              sddl = params + 5;
 +      if (!sddl) {
 +              sddl = os_strstr(params, " SDDL=");
 +              if (sddl)
 +                      sddl += 6;
 +      }
 +
 +      if (!sddl)
 +              return 0;
 +
 +      wpa_printf(MSG_DEBUG, "CTRL: SDDL='%s'", sddl);
 +      os_memset(&priv->attr, 0, sizeof(priv->attr));
 +      priv->attr.nLength = sizeof(priv->attr);
 +      priv->attr.bInheritHandle = FALSE;
 +      t_sddl = wpa_strdup_tchar(sddl);
 +      if (t_sddl == NULL)
 +              return -1;
 +      if (!ConvertStringSecurityDescriptorToSecurityDescriptor(
 +                  t_sddl, SDDL_REVISION_1,
 +                  (PSECURITY_DESCRIPTOR *) (void *)
 +                  &priv->attr.lpSecurityDescriptor,
 +                  NULL)) {
 +              os_free(t_sddl);
 +              wpa_printf(MSG_ERROR, "CTRL: SDDL='%s' - could not convert to "
 +                         "security descriptor: %d",
 +                         sddl, (int) GetLastError());
 +              return -1;
 +      }
 +      os_free(t_sddl);
 +
 +      priv->sec_attr_set = 1;
 +
 +      return 0;
 +}
 +
 +
++static void wpa_supplicant_ctrl_iface_msg_cb(void *ctx, int level,
++                                           enum wpa_msg_type type,
 +                                           const char *txt, size_t len)
 +{
 +      struct wpa_supplicant *wpa_s = ctx;
 +      if (wpa_s == NULL || wpa_s->ctrl_iface == NULL)
 +              return;
 +      wpa_supplicant_ctrl_iface_send(wpa_s->ctrl_iface, level, txt, len);
 +}
 +
 +
 +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;
 +      priv->wpa_s = wpa_s;
 +
 +      if (wpa_s->conf->ctrl_interface == NULL)
 +              return priv;
 +
 +      if (ctrl_iface_parse(priv, wpa_s->conf->ctrl_interface) < 0) {
 +              os_free(priv);
 +              return NULL;
 +      }
 +
 +      if (ctrl_open_pipe(priv) < 0) {
 +              os_free(priv);
 +              return NULL;
 +      }
 +
 +      wpa_msg_register_cb(wpa_supplicant_ctrl_iface_msg_cb);
 +
 +      return priv;
 +}
 +
 +
 +void wpa_supplicant_ctrl_iface_deinit(struct ctrl_iface_priv *priv)
 +{
 +      while (priv->ctrl_dst)
 +              ctrl_close_pipe(priv->ctrl_dst);
 +      if (priv->sec_attr_set)
 +              LocalFree(priv->attr.lpSecurityDescriptor);
 +      os_free(priv);
 +}
 +
 +
 +static void wpa_supplicant_ctrl_iface_send(struct ctrl_iface_priv *priv,
 +                                         int level, const char *buf,
 +                                         size_t len)
 +{
 +      struct wpa_ctrl_dst *dst, *next;
 +      char levelstr[10];
 +      int idx;
 +      char *sbuf;
 +      int llen;
 +      DWORD written;
 +
 +      dst = priv->ctrl_dst;
 +      if (dst == NULL)
 +              return;
 +
 +      os_snprintf(levelstr, sizeof(levelstr), "<%d>", level);
 +
 +      llen = os_strlen(levelstr);
 +      sbuf = os_malloc(llen + len);
 +      if (sbuf == NULL)
 +              return;
 +
 +      os_memcpy(sbuf, levelstr, llen);
 +      os_memcpy(sbuf + llen, buf, len);
 +
 +      idx = 0;
 +      while (dst) {
 +              next = dst->next;
 +              if (dst->attached && level >= dst->debug_level) {
 +                      wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor send %p",
 +                                 dst);
 +                      if (!WriteFile(dst->pipe, sbuf, llen + len, &written,
 +                                     NULL)) {
 +                              wpa_printf(MSG_DEBUG, "CTRL: WriteFile to dst "
 +                                         "%p failed: %d",
 +                                         dst, (int) GetLastError());
 +                              dst->errors++;
 +                              if (dst->errors > 10)
 +                                      ctrl_close_pipe(dst);
 +                      } else
 +                              dst->errors = 0;
 +              }
 +              idx++;
 +              dst = next;
 +      }
 +      os_free(sbuf);
 +}
 +
 +
 +void wpa_supplicant_ctrl_iface_wait(struct ctrl_iface_priv *priv)
 +{
 +      wpa_printf(MSG_DEBUG, "CTRL_IFACE - %s - wait for monitor",
 +                 priv->wpa_s->ifname);
 +      if (priv->ctrl_dst == NULL)
 +              return;
 +      WaitForSingleObject(priv->ctrl_dst->pipe, INFINITE);
 +}
 +
 +
 +/* Global ctrl_iface */
 +
 +struct ctrl_iface_global_priv;
 +
 +struct wpa_global_dst {
 +      /* Note: OVERLAPPED must be the first member of struct wpa_global_dst
 +       */
 +      OVERLAPPED overlap;
 +      struct wpa_global_dst *next, *prev;
 +      struct ctrl_iface_global_priv *priv;
 +      HANDLE pipe;
 +      char req_buf[REQUEST_BUFSIZE];
 +      char *rsp_buf;
 +      int used;
 +};
 +
 +struct ctrl_iface_global_priv {
 +      struct wpa_global *global;
 +      struct wpa_global_dst *ctrl_dst;
 +};
 +
 +
 +static void global_flush_broken_pipes(struct ctrl_iface_global_priv *priv)
 +{
 +      struct wpa_global_dst *dst, *next;
 +
 +      dst = priv->ctrl_dst;
 +
 +      while (dst) {
 +              next = dst->next;
 +              if (ctrl_broken_pipe(dst->pipe, dst->used)) {
 +                      wpa_printf(MSG_DEBUG, "CTRL: closing broken pipe %p",
 +                                 dst);
 +                      global_close_pipe(dst);
 +              }
 +              dst = next;
 +      }
 +}
 +
 +
 +static int global_open_pipe(struct ctrl_iface_global_priv *priv)
 +{
 +      struct wpa_global_dst *dst;
 +      DWORD err;
 +
 +      dst = os_zalloc(sizeof(*dst));
 +      if (dst == NULL)
 +              return -1;
 +      wpa_printf(MSG_DEBUG, "CTRL: Open pipe %p", dst);
 +
 +      dst->priv = priv;
 +      dst->pipe = INVALID_HANDLE_VALUE;
 +
 +      dst->overlap.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
 +      if (dst->overlap.hEvent == NULL) {
 +              wpa_printf(MSG_ERROR, "CTRL: CreateEvent failed: %d",
 +                         (int) GetLastError());
 +              goto fail;
 +      }
 +
 +      eloop_register_event(dst->overlap.hEvent,
 +                           sizeof(dst->overlap.hEvent),
 +                           wpa_supplicant_global_iface_receive, dst, NULL);
 +
 +      /* TODO: add support for configuring access list for the pipe */
 +      dst->pipe = CreateNamedPipe(NAMED_PIPE_PREFIX,
 +                                  PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
 +                                  PIPE_TYPE_MESSAGE |
 +                                  PIPE_READMODE_MESSAGE |
 +                                  PIPE_WAIT,
 +                                  10, REPLY_BUFSIZE, REQUEST_BUFSIZE,
 +                                  1000, NULL);
 +      if (dst->pipe == INVALID_HANDLE_VALUE) {
 +              wpa_printf(MSG_ERROR, "CTRL: CreateNamedPipe failed: %d",
 +                         (int) GetLastError());
 +              goto fail;
 +      }
 +
 +      if (ConnectNamedPipe(dst->pipe, &dst->overlap)) {
 +              wpa_printf(MSG_ERROR, "CTRL: ConnectNamedPipe failed: %d",
 +                         (int) GetLastError());
 +              CloseHandle(dst->pipe);
 +              os_free(dst);
 +              return -1;
 +      }
 +
 +      err = GetLastError();
 +      switch (err) {
 +      case ERROR_IO_PENDING:
 +              wpa_printf(MSG_DEBUG, "CTRL: ConnectNamedPipe: connection in "
 +                         "progress");
 +              break;
 +      case ERROR_PIPE_CONNECTED:
 +              wpa_printf(MSG_DEBUG, "CTRL: ConnectNamedPipe: already "
 +                         "connected");
 +              if (SetEvent(dst->overlap.hEvent))
 +                      break;
 +              /* fall through */
 +      default:
 +              wpa_printf(MSG_DEBUG, "CTRL: ConnectNamedPipe error: %d",
 +                         (int) err);
 +              CloseHandle(dst->pipe);
 +              os_free(dst);
 +              return -1;
 +      }
 +
 +      dst->next = priv->ctrl_dst;
 +      if (dst->next)
 +              dst->next->prev = dst;
 +      priv->ctrl_dst = dst;
 +
 +      return 0;
 +
 +fail:
 +      global_close_pipe(dst);
 +      return -1;
 +}
 +
 +
 +static void global_close_pipe(struct wpa_global_dst *dst)
 +{
 +      wpa_printf(MSG_DEBUG, "CTRL: close pipe %p", dst);
 +
 +      if (dst->overlap.hEvent) {
 +              eloop_unregister_event(dst->overlap.hEvent,
 +                                     sizeof(dst->overlap.hEvent));
 +              CloseHandle(dst->overlap.hEvent);
 +      }
 +
 +      if (dst->pipe != INVALID_HANDLE_VALUE) {
 +              /*
 +               * Could use FlushFileBuffers() here to guarantee that all data
 +               * gets delivered to the client, but that can block, so let's
 +               * not do this for now.
 +               * FlushFileBuffers(dst->pipe);
 +               */
 +              CloseHandle(dst->pipe);
 +      }
 +
 +      if (dst->prev)
 +              dst->prev->next = dst->next;
 +      else
 +              dst->priv->ctrl_dst = dst->next;
 +      if (dst->next)
 +              dst->next->prev = dst->prev;
 +
 +      os_free(dst->rsp_buf);
 +      os_free(dst);
 +}
 +
 +
 +static VOID WINAPI global_iface_write_completed(DWORD err, DWORD bytes,
 +                                              LPOVERLAPPED overlap)
 +{
 +      struct wpa_global_dst *dst = (struct wpa_global_dst *) overlap;
 +      wpa_printf(MSG_DEBUG, "CTRL: Overlapped write completed: dst=%p "
 +                 "err=%d bytes=%d", dst, (int) err, (int) bytes);
 +      if (err) {
 +              global_close_pipe(dst);
 +              return;
 +      }
 +
 +      os_free(dst->rsp_buf);
 +      dst->rsp_buf = NULL;
 +
 +      if (!ReadFileEx(dst->pipe, dst->req_buf, sizeof(dst->req_buf),
 +                      &dst->overlap, global_iface_read_completed)) {
 +              wpa_printf(MSG_DEBUG, "CTRL: ReadFileEx failed: %d",
 +                         (int) GetLastError());
 +              global_close_pipe(dst);
 +              /* FIX: if this was the pipe waiting for new global
 +               * connections, at this point there are no open global pipes..
 +               * Should try to open a new pipe.. */
 +              return;
 +      }
 +      wpa_printf(MSG_DEBUG, "CTRL: Overlapped read started for %p", dst);
 +}
 +
 +
 +static void wpa_supplicant_global_iface_rx(struct wpa_global_dst *dst,
 +                                         size_t len)
 +{
 +      struct wpa_global *global = dst->priv->global;
 +      char *reply = NULL, *send_buf;
 +      size_t reply_len = 0, send_len;
 +      char *buf = dst->req_buf;
 +
 +      dst->used = 1;
 +      if (len >= REQUEST_BUFSIZE)
 +              len = REQUEST_BUFSIZE - 1;
 +      buf[len] = '\0';
 +
 +      reply = wpa_supplicant_global_ctrl_iface_process(global, buf,
 +                                                       &reply_len);
 +      if (reply) {
 +              send_buf = reply;
 +              send_len = reply_len;
 +      } else if (reply_len) {
 +              send_buf = "FAIL\n";
 +              send_len = 5;
 +      } else {
 +              os_free(dst->rsp_buf);
 +              dst->rsp_buf = NULL;
 +              return;
 +      }
 +
 +      os_free(dst->rsp_buf);
 +      dst->rsp_buf = os_malloc(send_len);
 +      if (dst->rsp_buf == NULL) {
 +              global_close_pipe(dst);
 +              os_free(reply);
 +              return;
 +      }
 +      os_memcpy(dst->rsp_buf, send_buf, send_len);
 +      os_free(reply);
 +
 +      if (!WriteFileEx(dst->pipe, dst->rsp_buf, send_len, &dst->overlap,
 +                       global_iface_write_completed)) {
 +              wpa_printf(MSG_DEBUG, "CTRL: WriteFileEx failed: %d",
 +                         (int) GetLastError());
 +              global_close_pipe(dst);
 +      } else {
 +              wpa_printf(MSG_DEBUG, "CTRL: Overlapped write started for %p",
 +                         dst);
 +      }
 +}
 +
 +
 +static VOID WINAPI global_iface_read_completed(DWORD err, DWORD bytes,
 +                                             LPOVERLAPPED overlap)
 +{
 +      struct wpa_global_dst *dst = (struct wpa_global_dst *) overlap;
 +      wpa_printf(MSG_DEBUG, "CTRL: Overlapped read completed: dst=%p err=%d "
 +                 "bytes=%d", dst, (int) err, (int) bytes);
 +      if (err == 0 && bytes > 0)
 +              wpa_supplicant_global_iface_rx(dst, bytes);
 +}
 +
 +
 +static void wpa_supplicant_global_iface_receive(void *eloop_data,
 +                                              void *user_ctx)
 +{
 +      struct wpa_global_dst *dst = eloop_data;
 +      struct ctrl_iface_global_priv *priv = dst->priv;
 +      DWORD bytes;
 +
 +      wpa_printf(MSG_DEBUG, "CTRL: wpa_supplicant_global_iface_receive");
 +      ResetEvent(dst->overlap.hEvent);
 +
 +      if (!GetOverlappedResult(dst->pipe, &dst->overlap, &bytes, FALSE)) {
 +              wpa_printf(MSG_DEBUG, "CTRL: GetOverlappedResult failed: %d",
 +                         (int) GetLastError());
 +              return;
 +      }
 +      wpa_printf(MSG_DEBUG, "CTRL: GetOverlappedResult: New client "
 +                 "connected");
 +
 +      /* Open a new named pipe for the next client. */
 +      if (global_open_pipe(priv) < 0) {
 +              wpa_printf(MSG_DEBUG, "CTRL: global_open_pipe failed");
 +              return;
 +      }
 +
 +      /* Use write completion function to start reading a command */
 +      global_iface_write_completed(0, 0, &dst->overlap);
 +
 +      global_flush_broken_pipes(priv);
 +}
 +
 +
 +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;
 +      priv->global = global;
 +
 +      if (global_open_pipe(priv) < 0) {
 +              os_free(priv);
 +              return NULL;
 +      }
 +
 +      return priv;
 +}
 +
 +
 +void
 +wpa_supplicant_global_ctrl_iface_deinit(struct ctrl_iface_global_priv *priv)
 +{
 +      while (priv->ctrl_dst)
 +              global_close_pipe(priv->ctrl_dst);
 +      os_free(priv);
 +}
index bf6a3df6c3c51468db10fce40b75721a6423ee3f,0000000000000000000000000000000000000000..76f69f2b57bb26aef03d672f754e36f87b20885e
mode 100644,000000..100644
--- /dev/null
@@@ -1,689 -1,0 +1,690 @@@
- static void wpa_supplicant_ctrl_iface_msg_cb(void *ctx, int level, int global,
 +/*
 + * WPA Supplicant / UDP socket -based control interface
 + * Copyright (c) 2004-2005, Jouni Malinen <j@w1.fi>
 + *
 + * This software may be distributed under the terms of the BSD license.
 + * See README for more details.
 + */
 +
 +#include "includes.h"
 +
 +#include "common.h"
 +#include "eloop.h"
 +#include "config.h"
 +#include "eapol_supp/eapol_supp_sm.h"
 +#include "wpa_supplicant_i.h"
 +#include "ctrl_iface.h"
 +#include "common/wpa_ctrl.h"
 +
 +
 +#define COOKIE_LEN 8
 +
 +/* Per-interface ctrl_iface */
 +
 +/**
 + * struct wpa_ctrl_dst - Internal data structure of control interface monitors
 + *
 + * This structure is used to store information about registered control
 + * interface monitors into struct wpa_supplicant. This data is private to
 + * ctrl_iface_udp.c and should not be touched directly from other files.
 + */
 +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;
 +};
 +
 +
 +struct ctrl_iface_priv {
 +      struct wpa_supplicant *wpa_s;
 +      int sock;
 +      struct wpa_ctrl_dst *ctrl_dst;
 +      u8 cookie[COOKIE_LEN];
 +};
 +
 +
 +static void wpa_supplicant_ctrl_iface_send(struct ctrl_iface_priv *priv,
 +                                         int level, const char *buf,
 +                                         size_t len);
 +
 +
 +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(*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);
 +                      return 0;
 +              }
 +              prev = dst;
 +              dst = dst->next;
 +      }
 +      return -1;
 +}
 +
 +
 +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;
 +              }
 +              dst = dst->next;
 +      }
 +
 +      return -1;
 +}
 +
 +
 +static char *
 +wpa_supplicant_ctrl_iface_get_cookie(struct ctrl_iface_priv *priv,
 +                                   size_t *reply_len)
 +{
 +      char *reply;
 +      reply = os_malloc(7 + 2 * COOKIE_LEN + 1);
 +      if (reply == NULL) {
 +              *reply_len = 1;
 +              return NULL;
 +      }
 +
 +      os_memcpy(reply, "COOKIE=", 7);
 +      wpa_snprintf_hex(reply + 7, 2 * COOKIE_LEN + 1,
 +                       priv->cookie, COOKIE_LEN);
 +
 +      *reply_len = 7 + 2 * COOKIE_LEN;
 +      return reply;
 +}
 +
 +
 +static void wpa_supplicant_ctrl_iface_receive(int sock, void *eloop_ctx,
 +                                            void *sock_ctx)
 +{
 +      struct wpa_supplicant *wpa_s = 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;
 +      int new_attached = 0;
 +      u8 cookie[COOKIE_LEN];
 +
 +      res = recvfrom(sock, buf, sizeof(buf) - 1, 0,
 +                     (struct sockaddr *) &from, &fromlen);
 +      if (res < 0) {
 +              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
 +               * frames since the socket is bound to only localhost address.
 +               * Just in case, drop the frame if it is coming from any other
 +               * address.
 +               */
 +              wpa_printf(MSG_DEBUG, "CTRL: Drop packet from unexpected "
 +                         "source %s", inet_ntoa(from.sin_addr));
 +              return;
 +      }
 +#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
 +#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
 +
 +      buf[res] = '\0';
 +
 +      if (os_strcmp(buf, "GET_COOKIE") == 0) {
 +              reply = wpa_supplicant_ctrl_iface_get_cookie(priv, &reply_len);
 +              goto done;
 +      }
 +
 +      /*
 +       * Require that the client includes a prefix with the 'cookie' value
 +       * fetched with GET_COOKIE command. This is used to verify that the
 +       * client has access to a bidirectional link over UDP in order to
 +       * avoid attacks using forged localhost IP address even if the OS does
 +       * not block such frames from remote destinations.
 +       */
 +      if (os_strncmp(buf, "COOKIE=", 7) != 0) {
 +              wpa_printf(MSG_DEBUG, "CTLR: No cookie in the request - "
 +                         "drop request");
 +              return;
 +      }
 +
 +      if (hexstr2bin(buf + 7, cookie, COOKIE_LEN) < 0) {
 +              wpa_printf(MSG_DEBUG, "CTLR: Invalid cookie format in the "
 +                         "request - drop request");
 +              return;
 +      }
 +
 +      if (os_memcmp(cookie, priv->cookie, COOKIE_LEN) != 0) {
 +              wpa_printf(MSG_DEBUG, "CTLR: Invalid cookie in the request - "
 +                         "drop request");
 +              return;
 +      }
 +
 +      pos = buf + 7 + 2 * COOKIE_LEN;
 +      while (*pos == ' ')
 +              pos++;
 +
 +      if (os_strcmp(pos, "ATTACH") == 0) {
 +              if (wpa_supplicant_ctrl_iface_attach(priv, &from, fromlen))
 +                      reply_len = 1;
 +              else {
 +                      new_attached = 1;
 +                      reply_len = 2;
 +              }
 +      } else if (os_strcmp(pos, "DETACH") == 0) {
 +              if (wpa_supplicant_ctrl_iface_detach(priv, &from, fromlen))
 +                      reply_len = 1;
 +              else
 +                      reply_len = 2;
 +      } else if (os_strncmp(pos, "LEVEL ", 6) == 0) {
 +              if (wpa_supplicant_ctrl_iface_level(priv, &from, fromlen,
 +                                                  pos + 6))
 +                      reply_len = 1;
 +              else
 +                      reply_len = 2;
 +      } else {
 +              reply = wpa_supplicant_ctrl_iface_process(wpa_s, pos,
 +                                                        &reply_len);
 +      }
 +
 + done:
 +      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 (new_attached)
 +              eapol_sm_notify_ctrl_attached(wpa_s->eapol);
 +}
 +
 +
++static void wpa_supplicant_ctrl_iface_msg_cb(void *ctx, int level,
++                                           enum wpa_msg_type type,
 +                                           const char *txt, size_t len)
 +{
 +      struct wpa_supplicant *wpa_s = ctx;
 +      if (wpa_s == NULL || wpa_s->ctrl_iface == NULL)
 +              return;
 +      wpa_supplicant_ctrl_iface_send(wpa_s->ctrl_iface, level, txt, len);
 +}
 +
 +
 +struct ctrl_iface_priv *
 +wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s)
 +{
 +      struct ctrl_iface_priv *priv;
 +      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)
 +              return NULL;
 +      priv->wpa_s = wpa_s;
 +      priv->sock = -1;
 +      os_get_random(priv->cookie, COOKIE_LEN);
 +
 +      if (wpa_s->conf->ctrl_interface == NULL)
 +              return priv;
 +
 +      priv->sock = socket(domain, SOCK_DGRAM, 0);
 +      if (priv->sock < 0) {
 +              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;
 +              wpa_printf(MSG_ERROR, "bind(AF_INET): %s", strerror(errno));
 +              goto fail;
 +      }
 +
 +#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE
 +      wpa_msg(wpa_s, MSG_DEBUG, "ctrl_iface_init UDP port: %d", port);
 +#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
 +
 +      eloop_register_read_sock(priv->sock, wpa_supplicant_ctrl_iface_receive,
 +                               wpa_s, priv);
 +      wpa_msg_register_cb(wpa_supplicant_ctrl_iface_msg_cb);
 +
 +      return priv;
 +
 +fail:
 +      if (priv->sock >= 0)
 +              close(priv->sock);
 +      os_free(priv);
 +      return NULL;
 +}
 +
 +
 +void wpa_supplicant_ctrl_iface_deinit(struct ctrl_iface_priv *priv)
 +{
 +      struct wpa_ctrl_dst *dst, *prev;
 +
 +      if (priv->sock > -1) {
 +              eloop_unregister_read_sock(priv->sock);
 +              if (priv->ctrl_dst) {
 +                      /*
 +                       * 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(0, 100000);
 +              }
 +              close(priv->sock);
 +              priv->sock = -1;
 +      }
 +
 +      dst = priv->ctrl_dst;
 +      while (dst) {
 +              prev = dst;
 +              dst = dst->next;
 +              os_free(prev);
 +      }
 +      os_free(priv);
 +}
 +
 +
 +static void wpa_supplicant_ctrl_iface_send(struct ctrl_iface_priv *priv,
 +                                         int level, const char *buf,
 +                                         size_t len)
 +{
 +      struct wpa_ctrl_dst *dst, *next;
 +      char levelstr[10];
 +      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)
 +              return;
 +
 +      os_snprintf(levelstr, sizeof(levelstr), "<%d>", level);
 +
 +      llen = os_strlen(levelstr);
 +      sbuf = os_malloc(llen + len);
 +      if (sbuf == NULL)
 +              return;
 +
 +      os_memcpy(sbuf, levelstr, llen);
 +      os_memcpy(sbuf + llen, buf, len);
 +
 +      idx = 0;
 +      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) {
 +                              wpa_printf(MSG_ERROR,
 +                                         "sendto(CTRL_IFACE monitor): %s",
 +                                         strerror(errno));
 +                              dst->errors++;
 +                              if (dst->errors > 10) {
 +                                      wpa_supplicant_ctrl_iface_detach(
 +                                              priv, &dst->addr,
 +                                              dst->addrlen);
 +                              }
 +                      } else
 +                              dst->errors = 0;
 +              }
 +              idx++;
 +              dst = next;
 +      }
 +      os_free(sbuf);
 +}
 +
 +
 +void wpa_supplicant_ctrl_iface_wait(struct ctrl_iface_priv *priv)
 +{
 +      wpa_printf(MSG_DEBUG, "CTRL_IFACE - %s - wait for monitor",
 +                 priv->wpa_s->ifname);
 +      eloop_wait_for_read_sock(priv->sock);
 +}
 +
 +
 +/* Global ctrl_iface */
 +
 +struct ctrl_iface_global_priv {
 +      int sock;
 +      u8 cookie[COOKIE_LEN];
 +};
 +
 +
 +static char *
 +wpa_supplicant_global_get_cookie(struct ctrl_iface_global_priv *priv,
 +                               size_t *reply_len)
 +{
 +      char *reply;
 +      reply = os_malloc(7 + 2 * COOKIE_LEN + 1);
 +      if (reply == NULL) {
 +              *reply_len = 1;
 +              return NULL;
 +      }
 +
 +      os_memcpy(reply, "COOKIE=", 7);
 +      wpa_snprintf_hex(reply + 7, 2 * COOKIE_LEN + 1,
 +                       priv->cookie, COOKIE_LEN);
 +
 +      *reply_len = 7 + 2 * COOKIE_LEN;
 +      return reply;
 +}
 +
 +
 +static void wpa_supplicant_global_ctrl_iface_receive(int sock, void *eloop_ctx,
 +                                                   void *sock_ctx)
 +{
 +      struct wpa_global *global = eloop_ctx;
 +      struct ctrl_iface_global_priv *priv = sock_ctx;
 +      char buf[256], *pos;
 +      int res;
 +      struct sockaddr_in from;
 +      socklen_t fromlen = sizeof(from);
 +      char *reply;
 +      size_t reply_len;
 +      u8 cookie[COOKIE_LEN];
 +
 +      res = recvfrom(sock, buf, sizeof(buf) - 1, 0,
 +                     (struct sockaddr *) &from, &fromlen);
 +      if (res < 0) {
 +              wpa_printf(MSG_ERROR, "recvfrom(ctrl_iface): %s",
 +                         strerror(errno));
 +              return;
 +      }
 +
 +#ifndef CONFIG_CTRL_IFACE_UDP_REMOTE
 +      if (from.sin_addr.s_addr != htonl((127 << 24) | 1)) {
 +              /*
 +               * The OS networking stack is expected to drop this kind of
 +               * frames since the socket is bound to only localhost address.
 +               * Just in case, drop the frame if it is coming from any other
 +               * address.
 +               */
 +              wpa_printf(MSG_DEBUG, "CTRL: Drop packet from unexpected "
 +                         "source %s", inet_ntoa(from.sin_addr));
 +              return;
 +      }
 +#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
 +
 +      buf[res] = '\0';
 +
 +      if (os_strcmp(buf, "GET_COOKIE") == 0) {
 +              reply = wpa_supplicant_global_get_cookie(priv, &reply_len);
 +              goto done;
 +      }
 +
 +      if (os_strncmp(buf, "COOKIE=", 7) != 0) {
 +              wpa_printf(MSG_DEBUG, "CTLR: No cookie in the request - "
 +                         "drop request");
 +              return;
 +      }
 +
 +      if (hexstr2bin(buf + 7, cookie, COOKIE_LEN) < 0) {
 +              wpa_printf(MSG_DEBUG, "CTLR: Invalid cookie format in the "
 +                         "request - drop request");
 +              return;
 +      }
 +
 +      if (os_memcmp(cookie, priv->cookie, COOKIE_LEN) != 0) {
 +              wpa_printf(MSG_DEBUG, "CTLR: Invalid cookie in the request - "
 +                         "drop request");
 +              return;
 +      }
 +
 +      pos = buf + 7 + 2 * COOKIE_LEN;
 +      while (*pos == ' ')
 +              pos++;
 +
 +      reply = wpa_supplicant_global_ctrl_iface_process(global, pos,
 +                                                       &reply_len);
 +
 + done:
 +      if (reply) {
 +              sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from,
 +                     fromlen);
 +              os_free(reply);
 +      } else if (reply_len) {
 +              sendto(sock, "FAIL\n", 5, 0, (struct sockaddr *) &from,
 +                     fromlen);
 +      }
 +}
 +
 +
 +struct ctrl_iface_global_priv *
 +wpa_supplicant_global_ctrl_iface_init(struct wpa_global *global)
 +{
 +      struct ctrl_iface_global_priv *priv;
 +      struct sockaddr_in addr;
 +      int port = WPA_GLOBAL_CTRL_IFACE_PORT;
 +
 +      priv = os_zalloc(sizeof(*priv));
 +      if (priv == NULL)
 +              return NULL;
 +      priv->sock = -1;
 +      os_get_random(priv->cookie, COOKIE_LEN);
 +
 +      if (global->params.ctrl_interface == NULL)
 +              return priv;
 +
 +      wpa_printf(MSG_DEBUG, "Global control interface '%s'",
 +                 global->params.ctrl_interface);
 +
 +      priv->sock = socket(PF_INET, SOCK_DGRAM, 0);
 +      if (priv->sock < 0) {
 +              wpa_printf(MSG_ERROR, "socket(PF_INET): %s", strerror(errno));
 +              goto fail;
 +      }
 +
 +      os_memset(&addr, 0, sizeof(addr));
 +      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 */
 +try_again:
 +      addr.sin_port = htons(port);
 +      if (bind(priv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
 +              port++;
 +              if ((port - WPA_GLOBAL_CTRL_IFACE_PORT) <
 +                  WPA_GLOBAL_CTRL_IFACE_PORT_LIMIT)
 +                      goto try_again;
 +              wpa_printf(MSG_ERROR, "bind(AF_INET): %s", strerror(errno));
 +              goto fail;
 +      }
 +
 +#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE
 +      wpa_printf(MSG_DEBUG, "global_ctrl_iface_init UDP port: %d", port);
 +#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
 +
 +      eloop_register_read_sock(priv->sock,
 +                               wpa_supplicant_global_ctrl_iface_receive,
 +                               global, priv);
 +
 +      return priv;
 +
 +fail:
 +      if (priv->sock >= 0)
 +              close(priv->sock);
 +      os_free(priv);
 +      return NULL;
 +}
 +
 +
 +void
 +wpa_supplicant_global_ctrl_iface_deinit(struct ctrl_iface_global_priv *priv)
 +{
 +      if (priv->sock >= 0) {
 +              eloop_unregister_read_sock(priv->sock);
 +              close(priv->sock);
 +      }
 +      os_free(priv);
 +}
index b1ac76668897e07e2aa286156cd6856811b6eff7,0000000000000000000000000000000000000000..11f2814731308f6840714094af9e6bcac962d0f8
mode 100644,000000..100644
--- /dev/null
@@@ -1,1120 -1,0 +1,1218 @@@
- static void wpa_supplicant_ctrl_iface_msg_cb(void *ctx, int level, int global,
 +/*
 + * WPA Supplicant / UNIX domain socket -based control interface
 + * 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 "includes.h"
 +#include <sys/un.h>
 +#include <sys/stat.h>
 +#include <grp.h>
 +#include <stddef.h>
 +#include <unistd.h>
 +#include <fcntl.h>
++#ifdef __linux__
++#include <sys/ioctl.h>
++#include <linux/sockios.h>
++#endif /* __linux__ */
 +#ifdef ANDROID
 +#include <cutils/sockets.h>
 +#endif /* ANDROID */
 +
 +#include "utils/common.h"
 +#include "utils/eloop.h"
 +#include "utils/list.h"
 +#include "eapol_supp/eapol_supp_sm.h"
 +#include "config.h"
 +#include "wpa_supplicant_i.h"
 +#include "ctrl_iface.h"
 +
 +/* Per-interface ctrl_iface */
 +
 +/**
 + * struct wpa_ctrl_dst - Internal data structure of control interface monitors
 + *
 + * This structure is used to store information about registered control
 + * interface monitors into struct wpa_supplicant. This data is private to
 + * ctrl_iface_unix.c and should not be touched directly from other files.
 + */
 +struct wpa_ctrl_dst {
 +      struct dl_list list;
 +      struct sockaddr_un addr;
 +      socklen_t addrlen;
 +      int debug_level;
 +      int errors;
 +};
 +
 +
 +struct ctrl_iface_priv {
 +      struct wpa_supplicant *wpa_s;
 +      int sock;
 +      struct dl_list ctrl_dst;
 +      int android_control_socket;
 +};
 +
 +
 +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(struct wpa_supplicant *wpa_s,
 +                                         const char *ifname, int sock,
 +                                         struct dl_list *ctrl_dst,
 +                                         int level, const char *buf,
 +                                         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 void wpas_ctrl_sock_debug(const char *title, int sock, const char *buf,
++                               size_t len)
++{
++#ifdef __linux__
++      socklen_t optlen;
++      int sndbuf, outq;
++      int level = MSG_MSGDUMP;
++
++      if (len >= 5 && os_strncmp(buf, "PONG\n", 5) == 0)
++              level = MSG_EXCESSIVE;
++
++      optlen = sizeof(sndbuf);
++      sndbuf = 0;
++      if (getsockopt(sock, SOL_SOCKET, SO_SNDBUF, &sndbuf, &optlen) < 0)
++              sndbuf = -1;
++
++      if (ioctl(sock, SIOCOUTQ, &outq) < 0)
++              outq = -1;
++
++      wpa_printf(level,
++                 "CTRL-DEBUG: %s: sock=%d sndbuf=%d outq=%d send_len=%d",
++                 title, sock, sndbuf, outq, (int) len);
++#endif /* __linux__ */
++}
++
++
 +static int wpa_supplicant_ctrl_iface_attach(struct dl_list *ctrl_dst,
 +                                          struct sockaddr_un *from,
 +                                          socklen_t fromlen, int global)
 +{
 +      struct wpa_ctrl_dst *dst;
 +      char addr_txt[200];
 +
 +      dst = os_zalloc(sizeof(*dst));
 +      if (dst == NULL)
 +              return -1;
 +      os_memcpy(&dst->addr, from, sizeof(struct sockaddr_un));
 +      dst->addrlen = fromlen;
 +      dst->debug_level = MSG_INFO;
 +      dl_list_add(ctrl_dst, &dst->list);
 +      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;
 +}
 +
 +
 +static int wpa_supplicant_ctrl_iface_detach(struct dl_list *ctrl_dst,
 +                                          struct sockaddr_un *from,
 +                                          socklen_t fromlen)
 +{
 +      struct wpa_ctrl_dst *dst;
 +
 +      dl_list_for_each(dst, ctrl_dst, struct wpa_ctrl_dst, list) {
 +              if (fromlen == dst->addrlen &&
 +                  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);
 +                      return 0;
 +              }
 +      }
 +      return -1;
 +}
 +
 +
 +static int wpa_supplicant_ctrl_iface_level(struct ctrl_iface_priv *priv,
 +                                         struct sockaddr_un *from,
 +                                         socklen_t fromlen,
 +                                         char *level)
 +{
 +      struct wpa_ctrl_dst *dst;
 +
 +      wpa_printf(MSG_DEBUG, "CTRL_IFACE LEVEL %s", level);
 +
 +      dl_list_for_each(dst, &priv->ctrl_dst, struct wpa_ctrl_dst, list) {
 +              if (fromlen == dst->addrlen &&
 +                  os_memcmp(from->sun_path, dst->addr.sun_path,
 +                            fromlen - offsetof(struct sockaddr_un, sun_path))
 +                  == 0) {
 +                      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;
 +              }
 +      }
 +
 +      return -1;
 +}
 +
 +
 +static void wpa_supplicant_ctrl_iface_receive(int sock, void *eloop_ctx,
 +                                            void *sock_ctx)
 +{
 +      struct wpa_supplicant *wpa_s = eloop_ctx;
 +      struct ctrl_iface_priv *priv = sock_ctx;
 +      char buf[4096];
 +      int res;
 +      struct sockaddr_un from;
 +      socklen_t fromlen = sizeof(from);
 +      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) {
 +              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, 0))
 +                      reply_len = 1;
 +              else {
 +                      new_attached = 1;
 +                      reply_len = 2;
 +              }
 +      } else if (os_strcmp(buf, "DETACH") == 0) {
 +              if (wpa_supplicant_ctrl_iface_detach(&priv->ctrl_dst, &from,
 +                                                   fromlen))
 +                      reply_len = 1;
 +              else
 +                      reply_len = 2;
 +      } else if (os_strncmp(buf, "LEVEL ", 6) == 0) {
 +              if (wpa_supplicant_ctrl_iface_level(priv, &from, fromlen,
 +                                                  buf + 6))
 +                      reply_len = 1;
 +              else
 +                      reply_len = 2;
 +      } else {
 +              reply_buf = wpa_supplicant_ctrl_iface_process(wpa_s, buf,
 +                                                            &reply_len);
 +              reply = reply_buf;
++
++              /*
++               * There could be some password/key material in the command, so
++               * clear the buffer explicitly now that it is not needed
++               * anymore.
++               */
++              os_memset(buf, 0, res);
 +      }
 +
 +      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) {
++              wpas_ctrl_sock_debug("ctrl_sock-sendto", sock, reply,
++                                   reply_len);
 +              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);
 +}
 +
 +
 +static char * wpa_supplicant_ctrl_iface_path(struct wpa_supplicant *wpa_s)
 +{
 +      char *buf;
 +      size_t len;
 +      char *pbuf, *dir = NULL;
 +      int res;
 +
 +      if (wpa_s->conf->ctrl_interface == NULL)
 +              return NULL;
 +
 +      pbuf = os_strdup(wpa_s->conf->ctrl_interface);
 +      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)
 +                      *gid_str = '\0';
 +      } else
 +              dir = pbuf;
 +
 +      len = os_strlen(dir) + os_strlen(wpa_s->ifname) + 2;
 +      buf = os_malloc(len);
 +      if (buf == NULL) {
 +              os_free(pbuf);
 +              return NULL;
 +      }
 +
 +      res = os_snprintf(buf, len, "%s/%s", dir, wpa_s->ifname);
 +      if (os_snprintf_error(len, res)) {
 +              os_free(pbuf);
 +              os_free(buf);
 +              return NULL;
 +      }
 +#ifdef __CYGWIN__
 +      {
 +              /* Windows/WinPcap uses interface names that are not suitable
 +               * as a file name - convert invalid chars to underscores */
 +              char *pos = buf;
 +              while (*pos) {
 +                      if (*pos == '\\')
 +                              *pos = '_';
 +                      pos++;
 +              }
 +      }
 +#endif /* __CYGWIN__ */
 +      os_free(pbuf);
 +      return buf;
 +}
 +
 +
-       if (global != 2 && wpa_s->global->ctrl_iface) {
++static void wpa_supplicant_ctrl_iface_msg_cb(void *ctx, int level,
++                                           enum wpa_msg_type type,
 +                                           const char *txt, size_t len)
 +{
 +      struct wpa_supplicant *wpa_s = ctx;
 +
 +      if (wpa_s == NULL)
 +              return;
 +
-                       wpa_supplicant_ctrl_iface_send(wpa_s, global ? NULL :
-                                                      wpa_s->ifname,
-                                                      priv->sock,
-                                                      &priv->ctrl_dst,
-                                                      level, txt, len, NULL,
-                                                      priv);
++      if (type != WPA_MSG_NO_GLOBAL && wpa_s->global->ctrl_iface) {
 +              struct ctrl_iface_global_priv *priv = wpa_s->global->ctrl_iface;
 +              if (!dl_list_empty(&priv->ctrl_dst)) {
-       if (wpa_s->ctrl_iface == NULL)
++                      wpa_supplicant_ctrl_iface_send(
++                              wpa_s,
++                              type != WPA_MSG_PER_INTERFACE ?
++                              NULL : wpa_s->ifname,
++                              priv->sock, &priv->ctrl_dst, level, txt, len,
++                              NULL, priv);
 +              }
 +      }
 +
-                       wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor sent successfully to %s",
++      if (type == WPA_MSG_ONLY_GLOBAL || wpa_s->ctrl_iface == NULL)
 +              return;
 +      wpa_supplicant_ctrl_iface_send(wpa_s, NULL, wpa_s->ctrl_iface->sock,
 +                                     &wpa_s->ctrl_iface->ctrl_dst,
 +                                     level, txt, len, wpa_s->ctrl_iface,
 +                                     NULL);
 +}
 +
 +
 +static int wpas_ctrl_iface_open_sock(struct wpa_supplicant *wpa_s,
 +                                   struct ctrl_iface_priv *priv)
 +{
 +      struct sockaddr_un addr;
 +      char *fname = NULL;
 +      gid_t gid = 0;
 +      int gid_set = 0;
 +      char *buf, *dir = NULL, *gid_str = NULL;
 +      struct group *grp;
 +      char *endp;
 +      int flags;
 +
 +      buf = os_strdup(wpa_s->conf->ctrl_interface);
 +      if (buf == NULL)
 +              goto fail;
 +#ifdef ANDROID
 +      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) {
 +              priv->android_control_socket = 1;
 +              goto havesock;
 +      }
 +#endif /* ANDROID */
 +      if (os_strncmp(buf, "DIR=", 4) == 0) {
 +              dir = buf + 4;
 +              gid_str = os_strstr(dir, " GROUP=");
 +              if (gid_str) {
 +                      *gid_str = '\0';
 +                      gid_str += 7;
 +              }
 +      } else {
 +              dir = buf;
 +              gid_str = wpa_s->conf->ctrl_interface_group;
 +      }
 +
 +      if (mkdir(dir, S_IRWXU | S_IRWXG) < 0) {
 +              if (errno == EEXIST) {
 +                      wpa_printf(MSG_DEBUG, "Using existing control "
 +                                 "interface directory.");
 +              } else {
 +                      wpa_printf(MSG_ERROR, "mkdir[ctrl_interface=%s]: %s",
 +                                 dir, strerror(errno));
 +                      goto fail;
 +              }
 +      }
 +
 +#ifdef ANDROID
 +      /*
 +       * wpa_supplicant is started from /init.*.rc on Android and that seems
 +       * to be using umask 0077 which would leave the control interface
 +       * directory without group access. This breaks things since Wi-Fi
 +       * framework assumes that this directory can be accessed by other
 +       * applications in the wifi group. Fix this by adding group access even
 +       * if umask value would prevent this.
 +       */
 +      if (chmod(dir, S_IRWXU | S_IRWXG) < 0) {
 +              wpa_printf(MSG_ERROR, "CTRL: Could not chmod directory: %s",
 +                         strerror(errno));
 +              /* Try to continue anyway */
 +      }
 +#endif /* ANDROID */
 +
 +      if (gid_str) {
 +              grp = getgrnam(gid_str);
 +              if (grp) {
 +                      gid = grp->gr_gid;
 +                      gid_set = 1;
 +                      wpa_printf(MSG_DEBUG, "ctrl_interface_group=%d"
 +                                 " (from group name '%s')",
 +                                 (int) gid, gid_str);
 +              } else {
 +                      /* Group name not found - try to parse this as gid */
 +                      gid = strtol(gid_str, &endp, 10);
 +                      if (*gid_str == '\0' || *endp != '\0') {
 +                              wpa_printf(MSG_ERROR, "CTRL: Invalid group "
 +                                         "'%s'", gid_str);
 +                              goto fail;
 +                      }
 +                      gid_set = 1;
 +                      wpa_printf(MSG_DEBUG, "ctrl_interface_group=%d",
 +                                 (int) gid);
 +              }
 +      }
 +
 +      if (gid_set && chown(dir, -1, gid) < 0) {
 +              wpa_printf(MSG_ERROR, "chown[ctrl_interface=%s,gid=%d]: %s",
 +                         dir, (int) gid, strerror(errno));
 +              goto fail;
 +      }
 +
 +      /* Make sure the group can enter and read the directory */
 +      if (gid_set &&
 +          chmod(dir, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP) < 0) {
 +              wpa_printf(MSG_ERROR, "CTRL: chmod[ctrl_interface]: %s",
 +                         strerror(errno));
 +              goto fail;
 +      }
 +
 +      if (os_strlen(dir) + 1 + os_strlen(wpa_s->ifname) >=
 +          sizeof(addr.sun_path)) {
 +              wpa_printf(MSG_ERROR, "ctrl_iface path limit exceeded");
 +              goto fail;
 +      }
 +
 +      priv->sock = socket(PF_UNIX, SOCK_DGRAM, 0);
 +      if (priv->sock < 0) {
 +              wpa_printf(MSG_ERROR, "socket(PF_UNIX): %s", strerror(errno));
 +              goto fail;
 +      }
 +
 +      os_memset(&addr, 0, sizeof(addr));
 +#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
 +      addr.sun_len = sizeof(addr);
 +#endif /* __FreeBSD__ */
 +      addr.sun_family = AF_UNIX;
 +      fname = wpa_supplicant_ctrl_iface_path(wpa_s);
 +      if (fname == NULL)
 +              goto fail;
 +      os_strlcpy(addr.sun_path, fname, sizeof(addr.sun_path));
 +      if (bind(priv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
 +              wpa_printf(MSG_DEBUG, "ctrl_iface bind(PF_UNIX) failed: %s",
 +                         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(fname) < 0) {
 +                              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) {
 +                              wpa_printf(MSG_ERROR, "supp-ctrl-iface-init: bind(PF_UNIX): %s",
 +                                         strerror(errno));
 +                              goto fail;
 +                      }
 +                      wpa_printf(MSG_DEBUG, "Successfully replaced leftover "
 +                                 "ctrl_iface socket '%s'", fname);
 +              } else {
 +                      wpa_printf(MSG_INFO, "ctrl_iface exists and seems to "
 +                                 "be in use - cannot override it");
 +                      wpa_printf(MSG_INFO, "Delete '%s' manually if it is "
 +                                 "not used anymore", fname);
 +                      os_free(fname);
 +                      fname = NULL;
 +                      goto fail;
 +              }
 +      }
 +
 +      if (gid_set && chown(fname, -1, gid) < 0) {
 +              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) {
 +              wpa_printf(MSG_ERROR, "chmod[ctrl_interface=%s]: %s",
 +                         fname, strerror(errno));
 +              goto fail;
 +      }
 +      os_free(fname);
 +
 +#ifdef ANDROID
 +havesock:
 +#endif /* ANDROID */
 +
 +      /*
 +       * 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_ctrl_iface_receive,
 +                               wpa_s, priv);
 +      wpa_msg_register_cb(wpa_supplicant_ctrl_iface_msg_cb);
 +
 +      os_free(buf);
 +      return 0;
 +
 +fail:
 +      if (priv->sock >= 0) {
 +              close(priv->sock);
 +              priv->sock = -1;
 +      }
 +      if (fname) {
 +              unlink(fname);
 +              os_free(fname);
 +      }
 +      os_free(buf);
 +      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;
 +
++#ifdef ANDROID
++      if (wpa_s->global->params.ctrl_interface) {
++              int same = 0;
++
++              if (wpa_s->global->params.ctrl_interface[0] == '/') {
++                      if (os_strcmp(wpa_s->global->params.ctrl_interface,
++                                    wpa_s->conf->ctrl_interface) == 0)
++                              same = 1;
++              } else if (os_strncmp(wpa_s->global->params.ctrl_interface,
++                                    "@android:", 9) == 0 ||
++                         os_strncmp(wpa_s->global->params.ctrl_interface,
++                                    "@abstract:", 10) == 0) {
++                      char *pos;
++
++                      /*
++                       * Currently, Android uses @android:wpa_* as the naming
++                       * convention for the global ctrl interface. This logic
++                       * needs to be revisited if the above naming convention
++                       * is modified.
++                       */
++                      pos = os_strchr(wpa_s->global->params.ctrl_interface,
++                                      '_');
++                      if (pos &&
++                          os_strcmp(pos + 1,
++                                    wpa_s->conf->ctrl_interface) == 0)
++                              same = 1;
++              }
++
++              if (same) {
++                      /*
++                       * The invalid configuration combination might be
++                       * possible to hit in an Android OTA upgrade case, so
++                       * instead of refusing to start the wpa_supplicant
++                       * process, do not open the per-interface ctrl_iface
++                       * and continue with the global control interface that
++                       * was set from the command line since the Wi-Fi
++                       * framework will use it for operations.
++                       */
++                      wpa_printf(MSG_ERROR,
++                                 "global ctrl interface %s matches ctrl interface %s - do not open per-interface ctrl interface",
++                                 wpa_s->global->params.ctrl_interface,
++                                 wpa_s->conf->ctrl_interface);
++                      return priv;
++              }
++      }
++#endif /* ANDROID */
++
 +      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;
 +}
 +
 +
 +void wpa_supplicant_ctrl_iface_deinit(struct ctrl_iface_priv *priv)
 +{
 +      struct wpa_ctrl_dst *dst, *prev;
 +
 +      if (priv->sock > -1) {
 +              char *fname;
 +              char *buf, *dir = NULL;
 +              eloop_unregister_read_sock(priv->sock);
 +              if (!dl_list_empty(&priv->ctrl_dst)) {
 +                      /*
 +                       * 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(0, 100000);
 +              }
 +              close(priv->sock);
 +              priv->sock = -1;
 +              fname = wpa_supplicant_ctrl_iface_path(priv->wpa_s);
 +              if (fname) {
 +                      unlink(fname);
 +                      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)
 +                              *gid_str = '\0';
 +              } else
 +                      dir = buf;
 +
 +              if (rmdir(dir) < 0) {
 +                      if (errno == ENOTEMPTY) {
 +                              wpa_printf(MSG_DEBUG, "Control interface "
 +                                         "directory not empty - leaving it "
 +                                         "behind");
 +                      } else {
 +                              wpa_printf(MSG_ERROR,
 +                                         "rmdir[ctrl_interface=%s]: %s",
 +                                         dir, strerror(errno));
 +                      }
 +              }
 +              os_free(buf);
 +      }
 +
 +free_dst:
 +      dl_list_for_each_safe(dst, prev, &priv->ctrl_dst, struct wpa_ctrl_dst,
 +                            list)
 +              os_free(dst);
 +      os_free(priv);
 +}
 +
 +
 +/**
 + * wpa_supplicant_ctrl_iface_send - Send a control interface packet to monitors
 + * @ifname: Interface name for global control socket or %NULL
 + * @sock: Local socket fd
 + * @ctrl_dst: List of attached listeners
 + * @level: Priority level of the message
 + * @buf: Message data
 + * @len: Message length
 + *
 + * Send a packet to all monitor programs attached to the control interface.
 + */
 +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,
 +                                         struct ctrl_iface_priv *priv,
 +                                         struct ctrl_iface_global_priv *gp)
 +{
 +      struct wpa_ctrl_dst *dst, *next;
 +      char levelstr[10];
 +      int idx, res;
 +      struct msghdr msg;
 +      struct iovec io[5];
 +
 +      if (sock < 0 || dl_list_empty(ctrl_dst))
 +              return;
 +
 +      res = os_snprintf(levelstr, sizeof(levelstr), "<%d>", level);
 +      if (os_snprintf_error(sizeof(levelstr), res))
 +              return;
 +      idx = 0;
 +      if (ifname) {
 +              io[idx].iov_base = "IFNAME=";
 +              io[idx].iov_len = 7;
 +              idx++;
 +              io[idx].iov_base = (char *) ifname;
 +              io[idx].iov_len = os_strlen(ifname);
 +              idx++;
 +              io[idx].iov_base = " ";
 +              io[idx].iov_len = 1;
 +              idx++;
 +      }
 +      io[idx].iov_base = levelstr;
 +      io[idx].iov_len = os_strlen(levelstr);
 +      idx++;
 +      io[idx].iov_base = (char *) buf;
 +      io[idx].iov_len = len;
 +      idx++;
 +      os_memset(&msg, 0, sizeof(msg));
 +      msg.msg_iov = io;
 +      msg.msg_iovlen = idx;
 +
 +      dl_list_for_each_safe(dst, next, ctrl_dst, struct wpa_ctrl_dst, list) {
 +              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;
++              wpas_ctrl_sock_debug("ctrl_sock-sendmsg", sock, buf, len);
 +              if (sendmsg(sock, &msg, MSG_DONTWAIT) >= 0) {
++                      wpa_printf(MSG_MSGDUMP,
++                                 "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;
 +                      }
 +              }
 +      }
 +}
 +
 +
 +void wpa_supplicant_ctrl_iface_wait(struct ctrl_iface_priv *priv)
 +{
 +      char buf[256];
 +      int res;
 +      struct sockaddr_un from;
 +      socklen_t fromlen = sizeof(from);
 +
 +      for (;;) {
 +              wpa_printf(MSG_DEBUG, "CTRL_IFACE - %s - wait for monitor to "
 +                         "attach", priv->wpa_s->ifname);
 +              eloop_wait_for_read_sock(priv->sock);
 +
 +              res = recvfrom(priv->sock, buf, sizeof(buf) - 1, 0,
 +                             (struct sockaddr *) &from, &fromlen);
 +              if (res < 0) {
 +                      wpa_printf(MSG_ERROR, "recvfrom(ctrl_iface): %s",
 +                                 strerror(errno));
 +                      continue;
 +              }
 +              buf[res] = '\0';
 +
 +              if (os_strcmp(buf, "ATTACH") == 0) {
 +                      /* handle ATTACH signal of first monitor interface */
 +                      if (!wpa_supplicant_ctrl_iface_attach(&priv->ctrl_dst,
 +                                                            &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 {
 +                              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 */
 +                      if (sendto(priv->sock, "FAIL\n", 5, 0,
 +                                 (struct sockaddr *) &from, fromlen) < 0) {
 +                              wpa_printf(MSG_DEBUG,
 +                                         "ctrl_iface sendto failed: %s",
 +                                         strerror(errno));
 +                      }
 +              }
 +      }
 +}
 +
 +
 +/* Global ctrl_iface */
 +
 +static void wpa_supplicant_global_ctrl_iface_receive(int sock, void *eloop_ctx,
 +                                                   void *sock_ctx)
 +{
 +      struct wpa_global *global = eloop_ctx;
 +      struct ctrl_iface_global_priv *priv = sock_ctx;
 +      char buf[4096];
 +      int res;
 +      struct sockaddr_un from;
 +      socklen_t fromlen = sizeof(from);
 +      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) {
 +              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, 1))
 +                      reply_len = 1;
 +              else
 +                      reply_len = 2;
 +      } else if (os_strcmp(buf, "DETACH") == 0) {
 +              if (wpa_supplicant_ctrl_iface_detach(&priv->ctrl_dst, &from,
 +                                                   fromlen))
 +                      reply_len = 1;
 +              else
 +                      reply_len = 2;
 +      } else {
 +              reply_buf = wpa_supplicant_global_ctrl_iface_process(
 +                      global, buf, &reply_len);
 +              reply = reply_buf;
++
++              /*
++               * There could be some password/key material in the command, so
++               * clear the buffer explicitly now that it is not needed
++               * anymore.
++               */
++              os_memset(buf, 0, res);
 +      }
 +
 +      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) {
++              wpas_ctrl_sock_debug("global_ctrl_sock-sendto",
++                                   sock, reply, reply_len);
 +              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);
 +}
 +
 +
 +static int wpas_global_ctrl_iface_open_sock(struct wpa_global *global,
 +                                          struct ctrl_iface_global_priv *priv)
 +{
 +      struct sockaddr_un addr;
 +      const char *ctrl = global->params.ctrl_interface;
 +      int flags;
 +
 +      wpa_printf(MSG_DEBUG, "Global control interface '%s'", ctrl);
 +
 +#ifdef ANDROID
 +      if (os_strncmp(ctrl, "@android:", 9) == 0) {
 +              priv->sock = android_get_control_socket(ctrl + 9);
 +              if (priv->sock < 0) {
 +                      wpa_printf(MSG_ERROR, "Failed to open Android control "
 +                                 "socket '%s'", ctrl + 9);
 +                      goto fail;
 +              }
 +              wpa_printf(MSG_DEBUG, "Using Android control socket '%s'",
 +                         ctrl + 9);
 +              priv->android_control_socket = 1;
 +              goto havesock;
 +      }
 +
 +      if (os_strncmp(ctrl, "@abstract:", 10) != 0) {
 +              /*
 +               * Backwards compatibility - try to open an Android control
 +               * socket and if that fails, assume this was a UNIX domain
 +               * socket instead.
 +               */
 +              priv->sock = android_get_control_socket(ctrl);
 +              if (priv->sock >= 0) {
 +                      wpa_printf(MSG_DEBUG,
 +                                 "Using Android control socket '%s'",
 +                                 ctrl);
 +                      priv->android_control_socket = 1;
 +                      goto havesock;
 +              }
 +      }
 +#endif /* ANDROID */
 +
 +      priv->sock = socket(PF_UNIX, SOCK_DGRAM, 0);
 +      if (priv->sock < 0) {
 +              wpa_printf(MSG_ERROR, "socket(PF_UNIX): %s", strerror(errno));
 +              goto fail;
 +      }
 +
 +      os_memset(&addr, 0, sizeof(addr));
 +#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
 +      addr.sun_len = sizeof(addr);
 +#endif /* __FreeBSD__ */
 +      addr.sun_family = AF_UNIX;
 +
 +      if (os_strncmp(ctrl, "@abstract:", 10) == 0) {
 +              addr.sun_path[0] = '\0';
 +              os_strlcpy(addr.sun_path + 1, ctrl + 10,
 +                         sizeof(addr.sun_path) - 1);
 +              if (bind(priv->sock, (struct sockaddr *) &addr, sizeof(addr)) <
 +                  0) {
 +                      wpa_printf(MSG_ERROR, "supp-global-ctrl-iface-init: "
 +                                 "bind(PF_UNIX;%s) failed: %s",
 +                                 ctrl, strerror(errno));
 +                      goto fail;
 +              }
 +              wpa_printf(MSG_DEBUG, "Using Abstract control socket '%s'",
 +                         ctrl + 10);
 +              goto havesock;
 +      }
 +
 +      os_strlcpy(addr.sun_path, ctrl, sizeof(addr.sun_path));
 +      if (bind(priv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
 +              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) {
 +                              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) {
 +                              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 "
 +                                 "ctrl_iface socket '%s'",
 +                                 ctrl);
 +              } else {
 +                      wpa_printf(MSG_INFO, "ctrl_iface exists and seems to "
 +                                 "be in use - cannot override it");
 +                      wpa_printf(MSG_INFO, "Delete '%s' manually if it is "
 +                                 "not used anymore",
 +                                 ctrl);
 +                      goto fail;
 +              }
 +      }
 +
 +      wpa_printf(MSG_DEBUG, "Using UNIX control socket '%s'", ctrl);
 +
 +      if (global->params.ctrl_interface_group) {
 +              char *gid_str = global->params.ctrl_interface_group;
 +              gid_t gid = 0;
 +              struct group *grp;
 +              char *endp;
 +
 +              grp = getgrnam(gid_str);
 +              if (grp) {
 +                      gid = grp->gr_gid;
 +                      wpa_printf(MSG_DEBUG, "ctrl_interface_group=%d"
 +                                 " (from group name '%s')",
 +                                 (int) gid, gid_str);
 +              } else {
 +                      /* Group name not found - try to parse this as gid */
 +                      gid = strtol(gid_str, &endp, 10);
 +                      if (*gid_str == '\0' || *endp != '\0') {
 +                              wpa_printf(MSG_ERROR, "CTRL: Invalid group "
 +                                         "'%s'", gid_str);
 +                              goto fail;
 +                      }
 +                      wpa_printf(MSG_DEBUG, "ctrl_interface_group=%d",
 +                                 (int) gid);
 +              }
 +              if (chown(ctrl, -1, gid) < 0) {
 +                      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) {
 +                      wpa_printf(MSG_ERROR,
 +                                 "chmod[global_ctrl_interface=%s]: %s",
 +                                 ctrl, strerror(errno));
 +                      goto fail;
 +              }
 +      } else {
 +              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 0;
 +
 +fail:
 +      if (priv->sock >= 0) {
 +              close(priv->sock);
 +              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;
 +}
 +
 +
 +void
 +wpa_supplicant_global_ctrl_iface_deinit(struct ctrl_iface_global_priv *priv)
 +{
 +      struct wpa_ctrl_dst *dst, *prev;
 +
 +      if (priv->sock >= 0) {
 +              eloop_unregister_read_sock(priv->sock);
 +              close(priv->sock);
 +      }
 +      if (priv->global->params.ctrl_interface)
 +              unlink(priv->global->params.ctrl_interface);
 +      dl_list_for_each_safe(dst, prev, &priv->ctrl_dst, struct wpa_ctrl_dst,
 +                            list)
 +              os_free(dst);
 +      os_free(priv);
 +}
index 30ef03a7453b7a35a8e493af487f81002a1276f1,0000000000000000000000000000000000000000..67d0e2877a478ba5f683ca0c463b5fc9305dd931
mode 100644,000000..100644
--- /dev/null
@@@ -1,3825 -1,0 +1,4107 @@@
-       if (iface == NULL)
 +/*
 + * 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>
 + *
 + * 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/ieee802_11_defs.h"
 +#include "wps/wps.h"
 +#include "../config.h"
 +#include "../wpa_supplicant_i.h"
 +#include "../bss.h"
 +#include "../wpas_glue.h"
 +#include "dbus_new_helpers.h"
 +#include "dbus_dict_helpers.h"
 +#include "dbus_new.h"
 +#include "dbus_new_handlers.h"
 +#include "dbus_common_i.h"
 +#include "dbus_new_handlers_p2p.h"
 +#include "p2p/p2p.h"
 +#include "../p2p_supplicant.h"
 +
 +#ifdef CONFIG_AP /* until needed by something else */
 +
 +/*
 + * NameOwnerChanged handling
 + *
 + * Some services we provide allow an application to register for
 + * a signal that it needs. While it can also unregister, we must
 + * be prepared for the case where the application simply crashes
 + * and thus doesn't clean up properly. The way to handle this in
 + * DBus is to register for the NameOwnerChanged signal which will
 + * signal an owner change to NULL if the peer closes the socket
 + * for whatever reason.
 + *
 + * Handle this signal via a filter function whenever necessary.
 + * The code below also handles refcounting in case in the future
 + * there will be multiple instances of this subscription scheme.
 + */
 +static const char wpas_dbus_noc_filter_str[] =
 +      "interface=org.freedesktop.DBus,member=NameOwnerChanged";
 +
 +
 +static DBusHandlerResult noc_filter(DBusConnection *conn,
 +                                  DBusMessage *message, void *data)
 +{
 +      struct wpas_dbus_priv *priv = data;
 +
 +      if (dbus_message_get_type(message) != DBUS_MESSAGE_TYPE_SIGNAL)
 +              return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
 +
 +      if (dbus_message_is_signal(message, DBUS_INTERFACE_DBUS,
 +                                 "NameOwnerChanged")) {
 +              const char *name;
 +              const char *prev_owner;
 +              const char *new_owner;
 +              DBusError derr;
 +              struct wpa_supplicant *wpa_s;
 +
 +              dbus_error_init(&derr);
 +
 +              if (!dbus_message_get_args(message, &derr,
 +                                         DBUS_TYPE_STRING, &name,
 +                                         DBUS_TYPE_STRING, &prev_owner,
 +                                         DBUS_TYPE_STRING, &new_owner,
 +                                         DBUS_TYPE_INVALID)) {
 +                      /* Ignore this error */
 +                      dbus_error_free(&derr);
 +                      return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
 +              }
 +
 +              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)) {
 +                              /* probe request owner disconnected */
 +                              os_free(wpa_s->preq_notify_peer);
 +                              wpa_s->preq_notify_peer = NULL;
 +                              wpas_dbus_unsubscribe_noc(priv);
 +                      }
 +              }
 +      }
 +
 +      return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
 +}
 +
 +
 +void wpas_dbus_subscribe_noc(struct wpas_dbus_priv *priv)
 +{
 +      priv->dbus_noc_refcnt++;
 +      if (priv->dbus_noc_refcnt > 1)
 +              return;
 +
 +      if (!dbus_connection_add_filter(priv->con, noc_filter, priv, NULL)) {
 +              wpa_printf(MSG_ERROR, "dbus: failed to add filter");
 +              return;
 +      }
 +
 +      dbus_bus_add_match(priv->con, wpas_dbus_noc_filter_str, NULL);
 +}
 +
 +
 +void wpas_dbus_unsubscribe_noc(struct wpas_dbus_priv *priv)
 +{
 +      priv->dbus_noc_refcnt--;
 +      if (priv->dbus_noc_refcnt > 0)
 +              return;
 +
 +      dbus_bus_remove_match(priv->con, wpas_dbus_noc_filter_str, NULL);
 +      dbus_connection_remove_filter(priv->con, noc_filter, priv);
 +}
 +
 +#endif /* CONFIG_AP */
 +
 +
 +/**
 + * wpas_dbus_signal_interface - Send a interface related event signal
 + * @wpa_s: %wpa_supplicant network interface data
 + * @sig_name: signal name - InterfaceAdded or InterfaceRemoved
 + * @properties: Whether to add second argument with object properties
 + *
 + * Notify listeners about event related with interface
 + */
 +static void wpas_dbus_signal_interface(struct wpa_supplicant *wpa_s,
 +                                     const char *sig_name, int properties)
 +{
 +      struct wpas_dbus_priv *iface;
 +      DBusMessage *msg;
 +      DBusMessageIter iter;
 +
 +      iface = wpa_s->global->dbus;
 +
 +      /* Do nothing if the control interface is not turned on */
-       if (iface == NULL)
++      if (iface == NULL || !wpa_s->dbus_new_path)
 +              return;
 +
 +      msg = dbus_message_new_signal(WPAS_DBUS_NEW_PATH,
 +                                    WPAS_DBUS_NEW_INTERFACE, sig_name);
 +      if (msg == NULL)
 +              return;
 +
 +      dbus_message_iter_init_append(msg, &iter);
 +      if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH,
 +                                          &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);
 +}
 +
 +
 +/**
 + * wpas_dbus_signal_interface_added - Send a interface created signal
 + * @wpa_s: %wpa_supplicant network interface data
 + *
 + * Notify listeners about creating new interface
 + */
 +static void wpas_dbus_signal_interface_added(struct wpa_supplicant *wpa_s)
 +{
 +      wpas_dbus_signal_interface(wpa_s, "InterfaceAdded", TRUE);
 +}
 +
 +
 +/**
 + * wpas_dbus_signal_interface_removed - Send a interface removed signal
 + * @wpa_s: %wpa_supplicant network interface data
 + *
 + * Notify listeners about removing interface
 + */
 +static void wpas_dbus_signal_interface_removed(struct wpa_supplicant *wpa_s)
 +{
 +      wpas_dbus_signal_interface(wpa_s, "InterfaceRemoved", FALSE);
 +
 +}
 +
 +
 +/**
 + * wpas_dbus_signal_scan_done - send scan done signal
 + * @wpa_s: %wpa_supplicant network interface data
 + * @success: indicates if scanning succeed or failed
 + *
 + * Notify listeners about finishing a scan
 + */
 +void wpas_dbus_signal_scan_done(struct wpa_supplicant *wpa_s, int success)
 +{
 +      struct wpas_dbus_priv *iface;
 +      DBusMessage *msg;
 +      dbus_bool_t succ;
 +
 +      iface = wpa_s->global->dbus;
 +
 +      /* Do nothing if the control interface is not turned on */
-       if (iface == NULL)
++      if (iface == NULL || !wpa_s->dbus_new_path)
 +              return;
 +
 +      msg = dbus_message_new_signal(wpa_s->dbus_new_path,
 +                                    WPAS_DBUS_NEW_IFACE_INTERFACE,
 +                                    "ScanDone");
 +      if (msg == NULL)
 +              return;
 +
 +      succ = success ? TRUE : FALSE;
 +      if (dbus_message_append_args(msg, DBUS_TYPE_BOOLEAN, &succ,
 +                                   DBUS_TYPE_INVALID))
 +              dbus_connection_send(iface->con, msg, NULL);
 +      else
 +              wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
 +      dbus_message_unref(msg);
 +}
 +
 +
 +/**
 + * 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
 + * @properties: Whether to add second argument with object properties
 + *
 + * Notify listeners about event related with BSS
 + */
 +static void wpas_dbus_signal_bss(struct wpa_supplicant *wpa_s,
 +                               const char *bss_obj_path,
 +                               const char *sig_name, int properties)
 +{
 +      struct wpas_dbus_priv *iface;
 +      DBusMessage *msg;
 +      DBusMessageIter iter;
 +
 +      iface = wpa_s->global->dbus;
 +
 +      /* Do nothing if the control interface is not turned on */
-       if (iface == NULL)
++      if (iface == NULL || !wpa_s->dbus_new_path)
 +              return;
 +
 +      msg = dbus_message_new_signal(wpa_s->dbus_new_path,
 +                                    WPAS_DBUS_NEW_IFACE_INTERFACE,
 +                                    sig_name);
 +      if (msg == NULL)
 +              return;
 +
 +      dbus_message_iter_init_append(msg, &iter);
 +      if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH,
 +                                          &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);
 +}
 +
 +
 +/**
 + * wpas_dbus_signal_bss_added - Send a BSS added signal
 + * @wpa_s: %wpa_supplicant network interface data
 + * @bss_obj_path: new BSS object path
 + *
 + * Notify listeners about adding new BSS
 + */
 +static void wpas_dbus_signal_bss_added(struct wpa_supplicant *wpa_s,
 +                                     const char *bss_obj_path)
 +{
 +      wpas_dbus_signal_bss(wpa_s, bss_obj_path, "BSSAdded", TRUE);
 +}
 +
 +
 +/**
 + * wpas_dbus_signal_bss_removed - Send a BSS removed signal
 + * @wpa_s: %wpa_supplicant network interface data
 + * @bss_obj_path: BSS object path
 + *
 + * Notify listeners about removing BSS
 + */
 +static void wpas_dbus_signal_bss_removed(struct wpa_supplicant *wpa_s,
 +                                       const char *bss_obj_path)
 +{
 +      wpas_dbus_signal_bss(wpa_s, bss_obj_path, "BSSRemoved", FALSE);
 +}
 +
 +
 +/**
 + * wpas_dbus_signal_blob - Send a blob related event signal
 + * @wpa_s: %wpa_supplicant network interface data
 + * @name: blob name
 + * @sig_name: signal name - BlobAdded or BlobRemoved
 + *
 + * Notify listeners about event related with blob
 + */
 +static void wpas_dbus_signal_blob(struct wpa_supplicant *wpa_s,
 +                                const char *name, const char *sig_name)
 +{
 +      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)
++      if (iface == NULL || !wpa_s->dbus_new_path)
 +              return;
 +
 +      msg = dbus_message_new_signal(wpa_s->dbus_new_path,
 +                                    WPAS_DBUS_NEW_IFACE_INTERFACE,
 +                                    sig_name);
 +      if (msg == NULL)
 +              return;
 +
 +      if (dbus_message_append_args(msg, DBUS_TYPE_STRING, &name,
 +                                   DBUS_TYPE_INVALID))
 +              dbus_connection_send(iface->con, msg, NULL);
 +      else
 +              wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
 +      dbus_message_unref(msg);
 +}
 +
 +
 +/**
 + * wpas_dbus_signal_blob_added - Send a blob added signal
 + * @wpa_s: %wpa_supplicant network interface data
 + * @name: blob name
 + *
 + * Notify listeners about adding a new blob
 + */
 +void wpas_dbus_signal_blob_added(struct wpa_supplicant *wpa_s,
 +                               const char *name)
 +{
 +      wpas_dbus_signal_blob(wpa_s, name, "BlobAdded");
 +}
 +
 +
 +/**
 + * wpas_dbus_signal_blob_removed - Send a blob removed signal
 + * @wpa_s: %wpa_supplicant network interface data
 + * @name: blob name
 + *
 + * Notify listeners about removing blob
 + */
 +void wpas_dbus_signal_blob_removed(struct wpa_supplicant *wpa_s,
 +                                 const char *name)
 +{
 +      wpas_dbus_signal_blob(wpa_s, name, "BlobRemoved");
 +}
 +
 +
 +/**
 + * wpas_dbus_signal_network - Send a network related event signal
 + * @wpa_s: %wpa_supplicant network interface data
 + * @id: new network id
 + * @sig_name: signal name - NetworkAdded, NetworkRemoved or NetworkSelected
 + * @properties: determines if add second argument with object properties
 + *
 + * Notify listeners about event related with configured network
 + */
 +static void wpas_dbus_signal_network(struct wpa_supplicant *wpa_s,
 +                                   int id, const char *sig_name,
 +                                   int properties)
 +{
 +      struct wpas_dbus_priv *iface;
 +      DBusMessage *msg;
 +      DBusMessageIter iter;
 +      char net_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)
++      if (iface == NULL || !wpa_s->dbus_new_path)
 +              return;
 +
 +      os_snprintf(net_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
 +                  "%s/" WPAS_DBUS_NEW_NETWORKS_PART "/%u",
 +                  wpa_s->dbus_new_path, id);
 +
 +      msg = dbus_message_new_signal(wpa_s->dbus_new_path,
 +                                    WPAS_DBUS_NEW_IFACE_INTERFACE,
 +                                    sig_name);
 +      if (msg == NULL)
 +              return;
 +
 +      dbus_message_iter_init_append(msg, &iter);
 +      path = net_obj_path;
 +      if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH,
 +                                          &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);
 +}
 +
 +
 +/**
 + * wpas_dbus_signal_network_added - Send a network added signal
 + * @wpa_s: %wpa_supplicant network interface data
 + * @id: new network id
 + *
 + * Notify listeners about adding new network
 + */
 +static void wpas_dbus_signal_network_added(struct wpa_supplicant *wpa_s,
 +                                         int id)
 +{
 +      wpas_dbus_signal_network(wpa_s, id, "NetworkAdded", TRUE);
 +}
 +
 +
 +/**
 + * wpas_dbus_signal_network_removed - Send a network removed signal
 + * @wpa_s: %wpa_supplicant network interface data
 + * @id: network id
 + *
 + * Notify listeners about removing a network
 + */
 +static void wpas_dbus_signal_network_removed(struct wpa_supplicant *wpa_s,
 +                                           int id)
 +{
 +      wpas_dbus_signal_network(wpa_s, id, "NetworkRemoved", FALSE);
 +}
 +
 +
 +/**
 + * wpas_dbus_signal_network_selected - Send a network selected signal
 + * @wpa_s: %wpa_supplicant network interface data
 + * @id: network id
 + *
 + * Notify listeners about selecting a network
 + */
 +void wpas_dbus_signal_network_selected(struct wpa_supplicant *wpa_s, int id)
 +{
 +      wpas_dbus_signal_network(wpa_s, id, "NetworkSelected", FALSE);
 +}
 +
 +
 +/**
 + * wpas_dbus_signal_network_request - Indicate that additional information
 + * (EAP password, etc.) is required to complete the association to this SSID
 + * @wpa_s: %wpa_supplicant network interface data
 + * @rtype: The specific additional information required
 + * @default_text: Optional description of required information
 + *
 + * Request additional information or passwords to complete an association
 + * request.
 + */
 +void wpas_dbus_signal_network_request(struct wpa_supplicant *wpa_s,
 +                                    struct wpa_ssid *ssid,
 +                                    enum wpa_ctrl_req_type rtype,
 +                                    const char *default_txt)
 +{
 +      struct wpas_dbus_priv *iface;
 +      DBusMessage *msg;
 +      DBusMessageIter iter;
 +      char net_obj_path[WPAS_DBUS_OBJECT_PATH_MAX];
 +      const char *field, *txt = NULL, *net_ptr;
 +
 +      iface = wpa_s->global->dbus;
 +
 +      /* Do nothing if the control interface is not turned on */
-       if (iface == NULL)
++      if (iface == NULL || !wpa_s->dbus_new_path)
 +              return;
 +
 +      field = wpa_supplicant_ctrl_req_to_string(rtype, default_txt, &txt);
 +      if (field == NULL)
 +              return;
 +
 +      msg = dbus_message_new_signal(wpa_s->dbus_new_path,
 +                                    WPAS_DBUS_NEW_IFACE_INTERFACE,
 +                                    "NetworkRequest");
 +      if (msg == NULL)
 +              return;
 +
 +      os_snprintf(net_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
 +                  "%s/" WPAS_DBUS_NEW_NETWORKS_PART "/%u",
 +                  wpa_s->dbus_new_path, ssid->id);
 +      net_ptr = &net_obj_path[0];
 +
 +      dbus_message_iter_init_append(msg, &iter);
 +      if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH,
 +                                          &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);
 +}
 +
 +
 +/**
 + * wpas_dbus_signal_network_enabled_changed - Signals Enabled property changes
 + * @wpa_s: %wpa_supplicant network interface data
 + * @ssid: configured network which Enabled property has changed
 + *
 + * Sends PropertyChanged signals containing new value of Enabled property
 + * for specified network
 + */
 +void wpas_dbus_signal_network_enabled_changed(struct wpa_supplicant *wpa_s,
 +                                            struct wpa_ssid *ssid)
 +{
 +
 +      char path[WPAS_DBUS_OBJECT_PATH_MAX];
 +
++      if (!wpa_s->dbus_new_path)
++              return;
 +      os_snprintf(path, WPAS_DBUS_OBJECT_PATH_MAX,
 +                  "%s/" WPAS_DBUS_NEW_NETWORKS_PART "/%d",
 +                  wpa_s->dbus_new_path, ssid->id);
 +
 +      wpa_dbus_mark_property_changed(wpa_s->global->dbus, path,
 +                                     WPAS_DBUS_NEW_IFACE_NETWORK, "Enabled");
 +}
 +
 +
 +#ifdef CONFIG_WPS
 +
++/**
++ * wpas_dbus_signal_wps_event_pbc_overlap - Signals PBC overlap WPS event
++ * @wpa_s: %wpa_supplicant network interface data
++ *
++ * Sends Event dbus signal with name "pbc-overlap" and empty dict as arguments
++ */
++void wpas_dbus_signal_wps_event_pbc_overlap(struct wpa_supplicant *wpa_s)
++{
++
++      DBusMessage *msg;
++      DBusMessageIter iter, dict_iter;
++      struct wpas_dbus_priv *iface;
++      char *key = "pbc-overlap";
++
++      iface = wpa_s->global->dbus;
++
++      /* Do nothing if the control interface is not turned on */
++      if (iface == NULL || !wpa_s->dbus_new_path)
++              return;
++
++      msg = dbus_message_new_signal(wpa_s->dbus_new_path,
++                                    WPAS_DBUS_NEW_IFACE_WPS, "Event");
++      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_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_wps_event_success - Signals Success WPS event
 + * @wpa_s: %wpa_supplicant network interface data
 + *
 + * Sends Event dbus signal with name "success" and empty dict as arguments
 + */
 +void wpas_dbus_signal_wps_event_success(struct wpa_supplicant *wpa_s)
 +{
 +
 +      DBusMessage *msg;
 +      DBusMessageIter iter, dict_iter;
 +      struct wpas_dbus_priv *iface;
 +      char *key = "success";
 +
 +      iface = wpa_s->global->dbus;
 +
 +      /* Do nothing if the control interface is not turned on */
-       if (iface == NULL)
++      if (iface == NULL || !wpa_s->dbus_new_path)
 +              return;
 +
 +      msg = dbus_message_new_signal(wpa_s->dbus_new_path,
 +                                    WPAS_DBUS_NEW_IFACE_WPS, "Event");
 +      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_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_wps_event_fail - Signals Fail WPS event
 + * @wpa_s: %wpa_supplicant network interface data
++ * @fail: WPS failure information
 + *
 + * Sends Event dbus signal with name "fail" and dictionary containing
 + * "msg field with fail message number (int32) as arguments
 + */
 +void wpas_dbus_signal_wps_event_fail(struct wpa_supplicant *wpa_s,
 +                                   struct wps_event_fail *fail)
 +{
 +
 +      DBusMessage *msg;
 +      DBusMessageIter iter, dict_iter;
 +      struct wpas_dbus_priv *iface;
 +      char *key = "fail";
 +
 +      iface = wpa_s->global->dbus;
 +
 +      /* Do nothing if the control interface is not turned on */
-       if (iface == NULL)
++      if (iface == NULL || !wpa_s->dbus_new_path)
 +              return;
 +
 +      msg = dbus_message_new_signal(wpa_s->dbus_new_path,
 +                                    WPAS_DBUS_NEW_IFACE_WPS, "Event");
 +      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_int32(&dict_iter, "config_error",
++                                      fail->config_error) ||
++          !wpa_dbus_dict_append_int32(&dict_iter, "error_indication",
++                                      fail->error_indication) ||
 +          !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_wps_event_m2d - Signals M2D WPS event
 + * @wpa_s: %wpa_supplicant network interface data
++ * @m2d: M2D event data information
 + *
 + * Sends Event dbus signal with name "m2d" and dictionary containing
 + * fields of wps_event_m2d structure.
 + */
 +void wpas_dbus_signal_wps_event_m2d(struct wpa_supplicant *wpa_s,
 +                                  struct wps_event_m2d *m2d)
 +{
 +
 +      DBusMessage *msg;
 +      DBusMessageIter iter, dict_iter;
 +      struct wpas_dbus_priv *iface;
 +      char *key = "m2d";
 +
 +      iface = wpa_s->global->dbus;
 +
 +      /* Do nothing if the control interface is not turned on */
-       if (iface == NULL)
++      if (iface == NULL || !wpa_s->dbus_new_path)
 +              return;
 +
 +      msg = dbus_message_new_signal(wpa_s->dbus_new_path,
 +                                    WPAS_DBUS_NEW_IFACE_WPS, "Event");
 +      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_uint16(&dict_iter, "config_methods",
 +                                       m2d->config_methods) ||
 +          !wpa_dbus_dict_append_byte_array(&dict_iter, "manufacturer",
 +                                           (const char *) m2d->manufacturer,
 +                                           m2d->manufacturer_len) ||
 +          !wpa_dbus_dict_append_byte_array(&dict_iter, "model_name",
 +                                           (const char *) m2d->model_name,
 +                                           m2d->model_name_len) ||
 +          !wpa_dbus_dict_append_byte_array(&dict_iter, "model_number",
 +                                           (const char *) m2d->model_number,
 +                                           m2d->model_number_len) ||
 +          !wpa_dbus_dict_append_byte_array(&dict_iter, "serial_number",
 +                                           (const char *)
 +                                           m2d->serial_number,
 +                                           m2d->serial_number_len) ||
 +          !wpa_dbus_dict_append_byte_array(&dict_iter, "dev_name",
 +                                           (const char *) m2d->dev_name,
 +                                           m2d->dev_name_len) ||
 +          !wpa_dbus_dict_append_byte_array(&dict_iter, "primary_dev_type",
 +                                           (const char *)
 +                                           m2d->primary_dev_type, 8) ||
 +          !wpa_dbus_dict_append_uint16(&dict_iter, "config_error",
 +                                       m2d->config_error) ||
 +          !wpa_dbus_dict_append_uint16(&dict_iter, "dev_password_id",
 +                                       m2d->dev_password_id) ||
 +          !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_wps_cred - Signals new credentials
 + * @wpa_s: %wpa_supplicant network interface data
++ * @cred: WPS Credential information
 + *
 + * Sends signal with credentials in directory argument
 + */
 +void wpas_dbus_signal_wps_cred(struct wpa_supplicant *wpa_s,
 +                             const struct wps_credential *cred)
 +{
 +      DBusMessage *msg;
 +      DBusMessageIter iter, dict_iter;
 +      struct wpas_dbus_priv *iface;
 +      char *auth_type[5]; /* we have five possible authentication types */
 +      int at_num = 0;
 +      char *encr_type[3]; /* we have three possible encryption types */
 +      int et_num = 0;
 +
 +      iface = wpa_s->global->dbus;
 +
 +      /* Do nothing if the control interface is not turned on */
-       if (iface == NULL)
++      if (iface == NULL || !wpa_s->dbus_new_path)
 +              return;
 +
 +      msg = dbus_message_new_signal(wpa_s->dbus_new_path,
 +                                    WPAS_DBUS_NEW_IFACE_WPS,
 +                                    "Credentials");
 +      if (msg == NULL)
 +              return;
 +
 +      dbus_message_iter_init_append(msg, &iter);
 +      if (!wpa_dbus_dict_open_write(&iter, &dict_iter))
 +              goto nomem;
 +
 +      if (cred->auth_type & WPS_AUTH_OPEN)
 +              auth_type[at_num++] = "open";
 +      if (cred->auth_type & WPS_AUTH_WPAPSK)
 +              auth_type[at_num++] = "wpa-psk";
 +      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";
 +
 +      if (cred->encr_type & WPS_ENCR_NONE)
 +              encr_type[et_num++] = "none";
 +      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 &&
 +           !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",
 +                                             (const char **) auth_type,
 +                                             at_num) ||
 +          !wpa_dbus_dict_append_string_array(&dict_iter, "EncrType",
 +                                             (const char **) encr_type,
 +                                             et_num) ||
 +          !wpa_dbus_dict_append_byte_array(&dict_iter, "Key",
 +                                           (const char *) cred->key,
 +                                           cred->key_len) ||
 +          !wpa_dbus_dict_append_uint32(&dict_iter, "KeyIndex",
 +                                       cred->key_idx) ||
 +          !wpa_dbus_dict_close_write(&iter, &dict_iter))
 +              goto nomem;
 +
 +      dbus_connection_send(iface->con, msg, NULL);
 +
 +nomem:
 +      dbus_message_unref(msg);
 +}
 +
 +#endif /* CONFIG_WPS */
 +
 +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)
 +{
 +      struct wpas_dbus_priv *iface;
 +      DBusMessage *msg;
 +      DBusMessageIter iter, dict_iter;
 +
 +      iface = wpa_s->global->dbus;
 +
 +      /* Do nothing if the control interface is not turned on */
-       if (iface == NULL)
++      if (iface == NULL || !wpa_s->dbus_new_path)
 +              return;
 +
 +      msg = dbus_message_new_signal(wpa_s->dbus_new_path,
 +                                    WPAS_DBUS_NEW_IFACE_INTERFACE,
 +                                    "Certification");
 +      if (msg == NULL)
 +              return;
 +
 +      dbus_message_iter_init_append(msg, &iter);
 +      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);
 +}
 +
 +
 +void wpas_dbus_signal_eap_status(struct wpa_supplicant *wpa_s,
 +                               const char *status, const char *parameter)
 +{
 +      struct wpas_dbus_priv *iface;
 +      DBusMessage *msg;
 +      DBusMessageIter iter;
 +
 +      iface = wpa_s->global->dbus;
 +
 +      /* Do nothing if the control interface is not turned on */
-       if (iface == NULL)
++      if (iface == NULL || !wpa_s->dbus_new_path)
 +              return;
 +
 +      msg = dbus_message_new_signal(wpa_s->dbus_new_path,
 +                                    WPAS_DBUS_NEW_IFACE_INTERFACE,
 +                                    "EAP");
 +      if (msg == NULL)
 +              return;
 +
 +      dbus_message_iter_init_append(msg, &iter);
 +
 +      if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &status) ||
 +          !dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING,
 +                                          &parameter))
 +              wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
 +      else
 +              dbus_connection_send(iface->con, msg, NULL);
 +      dbus_message_unref(msg);
 +}
 +
 +
 +/**
 + * wpas_dbus_signal_sta - Send a station related event signal
 + * @wpa_s: %wpa_supplicant network interface data
 + * @sta: station mac address
 + * @sig_name: signal name - StaAuthorized or StaDeauthorized
 + *
 + * Notify listeners about event related with station
 + */
 +static void wpas_dbus_signal_sta(struct wpa_supplicant *wpa_s,
 +                               const u8 *sta, const char *sig_name)
 +{
 +      struct wpas_dbus_priv *iface;
 +      DBusMessage *msg;
 +      char sta_mac[WPAS_DBUS_OBJECT_PATH_MAX];
 +      char *dev_mac;
 +
 +      os_snprintf(sta_mac, WPAS_DBUS_OBJECT_PATH_MAX, MACSTR, MAC2STR(sta));
 +      dev_mac = sta_mac;
 +
 +      iface = wpa_s->global->dbus;
 +
 +      /* Do nothing if the control interface is not turned on */
-       if (!wpa_s->dbus_groupobj_path)
++      if (iface == NULL || !wpa_s->dbus_new_path)
 +              return;
 +
 +      msg = dbus_message_new_signal(wpa_s->dbus_new_path,
 +                                    WPAS_DBUS_NEW_IFACE_INTERFACE, sig_name);
 +      if (msg == NULL)
 +              return;
 +
 +      if (dbus_message_append_args(msg, DBUS_TYPE_STRING, &dev_mac,
 +                                   DBUS_TYPE_INVALID))
 +              dbus_connection_send(iface->con, msg, NULL);
 +      else
 +              wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
 +      dbus_message_unref(msg);
 +
 +      wpa_printf(MSG_DEBUG, "dbus: Station MAC address '%s' '%s'",
 +                 sta_mac, sig_name);
 +}
 +
 +
 +/**
 + * wpas_dbus_signal_sta_authorized - Send a STA authorized signal
 + * @wpa_s: %wpa_supplicant network interface data
 + * @sta: station mac address
 + *
 + * Notify listeners a new station has been authorized
 + */
 +void wpas_dbus_signal_sta_authorized(struct wpa_supplicant *wpa_s,
 +                                   const u8 *sta)
 +{
 +      wpas_dbus_signal_sta(wpa_s, sta, "StaAuthorized");
 +}
 +
 +
 +/**
 + * wpas_dbus_signal_sta_deauthorized - Send a STA deauthorized signal
 + * @wpa_s: %wpa_supplicant network interface data
 + * @sta: station mac address
 + *
 + * Notify listeners a station has been deauthorized
 + */
 +void wpas_dbus_signal_sta_deauthorized(struct wpa_supplicant *wpa_s,
 +                                     const u8 *sta)
 +{
 +      wpas_dbus_signal_sta(wpa_s, sta, "StaDeauthorized");
 +}
 +
 +
 +#ifdef CONFIG_P2P
 +
 +/**
 + * wpas_dbus_signal_p2p_group_removed - Signals P2P group was removed
 + * @wpa_s: %wpa_supplicant network interface data
 + * @role: role of this device (client or GO)
 + * Sends signal with i/f name and role as string arguments
 + */
 +void wpas_dbus_signal_p2p_group_removed(struct wpa_supplicant *wpa_s,
 +                                      const char *role)
 +{
 +      DBusMessage *msg;
 +      DBusMessageIter iter, dict_iter;
 +      struct wpas_dbus_priv *iface = wpa_s->global->dbus;
 +      struct wpa_supplicant *parent;
 +
 +      /* Do nothing if the control interface is not turned on */
 +      if (iface == NULL)
 +              return;
 +
 +      parent = wpa_s->parent;
 +      if (parent->p2p_mgmt)
 +              parent = parent->parent;
 +
-                                    const u8 *src, u16 dev_passwd_id)
++      if (!wpa_s->dbus_groupobj_path || !wpa_s->dbus_new_path ||
++          !parent->dbus_new_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 (!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);
 +      dbus_message_unref(msg);
 +}
 +
 +
 +/**
 + * wpas_dbus_signal_p2p_provision_discovery - Signals various PD events
 + *
 + * @dev_addr - who sent the request or responded to our request.
 + * @request - Will be 1 if request, 0 for response.
 + * @status - valid only in case of response
 + * @config_methods - wps config methods
 + * @generated_pin - pin to be displayed in case of WPS_CONFIG_DISPLAY method
 + *
 + * Sends following provision discovery related events:
 + *    ProvisionDiscoveryRequestDisplayPin
 + *    ProvisionDiscoveryResponseDisplayPin
 + *    ProvisionDiscoveryRequestEnterPin
 + *    ProvisionDiscoveryResponseEnterPin
 + *    ProvisionDiscoveryPBCRequest
 + *    ProvisionDiscoveryPBCResponse
 + *
 + *    TODO::
 + *    ProvisionDiscoveryFailure (timeout case)
 + */
 +void wpas_dbus_signal_p2p_provision_discovery(struct wpa_supplicant *wpa_s,
 +                                            const u8 *dev_addr, int request,
 +                                            enum p2p_prov_disc_status status,
 +                                            u16 config_methods,
 +                                            unsigned int generated_pin)
 +{
 +      DBusMessage *msg;
 +      DBusMessageIter iter;
 +      struct wpas_dbus_priv *iface;
 +      char *_signal;
 +      int add_pin = 0;
 +      char peer_obj_path[WPAS_DBUS_OBJECT_PATH_MAX], *path;
 +      int error_ret = 1;
 +      char pin[9], *p_pin = NULL;
 +
 +      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;
++      if (!wpa_s->dbus_new_path)
++              return;
 +
 +      if (request || !status) {
 +              if (config_methods & WPS_CONFIG_DISPLAY)
 +                      _signal = request ?
 +                               "ProvisionDiscoveryRequestDisplayPin" :
 +                               "ProvisionDiscoveryResponseEnterPin";
 +              else if (config_methods & WPS_CONFIG_KEYPAD)
 +                      _signal = request ?
 +                               "ProvisionDiscoveryRequestEnterPin" :
 +                               "ProvisionDiscoveryResponseDisplayPin";
 +              else if (config_methods & WPS_CONFIG_PUSHBUTTON)
 +                      _signal = request ? "ProvisionDiscoveryPBCRequest" :
 +                                 "ProvisionDiscoveryPBCResponse";
 +              else
 +                      return; /* Unknown or un-supported method */
 +      } else {
 +              /* Explicit check for failure response */
 +              _signal = "ProvisionDiscoveryFailure";
 +      }
 +
 +      add_pin = ((request && (config_methods & WPS_CONFIG_DISPLAY)) ||
 +                 (!request && !status &&
 +                      (config_methods & WPS_CONFIG_KEYPAD)));
 +
 +      if (add_pin) {
 +              os_snprintf(pin, sizeof(pin), "%08d", generated_pin);
 +              p_pin = pin;
 +      }
 +
 +      msg = dbus_message_new_signal(wpa_s->dbus_new_path,
 +                                    WPAS_DBUS_NEW_IFACE_P2PDEVICE, _signal);
 +      if (msg == NULL)
 +              return;
 +
 +      /* Check if this is a known peer */
 +      if (!p2p_peer_known(wpa_s->global->p2p, dev_addr))
 +              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(dev_addr));
 +
 +      path = peer_obj_path;
 +
 +      dbus_message_iter_init_append(msg, &iter);
 +
 +      if (!dbus_message_iter_append_basic(&iter,
 +                                          DBUS_TYPE_OBJECT_PATH,
 +                                          &path))
 +                      goto error;
 +
 +      if (!request && status)
 +              /* Attach status to ProvisionDiscoveryFailure */
 +              error_ret = !dbus_message_iter_append_basic(&iter,
 +                                                  DBUS_TYPE_INT32,
 +                                                  &status);
 +      else
 +              error_ret = (add_pin &&
 +                               !dbus_message_iter_append_basic(&iter,
 +                                                      DBUS_TYPE_STRING,
 +                                                      &p_pin));
 +
 +error:
 +      if (!error_ret)
 +              dbus_connection_send(iface->con, msg, NULL);
 +      else
 +              wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
 +
 +      dbus_message_unref(msg);
 +}
 +
 +
++/**
++ * wpas_dbus_signal_p2p_go_neg_req - Signal P2P GO Negotiation Request RX
++ * @wpa_s: %wpa_supplicant network interface data
++ * @src: Source address of the message triggering this notification
++ * @dev_passwd_id: WPS Device Password Id
++ * @go_intent: Peer's GO Intent value
++ *
++ * Sends signal to notify that a peer P2P Device is requesting group owner
++ * negotiation with us.
++ */
 +void wpas_dbus_signal_p2p_go_neg_req(struct wpa_supplicant *wpa_s,
-                                           &dev_passwd_id))
++                                   const u8 *src, u16 dev_passwd_id,
++                                   u8 go_intent)
 +{
 +      DBusMessage *msg;
 +      DBusMessageIter 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;
++      if (!wpa_s->dbus_new_path)
++              return;
 +
 +      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));
 +      path = peer_obj_path;
 +
 +      msg = dbus_message_new_signal(wpa_s->dbus_new_path,
 +                                    WPAS_DBUS_NEW_IFACE_P2PDEVICE,
 +                                    "GONegotiationRequest");
 +      if (msg == NULL)
 +              return;
 +
 +      dbus_message_iter_init_append(msg, &iter);
 +
 +      if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH,
 +                                          &path) ||
 +          !dbus_message_iter_append_basic(&iter, DBUS_TYPE_UINT16,
-       if (os_memcmp(ssid->ssid, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN))
++                                          &dev_passwd_id) ||
++          !dbus_message_iter_append_basic(&iter, DBUS_TYPE_BYTE,
++                                          &go_intent))
 +              wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
 +      else
 +              dbus_connection_send(iface->con, msg, NULL);
 +
 +      dbus_message_unref(msg);
 +}
 +
 +
 +static int wpas_dbus_get_group_obj_path(struct wpa_supplicant *wpa_s,
 +                                      const struct wpa_ssid *ssid,
 +                                      char *group_obj_path)
 +{
 +      char group_name[3];
 +
-       if (iface == NULL)
++      if (!wpa_s->dbus_new_path ||
++          os_memcmp(ssid->ssid, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN))
 +              return -1;
 +
 +      os_memcpy(group_name, ssid->ssid + P2P_WILDCARD_SSID_LEN, 2);
 +      group_name[2] = '\0';
 +
 +      os_snprintf(group_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
 +                  "%s/" WPAS_DBUS_NEW_P2P_GROUPS_PART "/%s",
 +                  wpa_s->dbus_new_path, group_name);
 +
 +      return 0;
 +}
 +
 +
 +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
 + * irrespective of the role (client/GO) of the current device
 + *
 + * @wpa_s: %wpa_supplicant network interface data
 + * @ssid: SSID object
 + * @client: this device is P2P client
 + * @network_id: network id of the group started, use instead of ssid->id
 + *    to account for persistent groups
 + */
 +void wpas_dbus_signal_p2p_group_started(struct wpa_supplicant *wpa_s,
 +                                      const struct wpa_ssid *ssid,
 +                                      int client, int network_id)
 +{
 +      DBusMessage *msg;
 +      DBusMessageIter iter, dict_iter;
 +      struct wpas_dbus_priv *iface;
 +      struct wpa_supplicant *parent;
 +
 +      parent = wpa_s->parent;
 +      if (parent->p2p_mgmt)
 +              parent = parent->parent;
 +
 +      iface = parent->global->dbus;
 +
 +      /* Do nothing if the control interface is not turned on */
-  *
-  * Method to emit GONegotiation Success or Failure signals based
-  * on status.
-  * @status: Status of the GO neg request. 0 for success, other for errors.
++      if (iface == NULL || !parent->dbus_new_path || !wpa_s->dbus_new_path)
 +              return;
 +
 +      if (wpa_s->dbus_groupobj_path == NULL)
 +              return;
 +
 +      /* New interface has been created for this group */
 +      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);
 +      /*
 +       * 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_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) ||
 +          !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);
 +      }
 +      dbus_message_unref(msg);
 +}
 +
 +
 +/**
-       if (iface == NULL)
++ * wpas_dbus_signal_p2p_go_neg_resp - Emit GONegotiation Success/Failure signal
++ * @wpa_s: %wpa_supplicant network interface data
++ * @res: Result of the GO Neg Request
 + */
 +void wpas_dbus_signal_p2p_go_neg_resp(struct wpa_supplicant *wpa_s,
 +                                    struct p2p_go_neg_results *res)
 +{
 +      DBusMessage *msg;
 +      DBusMessageIter iter, dict_iter;
 +      DBusMessageIter iter_dict_entry, iter_dict_val, iter_dict_array;
 +      struct wpas_dbus_priv *iface;
 +      char peer_obj_path[WPAS_DBUS_OBJECT_PATH_MAX], *path;
 +      dbus_int32_t freqs[P2P_MAX_CHANNELS];
 +      dbus_int32_t *f_array = freqs;
 +
 +
 +      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 */
-  *
-  * Method to emit Invitation Result signal based on status and
-  * bssid
-  * @status: Status of the Invite request. 0 for success, other
-  * for errors
-  * @bssid : Basic Service Set Identifier
++      if (iface == NULL || !wpa_s->dbus_new_path)
 +              return;
 +
 +      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(res->peer_device_addr));
 +      path = peer_obj_path;
 +
 +      msg = dbus_message_new_signal(wpa_s->dbus_new_path,
 +                                    WPAS_DBUS_NEW_IFACE_P2PDEVICE,
 +                                    res->status ? "GONegotiationFailure" :
 +                                                  "GONegotiationSuccess");
 +      if (msg == NULL)
 +              return;
 +
 +      dbus_message_iter_init_append(msg, &iter);
 +      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;
 +
 +      if (!res->status) {
 +              int i = 0;
 +              int freq_list_num = 0;
 +
 +              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",
 +                                              res->freq) ||
 +                  !wpa_dbus_dict_append_byte_array(&dict_iter, "ssid",
 +                                                   (const char *) res->ssid,
 +                                                   res->ssid_len) ||
 +                  !wpa_dbus_dict_append_byte_array(&dict_iter,
 +                                                   "peer_device_addr",
 +                                                   (const char *)
 +                                                   res->peer_device_addr,
 +                                                   ETH_ALEN) ||
 +                  !wpa_dbus_dict_append_byte_array(&dict_iter,
 +                                                   "peer_interface_addr",
 +                                                   (const char *)
 +                                                   res->peer_interface_addr,
 +                                                   ETH_ALEN) ||
 +                  !wpa_dbus_dict_append_string(&dict_iter, "wps_method",
 +                                               p2p_wps_method_text(
 +                                                       res->wps_method)))
 +                      goto err;
 +
 +              for (i = 0; i < P2P_MAX_CHANNELS; i++) {
 +                      if (res->freq_list[i]) {
 +                              freqs[i] = res->freq_list[i];
 +                              freq_list_num++;
 +                      }
 +              }
 +
 +              if (!wpa_dbus_dict_begin_array(&dict_iter,
 +                                             "frequency_list",
 +                                             DBUS_TYPE_INT32_AS_STRING,
 +                                             &iter_dict_entry,
 +                                             &iter_dict_val,
 +                                             &iter_dict_array) ||
 +                  !dbus_message_iter_append_fixed_array(&iter_dict_array,
 +                                                        DBUS_TYPE_INT32,
 +                                                        &f_array,
 +                                                        freq_list_num) ||
 +                  !wpa_dbus_dict_end_array(&dict_iter,
 +                                           &iter_dict_entry,
 +                                           &iter_dict_val,
 +                                           &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",
 +                                               res->peer_config_timeout))
 +                      goto err;
 +      }
 +
 +      if (!wpa_dbus_dict_close_write(&iter, &dict_iter))
 +              goto err;
 +
 +      dbus_connection_send(iface->con, msg, NULL);
 +err:
 +      dbus_message_unref(msg);
 +}
 +
 +
 +/**
-       if (wpa_s == NULL || wpa_s->global == NULL)
++ * wpas_dbus_signal_p2p_invitation_result - Emit InvitationResult signal
++ * @wpa_s: %wpa_supplicant network interface data
++ * @status: Status of invitation process
++ * @bssid: Basic Service Set Identifier
 + */
 +void wpas_dbus_signal_p2p_invitation_result(struct wpa_supplicant *wpa_s,
 +                                          int status, const u8 *bssid)
 +{
 +      DBusMessage *msg;
 +      DBusMessageIter iter, dict_iter;
 +      struct wpas_dbus_priv *iface;
 +
 +      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;
++      if (!wpa_s->dbus_new_path)
++              return;
 +
 +      msg = dbus_message_new_signal(wpa_s->dbus_new_path,
 +                                    WPAS_DBUS_NEW_IFACE_P2PDEVICE,
 +                                    "InvitationResult");
 +
 +      if (msg == NULL)
 +              return;
 +
 +      dbus_message_iter_init_append(msg, &iter);
 +      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);
 +}
 +
 +
 +/**
 + *
 + * Method to emit a signal for a peer joining the group.
 + * The signal will carry path to the group member object
 + * constructed using p2p i/f addr used for connecting.
 + *
 + * @wpa_s: %wpa_supplicant network interface data
 + * @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 *peer_addr)
 +{
 +      struct wpas_dbus_priv *iface;
 +      DBusMessage *msg;
 +      DBusMessageIter iter;
 +      char peer_obj_path[WPAS_DBUS_OBJECT_PATH_MAX], *path;
 +      struct wpa_supplicant *parent;
 +
 +      iface = wpa_s->global->dbus;
 +
 +      /* Do nothing if the control interface is not turned on */
 +      if (iface == NULL)
 +              return;
 +
 +      if (!wpa_s->dbus_groupobj_path)
 +              return;
 +
 +      parent = wpa_s->parent;
 +      if (parent->p2p_mgmt)
 +              parent = parent->parent;
++      if (!parent->dbus_new_path)
++              return;
 +
 +      os_snprintf(peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
 +                      "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART "/"
 +                      COMPACT_MACSTR,
 +                      parent->dbus_new_path, MAC2STR(peer_addr));
 +
 +      msg = dbus_message_new_signal(wpa_s->dbus_groupobj_path,
 +                                    WPAS_DBUS_NEW_IFACE_P2P_GROUP,
 +                                    "PeerJoined");
 +      if (msg == NULL)
 +              return;
 +
 +      dbus_message_iter_init_append(msg, &iter);
 +      path = peer_obj_path;
 +      if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH,
 +                                          &path)) {
 +              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);
 +}
 +
 +
 +/**
 + *
 + * Method to emit a signal for a peer disconnecting the group.
 + * The signal will carry path to the group member object
 + * constructed using the P2P Device Address of the peer.
 + *
 + * @wpa_s: %wpa_supplicant network interface data
 + * @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 *peer_addr)
 +{
 +      struct wpas_dbus_priv *iface;
 +      DBusMessage *msg;
 +      DBusMessageIter iter;
 +      char peer_obj_path[WPAS_DBUS_OBJECT_PATH_MAX], *path;
 +      struct wpa_supplicant *parent;
 +
 +      iface = wpa_s->global->dbus;
 +
 +      /* Do nothing if the control interface is not turned on */
 +      if (iface == NULL)
 +              return;
 +
 +      if (!wpa_s->dbus_groupobj_path)
 +              return;
 +
 +      parent = wpa_s->parent;
 +      if (parent->p2p_mgmt)
 +              parent = parent->parent;
++      if (!parent->dbus_new_path)
++              return;
 +
 +      os_snprintf(peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
 +                      "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART "/"
 +                      COMPACT_MACSTR,
 +                      parent->dbus_new_path, MAC2STR(peer_addr));
 +
 +      msg = dbus_message_new_signal(wpa_s->dbus_groupobj_path,
 +                                    WPAS_DBUS_NEW_IFACE_P2P_GROUP,
 +                                    "PeerDisconnected");
 +      if (msg == NULL)
 +              return;
 +
 +      dbus_message_iter_init_append(msg, &iter);
 +      path = peer_obj_path;
 +      if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH,
 +                                          &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);
 +}
 +
 +
 +/**
 + *
 + * Method to emit a signal for a service discovery request.
 + * The signal will carry station address, frequency, dialog token,
 + * update indicator and it tlvs
 + *
 + * @wpa_s: %wpa_supplicant network interface data
 + * @sa: station addr (p2p i/f) of the peer
 + * @dialog_token: service discovery request dialog token
 + * @update_indic: service discovery request update indicator
 + * @tlvs: service discovery request genrated byte array of tlvs
 + * @tlvs_len: service discovery request tlvs length
 + */
 +void wpas_dbus_signal_p2p_sd_request(struct wpa_supplicant *wpa_s,
 +                                   int freq, const u8 *sa, u8 dialog_token,
 +                                   u16 update_indic, const u8 *tlvs,
 +                                   size_t tlvs_len)
 +{
 +      DBusMessage *msg;
 +      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;
++      if (!wpa_s->dbus_new_path)
++              return;
 +
 +      /* 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;
 +
 +      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));
 +
 +      path = peer_obj_path;
 +
 +      dbus_message_iter_init_append(msg, &iter);
 +      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",
 +                                      dialog_token) ||
 +          !wpa_dbus_dict_append_uint16(&dict_iter, "update_indicator",
 +                                       update_indic) ||
 +          !wpa_dbus_dict_append_byte_array(&dict_iter, "tlvs",
 +                                           (const char *) tlvs,
 +                                           tlvs_len) ||
 +          !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);
 +}
 +
 +
 +/**
 + *
 + * Method to emit a signal for a service discovery response.
 + * The signal will carry station address, update indicator and it
 + * tlvs
 + *
 + * @wpa_s: %wpa_supplicant network interface data
 + * @sa: station addr (p2p i/f) of the peer
 + * @update_indic: service discovery request update indicator
 + * @tlvs: service discovery request genrated byte array of tlvs
 + * @tlvs_len: service discovery request tlvs length
 + */
 +void wpas_dbus_signal_p2p_sd_response(struct wpa_supplicant *wpa_s,
 +                                    const u8 *sa, u16 update_indic,
 +                                    const u8 *tlvs, size_t tlvs_len)
 +{
 +      DBusMessage *msg;
 +      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;
++      if (!wpa_s->dbus_new_path)
++              return;
 +
 +      /* 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");
 +      if (msg == NULL)
 +              return;
 +
 +      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));
 +
 +      path = peer_obj_path;
 +
 +      dbus_message_iter_init_append(msg, &iter);
 +      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) ||
 +          !wpa_dbus_dict_append_byte_array(&dict_iter, "tlvs",
 +                                           (const char *) tlvs,
 +                                           tlvs_len) ||
 +          !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_persistent_group - Send a persistent group related
 + *    event signal
 + * @wpa_s: %wpa_supplicant network interface data
 + * @id: new persistent group id
 + * @sig_name: signal name - PersistentGroupAdded, PersistentGroupRemoved
 + * @properties: determines if add second argument with object properties
 + *
 + * Notify listeners about an event related to persistent groups.
 + */
 +static void wpas_dbus_signal_persistent_group(struct wpa_supplicant *wpa_s,
 +                                            int id, const char *sig_name,
 +                                            int properties)
 +{
 +      struct wpas_dbus_priv *iface;
 +      DBusMessage *msg;
 +      DBusMessageIter iter;
 +      char pgrp_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;
++      if (!wpa_s->dbus_new_path)
++              return;
 +
 +      os_snprintf(pgrp_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
 +                  "%s/" WPAS_DBUS_NEW_PERSISTENT_GROUPS_PART "/%u",
 +                  wpa_s->dbus_new_path, id);
 +
 +      msg = dbus_message_new_signal(wpa_s->dbus_new_path,
 +                                    WPAS_DBUS_NEW_IFACE_P2PDEVICE,
 +                                    sig_name);
 +      if (msg == NULL)
 +              return;
 +
 +      dbus_message_iter_init_append(msg, &iter);
 +      path = pgrp_obj_path;
 +      if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH,
 +                                          &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);
 +
 +      dbus_message_unref(msg);
 +}
 +
 +
 +/**
 + * wpas_dbus_signal_persistent_group_added - Send a persistent_group
 + *    added signal
 + * @wpa_s: %wpa_supplicant network interface data
 + * @id: new persistent group id
 + *
 + * Notify listeners about addition of a new persistent group.
 + */
 +static void wpas_dbus_signal_persistent_group_added(
 +      struct wpa_supplicant *wpa_s, int id)
 +{
 +      wpas_dbus_signal_persistent_group(wpa_s, id, "PersistentGroupAdded",
 +                                        TRUE);
 +}
 +
 +
 +/**
 + * wpas_dbus_signal_persistent_group_removed - Send a persistent_group
 + *    removed signal
 + * @wpa_s: %wpa_supplicant network interface data
 + * @id: persistent group id
 + *
 + * Notify listeners about removal of a persistent group.
 + */
 +static void wpas_dbus_signal_persistent_group_removed(
 +      struct wpa_supplicant *wpa_s, int id)
 +{
 +      wpas_dbus_signal_persistent_group(wpa_s, id, "PersistentGroupRemoved",
 +                                        FALSE);
 +}
 +
 +
 +/**
 + * wpas_dbus_signal_p2p_wps_failed - Signals WpsFailed event
 + * @wpa_s: %wpa_supplicant network interface data
++ * @fail: WPS failure information
 + *
 + * Sends Event dbus signal with name "fail" and dictionary containing
 + * "msg" field with fail message number (int32) as arguments
 + */
 +void wpas_dbus_signal_p2p_wps_failed(struct wpa_supplicant *wpa_s,
 +                                   struct wps_event_fail *fail)
 +{
 +
 +      DBusMessage *msg;
 +      DBusMessageIter iter, dict_iter;
 +      struct wpas_dbus_priv *iface;
 +      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;
 +
++      if (!wpa_s->dbus_new_path)
++              return;
 +      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
++ * @reason: indicates the reason code for group formation failure
++ *
++ * Sends Event dbus signal and string reason code when available.
++ */
++void wpas_dbus_signal_p2p_group_formation_failure(struct wpa_supplicant *wpa_s,
++                                                const char *reason)
++{
++      DBusMessage *msg;
++      struct wpas_dbus_priv *iface;
++
++      iface = wpa_s->global->dbus;
++
++      /* Do nothing if the control interface is not turned on */
++      if (iface == NULL)
++              return;
++
++      msg = dbus_message_new_signal(wpa_s->dbus_new_path,
++                                    WPAS_DBUS_NEW_IFACE_P2PDEVICE,
++                                    "GroupFormationFailure");
++      if (msg == NULL)
++              return;
++
++      if (dbus_message_append_args(msg, DBUS_TYPE_STRING, &reason,
++                                   DBUS_TYPE_INVALID))
++              dbus_connection_send(iface->con, msg, NULL);
++      else
++              wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
++
++      dbus_message_unref(msg);
++}
++
++
++/**
++ * wpas_dbus_signal_p2p_invitation_received - Emit InvitationReceived signal
++ * @wpa_s: %wpa_supplicant network interface data
++ * @sa: Source address of the Invitation Request
++ * @dev_add: GO Device Address
++ * @bssid: P2P Group BSSID or %NULL if not received
++ * @id: Persistent group id or %0 if not persistent group
++ * @op_freq: Operating frequency for the group
++ */
++
++void wpas_dbus_signal_p2p_invitation_received(struct wpa_supplicant *wpa_s,
++                                            const u8 *sa, const u8 *dev_addr,
++                                            const u8 *bssid, int id,
++                                            int op_freq)
++{
++      DBusMessage *msg;
++      DBusMessageIter iter, dict_iter;
++      struct wpas_dbus_priv *iface;
++
++      iface = wpa_s->global->dbus;
++
++      /* Do nothing if the control interface is not turned on */
++      if (iface == NULL)
++              return;
++
++      msg = dbus_message_new_signal(wpa_s->dbus_new_path,
++                                    WPAS_DBUS_NEW_IFACE_P2PDEVICE,
++                                    "InvitationReceived");
++      if (msg == NULL)
++              return;
++
++      dbus_message_iter_init_append(msg, &iter);
++      if (!wpa_dbus_dict_open_write(&iter, &dict_iter) ||
++          (sa &&
++           !wpa_dbus_dict_append_byte_array(&dict_iter, "sa",
++                                            (const char *) sa, ETH_ALEN)) ||
++          (dev_addr &&
++           !wpa_dbus_dict_append_byte_array(&dict_iter, "go_dev_addr",
++                                            (const char *) dev_addr,
++                                            ETH_ALEN)) ||
++          (bssid &&
++           !wpa_dbus_dict_append_byte_array(&dict_iter, "bssid",
++                                            (const char *) bssid,
++                                            ETH_ALEN)) ||
++          (id &&
++           !wpa_dbus_dict_append_int32(&dict_iter, "persistent_id", id)) ||
++          !wpa_dbus_dict_append_int32(&dict_iter, "op_freq", op_freq) ||
++          !wpa_dbus_dict_close_write(&iter, &dict_iter)) {
++              dbus_message_unref(msg);
++              return;
++      }
++
++      dbus_connection_send(iface->con, msg, NULL);
++}
++
++
 +#endif /* CONFIG_P2P */
 +
 +
 +/**
 + * wpas_dbus_signal_prop_changed - Signals change of property
 + * @wpa_s: %wpa_supplicant network interface data
 + * @property: indicates which property has changed
 + *
 + * Sends PropertyChanged signals with path, interface and arguments
 + * depending on which property has changed.
 + */
 +void wpas_dbus_signal_prop_changed(struct wpa_supplicant *wpa_s,
 +                                 enum wpas_dbus_prop property)
 +{
 +      char *prop;
 +      dbus_bool_t flush;
 +
 +      if (wpa_s->dbus_new_path == NULL)
 +              return; /* Skip signal since D-Bus setup is not yet ready */
 +
 +      flush = FALSE;
 +      switch (property) {
 +      case WPAS_DBUS_PROP_AP_SCAN:
 +              prop = "ApScan";
 +              break;
 +      case WPAS_DBUS_PROP_SCANNING:
 +              prop = "Scanning";
 +              break;
 +      case WPAS_DBUS_PROP_STATE:
 +              prop = "State";
 +              break;
 +      case WPAS_DBUS_PROP_CURRENT_BSS:
 +              prop = "CurrentBSS";
 +              break;
 +      case WPAS_DBUS_PROP_CURRENT_NETWORK:
 +              prop = "CurrentNetwork";
 +              break;
 +      case WPAS_DBUS_PROP_BSSS:
 +              prop = "BSSs";
 +              break;
 +      case WPAS_DBUS_PROP_CURRENT_AUTH_MODE:
 +              prop = "CurrentAuthMode";
 +              break;
 +      case WPAS_DBUS_PROP_DISCONNECT_REASON:
 +              prop = "DisconnectReason";
 +              flush = TRUE;
 +              break;
 +      default:
 +              wpa_printf(MSG_ERROR, "dbus: %s: Unknown Property value %d",
 +                         __func__, property);
 +              return;
 +      }
 +
 +      wpa_dbus_mark_property_changed(wpa_s->global->dbus,
 +                                     wpa_s->dbus_new_path,
 +                                     WPAS_DBUS_NEW_IFACE_INTERFACE, prop);
 +      if (flush) {
 +              wpa_dbus_flush_object_changed_properties(
 +                      wpa_s->global->dbus->con, wpa_s->dbus_new_path);
 +      }
 +}
 +
 +
 +/**
 + * wpas_dbus_bss_signal_prop_changed - Signals change of BSS property
 + * @wpa_s: %wpa_supplicant network interface data
 + * @property: indicates which property has changed
 + * @id: unique BSS identifier
 + *
 + * Sends PropertyChanged signals with path, interface, and arguments depending
 + * on which property has changed.
 + */
 +void wpas_dbus_bss_signal_prop_changed(struct wpa_supplicant *wpa_s,
 +                                     enum wpas_dbus_bss_prop property,
 +                                     unsigned int id)
 +{
 +      char path[WPAS_DBUS_OBJECT_PATH_MAX];
 +      char *prop;
 +
++      if (!wpa_s->dbus_new_path)
++              return;
++
 +      switch (property) {
 +      case WPAS_DBUS_BSS_PROP_SIGNAL:
 +              prop = "Signal";
 +              break;
 +      case WPAS_DBUS_BSS_PROP_FREQ:
 +              prop = "Frequency";
 +              break;
 +      case WPAS_DBUS_BSS_PROP_MODE:
 +              prop = "Mode";
 +              break;
 +      case WPAS_DBUS_BSS_PROP_PRIVACY:
 +              prop = "Privacy";
 +              break;
 +      case WPAS_DBUS_BSS_PROP_RATES:
 +              prop = "Rates";
 +              break;
 +      case WPAS_DBUS_BSS_PROP_WPA:
 +              prop = "WPA";
 +              break;
 +      case WPAS_DBUS_BSS_PROP_RSN:
 +              prop = "RSN";
 +              break;
 +      case WPAS_DBUS_BSS_PROP_WPS:
 +              prop = "WPS";
 +              break;
 +      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);
 +              return;
 +      }
 +
 +      os_snprintf(path, WPAS_DBUS_OBJECT_PATH_MAX,
 +                  "%s/" WPAS_DBUS_NEW_BSSIDS_PART "/%u",
 +                  wpa_s->dbus_new_path, id);
 +
 +      wpa_dbus_mark_property_changed(wpa_s->global->dbus, path,
 +                                     WPAS_DBUS_NEW_IFACE_BSS, prop);
 +}
 +
 +
 +/**
 + * wpas_dbus_signal_debug_level_changed - Signals change of debug param
 + * @global: wpa_global structure
 + *
 + * Sends PropertyChanged signals informing that debug level has changed.
 + */
 +void wpas_dbus_signal_debug_level_changed(struct wpa_global *global)
 +{
 +      wpa_dbus_mark_property_changed(global->dbus, WPAS_DBUS_NEW_PATH,
 +                                     WPAS_DBUS_NEW_INTERFACE,
 +                                     "DebugLevel");
 +}
 +
 +
 +/**
 + * wpas_dbus_signal_debug_timestamp_changed - Signals change of debug param
 + * @global: wpa_global structure
 + *
 + * Sends PropertyChanged signals informing that debug timestamp has changed.
 + */
 +void wpas_dbus_signal_debug_timestamp_changed(struct wpa_global *global)
 +{
 +      wpa_dbus_mark_property_changed(global->dbus, WPAS_DBUS_NEW_PATH,
 +                                     WPAS_DBUS_NEW_INTERFACE,
 +                                     "DebugTimestamp");
 +}
 +
 +
 +/**
 + * wpas_dbus_signal_debug_show_keys_changed - Signals change of debug param
 + * @global: wpa_global structure
 + *
 + * Sends PropertyChanged signals informing that debug show_keys has changed.
 + */
 +void wpas_dbus_signal_debug_show_keys_changed(struct wpa_global *global)
 +{
 +      wpa_dbus_mark_property_changed(global->dbus, WPAS_DBUS_NEW_PATH,
 +                                     WPAS_DBUS_NEW_INTERFACE,
 +                                     "DebugShowKeys");
 +}
 +
 +
 +static void wpas_dbus_register(struct wpa_dbus_object_desc *obj_desc,
 +                             void *priv,
 +                             WPADBusArgumentFreeFunction priv_free,
 +                             const struct wpa_dbus_method_desc *methods,
 +                             const struct wpa_dbus_property_desc *properties,
 +                             const struct wpa_dbus_signal_desc *signals)
 +{
 +      int n;
 +
 +      obj_desc->user_data = priv;
 +      obj_desc->user_data_free_func = priv_free;
 +      obj_desc->methods = methods;
 +      obj_desc->properties = properties;
 +      obj_desc->signals = signals;
 +
 +      for (n = 0; properties && properties->dbus_property; properties++)
 +              n++;
 +
 +      obj_desc->prop_changed_flags = os_zalloc(n);
 +      if (!obj_desc->prop_changed_flags)
 +              wpa_printf(MSG_DEBUG, "dbus: %s: can't register handlers",
 +                         __func__);
 +}
 +
 +
 +static const struct wpa_dbus_method_desc wpas_dbus_global_methods[] = {
 +      { "CreateInterface", WPAS_DBUS_NEW_INTERFACE,
 +        (WPADBusMethodHandler) wpas_dbus_handler_create_interface,
 +        {
 +                { "args", "a{sv}", ARG_IN },
 +                { "path", "o", ARG_OUT },
 +                END_ARGS
 +        }
 +      },
 +      { "RemoveInterface", WPAS_DBUS_NEW_INTERFACE,
 +        (WPADBusMethodHandler) wpas_dbus_handler_remove_interface,
 +        {
 +                { "path", "o", ARG_IN },
 +                END_ARGS
 +        }
 +      },
 +      { "GetInterface", WPAS_DBUS_NEW_INTERFACE,
 +        (WPADBusMethodHandler) wpas_dbus_handler_get_interface,
 +        {
 +                { "ifname", "s", ARG_IN },
 +                { "path", "o", ARG_OUT },
 +                END_ARGS
 +        }
 +      },
 +      { NULL, NULL, NULL, { END_ARGS } }
 +};
 +
 +static const struct wpa_dbus_property_desc wpas_dbus_global_properties[] = {
 +      { "DebugLevel", WPAS_DBUS_NEW_INTERFACE, "s",
 +        wpas_dbus_getter_debug_level,
 +        wpas_dbus_setter_debug_level
 +      },
 +      { "DebugTimestamp", WPAS_DBUS_NEW_INTERFACE, "b",
 +        wpas_dbus_getter_debug_timestamp,
 +        wpas_dbus_setter_debug_timestamp
 +      },
 +      { "DebugShowKeys", WPAS_DBUS_NEW_INTERFACE, "b",
 +        wpas_dbus_getter_debug_show_keys,
 +        wpas_dbus_setter_debug_show_keys
 +      },
 +      { "Interfaces", WPAS_DBUS_NEW_INTERFACE, "ao",
 +        wpas_dbus_getter_interfaces,
 +        NULL
 +      },
 +      { "EapMethods", WPAS_DBUS_NEW_INTERFACE, "as",
 +        wpas_dbus_getter_eap_methods,
 +        NULL
 +      },
 +      { "Capabilities", WPAS_DBUS_NEW_INTERFACE, "as",
 +        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 }
 +};
 +
 +static const struct wpa_dbus_signal_desc wpas_dbus_global_signals[] = {
 +      { "InterfaceAdded", WPAS_DBUS_NEW_INTERFACE,
 +        {
 +                { "path", "o", ARG_OUT },
 +                { "properties", "a{sv}", ARG_OUT },
 +                END_ARGS
 +        }
 +      },
 +      { "InterfaceRemoved", WPAS_DBUS_NEW_INTERFACE,
 +        {
 +                { "path", "o", ARG_OUT },
 +                END_ARGS
 +        }
 +      },
 +      /* Deprecated: use org.freedesktop.DBus.Properties.PropertiesChanged */
 +      { "PropertiesChanged", WPAS_DBUS_NEW_INTERFACE,
 +        {
 +                { "properties", "a{sv}", ARG_OUT },
 +                END_ARGS
 +        }
 +      },
 +      { NULL, NULL, { END_ARGS } }
 +};
 +
 +
 +/**
 + * wpas_dbus_ctrl_iface_init - Initialize dbus control interface
 + * @global: Pointer to global data from wpa_supplicant_init()
 + * Returns: 0 on success or -1 on failure
 + *
 + * Initialize the dbus control interface for wpa_supplicantand and start
 + * receiving commands from external programs over the bus.
 + */
 +int wpas_dbus_ctrl_iface_init(struct wpas_dbus_priv *priv)
 +{
 +      struct wpa_dbus_object_desc *obj_desc;
 +      int ret;
 +
 +      obj_desc = os_zalloc(sizeof(struct wpa_dbus_object_desc));
 +      if (!obj_desc) {
 +              wpa_printf(MSG_ERROR,
 +                         "Not enough memory to create object description");
 +              return -1;
 +      }
 +
 +      wpas_dbus_register(obj_desc, priv->global, NULL,
 +                         wpas_dbus_global_methods,
 +                         wpas_dbus_global_properties,
 +                         wpas_dbus_global_signals);
 +
 +      wpa_printf(MSG_DEBUG, "dbus: Register D-Bus object '%s'",
 +                 WPAS_DBUS_NEW_PATH);
 +      ret = wpa_dbus_ctrl_iface_init(priv, WPAS_DBUS_NEW_PATH,
 +                                     WPAS_DBUS_NEW_SERVICE,
 +                                     obj_desc);
 +      if (ret < 0)
 +              free_dbus_object_desc(obj_desc);
 +      else
 +              priv->dbus_new_initialized = 1;
 +
 +      return ret;
 +}
 +
 +
 +/**
 + * wpas_dbus_ctrl_iface_deinit - Deinitialize dbus ctrl interface for
 + * wpa_supplicant
 + * @iface: Pointer to dbus private data from wpas_dbus_init()
 + *
 + * Deinitialize the dbus control interface that was initialized with
 + * wpas_dbus_ctrl_iface_init().
 + */
 +void wpas_dbus_ctrl_iface_deinit(struct wpas_dbus_priv *iface)
 +{
 +      if (!iface->dbus_new_initialized)
 +              return;
 +      wpa_printf(MSG_DEBUG, "dbus: Unregister D-Bus object '%s'",
 +                 WPAS_DBUS_NEW_PATH);
 +      dbus_connection_unregister_object_path(iface->con,
 +                                             WPAS_DBUS_NEW_PATH);
 +}
 +
 +
 +static void wpa_dbus_free(void *ptr)
 +{
 +      os_free(ptr);
 +}
 +
 +
 +static const struct wpa_dbus_property_desc wpas_dbus_network_properties[] = {
 +      { "Properties", WPAS_DBUS_NEW_IFACE_NETWORK, "a{sv}",
 +        wpas_dbus_getter_network_properties,
 +        wpas_dbus_setter_network_properties
 +      },
 +      { "Enabled", WPAS_DBUS_NEW_IFACE_NETWORK, "b",
 +        wpas_dbus_getter_enabled,
 +        wpas_dbus_setter_enabled
 +      },
 +      { NULL, NULL, NULL, NULL, NULL }
 +};
 +
 +
 +static const struct wpa_dbus_signal_desc wpas_dbus_network_signals[] = {
 +      /* Deprecated: use org.freedesktop.DBus.Properties.PropertiesChanged */
 +      { "PropertiesChanged", WPAS_DBUS_NEW_IFACE_NETWORK,
 +        {
 +                { "properties", "a{sv}", ARG_OUT },
 +                END_ARGS
 +        }
 +      },
 +      { NULL, NULL, { END_ARGS } }
 +};
 +
 +
 +/**
 + * wpas_dbus_register_network - Register a configured network with dbus
 + * @wpa_s: wpa_supplicant interface structure
 + * @ssid: network configuration data
 + * Returns: 0 on success, -1 on failure
 + *
 + * Registers network representing object with dbus
 + */
 +int wpas_dbus_register_network(struct wpa_supplicant *wpa_s,
 +                             struct wpa_ssid *ssid)
 +{
 +      struct wpas_dbus_priv *ctrl_iface;
 +      struct wpa_dbus_object_desc *obj_desc;
 +      struct network_handler_args *arg;
 +      char net_obj_path[WPAS_DBUS_OBJECT_PATH_MAX];
 +
 +#ifdef CONFIG_P2P
 +      /*
 +       * If it is a persistent group register it as such.
 +       * This is to handle cases where an interface is being initialized
 +       * with a list of networks read from config.
 +       */
 +      if (network_is_persistent_group(ssid))
 +              return wpas_dbus_register_persistent_group(wpa_s, ssid);
 +#endif /* CONFIG_P2P */
 +
 +      /* Do nothing if the control interface is not turned on */
-       if (wpa_s == NULL || wpa_s->global == NULL)
++      if (wpa_s == NULL || wpa_s->global == NULL || !wpa_s->dbus_new_path)
 +              return 0;
 +      ctrl_iface = wpa_s->global->dbus;
 +      if (ctrl_iface == NULL)
 +              return 0;
 +
 +      os_snprintf(net_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
 +                  "%s/" WPAS_DBUS_NEW_NETWORKS_PART "/%u",
 +                  wpa_s->dbus_new_path, ssid->id);
 +
 +      wpa_printf(MSG_DEBUG, "dbus: Register network object '%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");
 +              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");
 +              goto err;
 +      }
 +
 +      arg->wpa_s = wpa_s;
 +      arg->ssid = ssid;
 +
 +      wpas_dbus_register(obj_desc, arg, wpa_dbus_free, NULL,
 +                         wpas_dbus_network_properties,
 +                         wpas_dbus_network_signals);
 +
 +      if (wpa_dbus_register_object_per_iface(ctrl_iface, net_obj_path,
 +                                             wpa_s->ifname, obj_desc))
 +              goto err;
 +
 +      wpas_dbus_signal_network_added(wpa_s, ssid->id);
 +
 +      return 0;
 +
 +err:
 +      free_dbus_object_desc(obj_desc);
 +      return -1;
 +}
 +
 +
 +/**
 + * wpas_dbus_unregister_network - Unregister a configured network from dbus
 + * @wpa_s: wpa_supplicant interface structure
 + * @nid: network id
 + * Returns: 0 on success, -1 on failure
 + *
 + * Unregisters network representing object from dbus
 + */
 +int wpas_dbus_unregister_network(struct wpa_supplicant *wpa_s, int nid)
 +{
 +      struct wpas_dbus_priv *ctrl_iface;
 +      char net_obj_path[WPAS_DBUS_OBJECT_PATH_MAX];
 +      int ret;
 +#ifdef CONFIG_P2P
 +      struct wpa_ssid *ssid;
 +
 +      ssid = wpa_config_get_network(wpa_s->conf, nid);
 +
 +      /* If it is a persistent group unregister it as such */
 +      if (ssid && network_is_persistent_group(ssid))
 +              return wpas_dbus_unregister_persistent_group(wpa_s, nid);
 +#endif /* CONFIG_P2P */
 +
 +      /* Do nothing if the control interface is not turned on */
 +      if (wpa_s->global == NULL || wpa_s->dbus_new_path == NULL)
 +              return 0;
 +      ctrl_iface = wpa_s->global->dbus;
 +      if (ctrl_iface == NULL)
 +              return 0;
 +
 +      os_snprintf(net_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
 +                  "%s/" WPAS_DBUS_NEW_NETWORKS_PART "/%u",
 +                  wpa_s->dbus_new_path, nid);
 +
 +      wpa_printf(MSG_DEBUG, "dbus: Unregister network object '%s'",
 +                 net_obj_path);
 +      ret = wpa_dbus_unregister_object_per_iface(ctrl_iface, net_obj_path);
 +
 +      if (!ret)
 +              wpas_dbus_signal_network_removed(wpa_s, nid);
 +
 +      return ret;
 +}
 +
 +
 +static const struct wpa_dbus_property_desc wpas_dbus_bss_properties[] = {
 +      { "SSID", WPAS_DBUS_NEW_IFACE_BSS, "ay",
 +        wpas_dbus_getter_bss_ssid,
 +        NULL
 +      },
 +      { "BSSID", WPAS_DBUS_NEW_IFACE_BSS, "ay",
 +        wpas_dbus_getter_bss_bssid,
 +        NULL
 +      },
 +      { "Privacy", WPAS_DBUS_NEW_IFACE_BSS, "b",
 +        wpas_dbus_getter_bss_privacy,
 +        NULL
 +      },
 +      { "Mode", WPAS_DBUS_NEW_IFACE_BSS, "s",
 +        wpas_dbus_getter_bss_mode,
 +        NULL
 +      },
 +      { "Signal", WPAS_DBUS_NEW_IFACE_BSS, "n",
 +        wpas_dbus_getter_bss_signal,
 +        NULL
 +      },
 +      { "Frequency", WPAS_DBUS_NEW_IFACE_BSS, "q",
 +        wpas_dbus_getter_bss_frequency,
 +        NULL
 +      },
 +      { "Rates", WPAS_DBUS_NEW_IFACE_BSS, "au",
 +        wpas_dbus_getter_bss_rates,
 +        NULL
 +      },
 +      { "WPA", WPAS_DBUS_NEW_IFACE_BSS, "a{sv}",
 +        wpas_dbus_getter_bss_wpa,
 +        NULL
 +      },
 +      { "RSN", WPAS_DBUS_NEW_IFACE_BSS, "a{sv}",
 +        wpas_dbus_getter_bss_rsn,
 +        NULL
 +      },
 +      { "WPS", WPAS_DBUS_NEW_IFACE_BSS, "a{sv}",
 +        wpas_dbus_getter_bss_wps,
 +        NULL
 +      },
 +      { "IEs", WPAS_DBUS_NEW_IFACE_BSS, "ay",
 +        wpas_dbus_getter_bss_ies,
 +        NULL
 +      },
 +      { "Age", WPAS_DBUS_NEW_IFACE_BSS, "u",
 +        wpas_dbus_getter_bss_age,
 +        NULL
 +      },
 +      { NULL, NULL, NULL, NULL, NULL }
 +};
 +
 +
 +static const struct wpa_dbus_signal_desc wpas_dbus_bss_signals[] = {
 +      /* Deprecated: use org.freedesktop.DBus.Properties.PropertiesChanged */
 +      { "PropertiesChanged", WPAS_DBUS_NEW_IFACE_BSS,
 +        {
 +                { "properties", "a{sv}", ARG_OUT },
 +                END_ARGS
 +        }
 +      },
 +      { NULL, NULL, { END_ARGS } }
 +};
 +
 +
 +/**
 + * wpas_dbus_unregister_bss - Unregister a scanned BSS from dbus
 + * @wpa_s: wpa_supplicant interface structure
 + * @bssid: scanned network bssid
 + * @id: unique BSS identifier
 + * Returns: 0 on success, -1 on failure
 + *
 + * Unregisters BSS representing object from dbus
 + */
 +int wpas_dbus_unregister_bss(struct wpa_supplicant *wpa_s,
 +                           u8 bssid[ETH_ALEN], unsigned int id)
 +{
 +      struct wpas_dbus_priv *ctrl_iface;
 +      char bss_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)
++      if (wpa_s == NULL || wpa_s->global == NULL || !wpa_s->dbus_new_path)
 +              return 0;
 +      ctrl_iface = wpa_s->global->dbus;
 +      if (ctrl_iface == NULL)
 +              return 0;
 +
 +      os_snprintf(bss_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
 +                  "%s/" WPAS_DBUS_NEW_BSSIDS_PART "/%u",
 +                  wpa_s->dbus_new_path, id);
 +
 +      wpa_printf(MSG_DEBUG, "dbus: Unregister BSS object '%s'",
 +                 bss_obj_path);
 +      if (wpa_dbus_unregister_object_per_iface(ctrl_iface, bss_obj_path)) {
 +              wpa_printf(MSG_ERROR, "dbus: Cannot unregister BSS object %s",
 +                         bss_obj_path);
 +              return -1;
 +      }
 +
 +      wpas_dbus_signal_bss_removed(wpa_s, bss_obj_path);
 +      wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_BSSS);
 +
 +      return 0;
 +}
 +
 +
 +/**
 + * wpas_dbus_register_bss - Register a scanned BSS with dbus
 + * @wpa_s: wpa_supplicant interface structure
 + * @bssid: scanned network bssid
 + * @id: unique BSS identifier
 + * Returns: 0 on success, -1 on failure
 + *
 + * Registers BSS representing object with dbus
 + */
 +int wpas_dbus_register_bss(struct wpa_supplicant *wpa_s,
 +                         u8 bssid[ETH_ALEN], unsigned int id)
 +{
 +      struct wpas_dbus_priv *ctrl_iface;
 +      struct wpa_dbus_object_desc *obj_desc;
 +      char bss_obj_path[WPAS_DBUS_OBJECT_PATH_MAX];
 +      struct bss_handler_args *arg;
 +
 +      /* Do nothing if the control interface is not turned on */
-                 { "dev_passwd_id", "i", ARG_OUT },
++      if (wpa_s == NULL || wpa_s->global == NULL || !wpa_s->dbus_new_path)
 +              return 0;
 +      ctrl_iface = wpa_s->global->dbus;
 +      if (ctrl_iface == NULL)
 +              return 0;
 +
 +      os_snprintf(bss_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
 +                  "%s/" WPAS_DBUS_NEW_BSSIDS_PART "/%u",
 +                  wpa_s->dbus_new_path, id);
 +
 +      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;
 +      }
 +
 +      arg = os_zalloc(sizeof(struct bss_handler_args));
 +      if (!arg) {
 +              wpa_printf(MSG_ERROR,
 +                         "Not enough memory to create arguments for handler");
 +              goto err;
 +      }
 +      arg->wpa_s = wpa_s;
 +      arg->id = id;
 +
 +      wpas_dbus_register(obj_desc, arg, wpa_dbus_free, NULL,
 +                         wpas_dbus_bss_properties,
 +                         wpas_dbus_bss_signals);
 +
 +      wpa_printf(MSG_DEBUG, "dbus: Register BSS object '%s'",
 +                 bss_obj_path);
 +      if (wpa_dbus_register_object_per_iface(ctrl_iface, bss_obj_path,
 +                                             wpa_s->ifname, obj_desc)) {
 +              wpa_printf(MSG_ERROR,
 +                         "Cannot register BSSID dbus object %s.",
 +                         bss_obj_path);
 +              goto err;
 +      }
 +
 +      wpas_dbus_signal_bss_added(wpa_s, bss_obj_path);
 +      wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_BSSS);
 +
 +      return 0;
 +
 +err:
 +      free_dbus_object_desc(obj_desc);
 +      return -1;
 +}
 +
 +
 +static const struct wpa_dbus_method_desc wpas_dbus_interface_methods[] = {
 +      { "Scan", WPAS_DBUS_NEW_IFACE_INTERFACE,
 +        (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,
 +        {
 +                END_ARGS
 +        }
 +      },
 +      { "AddNetwork", WPAS_DBUS_NEW_IFACE_INTERFACE,
 +        (WPADBusMethodHandler) wpas_dbus_handler_add_network,
 +        {
 +                { "args", "a{sv}", ARG_IN },
 +                { "path", "o", ARG_OUT },
 +                END_ARGS
 +        }
 +      },
 +      { "Reassociate", WPAS_DBUS_NEW_IFACE_INTERFACE,
 +        (WPADBusMethodHandler) wpas_dbus_handler_reassociate,
 +        {
 +                END_ARGS
 +        }
 +      },
 +      { "Reattach", WPAS_DBUS_NEW_IFACE_INTERFACE,
 +        (WPADBusMethodHandler) wpas_dbus_handler_reattach,
 +        {
 +                END_ARGS
 +        }
 +      },
++      { "Reconnect", WPAS_DBUS_NEW_IFACE_INTERFACE,
++        (WPADBusMethodHandler) wpas_dbus_handler_reconnect,
++        {
++                END_ARGS
++        }
++      },
 +      { "RemoveNetwork", WPAS_DBUS_NEW_IFACE_INTERFACE,
 +        (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,
 +        {
 +                END_ARGS
 +        }
 +      },
 +      { "SelectNetwork", WPAS_DBUS_NEW_IFACE_INTERFACE,
 +        (WPADBusMethodHandler) wpas_dbus_handler_select_network,
 +        {
 +                { "path", "o", ARG_IN },
 +                END_ARGS
 +        }
 +      },
 +      { "NetworkReply", WPAS_DBUS_NEW_IFACE_INTERFACE,
 +        (WPADBusMethodHandler) wpas_dbus_handler_network_reply,
 +        {
 +                { "path", "o", ARG_IN },
 +                { "field", "s", ARG_IN },
 +                { "value", "s", ARG_IN },
 +                END_ARGS
 +        }
 +      },
 +#ifndef CONFIG_NO_CONFIG_BLOBS
 +      { "AddBlob", WPAS_DBUS_NEW_IFACE_INTERFACE,
 +        (WPADBusMethodHandler) wpas_dbus_handler_add_blob,
 +        {
 +                { "name", "s", ARG_IN },
 +                { "data", "ay", ARG_IN },
 +                END_ARGS
 +        }
 +      },
 +      { "GetBlob", WPAS_DBUS_NEW_IFACE_INTERFACE,
 +        (WPADBusMethodHandler) wpas_dbus_handler_get_blob,
 +        {
 +                { "name", "s", ARG_IN },
 +                { "data", "ay", ARG_OUT },
 +                END_ARGS
 +        }
 +      },
 +      { "RemoveBlob", WPAS_DBUS_NEW_IFACE_INTERFACE,
 +        (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,
 +        {
 +                { "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
++        }
++      },
 +#endif /* CONFIG_WPS */
 +#ifdef CONFIG_P2P
 +      { "Find", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
 +        (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,
 +        {
 +                END_ARGS
 +        }
 +      },
 +      { "Listen", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
 +        (WPADBusMethodHandler) wpas_dbus_handler_p2p_listen,
 +        {
 +                { "timeout", "i", ARG_IN },
 +                END_ARGS
 +        }
 +      },
 +      { "ExtendedListen", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
 +        (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,
 +        {
 +                { "args", "a{sv}", ARG_IN },
 +                END_ARGS
 +        }
 +      },
 +      { "ProvisionDiscoveryRequest", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
 +        (WPADBusMethodHandler) wpas_dbus_handler_p2p_prov_disc_req,
 +        {
 +                { "peer", "o", ARG_IN },
 +                { "config_method", "s", ARG_IN },
 +                END_ARGS
 +        }
 +      },
 +      { "Connect", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
 +        (WPADBusMethodHandler) wpas_dbus_handler_p2p_connect,
 +        {
 +                { "args", "a{sv}", ARG_IN },
 +                { "generated_pin", "s", ARG_OUT },
 +                END_ARGS
 +        }
 +      },
 +      { "GroupAdd", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
 +        (WPADBusMethodHandler) wpas_dbus_handler_p2p_group_add,
 +        {
 +                { "args", "a{sv}", ARG_IN },
 +                END_ARGS
 +        }
 +      },
++      { "Cancel", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
++        (WPADBusMethodHandler) wpas_dbus_handler_p2p_cancel,
++        {
++                END_ARGS
++        }
++      },
 +      { "Invite", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
 +        (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,
 +        {
 +                END_ARGS
 +        }
 +      },
 +      { "RejectPeer", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
 +        (WPADBusMethodHandler) wpas_dbus_handler_p2p_rejectpeer,
 +        {
 +                { "peer", "o", ARG_IN },
 +                END_ARGS
 +        }
 +      },
++      { "RemoveClient", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
++        (WPADBusMethodHandler) wpas_dbus_handler_p2p_remove_client,
++        {
++                { "args", "a{sv}", ARG_IN },
++                END_ARGS
++        }
++      },
 +      { "Flush", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
 +        (WPADBusMethodHandler) wpas_dbus_handler_p2p_flush,
 +        {
 +                END_ARGS
 +        }
 +      },
 +      { "AddService", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
 +        (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,
 +        {
 +                { "args", "a{sv}", ARG_IN },
 +                END_ARGS
 +        }
 +      },
 +      { "FlushService", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
 +        (WPADBusMethodHandler) wpas_dbus_handler_p2p_flush_service,
 +        {
 +                END_ARGS
 +        }
 +      },
 +      { "ServiceDiscoveryRequest", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
 +        (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,
 +        {
 +                { "args", "a{sv}", ARG_IN },
 +                END_ARGS
 +        }
 +      },
 +      { "ServiceDiscoveryCancelRequest", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
 +        (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,
 +        {
 +                { "arg", "i", ARG_IN },
 +                END_ARGS
 +        }
 +      },
 +      { "AddPersistentGroup", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
 +        (WPADBusMethodHandler) wpas_dbus_handler_add_persistent_group,
 +        {
 +                { "args", "a{sv}", ARG_IN },
 +                { "path", "o", ARG_OUT },
 +                END_ARGS
 +        }
 +      },
 +      { "RemovePersistentGroup", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
 +        (WPADBusMethodHandler) wpas_dbus_handler_remove_persistent_group,
 +        {
 +                { "path", "o", ARG_IN },
 +                END_ARGS
 +        }
 +      },
 +      { "RemoveAllPersistentGroups", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
 +        (WPADBusMethodHandler)
 +        wpas_dbus_handler_remove_all_persistent_groups,
 +        {
 +                END_ARGS
 +        }
 +      },
 +#endif /* CONFIG_P2P */
 +      { "FlushBSS", WPAS_DBUS_NEW_IFACE_INTERFACE,
 +        (WPADBusMethodHandler) wpas_dbus_handler_flush_bss,
 +        {
 +                { "age", "u", ARG_IN },
 +                END_ARGS
 +        }
 +      },
 +#ifdef CONFIG_AP
 +      { "SubscribeProbeReq", WPAS_DBUS_NEW_IFACE_INTERFACE,
 +        (WPADBusMethodHandler) wpas_dbus_handler_subscribe_preq,
 +        {
 +                END_ARGS
 +        }
 +      },
 +      { "UnsubscribeProbeReq", WPAS_DBUS_NEW_IFACE_INTERFACE,
 +        (WPADBusMethodHandler) wpas_dbus_handler_unsubscribe_preq,
 +        {
 +                END_ARGS
 +        }
 +      },
 +#endif /* CONFIG_AP */
 +      { "EAPLogoff", WPAS_DBUS_NEW_IFACE_INTERFACE,
 +        (WPADBusMethodHandler) wpas_dbus_handler_eap_logoff,
 +        {
 +                END_ARGS
 +        }
 +      },
 +      { "EAPLogon", WPAS_DBUS_NEW_IFACE_INTERFACE,
 +        (WPADBusMethodHandler) wpas_dbus_handler_eap_logon,
 +        {
 +                END_ARGS
 +        }
 +      },
 +#ifdef CONFIG_AUTOSCAN
 +      { "AutoScan", WPAS_DBUS_NEW_IFACE_INTERFACE,
 +        (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 } }
 +};
 +
 +static const struct wpa_dbus_property_desc wpas_dbus_interface_properties[] = {
 +      { "Capabilities", WPAS_DBUS_NEW_IFACE_INTERFACE, "a{sv}",
 +        wpas_dbus_getter_capabilities,
 +        NULL
 +      },
 +      { "State", WPAS_DBUS_NEW_IFACE_INTERFACE, "s",
 +        wpas_dbus_getter_state,
 +        NULL
 +      },
 +      { "Scanning", WPAS_DBUS_NEW_IFACE_INTERFACE, "b",
 +        wpas_dbus_getter_scanning,
 +        NULL
 +      },
 +      { "ApScan", WPAS_DBUS_NEW_IFACE_INTERFACE, "u",
 +        wpas_dbus_getter_ap_scan,
 +        wpas_dbus_setter_ap_scan
 +      },
 +      { "BSSExpireAge", WPAS_DBUS_NEW_IFACE_INTERFACE, "u",
 +        wpas_dbus_getter_bss_expire_age,
 +        wpas_dbus_setter_bss_expire_age
 +      },
 +      { "BSSExpireCount", WPAS_DBUS_NEW_IFACE_INTERFACE, "u",
 +        wpas_dbus_getter_bss_expire_count,
 +        wpas_dbus_setter_bss_expire_count
 +      },
 +      { "Country", WPAS_DBUS_NEW_IFACE_INTERFACE, "s",
 +        wpas_dbus_getter_country,
 +        wpas_dbus_setter_country
 +      },
 +      { "Ifname", WPAS_DBUS_NEW_IFACE_INTERFACE, "s",
 +        wpas_dbus_getter_ifname,
 +        NULL
 +      },
 +      { "Driver", WPAS_DBUS_NEW_IFACE_INTERFACE, "s",
 +        wpas_dbus_getter_driver,
 +        NULL
 +      },
 +      { "BridgeIfname", WPAS_DBUS_NEW_IFACE_INTERFACE, "s",
 +        wpas_dbus_getter_bridge_ifname,
 +        NULL
 +      },
 +      { "CurrentBSS", WPAS_DBUS_NEW_IFACE_INTERFACE, "o",
 +        wpas_dbus_getter_current_bss,
 +        NULL
 +      },
 +      { "CurrentNetwork", WPAS_DBUS_NEW_IFACE_INTERFACE, "o",
 +        wpas_dbus_getter_current_network,
 +        NULL
 +      },
 +      { "CurrentAuthMode", WPAS_DBUS_NEW_IFACE_INTERFACE, "s",
 +        wpas_dbus_getter_current_auth_mode,
 +        NULL
 +      },
 +      { "Blobs", WPAS_DBUS_NEW_IFACE_INTERFACE, "a{say}",
 +        wpas_dbus_getter_blobs,
 +        NULL
 +      },
 +      { "BSSs", WPAS_DBUS_NEW_IFACE_INTERFACE, "ao",
 +        wpas_dbus_getter_bsss,
 +        NULL
 +      },
 +      { "Networks", WPAS_DBUS_NEW_IFACE_INTERFACE, "ao",
 +        wpas_dbus_getter_networks,
 +        NULL
 +      },
 +      { "FastReauth", WPAS_DBUS_NEW_IFACE_INTERFACE, "b",
 +        wpas_dbus_getter_fast_reauth,
 +        wpas_dbus_setter_fast_reauth
 +      },
 +      { "ScanInterval", WPAS_DBUS_NEW_IFACE_INTERFACE, "i",
 +        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}",
 +        wpas_dbus_getter_p2p_device_config,
 +        wpas_dbus_setter_p2p_device_config
 +      },
 +      { "Peers", WPAS_DBUS_NEW_IFACE_P2PDEVICE, "ao",
 +        wpas_dbus_getter_p2p_peers,
 +        NULL
 +      },
 +      { "Role", WPAS_DBUS_NEW_IFACE_P2PDEVICE, "s",
 +        wpas_dbus_getter_p2p_role,
 +        NULL
 +      },
 +      { "Group", WPAS_DBUS_NEW_IFACE_P2PDEVICE, "o",
 +        wpas_dbus_getter_p2p_group,
 +        NULL
 +      },
 +      { "PeerGO", WPAS_DBUS_NEW_IFACE_P2PDEVICE, "o",
 +        wpas_dbus_getter_p2p_peergo,
 +        NULL
 +      },
 +      { "PersistentGroups", WPAS_DBUS_NEW_IFACE_P2PDEVICE, "ao",
 +        wpas_dbus_getter_persistent_groups,
 +        NULL
 +      },
 +#endif /* CONFIG_P2P */
 +      { "DisconnectReason", WPAS_DBUS_NEW_IFACE_INTERFACE, "i",
 +        wpas_dbus_getter_disconnect_reason,
 +        NULL
 +      },
 +      { NULL, NULL, NULL, NULL, NULL }
 +};
 +
 +static const struct wpa_dbus_signal_desc wpas_dbus_interface_signals[] = {
 +      { "ScanDone", WPAS_DBUS_NEW_IFACE_INTERFACE,
 +        {
 +                { "success", "b", ARG_OUT },
 +                END_ARGS
 +        }
 +      },
 +      { "BSSAdded", WPAS_DBUS_NEW_IFACE_INTERFACE,
 +        {
 +                { "path", "o", ARG_OUT },
 +                { "properties", "a{sv}", ARG_OUT },
 +                END_ARGS
 +        }
 +      },
 +      { "BSSRemoved", WPAS_DBUS_NEW_IFACE_INTERFACE,
 +        {
 +                { "path", "o", ARG_OUT },
 +                END_ARGS
 +        }
 +      },
 +      { "BlobAdded", WPAS_DBUS_NEW_IFACE_INTERFACE,
 +        {
 +                { "name", "s", ARG_OUT },
 +                END_ARGS
 +        }
 +      },
 +      { "BlobRemoved", WPAS_DBUS_NEW_IFACE_INTERFACE,
 +        {
 +                { "name", "s", ARG_OUT },
 +                END_ARGS
 +        }
 +      },
 +      { "NetworkAdded", WPAS_DBUS_NEW_IFACE_INTERFACE,
 +        {
 +                { "path", "o", ARG_OUT },
 +                { "properties", "a{sv}", ARG_OUT },
 +                END_ARGS
 +        }
 +      },
 +      { "NetworkRemoved", WPAS_DBUS_NEW_IFACE_INTERFACE,
 +        {
 +                { "path", "o", ARG_OUT },
 +                END_ARGS
 +        }
 +      },
 +      { "NetworkSelected", WPAS_DBUS_NEW_IFACE_INTERFACE,
 +        {
 +                { "path", "o", ARG_OUT },
 +                END_ARGS
 +        }
 +      },
 +      /* Deprecated: use org.freedesktop.DBus.Properties.PropertiesChanged */
 +      { "PropertiesChanged", WPAS_DBUS_NEW_IFACE_INTERFACE,
 +        {
 +                { "properties", "a{sv}", ARG_OUT },
 +                END_ARGS
 +        }
 +      },
 +#ifdef CONFIG_WPS
 +      { "Event", WPAS_DBUS_NEW_IFACE_WPS,
 +        {
 +                { "name", "s", ARG_OUT },
 +                { "args", "a{sv}", ARG_OUT },
 +                END_ARGS
 +        }
 +      },
 +      { "Credentials", WPAS_DBUS_NEW_IFACE_WPS,
 +        {
 +                { "credentials", "a{sv}", ARG_OUT },
 +                END_ARGS
 +        }
 +      },
 +      /* Deprecated: use org.freedesktop.DBus.Properties.PropertiesChanged */
 +      { "PropertiesChanged", WPAS_DBUS_NEW_IFACE_WPS,
 +        {
 +                { "properties", "a{sv}", ARG_OUT },
 +                END_ARGS
 +        }
 +      },
 +#endif /* CONFIG_WPS */
 +#ifdef CONFIG_P2P
 +      { "DeviceFound", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
 +        {
 +                { "path", "o", ARG_OUT },
 +                END_ARGS
 +        }
 +      },
 +      { "DeviceLost", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
 +        {
 +                { "path", "o", ARG_OUT },
 +                END_ARGS
 +        }
 +      },
++      { "FindStopped", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
++        {
++                END_ARGS
++        }
++      },
 +      { "ProvisionDiscoveryRequestDisplayPin", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
 +        {
 +                { "peer_object", "o", ARG_OUT },
 +                { "pin", "s", ARG_OUT },
 +                END_ARGS
 +        }
 +      },
 +      { "ProvisionDiscoveryResponseDisplayPin", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
 +        {
 +                { "peer_object", "o", ARG_OUT },
 +                { "pin", "s", ARG_OUT },
 +                END_ARGS
 +        }
 +      },
 +      { "ProvisionDiscoveryRequestEnterPin", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
 +        {
 +                { "peer_object", "o", ARG_OUT },
 +                END_ARGS
 +        }
 +      },
 +      { "ProvisionDiscoveryResponseEnterPin", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
 +        {
 +                { "peer_object", "o", ARG_OUT },
 +                END_ARGS
 +        }
 +      },
 +      { "ProvisionDiscoveryPBCRequest", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
 +        {
 +                { "peer_object", "o", ARG_OUT },
 +                END_ARGS
 +        }
 +      },
 +      { "ProvisionDiscoveryPBCResponse", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
 +        {
 +                { "peer_object", "o", ARG_OUT },
 +                END_ARGS
 +        }
 +      },
 +      { "ProvisionDiscoveryFailure", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
 +        {
 +                { "peer_object", "o", ARG_OUT },
 +                { "status", "i", ARG_OUT },
 +                END_ARGS
 +        }
 +      },
 +      { "GroupStarted", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
 +        {
 +                { "properties", "a{sv}", ARG_OUT },
 +                END_ARGS
 +        }
 +      },
++      { "GroupFormationFailure", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
++        {
++                { "reason", "s", ARG_OUT },
++                END_ARGS
++        }
++      },
 +      { "GONegotiationSuccess", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
 +        {
 +                { "properties", "a{sv}", ARG_OUT },
 +                END_ARGS
 +        }
 +      },
 +      { "GONegotiationFailure", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
 +        {
 +                { "properties", "a{sv}", ARG_OUT },
 +                END_ARGS
 +        }
 +      },
 +      { "GONegotiationRequest", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
 +        {
 +                { "path", "o", ARG_OUT },
-       if (iface == NULL)
++                { "dev_passwd_id", "q", ARG_OUT },
++                { "device_go_intent", "y", ARG_OUT },
 +                END_ARGS
 +        }
 +      },
 +      { "InvitationResult", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
 +        {
 +                { "invite_result", "a{sv}", ARG_OUT },
 +                END_ARGS
 +        }
 +      },
 +      { "GroupFinished", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
 +        {
 +                { "properties", "a{sv}", ARG_OUT },
 +                END_ARGS
 +        }
 +      },
 +      { "ServiceDiscoveryRequest", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
 +        {
 +                { "sd_request", "a{sv}", ARG_OUT },
 +                END_ARGS
 +        }
 +      },
 +      { "ServiceDiscoveryResponse", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
 +        {
 +                { "sd_response", "a{sv}", ARG_OUT },
 +                END_ARGS
 +        }
 +      },
 +      { "PersistentGroupAdded", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
 +        {
 +                { "path", "o", ARG_OUT },
 +                { "properties", "a{sv}", ARG_OUT },
 +                END_ARGS
 +        }
 +      },
 +      { "PersistentGroupRemoved", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
 +        {
 +                { "path", "o", ARG_OUT },
 +                END_ARGS
 +        }
 +      },
 +      { "WpsFailed", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
 +        {
 +                { "name", "s", ARG_OUT },
 +                { "args", "a{sv}", ARG_OUT },
 +                END_ARGS
 +        }
 +      },
++      { "InvitationReceived", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
++        {
++                { "properties", "a{sv}", ARG_OUT },
++                END_ARGS
++        }
++      },
 +#endif /* CONFIG_P2P */
 +#ifdef CONFIG_AP
 +      { "ProbeRequest", WPAS_DBUS_NEW_IFACE_INTERFACE,
 +        {
 +                { "args", "a{sv}", ARG_OUT },
 +                END_ARGS
 +        }
 +      },
 +#endif /* CONFIG_AP */
 +      { "Certification", WPAS_DBUS_NEW_IFACE_INTERFACE,
 +        {
 +                { "certification", "a{sv}", ARG_OUT },
 +                END_ARGS
 +        }
 +      },
 +      { "EAP", WPAS_DBUS_NEW_IFACE_INTERFACE,
 +        {
 +                { "status", "s", ARG_OUT },
 +                { "parameter", "s", ARG_OUT },
 +                END_ARGS
 +        }
 +      },
 +      { "StaAuthorized", WPAS_DBUS_NEW_IFACE_INTERFACE,
 +        {
 +                { "name", "s", ARG_OUT },
 +                END_ARGS
 +        }
 +      },
 +      { "StaDeauthorized", WPAS_DBUS_NEW_IFACE_INTERFACE,
 +        {
 +                { "name", "s", ARG_OUT },
 +                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 } }
 +};
 +
 +
++/**
++ * wpas_dbus_register_interface - Register an interface with D-Bus
++ * @wpa_s: wpa_supplicant interface structure
++ * Returns: 0 on success, -1 on failure
++ */
 +int wpas_dbus_register_interface(struct wpa_supplicant *wpa_s)
 +{
 +
 +      struct wpa_dbus_object_desc *obj_desc = NULL;
 +      struct wpas_dbus_priv *ctrl_iface = wpa_s->global->dbus;
 +      int next;
 +
 +      /* Do nothing if the control interface is not turned on */
 +      if (ctrl_iface == NULL)
 +              return 0;
 +
 +      /* Create and set the interface's object path */
 +      wpa_s->dbus_new_path = os_zalloc(WPAS_DBUS_OBJECT_PATH_MAX);
 +      if (wpa_s->dbus_new_path == NULL)
 +              return -1;
 +      next = ctrl_iface->next_objid++;
 +      os_snprintf(wpa_s->dbus_new_path, WPAS_DBUS_OBJECT_PATH_MAX,
 +                  WPAS_DBUS_NEW_PATH_INTERFACES "/%u",
 +                  next);
 +
 +      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;
 +      }
 +
 +      wpas_dbus_register(obj_desc, wpa_s, NULL, wpas_dbus_interface_methods,
 +                         wpas_dbus_interface_properties,
 +                         wpas_dbus_interface_signals);
 +
 +      wpa_printf(MSG_DEBUG, "dbus: Register interface object '%s'",
 +                 wpa_s->dbus_new_path);
 +      if (wpa_dbus_register_object_per_iface(ctrl_iface,
 +                                             wpa_s->dbus_new_path,
 +                                             wpa_s->ifname, obj_desc))
 +              goto err;
 +
 +      wpas_dbus_signal_interface_added(wpa_s);
 +
 +      return 0;
 +
 +err:
 +      os_free(wpa_s->dbus_new_path);
 +      wpa_s->dbus_new_path = NULL;
 +      free_dbus_object_desc(obj_desc);
 +      return -1;
 +}
 +
 +
++/**
++ * wpas_dbus_unregister_interface - Unregister the interface from D-Bus
++ * @wpa_s: wpa_supplicant interface structure
++ * Returns: 0 on success, -1 on failure
++ */
 +int wpas_dbus_unregister_interface(struct wpa_supplicant *wpa_s)
 +{
 +      struct wpas_dbus_priv *ctrl_iface;
 +
 +      /* Do nothing if the control interface is not turned on */
 +      if (wpa_s == NULL || wpa_s->global == NULL)
 +              return 0;
 +      ctrl_iface = wpa_s->global->dbus;
 +      if (ctrl_iface == NULL || wpa_s->dbus_new_path == NULL)
 +              return 0;
 +
 +      wpa_printf(MSG_DEBUG, "dbus: Unregister interface object '%s'",
 +                 wpa_s->dbus_new_path);
 +
 +#ifdef CONFIG_AP
 +      if (wpa_s->preq_notify_peer) {
 +              wpas_dbus_unsubscribe_noc(ctrl_iface);
 +              os_free(wpa_s->preq_notify_peer);
 +              wpa_s->preq_notify_peer = NULL;
 +      }
 +#endif /* CONFIG_AP */
 +
 +      if (wpa_dbus_unregister_object_per_iface(ctrl_iface,
 +                                               wpa_s->dbus_new_path))
 +              return -1;
 +
 +      wpas_dbus_signal_interface_removed(wpa_s);
 +
 +      os_free(wpa_s->dbus_new_path);
 +      wpa_s->dbus_new_path = NULL;
 +
 +      return 0;
 +}
 +
 +#ifdef CONFIG_P2P
 +
 +static const struct wpa_dbus_property_desc wpas_dbus_p2p_peer_properties[] = {
 +      { "DeviceName", WPAS_DBUS_NEW_IFACE_P2P_PEER, "s",
 +        wpas_dbus_getter_p2p_peer_device_name,
 +        NULL
 +      },
++      { "Manufacturer", WPAS_DBUS_NEW_IFACE_P2P_PEER, "s",
++        wpas_dbus_getter_p2p_peer_manufacturer,
++        NULL
++      },
++      { "ModelName", WPAS_DBUS_NEW_IFACE_P2P_PEER, "s",
++        wpas_dbus_getter_p2p_peer_modelname,
++        NULL
++      },
++      { "ModelNumber", WPAS_DBUS_NEW_IFACE_P2P_PEER, "s",
++        wpas_dbus_getter_p2p_peer_modelnumber,
++        NULL
++      },
++      { "SerialNumber", WPAS_DBUS_NEW_IFACE_P2P_PEER, "s",
++        wpas_dbus_getter_p2p_peer_serialnumber,
++        NULL
++      },
 +      { "PrimaryDeviceType", WPAS_DBUS_NEW_IFACE_P2P_PEER, "ay",
 +        wpas_dbus_getter_p2p_peer_primary_device_type,
 +        NULL
 +      },
 +      { "config_method", WPAS_DBUS_NEW_IFACE_P2P_PEER, "q",
 +        wpas_dbus_getter_p2p_peer_config_method,
 +        NULL
 +      },
 +      { "level", WPAS_DBUS_NEW_IFACE_P2P_PEER, "i",
 +        wpas_dbus_getter_p2p_peer_level,
 +        NULL
 +      },
 +      { "devicecapability", WPAS_DBUS_NEW_IFACE_P2P_PEER, "y",
 +        wpas_dbus_getter_p2p_peer_device_capability,
 +        NULL
 +      },
 +      { "groupcapability", WPAS_DBUS_NEW_IFACE_P2P_PEER, "y",
 +        wpas_dbus_getter_p2p_peer_group_capability,
 +        NULL
 +      },
 +      { "SecondaryDeviceTypes", WPAS_DBUS_NEW_IFACE_P2P_PEER, "aay",
 +        wpas_dbus_getter_p2p_peer_secondary_device_types,
 +        NULL
 +      },
 +      { "VendorExtension", WPAS_DBUS_NEW_IFACE_P2P_PEER, "aay",
 +        wpas_dbus_getter_p2p_peer_vendor_extension,
 +        NULL
 +      },
 +      { "IEs", WPAS_DBUS_NEW_IFACE_P2P_PEER, "ay",
 +        wpas_dbus_getter_p2p_peer_ies,
 +        NULL
 +      },
 +      { "DeviceAddress", WPAS_DBUS_NEW_IFACE_P2P_PEER, "ay",
 +        wpas_dbus_getter_p2p_peer_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 } }
 +};
 +
 +/**
 + * wpas_dbus_signal_peer - Send a peer related event signal
 + * @wpa_s: %wpa_supplicant network interface data
 + * @dev: peer device object
 + * @interface: name of the interface emitting this signal.
 + *    In case of peer objects, it would be emitted by either
 + *    the "interface object" or by "peer objects"
 + * @sig_name: signal name - DeviceFound
 + *
 + * Notify listeners about event related with newly found p2p peer device
 + */
 +static void wpas_dbus_signal_peer(struct wpa_supplicant *wpa_s,
 +                                const u8 *dev_addr, const char *interface,
 +                                const char *sig_name)
 +{
 +      struct wpas_dbus_priv *iface;
 +      DBusMessage *msg;
 +      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 */
-  * @dev: peer device object
++      if (iface == NULL || !wpa_s->dbus_new_path)
 +              return;
 +
 +      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));
 +
 +      msg = dbus_message_new_signal(wpa_s->dbus_new_path, interface,
 +                                    sig_name);
 +      if (msg == NULL)
 +              return;
 +
 +      dbus_message_iter_init_append(msg, &iter);
 +      path = peer_obj_path;
 +      if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH,
 +                                          &path))
 +              wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
 +      else
 +              dbus_connection_send(iface->con, msg, NULL);
 +
 +      dbus_message_unref(msg);
 +}
 +
 +
 +/**
 + * wpas_dbus_signal_peer_found - Send a peer found signal
 + * @wpa_s: %wpa_supplicant network interface data
-  * @dev: peer device object
++ * @dev_addr: Peer P2P Device Address
 + *
 + * Notify listeners about find a p2p peer device found
 + */
 +void wpas_dbus_signal_peer_device_found(struct wpa_supplicant *wpa_s,
 +                                      const u8 *dev_addr)
 +{
 +      wpas_dbus_signal_peer(wpa_s, dev_addr,
 +                            WPAS_DBUS_NEW_IFACE_P2PDEVICE,
 +                            "DeviceFound");
 +}
 +
 +/**
 + * wpas_dbus_signal_peer_lost - Send a peer lost signal
 + * @wpa_s: %wpa_supplicant network interface data
-  * @ssid: network configuration data
++ * @dev_addr: Peer P2P Device Address
 + *
 + * Notify listeners about lost a p2p peer device
 + */
 +void wpas_dbus_signal_peer_device_lost(struct wpa_supplicant *wpa_s,
 +                                     const u8 *dev_addr)
 +{
 +      wpas_dbus_signal_peer(wpa_s, dev_addr,
 +                            WPAS_DBUS_NEW_IFACE_P2PDEVICE,
 +                            "DeviceLost");
 +}
 +
 +/**
 + * wpas_dbus_register_peer - Register a discovered peer object with dbus
 + * @wpa_s: wpa_supplicant interface structure
-       if (wpa_s->p2p_mgmt)
-               wpa_s = wpa_s->parent;
++ * @dev_addr: P2P Device Address of the peer
 + * Returns: 0 on success, -1 on failure
 + *
 + * Registers network representing object with dbus
 + */
 +int wpas_dbus_register_peer(struct wpa_supplicant *wpa_s, const u8 *dev_addr)
 +{
 +      struct wpas_dbus_priv *ctrl_iface;
 +      struct wpa_dbus_object_desc *obj_desc;
 +      struct peer_handler_args *arg;
 +      char peer_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 0;
 +
 +      ctrl_iface = wpa_s->global->dbus;
 +      if (ctrl_iface == NULL)
 +              return 0;
 +
-       if (wpa_s == NULL || wpa_s->global == NULL ||
-           wpa_s->dbus_new_path == NULL)
++      wpa_s = wpa_s->parent->parent;
++      if (!wpa_s->dbus_new_path)
++              return 0;
 +
 +      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_printf(MSG_INFO, "dbus: Register peer object '%s'",
 +                 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");
 +              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");
 +              goto err;
 +      }
 +
 +      arg->wpa_s = wpa_s;
 +      os_memcpy(arg->p2p_device_addr, dev_addr, ETH_ALEN);
 +
 +      wpas_dbus_register(obj_desc, arg, wpa_dbus_free,
 +                         NULL,
 +                         wpas_dbus_p2p_peer_properties,
 +                         wpas_dbus_p2p_peer_signals);
 +
 +      if (wpa_dbus_register_object_per_iface(ctrl_iface, peer_obj_path,
 +                                             wpa_s->ifname, obj_desc))
 +              goto err;
 +
 +      return 0;
 +
 +err:
 +      free_dbus_object_desc(obj_desc);
 +      return -1;
 +}
 +
 +/**
 + * wpas_dbus_unregister_peer - Unregister a peer object with dbus
 + * @wpa_s: wpa_supplicant interface structure
 + * @dev_addr: p2p device addr
 + * Returns: 0 on success, -1 on failure
 + *
 + * Registers network representing object with dbus
 + */
 +int wpas_dbus_unregister_peer(struct wpa_supplicant *wpa_s,
 +                                const u8 *dev_addr)
 +{
 +      struct wpas_dbus_priv *ctrl_iface;
 +      char peer_obj_path[WPAS_DBUS_OBJECT_PATH_MAX];
 +      int ret;
 +
 +      /* Do nothing if the control interface is not turned on */
-       if (wpa_s->p2p_mgmt)
-               wpa_s = wpa_s->parent;
++      if (wpa_s == NULL || wpa_s->global == NULL)
 +              return 0;
 +
-       if (wpa_s == NULL || wpa_s->global == NULL ||
-           wpa_s->dbus_new_path == NULL)
++      wpa_s = wpa_s->parent->parent;
++      if (!wpa_s->dbus_new_path)
++              return 0;
 +
 +      ctrl_iface = wpa_s->global->dbus;
 +      if (ctrl_iface == NULL)
 +              return 0;
 +
 +      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_printf(MSG_INFO, "dbus: Unregister peer object '%s'",
 +                 peer_obj_path);
 +      ret = wpa_dbus_unregister_object_per_iface(ctrl_iface, peer_obj_path);
 +
 +      return ret;
 +}
 +
 +
++/**
++ * wpas_dbus_signal_p2p_find_stopped - Send P2P Find stopped signal
++ * @wpa_s: %wpa_supplicant network interface data
++ *
++ * Notify listeners about P2P Find stopped
++ */
++void wpas_dbus_signal_p2p_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 || !wpa_s->dbus_new_path)
++              return;
++
++      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_peer_groups_changed - Send peer group change property signal
++ * @wpa_s: %wpa_supplicant network interface data
++ * @dev_addr: P2P Device Address
++ *
++ * Notify listeners about peer Groups property changes.
++ */
 +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;
 +
++      if (!wpa_s->dbus_new_path)
++              return;
 +      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,
 +        NULL
 +      },
 +      { "Group", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "o",
 +        wpas_dbus_getter_p2p_group,
 +        NULL
 +      },
 +      { "Role", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "s",
 +        wpas_dbus_getter_p2p_role,
 +        NULL
 +      },
 +      { "SSID", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "ay",
 +        wpas_dbus_getter_p2p_group_ssid,
 +        NULL
 +      },
 +      { "BSSID", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "ay",
 +        wpas_dbus_getter_p2p_group_bssid,
 +        NULL
 +      },
 +      { "Frequency", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "q",
 +        wpas_dbus_getter_p2p_group_frequency,
 +        NULL
 +      },
 +      { "Passphrase", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "s",
 +        wpas_dbus_getter_p2p_group_passphrase,
 +        NULL
 +      },
 +      { "PSK", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "ay",
 +        wpas_dbus_getter_p2p_group_psk,
 +        NULL
 +      },
 +      { "WPSVendorExtensions", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "aay",
 +        wpas_dbus_getter_p2p_group_vendor_ext,
 +        wpas_dbus_setter_p2p_group_vendor_ext
 +      },
 +      { NULL, NULL, NULL, NULL, NULL }
 +};
 +
 +static const struct wpa_dbus_signal_desc wpas_dbus_p2p_group_signals[] = {
 +      { "PeerJoined", WPAS_DBUS_NEW_IFACE_P2P_GROUP,
 +        {
 +                { "peer", "o", ARG_OUT },
 +                END_ARGS
 +        }
 +      },
 +      { "PeerDisconnected", WPAS_DBUS_NEW_IFACE_P2P_GROUP,
 +        {
 +                { "peer", "o", ARG_OUT },
 +                END_ARGS
 +        }
 +      },
 +      { NULL, NULL, { END_ARGS } }
 +};
 +
 +/**
 + * wpas_dbus_register_p2p_group - Register a p2p group object with dbus
 + * @wpa_s: wpa_supplicant interface structure
 + * @ssid: SSID struct
 + * Returns: 0 on success, -1 on failure
 + *
 + * Registers p2p group representing object with dbus
 + */
 +void wpas_dbus_register_p2p_group(struct wpa_supplicant *wpa_s,
 +                                struct wpa_ssid *ssid)
 +{
 +      struct wpas_dbus_priv *ctrl_iface;
 +      struct wpa_dbus_object_desc *obj_desc;
 +      char group_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) {
 +              wpa_printf(MSG_INFO, "%s: Group object '%s' already exists",
 +                         __func__, wpa_s->dbus_groupobj_path);
 +              return;
 +      }
 +
 +      if (wpas_dbus_get_group_obj_path(wpa_s, ssid, group_obj_path) < 0)
 +              return;
 +
 +      wpa_s->dbus_groupobj_path = os_strdup(group_obj_path);
 +      if (wpa_s->dbus_groupobj_path == NULL)
 +              return;
 +
 +      wpa_printf(MSG_INFO, "dbus: Register group object '%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");
 +              goto err;
 +      }
 +
 +      wpas_dbus_register(obj_desc, wpa_s, NULL, NULL,
 +                         wpas_dbus_p2p_group_properties,
 +                         wpas_dbus_p2p_group_signals);
 +
 +      if (wpa_dbus_register_object_per_iface(ctrl_iface, group_obj_path,
 +                                             wpa_s->ifname, obj_desc))
 +              goto err;
 +
 +      return;
 +
 +err:
 +      if (wpa_s->dbus_groupobj_path) {
 +              os_free(wpa_s->dbus_groupobj_path);
 +              wpa_s->dbus_groupobj_path = NULL;
 +      }
 +
 +      free_dbus_object_desc(obj_desc);
 +}
 +
 +/**
 + * wpas_dbus_unregister_p2p_group - Unregister a p2p group object from dbus
 + * @wpa_s: wpa_supplicant interface structure
 + * @ssid: network name of the p2p group started
 + */
 +void wpas_dbus_unregister_p2p_group(struct wpa_supplicant *wpa_s,
 +                                  const struct wpa_ssid *ssid)
 +{
 +      struct wpas_dbus_priv *ctrl_iface;
 +
 +      /* Do nothing if the control interface is not turned on */
 +      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;
 +
 +      if (!wpa_s->dbus_groupobj_path) {
 +              wpa_printf(MSG_DEBUG,
 +                         "%s: Group object '%s' already unregistered",
 +                         __func__, wpa_s->dbus_groupobj_path);
 +              return;
 +      }
 +
 +      peer_groups_changed(wpa_s);
 +
 +      wpa_printf(MSG_DEBUG, "dbus: Unregister group object '%s'",
 +                 wpa_s->dbus_groupobj_path);
 +
 +      wpa_dbus_unregister_object_per_iface(ctrl_iface,
 +                                           wpa_s->dbus_groupobj_path);
 +
 +      os_free(wpa_s->dbus_groupobj_path);
 +      wpa_s->dbus_groupobj_path = NULL;
 +}
 +
 +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,
 +        wpas_dbus_setter_persistent_group_properties
 +      },
 +      { NULL, NULL, NULL, NULL, NULL }
 +};
 +
 +/* No signals intended for persistent group objects */
 +
 +/**
 + * wpas_dbus_register_persistent_group - Register a configured(saved)
 + *    persistent group with dbus
 + * @wpa_s: wpa_supplicant interface structure
 + * @ssid: persistent group (still represented as a network within wpa)
 + *      configuration data
 + * Returns: 0 on success, -1 on failure
 + *
 + * Registers a persistent group representing object with dbus.
 + */
 +int wpas_dbus_register_persistent_group(struct wpa_supplicant *wpa_s,
 +                                      struct wpa_ssid *ssid)
 +{
 +      struct wpas_dbus_priv *ctrl_iface;
 +      struct wpa_dbus_object_desc *obj_desc;
 +      struct network_handler_args *arg;
 +      char pgrp_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 0;
++      wpa_s = wpa_s->parent->parent;
++      if (!wpa_s->dbus_new_path)
++              return 0;
 +
 +      /* Make sure ssid is a persistent group */
 +      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;
 +
 +      /*
 +       * Intentionally not coming up with different numbering scheme
 +       * for persistent groups.
 +       */
 +      os_snprintf(pgrp_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
 +                  "%s/" WPAS_DBUS_NEW_PERSISTENT_GROUPS_PART "/%u",
 +                  wpa_s->dbus_new_path, ssid->id);
 +
 +      wpa_printf(MSG_DEBUG, "dbus: Register persistent group object '%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");
 +              goto err;
 +      }
 +
 +      /*
 +       * Reusing the same context structure as that for networks
 +       * since these are represented using same data structure.
 +       */
 +      /* 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");
 +              goto err;
 +      }
 +
 +      arg->wpa_s = wpa_s;
 +      arg->ssid = ssid;
 +
 +      wpas_dbus_register(obj_desc, arg, wpa_dbus_free, NULL,
 +                         wpas_dbus_persistent_group_properties,
 +                         NULL);
 +
 +      if (wpa_dbus_register_object_per_iface(ctrl_iface, pgrp_obj_path,
 +                                             wpa_s->ifname, obj_desc))
 +              goto err;
 +
 +      wpas_dbus_signal_persistent_group_added(wpa_s, ssid->id);
 +
 +      return 0;
 +
 +err:
 +      free_dbus_object_desc(obj_desc);
 +      return -1;
 +}
 +
 +
 +/**
 + * wpas_dbus_unregister_persistent_group - Unregister a persistent_group
 + *    from dbus
 + * @wpa_s: wpa_supplicant interface structure
 + * @nid: network id
 + * Returns: 0 on success, -1 on failure
 + *
 + * Unregisters persistent group representing object from dbus
 + *
 + * NOTE: There is a slight issue with the semantics here. While the
 + * implementation simply means the persistent group is unloaded from memory,
 + * it should not get interpreted as the group is actually being erased/removed
 + * from persistent storage as well.
 + */
 +int wpas_dbus_unregister_persistent_group(struct wpa_supplicant *wpa_s,
 +                                        int nid)
 +{
 +      struct wpas_dbus_priv *ctrl_iface;
 +      char pgrp_obj_path[WPAS_DBUS_OBJECT_PATH_MAX];
 +      int ret;
 +
 +      /* Do nothing if the control interface is not turned on */
-       if (wpa_s->p2p_mgmt)
-               wpa_s = wpa_s->parent;
++      if (wpa_s == NULL || wpa_s->global == NULL)
 +              return 0;
 +
-       if (ctrl_iface == NULL)
++      wpa_s = wpa_s->parent->parent;
 +
 +      ctrl_iface = wpa_s->global->dbus;
++      if (ctrl_iface == NULL || !wpa_s->dbus_new_path)
 +              return 0;
 +
 +      os_snprintf(pgrp_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
 +                  "%s/" WPAS_DBUS_NEW_PERSISTENT_GROUPS_PART "/%u",
 +                  wpa_s->dbus_new_path, nid);
 +
 +      wpa_printf(MSG_DEBUG, "dbus: Unregister persistent group object '%s'",
 +                 pgrp_obj_path);
 +      ret = wpa_dbus_unregister_object_per_iface(ctrl_iface, pgrp_obj_path);
 +
 +      if (!ret)
 +              wpas_dbus_signal_persistent_group_removed(wpa_s, nid);
 +
 +      return ret;
 +}
 +
 +#endif /* CONFIG_P2P */
index d162d2b663df61b4cdfd8ee1aa13df3280f74088,0000000000000000000000000000000000000000..6d240fffce786fecb79b2fb57d7d81a07fc9cd8c
mode 100644,000000..100644
--- /dev/null
@@@ -1,524 -1,0 +1,557 @@@
-                                    const u8 *src, u16 dev_passwd_id);
 +/*
 + * 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>
 + *
 + * This software may be distributed under the terms of the BSD license.
 + * See README for more details.
 + */
 +
 +#ifndef CTRL_IFACE_DBUS_NEW_H
 +#define CTRL_IFACE_DBUS_NEW_H
 +
 +#include "common/defs.h"
 +#include "p2p/p2p.h"
 +
 +struct wpa_global;
 +struct wpa_supplicant;
 +struct wpa_ssid;
 +struct wps_event_m2d;
 +struct wps_event_fail;
 +struct wps_credential;
 +
 +enum wpas_dbus_prop {
 +      WPAS_DBUS_PROP_AP_SCAN,
 +      WPAS_DBUS_PROP_SCANNING,
 +      WPAS_DBUS_PROP_STATE,
 +      WPAS_DBUS_PROP_CURRENT_BSS,
 +      WPAS_DBUS_PROP_CURRENT_NETWORK,
 +      WPAS_DBUS_PROP_CURRENT_AUTH_MODE,
 +      WPAS_DBUS_PROP_BSSS,
 +      WPAS_DBUS_PROP_DISCONNECT_REASON,
 +};
 +
 +enum wpas_dbus_bss_prop {
 +      WPAS_DBUS_BSS_PROP_SIGNAL,
 +      WPAS_DBUS_BSS_PROP_FREQ,
 +      WPAS_DBUS_BSS_PROP_MODE,
 +      WPAS_DBUS_BSS_PROP_PRIVACY,
 +      WPAS_DBUS_BSS_PROP_RATES,
 +      WPAS_DBUS_BSS_PROP_WPA,
 +      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
 +
 +#define WPAS_DBUS_NEW_SERVICE         "fi.w1.wpa_supplicant1"
 +#define WPAS_DBUS_NEW_PATH            "/fi/w1/wpa_supplicant1"
 +#define WPAS_DBUS_NEW_INTERFACE               "fi.w1.wpa_supplicant1"
 +
 +#define WPAS_DBUS_NEW_PATH_INTERFACES WPAS_DBUS_NEW_PATH "/Interfaces"
 +#define WPAS_DBUS_NEW_IFACE_INTERFACE WPAS_DBUS_NEW_INTERFACE ".Interface"
 +#define WPAS_DBUS_NEW_IFACE_WPS WPAS_DBUS_NEW_IFACE_INTERFACE ".WPS"
 +
 +#define WPAS_DBUS_NEW_NETWORKS_PART "Networks"
 +#define WPAS_DBUS_NEW_IFACE_NETWORK WPAS_DBUS_NEW_INTERFACE ".Network"
 +
 +#define WPAS_DBUS_NEW_BSSIDS_PART "BSSs"
 +#define WPAS_DBUS_NEW_IFACE_BSS       WPAS_DBUS_NEW_INTERFACE ".BSS"
 +
 +#define WPAS_DBUS_NEW_IFACE_P2PDEVICE \
 +              WPAS_DBUS_NEW_IFACE_INTERFACE ".P2PDevice"
 +
 +/*
 + * Groups correspond to P2P groups where this device is a GO (owner)
 + */
 +#define WPAS_DBUS_NEW_P2P_GROUPS_PART "Groups"
 +#define       WPAS_DBUS_NEW_IFACE_P2P_GROUP WPAS_DBUS_NEW_INTERFACE ".Group"
 +
 +/*
 + * Different dbus object for persistent groups so they do not get confused
 + * with regular (configured) network objects.
 + */
 +#define WPAS_DBUS_NEW_PERSISTENT_GROUPS_PART "PersistentGroups"
 +#define WPAS_DBUS_NEW_IFACE_PERSISTENT_GROUP \
 +      WPAS_DBUS_NEW_INTERFACE ".PersistentGroup"
 +
 +#define WPAS_DBUS_NEW_P2P_PEERS_PART  "Peers"
 +#define       WPAS_DBUS_NEW_IFACE_P2P_PEER WPAS_DBUS_NEW_INTERFACE ".Peer"
 +
 +/* Top-level Errors */
 +#define WPAS_DBUS_ERROR_UNKNOWN_ERROR \
 +      WPAS_DBUS_NEW_INTERFACE ".UnknownError"
 +#define WPAS_DBUS_ERROR_INVALID_ARGS \
 +      WPAS_DBUS_NEW_INTERFACE ".InvalidArgs"
 +
 +#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"
 +
 +#define WPAS_DBUS_ERROR_NOT_CONNECTED \
 +      WPAS_DBUS_NEW_INTERFACE ".NotConnected"
 +#define WPAS_DBUS_ERROR_NETWORK_UNKNOWN \
 +      WPAS_DBUS_NEW_INTERFACE ".NetworkUnknown"
 +
 +#define WPAS_DBUS_ERROR_CONNECT_CHANNEL_UNAVAILABLE \
 +      WPAS_DBUS_NEW_INTERFACE ".ConnectChannelUnavailable"
 +#define WPAS_DBUS_ERROR_CONNECT_CHANNEL_UNSUPPORTED \
 +      WPAS_DBUS_NEW_INTERFACE ".ConnectChannelUnsupported"
 +#define WPAS_DBUS_ERROR_CONNECT_UNSPECIFIED_ERROR \
 +      WPAS_DBUS_NEW_INTERFACE ".ConnectUnspecifiedError"
 +
 +#define WPAS_DBUS_ERROR_BLOB_EXISTS \
 +      WPAS_DBUS_NEW_INTERFACE ".BlobExists"
 +#define WPAS_DBUS_ERROR_BLOB_UNKNOWN \
 +      WPAS_DBUS_NEW_INTERFACE ".BlobUnknown"
 +
 +#define WPAS_DBUS_ERROR_SUBSCRIPTION_IN_USE \
 +      WPAS_DBUS_NEW_INTERFACE ".SubscriptionInUse"
 +#define WPAS_DBUS_ERROR_NO_SUBSCRIPTION \
 +      WPAS_DBUS_NEW_INTERFACE ".NoSubscription"
 +#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);
 +
 +
 +#ifdef CONFIG_CTRL_IFACE_DBUS_NEW
 +
 +int wpas_dbus_ctrl_iface_init(struct wpas_dbus_priv *priv);
 +void wpas_dbus_ctrl_iface_deinit(struct wpas_dbus_priv *iface);
 +
 +int wpas_dbus_register_interface(struct wpa_supplicant *wpa_s);
 +int wpas_dbus_unregister_interface(struct wpa_supplicant *wpa_s);
 +void wpas_dbus_signal_prop_changed(struct wpa_supplicant *wpa_s,
 +                                 enum wpas_dbus_prop property);
 +void wpas_dbus_bss_signal_prop_changed(struct wpa_supplicant *wpa_s,
 +                                     enum wpas_dbus_bss_prop property,
 +                                     unsigned int id);
 +void wpas_dbus_signal_network_enabled_changed(struct wpa_supplicant *wpa_s,
 +                                            struct wpa_ssid *ssid);
 +void wpas_dbus_signal_network_selected(struct wpa_supplicant *wpa_s, int id);
 +void wpas_dbus_signal_network_request(struct wpa_supplicant *wpa_s,
 +                                    struct wpa_ssid *ssid,
 +                                    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_wps_cred(struct wpa_supplicant *wpa_s,
 +                             const struct wps_credential *cred);
 +void wpas_dbus_signal_wps_event_m2d(struct wpa_supplicant *wpa_s,
 +                                  struct wps_event_m2d *m2d);
 +void wpas_dbus_signal_wps_event_fail(struct wpa_supplicant *wpa_s,
 +                                   struct wps_event_fail *fail);
 +void wpas_dbus_signal_wps_event_success(struct wpa_supplicant *wpa_s);
++void wpas_dbus_signal_wps_event_pbc_overlap(struct wpa_supplicant *wpa_s);
 +int wpas_dbus_register_network(struct wpa_supplicant *wpa_s,
 +                             struct wpa_ssid *ssid);
 +int wpas_dbus_unregister_network(struct wpa_supplicant *wpa_s, int nid);
 +int wpas_dbus_unregister_bss(struct wpa_supplicant *wpa_s,
 +                           u8 bssid[ETH_ALEN], unsigned int id);
 +int wpas_dbus_register_bss(struct wpa_supplicant *wpa_s,
 +                         u8 bssid[ETH_ALEN], unsigned int id);
 +void wpas_dbus_signal_blob_added(struct wpa_supplicant *wpa_s,
 +                               const char *name);
 +void wpas_dbus_signal_blob_removed(struct wpa_supplicant *wpa_s,
 +                                 const char *name);
 +void wpas_dbus_signal_debug_level_changed(struct wpa_global *global);
 +void wpas_dbus_signal_debug_timestamp_changed(struct wpa_global *global);
 +void wpas_dbus_signal_debug_show_keys_changed(struct wpa_global *global);
 +
 +int wpas_dbus_register_peer(struct wpa_supplicant *wpa_s, const u8 *dev_addr);
++void wpas_dbus_signal_p2p_find_stopped(struct wpa_supplicant *wpa_s);
 +void wpas_dbus_signal_peer_device_found(struct wpa_supplicant *wpa_s,
 +                                         const u8 *dev_addr);
 +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,
 +                                            const u8 *dev_addr, int request,
 +                                            enum p2p_prov_disc_status status,
 +                                            u16 config_methods,
 +                                            unsigned int generated_pin);
 +void wpas_dbus_signal_p2p_go_neg_req(struct wpa_supplicant *wpa_s,
- static inline void wpas_dbus_signal_p2p_go_neg_req(
-                               struct wpa_supplicant *wpa_s,
-                               const u8 *src,
-                               u16 dev_passwd_id)
++                                   const u8 *src, u16 dev_passwd_id,
++                                   u8 go_intent);
 +void wpas_dbus_signal_p2p_group_started(struct wpa_supplicant *wpa_s,
 +                                      const struct wpa_ssid *ssid,
 +                                      int client, int network_id);
++void wpas_dbus_signal_p2p_group_formation_failure(struct wpa_supplicant *wpa_s,
++                                                const char *reason);
 +void wpas_dbus_register_p2p_group(struct wpa_supplicant *wpa_s,
 +                                struct wpa_ssid *ssid);
 +void wpas_dbus_signal_p2p_go_neg_resp(struct wpa_supplicant *wpa_s,
 +                                    struct p2p_go_neg_results *res);
 +void wpas_dbus_unregister_p2p_group(struct wpa_supplicant *wpa_s,
 +                                  const struct wpa_ssid *ssid);
 +int wpas_dbus_register_persistent_group(struct wpa_supplicant *wpa_s,
 +                                      struct wpa_ssid *ssid);
 +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_signal_p2p_peer_disconnected(struct wpa_supplicant *wpa_s,
 +                                          const u8 *member);
 +void wpas_dbus_signal_p2p_sd_request(struct wpa_supplicant *wpa_s,
 +                                   int freq, const u8 *sa, u8 dialog_token,
 +                                   u16 update_indic, const u8 *tlvs,
 +                                   size_t tlvs_len);
 +void wpas_dbus_signal_p2p_sd_response(struct wpa_supplicant *wpa_s,
 +                                    const u8 *sa, u16 update_indic,
 +                                    const u8 *tlvs, size_t tlvs_len);
 +void wpas_dbus_signal_p2p_peer_joined(struct wpa_supplicant *wpa_s,
 +                              const u8 *member);
 +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,
 +                         const u8 *addr, const u8 *dst, const u8 *bssid,
 +                         const u8 *ie, size_t ie_len, u32 ssi_signal);
 +void wpas_dbus_signal_eap_status(struct wpa_supplicant *wpa_s,
 +                               const char *status, const char *parameter);
 +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_invitation_received(struct wpa_supplicant *wpa_s,
++                                            const u8 *sa, const u8 *dev_addr,
++                                            const u8 *bssid, int id,
++                                            int op_freq);
 +
 +#else /* CONFIG_CTRL_IFACE_DBUS_NEW */
 +
 +static inline int wpas_dbus_register_interface(struct wpa_supplicant *wpa_s)
 +{
 +      return 0;
 +}
 +
 +static inline int wpas_dbus_unregister_interface(struct wpa_supplicant *wpa_s)
 +{
 +      return 0;
 +}
 +
 +#define wpas_dbus_signal_state_changed(w, n, o) do { } while (0)
 +
 +static inline void wpas_dbus_signal_prop_changed(struct wpa_supplicant *wpa_s,
 +                                               enum wpas_dbus_prop property)
 +{
 +}
 +
 +static inline void wpas_dbus_bss_signal_prop_changed(
 +      struct wpa_supplicant *wpa_s, enum wpas_dbus_bss_prop property,
 +      unsigned int id)
 +{
 +}
 +
 +static inline void wpas_dbus_signal_network_enabled_changed(
 +      struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid)
 +{
 +}
 +
 +static inline void wpas_dbus_signal_network_selected(
 +      struct wpa_supplicant *wpa_s, int id)
 +{
 +}
 +
 +static inline void wpas_dbus_signal_network_request(
 +      struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
 +      enum wpa_ctrl_req_type rtype, const char *default_txt)
 +{
 +}
 +
 +static inline void wpas_dbus_signal_scan_done(struct wpa_supplicant *wpa_s,
 +                                            int success)
 +{
 +}
 +
 +static inline void wpas_dbus_signal_wps_cred(struct wpa_supplicant *wpa_s,
 +                                           const struct wps_credential *cred)
 +{
 +}
 +
 +static inline void wpas_dbus_signal_wps_event_m2d(struct wpa_supplicant *wpa_s,
 +                                                struct wps_event_m2d *m2d)
 +{
 +}
 +
 +static inline void wpas_dbus_signal_wps_event_fail(
 +      struct wpa_supplicant *wpa_s, struct wps_event_fail *fail)
 +{
 +}
 +
 +static inline void wpas_dbus_signal_wps_event_success(
 +      struct wpa_supplicant *wpa_s)
 +{
 +}
 +
++static inline void wpas_dbus_signal_wps_event_pbc_overlap(
++      struct wpa_supplicant *wpa_s)
++{
++}
++
 +static inline int wpas_dbus_register_network(struct wpa_supplicant *wpa_s,
 +                                           struct wpa_ssid *ssid)
 +{
 +      return 0;
 +}
 +
 +static inline int wpas_dbus_unregister_network(struct wpa_supplicant *wpa_s,
 +                                             int nid)
 +{
 +      return 0;
 +}
 +
 +static inline int wpas_dbus_unregister_bss(struct wpa_supplicant *wpa_s,
 +                                         u8 bssid[ETH_ALEN], unsigned int id)
 +{
 +      return 0;
 +}
 +
 +static inline int wpas_dbus_register_bss(struct wpa_supplicant *wpa_s,
 +                                       u8 bssid[ETH_ALEN], unsigned int id)
 +{
 +      return 0;
 +}
 +
 +static inline void wpas_dbus_signal_blob_added(struct wpa_supplicant *wpa_s,
 +                                             const char *name)
 +{
 +}
 +
 +static inline void wpas_dbus_signal_blob_removed(struct wpa_supplicant *wpa_s,
 +                                               const char *name)
 +{
 +}
 +
 +static inline void wpas_dbus_signal_debug_level_changed(
 +      struct wpa_global *global)
 +{
 +}
 +
 +static inline void wpas_dbus_signal_debug_timestamp_changed(
 +      struct wpa_global *global)
 +{
 +}
 +
 +static inline void wpas_dbus_signal_debug_show_keys_changed(
 +      struct wpa_global *global)
 +{
 +}
 +
 +static inline int wpas_dbus_register_peer(struct wpa_supplicant *wpa_s,
 +                                        const u8 *dev_addr)
 +{
 +      return 0;
 +}
 +
 +static inline int wpas_dbus_unregister_peer(struct wpa_supplicant *wpa_s,
 +                                          const u8 *dev_addr)
 +{
 +      return 0;
 +}
 +
 +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)
 +{
 +}
 +
 +static inline void
 +wpas_dbus_signal_p2p_provision_discovery(struct wpa_supplicant *wpa_s,
 +                                       const u8 *dev_addr, int request,
 +                                       enum p2p_prov_disc_status status,
 +                                       u16 config_methods,
 +                                       unsigned int generated_pin)
 +{
 +}
 +
++static inline void wpas_dbus_signal_p2p_go_neg_req(struct wpa_supplicant *wpa_s,
++                                                 const u8 *src,
++                                                 u16 dev_passwd_id,
++                                                 u8 go_intent)
 +{
 +}
 +
 +static inline void
 +wpas_dbus_signal_p2p_group_started(struct wpa_supplicant *wpa_s,
 +                                 const struct wpa_ssid *ssid,
 +                                 int client, int network_id)
 +{
 +}
 +
++static inline void
++wpas_dbus_signal_p2p_group_formation_failure(struct wpa_supplicant *wpa_s,
++                                           const char *reason)
++{
++}
++
 +static inline void
 +wpas_dbus_register_p2p_group(struct wpa_supplicant *wpa_s,
 +                           struct wpa_ssid *ssid)
 +{
 +}
 +
 +static inline int wpas_dbus_register_persistent_group(
 +      struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid)
 +{
 +      return 0;
 +}
 +
 +static inline int wpas_dbus_unregister_persistent_group(
 +      struct wpa_supplicant *wpa_s, int nid)
 +{
 +      return 0;
 +}
 +
 +static inline void
 +wpas_dbus_signal_p2p_go_neg_resp(struct wpa_supplicant *wpa_s,
 +                               struct p2p_go_neg_results *res)
 +{
 +}
 +
 +static inline void
 +wpas_dbus_unregister_p2p_group(struct wpa_supplicant *wpa_s,
 +                             const struct wpa_ssid *ssid)
 +{
 +}
 +
 +static inline void wpas_dbus_signal_p2p_invitation_result(
 +                              struct wpa_supplicant *wpa_s, int status,
 +                              const u8 *bssid)
 +{
 +}
 +
 +static inline void
 +wpas_dbus_register_p2p_groupmember(struct wpa_supplicant *wpa_s,
 +                                 const u8 *p2p_if_addr)
 +{
 +}
 +
 +static inline void
 +wpas_dbus_signal_p2p_sd_request(struct wpa_supplicant *wpa_s, int freq,
 +                              const u8 *sa, u8 dialog_token, u16 update_indic,
 +                              const u8 *tlvs, size_t tlvs_len)
 +{
 +}
 +
 +static inline void
 +wpas_dbus_signal_p2p_sd_response(struct wpa_supplicant *wpa_s,
 +                               const u8 *sa, u16 update_indic,
 +                               const u8 *tlvs, size_t tlvs_len)
 +{
 +}
 +
 +static inline void
 +wpas_dbus_unregister_p2p_groupmember(struct wpa_supplicant *wpa_s,
 +                                   const u8 *p2p_if_addr)
 +{
 +}
 +
 +static inline void
 +wpas_dbus_signal_p2p_peer_joined(struct wpa_supplicant *wpa_s,
 +                               const u8 *member)
 +{
 +}
 +
++static inline void
++wpas_dbus_signal_p2p_find_stopped(struct wpa_supplicant *wpa_s)
++{
++}
++
 +static inline void
 +wpas_dbus_signal_peer_device_found(struct wpa_supplicant *wpa_s,
 +                                 const u8 *dev_addr)
 +{
 +}
 +
 +static inline void
 +wpas_dbus_signal_peer_device_lost(struct wpa_supplicant *wpa_s,
 +                                const u8 *dev_addr)
 +{
 +}
 +
 +static inline void
 +wpas_dbus_signal_p2p_peer_disconnected(struct wpa_supplicant *wpa_s,
 +                                     const u8 *member)
 +{
 +}
 +
 +static inline void
 +wpas_dbus_signal_p2p_wps_failed(struct wpa_supplicant *wpa_s,
 +                              struct wps_event_fail *fail)
 +{
 +}
 +
 +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)
 +{
 +}
 +
 +static inline void wpas_dbus_signal_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)
 +{
 +}
 +
 +static inline void wpas_dbus_signal_eap_status(struct wpa_supplicant *wpa_s,
 +                                             const char *status,
 +                                             const char *parameter)
 +{
 +}
 +
 +static inline
 +void wpas_dbus_signal_sta_authorized(struct wpa_supplicant *wpa_s,
 +                                   const u8 *sta)
 +{
 +}
 +
 +static inline
 +void wpas_dbus_signal_sta_deauthorized(struct wpa_supplicant *wpa_s,
 +                                     const u8 *sta)
 +{
 +}
 +
++static inline
++void wpas_dbus_signal_p2p_invitation_received(struct wpa_supplicant *wpa_s,
++                                            const u8 *sa, const u8 *dev_addr,
++                                            const u8 *bssid, int id,
++                                            int op_freq)
++{
++}
++
 +#endif /* CONFIG_CTRL_IFACE_DBUS_NEW */
 +
 +#endif /* CTRL_IFACE_DBUS_H_NEW */
index f2e62ca96386478b3f4af6395db862378d35027d,0000000000000000000000000000000000000000..67562a547172ff614893e68cc75dd075614b954d
mode 100644,000000..100644
--- /dev/null
@@@ -1,4147 -1,0 +1,4213 @@@
-               if (os_strcmp(wpa_s->dbus_new_path, path) == 0)
 +/*
 + * 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-2015, 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/ieee802_11_defs.h"
 +#include "eap_peer/eap_methods.h"
 +#include "eapol_supp/eapol_supp_sm.h"
 +#include "rsn_supp/wpa.h"
 +#include "../config.h"
 +#include "../wpa_supplicant_i.h"
 +#include "../driver_i.h"
 +#include "../notify.h"
 +#include "../bss.h"
 +#include "../scan.h"
 +#include "../autoscan.h"
 +#include "dbus_new_helpers.h"
 +#include "dbus_new.h"
 +#include "dbus_new_handlers.h"
 +#include "dbus_dict_helpers.h"
 +#include "dbus_common_i.h"
 +#include "drivers/driver.h"
 +
 +static const char * const debug_strings[] = {
 +      "excessive", "msgdump", "debug", "info", "warning", "error", NULL
 +};
 +
 +
 +/**
 + * 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
 + *
 + * Convenience function to create and return an UnknownError
 + */
 +DBusMessage * wpas_dbus_error_unknown_error(DBusMessage *message,
 +                                          const char *arg)
 +{
 +      return dbus_message_new_error(message, WPAS_DBUS_ERROR_UNKNOWN_ERROR,
 +                                    arg);
 +}
 +
 +
 +/**
 + * wpas_dbus_error_iface_unknown - Return a new invalid interface error message
 + * @message: Pointer to incoming dbus message this error refers to
 + * Returns: A dbus error message
 + *
 + * Convenience function to create and return an invalid interface error
 + */
 +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.");
 +}
 +
 +
 +/**
 + * wpas_dbus_error_network_unknown - Return a new NetworkUnknown error message
 + * @message: Pointer to incoming dbus message this error refers to
 + * Returns: a dbus error message
 + *
 + * Convenience function to create and return an invalid network error
 + */
 +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.");
 +}
 +
 +
 +/**
 + * wpas_dbus_error_invalid_args - Return a new InvalidArgs error message
 + * @message: Pointer to incoming dbus message this error refers to
 + * Returns: a dbus error message
 + *
 + * Convenience function to create and return an invalid options error
 + */
 +DBusMessage * wpas_dbus_error_invalid_args(DBusMessage *message,
 +                                        const char *arg)
 +{
 +      DBusMessage *reply;
 +
 +      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);
 +
 +      return reply;
 +}
 +
 +
 +/**
 + * 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
 +};
 +
 +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;
 +              i++;
 +      }
 +      return TRUE;
 +}
 +
 +/**
 + * get_iface_by_dbus_path - Get a new network interface
 + * @global: Pointer to global data from wpa_supplicant_init()
 + * @path: Pointer to a dbus object path representing an interface
 + * Returns: Pointer to the interface or %NULL if not found
 + */
 +static struct wpa_supplicant * get_iface_by_dbus_path(
 +      struct wpa_global *global, const char *path)
 +{
 +      struct wpa_supplicant *wpa_s;
 +
 +      for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
-                               if (size <= 0)
++              if (wpa_s->dbus_new_path &&
++                  os_strcmp(wpa_s->dbus_new_path, path) == 0)
 +                      return wpa_s;
 +      }
 +      return NULL;
 +}
 +
 +
 +/**
 + * set_network_properties - Set properties of a configured network
 + * @wpa_s: wpa_supplicant structure for a network interface
 + * @ssid: wpa_ssid structure for a configured network
 + * @iter: DBus message iterator containing dictionary of network
 + * properties to set.
 + * @error: On failure, an error describing the failure
 + * Returns: TRUE if the request succeeds, FALSE if it failed
 + *
 + * Sets network configuration with parameters given id DBus dictionary
 + */
 +dbus_bool_t set_network_properties(struct wpa_supplicant *wpa_s,
 +                                 struct wpa_ssid *ssid,
 +                                 DBusMessageIter *iter,
 +                                 DBusError *error)
 +{
 +      struct wpa_dbus_dict_entry entry = { .type = DBUS_TYPE_STRING };
 +      DBusMessageIter iter_dict;
 +      char *value = NULL;
 +
 +      if (!wpa_dbus_dict_open_read(iter, &iter_dict, error))
 +              return FALSE;
 +
 +      while (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
 +              size_t size = 50;
 +              int ret;
 +
 +              if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
 +                      goto error;
 +
 +              value = NULL;
 +              if (entry.type == DBUS_TYPE_ARRAY &&
 +                  entry.array_type == DBUS_TYPE_BYTE) {
 +                      if (entry.array_len <= 0)
 +                              goto error;
 +
 +                      size = entry.array_len * 2 + 1;
 +                      value = os_zalloc(size);
 +                      if (value == NULL)
 +                              goto error;
 +
 +                      ret = wpa_snprintf_hex(value, size,
 +                                             (u8 *) entry.bytearray_value,
 +                                             entry.array_len);
 +                      if (ret <= 0)
 +                              goto error;
 +              } else if (entry.type == DBUS_TYPE_STRING) {
 +                      if (should_quote_opt(entry.key)) {
 +                              size = os_strlen(entry.str_value);
-               if (wpa_s) {
++                              if (size == 0)
 +                                      goto error;
 +
 +                              size += 3;
 +                              value = os_zalloc(size);
 +                              if (value == NULL)
 +                                      goto error;
 +
 +                              ret = os_snprintf(value, size, "\"%s\"",
 +                                                entry.str_value);
 +                              if (os_snprintf_error(size, ret))
 +                                      goto error;
 +                      } else {
 +                              value = os_strdup(entry.str_value);
 +                              if (value == NULL)
 +                                      goto error;
 +                      }
 +              } else if (entry.type == DBUS_TYPE_UINT32) {
 +                      value = os_zalloc(size);
 +                      if (value == NULL)
 +                              goto error;
 +
 +                      ret = os_snprintf(value, size, "%u",
 +                                        entry.uint32_value);
 +                      if (os_snprintf_error(size, ret))
 +                              goto error;
 +              } else if (entry.type == DBUS_TYPE_INT32) {
 +                      value = os_zalloc(size);
 +                      if (value == NULL)
 +                              goto error;
 +
 +                      ret = os_snprintf(value, size, "%d",
 +                                        entry.int32_value);
 +                      if (os_snprintf_error(size, ret))
 +                              goto error;
 +              } else
 +                      goto error;
 +
 +              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))
 +                      wpa_config_update_psk(ssid);
 +              else if (os_strcmp(entry.key, "priority") == 0)
 +                      wpa_config_update_prio_list(wpa_s->conf);
 +
 +              os_free(value);
 +              value = NULL;
 +              wpa_dbus_dict_entry_clear(&entry);
 +      }
 +
 +      return TRUE;
 +
 +error:
 +      os_free(value);
 +      wpa_dbus_dict_entry_clear(&entry);
 +      dbus_set_error_const(error, DBUS_ERROR_INVALID_ARGS,
 +                           "invalid message format");
 +      return FALSE;
 +}
 +
 +
 +/**
 + * wpas_dbus_simple_property_getter - Get basic type property
 + * @iter: Message iter to use when appending arguments
 + * @type: DBus type of property (must be basic type)
 + * @val: pointer to place holding property value
 + * @error: On failure an error describing the failure
 + * Returns: TRUE if the request was successful, FALSE if it failed
 + *
 + * Generic getter for basic type properties. Type is required to be basic.
 + */
 +dbus_bool_t wpas_dbus_simple_property_getter(DBusMessageIter *iter,
 +                                           const int type,
 +                                           const void *val,
 +                                           DBusError *error)
 +{
 +      DBusMessageIter variant_iter;
 +
 +      if (!dbus_type_is_basic(type)) {
 +              dbus_set_error(error, DBUS_ERROR_FAILED,
 +                             "%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) ||
 +          !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;
 +}
 +
 +
 +/**
 + * wpas_dbus_simple_property_setter - Set basic type property
 + * @message: Pointer to incoming dbus message
 + * @type: DBus type of property (must be basic type)
 + * @val: pointer to place where value being set will be stored
 + * Returns: TRUE if the request was successful, FALSE if it failed
 + *
 + * Generic setter for basic type properties. Type is required to be basic.
 + */
 +dbus_bool_t wpas_dbus_simple_property_setter(DBusMessageIter *iter,
 +                                           DBusError *error,
 +                                           const int type, void *val)
 +{
 +      DBusMessageIter variant_iter;
 +
 +      if (!dbus_type_is_basic(type)) {
 +              dbus_set_error(error, DBUS_ERROR_FAILED,
 +                             "%s: given type is not basic", __func__);
 +              return FALSE;
 +      }
 +
 +      /* Look at the new value */
 +      dbus_message_iter_recurse(iter, &variant_iter);
 +      if (dbus_message_iter_get_arg_type(&variant_iter) != type) {
 +              dbus_set_error_const(error, DBUS_ERROR_FAILED,
 +                                   "wrong property type");
 +              return FALSE;
 +      }
 +      dbus_message_iter_get_basic(&variant_iter, val);
 +
 +      return TRUE;
 +}
 +
 +
 +/**
 + * wpas_dbus_simple_array_property_getter - Get array type property
 + * @iter: Pointer to incoming dbus message iterator
 + * @type: DBus type of property array elements (must be basic type)
 + * @array: pointer to array of elements to put into response message
 + * @array_len: length of above array
 + * @error: a pointer to an error to fill on failure
 + * Returns: TRUE if the request succeeded, FALSE if it failed
 + *
 + * Generic getter for array type properties. Array elements type is
 + * required to be basic.
 + */
 +dbus_bool_t wpas_dbus_simple_array_property_getter(DBusMessageIter *iter,
 +                                                 const int type,
 +                                                 const void *array,
 +                                                 size_t array_len,
 +                                                 DBusError *error)
 +{
 +      DBusMessageIter variant_iter, array_iter;
 +      char type_str[] = "a?"; /* ? will be replaced with subtype letter; */
 +      const char *sub_type_str;
 +      size_t element_size, i;
 +
 +      if (!dbus_type_is_basic(type)) {
 +              dbus_set_error(error, DBUS_ERROR_FAILED,
 +                             "%s: given type is not basic", __func__);
 +              return FALSE;
 +      }
 +
 +      sub_type_str = wpa_dbus_type_as_string(type);
 +      type_str[1] = sub_type_str[0];
 +
 +      if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
 +                                            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", __func__);
 +              return FALSE;
 +      }
 +
 +      switch (type) {
 +      case DBUS_TYPE_BYTE:
 +      case DBUS_TYPE_BOOLEAN:
 +              element_size = 1;
 +              break;
 +      case DBUS_TYPE_INT16:
 +      case DBUS_TYPE_UINT16:
 +              element_size = sizeof(uint16_t);
 +              break;
 +      case DBUS_TYPE_INT32:
 +      case DBUS_TYPE_UINT32:
 +              element_size = sizeof(uint32_t);
 +              break;
 +      case DBUS_TYPE_INT64:
 +      case DBUS_TYPE_UINT64:
 +              element_size = sizeof(uint64_t);
 +              break;
 +      case DBUS_TYPE_DOUBLE:
 +              element_size = sizeof(double);
 +              break;
 +      case DBUS_TYPE_STRING:
 +      case DBUS_TYPE_OBJECT_PATH:
 +              element_size = sizeof(char *);
 +              break;
 +      default:
 +              dbus_set_error(error, DBUS_ERROR_FAILED,
 +                             "%s: unknown element type %d", __func__, type);
 +              return FALSE;
 +      }
 +
 +      for (i = 0; i < array_len; i++) {
 +              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(&variant_iter, &array_iter) ||
 +          !dbus_message_iter_close_container(iter, &variant_iter)) {
 +              dbus_set_error(error, DBUS_ERROR_FAILED,
 +                             "%s: failed to construct message 3", __func__);
 +              return FALSE;
 +      }
 +
 +      return TRUE;
 +}
 +
 +
 +/**
 + * wpas_dbus_simple_array_array_property_getter - Get array array type property
 + * @iter: Pointer to incoming dbus message iterator
 + * @type: DBus type of property array elements (must be basic type)
 + * @array: pointer to array of elements to put into response message
 + * @array_len: length of above array
 + * @error: a pointer to an error to fill on failure
 + * Returns: TRUE if the request succeeded, FALSE if it failed
 + *
 + * Generic getter for array type properties. Array elements type is
 + * required to be basic.
 + */
 +dbus_bool_t wpas_dbus_simple_array_array_property_getter(DBusMessageIter *iter,
 +                                                       const int type,
 +                                                       struct wpabuf **array,
 +                                                       size_t array_len,
 +                                                       DBusError *error)
 +{
 +      DBusMessageIter variant_iter, array_iter;
 +      char type_str[] = "aa?";
 +      char inner_type_str[] = "a?";
 +      const char *sub_type_str;
 +      size_t i;
 +
 +      if (!dbus_type_is_basic(type)) {
 +              dbus_set_error(error, DBUS_ERROR_FAILED,
 +                             "%s: given type is not basic", __func__);
 +              return FALSE;
 +      }
 +
 +      sub_type_str = wpa_dbus_type_as_string(type);
 +      type_str[2] = sub_type_str[0];
 +      inner_type_str[1] = sub_type_str[0];
 +
 +      if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
 +                                            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", __func__);
 +              return FALSE;
 +      }
 +
 +      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_message_iter_close_container(iter, &variant_iter)) {
 +              dbus_set_error(error, DBUS_ERROR_FAILED,
 +                             "%s: failed to close message", __func__);
 +              return FALSE;
 +      }
 +
 +      return TRUE;
 +}
 +
 +
 +/**
 + * wpas_dbus_handler_create_interface - Request registration of a network iface
 + * @message: Pointer to incoming dbus message
 + * @global: %wpa_supplicant global data structure
 + * Returns: The object path of the new interface object,
 + *          or a dbus error message with more information
 + *
 + * Handler function for "CreateInterface" method call. Handles requests
 + * by dbus clients to register a network interface that wpa_supplicant
 + * will manage.
 + */
 +DBusMessage * wpas_dbus_handler_create_interface(DBusMessage *message,
 +                                               struct wpa_global *global)
 +{
 +      DBusMessageIter iter_dict;
 +      DBusMessage *reply = NULL;
 +      DBusMessageIter iter;
 +      struct wpa_dbus_dict_entry entry;
 +      char *driver = NULL;
 +      char *ifname = NULL;
 +      char *confname = NULL;
 +      char *bridge_ifname = NULL;
 +
 +      dbus_message_iter_init(message, &iter);
 +
 +      if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL))
 +              goto error;
 +      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") == 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 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 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 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 oom;
 +              } else {
 +                      wpa_dbus_dict_entry_clear(&entry);
 +                      goto error;
 +              }
 +      }
 +
 +      if (ifname == NULL)
 +              goto error; /* Required Ifname argument missing */
 +
 +      /*
 +       * 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.");
 +      } 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. */
 +              wpa_s = wpa_supplicant_add_iface(global, &iface, NULL);
-       if (wpa_s == NULL)
++              if (wpa_s && wpa_s->dbus_new_path) {
 +                      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);
 +              } else {
 +                      reply = wpas_dbus_error_unknown_error(
 +                              message,
 +                              "wpa_supplicant couldn't grab this interface.");
 +              }
 +      }
 +
 +out:
 +      os_free(driver);
 +      os_free(ifname);
 +      os_free(confname);
 +      os_free(bridge_ifname);
 +      return reply;
 +
 +error:
 +      reply = wpas_dbus_error_invalid_args(message, NULL);
 +      goto out;
 +oom:
 +      reply = wpas_dbus_error_no_memory(message);
 +      goto out;
 +}
 +
 +
 +/**
 + * wpas_dbus_handler_remove_interface - Request deregistration of an interface
 + * @message: Pointer to incoming dbus message
 + * @global: wpa_supplicant global data structure
 + * Returns: a dbus message containing a UINT32 indicating success (1) or
 + *          failure (0), or returns a dbus error message with more information
 + *
 + * Handler function for "removeInterface" method call.  Handles requests
 + * by dbus clients to deregister a network interface that wpa_supplicant
 + * currently manages.
 + */
 +DBusMessage * wpas_dbus_handler_remove_interface(DBusMessage *message,
 +                                               struct wpa_global *global)
 +{
 +      struct wpa_supplicant *wpa_s;
 +      char *path;
 +      DBusMessage *reply = NULL;
 +
 +      dbus_message_get_args(message, NULL, DBUS_TYPE_OBJECT_PATH, &path,
 +                            DBUS_TYPE_INVALID);
 +
 +      wpa_s = get_iface_by_dbus_path(global, path);
 +      if (wpa_s == NULL)
 +              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.");
 +      }
 +
 +      return reply;
 +}
 +
 +
 +/**
 + * wpas_dbus_handler_get_interface - Get the object path for an interface name
 + * @message: Pointer to incoming dbus message
 + * @global: %wpa_supplicant global data structure
 + * Returns: The object path of the interface object,
 + *          or a dbus error message with more information
 + *
 + * Handler function for "getInterface" method call.
 + */
 +DBusMessage * wpas_dbus_handler_get_interface(DBusMessage *message,
 +                                            struct wpa_global *global)
 +{
 +      DBusMessage *reply = NULL;
 +      const char *ifname;
 +      const char *path;
 +      struct wpa_supplicant *wpa_s;
 +
 +      dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &ifname,
 +                            DBUS_TYPE_INVALID);
 +
 +      wpa_s = wpa_supplicant_get_iface(global, ifname);
-       for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next)
-               num++;
++      if (wpa_s == NULL || wpa_s->dbus_new_path == NULL)
 +              return wpas_dbus_error_iface_unknown(message);
 +
 +      path = wpa_s->dbus_new_path;
 +      reply = dbus_message_new_method_return(message);
 +      if (reply == 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 wpas_dbus_error_no_memory(message);
 +      }
 +
 +      return reply;
 +}
 +
 +
 +/**
 + * wpas_dbus_getter_debug_level - Get debug level
 + * @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 "DebugLevel" property.
 + */
 +dbus_bool_t wpas_dbus_getter_debug_level(DBusMessageIter *iter,
 +                                       DBusError *error,
 +                                       void *user_data)
 +{
 +      const char *str;
 +      int idx = wpa_debug_level;
 +
 +      if (idx < 0)
 +              idx = 0;
 +      if (idx > 5)
 +              idx = 5;
 +      str = debug_strings[idx];
 +      return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING,
 +                                              &str, error);
 +}
 +
 +
 +/**
 + * wpas_dbus_getter_debug_timestamp - Get debug timestamp
 + * @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 "DebugTimestamp" property.
 + */
 +dbus_bool_t wpas_dbus_getter_debug_timestamp(DBusMessageIter *iter,
 +                                           DBusError *error,
 +                                           void *user_data)
 +{
 +      return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_BOOLEAN,
 +                                              &wpa_debug_timestamp, error);
 +
 +}
 +
 +
 +/**
 + * wpas_dbus_getter_debug_show_keys - Get debug show keys
 + * @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 "DebugShowKeys" property.
 + */
 +dbus_bool_t wpas_dbus_getter_debug_show_keys(DBusMessageIter *iter,
 +                                           DBusError *error,
 +                                           void *user_data)
 +{
 +      return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_BOOLEAN,
 +                                              &wpa_debug_show_keys, error);
 +
 +}
 +
 +/**
 + * wpas_dbus_setter_debug_level - Set debug level
 + * @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 "DebugLevel" property.
 + */
 +dbus_bool_t wpas_dbus_setter_debug_level(DBusMessageIter *iter,
 +                                       DBusError *error, void *user_data)
 +{
 +      struct wpa_global *global = user_data;
 +      const char *str = NULL;
 +      int i, val = -1;
 +
 +      if (!wpas_dbus_simple_property_setter(iter, error, DBUS_TYPE_STRING,
 +                                            &str))
 +              return FALSE;
 +
 +      for (i = 0; debug_strings[i]; i++)
 +              if (os_strcmp(debug_strings[i], str) == 0) {
 +                      val = i;
 +                      break;
 +              }
 +
 +      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");
 +              return FALSE;
 +      }
 +
 +      return TRUE;
 +}
 +
 +
 +/**
 + * wpas_dbus_setter_debug_timestamp - Set debug timestamp
 + * @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 "DebugTimestamp" property.
 + */
 +dbus_bool_t wpas_dbus_setter_debug_timestamp(DBusMessageIter *iter,
 +                                           DBusError *error,
 +                                           void *user_data)
 +{
 +      struct wpa_global *global = user_data;
 +      dbus_bool_t val;
 +
 +      if (!wpas_dbus_simple_property_setter(iter, error, DBUS_TYPE_BOOLEAN,
 +                                            &val))
 +              return FALSE;
 +
 +      wpa_supplicant_set_debug_params(global, wpa_debug_level, val ? 1 : 0,
 +                                      wpa_debug_show_keys);
 +      return TRUE;
 +}
 +
 +
 +/**
 + * wpas_dbus_setter_debug_show_keys - Set debug show keys
 + * @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 "DebugShowKeys" property.
 + */
 +dbus_bool_t wpas_dbus_setter_debug_show_keys(DBusMessageIter *iter,
 +                                           DBusError *error,
 +                                           void *user_data)
 +{
 +      struct wpa_global *global = user_data;
 +      dbus_bool_t val;
 +
 +      if (!wpas_dbus_simple_property_setter(iter, error, DBUS_TYPE_BOOLEAN,
 +                                            &val))
 +              return FALSE;
 +
 +      wpa_supplicant_set_debug_params(global, wpa_debug_level,
 +                                      wpa_debug_timestamp,
 +                                      val ? 1 : 0);
 +      return TRUE;
 +}
 +
 +
 +/**
 + * wpas_dbus_getter_interfaces - Request registered interfaces list
 + * @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 "Interfaces" property. Handles requests
 + * by dbus clients to return list of registered interfaces objects
 + * paths
 + */
 +dbus_bool_t wpas_dbus_getter_interfaces(DBusMessageIter *iter,
 +                                      DBusError *error,
 +                                      void *user_data)
 +{
 +      struct wpa_global *global = user_data;
 +      struct wpa_supplicant *wpa_s;
 +      const char **paths;
 +      unsigned int i = 0, num = 0;
 +      dbus_bool_t success;
 +
-       for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next)
-               paths[i++] = wpa_s->dbus_new_path;
++      for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
++              if (wpa_s->dbus_new_path)
++                      num++;
++      }
 +
 +      paths = os_calloc(num, sizeof(char *));
 +      if (!paths) {
 +              dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
 +              return FALSE;
 +      }
 +
-               if (len > MAX_SSID_LEN) {
++      for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
++              if (wpa_s->dbus_new_path)
++                      paths[i++] = wpa_s->dbus_new_path;
++      }
 +
 +      success = wpas_dbus_simple_array_property_getter(iter,
 +                                                       DBUS_TYPE_OBJECT_PATH,
 +                                                       paths, num, error);
 +
 +      os_free(paths);
 +      return success;
 +}
 +
 +
 +/**
 + * wpas_dbus_getter_eap_methods - Request supported EAP methods list
 + * @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 "EapMethods" property. Handles requests
 + * by dbus clients to return list of strings with supported EAP methods
 + */
 +dbus_bool_t wpas_dbus_getter_eap_methods(DBusMessageIter *iter,
 +                                       DBusError *error, void *user_data)
 +{
 +      char **eap_methods;
 +      size_t num_items = 0;
 +      dbus_bool_t success;
 +
 +      eap_methods = eap_get_names_as_string_array(&num_items);
 +      if (!eap_methods) {
 +              dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
 +              return FALSE;
 +      }
 +
 +      success = wpas_dbus_simple_array_property_getter(iter,
 +                                                       DBUS_TYPE_STRING,
 +                                                       eap_methods,
 +                                                       num_items, error);
 +
 +      while (num_items)
 +              os_free(eap_methods[--num_items]);
 +      os_free(eap_methods);
 +      return success;
 +}
 +
 +
 +/**
 + * wpas_dbus_getter_global_capabilities - Request supported global 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. Handles requests by dbus clients to
 + * return a list of strings with supported capabilities like AP, RSN IBSS,
 + * and P2P that are determined at compile time.
 + */
 +dbus_bool_t wpas_dbus_getter_global_capabilities(DBusMessageIter *iter,
 +                                               DBusError *error,
 +                                               void *user_data)
 +{
 +      const char *capabilities[5] = { NULL, NULL, NULL, NULL, NULL };
 +      size_t num_items = 0;
 +
 +#ifdef CONFIG_AP
 +      capabilities[num_items++] = "ap";
 +#endif /* CONFIG_AP */
 +#ifdef CONFIG_IBSS_RSN
 +      capabilities[num_items++] = "ibss-rsn";
 +#endif /* CONFIG_IBSS_RSN */
 +#ifdef CONFIG_P2P
 +      capabilities[num_items++] = "p2p";
 +#endif /* CONFIG_P2P */
 +#ifdef CONFIG_INTERWORKING
 +      capabilities[num_items++] = "interworking";
 +#endif /* CONFIG_INTERWORKING */
 +
 +      return wpas_dbus_simple_array_property_getter(iter,
 +                                                    DBUS_TYPE_STRING,
 +                                                    capabilities,
 +                                                    num_items, error);
 +}
 +
 +
 +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, "%s[dbus]: Type must be a string",
 +                         __func__);
 +              *reply = wpas_dbus_error_invalid_args(
 +                      message, "Wrong Type value type. String required");
 +              return -1;
 +      }
 +      dbus_message_iter_get_basic(var, type);
 +      return 0;
 +}
 +
 +
 +static int wpas_dbus_get_scan_ssids(DBusMessage *message, DBusMessageIter *var,
 +                                  struct wpa_driver_scan_params *params,
 +                                  DBusMessage **reply)
 +{
 +      struct wpa_driver_scan_ssid *ssids = params->ssids;
 +      size_t ssids_num = 0;
 +      u8 *ssid;
 +      DBusMessageIter array_iter, sub_array_iter;
 +      char *val;
 +      int len;
 +
 +      if (dbus_message_iter_get_arg_type(var) != DBUS_TYPE_ARRAY) {
 +              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");
 +              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,
 +                         "%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");
 +              return -1;
 +      }
 +
 +      while (dbus_message_iter_get_arg_type(&array_iter) == DBUS_TYPE_ARRAY) {
 +              if (ssids_num >= WPAS_MAX_SCAN_SSIDS) {
 +                      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");
 +                      return -1;
 +              }
 +
 +              dbus_message_iter_recurse(&array_iter, &sub_array_iter);
 +
 +              dbus_message_iter_get_fixed_array(&sub_array_iter, &val, &len);
 +
-                                  __func__, len, MAX_SSID_LEN);
++              if (len > SSID_MAX_LEN) {
 +                      wpa_printf(MSG_DEBUG,
 +                                 "%s[dbus]: SSID too long (len=%d max_len=%d)",
-               } else if (params.freqs && params.freqs[0]) {
-                       if (wpa_supplicant_trigger_scan(wpa_s, &params)) {
-                               reply = wpas_dbus_error_scan_error(
-                                       message, "Scan request rejected");
-                       }
++                                 __func__, len, SSID_MAX_LEN);
 +                      *reply = wpas_dbus_error_invalid_args(
 +                              message, "Invalid SSID: too long");
 +                      return -1;
 +              }
 +
 +              if (len != 0) {
 +                      ssid = os_malloc(len);
 +                      if (ssid == NULL) {
 +                              *reply = wpas_dbus_error_no_memory(message);
 +                              return -1;
 +                      }
 +                      os_memcpy(ssid, val, len);
 +              } else {
 +                      /* Allow zero-length SSIDs */
 +                      ssid = NULL;
 +              }
 +
 +              ssids[ssids_num].ssid = ssid;
 +              ssids[ssids_num].ssid_len = len;
 +
 +              dbus_message_iter_next(&array_iter);
 +              ssids_num++;
 +      }
 +
 +      params->num_ssids = ssids_num;
 +      return 0;
 +}
 +
 +
 +static int wpas_dbus_get_scan_ies(DBusMessage *message, DBusMessageIter *var,
 +                                struct wpa_driver_scan_params *params,
 +                                DBusMessage **reply)
 +{
 +      u8 *ies = NULL, *nies;
 +      int ies_len = 0;
 +      DBusMessageIter array_iter, sub_array_iter;
 +      char *val;
 +      int len;
 +
 +      if (dbus_message_iter_get_arg_type(var) != DBUS_TYPE_ARRAY) {
 +              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");
 +              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,
 +                         "%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) {
 +              dbus_message_iter_recurse(&array_iter, &sub_array_iter);
 +
 +              dbus_message_iter_get_fixed_array(&sub_array_iter, &val, &len);
 +              if (len == 0) {
 +                      dbus_message_iter_next(&array_iter);
 +                      continue;
 +              }
 +
 +              nies = os_realloc(ies, ies_len + len);
 +              if (nies == NULL) {
 +                      os_free(ies);
 +                      *reply = wpas_dbus_error_no_memory(message);
 +                      return -1;
 +              }
 +              ies = nies;
 +              os_memcpy(ies + ies_len, val, len);
 +              ies_len += len;
 +
 +              dbus_message_iter_next(&array_iter);
 +      }
 +
 +      params->extra_ies = ies;
 +      params->extra_ies_len = ies_len;
 +      return 0;
 +}
 +
 +
 +static int wpas_dbus_get_scan_channels(DBusMessage *message,
 +                                     DBusMessageIter *var,
 +                                     struct wpa_driver_scan_params *params,
 +                                     DBusMessage **reply)
 +{
 +      DBusMessageIter array_iter, sub_array_iter;
 +      int *freqs = NULL, *nfreqs;
 +      int freqs_num = 0;
 +
 +      if (dbus_message_iter_get_arg_type(var) != DBUS_TYPE_ARRAY) {
 +              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");
 +              return -1;
 +      }
 +
 +      dbus_message_iter_recurse(var, &array_iter);
 +
 +      if (dbus_message_iter_get_arg_type(&array_iter) != DBUS_TYPE_STRUCT) {
 +              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");
 +              return -1;
 +      }
 +
 +      while (dbus_message_iter_get_arg_type(&array_iter) == DBUS_TYPE_STRUCT)
 +      {
 +              int freq, width;
 +
 +              dbus_message_iter_recurse(&array_iter, &sub_array_iter);
 +
 +              if (dbus_message_iter_get_arg_type(&sub_array_iter) !=
 +                  DBUS_TYPE_UINT32) {
 +                      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");
 +                      os_free(freqs);
 +                      return -1;
 +              }
 +              dbus_message_iter_get_basic(&sub_array_iter, &freq);
 +
 +              if (!dbus_message_iter_next(&sub_array_iter) ||
 +                  dbus_message_iter_get_arg_type(&sub_array_iter) !=
 +                  DBUS_TYPE_UINT32) {
 +                      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");
 +                      os_free(freqs);
 +                      return -1;
 +              }
 +
 +              dbus_message_iter_get_basic(&sub_array_iter, &width);
 +
 +#define FREQS_ALLOC_CHUNK 32
 +              if (freqs_num % FREQS_ALLOC_CHUNK == 0) {
 +                      nfreqs = os_realloc_array(
 +                              freqs, freqs_num + FREQS_ALLOC_CHUNK,
 +                              sizeof(int));
 +                      if (nfreqs == NULL)
 +                              os_free(freqs);
 +                      freqs = nfreqs;
 +              }
 +              if (freqs == NULL) {
 +                      *reply = wpas_dbus_error_no_memory(message);
 +                      return -1;
 +              }
 +
 +              freqs[freqs_num] = freq;
 +
 +              freqs_num++;
 +              dbus_message_iter_next(&array_iter);
 +      }
 +
 +      nfreqs = os_realloc_array(freqs, freqs_num + 1, sizeof(int));
 +      if (nfreqs == NULL)
 +              os_free(freqs);
 +      freqs = nfreqs;
 +      if (freqs == NULL) {
 +              *reply = wpas_dbus_error_no_memory(message);
 +              return -1;
 +      }
 +      freqs[freqs_num] = 0;
 +
 +      params->freqs = freqs;
 +      return 0;
 +}
 +
 +
 +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
 + * @wpa_s: wpa_supplicant structure for a network interface
 + * Returns: NULL indicating success or DBus error message on failure
 + *
 + * Handler function for "Scan" method call of a network device. Requests
 + * that wpa_supplicant perform a wireless scan as soon as possible
 + * on a particular wireless interface.
 + */
 +DBusMessage * wpas_dbus_handler_scan(DBusMessage *message,
 +                                   struct wpa_supplicant *wpa_s)
 +{
 +      DBusMessage *reply = NULL;
 +      DBusMessageIter iter, dict_iter, entry_iter, variant_iter;
 +      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));
 +
 +      dbus_message_iter_init(message, &iter);
 +
 +      dbus_message_iter_recurse(&iter, &dict_iter);
 +
 +      while (dbus_message_iter_get_arg_type(&dict_iter) ==
 +             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);
 +              dbus_message_iter_recurse(&entry_iter, &variant_iter);
 +
 +              if (os_strcmp(key, "Type") == 0) {
 +                      if (wpas_dbus_get_scan_type(message, &variant_iter,
 +                                                  &type, &reply) < 0)
 +                              goto out;
 +              } else if (os_strcmp(key, "SSIDs") == 0) {
 +                      if (wpas_dbus_get_scan_ssids(message, &variant_iter,
 +                                                   &params, &reply) < 0)
 +                              goto out;
 +              } else if (os_strcmp(key, "IEs") == 0) {
 +                      if (wpas_dbus_get_scan_ies(message, &variant_iter,
 +                                                 &params, &reply) < 0)
 +                              goto out;
 +              } else if (os_strcmp(key, "Channels") == 0) {
 +                      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, "%s[dbus]: Unknown argument %s",
 +                                 __func__, key);
 +                      reply = wpas_dbus_error_invalid_args(message, key);
 +                      goto out;
 +              }
 +
 +              dbus_message_iter_next(&dict_iter);
 +      }
 +
 +      if (!type) {
 +              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") == 0) {
 +              if (params.num_ssids || params.extra_ies_len) {
 +                      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");
 +                      goto out;
-                       wpa_s->scan_req = MANUAL_SCAN_REQ;
-                       wpa_supplicant_req_scan(wpa_s, 0, 0);
 +              } else {
-       ssid = wpa_config_add_network(wpa_s->conf);
++                      if (wpa_s->sched_scanning) {
++                              wpa_printf(MSG_DEBUG,
++                                         "%s[dbus]: Stop ongoing sched_scan to allow requested scan to proceed",
++                                         __func__);
++                              wpa_supplicant_cancel_sched_scan(wpa_s);
++                      }
++
++                      if (params.freqs && params.freqs[0]) {
++                              wpa_s->last_scan_req = MANUAL_SCAN_REQ;
++                              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") == 0) {
 +              if (!params.num_ssids) {
 +                      /* Add wildcard ssid */
 +                      params.num_ssids++;
 +              }
 +#ifdef CONFIG_AUTOSCAN
 +              autoscan_deinit(wpa_s);
 +#endif /* CONFIG_AUTOSCAN */
++              if (wpa_s->sched_scanning) {
++                      wpa_printf(MSG_DEBUG,
++                                 "%s[dbus]: Stop ongoing sched_scan to allow requested scan to proceed",
++                                 __func__);
++                      wpa_supplicant_cancel_sched_scan(wpa_s);
++              }
++
++              wpa_s->last_scan_req = MANUAL_SCAN_REQ;
 +              if (wpa_supplicant_trigger_scan(wpa_s, &params)) {
 +                      reply = wpas_dbus_error_scan_error(
 +                              message, "Scan request rejected");
 +              }
 +      } else {
 +              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);
 +      os_free((u8 *) params.extra_ies);
 +      os_free(params.freqs);
 +      return reply;
 +}
 +
 +
 +/**
 + * 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
 + * @wpa_s: wpa_supplicant structure for a network interface
 + * Returns: NotConnected DBus error message if already not connected
 + * or NULL otherwise.
 + *
 + * Handler function for "Disconnect" method call of network interface.
 + */
 +DBusMessage * wpas_dbus_handler_disconnect(DBusMessage *message,
 +                                         struct wpa_supplicant *wpa_s)
 +{
 +      if (wpa_s->current_ssid != NULL) {
 +              wpa_s->disconnected = 1;
 +              wpa_supplicant_deauthenticate(wpa_s,
 +                                            WLAN_REASON_DEAUTH_LEAVING);
 +
 +              return NULL;
 +      }
 +
 +      return dbus_message_new_error(message, WPAS_DBUS_ERROR_NOT_CONNECTED,
 +                                    "This interface is not connected");
 +}
 +
 +
 +/**
 + * wpas_dbus_new_iface_add_network - Add a new configured network
 + * @message: Pointer to incoming dbus message
 + * @wpa_s: wpa_supplicant structure for a network interface
 + * Returns: A dbus message containing the object path of the new network
 + *
 + * Handler function for "AddNetwork" method call of a network interface.
 + */
 +DBusMessage * wpas_dbus_handler_add_network(DBusMessage *message,
 +                                          struct wpa_supplicant *wpa_s)
 +{
 +      DBusMessage *reply = NULL;
 +      DBusMessageIter iter;
 +      struct wpa_ssid *ssid = NULL;
 +      char path_buf[WPAS_DBUS_OBJECT_PATH_MAX], *path = path_buf;
 +      DBusError error;
 +
 +      dbus_message_iter_init(message, &iter);
 +
-       if (iface == NULL || net_id == NULL ||
++      if (wpa_s->dbus_new_path)
++              ssid = wpa_config_add_network(wpa_s->conf);
 +      if (ssid == NULL) {
 +              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.");
 +              goto err;
 +      }
 +      wpas_notify_network_added(wpa_s, ssid);
 +      ssid->disabled = 1;
 +      wpa_config_set_network_defaults(ssid);
 +
 +      dbus_error_init(&error);
 +      if (!set_network_properties(wpa_s, ssid, &iter, &error)) {
 +              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");
 +              dbus_error_free(&error);
 +              goto err;
 +      }
 +
 +      /* Construct the object path for this network. */
 +      os_snprintf(path, WPAS_DBUS_OBJECT_PATH_MAX,
 +                  "%s/" WPAS_DBUS_NEW_NETWORKS_PART "/%d",
 +                  wpa_s->dbus_new_path, ssid->id);
 +
 +      reply = dbus_message_new_method_return(message);
 +      if (reply == 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 = wpas_dbus_error_no_memory(message);
 +              goto err;
 +      }
 +
 +      return reply;
 +
 +err:
 +      if (ssid) {
 +              wpas_notify_network_removed(wpa_s, ssid);
 +              wpa_config_remove_network(wpa_s->conf, ssid->id);
 +      }
 +      return reply;
 +}
 +
 +
 +/**
 + * wpas_dbus_handler_reassociate - Reassociate
 + * @message: Pointer to incoming dbus message
 + * @wpa_s: wpa_supplicant structure for a network interface
 + * Returns: InterfaceDisabled DBus error message if disabled
 + * or NULL otherwise.
 + *
 + * Handler function for "Reassociate" method call of network interface.
 + */
 +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;
 +      }
 +
 +      return dbus_message_new_error(message, WPAS_DBUS_ERROR_NOT_CONNECTED,
 +                                    "This interface is not connected");
 +}
 +
 +
++/**
++ * wpas_dbus_handler_reconnect - Reconnect if disconnected
++ * @message: Pointer to incoming dbus message
++ * @wpa_s: wpa_supplicant structure for a network interface
++ * Returns: InterfaceDisabled DBus error message if disabled
++ * or NULL otherwise.
++ *
++ * Handler function for "Reconnect" method call of network interface.
++ */
++DBusMessage * wpas_dbus_handler_reconnect(DBusMessage *message,
++              struct wpa_supplicant *wpa_s)
++{
++      if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) {
++              return dbus_message_new_error(message,
++                                            WPAS_DBUS_ERROR_IFACE_DISABLED,
++                                            "This interface is disabled");
++      }
++
++      if (wpa_s->disconnected)
++              wpas_request_connection(wpa_s);
++      return NULL;
++}
++
++
 +/**
 + * wpas_dbus_handler_remove_network - Remove a configured network
 + * @message: Pointer to incoming dbus message
 + * @wpa_s: wpa_supplicant structure for a network interface
 + * Returns: NULL on success or dbus error on failure
 + *
 + * Handler function for "RemoveNetwork" method call of a network interface.
 + */
 +DBusMessage * wpas_dbus_handler_remove_network(DBusMessage *message,
 +                                             struct wpa_supplicant *wpa_s)
 +{
 +      DBusMessage *reply = NULL;
 +      const char *op;
 +      char *iface, *net_id;
 +      int id;
 +      struct wpa_ssid *ssid;
 +      int was_disabled;
 +
 +      dbus_message_get_args(message, NULL, DBUS_TYPE_OBJECT_PATH, &op,
 +                            DBUS_TYPE_INVALID);
 +
 +      /* Extract the network ID and ensure the network */
 +      /* is actually a child of this interface */
 +      iface = wpas_dbus_new_decompose_object_path(op,
 +                                                  WPAS_DBUS_NEW_NETWORKS_PART,
 +                                                  &net_id);
-       if (iface == NULL || net_id == NULL ||
++      if (iface == NULL || net_id == NULL || !wpa_s->dbus_new_path ||
 +          os_strcmp(iface, wpa_s->dbus_new_path) != 0) {
 +              reply = wpas_dbus_error_invalid_args(message, op);
 +              goto out;
 +      }
 +
 +      errno = 0;
 +      id = strtoul(net_id, NULL, 10);
 +      if (errno != 0) {
 +              reply = wpas_dbus_error_invalid_args(message, op);
 +              goto out;
 +      }
 +
 +      ssid = wpa_config_get_network(wpa_s->conf, id);
 +      if (ssid == NULL) {
 +              reply = wpas_dbus_error_network_unknown(message);
 +              goto out;
 +      }
 +
 +      was_disabled = ssid->disabled;
 +
 +      wpas_notify_network_removed(wpa_s, ssid);
 +
 +      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_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);
 +      return reply;
 +}
 +
 +
 +static void remove_network(void *arg, struct wpa_ssid *ssid)
 +{
 +      struct wpa_supplicant *wpa_s = arg;
 +
 +      wpas_notify_network_removed(wpa_s, ssid);
 +
 +      if (wpa_config_remove_network(wpa_s->conf, ssid->id) < 0) {
 +              wpa_printf(MSG_ERROR,
 +                         "%s[dbus]: error occurred when removing network %d",
 +                         __func__, ssid->id);
 +              return;
 +      }
 +
 +      if (ssid == wpa_s->current_ssid)
 +              wpa_supplicant_deauthenticate(wpa_s,
 +                                            WLAN_REASON_DEAUTH_LEAVING);
 +}
 +
 +
 +/**
 + * wpas_dbus_handler_remove_all_networks - Remove all configured networks
 + * @message: Pointer to incoming dbus message
 + * @wpa_s: wpa_supplicant structure for a network interface
 + * Returns: NULL on success or dbus error on failure
 + *
 + * Handler function for "RemoveAllNetworks" method call of a network interface.
 + */
 +DBusMessage * wpas_dbus_handler_remove_all_networks(
 +      DBusMessage *message, struct wpa_supplicant *wpa_s)
 +{
 +      if (wpa_s->sched_scanning)
 +              wpa_supplicant_cancel_sched_scan(wpa_s);
 +
 +      /* NB: could check for failure and return an error */
 +      wpa_config_foreach_network(wpa_s->conf, remove_network, wpa_s);
 +      return NULL;
 +}
 +
 +
 +/**
 + * wpas_dbus_handler_select_network - Attempt association with a network
 + * @message: Pointer to incoming dbus message
 + * @wpa_s: wpa_supplicant structure for a network interface
 + * Returns: NULL on success or dbus error on failure
 + *
 + * Handler function for "SelectNetwork" method call of network interface.
 + */
 +DBusMessage * wpas_dbus_handler_select_network(DBusMessage *message,
 +                                             struct wpa_supplicant *wpa_s)
 +{
 +      DBusMessage *reply = NULL;
 +      const char *op;
 +      char *iface, *net_id;
 +      int id;
 +      struct wpa_ssid *ssid;
 +
 +      dbus_message_get_args(message, NULL, DBUS_TYPE_OBJECT_PATH, &op,
 +                            DBUS_TYPE_INVALID);
 +
 +      /* Extract the network ID and ensure the network */
 +      /* is actually a child of this interface */
 +      iface = wpas_dbus_new_decompose_object_path(op,
 +                                                  WPAS_DBUS_NEW_NETWORKS_PART,
 +                                                  &net_id);
-       if (iface == NULL || net_id == NULL ||
++      if (iface == NULL || net_id == NULL || !wpa_s->dbus_new_path ||
 +          os_strcmp(iface, wpa_s->dbus_new_path) != 0) {
 +              reply = wpas_dbus_error_invalid_args(message, op);
 +              goto out;
 +      }
 +
 +      errno = 0;
 +      id = strtoul(net_id, NULL, 10);
 +      if (errno != 0) {
 +              reply = wpas_dbus_error_invalid_args(message, op);
 +              goto out;
 +      }
 +
 +      ssid = wpa_config_get_network(wpa_s->conf, id);
 +      if (ssid == NULL) {
 +              reply = wpas_dbus_error_network_unknown(message);
 +              goto out;
 +      }
 +
 +      /* Finally, associate with the network */
 +      wpa_supplicant_select_network(wpa_s, ssid);
 +
 +out:
 +      os_free(iface);
 +      return reply;
 +}
 +
 +
 +/**
 + * wpas_dbus_handler_network_reply - Reply to a NetworkRequest signal
 + * @message: Pointer to incoming dbus message
 + * @wpa_s: wpa_supplicant structure for a network interface
 + * Returns: NULL on success or dbus error on failure
 + *
 + * Handler function for "NetworkReply" method call of network interface.
 + */
 +DBusMessage * wpas_dbus_handler_network_reply(DBusMessage *message,
 +                                            struct wpa_supplicant *wpa_s)
 +{
 +#ifdef IEEE8021X_EAPOL
 +      DBusMessage *reply = NULL;
 +      const char *op, *field, *value;
 +      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))
 +              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,
 +                                                  WPAS_DBUS_NEW_NETWORKS_PART,
 +                                                  &net_id);
-       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");
++      if (iface == NULL || net_id == NULL || !wpa_s->dbus_new_path ||
 +          os_strcmp(iface, wpa_s->dbus_new_path) != 0) {
 +              reply = wpas_dbus_error_invalid_args(message, op);
 +              goto out;
 +      }
 +
 +      errno = 0;
 +      id = strtoul(net_id, NULL, 10);
 +      if (errno != 0) {
 +              reply = wpas_dbus_error_invalid_args(message, net_id);
 +              goto out;
 +      }
 +
 +      ssid = wpa_config_get_network(wpa_s->conf, id);
 +      if (ssid == NULL) {
 +              reply = wpas_dbus_error_network_unknown(message);
 +              goto out;
 +      }
 +
 +      if (wpa_supplicant_ctrl_iface_ctrl_rsp_handle(wpa_s, ssid,
 +                                                    field, value) < 0)
 +              reply = wpas_dbus_error_invalid_args(message, field);
 +      else {
 +              /* Tell EAP to retry immediately */
 +              eapol_sm_notify_ctrl_response(wpa_s->eapol);
 +      }
 +
 +out:
 +      os_free(iface);
 +      return reply;
 +#else /* IEEE8021X_EAPOL */
 +      wpa_printf(MSG_DEBUG, "CTRL_IFACE: 802.1X not included");
 +      return wpas_dbus_error_unknown_error(message, "802.1X not included");
 +#endif /* IEEE8021X_EAPOL */
 +}
 +
 +
 +#ifndef CONFIG_NO_CONFIG_BLOBS
 +
 +/**
 + * wpas_dbus_handler_add_blob - Store named binary blob (ie, for certificates)
 + * @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
 + *
 + * Asks wpa_supplicant to internally store a binary blobs.
 + */
 +DBusMessage * wpas_dbus_handler_add_blob(DBusMessage *message,
 +                                       struct wpa_supplicant *wpa_s)
 +{
 +      DBusMessage *reply = NULL;
 +      DBusMessageIter iter, array_iter;
 +
 +      char *blob_name;
 +      u8 *blob_data;
 +      int blob_len;
 +      struct wpa_config_blob *blob = NULL;
 +
 +      dbus_message_iter_init(message, &iter);
 +      dbus_message_iter_get_basic(&iter, &blob_name);
 +
 +      if (wpa_config_get_blob(wpa_s->conf, blob_name)) {
 +              return dbus_message_new_error(message,
 +                                            WPAS_DBUS_ERROR_BLOB_EXISTS,
 +                                            NULL);
 +      }
 +
 +      dbus_message_iter_next(&iter);
 +      dbus_message_iter_recurse(&iter, &array_iter);
 +
 +      dbus_message_iter_get_fixed_array(&array_iter, &blob_data, &blob_len);
 +
 +      blob = os_zalloc(sizeof(*blob));
 +      if (!blob) {
 +              reply = wpas_dbus_error_no_memory(message);
 +              goto err;
 +      }
 +
 +      blob->data = os_malloc(blob_len);
 +      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;
 +
 +      wpa_config_set_blob(wpa_s->conf, blob);
 +      wpas_notify_blob_added(wpa_s, blob->name);
 +
 +      return reply;
 +
 +err:
 +      if (blob) {
 +              os_free(blob->name);
 +              os_free(blob->data);
 +              os_free(blob);
 +      }
 +      return reply;
 +}
 +
 +
 +/**
 + * wpas_dbus_handler_get_blob - Get named binary blob (ie, for certificates)
 + * @message: Pointer to incoming dbus message
 + * @wpa_s: %wpa_supplicant data structure
 + * Returns: A dbus message containing array of bytes (blob)
 + *
 + * Gets one wpa_supplicant's binary blobs.
 + */
 +DBusMessage * wpas_dbus_handler_get_blob(DBusMessage *message,
 +                                       struct wpa_supplicant *wpa_s)
 +{
 +      DBusMessage *reply = NULL;
 +      DBusMessageIter iter, array_iter;
 +
 +      char *blob_name;
 +      const struct wpa_config_blob *blob;
 +
 +      dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &blob_name,
 +                            DBUS_TYPE_INVALID);
 +
 +      blob = wpa_config_get_blob(wpa_s->conf, blob_name);
 +      if (!blob) {
 +              return dbus_message_new_error(message,
 +                                            WPAS_DBUS_ERROR_BLOB_UNKNOWN,
 +                                            "Blob id not set");
 +      }
 +
 +      reply = dbus_message_new_method_return(message);
 +      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_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 = wpas_dbus_error_no_memory(message);
 +      }
 +
 +      return reply;
 +}
 +
 +
 +/**
 + * wpas_remove_handler_remove_blob - Remove named binary blob
 + * @message: Pointer to incoming dbus message
 + * @wpa_s: %wpa_supplicant data structure
 + * Returns: NULL on success or dbus error
 + *
 + * Asks wpa_supplicant to internally remove a binary blobs.
 + */
 +DBusMessage * wpas_dbus_handler_remove_blob(DBusMessage *message,
 +                                          struct wpa_supplicant *wpa_s)
 +{
 +      DBusMessage *reply = NULL;
 +      char *blob_name;
 +
 +      dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &blob_name,
 +                            DBUS_TYPE_INVALID);
 +
 +      if (wpa_config_remove_blob(wpa_s->conf, blob_name)) {
 +              return dbus_message_new_error(message,
 +                                            WPAS_DBUS_ERROR_BLOB_UNKNOWN,
 +                                            "Blob id not set");
 +      }
 +      wpas_notify_blob_removed(wpa_s, blob_name);
 +
 +      return reply;
 +
 +}
 +
 +#endif /* CONFIG_NO_CONFIG_BLOBS */
 +
 +
 +/*
 + * wpas_dbus_handler_flush_bss - Flush the BSS cache
 + * @message: Pointer to incoming dbus message
 + * @wpa_s: wpa_supplicant structure for a network interface
 + * Returns: NULL
 + *
 + * Handler function for "FlushBSS" method call of network interface.
 + */
 +DBusMessage * wpas_dbus_handler_flush_bss(DBusMessage *message,
 +                                        struct wpa_supplicant *wpa_s)
 +{
 +      dbus_uint32_t age;
 +
 +      dbus_message_get_args(message, NULL, DBUS_TYPE_UINT32, &age,
 +                            DBUS_TYPE_INVALID);
 +
 +      if (age == 0)
 +              wpa_bss_flush(wpa_s);
 +      else
 +              wpa_bss_flush_by_age(wpa_s, age);
 +
 +      return NULL;
 +}
 +
 +
 +#ifdef CONFIG_AUTOSCAN
 +/**
 + * wpas_dbus_handler_autoscan - Set autoscan parameters for the interface
 + * @message: Pointer to incoming dbus message
 + * @wpa_s: wpa_supplicant structure for a network interface
 + * Returns: NULL
 + *
 + * Handler function for "AutoScan" method call of network interface.
 + */
 +DBusMessage * wpas_dbus_handler_autoscan(DBusMessage *message,
 +                                       struct wpa_supplicant *wpa_s)
 +{
 +      DBusMessage *reply = NULL;
 +      enum wpa_states state = wpa_s->wpa_state;
 +      char *arg;
 +
 +      dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &arg,
 +                            DBUS_TYPE_INVALID);
 +
 +      if (arg != NULL && os_strlen(arg) > 0) {
 +              char *tmp;
 +
 +              tmp = os_strdup(arg);
 +              if (tmp == NULL) {
 +                      reply = wpas_dbus_error_no_memory(message);
 +              } else {
 +                      os_free(wpa_s->conf->autoscan);
 +                      wpa_s->conf->autoscan = tmp;
 +                      if (state == WPA_DISCONNECTED || state == WPA_INACTIVE)
 +                              autoscan_init(wpa_s, 1);
 +                      else if (state == WPA_SCANNING)
 +                              wpa_supplicant_reinit_autoscan(wpa_s);
 +              }
 +      } else if (arg != NULL && os_strlen(arg) == 0) {
 +              os_free(wpa_s->conf->autoscan);
 +              wpa_s->conf->autoscan = NULL;
 +              autoscan_deinit(wpa_s);
 +      } else
 +              reply = dbus_message_new_error(message,
 +                                             DBUS_ERROR_INVALID_ARGS,
 +                                             NULL);
 +
 +      return reply;
 +}
 +#endif /* CONFIG_AUTOSCAN */
 +
 +
 +/*
 + * wpas_dbus_handler_eap_logoff - IEEE 802.1X EAPOL state machine logoff
 + * @message: Pointer to incoming dbus message
 + * @wpa_s: wpa_supplicant structure for a network interface
 + * Returns: NULL
 + *
 + * Handler function for "EAPLogoff" method call of network interface.
 + */
 +DBusMessage * wpas_dbus_handler_eap_logoff(DBusMessage *message,
 +                                         struct wpa_supplicant *wpa_s)
 +{
 +      eapol_sm_notify_logoff(wpa_s->eapol, TRUE);
 +      return NULL;
 +}
 +
 +
 +/*
 + * wpas_dbus_handler_eap_logon - IEEE 802.1X EAPOL state machine logon
 + * @message: Pointer to incoming dbus message
 + * @wpa_s: wpa_supplicant structure for a network interface
 + * Returns: NULL
 + *
 + * Handler function for "EAPLogin" method call of network interface.
 + */
 +DBusMessage * wpas_dbus_handler_eap_logon(DBusMessage *message,
 +                                        struct wpa_supplicant *wpa_s)
 +{
 +      eapol_sm_notify_logoff(wpa_s->eapol, FALSE);
 +      return NULL;
 +}
 +
 +
 +#ifdef CONFIG_TDLS
 +
 +static int get_peer_hwaddr_helper(DBusMessage *message, const char *func_name,
 +                                u8 *peer_address, DBusMessage **error)
 +{
 +      const char *peer_string;
 +
 +      *error = NULL;
 +
 +      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;
 +      }
 +
 +      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;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +/*
 + * 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 (get_peer_hwaddr_helper(message, __func__, peer, &error_reply) < 0)
 +              return error_reply;
 +
 +      wpa_printf(MSG_DEBUG, "DBUS TDLS_DISCOVER " MACSTR, MAC2STR(peer));
 +
 +      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.");
 +
-       if (wpa_s->current_bss)
++      if (wpa_s->dbus_new_path) {
++              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))
 +                      goto nomem;
 +      }
 +
 +      /***** group cipher */
 +      if (res < 0) {
 +              const char *args[] = {
 +                      "ccmp", "tkip", "wep104", "wep40"
 +              };
 +
 +              if (!wpa_dbus_dict_append_string_array(
 +                          &iter_dict, "Group", args,
 +                          ARRAY_SIZE(args)))
 +                      goto nomem;
 +      } else {
 +              if (!wpa_dbus_dict_begin_string_array(&iter_dict, "Group",
 +                                                    &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.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))
 +                      goto nomem;
 +      }
 +
 +      /***** key management */
 +      if (res < 0) {
 +              const char *args[] = {
 +                      "wpa-psk", "wpa-eap", "ieee8021x", "wpa-none",
 +#ifdef CONFIG_WPS
 +                      "wps",
 +#endif /* CONFIG_WPS */
 +                      "none"
 +              };
 +              if (!wpa_dbus_dict_append_string_array(
 +                          &iter_dict, "KeyMgmt", args,
 +                          ARRAY_SIZE(args)))
 +                      goto nomem;
 +      } else {
 +              if (!wpa_dbus_dict_begin_string_array(&iter_dict, "KeyMgmt",
 +                                                    &iter_dict_entry,
 +                                                    &iter_dict_val,
 +                                                    &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") ||
 +                          ((capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FT) &&
 +                           !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(
 +                                  &iter_array, "wpa-eap-sha256"))
 +                              goto nomem;
 +#endif /* CONFIG_IEEE80211W */
 +              }
 +
 +              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") ||
 +                          ((capa.key_mgmt &
 +                            WPA_DRIVER_CAPA_KEY_MGMT_FT_PSK) &&
 +                           !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(
 +                                  &iter_array, "wpa-psk-sha256"))
 +                              goto nomem;
 +#endif /* CONFIG_IEEE80211W */
 +              }
 +
 +              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
 +              if (!wpa_dbus_dict_string_array_add_element(&iter_array,
 +                                                          "wps"))
 +                      goto nomem;
 +#endif /* CONFIG_WPS */
 +
 +              if (!wpa_dbus_dict_end_string_array(&iter_dict,
 +                                                  &iter_dict_entry,
 +                                                  &iter_dict_val,
 +                                                  &iter_array))
 +                      goto nomem;
 +      }
 +
 +      /***** WPA protocol */
 +      if (res < 0) {
 +              const char *args[] = { "rsn", "wpa" };
 +
 +              if (!wpa_dbus_dict_append_string_array(
 +                          &iter_dict, "Protocol", args,
 +                          ARRAY_SIZE(args)))
 +                      goto nomem;
 +      } else {
 +              if (!wpa_dbus_dict_begin_string_array(&iter_dict, "Protocol",
 +                                                    &iter_dict_entry,
 +                                                    &iter_dict_val,
 +                                                    &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))
 +                      goto nomem;
 +      }
 +
 +      /***** auth alg */
 +      if (res < 0) {
 +              const char *args[] = { "open", "shared", "leap" };
 +
 +              if (!wpa_dbus_dict_append_string_array(
 +                          &iter_dict, "AuthAlg", args,
 +                          ARRAY_SIZE(args)))
 +                      goto nomem;
 +      } else {
 +              if (!wpa_dbus_dict_begin_string_array(&iter_dict, "AuthAlg",
 +                                                    &iter_dict_entry,
 +                                                    &iter_dict_val,
 +                                                    &iter_array))
 +                      goto nomem;
 +
 +              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))
 +                      goto nomem;
 +      }
 +
 +      /***** Scan */
 +      if (!wpa_dbus_dict_append_string_array(&iter_dict, "Scan", scans,
 +                                             ARRAY_SIZE(scans)))
 +              goto nomem;
 +
 +      /***** Modes */
 +      if (!wpa_dbus_dict_begin_string_array(&iter_dict, "Modes",
 +                                            &iter_dict_entry,
 +                                            &iter_dict_val,
 +                                            &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))
 +              goto nomem;
 +      /***** Modes end */
 +
 +      if (res >= 0) {
 +              dbus_int32_t max_scan_ssid = capa.max_scan_ssids;
 +
 +              if (!wpa_dbus_dict_append_int32(&iter_dict, "MaxScanSSID",
 +                                              max_scan_ssid))
 +                      goto nomem;
 +      }
 +
 +      if (!wpa_dbus_dict_close_write(&variant_iter, &iter_dict) ||
 +          !dbus_message_iter_close_container(iter, &variant_iter))
 +              goto nomem;
 +
 +      return TRUE;
 +
 +nomem:
 +      dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
 +      return FALSE;
 +}
 +
 +
 +/**
 + * wpas_dbus_getter_state - Get interface 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 "State" property.
 + */
 +dbus_bool_t wpas_dbus_getter_state(DBusMessageIter *iter, DBusError *error,
 +                                 void *user_data)
 +{
 +      struct wpa_supplicant *wpa_s = user_data;
 +      const char *str_state;
 +      char *state_ls, *tmp;
 +      dbus_bool_t success = FALSE;
 +
 +      str_state = wpa_supplicant_state_txt(wpa_s->wpa_state);
 +
 +      /* make state string lowercase to fit new DBus API convention
 +       */
 +      state_ls = tmp = os_strdup(str_state);
 +      if (!tmp) {
 +              dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
 +              return FALSE;
 +      }
 +      while (*tmp) {
 +              *tmp = tolower(*tmp);
 +              tmp++;
 +      }
 +
 +      success = wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING,
 +                                                 &state_ls, error);
 +
 +      os_free(state_ls);
 +
 +      return success;
 +}
 +
 +
 +/**
 + * wpas_dbus_new_iface_get_scanning - Get interface scanning 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 "scanning" property.
 + */
 +dbus_bool_t wpas_dbus_getter_scanning(DBusMessageIter *iter, DBusError *error,
 +                                    void *user_data)
 +{
 +      struct wpa_supplicant *wpa_s = user_data;
 +      dbus_bool_t scanning = wpa_s->scanning ? TRUE : FALSE;
 +
 +      return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_BOOLEAN,
 +                                              &scanning, error);
 +}
 +
 +
 +/**
 + * wpas_dbus_getter_ap_scan - Control roaming mode
 + * @iter: Pointer to incoming dbus message iter
 + * @error: Location to store error on failure
 + * @user_data: Function specific data
 + * Returns: TRUE on success, FALSE on failure
 + *
 + * Getter function for "ApScan" property.
 + */
 +dbus_bool_t wpas_dbus_getter_ap_scan(DBusMessageIter *iter, DBusError *error,
 +                                   void *user_data)
 +{
 +      struct wpa_supplicant *wpa_s = user_data;
 +      dbus_uint32_t ap_scan = wpa_s->conf->ap_scan;
 +
 +      return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_UINT32,
 +                                              &ap_scan, error);
 +}
 +
 +
 +/**
 + * wpas_dbus_setter_ap_scan - Control roaming mode
 + * @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 function for "ApScan" property.
 + */
 +dbus_bool_t wpas_dbus_setter_ap_scan(DBusMessageIter *iter, DBusError *error,
 +                                   void *user_data)
 +{
 +      struct wpa_supplicant *wpa_s = user_data;
 +      dbus_uint32_t ap_scan;
 +
 +      if (!wpas_dbus_simple_property_setter(iter, error, DBUS_TYPE_UINT32,
 +                                            &ap_scan))
 +              return FALSE;
 +
 +      if (wpa_supplicant_set_ap_scan(wpa_s, ap_scan)) {
 +              dbus_set_error_const(error, DBUS_ERROR_FAILED,
 +                                   "ap_scan must be 0, 1, or 2");
 +              return FALSE;
 +      }
 +      return TRUE;
 +}
 +
 +
 +/**
 + * wpas_dbus_getter_fast_reauth - Control fast
 + * reauthentication (TLS session resumption)
 + * @iter: Pointer to incoming dbus message iter
 + * @error: Location to store error on failure
 + * @user_data: Function specific data
 + * Returns: TRUE on success, FALSE on failure
 + *
 + * Getter function for "FastReauth" property.
 + */
 +dbus_bool_t wpas_dbus_getter_fast_reauth(DBusMessageIter *iter,
 +                                       DBusError *error,
 +                                       void *user_data)
 +{
 +      struct wpa_supplicant *wpa_s = user_data;
 +      dbus_bool_t fast_reauth = wpa_s->conf->fast_reauth ? TRUE : FALSE;
 +
 +      return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_BOOLEAN,
 +                                              &fast_reauth, error);
 +}
 +
 +
 +/**
 + * wpas_dbus_setter_fast_reauth - Control fast
 + * reauthentication (TLS session resumption)
 + * @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 function for "FastReauth" property.
 + */
 +dbus_bool_t wpas_dbus_setter_fast_reauth(DBusMessageIter *iter,
 +                                   DBusError *error,
 +                                   void *user_data)
 +{
 +      struct wpa_supplicant *wpa_s = user_data;
 +      dbus_bool_t fast_reauth;
 +
 +      if (!wpas_dbus_simple_property_setter(iter, error, DBUS_TYPE_BOOLEAN,
 +                                            &fast_reauth))
 +              return FALSE;
 +
 +      wpa_s->conf->fast_reauth = fast_reauth;
 +      return TRUE;
 +}
 +
 +
 +/**
 + * wpas_dbus_getter_disconnect_reason - Get most recent reason for disconnect
 + * @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 "DisconnectReason" property.  The reason is negative if it is
 + * locally generated.
 + */
 +dbus_bool_t wpas_dbus_getter_disconnect_reason(DBusMessageIter *iter,
 +                                             DBusError *error,
 +                                             void *user_data)
 +{
 +      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);
 +}
 +
 +
 +/**
 + * wpas_dbus_getter_bss_expire_age - Get BSS entry expiration age
 + * @iter: Pointer to incoming dbus message iter
 + * @error: Location to store error on failure
 + * @user_data: Function specific data
 + * Returns: TRUE on success, FALSE on failure
 + *
 + * Getter function for "BSSExpireAge" property.
 + */
 +dbus_bool_t wpas_dbus_getter_bss_expire_age(DBusMessageIter *iter,
 +                                          DBusError *error,
 +                                          void *user_data)
 +{
 +      struct wpa_supplicant *wpa_s = user_data;
 +      dbus_uint32_t expire_age = wpa_s->conf->bss_expiration_age;
 +
 +      return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_UINT32,
 +                                              &expire_age, error);
 +}
 +
 +
 +/**
 + * wpas_dbus_setter_bss_expire_age - Control BSS entry expiration age
 + * @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 function for "BSSExpireAge" property.
 + */
 +dbus_bool_t wpas_dbus_setter_bss_expire_age(DBusMessageIter *iter,
 +                                          DBusError *error,
 +                                          void *user_data)
 +{
 +      struct wpa_supplicant *wpa_s = user_data;
 +      dbus_uint32_t expire_age;
 +
 +      if (!wpas_dbus_simple_property_setter(iter, error, DBUS_TYPE_UINT32,
 +                                            &expire_age))
 +              return FALSE;
 +
 +      if (wpa_supplicant_set_bss_expiration_age(wpa_s, expire_age)) {
 +              dbus_set_error_const(error, DBUS_ERROR_FAILED,
 +                                   "BSSExpireAge must be >= 10");
 +              return FALSE;
 +      }
 +      return TRUE;
 +}
 +
 +
 +/**
 + * wpas_dbus_getter_bss_expire_count - Get BSS entry expiration scan count
 + * @iter: Pointer to incoming dbus message iter
 + * @error: Location to store error on failure
 + * @user_data: Function specific data
 + * Returns: TRUE on success, FALSE on failure
 + *
 + * Getter function for "BSSExpireCount" property.
 + */
 +dbus_bool_t wpas_dbus_getter_bss_expire_count(DBusMessageIter *iter,
 +                                            DBusError *error,
 +                                            void *user_data)
 +{
 +      struct wpa_supplicant *wpa_s = user_data;
 +      dbus_uint32_t expire_count = wpa_s->conf->bss_expiration_scan_count;
 +
 +      return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_UINT32,
 +                                              &expire_count, error);
 +}
 +
 +
 +/**
 + * wpas_dbus_setter_bss_expire_count - Control BSS entry expiration scan count
 + * @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 function for "BSSExpireCount" property.
 + */
 +dbus_bool_t wpas_dbus_setter_bss_expire_count(DBusMessageIter *iter,
 +                                            DBusError *error,
 +                                            void *user_data)
 +{
 +      struct wpa_supplicant *wpa_s = user_data;
 +      dbus_uint32_t expire_count;
 +
 +      if (!wpas_dbus_simple_property_setter(iter, error, DBUS_TYPE_UINT32,
 +                                            &expire_count))
 +              return FALSE;
 +
 +      if (wpa_supplicant_set_bss_expiration_count(wpa_s, expire_count)) {
 +              dbus_set_error_const(error, DBUS_ERROR_FAILED,
 +                                   "BSSExpireCount must be > 0");
 +              return FALSE;
 +      }
 +      return TRUE;
 +}
 +
 +
 +/**
 + * wpas_dbus_getter_country - Control country code
 + * @iter: Pointer to incoming dbus message iter
 + * @error: Location to store error on failure
 + * @user_data: Function specific data
 + * Returns: TRUE on success, FALSE on failure
 + *
 + * Getter function for "Country" property.
 + */
 +dbus_bool_t wpas_dbus_getter_country(DBusMessageIter *iter, DBusError *error,
 +                                   void *user_data)
 +{
 +      struct wpa_supplicant *wpa_s = user_data;
 +      char country[3];
 +      char *str = country;
 +
 +      country[0] = wpa_s->conf->country[0];
 +      country[1] = wpa_s->conf->country[1];
 +      country[2] = '\0';
 +
 +      return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING,
 +                                              &str, error);
 +}
 +
 +
 +/**
 + * wpas_dbus_setter_country - Control country code
 + * @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 function for "Country" property.
 + */
 +dbus_bool_t wpas_dbus_setter_country(DBusMessageIter *iter, DBusError *error,
 +                                   void *user_data)
 +{
 +      struct wpa_supplicant *wpa_s = user_data;
 +      const char *country;
 +
 +      if (!wpas_dbus_simple_property_setter(iter, error, DBUS_TYPE_STRING,
 +                                            &country))
 +              return FALSE;
 +
 +      if (!country[0] || !country[1]) {
 +              dbus_set_error_const(error, DBUS_ERROR_FAILED,
 +                                   "invalid country code");
 +              return FALSE;
 +      }
 +
 +      if (wpa_s->drv_priv != NULL && wpa_drv_set_country(wpa_s, country)) {
 +              wpa_printf(MSG_DEBUG, "Failed to set country");
 +              dbus_set_error_const(error, DBUS_ERROR_FAILED,
 +                                   "failed to set country code");
 +              return FALSE;
 +      }
 +
 +      wpa_s->conf->country[0] = country[0];
 +      wpa_s->conf->country[1] = country[1];
 +      return TRUE;
 +}
 +
 +
 +/**
 + * wpas_dbus_getter_scan_interval - Get scan interval
 + * @iter: Pointer to incoming dbus message iter
 + * @error: Location to store error on failure
 + * @user_data: Function specific data
 + * Returns: TRUE on success, FALSE on failure
 + *
 + * Getter function for "ScanInterval" property.
 + */
 +dbus_bool_t wpas_dbus_getter_scan_interval(DBusMessageIter *iter,
 +                                         DBusError *error,
 +                                         void *user_data)
 +{
 +      struct wpa_supplicant *wpa_s = user_data;
 +      dbus_int32_t scan_interval = wpa_s->scan_interval;
 +
 +      return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_INT32,
 +                                              &scan_interval, error);
 +}
 +
 +
 +/**
 + * wpas_dbus_setter_scan_interval - Control scan interval
 + * @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 function for "ScanInterval" property.
 + */
 +dbus_bool_t wpas_dbus_setter_scan_interval(DBusMessageIter *iter,
 +                                         DBusError *error,
 +                                         void *user_data)
 +{
 +      struct wpa_supplicant *wpa_s = user_data;
 +      dbus_int32_t scan_interval;
 +
 +      if (!wpas_dbus_simple_property_setter(iter, error, DBUS_TYPE_INT32,
 +                                            &scan_interval))
 +              return FALSE;
 +
 +      if (wpa_supplicant_set_scan_interval(wpa_s, scan_interval)) {
 +              dbus_set_error_const(error, DBUS_ERROR_FAILED,
 +                                   "scan_interval must be >= 0");
 +              return FALSE;
 +      }
 +      return TRUE;
 +}
 +
 +
 +/**
 + * wpas_dbus_getter_ifname - Get interface name
 + * @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 "Ifname" property.
 + */
 +dbus_bool_t wpas_dbus_getter_ifname(DBusMessageIter *iter, DBusError *error,
 +                                  void *user_data)
 +{
 +      struct wpa_supplicant *wpa_s = user_data;
 +      const char *ifname = wpa_s->ifname;
 +
 +      return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING,
 +                                              &ifname, error);
 +}
 +
 +
 +/**
 + * wpas_dbus_getter_driver - Get interface name
 + * @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 "Driver" property.
 + */
 +dbus_bool_t wpas_dbus_getter_driver(DBusMessageIter *iter, DBusError *error,
 +                                  void *user_data)
 +{
 +      struct wpa_supplicant *wpa_s = user_data;
 +      const char *driver;
 +
 +      if (wpa_s->driver == NULL || wpa_s->driver->name == NULL) {
 +              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;
 +      }
 +
 +      driver = wpa_s->driver->name;
 +      return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING,
 +                                              &driver, error);
 +}
 +
 +
 +/**
 + * wpas_dbus_getter_current_bss - Get current bss object path
 + * @iter: Pointer to incoming dbus message iter
 + * @error: Location to store error on failure
 + * @user_data: Function specific data
 + * Returns: TRUE on success, FALSE on failure
 + *
 + * Getter for "CurrentBSS" property.
 + */
 +dbus_bool_t wpas_dbus_getter_current_bss(DBusMessageIter *iter,
 +                                       DBusError *error,
 +                                       void *user_data)
 +{
 +      struct wpa_supplicant *wpa_s = user_data;
 +      char path_buf[WPAS_DBUS_OBJECT_PATH_MAX], *bss_obj_path = path_buf;
 +
-       if (wpa_s->current_ssid)
++      if (wpa_s->current_bss && wpa_s->dbus_new_path)
 +              os_snprintf(bss_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
 +                          "%s/" WPAS_DBUS_NEW_BSSIDS_PART "/%u",
 +                          wpa_s->dbus_new_path, wpa_s->current_bss->id);
 +      else
 +              os_snprintf(bss_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, "/");
 +
 +      return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_OBJECT_PATH,
 +                                              &bss_obj_path, error);
 +}
 +
 +
 +/**
 + * wpas_dbus_getter_current_network - Get current network object path
 + * @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 "CurrentNetwork" property.
 + */
 +dbus_bool_t wpas_dbus_getter_current_network(DBusMessageIter *iter,
 +                                           DBusError *error,
 +                                           void *user_data)
 +{
 +      struct wpa_supplicant *wpa_s = user_data;
 +      char path_buf[WPAS_DBUS_OBJECT_PATH_MAX], *net_obj_path = path_buf;
 +
-       if (!wpa_dbus_dict_append_string(&iter_dict, "Type", type) ||
++      if (wpa_s->current_ssid && wpa_s->dbus_new_path)
 +              os_snprintf(net_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
 +                          "%s/" WPAS_DBUS_NEW_NETWORKS_PART "/%u",
 +                          wpa_s->dbus_new_path, wpa_s->current_ssid->id);
 +      else
 +              os_snprintf(net_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, "/");
 +
 +      return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_OBJECT_PATH,
 +                                              &net_obj_path, error);
 +}
 +
 +
 +/**
 + * wpas_dbus_getter_current_auth_mode - Get current authentication type
 + * @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 "CurrentAuthMode" property.
 + */
 +dbus_bool_t wpas_dbus_getter_current_auth_mode(DBusMessageIter *iter,
 +                                             DBusError *error,
 +                                             void *user_data)
 +{
 +      struct wpa_supplicant *wpa_s = user_data;
 +      const char *eap_mode;
 +      const char *auth_mode;
 +      char eap_mode_buf[WPAS_DBUS_AUTH_MODE_MAX];
 +
 +      if (wpa_s->wpa_state != WPA_COMPLETED) {
 +              auth_mode = "INACTIVE";
 +      } else if (wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X ||
 +          wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) {
 +              eap_mode = wpa_supplicant_get_eap_mode(wpa_s);
 +              os_snprintf(eap_mode_buf, WPAS_DBUS_AUTH_MODE_MAX,
 +                          "EAP-%s", eap_mode);
 +              auth_mode = eap_mode_buf;
 +
 +      } else {
 +              auth_mode = wpa_key_mgmt_txt(wpa_s->key_mgmt,
 +                                           wpa_s->current_ssid->proto);
 +      }
 +
 +      return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING,
 +                                              &auth_mode, error);
 +}
 +
 +
 +/**
 + * wpas_dbus_getter_bridge_ifname - Get interface name
 + * @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 "BridgeIfname" property.
 + */
 +dbus_bool_t wpas_dbus_getter_bridge_ifname(DBusMessageIter *iter,
 +                                         DBusError *error,
 +                                         void *user_data)
 +{
 +      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);
 +}
 +
 +
 +/**
 + * wpas_dbus_getter_bsss - Get array of BSSs objects
 + * @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 "BSSs" property.
 + */
 +dbus_bool_t wpas_dbus_getter_bsss(DBusMessageIter *iter, DBusError *error,
 +                                void *user_data)
 +{
 +      struct wpa_supplicant *wpa_s = user_data;
 +      struct wpa_bss *bss;
 +      char **paths;
 +      unsigned int i = 0;
 +      dbus_bool_t success = FALSE;
 +
++      if (!wpa_s->dbus_new_path) {
++              dbus_set_error(error, DBUS_ERROR_FAILED,
++                             "%s: no D-Bus interface", __func__);
++              return FALSE;
++      }
++
 +      paths = os_calloc(wpa_s->num_bss, sizeof(char *));
 +      if (!paths) {
 +              dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
 +              return FALSE;
 +      }
 +
 +      /* 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) {
 +              paths[i] = os_zalloc(WPAS_DBUS_OBJECT_PATH_MAX);
 +              if (paths[i] == NULL) {
 +                      dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY,
 +                                           "no memory");
 +                      goto out;
 +              }
 +              /* Construct the object path for this BSS. */
 +              os_snprintf(paths[i++], WPAS_DBUS_OBJECT_PATH_MAX,
 +                          "%s/" WPAS_DBUS_NEW_BSSIDS_PART "/%u",
 +                          wpa_s->dbus_new_path, bss->id);
 +      }
 +
 +      success = wpas_dbus_simple_array_property_getter(iter,
 +                                                       DBUS_TYPE_OBJECT_PATH,
 +                                                       paths, wpa_s->num_bss,
 +                                                       error);
 +
 +out:
 +      while (i)
 +              os_free(paths[--i]);
 +      os_free(paths);
 +      return success;
 +}
 +
 +
 +/**
 + * wpas_dbus_getter_networks - Get array of networks objects
 + * @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 "Networks" property.
 + */
 +dbus_bool_t wpas_dbus_getter_networks(DBusMessageIter *iter, DBusError *error,
 +                                    void *user_data)
 +{
 +      struct wpa_supplicant *wpa_s = user_data;
 +      struct wpa_ssid *ssid;
 +      char **paths;
 +      unsigned int i = 0, num = 0;
 +      dbus_bool_t success = FALSE;
 +
++      if (!wpa_s->dbus_new_path) {
++              dbus_set_error(error, DBUS_ERROR_FAILED,
++                             "%s: no D-Bus interface", __func__);
++              return FALSE;
++      }
++
 +      for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next)
 +              if (!network_is_persistent_group(ssid))
 +                      num++;
 +
 +      paths = os_calloc(num, sizeof(char *));
 +      if (!paths) {
 +              dbus_set_error(error, DBUS_ERROR_NO_MEMORY, "no memory");
 +              return FALSE;
 +      }
 +
 +      /* Loop through configured networks and append object path of each */
 +      for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
 +              if (network_is_persistent_group(ssid))
 +                      continue;
 +              paths[i] = os_zalloc(WPAS_DBUS_OBJECT_PATH_MAX);
 +              if (paths[i] == NULL) {
 +                      dbus_set_error(error, DBUS_ERROR_NO_MEMORY,
 +                                     "no memory");
 +                      goto out;
 +              }
 +
 +              /* Construct the object path for this network. */
 +              os_snprintf(paths[i++], WPAS_DBUS_OBJECT_PATH_MAX,
 +                          "%s/" WPAS_DBUS_NEW_NETWORKS_PART "/%d",
 +                          wpa_s->dbus_new_path, ssid->id);
 +      }
 +
 +      success = wpas_dbus_simple_array_property_getter(iter,
 +                                                       DBUS_TYPE_OBJECT_PATH,
 +                                                       paths, num, error);
 +
 +out:
 +      while (i)
 +              os_free(paths[--i]);
 +      os_free(paths);
 +      return success;
 +}
 +
 +
 +/**
 + * 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
 + * @user_data: Function specific data
 + * Returns: TRUE on success, FALSE on failure
 + *
 + * Getter for "Blobs" property.
 + */
 +dbus_bool_t wpas_dbus_getter_blobs(DBusMessageIter *iter, DBusError *error,
 +                                 void *user_data)
 +{
 +      struct wpa_supplicant *wpa_s = user_data;
 +      DBusMessageIter variant_iter, dict_iter, entry_iter, array_iter;
 +      struct wpa_config_blob *blob;
 +
 +      if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
 +                                            "a{say}", &variant_iter) ||
 +          !dbus_message_iter_open_container(&variant_iter, DBUS_TYPE_ARRAY,
 +                                            "{say}", &dict_iter)) {
 +              dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
 +              return FALSE;
 +      }
 +
 +      blob = wpa_s->conf->blobs;
 +      while (blob) {
 +              if (!dbus_message_iter_open_container(&dict_iter,
 +                                                    DBUS_TYPE_DICT_ENTRY,
 +                                                    NULL, &entry_iter) ||
 +                  !dbus_message_iter_append_basic(&entry_iter,
 +                                                  DBUS_TYPE_STRING,
 +                                                  &(blob->name)) ||
 +                  !dbus_message_iter_open_container(&entry_iter,
 +                                                    DBUS_TYPE_ARRAY,
 +                                                    DBUS_TYPE_BYTE_AS_STRING,
 +                                                    &array_iter) ||
 +                  !dbus_message_iter_append_fixed_array(&array_iter,
 +                                                        DBUS_TYPE_BYTE,
 +                                                        &(blob->data),
 +                                                        blob->len) ||
 +                  !dbus_message_iter_close_container(&entry_iter,
 +                                                     &array_iter) ||
 +                  !dbus_message_iter_close_container(&dict_iter,
 +                                                     &entry_iter)) {
 +                      dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY,
 +                                           "no memory");
 +                      return FALSE;
 +              }
 +
 +              blob = blob->next;
 +      }
 +
 +      if (!dbus_message_iter_close_container(&variant_iter, &dict_iter) ||
 +          !dbus_message_iter_close_container(iter, &variant_iter)) {
 +              dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
 +              return FALSE;
 +      }
 +
 +      return TRUE;
 +}
 +
 +
 +static struct wpa_bss * get_bss_helper(struct bss_handler_args *args,
 +                                     DBusError *error, const char *func_name)
 +{
 +      struct wpa_bss *res = wpa_bss_get_id(args->wpa_s, args->id);
 +
 +      if (!res) {
 +              wpa_printf(MSG_ERROR, "%s[dbus]: no bss with id %d found",
 +                         func_name, args->id);
 +              dbus_set_error(error, DBUS_ERROR_FAILED,
 +                             "%s: BSS %d not found",
 +                             func_name, args->id);
 +      }
 +
 +      return res;
 +}
 +
 +
 +/**
 + * wpas_dbus_getter_bss_bssid - Return the BSSID of a BSS
 + * @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 "BSSID" property.
 + */
 +dbus_bool_t wpas_dbus_getter_bss_bssid(DBusMessageIter *iter, DBusError *error,
 +                                     void *user_data)
 +{
 +      struct bss_handler_args *args = user_data;
 +      struct wpa_bss *res;
 +
 +      res = get_bss_helper(args, error, __func__);
 +      if (!res)
 +              return FALSE;
 +
 +      return wpas_dbus_simple_array_property_getter(iter, DBUS_TYPE_BYTE,
 +                                                    res->bssid, ETH_ALEN,
 +                                                    error);
 +}
 +
 +
 +/**
 + * wpas_dbus_getter_bss_ssid - Return the SSID of a BSS
 + * @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 "SSID" property.
 + */
 +dbus_bool_t wpas_dbus_getter_bss_ssid(DBusMessageIter *iter, DBusError *error,
 +                                    void *user_data)
 +{
 +      struct bss_handler_args *args = user_data;
 +      struct wpa_bss *res;
 +
 +      res = get_bss_helper(args, error, __func__);
 +      if (!res)
 +              return FALSE;
 +
 +      return wpas_dbus_simple_array_property_getter(iter, DBUS_TYPE_BYTE,
 +                                                    res->ssid, res->ssid_len,
 +                                                    error);
 +}
 +
 +
 +/**
 + * wpas_dbus_getter_bss_privacy - Return the privacy flag of a BSS
 + * @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 "Privacy" property.
 + */
 +dbus_bool_t wpas_dbus_getter_bss_privacy(DBusMessageIter *iter,
 +                                       DBusError *error, void *user_data)
 +{
 +      struct bss_handler_args *args = user_data;
 +      struct wpa_bss *res;
 +      dbus_bool_t privacy;
 +
 +      res = get_bss_helper(args, error, __func__);
 +      if (!res)
 +              return FALSE;
 +
 +      privacy = (res->caps & IEEE80211_CAP_PRIVACY) ? TRUE : FALSE;
 +      return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_BOOLEAN,
 +                                              &privacy, error);
 +}
 +
 +
 +/**
 + * wpas_dbus_getter_bss_mode - Return the mode of a BSS
 + * @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 "Mode" property.
 + */
 +dbus_bool_t wpas_dbus_getter_bss_mode(DBusMessageIter *iter, DBusError *error,
 +                                    void *user_data)
 +{
 +      struct bss_handler_args *args = user_data;
 +      struct wpa_bss *res;
 +      const char *mode;
 +
 +      res = get_bss_helper(args, error, __func__);
 +      if (!res)
 +              return FALSE;
 +      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);
 +}
 +
 +
 +/**
 + * wpas_dbus_getter_bss_level - Return the signal strength of a BSS
 + * @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 "Level" property.
 + */
 +dbus_bool_t wpas_dbus_getter_bss_signal(DBusMessageIter *iter,
 +                                      DBusError *error, void *user_data)
 +{
 +      struct bss_handler_args *args = user_data;
 +      struct wpa_bss *res;
 +      s16 level;
 +
 +      res = get_bss_helper(args, error, __func__);
 +      if (!res)
 +              return FALSE;
 +
 +      level = (s16) res->level;
 +      return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_INT16,
 +                                              &level, error);
 +}
 +
 +
 +/**
 + * wpas_dbus_getter_bss_frequency - Return the frequency of a BSS
 + * @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 "Frequency" property.
 + */
 +dbus_bool_t wpas_dbus_getter_bss_frequency(DBusMessageIter *iter,
 +                                         DBusError *error, void *user_data)
 +{
 +      struct bss_handler_args *args = user_data;
 +      struct wpa_bss *res;
 +      u16 freq;
 +
 +      res = get_bss_helper(args, error, __func__);
 +      if (!res)
 +              return FALSE;
 +
 +      freq = (u16) res->freq;
 +      return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_UINT16,
 +                                              &freq, error);
 +}
 +
 +
 +static int cmp_u8s_desc(const void *a, const void *b)
 +{
 +      return (*(u8 *) b - *(u8 *) a);
 +}
 +
 +
 +/**
 + * wpas_dbus_getter_bss_rates - Return available bit rates of a BSS
 + * @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 "Rates" property.
 + */
 +dbus_bool_t wpas_dbus_getter_bss_rates(DBusMessageIter *iter,
 +                                     DBusError *error, void *user_data)
 +{
 +      struct bss_handler_args *args = user_data;
 +      struct wpa_bss *res;
 +      u8 *ie_rates = NULL;
 +      u32 *real_rates;
 +      int rates_num, i;
 +      dbus_bool_t success = FALSE;
 +
 +      res = get_bss_helper(args, error, __func__);
 +      if (!res)
 +              return FALSE;
 +
 +      rates_num = wpa_bss_get_bit_rates(res, &ie_rates);
 +      if (rates_num < 0)
 +              return FALSE;
 +
 +      qsort(ie_rates, rates_num, 1, cmp_u8s_desc);
 +
 +      real_rates = os_malloc(sizeof(u32) * rates_num);
 +      if (!real_rates) {
 +              os_free(ie_rates);
 +              dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
 +              return FALSE;
 +      }
 +
 +      for (i = 0; i < rates_num; i++)
 +              real_rates[i] = ie_rates[i] * 500000;
 +
 +      success = wpas_dbus_simple_array_property_getter(iter, DBUS_TYPE_UINT32,
 +                                                       real_rates, rates_num,
 +                                                       error);
 +
 +      os_free(ie_rates);
 +      os_free(real_rates);
 +      return success;
 +}
 +
 +
 +static dbus_bool_t wpas_dbus_get_bss_security_prop(DBusMessageIter *iter,
 +                                                 struct wpa_ie_data *ie_data,
 +                                                 DBusError *error)
 +{
 +      DBusMessageIter iter_dict, variant_iter;
 +      const char *group;
 +      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,
 +                                            "a{sv}", &variant_iter))
 +              goto nomem;
 +
 +      if (!wpa_dbus_dict_open_write(&variant_iter, &iter_dict))
 +              goto nomem;
 +
 +      /* KeyMgmt */
 +      n = 0;
 +      if (ie_data->key_mgmt & WPA_KEY_MGMT_PSK)
 +              key_mgmt[n++] = "wpa-psk";
 +      if (ie_data->key_mgmt & WPA_KEY_MGMT_FT_PSK)
 +              key_mgmt[n++] = "wpa-ft-psk";
 +      if (ie_data->key_mgmt & WPA_KEY_MGMT_PSK_SHA256)
 +              key_mgmt[n++] = "wpa-psk-sha256";
 +      if (ie_data->key_mgmt & WPA_KEY_MGMT_IEEE8021X)
 +              key_mgmt[n++] = "wpa-eap";
 +      if (ie_data->key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X)
 +              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";
 +
 +      if (!wpa_dbus_dict_append_string_array(&iter_dict, "KeyMgmt",
 +                                             key_mgmt, n))
 +              goto nomem;
 +
 +      /* Group */
 +      switch (ie_data->group_cipher) {
 +      case WPA_CIPHER_WEP40:
 +              group = "wep40";
 +              break;
 +      case WPA_CIPHER_TKIP:
 +              group = "tkip";
 +              break;
 +      case WPA_CIPHER_CCMP:
 +              group = "ccmp";
 +              break;
 +      case WPA_CIPHER_GCMP:
 +              group = "gcmp";
 +              break;
 +      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;
 +      }
 +
 +      if (!wpa_dbus_dict_append_string(&iter_dict, "Group", group))
 +              goto nomem;
 +
 +      /* Pairwise */
 +      n = 0;
 +      if (ie_data->pairwise_cipher & WPA_CIPHER_TKIP)
 +              pairwise[n++] = "tkip";
 +      if (ie_data->pairwise_cipher & WPA_CIPHER_CCMP)
 +              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))
 +              goto nomem;
 +
 +      /* Management group (RSN only) */
 +      if (ie_data->proto == WPA_PROTO_RSN) {
 +              switch (ie_data->mgmt_group_cipher) {
 +#ifdef CONFIG_IEEE80211W
 +              case WPA_CIPHER_AES_128_CMAC:
 +                      group = "aes128cmac";
 +                      break;
 +#endif /* CONFIG_IEEE80211W */
 +              default:
 +                      group = "";
 +                      break;
 +              }
 +
 +              if (!wpa_dbus_dict_append_string(&iter_dict, "MgmtGroup",
 +                                               group))
 +                      goto nomem;
 +      }
 +
 +      if (!wpa_dbus_dict_close_write(&variant_iter, &iter_dict) ||
 +          !dbus_message_iter_close_container(iter, &variant_iter))
 +              goto nomem;
 +
 +      return TRUE;
 +
 +nomem:
 +      dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
 +      return FALSE;
 +}
 +
 +
 +/**
 + * wpas_dbus_getter_bss_wpa - Return the WPA options of a BSS
 + * @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 "WPA" property.
 + */
 +dbus_bool_t wpas_dbus_getter_bss_wpa(DBusMessageIter *iter, DBusError *error,
 +                                   void *user_data)
 +{
 +      struct bss_handler_args *args = user_data;
 +      struct wpa_bss *res;
 +      struct wpa_ie_data wpa_data;
 +      const u8 *ie;
 +
 +      res = get_bss_helper(args, error, __func__);
 +      if (!res)
 +              return FALSE;
 +
 +      os_memset(&wpa_data, 0, sizeof(wpa_data));
 +      ie = wpa_bss_get_vendor_ie(res, WPA_IE_VENDOR_TYPE);
 +      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);
 +}
 +
 +
 +/**
 + * wpas_dbus_getter_bss_rsn - Return the RSN options of a BSS
 + * @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 "RSN" property.
 + */
 +dbus_bool_t wpas_dbus_getter_bss_rsn(DBusMessageIter *iter, DBusError *error,
 +                                   void *user_data)
 +{
 +      struct bss_handler_args *args = user_data;
 +      struct wpa_bss *res;
 +      struct wpa_ie_data wpa_data;
 +      const u8 *ie;
 +
 +      res = get_bss_helper(args, error, __func__);
 +      if (!res)
 +              return FALSE;
 +
 +      os_memset(&wpa_data, 0, sizeof(wpa_data));
 +      ie = wpa_bss_get_ie(res, WLAN_EID_RSN);
 +      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);
 +}
 +
 +
 +/**
 + * wpas_dbus_getter_bss_wps - Return the WPS options of a BSS
 + * @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 "WPS" property.
 + */
 +dbus_bool_t wpas_dbus_getter_bss_wps(DBusMessageIter *iter, DBusError *error,
 +                                   void *user_data)
 +{
 +      struct bss_handler_args *args = user_data;
 +      struct wpa_bss *res;
 +#ifdef CONFIG_WPS
 +      struct wpabuf *wps_ie;
 +#endif /* CONFIG_WPS */
 +      DBusMessageIter iter_dict, variant_iter;
++      int wps_support = 0;
 +      const char *type = "";
 +
 +      res = get_bss_helper(args, error, __func__);
 +      if (!res)
 +              return FALSE;
 +
 +      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;
 +
 +#ifdef CONFIG_WPS
 +      wps_ie = wpa_bss_get_vendor_ie_multi(res, WPS_IE_VENDOR_TYPE);
 +      if (wps_ie) {
++              wps_support = 1;
 +              if (wps_is_selected_pbc_registrar(wps_ie))
 +                      type = "pbc";
 +              else if (wps_is_selected_pin_registrar(wps_ie))
 +                      type = "pin";
 +
 +              wpabuf_free(wps_ie);
 +      }
 +#endif /* CONFIG_WPS */
 +
-       if (priv == NULL)
++      if ((wps_support && !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;
 +
 +nomem:
 +      dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
 +      return FALSE;
 +}
 +
 +
 +/**
 + * wpas_dbus_getter_bss_ies - Return all IEs of a BSS
 + * @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 "IEs" property.
 + */
 +dbus_bool_t wpas_dbus_getter_bss_ies(DBusMessageIter *iter, DBusError *error,
 +                                   void *user_data)
 +{
 +      struct bss_handler_args *args = user_data;
 +      struct wpa_bss *res;
 +
 +      res = get_bss_helper(args, error, __func__);
 +      if (!res)
 +              return FALSE;
 +
 +      return wpas_dbus_simple_array_property_getter(iter, DBUS_TYPE_BYTE,
 +                                                    res + 1, res->ie_len,
 +                                                    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
 + * @user_data: Function specific data
 + * Returns: TRUE on success, FALSE on failure
 + *
 + * Getter for "enabled" property of a configured network.
 + */
 +dbus_bool_t wpas_dbus_getter_enabled(DBusMessageIter *iter, DBusError *error,
 +                                   void *user_data)
 +{
 +      struct network_handler_args *net = user_data;
 +      dbus_bool_t enabled = net->ssid->disabled ? FALSE : TRUE;
 +
 +      return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_BOOLEAN,
 +                                              &enabled, error);
 +}
 +
 +
 +/**
 + * wpas_dbus_setter_enabled - Mark a configured network as enabled or disabled
 + * @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 "Enabled" property of a configured network.
 + */
 +dbus_bool_t wpas_dbus_setter_enabled(DBusMessageIter *iter, DBusError *error,
 +                                   void *user_data)
 +{
 +      struct network_handler_args *net = user_data;
 +      struct wpa_supplicant *wpa_s;
 +      struct wpa_ssid *ssid;
 +      dbus_bool_t enable;
 +
 +      if (!wpas_dbus_simple_property_setter(iter, error, DBUS_TYPE_BOOLEAN,
 +                                            &enable))
 +              return FALSE;
 +
 +      wpa_s = net->wpa_s;
 +      ssid = net->ssid;
 +
 +      if (enable)
 +              wpa_supplicant_enable_network(wpa_s, ssid);
 +      else
 +              wpa_supplicant_disable_network(wpa_s, ssid);
 +
 +      return TRUE;
 +}
 +
 +
 +/**
 + * wpas_dbus_getter_network_properties - Get options for a configured network
 + * @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 "Properties" property of a configured network.
 + */
 +dbus_bool_t wpas_dbus_getter_network_properties(DBusMessageIter *iter,
 +                                              DBusError *error,
 +                                              void *user_data)
 +{
 +      struct network_handler_args *net = user_data;
 +      DBusMessageIter variant_iter, dict_iter;
 +      char **iterator;
 +      char **props = wpa_config_get_all(net->ssid, 1);
 +      dbus_bool_t success = FALSE;
 +
 +      if (!props) {
 +              dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
 +              return FALSE;
 +      }
 +
 +      if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, "a{sv}",
 +                                            &variant_iter) ||
 +          !wpa_dbus_dict_open_write(&variant_iter, &dict_iter)) {
 +              dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
 +              goto out;
 +      }
 +
 +      iterator = props;
 +      while (*iterator) {
 +              if (!wpa_dbus_dict_append_string(&dict_iter, *iterator,
 +                                               *(iterator + 1))) {
 +                      dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY,
 +                                           "no memory");
 +                      goto out;
 +              }
 +              iterator += 2;
 +      }
 +
 +
 +      if (!wpa_dbus_dict_close_write(&variant_iter, &dict_iter) ||
 +          !dbus_message_iter_close_container(iter, &variant_iter)) {
 +              dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
 +              goto out;
 +      }
 +
 +      success = TRUE;
 +
 +out:
 +      iterator = props;
 +      while (*iterator) {
 +              os_free(*iterator);
 +              iterator++;
 +      }
 +      os_free(props);
 +      return success;
 +}
 +
 +
 +/**
 + * wpas_dbus_setter_network_properties - Set options for a configured network
 + * @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 "Properties" property of a configured network.
 + */
 +dbus_bool_t wpas_dbus_setter_network_properties(DBusMessageIter *iter,
 +                                              DBusError *error,
 +                                              void *user_data)
 +{
 +      struct network_handler_args *net = user_data;
 +      struct wpa_ssid *ssid = net->ssid;
 +      DBusMessageIter variant_iter;
 +
 +      dbus_message_iter_recurse(iter, &variant_iter);
 +      return set_network_properties(net->wpa_s, ssid, &variant_iter, error);
 +}
 +
 +
 +#ifdef CONFIG_AP
 +
 +DBusMessage * wpas_dbus_handler_subscribe_preq(
 +      DBusMessage *message, struct wpa_supplicant *wpa_s)
 +{
 +      struct wpas_dbus_priv *priv = wpa_s->global->dbus;
 +      char *name;
 +
 +      if (wpa_s->preq_notify_peer != NULL) {
 +              if (os_strcmp(dbus_message_get_sender(message),
 +                            wpa_s->preq_notify_peer) == 0)
 +                      return NULL;
 +
 +              return dbus_message_new_error(message,
 +                      WPAS_DBUS_ERROR_SUBSCRIPTION_IN_USE,
 +                      "Another application is already subscribed");
 +      }
 +
 +      name = os_strdup(dbus_message_get_sender(message));
 +      if (!name)
 +              return wpas_dbus_error_no_memory(message);
 +
 +      wpa_s->preq_notify_peer = name;
 +
 +      /* Subscribe to clean up if application closes socket */
 +      wpas_dbus_subscribe_noc(priv);
 +
 +      /*
 +       * Double-check it's still alive to make sure that we didn't
 +       * miss the NameOwnerChanged signal, e.g. while strdup'ing.
 +       */
 +      if (!dbus_bus_name_has_owner(priv->con, name, NULL)) {
 +              /*
 +               * Application no longer exists, clean up.
 +               * The return value is irrelevant now.
 +               *
 +               * Need to check if the NameOwnerChanged handling
 +               * already cleaned up because we have processed
 +               * DBus messages while checking if the name still
 +               * has an owner.
 +               */
 +              if (!wpa_s->preq_notify_peer)
 +                      return NULL;
 +              os_free(wpa_s->preq_notify_peer);
 +              wpa_s->preq_notify_peer = NULL;
 +              wpas_dbus_unsubscribe_noc(priv);
 +      }
 +
 +      return NULL;
 +}
 +
 +
 +DBusMessage * wpas_dbus_handler_unsubscribe_preq(
 +      DBusMessage *message, struct wpa_supplicant *wpa_s)
 +{
 +      struct wpas_dbus_priv *priv = wpa_s->global->dbus;
 +
 +      if (!wpa_s->preq_notify_peer)
 +              return dbus_message_new_error(message,
 +                      WPAS_DBUS_ERROR_NO_SUBSCRIPTION,
 +                      "Not subscribed");
 +
 +      if (os_strcmp(wpa_s->preq_notify_peer,
 +                    dbus_message_get_sender(message)))
 +              return dbus_message_new_error(message,
 +                      WPAS_DBUS_ERROR_SUBSCRIPTION_EPERM,
 +                      "Can't unsubscribe others");
 +
 +      os_free(wpa_s->preq_notify_peer);
 +      wpa_s->preq_notify_peer = NULL;
 +      wpas_dbus_unsubscribe_noc(priv);
 +      return NULL;
 +}
 +
 +
 +void wpas_dbus_signal_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)
 +{
 +      DBusMessage *msg;
 +      DBusMessageIter iter, dict_iter;
 +      struct wpas_dbus_priv *priv = wpa_s->global->dbus;
 +
 +      /* Do nothing if the control interface is not turned on */
++      if (priv == NULL || !wpa_s->dbus_new_path)
 +              return;
 +
 +      if (wpa_s->preq_notify_peer == NULL)
 +              return;
 +
 +      msg = dbus_message_new_signal(wpa_s->dbus_new_path,
 +                                    WPAS_DBUS_NEW_IFACE_INTERFACE,
 +                                    "ProbeRequest");
 +      if (msg == NULL)
 +              return;
 +
 +      dbus_message_set_destination(msg, wpa_s->preq_notify_peer);
 +
 +      dbus_message_iter_init_append(msg, &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);
 +      goto out;
 +fail:
 +      wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
 +out:
 +      dbus_message_unref(msg);
 +}
 +
 +#endif /* CONFIG_AP */
index 6113db500390ef53d9eecc90f14d49e1c06855a0,0000000000000000000000000000000000000000..50f72ec507bf3e4acabee0cd5c05ef5f2fae51e2
mode 100644,000000..100644
--- /dev/null
@@@ -1,329 -1,0 +1,335 @@@
 +/*
 + * 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>
 + *
 + * This software may be distributed under the terms of the BSD license.
 + * See README for more details.
 + */
 +
 +#ifndef CTRL_IFACE_DBUS_NEW_HANDLERS_H
 +#define CTRL_IFACE_DBUS_NEW_HANDLERS_H
 +
 +struct network_handler_args {
 +      struct wpa_supplicant *wpa_s;
 +      struct wpa_ssid *ssid;
 +};
 +
 +struct bss_handler_args {
 +      struct wpa_supplicant *wpa_s;
 +      unsigned int id;
 +};
 +
 +dbus_bool_t wpas_dbus_simple_property_getter(DBusMessageIter *iter,
 +                                           const int type,
 +                                           const void *val,
 +                                           DBusError *error);
 +
 +dbus_bool_t wpas_dbus_simple_property_setter(DBusMessageIter *iter,
 +                                           DBusError *error,
 +                                           const int type, void *val);
 +
 +dbus_bool_t wpas_dbus_simple_array_property_getter(DBusMessageIter *iter,
 +                                                 const int type,
 +                                                 const void *array,
 +                                                 size_t array_len,
 +                                                 DBusError *error);
 +
 +dbus_bool_t wpas_dbus_simple_array_array_property_getter(DBusMessageIter *iter,
 +                                                       const int type,
 +                                                       struct wpabuf **array,
 +                                                       size_t array_len,
 +                                                       DBusError *error);
 +
 +DBusMessage * wpas_dbus_handler_create_interface(DBusMessage *message,
 +                                               struct wpa_global *global);
 +
 +DBusMessage * wpas_dbus_handler_remove_interface(DBusMessage *message,
 +                                               struct wpa_global *global);
 +
 +DBusMessage * wpas_dbus_handler_get_interface(DBusMessage *message,
 +                                            struct wpa_global *global);
 +
 +dbus_bool_t wpas_dbus_getter_debug_level(DBusMessageIter *iter,
 +                                       DBusError *error,
 +                                       void *user_data);
 +
 +dbus_bool_t wpas_dbus_getter_debug_timestamp(DBusMessageIter *iter,
 +                                           DBusError *error,
 +                                           void *user_data);
 +
 +dbus_bool_t wpas_dbus_getter_debug_show_keys(DBusMessageIter *iter,
 +                                           DBusError *error,
 +                                           void *user_data);
 +
 +dbus_bool_t wpas_dbus_setter_debug_level(DBusMessageIter *iter,
 +                                       DBusError *error, void *user_data);
 +
 +dbus_bool_t wpas_dbus_setter_debug_timestamp(DBusMessageIter *iter,
 +                                           DBusError *error,
 +                                           void *user_data);
 +
 +dbus_bool_t wpas_dbus_setter_debug_show_keys(DBusMessageIter *iter,
 +                                           DBusError *error,
 +                                           void *user_data);
 +
 +dbus_bool_t wpas_dbus_getter_interfaces(DBusMessageIter *iter,
 +                                      DBusError *error,
 +                                      void *user_data);
 +
 +dbus_bool_t wpas_dbus_getter_eap_methods(DBusMessageIter *iter,
 +                                       DBusError *error, void *user_data);
 +
 +dbus_bool_t wpas_dbus_getter_global_capabilities(DBusMessageIter *iter,
 +                                               DBusError *error,
 +                                               void *user_data);
 +
 +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);
 +
 +dbus_bool_t set_network_properties(struct wpa_supplicant *wpa_s,
 +                                 struct wpa_ssid *ssid,
 +                                 DBusMessageIter *iter,
 +                                 DBusError *error);
 +
 +DBusMessage * wpas_dbus_handler_add_network(DBusMessage *message,
 +                                          struct wpa_supplicant *wpa_s);
 +
 +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_reconnect(DBusMessage *message,
++                                        struct wpa_supplicant *wpa_s);
++
 +DBusMessage * wpas_dbus_handler_remove_network(DBusMessage *message,
 +                                             struct wpa_supplicant *wpa_s);
 +
 +DBusMessage * wpas_dbus_handler_remove_all_networks(
 +      DBusMessage *message, struct wpa_supplicant *wpa_s);
 +
 +DBusMessage * wpas_dbus_handler_select_network(DBusMessage *message,
 +                                             struct wpa_supplicant *wpa_s);
 +
 +DBusMessage * wpas_dbus_handler_network_reply(DBusMessage *message,
 +                                            struct wpa_supplicant *wpa_s);
 +
 +DBusMessage * wpas_dbus_handler_add_blob(DBusMessage *message,
 +                                       struct wpa_supplicant *wpa_s);
 +
 +DBusMessage * wpas_dbus_handler_get_blob(DBusMessage *message,
 +                                       struct wpa_supplicant *wpa_s);
 +
 +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);
 +
 +DBusMessage * wpas_dbus_handler_flush_bss(DBusMessage *message,
 +                                        struct wpa_supplicant *wpa_s);
 +
 +DBusMessage * wpas_dbus_handler_autoscan(DBusMessage *message,
 +                                       struct wpa_supplicant *wpa_s);
 +
 +DBusMessage * wpas_dbus_handler_eap_logoff(DBusMessage *message,
 +                                         struct wpa_supplicant *wpa_s);
 +
 +DBusMessage * wpas_dbus_handler_eap_logon(DBusMessage *message,
 +                                        struct wpa_supplicant *wpa_s);
 +
 +dbus_bool_t wpas_dbus_getter_capabilities(DBusMessageIter *iter,
 +                                        DBusError *error, void *user_data);
 +
 +dbus_bool_t wpas_dbus_getter_state(DBusMessageIter *iter, DBusError *error,
 +                                 void *user_data);
 +
 +dbus_bool_t wpas_dbus_getter_scanning(DBusMessageIter *iter, DBusError *error,
 +                                    void *user_data);
 +
 +dbus_bool_t wpas_dbus_getter_ap_scan(DBusMessageIter *iter, DBusError *error,
 +                                   void *user_data);
 +
 +dbus_bool_t wpas_dbus_setter_ap_scan(DBusMessageIter *iter, DBusError *error,
 +                                   void *user_data);
 +
 +dbus_bool_t wpas_dbus_getter_fast_reauth(DBusMessageIter *iter,
 +                                       DBusError *error,
 +                                       void *user_data);
 +
 +dbus_bool_t wpas_dbus_setter_fast_reauth(DBusMessageIter *iter,
 +                                       DBusError *error,
 +                                       void *user_data);
 +
 +dbus_bool_t wpas_dbus_getter_disconnect_reason(DBusMessageIter *iter,
 +                                             DBusError *error,
 +                                             void *user_data);
 +
 +dbus_bool_t wpas_dbus_getter_bss_expire_age(DBusMessageIter *iter,
 +                                          DBusError *error, void *user_data);
 +
 +dbus_bool_t wpas_dbus_setter_bss_expire_age(DBusMessageIter *iter,
 +                                          DBusError *error,
 +                                          void *user_data);
 +
 +dbus_bool_t wpas_dbus_getter_bss_expire_count(DBusMessageIter *iter,
 +                                            DBusError *error,
 +                                            void *user_data);
 +
 +dbus_bool_t wpas_dbus_setter_bss_expire_count(DBusMessageIter *iter,
 +                                            DBusError *error,
 +                                            void *user_data);
 +
 +dbus_bool_t wpas_dbus_getter_country(DBusMessageIter *iter, DBusError *error,
 +                                   void *user_data);
 +
 +dbus_bool_t wpas_dbus_setter_country(DBusMessageIter *iter, DBusError *error,
 +                                   void *user_data);
 +
 +dbus_bool_t wpas_dbus_getter_scan_interval(DBusMessageIter *iter,
 +                                         DBusError *error,
 +                                         void *user_data);
 +
 +dbus_bool_t wpas_dbus_setter_scan_interval(DBusMessageIter *iter,
 +                                         DBusError *error,
 +                                         void *user_data);
 +
 +dbus_bool_t wpas_dbus_getter_ifname(DBusMessageIter *iter, DBusError *error,
 +                                  void *user_data);
 +
 +dbus_bool_t wpas_dbus_getter_driver(DBusMessageIter *iter, DBusError *error,
 +                                  void *user_data);
 +
 +dbus_bool_t wpas_dbus_getter_bridge_ifname(DBusMessageIter *iter,
 +                                         DBusError *error,
 +                                         void *user_data);
 +
 +dbus_bool_t wpas_dbus_getter_current_bss(DBusMessageIter *iter,
 +                                       DBusError *error,
 +                                       void *user_data);
 +
 +dbus_bool_t wpas_dbus_getter_current_network(DBusMessageIter *iter,
 +                                           DBusError *error,
 +                                           void *user_data);
 +
 +dbus_bool_t wpas_dbus_getter_current_auth_mode(DBusMessageIter *iter,
 +                                             DBusError *error,
 +                                             void *user_data);
 +
 +dbus_bool_t wpas_dbus_getter_bsss(DBusMessageIter *iter, DBusError *error,
 +                                void *user_data);
 +
 +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);
 +
 +dbus_bool_t wpas_dbus_getter_bss_bssid(DBusMessageIter *iter, DBusError *error,
 +                                     void *user_data);
 +
 +dbus_bool_t wpas_dbus_getter_bss_ssid(DBusMessageIter *iter, DBusError *error,
 +                                    void *user_data);
 +
 +dbus_bool_t wpas_dbus_getter_bss_privacy(DBusMessageIter *iter,
 +                                       DBusError *error, void *user_data);
 +
 +dbus_bool_t wpas_dbus_getter_bss_mode(DBusMessageIter *iter, DBusError *error,
 +                                    void *user_data);
 +
 +dbus_bool_t wpas_dbus_getter_bss_signal(DBusMessageIter *iter,
 +                                      DBusError *error, void *user_data);
 +
 +dbus_bool_t wpas_dbus_getter_bss_frequency(DBusMessageIter *iter,
 +                                         DBusError *error, void *user_data);
 +
 +dbus_bool_t wpas_dbus_getter_bss_rates(DBusMessageIter *iter,
 +                                     DBusError *error, void *user_data);
 +
 +dbus_bool_t wpas_dbus_getter_bss_wpa(DBusMessageIter *iter, DBusError *error,
 +                                   void *user_data);
 +
 +dbus_bool_t wpas_dbus_getter_bss_rsn(DBusMessageIter *iter, DBusError *error,
 +                                   void *user_data);
 +
 +dbus_bool_t wpas_dbus_getter_bss_wps(DBusMessageIter *iter, DBusError *error,
 +                                   void *user_data);
 +
 +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);
 +
 +dbus_bool_t wpas_dbus_setter_enabled(DBusMessageIter *iter, DBusError *error,
 +                                   void *user_data);
 +
 +dbus_bool_t wpas_dbus_getter_network_properties(DBusMessageIter *iter,
 +                                              DBusError *error,
 +                                              void *user_data);
 +
 +dbus_bool_t wpas_dbus_setter_network_properties(DBusMessageIter *iter,
 +                                              DBusError *error,
 +                                              void *user_data);
 +
 +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);
++
 +dbus_bool_t wpas_dbus_getter_process_credentials(DBusMessageIter *iter,
 +      DBusError *error, void *user_data);
 +
 +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);
 +DBusMessage * wpas_dbus_handler_unsubscribe_preq(
 +      DBusMessage *message, struct wpa_supplicant *wpa_s);
 +
 +#endif /* CTRL_IFACE_DBUS_HANDLERS_NEW_H */
index 0eff76386fa4cda8fe216ceb701f361a31d462c1,0000000000000000000000000000000000000000..67c079e7506d75feb478c62ed33bd17a139489aa
mode 100644,000000..100644
--- /dev/null
@@@ -1,2717 -1,0 +1,2948 @@@
-       if (wpa_s->p2p_dev)
-               wpa_s = wpa_s->p2p_dev;
 +/*
 + * WPA Supplicant / dbus-based control interface (P2P)
 + * Copyright (c) 2011-2012, Intel Corporation
 + *
 + * This software may be distributed under the terms of the BSD license.
 + * See README for more details.
 + */
 +
 +#include "includes.h"
 +
 +#include "utils/includes.h"
 +#include "common.h"
 +#include "../config.h"
 +#include "../wpa_supplicant_i.h"
 +#include "../wps_supplicant.h"
 +#include "../notify.h"
 +#include "dbus_new_helpers.h"
 +#include "dbus_new.h"
 +#include "dbus_new_handlers.h"
 +#include "dbus_new_handlers_p2p.h"
 +#include "dbus_dict_helpers.h"
 +#include "p2p/p2p.h"
 +#include "common/ieee802_11_defs.h"
 +#include "ap/hostapd.h"
 +#include "ap/ap_config.h"
 +#include "ap/wps_hostapd.h"
 +
 +#include "../p2p_supplicant.h"
 +#include "../wifi_display.h"
 +
 +/**
 + * Parses out the mac address from the peer object path.
 + * @peer_path - object path of the form
 + *    /fi/w1/wpa_supplicant1/Interfaces/n/Peers/00112233445566 (no colons)
 + * @addr - out param must be of ETH_ALEN size
 + * Returns 0 if valid (including MAC), -1 otherwise
 + */
 +static int parse_peer_object_path(const char *peer_path, u8 addr[ETH_ALEN])
 +{
 +      const char *p;
 +
 +      if (!peer_path)
 +              return -1;
 +      p = os_strrchr(peer_path, '/');
 +      if (!p)
 +              return -1;
 +      p++;
 +      return hwaddr_compact_aton(p, addr);
 +}
 +
 +
 +/**
 + * wpas_dbus_error_persistent_group_unknown - Return a new PersistentGroupUnknown
 + * error message
 + * @message: Pointer to incoming dbus message this error refers to
 + * Returns: a dbus error message
 + *
 + * Convenience function to create and return an invalid persistent group error.
 + */
 +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.");
 +}
 +
 +
 +DBusMessage * wpas_dbus_handler_p2p_find(DBusMessage *message,
 +                                       struct wpa_supplicant *wpa_s)
 +{
 +      struct wpa_dbus_dict_entry entry;
 +      DBusMessage *reply = NULL;
 +      DBusMessageIter iter;
 +      DBusMessageIter iter_dict;
 +      unsigned int timeout = 0;
 +      enum p2p_discovery_type type = P2P_FIND_START_WITH_FULL;
 +      int num_req_dev_types = 0;
 +      unsigned int i;
 +      u8 *req_dev_types = NULL;
 +
 +      dbus_message_iter_init(message, &iter);
 +      entry.key = NULL;
 +
 +      if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL))
 +              goto error;
 +
 +      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, "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)
 +                              goto error_clear;
 +
 +                      os_free(req_dev_types);
 +                      req_dev_types =
 +                              os_malloc(WPS_DEV_TYPE_LEN * entry.array_len);
 +                      if (!req_dev_types)
 +                              goto error_clear;
 +
 +                      for (i = 0; i < entry.array_len; i++) {
 +                              if (wpabuf_len(entry.binarray_value[i]) !=
 +                                  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") == 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") == 0)
 +                              type = P2P_FIND_ONLY_SOCIAL;
 +                      else if (os_strcmp(entry.str_value, "progressive") == 0)
 +                              type = P2P_FIND_PROGRESSIVE;
 +                      else
 +                              goto error_clear;
 +              } else
 +                      goto error_clear;
 +              wpa_dbus_dict_entry_clear(&entry);
 +      }
 +
-       if (wpa_s->p2p_dev)
-               wpa_s = wpa_s->p2p_dev;
-       wpas_p2p_stop_find(wpa_s);
++      wpa_s = wpa_s->global->p2p_init_wpa_s;
 +
 +      wpas_p2p_find(wpa_s, timeout, type, num_req_dev_types, req_dev_types,
 +                    NULL, 0, 0, NULL, 0);
 +      os_free(req_dev_types);
 +      return reply;
 +
 +error_clear:
 +      wpa_dbus_dict_entry_clear(&entry);
 +error:
 +      os_free(req_dev_types);
 +      reply = wpas_dbus_error_invalid_args(message, entry.key);
 +      return reply;
 +}
 +
 +
 +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->global->p2p_init_wpa_s);
 +      return NULL;
 +}
 +
 +
 +DBusMessage * wpas_dbus_handler_p2p_rejectpeer(DBusMessage *message,
 +                                             struct wpa_supplicant *wpa_s)
 +{
 +      DBusMessageIter iter;
 +      char *peer_object_path = NULL;
 +      u8 peer_addr[ETH_ALEN];
 +
 +      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);
 +
-       if (wpa_s->p2p_dev)
-               wpa_s = wpa_s->p2p_dev;
++      wpa_s = wpa_s->global->p2p_init_wpa_s;
 +
 +      if (wpas_p2p_reject(wpa_s, peer_addr) < 0)
 +              return wpas_dbus_error_unknown_error(message,
 +                              "Failed to call wpas_p2p_reject method.");
 +
 +      return NULL;
 +}
 +
 +
 +DBusMessage * wpas_dbus_handler_p2p_listen(DBusMessage *message,
 +                                         struct wpa_supplicant *wpa_s)
 +{
 +      dbus_int32_t timeout = 0;
 +
 +      if (!dbus_message_get_args(message, NULL, DBUS_TYPE_INT32, &timeout,
 +                                 DBUS_TYPE_INVALID))
 +              return wpas_dbus_error_no_memory(message);
 +
-       if (wpa_s->p2p_dev)
-               wpa_s = wpa_s->p2p_dev;
++      wpa_s = wpa_s->global->p2p_init_wpa_s;
 +
 +      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;
 +}
 +
 +
 +DBusMessage * wpas_dbus_handler_p2p_extendedlisten(
 +      DBusMessage *message, struct wpa_supplicant *wpa_s)
 +{
 +      unsigned int period = 0, interval = 0;
 +      struct wpa_dbus_dict_entry entry;
 +      DBusMessageIter iter;
 +      DBusMessageIter iter_dict;
 +
 +      dbus_message_iter_init(message, &iter);
 +      entry.key = NULL;
 +
 +      if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL))
 +              goto error;
 +
 +      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, "period") == 0 &&
 +                  entry.type == DBUS_TYPE_INT32)
 +                      period = entry.uint32_value;
 +              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;
++      wpa_s = wpa_s->global->p2p_init_wpa_s;
 +
 +      if (wpas_p2p_ext_listen(wpa_s, period, interval))
 +              return wpas_dbus_error_unknown_error(
 +                      message, "failed to initiate a p2p_ext_listen.");
 +
 +      return NULL;
 +
 +error_clear:
 +      wpa_dbus_dict_entry_clear(&entry);
 +error:
 +      return wpas_dbus_error_invalid_args(message, entry.key);
 +}
 +
 +
 +DBusMessage * wpas_dbus_handler_p2p_presence_request(
 +      DBusMessage *message, struct wpa_supplicant *wpa_s)
 +{
 +      unsigned int dur1 = 0, int1 = 0, dur2 = 0, int2 = 0;
 +      struct wpa_dbus_dict_entry entry;
 +      DBusMessageIter iter;
 +      DBusMessageIter iter_dict;
 +
 +      dbus_message_iter_init(message, &iter);
 +      entry.key = NULL;
 +
 +      if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL))
 +              goto error;
 +
 +      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, "duration1") == 0 &&
 +                  entry.type == DBUS_TYPE_INT32)
 +                      dur1 = entry.uint32_value;
 +              else if (os_strcmp(entry.key, "interval1") == 0 &&
 +                       entry.type == DBUS_TYPE_INT32)
 +                      int1 = entry.uint32_value;
 +              else if (os_strcmp(entry.key, "duration2") == 0 &&
 +                       entry.type == DBUS_TYPE_INT32)
 +                      dur2 = entry.uint32_value;
 +              else if (os_strcmp(entry.key, "interval2") == 0 &&
 +                       entry.type == DBUS_TYPE_INT32)
 +                      int2 = 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_presence_req(wpa_s, dur1, int1, dur2, int2) < 0)
 +              return wpas_dbus_error_unknown_error(message,
 +                              "Failed to invoke presence request.");
 +
 +      return NULL;
 +
 +error_clear:
 +      wpa_dbus_dict_entry_clear(&entry);
 +error:
 +      return wpas_dbus_error_invalid_args(message, entry.key);
 +}
 +
 +
 +DBusMessage * wpas_dbus_handler_p2p_group_add(DBusMessage *message,
 +                                            struct wpa_supplicant *wpa_s)
 +{
 +      DBusMessageIter iter_dict;
 +      DBusMessage *reply = NULL;
 +      DBusMessageIter iter;
 +      struct wpa_dbus_dict_entry entry;
 +      char *pg_object_path = NULL;
 +      int persistent_group = 0;
 +      int freq = 0;
 +      char *iface = NULL;
 +      unsigned int group_id = 0;
 +      struct wpa_ssid *ssid;
 +
 +      dbus_message_iter_init(message, &iter);
 +
 +      if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL))
 +              goto inv_args;
 +
 +      while (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
 +              if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
 +                      goto inv_args;
 +
 +              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") ==
 +                         0 &&
 +                         entry.type == DBUS_TYPE_OBJECT_PATH)
 +                      pg_object_path = os_strdup(entry.str_value);
 +              else
 +                      goto inv_args_clear;
 +
 +              wpa_dbus_dict_entry_clear(&entry);
 +      }
 +
-                   os_strcmp(iface, wpa_s->dbus_new_path) != 0) {
++      wpa_s = wpa_s->global->p2p_init_wpa_s;
 +
 +      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, WPAS_DBUS_NEW_PERSISTENT_GROUPS_PART,
 +                      &net_id_str);
 +              if (iface == NULL || net_id_str == NULL ||
-                                                 NULL, 0)) {
++                  !wpa_s->parent->dbus_new_path ||
++                  os_strcmp(iface, wpa_s->parent->dbus_new_path) != 0) {
 +                      reply =
 +                          wpas_dbus_error_invalid_args(message,
 +                                                       pg_object_path);
 +                      goto out;
 +              }
 +
 +              group_id = strtoul(net_id_str, NULL, 10);
 +              if (errno == EINVAL) {
 +                      reply = wpas_dbus_error_invalid_args(
 +                                              message, pg_object_path);
 +                      goto out;
 +              }
 +
 +              /* Get the SSID structure from the persistent group id */
 +              ssid = wpa_config_get_network(wpa_s->conf, group_id);
 +              if (ssid == NULL || ssid->disabled != 2)
 +                      goto inv_args;
 +
 +              if (wpas_p2p_group_add_persistent(wpa_s, ssid, 0, freq, 0, 0, 0,
-       if (wpa_s->p2p_dev)
-               wpa_s = wpa_s->p2p_dev;
++                                                NULL, 0, 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, 0))
 +              goto inv_args;
 +
 +out:
 +      os_free(pg_object_path);
 +      os_free(iface);
 +      return reply;
 +inv_args_clear:
 +      wpa_dbus_dict_entry_clear(&entry);
 +inv_args:
 +      reply = wpas_dbus_error_invalid_args(message, NULL);
 +      goto out;
 +}
 +
 +
 +DBusMessage * wpas_dbus_handler_p2p_disconnect(DBusMessage *message,
 +                                             struct wpa_supplicant *wpa_s)
 +{
 +      if (wpas_p2p_disconnect(wpa_s))
 +              return wpas_dbus_error_unknown_error(message,
 +                                              "failed to disconnect");
 +
 +      return NULL;
 +}
 +
 +
 +static dbus_bool_t wpa_dbus_p2p_check_enabled(struct wpa_supplicant *wpa_s,
 +                                            DBusMessage *message,
 +                                            DBusMessage **out_reply,
 +                                            DBusError *error)
 +{
 +      /* Return an error message or an error if P2P isn't available */
 +      if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) {
 +              if (out_reply) {
 +                      *out_reply = dbus_message_new_error(
 +                              message, DBUS_ERROR_FAILED,
 +                              "P2P is not available for this interface");
 +              }
 +              dbus_set_error_const(error, DBUS_ERROR_FAILED,
 +                                   "P2P is not available for this interface");
 +              return FALSE;
 +      }
 +      return TRUE;
 +}
 +
 +
++DBusMessage * wpas_dbus_handler_p2p_remove_client(DBusMessage *message,
++                                                struct wpa_supplicant *wpa_s)
++{
++      DBusMessageIter iter_dict;
++      DBusMessage *reply = NULL;
++      DBusMessageIter iter;
++      struct wpa_dbus_dict_entry entry;
++      char *peer_object_path = NULL;
++      char *interface_addr = NULL;
++      u8 peer_addr[ETH_ALEN];
++
++      if (!wpa_dbus_p2p_check_enabled(wpa_s, message, &reply, NULL))
++              return reply;
++
++      dbus_message_iter_init(message, &iter);
++
++      if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL))
++              goto err;
++
++      while (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
++              if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
++                      goto err;
++
++              if (os_strcmp(entry.key, "peer") == 0 &&
++                  entry.type == DBUS_TYPE_OBJECT_PATH) {
++                      os_free(peer_object_path);
++                      peer_object_path = os_strdup(entry.str_value);
++                      wpa_dbus_dict_entry_clear(&entry);
++              } else if (os_strcmp(entry.key, "iface") == 0 &&
++                         entry.type == DBUS_TYPE_STRING) {
++                      os_free(interface_addr);
++                      interface_addr = os_strdup(entry.str_value);
++                      wpa_dbus_dict_entry_clear(&entry);
++              } else {
++                      wpa_dbus_dict_entry_clear(&entry);
++                      goto err;
++              }
++      }
++
++      if ((!peer_object_path && !interface_addr) ||
++          (peer_object_path &&
++           (parse_peer_object_path(peer_object_path, peer_addr) < 0 ||
++            !p2p_peer_known(wpa_s->global->p2p, peer_addr))) ||
++          (interface_addr && hwaddr_aton(interface_addr, peer_addr) < 0))
++              goto err;
++
++      wpas_p2p_remove_client(wpa_s, peer_addr, interface_addr != NULL);
++      reply = NULL;
++out:
++      os_free(peer_object_path);
++      os_free(interface_addr);
++      return reply;
++err:
++      reply = wpas_dbus_error_invalid_args(message, "Invalid address format");
++      goto out;
++}
++
++
 +DBusMessage * wpas_dbus_handler_p2p_flush(DBusMessage *message,
 +                                        struct wpa_supplicant *wpa_s)
 +{
 +      DBusMessage *reply = NULL;
 +
 +      if (!wpa_dbus_p2p_check_enabled(wpa_s, message, &reply, NULL))
 +              return reply;
 +
-       if (wpa_s->p2p_dev)
-               wpa_s = wpa_s->p2p_dev;
++      wpa_s = wpa_s->global->p2p_init_wpa_s;
 +
 +      os_memset(wpa_s->p2p_auth_invite, 0, ETH_ALEN);
 +      wpa_s->force_long_sd = 0;
 +      p2p_flush(wpa_s->global->p2p);
 +
 +      return NULL;
 +}
 +
 +
 +DBusMessage * wpas_dbus_handler_p2p_connect(DBusMessage *message,
 +                                          struct wpa_supplicant *wpa_s)
 +{
 +      DBusMessageIter iter_dict;
 +      DBusMessage *reply = NULL;
 +      DBusMessageIter iter;
 +      struct wpa_dbus_dict_entry entry;
 +      char *peer_object_path = NULL;
 +      int persistent_group = 0;
 +      int join = 0;
 +      int authorize_only = 0;
 +      int go_intent = -1;
 +      int freq = 0;
 +      u8 addr[ETH_ALEN];
 +      char *pin = NULL;
 +      enum p2p_wps_method wps_method = WPS_NOT_READY;
 +      int new_pin;
 +      char *err_msg = NULL;
 +      char *iface = NULL;
 +
 +      if (!wpa_dbus_p2p_check_enabled(wpa_s, message, &reply, NULL))
 +              return reply;
 +
 +      dbus_message_iter_init(message, &iter);
 +
 +      if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL))
 +              goto inv_args;
 +
 +      while (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
 +              if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
 +                      goto inv_args;
 +
 +              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") == 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") == 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") == 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") == 0)
 +                              wps_method = WPS_PIN_DISPLAY;
 +                      else if (os_strcmp(entry.str_value, "display") == 0)
 +                              wps_method = WPS_PIN_DISPLAY;
 +                      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") == 0 &&
 +                         entry.type == DBUS_TYPE_STRING) {
 +                      pin = os_strdup(entry.str_value);
 +              } else
 +                      goto inv_args_clear;
 +
 +              wpa_dbus_dict_entry_clear(&entry);
 +      }
 +
 +      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)
 +              goto inv_args;
 +
-       if (wpa_s->p2p_dev)
-               wpa_s = wpa_s->p2p_dev;
++      wpa_s = wpa_s->global->p2p_init_wpa_s;
 +
 +      new_pin = wpas_p2p_connect(wpa_s, addr, pin, wps_method,
 +                                 persistent_group, 0, join, authorize_only,
 +                                 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);
 +              dbus_message_append_args(reply, DBUS_TYPE_STRING,
 +                                       &generated_pin, DBUS_TYPE_INVALID);
 +      } else {
 +              switch (new_pin) {
 +              case -2:
 +                      err_msg =
 +                              "connect failed due to channel unavailability.";
 +                      iface = WPAS_DBUS_ERROR_CONNECT_CHANNEL_UNAVAILABLE;
 +                      break;
 +
 +              case -3:
 +                      err_msg = "connect failed due to unsupported channel.";
 +                      iface = WPAS_DBUS_ERROR_CONNECT_CHANNEL_UNSUPPORTED;
 +                      break;
 +
 +              default:
 +                      err_msg = "connect failed due to unspecified error.";
 +                      iface = WPAS_DBUS_ERROR_CONNECT_UNSPECIFIED_ERROR;
 +                      break;
 +              }
 +
 +              /*
 +               * TODO:
 +               * Do we need specialized errors corresponding to above
 +               * error conditions as against just returning a different
 +               * error message?
 +               */
 +              reply = dbus_message_new_error(message, iface, err_msg);
 +      }
 +
 +out:
 +      os_free(peer_object_path);
 +      os_free(pin);
 +      return reply;
 +inv_args_clear:
 +      wpa_dbus_dict_entry_clear(&entry);
 +inv_args:
 +      reply = wpas_dbus_error_invalid_args(message, NULL);
 +      goto out;
 +}
 +
 +
++/**
++ * wpas_dbus_handler_p2p_cancel - Cancel P2P group formation
++ * @message: Pointer to incoming dbus message
++ * @wpa_s: %wpa_supplicant data structure
++ * Returns: NULL on success or DBus error on failure
++ *
++ * Handler for "Cancel" method call. Returns NULL if P2P cancel succeeds or DBus
++ * error on P2P cancel failure
++ */
++DBusMessage * wpas_dbus_handler_p2p_cancel(DBusMessage *message,
++                                         struct wpa_supplicant *wpa_s)
++{
++      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)
 +{
 +      DBusMessageIter iter_dict;
 +      DBusMessage *reply = NULL;
 +      DBusMessageIter iter;
 +      struct wpa_dbus_dict_entry entry;
 +      char *peer_object_path = NULL;
 +      char *pg_object_path = NULL;
 +      char *iface = NULL;
 +      u8 peer_addr[ETH_ALEN];
 +      unsigned int group_id = 0;
 +      int persistent = 0;
 +      struct wpa_ssid *ssid;
 +
 +      if (!wpa_dbus_p2p_check_enabled(wpa_s, message, &reply, NULL))
 +              return reply;
 +
 +      dbus_message_iter_init(message, &iter);
 +
 +      if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL))
 +              goto err;
 +
 +      while (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
 +              if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
 +                      goto err;
 +
 +              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") ==
 +                         0 &&
 +                         entry.type == DBUS_TYPE_OBJECT_PATH) {
 +                      pg_object_path = os_strdup(entry.str_value);
 +                      persistent = 1;
 +                      wpa_dbus_dict_entry_clear(&entry);
 +              } else {
 +                      wpa_dbus_dict_entry_clear(&entry);
 +                      goto err;
 +              }
 +      }
 +
 +      if (parse_peer_object_path(peer_object_path, peer_addr) < 0 ||
 +          !p2p_peer_known(wpa_s->global->p2p, peer_addr))
 +              goto err;
 +
-                   os_strcmp(iface, wpa_s->dbus_new_path) != 0) {
++      wpa_s = wpa_s->global->p2p_init_wpa_s;
 +
 +      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,
 +                      WPAS_DBUS_NEW_PERSISTENT_GROUPS_PART,
 +                      &net_id_str);
 +              if (iface == NULL || net_id_str == NULL ||
-       if (wpa_s->p2p_dev)
-               wpa_s = wpa_s->p2p_dev;
++                  !wpa_s->parent->dbus_new_path ||
++                  os_strcmp(iface, wpa_s->parent->dbus_new_path) != 0) {
 +                      reply = wpas_dbus_error_invalid_args(message,
 +                                                           pg_object_path);
 +                      goto out;
 +              }
 +
 +              group_id = strtoul(net_id_str, NULL, 10);
 +              if (errno == EINVAL) {
 +                      reply = wpas_dbus_error_invalid_args(
 +                              message, pg_object_path);
 +                      goto out;
 +              }
 +
 +              /* Get the SSID structure from the persistent group id */
 +              ssid = wpa_config_get_network(wpa_s->conf, group_id);
 +              if (ssid == NULL || ssid->disabled != 2)
 +                      goto err;
 +
 +              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");
 +                      goto out;
 +              }
 +      } else {
 +              /*
 +               * No group ID means propose to a peer to join my active group
 +               */
 +              if (wpas_p2p_invite_group(wpa_s, wpa_s->ifname,
 +                                        peer_addr, NULL)) {
 +                      reply = wpas_dbus_error_unknown_error(
 +                              message, "Failed to join to an active group");
 +                      goto out;
 +              }
 +      }
 +
 +out:
 +      os_free(iface);
 +      os_free(pg_object_path);
 +      os_free(peer_object_path);
 +      return reply;
 +
 +err:
 +      reply = wpas_dbus_error_invalid_args(message, NULL);
 +      goto out;
 +}
 +
 +
 +DBusMessage * wpas_dbus_handler_p2p_prov_disc_req(DBusMessage *message,
 +                                                struct wpa_supplicant *wpa_s)
 +{
 +      DBusMessageIter iter;
 +      char *peer_object_path = NULL;
 +      char *config_method = NULL;
 +      u8 peer_addr[ETH_ALEN];
 +
 +      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, &config_method);
 +
 +      /*
 +       * Validation checks on config_method are being duplicated here
 +       * to be able to return invalid args reply since the error code
 +       * from p2p module are not granular enough (yet).
 +       */
 +      if (os_strcmp(config_method, "display") &&
 +          os_strcmp(config_method, "keypad") &&
 +          os_strcmp(config_method, "pbc") &&
 +          os_strcmp(config_method, "pushbutton"))
 +              return wpas_dbus_error_invalid_args(message, NULL);
 +
-       if (wpa_s->p2p_dev)
-               wpa_s = wpa_s->p2p_dev;
++      wpa_s = wpa_s->global->p2p_init_wpa_s;
 +
 +      if (wpas_p2p_prov_disc(wpa_s, peer_addr, config_method,
 +                             WPAS_P2P_PD_FOR_GO_NEG, NULL) < 0)
 +              return wpas_dbus_error_unknown_error(message,
 +                              "Failed to send provision discovery request");
 +
 +      return NULL;
 +}
 +
 +
 +/*
 + * P2P Device property accessor methods.
 + */
 +
 +dbus_bool_t wpas_dbus_getter_p2p_device_config(DBusMessageIter *iter,
 +                                             DBusError *error,
 +                                             void *user_data)
 +{
 +      struct wpa_supplicant *wpa_s = user_data;
 +      DBusMessageIter variant_iter, dict_iter;
 +      DBusMessageIter iter_secdev_dict_entry, iter_secdev_dict_val,
 +              iter_secdev_dict_array;
 +      const char *dev_name;
 +      int num_vendor_extensions = 0;
 +      int i;
 +      const struct wpabuf *vendor_ext[P2P_MAX_WPS_VENDOR_EXT];
 +
 +      if (!wpa_dbus_p2p_check_enabled(wpa_s, NULL, NULL, error))
 +              return FALSE;
 +
-       if (wpa_s->p2p_dev)
-               wpa_s = wpa_s->p2p_dev;
++      wpa_s = wpa_s->global->p2p_init_wpa_s;
 +
 +      if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
 +                                            "a{sv}", &variant_iter) ||
 +          !wpa_dbus_dict_open_write(&variant_iter, &dict_iter))
 +              goto err_no_mem;
 +
 +      /* DeviceName */
 +      dev_name = wpa_s->conf->device_name;
 +      if (dev_name &&
 +          !wpa_dbus_dict_append_string(&dict_iter, "DeviceName", dev_name))
 +              goto err_no_mem;
 +
 +      /* Primary device type */
 +      if (!wpa_dbus_dict_append_byte_array(&dict_iter, "PrimaryDeviceType",
 +                                           (char *) wpa_s->conf->device_type,
 +                                           WPS_DEV_TYPE_LEN))
 +              goto err_no_mem;
 +
 +      /* Secondary device types */
 +      if (wpa_s->conf->num_sec_device_types) {
 +              if (!wpa_dbus_dict_begin_array(&dict_iter,
 +                                             "SecondaryDeviceTypes",
 +                                             DBUS_TYPE_ARRAY_AS_STRING
 +                                             DBUS_TYPE_BYTE_AS_STRING,
 +                                             &iter_secdev_dict_entry,
 +                                             &iter_secdev_dict_val,
 +                                             &iter_secdev_dict_array))
 +                      goto err_no_mem;
 +
 +              for (i = 0; i < wpa_s->conf->num_sec_device_types; i++)
 +                      wpa_dbus_dict_bin_array_add_element(
 +                              &iter_secdev_dict_array,
 +                              wpa_s->conf->sec_device_type[i],
 +                              WPS_DEV_TYPE_LEN);
 +
 +              if (!wpa_dbus_dict_end_array(&dict_iter,
 +                                           &iter_secdev_dict_entry,
 +                                           &iter_secdev_dict_val,
 +                                           &iter_secdev_dict_array))
 +                      goto err_no_mem;
 +      }
 +
 +      /* Vendor Extensions */
 +      for (i = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) {
 +              if (wpa_s->conf->wps_vendor_ext[i] == NULL)
 +                      continue;
 +              vendor_ext[num_vendor_extensions++] =
 +                      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)) ||
 +          !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;
 +
 +      return TRUE;
 +
 +err_no_mem:
 +      dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
 +      return FALSE;
 +}
 +
 +
 +dbus_bool_t wpas_dbus_setter_p2p_device_config(DBusMessageIter *iter,
 +                                             DBusError *error,
 +                                             void *user_data)
 +{
 +      struct wpa_supplicant *wpa_s = user_data;
 +      DBusMessageIter variant_iter, iter_dict;
 +      struct wpa_dbus_dict_entry entry = {.type = DBUS_TYPE_STRING };
 +      unsigned int i;
 +
 +      if (!wpa_dbus_p2p_check_enabled(wpa_s, NULL, NULL, error))
 +              return FALSE;
 +
-       if (!wpa_dbus_p2p_check_enabled(wpa_s, NULL, NULL, error))
++      wpa_s = wpa_s->global->p2p_init_wpa_s;
 +
 +      dbus_message_iter_recurse(iter, &variant_iter);
 +      if (!wpa_dbus_dict_open_read(&variant_iter, &iter_dict, error))
 +              return FALSE;
 +
 +      while (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
 +              if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) {
 +                      dbus_set_error_const(error, DBUS_ERROR_INVALID_ARGS,
 +                                           "invalid message format");
 +                      return FALSE;
 +              }
 +
 +              if (os_strcmp(entry.key, "DeviceName") == 0) {
 +                      char *devname;
 +
 +                      if (entry.type != DBUS_TYPE_STRING)
 +                              goto error;
 +
 +                      devname = os_strdup(entry.str_value);
 +                      if (devname == NULL)
 +                              goto err_no_mem_clear;
 +
 +                      os_free(wpa_s->conf->device_name);
 +                      wpa_s->conf->device_name = devname;
 +
 +                      wpa_s->conf->changed_parameters |=
 +                              CFG_CHANGED_DEVICE_NAME;
 +              } else if (os_strcmp(entry.key, "PrimaryDeviceType") == 0) {
 +                      if (entry.type != DBUS_TYPE_ARRAY ||
 +                          entry.array_type != DBUS_TYPE_BYTE ||
 +                          entry.array_len != WPS_DEV_TYPE_LEN)
 +                              goto error;
 +
 +                      os_memcpy(wpa_s->conf->device_type,
 +                                entry.bytearray_value,
 +                                WPS_DEV_TYPE_LEN);
 +                      wpa_s->conf->changed_parameters |=
 +                              CFG_CHANGED_DEVICE_TYPE;
 +              } else if (os_strcmp(entry.key, "SecondaryDeviceTypes") == 0) {
 +                      if (entry.type != DBUS_TYPE_ARRAY ||
 +                          entry.array_type != WPAS_DBUS_TYPE_BINARRAY ||
 +                          entry.array_len > MAX_SEC_DEVICE_TYPES)
 +                              goto error;
 +
 +                      for (i = 0; i < entry.array_len; i++)
 +                              if (wpabuf_len(entry.binarray_value[i]) !=
 +                                  WPS_DEV_TYPE_LEN)
 +                                      goto err_no_mem_clear;
 +                      for (i = 0; i < entry.array_len; i++)
 +                              os_memcpy(wpa_s->conf->sec_device_type[i],
 +                                        wpabuf_head(entry.binarray_value[i]),
 +                                        WPS_DEV_TYPE_LEN);
 +                      wpa_s->conf->num_sec_device_types = entry.array_len;
 +                      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 ||
 +                          (entry.array_len > P2P_MAX_WPS_VENDOR_EXT))
 +                              goto error;
 +
 +                      wpa_s->conf->changed_parameters |=
 +                              CFG_CHANGED_VENDOR_EXTENSION;
 +
 +                      for (i = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) {
 +                              wpabuf_free(wpa_s->conf->wps_vendor_ext[i]);
 +                              if (i < entry.array_len) {
 +                                      wpa_s->conf->wps_vendor_ext[i] =
 +                                              entry.binarray_value[i];
 +                                      entry.binarray_value[i] = NULL;
 +                              } else
 +                                      wpa_s->conf->wps_vendor_ext[i] = NULL;
 +                      }
 +              } 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)
 +                      wpa_s->conf->persistent_reconnect = entry.bool_value;
 +              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) {
 +                      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) {
 +                      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) {
 +                      wpa_s->conf->p2p_oper_channel = entry.uint32_value;
 +                      wpa_s->conf->changed_parameters |=
 +                              CFG_CHANGED_P2P_OPER_CHANNEL;
 +              } else if (os_strcmp(entry.key, "SsidPostfix") == 0) {
 +                      char *postfix;
 +
 +                      if (entry.type != DBUS_TYPE_STRING)
 +                              goto error;
 +
 +                      postfix = os_strdup(entry.str_value);
 +                      if (!postfix)
 +                              goto err_no_mem_clear;
 +
 +                      os_free(wpa_s->conf->p2p_ssid_postfix);
 +                      wpa_s->conf->p2p_ssid_postfix = postfix;
 +
 +                      wpa_s->conf->changed_parameters |=
 +                                      CFG_CHANGED_P2P_SSID_POSTFIX;
 +              } 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)
 +                      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;
 +
 +              wpa_dbus_dict_entry_clear(&entry);
 +      }
 +
 +      if (wpa_s->conf->changed_parameters) {
 +              /* Some changed parameters requires to update config*/
 +              wpa_supplicant_update_config(wpa_s);
 +      }
 +
 +      return TRUE;
 +
 + error:
 +      dbus_set_error_const(error, DBUS_ERROR_INVALID_ARGS,
 +                           "invalid message format");
 +      wpa_dbus_dict_entry_clear(&entry);
 +      return FALSE;
 +
 + err_no_mem_clear:
 +      dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
 +      wpa_dbus_dict_entry_clear(&entry);
 +      return FALSE;
 +}
 +
 +
 +dbus_bool_t wpas_dbus_getter_p2p_peers(DBusMessageIter *iter, DBusError *error,
 +                                     void *user_data)
 +{
 +      struct wpa_supplicant *wpa_s = user_data;
 +      struct p2p_data *p2p = wpa_s->global->p2p;
 +      int next = 0, i = 0;
 +      int num = 0, out_of_mem = 0;
 +      const u8 *addr;
 +      const struct p2p_peer_info *peer_info = NULL;
 +      dbus_bool_t success = FALSE;
 +
 +      struct dl_list peer_objpath_list;
 +      struct peer_objpath_node {
 +              struct dl_list list;
 +              char path[WPAS_DBUS_OBJECT_PATH_MAX];
 +      } *node, *tmp;
 +
 +      char **peer_obj_paths = NULL;
 +
-                           wpa_s->dbus_new_path, MAC2STR(addr));
++      if (!wpa_dbus_p2p_check_enabled(wpa_s, NULL, NULL, error) ||
++          !wpa_s->parent->parent->dbus_new_path)
 +              return FALSE;
 +
 +      dl_list_init(&peer_objpath_list);
 +
 +      /* Get the first peer info */
 +      peer_info = p2p_get_peer_found(p2p, NULL, next);
 +
 +      /* Get next and accumulate them */
 +      next = 1;
 +      while (peer_info != NULL) {
 +              node = os_zalloc(sizeof(struct peer_objpath_node));
 +              if (!node) {
 +                      out_of_mem = 1;
 +                      goto error;
 +              }
 +
 +              addr = peer_info->p2p_device_addr;
 +              os_snprintf(node->path, WPAS_DBUS_OBJECT_PATH_MAX,
 +                          "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART
 +                          "/" COMPACT_MACSTR,
-                           wpa_s->dbus_new_path, MAC2STR(wpa_s->go_dev_addr));
++                          wpa_s->parent->parent->dbus_new_path,
++                          MAC2STR(addr));
 +              dl_list_add_tail(&peer_objpath_list, &node->list);
 +              num++;
 +
 +              peer_info = p2p_get_peer_found(p2p, addr, next);
 +      }
 +
 +      /*
 +       * Now construct the peer object paths in a form suitable for
 +       * array_property_getter helper below.
 +       */
 +      peer_obj_paths = os_calloc(num, sizeof(char *));
 +
 +      if (!peer_obj_paths) {
 +              out_of_mem = 1;
 +              goto error;
 +      }
 +
 +      dl_list_for_each_safe(node, tmp, &peer_objpath_list,
 +                            struct peer_objpath_node, list)
 +              peer_obj_paths[i++] = node->path;
 +
 +      success = wpas_dbus_simple_array_property_getter(iter,
 +                                                       DBUS_TYPE_OBJECT_PATH,
 +                                                       peer_obj_paths, num,
 +                                                       error);
 +
 +error:
 +      if (peer_obj_paths)
 +              os_free(peer_obj_paths);
 +
 +      dl_list_for_each_safe(node, tmp, &peer_objpath_list,
 +                            struct peer_objpath_node, list) {
 +              dl_list_del(&node->list);
 +              os_free(node);
 +      }
 +      if (out_of_mem)
 +              dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
 +
 +      return success;
 +}
 +
 +
 +enum wpas_p2p_role {
 +      WPAS_P2P_ROLE_DEVICE,
 +      WPAS_P2P_ROLE_GO,
 +      WPAS_P2P_ROLE_CLIENT,
 +};
 +
 +static enum wpas_p2p_role wpas_get_p2p_role(struct wpa_supplicant *wpa_s)
 +{
 +      struct wpa_ssid *ssid = wpa_s->current_ssid;
 +
 +      if (!ssid)
 +              return WPAS_P2P_ROLE_DEVICE;
 +      if (wpa_s->wpa_state != WPA_COMPLETED)
 +              return WPAS_P2P_ROLE_DEVICE;
 +
 +      switch (ssid->mode) {
 +      case WPAS_MODE_P2P_GO:
 +      case WPAS_MODE_P2P_GROUP_FORMATION:
 +              return WPAS_P2P_ROLE_GO;
 +      case WPAS_MODE_INFRA:
 +              if (ssid->p2p_group)
 +                      return WPAS_P2P_ROLE_CLIENT;
 +              return WPAS_P2P_ROLE_DEVICE;
 +      default:
 +              return WPAS_P2P_ROLE_DEVICE;
 +      }
 +}
 +
 +
 +dbus_bool_t wpas_dbus_getter_p2p_role(DBusMessageIter *iter, DBusError *error,
 +                                    void *user_data)
 +{
 +      struct wpa_supplicant *wpa_s = user_data;
 +      char *str;
 +
 +      switch (wpas_get_p2p_role(wpa_s)) {
 +      case WPAS_P2P_ROLE_GO:
 +              str = "GO";
 +              break;
 +      case WPAS_P2P_ROLE_CLIENT:
 +              str = "client";
 +              break;
 +      default:
 +              str = "device";
 +              break;
 +      }
 +
 +      return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING, &str,
 +                                              error);
 +}
 +
 +
 +dbus_bool_t wpas_dbus_getter_p2p_group(DBusMessageIter *iter, DBusError *error,
 +                                     void *user_data)
 +{
 +      struct wpa_supplicant *wpa_s = user_data;
 +      char path_buf[WPAS_DBUS_OBJECT_PATH_MAX];
 +      char *dbus_groupobj_path = path_buf;
 +
 +      if (wpa_s->dbus_groupobj_path == NULL)
 +              os_snprintf(dbus_groupobj_path, WPAS_DBUS_OBJECT_PATH_MAX,
 +                          "/");
 +      else
 +              os_snprintf(dbus_groupobj_path, WPAS_DBUS_OBJECT_PATH_MAX,
 +                          "%s", wpa_s->dbus_groupobj_path);
 +
 +      return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_OBJECT_PATH,
 +                                              &dbus_groupobj_path, error);
 +}
 +
 +
 +dbus_bool_t wpas_dbus_getter_p2p_peergo(DBusMessageIter *iter,
 +                                      DBusError *error, void *user_data)
 +{
 +      struct wpa_supplicant *wpa_s = user_data;
 +      char go_peer_obj_path[WPAS_DBUS_OBJECT_PATH_MAX], *path;
 +
++      if (!wpa_s->parent->parent->dbus_new_path)
++              return FALSE;
++
 +      if (wpas_get_p2p_role(wpa_s) != WPAS_P2P_ROLE_CLIENT)
 +              os_snprintf(go_peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, "/");
 +      else
 +              os_snprintf(go_peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
 +                          "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART "/"
 +                          COMPACT_MACSTR,
-       if (wpa_s->p2p_dev)
-               wpa_s = wpa_s->p2p_dev;
++                          wpa_s->parent->parent->dbus_new_path,
++                          MAC2STR(wpa_s->go_dev_addr));
 +
 +      path = go_peer_obj_path;
 +      return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_OBJECT_PATH,
 +                                              &path, error);
 +}
 +
 +
 +/*
 + * Peer object properties accessor methods
 + */
 +
 +dbus_bool_t wpas_dbus_getter_p2p_peer_device_name(DBusMessageIter *iter,
 +                                                DBusError *error,
 +                                                void *user_data)
 +{
 +      struct peer_handler_args *peer_args = user_data;
 +      const struct p2p_peer_info *info;
 +      char *tmp;
 +
 +      if (!wpa_dbus_p2p_check_enabled(peer_args->wpa_s, NULL, NULL, error))
 +              return FALSE;
 +
 +      /* get the peer 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;
 +      }
 +
 +      tmp = os_strdup(info->device_name);
 +      if (!tmp) {
 +              dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
 +              return FALSE;
 +      }
 +
 +      if (!wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING, &tmp,
 +                                            error)) {
 +              dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
 +              os_free(tmp);
 +              return FALSE;
 +      }
 +
 +      os_free(tmp);
 +      return TRUE;
 +}
 +
 +
++dbus_bool_t wpas_dbus_getter_p2p_peer_manufacturer(DBusMessageIter *iter,
++                                                 DBusError *error,
++                                                 void *user_data)
++{
++      struct peer_handler_args *peer_args = user_data;
++      const struct p2p_peer_info *info;
++      char *tmp;
++
++      if (!wpa_dbus_p2p_check_enabled(peer_args->wpa_s, NULL, NULL, error))
++              return FALSE;
++
++      /* get the peer 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;
++      }
++
++      tmp = os_strdup(info->manufacturer);
++      if (!tmp) {
++              dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
++              return FALSE;
++      }
++
++      if (!wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING, &tmp,
++                                            error)) {
++              dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
++              os_free(tmp);
++              return FALSE;
++      }
++
++      os_free(tmp);
++      return TRUE;
++}
++
++
++dbus_bool_t wpas_dbus_getter_p2p_peer_modelname(DBusMessageIter *iter,
++                                              DBusError *error,
++                                              void *user_data)
++{
++      struct peer_handler_args *peer_args = user_data;
++      const struct p2p_peer_info *info;
++      char *tmp;
++
++      if (!wpa_dbus_p2p_check_enabled(peer_args->wpa_s, NULL, NULL, error))
++              return FALSE;
++
++      /* get the peer 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;
++      }
++
++      tmp = os_strdup(info->model_name);
++      if (!tmp) {
++              dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
++              return FALSE;
++      }
++
++      if (!wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING, &tmp,
++                                            error)) {
++              dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
++              os_free(tmp);
++              return FALSE;
++      }
++
++      os_free(tmp);
++      return TRUE;
++}
++
++
++dbus_bool_t wpas_dbus_getter_p2p_peer_modelnumber(DBusMessageIter *iter,
++                                                DBusError *error,
++                                                void *user_data)
++{
++      struct peer_handler_args *peer_args = user_data;
++      const struct p2p_peer_info *info;
++      char *tmp;
++
++      if (!wpa_dbus_p2p_check_enabled(peer_args->wpa_s, NULL, NULL, error))
++              return FALSE;
++
++      /* get the peer 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;
++      }
++
++      tmp = os_strdup(info->model_number);
++      if (!tmp) {
++              dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
++              return FALSE;
++      }
++
++      if (!wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING, &tmp,
++                                            error)) {
++              dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
++              os_free(tmp);
++              return FALSE;
++      }
++
++      os_free(tmp);
++      return TRUE;
++}
++
++
++dbus_bool_t wpas_dbus_getter_p2p_peer_serialnumber(DBusMessageIter *iter,
++                                                 DBusError *error,
++                                                 void *user_data)
++{
++      struct peer_handler_args *peer_args = user_data;
++      const struct p2p_peer_info *info;
++      char *tmp;
++
++      if (!wpa_dbus_p2p_check_enabled(peer_args->wpa_s, NULL, NULL, error))
++              return FALSE;
++
++      /* get the peer 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;
++      }
++
++      tmp = os_strdup(info->serial_number);
++      if (!tmp) {
++              dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
++              return FALSE;
++      }
++
++      if (!wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING, &tmp,
++                                            error)) {
++              dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
++              os_free(tmp);
++              return FALSE;
++      }
++
++      os_free(tmp);
++      return TRUE;
++}
++
++
 +dbus_bool_t wpas_dbus_getter_p2p_peer_primary_device_type(
 +      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;
 +      }
 +
 +      if (!wpas_dbus_simple_array_property_getter(iter, DBUS_TYPE_BYTE,
 +                                                  (char *)
 +                                                  info->pri_dev_type,
 +                                                  WPS_DEV_TYPE_LEN, error)) {
 +              dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
 +              return FALSE;
 +      }
 +
 +      return TRUE;
 +}
 +
 +
 +dbus_bool_t wpas_dbus_getter_p2p_peer_config_method(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;
 +      }
 +
 +      if (!wpas_dbus_simple_property_getter(iter, DBUS_TYPE_UINT16,
 +                                            &info->config_methods, error)) {
 +              dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
 +              return FALSE;
 +      }
 +
 +      return TRUE;
 +}
 +
 +
 +dbus_bool_t wpas_dbus_getter_p2p_peer_level(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;
 +      }
 +
 +      if (!wpas_dbus_simple_property_getter(iter, DBUS_TYPE_INT32,
 +                                            &info->level, error)) {
 +              dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
 +              return FALSE;
 +      }
 +
 +      return TRUE;
 +}
 +
 +
 +dbus_bool_t wpas_dbus_getter_p2p_peer_device_capability(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;
 +      }
 +
 +      if (!wpas_dbus_simple_property_getter(iter, DBUS_TYPE_BYTE,
 +                                            &info->dev_capab, error)) {
 +              dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
 +              return FALSE;
 +      }
 +
 +      return TRUE;
 +}
 +
 +
 +dbus_bool_t wpas_dbus_getter_p2p_peer_group_capability(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;
 +      }
 +
 +      if (!wpas_dbus_simple_property_getter(iter, DBUS_TYPE_BYTE,
 +                                            &info->group_capab, error)) {
 +              dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
 +              return FALSE;
 +      }
 +
 +      return TRUE;
 +}
 +
 +
 +dbus_bool_t wpas_dbus_getter_p2p_peer_secondary_device_types(
 +      DBusMessageIter *iter, DBusError *error, void *user_data)
 +{
 +      struct peer_handler_args *peer_args = user_data;
 +      const struct p2p_peer_info *info;
 +      DBusMessageIter variant_iter, array_iter;
 +
 +      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 (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
 +                                            DBUS_TYPE_ARRAY_AS_STRING
 +                                            DBUS_TYPE_ARRAY_AS_STRING
 +                                            DBUS_TYPE_BYTE_AS_STRING,
 +                                            &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 1", __func__);
 +              return FALSE;
 +      }
 +
 +      if (info->wps_sec_dev_type_list_len) {
 +              const u8 *sec_dev_type_list = info->wps_sec_dev_type_list;
 +              int num_sec_device_types =
 +                      info->wps_sec_dev_type_list_len / WPS_DEV_TYPE_LEN;
 +              int i;
 +              DBusMessageIter inner_array_iter;
 +
 +              for (i = 0; i < num_sec_device_types; i++) {
 +                      if (!dbus_message_iter_open_container(
 +                                  &array_iter, DBUS_TYPE_ARRAY,
 +                                  DBUS_TYPE_BYTE_AS_STRING,
 +                                  &inner_array_iter) ||
 +                          !dbus_message_iter_append_fixed_array(
 +                                  &inner_array_iter, DBUS_TYPE_BYTE,
 +                                  &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 2 (%d)",
 +                                             __func__, i);
 +                              return FALSE;
 +                      }
 +
 +                      sec_dev_type_list += WPS_DEV_TYPE_LEN;
 +              }
 +      }
 +
 +      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 3", __func__);
 +              return FALSE;
 +      }
 +
 +      return TRUE;
 +}
 +
 +
 +dbus_bool_t wpas_dbus_getter_p2p_peer_vendor_extension(DBusMessageIter *iter,
 +                                                     DBusError *error,
 +                                                     void *user_data)
 +{
 +      struct wpabuf *vendor_extension[P2P_MAX_WPS_VENDOR_EXT];
 +      unsigned int i, num = 0;
 +      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;
 +      }
 +
 +      /* Add WPS vendor extensions attribute */
 +      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];
 +              num++;
 +      }
 +
 +      if (!wpas_dbus_simple_array_array_property_getter(iter, DBUS_TYPE_BYTE,
 +                                                        vendor_extension,
 +                                                        num, error))
 +              return FALSE;
 +
 +      return TRUE;
 +}
 +
 +
 +dbus_bool_t wpas_dbus_getter_p2p_peer_ies(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;
 +      }
 +
 +      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;
 +      }
 +
 +      return wpas_dbus_simple_array_property_getter(
 +              iter, DBUS_TYPE_BYTE, (char *) info->p2p_device_addr,
 +              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;
-                           wpa_s->dbus_new_path, ssid->id);
++      wpa_s = wpa_s->global->p2p_init_wpa_s;
 +
 +      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;
 +}
 +
 +
 +/**
 + * wpas_dbus_getter_persistent_groups - Get array of persistent group objects
 + * @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 "PersistentGroups" property.
 + */
 +dbus_bool_t wpas_dbus_getter_persistent_groups(DBusMessageIter *iter,
 +                                             DBusError *error,
 +                                             void *user_data)
 +{
 +      struct wpa_supplicant *wpa_s = user_data;
 +      struct wpa_ssid *ssid;
 +      char **paths;
 +      unsigned int i = 0, num = 0;
 +      dbus_bool_t success = FALSE;
 +
++      wpa_s = wpa_s->global->p2p_init_wpa_s;
++      if (!wpa_s->parent->dbus_new_path)
++              return FALSE;
++
 +      for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next)
 +              if (network_is_persistent_group(ssid))
 +                      num++;
 +
 +      paths = os_calloc(num, sizeof(char *));
 +      if (!paths) {
 +              dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
 +              return FALSE;
 +      }
 +
 +      /* Loop through configured networks and append object path of each */
 +      for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
 +              if (!network_is_persistent_group(ssid))
 +                      continue;
 +              paths[i] = os_zalloc(WPAS_DBUS_OBJECT_PATH_MAX);
 +              if (paths[i] == NULL) {
 +                      dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY,
 +                                           "no memory");
 +                      goto out;
 +              }
 +              /* Construct the object path for this network. */
 +              os_snprintf(paths[i++], WPAS_DBUS_OBJECT_PATH_MAX,
 +                          "%s/" WPAS_DBUS_NEW_PERSISTENT_GROUPS_PART "/%d",
-  * wpas_dbus_setter_persistent_group_properties - Get options for a persistent
++                          wpa_s->parent->dbus_new_path, ssid->id);
 +      }
 +
 +      success = wpas_dbus_simple_array_property_getter(iter,
 +                                                       DBUS_TYPE_OBJECT_PATH,
 +                                                       paths, num, error);
 +
 +out:
 +      while (i)
 +              os_free(paths[--i]);
 +      os_free(paths);
 +      return success;
 +}
 +
 +
 +/**
 + * wpas_dbus_getter_persistent_group_properties - Get options for a persistent
 + *    group
 + * @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 "Properties" property of a persistent group.
 + */
 +dbus_bool_t wpas_dbus_getter_persistent_group_properties(DBusMessageIter *iter,
 +                                                       DBusError *error,
 +                                                       void *user_data)
 +{
 +      struct network_handler_args *net = user_data;
 +
 +      /* Leveraging the fact that persistent group object is still
 +       * represented in same manner as network within.
 +       */
 +      return wpas_dbus_getter_network_properties(iter, error, net);
 +}
 +
 +
 +/**
-       ssid = wpa_config_add_network(wpa_s->conf);
++ * wpas_dbus_setter_persistent_group_properties - Set options for a persistent
 + *    group
 + * @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 "Properties" property of a persistent group.
 + */
 +dbus_bool_t wpas_dbus_setter_persistent_group_properties(DBusMessageIter *iter,
 +                                                       DBusError *error,
 +                                                       void *user_data)
 +{
 +      struct network_handler_args *net = user_data;
 +      struct wpa_ssid *ssid = net->ssid;
 +      DBusMessageIter variant_iter;
 +
 +      /*
 +       * Leveraging the fact that persistent group object is still
 +       * represented in same manner as network within.
 +       */
 +      dbus_message_iter_recurse(iter, &variant_iter);
 +      return set_network_properties(net->wpa_s, ssid, &variant_iter, error);
 +}
 +
 +
 +/**
 + * wpas_dbus_new_iface_add_persistent_group - Add a new configured
 + *    persistent_group
 + * @message: Pointer to incoming dbus message
 + * @wpa_s: wpa_supplicant structure for a network interface
 + * Returns: A dbus message containing the object path of the new
 + * persistent group
 + *
 + * Handler function for "AddPersistentGroup" method call of a P2P Device
 + * interface.
 + */
 +DBusMessage * wpas_dbus_handler_add_persistent_group(
 +      DBusMessage *message, struct wpa_supplicant *wpa_s)
 +{
 +      DBusMessage *reply = NULL;
 +      DBusMessageIter iter;
 +      struct wpa_ssid *ssid = NULL;
 +      char path_buf[WPAS_DBUS_OBJECT_PATH_MAX], *path = path_buf;
 +      DBusError error;
 +
 +      dbus_message_iter_init(message, &iter);
 +
-                   wpa_s->dbus_new_path, ssid->id);
++      wpa_s = wpa_s->global->p2p_init_wpa_s;
++      if (wpa_s->parent->dbus_new_path)
++              ssid = wpa_config_add_network(wpa_s->conf);
 +      if (ssid == NULL) {
 +              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.");
 +              goto err;
 +      }
 +
 +      /* Mark the ssid as being a persistent group before the notification */
 +      ssid->disabled = 2;
 +      ssid->p2p_persistent_group = 1;
 +      wpas_notify_persistent_group_added(wpa_s, ssid);
 +
 +      wpa_config_set_network_defaults(ssid);
 +
 +      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");
 +              dbus_error_free(&error);
 +              goto err;
 +      }
 +
 +      /* Construct the object path for this network. */
 +      os_snprintf(path, WPAS_DBUS_OBJECT_PATH_MAX,
 +                  "%s/" WPAS_DBUS_NEW_PERSISTENT_GROUPS_PART "/%d",
-           os_strcmp(iface, wpa_s->dbus_new_path) != 0) {
++                  wpa_s->parent->dbus_new_path, ssid->id);
 +
 +      reply = dbus_message_new_method_return(message);
 +      if (reply == 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 = wpas_dbus_error_no_memory(message);
 +              goto err;
 +      }
 +
 +      return reply;
 +
 +err:
 +      if (ssid) {
 +              wpas_notify_persistent_group_removed(wpa_s, ssid);
 +              wpa_config_remove_network(wpa_s->conf, ssid->id);
 +      }
 +      return reply;
 +}
 +
 +
 +/**
 + * wpas_dbus_handler_remove_persistent_group - Remove a configured persistent
 + *    group
 + * @message: Pointer to incoming dbus message
 + * @wpa_s: wpa_supplicant structure for a network interface
 + * Returns: NULL on success or dbus error on failure
 + *
 + * Handler function for "RemovePersistentGroup" method call of a P2P Device
 + * interface.
 + */
 +DBusMessage * wpas_dbus_handler_remove_persistent_group(
 +      DBusMessage *message, struct wpa_supplicant *wpa_s)
 +{
 +      DBusMessage *reply = NULL;
 +      const char *op;
 +      char *iface = NULL, *persistent_group_id;
 +      int id;
 +      struct wpa_ssid *ssid;
 +
 +      dbus_message_get_args(message, NULL, DBUS_TYPE_OBJECT_PATH, &op,
 +                            DBUS_TYPE_INVALID);
 +
++      wpa_s = wpa_s->global->p2p_init_wpa_s;
++
 +      /*
 +       * Extract the network ID and ensure the network is actually a child of
 +       * this interface.
 +       */
 +      iface = wpas_dbus_new_decompose_object_path(
 +              op, WPAS_DBUS_NEW_PERSISTENT_GROUPS_PART,
 +              &persistent_group_id);
 +      if (iface == NULL || persistent_group_id == NULL ||
-                           wpa_s->parent->dbus_new_path, MAC2STR(addr));
++          !wpa_s->parent->dbus_new_path ||
++          os_strcmp(iface, wpa_s->parent->dbus_new_path) != 0) {
 +              reply = wpas_dbus_error_invalid_args(message, op);
 +              goto out;
 +      }
 +
 +      id = strtoul(persistent_group_id, NULL, 10);
 +      if (errno == EINVAL) {
 +              reply = wpas_dbus_error_invalid_args(message, op);
 +              goto out;
 +      }
 +
 +      ssid = wpa_config_get_network(wpa_s->conf, id);
 +      if (ssid == NULL) {
 +              reply = wpas_dbus_error_persistent_group_unknown(message);
 +              goto out;
 +      }
 +
 +      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",
 +                         __func__, id);
 +              reply = wpas_dbus_error_unknown_error(
 +                      message,
 +                      "error removing the specified persistent group on this interface.");
 +              goto out;
 +      }
 +
 +out:
 +      os_free(iface);
 +      return reply;
 +}
 +
 +
 +static void remove_persistent_group(struct wpa_supplicant *wpa_s,
 +                                  struct wpa_ssid *ssid)
 +{
 +      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",
 +                         __func__, ssid->id);
 +              return;
 +      }
 +}
 +
 +
 +/**
 + * wpas_dbus_handler_remove_all_persistent_groups - Remove all configured
 + * persistent groups
 + * @message: Pointer to incoming dbus message
 + * @wpa_s: wpa_supplicant structure for a network interface
 + * Returns: NULL on success or dbus error on failure
 + *
 + * Handler function for "RemoveAllPersistentGroups" method call of a
 + * P2P Device interface.
 + */
 +DBusMessage * wpas_dbus_handler_remove_all_persistent_groups(
 +      DBusMessage *message, struct wpa_supplicant *wpa_s)
 +{
 +      struct wpa_ssid *ssid, *next;
 +      struct wpa_config *config;
 +
++      wpa_s = wpa_s->global->p2p_init_wpa_s;
++
 +      config = wpa_s->conf;
 +      ssid = config->ssid;
 +      while (ssid) {
 +              next = ssid->next;
 +              if (network_is_persistent_group(ssid))
 +                      remove_persistent_group(wpa_s, ssid);
 +              ssid = next;
 +      }
 +      return NULL;
 +}
 +
 +
 +/*
 + * Group object properties accessor methods
 + */
 +
 +dbus_bool_t wpas_dbus_getter_p2p_group_members(DBusMessageIter *iter,
 +                                             DBusError *error,
 +                                             void *user_data)
 +{
 +      struct wpa_supplicant *wpa_s = user_data;
 +      struct wpa_ssid *ssid;
 +      unsigned int num_members;
 +      char **paths;
 +      unsigned int i;
 +      void *next = NULL;
 +      const u8 *addr;
 +      dbus_bool_t success = FALSE;
 +
++      if (!wpa_s->parent->parent->dbus_new_path)
++              return FALSE;
++
 +      /* Verify correct role for this property */
 +      if (wpas_get_p2p_role(wpa_s) != WPAS_P2P_ROLE_GO) {
 +              return wpas_dbus_simple_array_property_getter(
 +                      iter, DBUS_TYPE_OBJECT_PATH, NULL, 0, error);
 +      }
 +
 +      ssid = wpa_s->conf->ssid;
 +      /* At present WPAS P2P_GO mode only applicable for p2p_go */
 +      if (ssid->mode != WPAS_MODE_P2P_GO &&
 +          ssid->mode != WPAS_MODE_AP &&
 +          ssid->mode != WPAS_MODE_P2P_GROUP_FORMATION)
 +              return FALSE;
 +
 +      num_members = p2p_get_group_num_members(wpa_s->p2p_group);
 +
 +      paths = os_calloc(num_members, sizeof(char *));
 +      if (!paths)
 +              goto out_of_memory;
 +
 +      i = 0;
 +      while ((addr = p2p_iterate_group_members(wpa_s->p2p_group, &next))) {
 +              paths[i] = os_zalloc(WPAS_DBUS_OBJECT_PATH_MAX);
 +              if (!paths[i])
 +                      goto out_of_memory;
 +              os_snprintf(paths[i], WPAS_DBUS_OBJECT_PATH_MAX,
 +                          "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART
 +                          "/" COMPACT_MACSTR,
++                          wpa_s->parent->parent->dbus_new_path,
++                          MAC2STR(addr));
 +              i++;
 +      }
 +
 +      success = wpas_dbus_simple_array_property_getter(iter,
 +                                                       DBUS_TYPE_OBJECT_PATH,
 +                                                       paths, num_members,
 +                                                       error);
 +
 +      for (i = 0; i < num_members; i++)
 +              os_free(paths[i]);
 +      os_free(paths);
 +      return success;
 +
 +out_of_memory:
 +      dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
 +      if (paths) {
 +              for (i = 0; i < num_members; i++)
 +                      os_free(paths[i]);
 +              os_free(paths);
 +      }
 +      return FALSE;
 +}
 +
 +
 +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(
 +              iter, DBUS_TYPE_BYTE, wpa_s->current_ssid->ssid,
 +              wpa_s->current_ssid->ssid_len, error);
 +}
 +
 +
 +dbus_bool_t wpas_dbus_getter_p2p_group_bssid(DBusMessageIter *iter,
 +                                           DBusError *error,
 +                                           void *user_data)
 +{
 +      struct wpa_supplicant *wpa_s = user_data;
 +      u8 role = wpas_get_p2p_role(wpa_s);
 +      u8 *p_bssid;
 +
 +      if (role == WPAS_P2P_ROLE_CLIENT) {
 +              if (wpa_s->current_ssid == NULL)
 +                      return FALSE;
 +              p_bssid = wpa_s->current_ssid->bssid;
 +      } else {
 +              if (wpa_s->ap_iface == NULL)
 +                      return FALSE;
 +              p_bssid = wpa_s->ap_iface->bss[0]->own_addr;
 +      }
 +
 +      return wpas_dbus_simple_array_property_getter(iter, DBUS_TYPE_BYTE,
 +                                                    p_bssid, ETH_ALEN,
 +                                                    error);
 +}
 +
 +
 +dbus_bool_t wpas_dbus_getter_p2p_group_frequency(DBusMessageIter *iter,
 +                                               DBusError *error,
 +                                               void *user_data)
 +{
 +      struct wpa_supplicant *wpa_s = user_data;
 +      u16 op_freq;
 +      u8 role = wpas_get_p2p_role(wpa_s);
 +
 +      if (role == WPAS_P2P_ROLE_CLIENT) {
 +              if (wpa_s->go_params == NULL)
 +                      return FALSE;
 +              op_freq = wpa_s->go_params->freq;
 +      } else {
 +              if (wpa_s->ap_iface == NULL)
 +                      return FALSE;
 +              op_freq = wpa_s->ap_iface->freq;
 +      }
 +
 +      return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_UINT16,
 +                                              &op_freq, error);
 +}
 +
 +
 +dbus_bool_t wpas_dbus_getter_p2p_group_passphrase(DBusMessageIter *iter,
 +                                                DBusError *error,
 +                                                void *user_data)
 +{
 +      struct wpa_supplicant *wpa_s = user_data;
 +      char *p_pass;
 +      struct wpa_ssid *ssid = wpa_s->current_ssid;
 +
 +      if (ssid == NULL)
 +              return FALSE;
 +
 +      p_pass = ssid->passphrase;
 +      if (!p_pass)
 +              p_pass = "";
 +
 +      return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING,
 +                                              &p_pass, error);
 +
 +}
 +
 +
 +dbus_bool_t wpas_dbus_getter_p2p_group_psk(DBusMessageIter *iter,
 +                                         DBusError *error, void *user_data)
 +{
 +      struct wpa_supplicant *wpa_s = user_data;
 +      u8 *p_psk = NULL;
 +      u8 psk_len = 0;
 +      struct wpa_ssid *ssid = wpa_s->current_ssid;
 +
 +      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);
 +}
 +
 +
 +dbus_bool_t wpas_dbus_getter_p2p_group_vendor_ext(DBusMessageIter *iter,
 +                                                DBusError *error,
 +                                                void *user_data)
 +{
 +      struct wpa_supplicant *wpa_s = user_data;
 +      struct hostapd_data *hapd;
 +      struct wpabuf *vendor_ext[MAX_WPS_VENDOR_EXTENSIONS];
 +      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) {
 +              if (wpa_s->ap_iface == NULL)
 +                      return FALSE;
 +              hapd = wpa_s->ap_iface->bss[0];
 +
 +              /* 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)
 +                              continue;
 +                      vendor_ext[num_vendor_ext++] =
 +                              hapd->conf->wps_vendor_ext[i];
 +              }
 +      }
 +
 +      /* Return vendor extensions or no data */
 +      return wpas_dbus_simple_array_array_property_getter(iter,
 +                                                          DBUS_TYPE_BYTE,
 +                                                          vendor_ext,
 +                                                          num_vendor_ext,
 +                                                          error);
 +}
 +
 +
 +dbus_bool_t wpas_dbus_setter_p2p_group_vendor_ext(DBusMessageIter *iter,
 +                                                DBusError *error,
 +                                                void *user_data)
 +{
 +      struct wpa_supplicant *wpa_s = user_data;
 +      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;
 +
 +      if (wpas_get_p2p_role(wpa_s) == WPAS_P2P_ROLE_GO &&
 +          wpa_s->ap_iface != NULL)
 +              hapd = wpa_s->ap_iface->bss[0];
 +      else
 +              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;
 +
 +      while (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
 +              if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) {
 +                      dbus_set_error_const(error, DBUS_ERROR_INVALID_ARGS,
 +                                           "invalid message format");
 +                      return FALSE;
 +              }
 +
 +              if (os_strcmp(entry.key, "WPSVendorExtensions") == 0) {
 +                      if (entry.type != DBUS_TYPE_ARRAY ||
 +                          entry.array_type != WPAS_DBUS_TYPE_BINARRAY ||
 +                          entry.array_len > MAX_WPS_VENDOR_EXTENSIONS)
 +                              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];
 +                                      entry.binarray_value[i] = NULL;
 +                              } else
 +                                      hapd->conf->wps_vendor_ext[i] = NULL;
 +                      }
 +
 +                      hostapd_update_wps(hapd);
 +              } else
 +                      goto error;
 +
 +              wpa_dbus_dict_entry_clear(&entry);
 +      }
 +
 +      return TRUE;
 +
 +error:
 +      wpa_dbus_dict_entry_clear(&entry);
 +      dbus_set_error_const(error, DBUS_ERROR_INVALID_ARGS,
 +                           "invalid message format");
 +      return FALSE;
 +}
 +
 +
 +DBusMessage * wpas_dbus_handler_p2p_add_service(DBusMessage *message,
 +                                              struct wpa_supplicant *wpa_s)
 +{
 +      DBusMessageIter iter_dict;
 +      DBusMessage *reply = NULL;
 +      DBusMessageIter iter;
 +      struct wpa_dbus_dict_entry entry;
 +      int upnp = 0;
 +      int bonjour = 0;
 +      char *service = NULL;
 +      struct wpabuf *query = NULL;
 +      struct wpabuf *resp = NULL;
 +      u8 version = 0;
 +
 +      dbus_message_iter_init(message, &iter);
 +
 +      if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL))
 +              goto error;
 +
 +      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, "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") == 0)
 +                              bonjour = 1;
 +                      else
 +                              goto error_clear;
 +              } else if (os_strcmp(entry.key, "version") == 0 &&
 +                         entry.type == DBUS_TYPE_INT32) {
 +                      version = entry.uint32_value;
 +              } 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") == 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") == 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);
 +              }
 +              wpa_dbus_dict_entry_clear(&entry);
 +      }
 +
 +      if (upnp == 1) {
 +              if (version <= 0 || service == NULL)
 +                      goto error;
 +
 +              if (wpas_p2p_service_add_upnp(wpa_s, version, service) != 0)
 +                      goto error;
 +
 +      } else if (bonjour == 1) {
 +              if (query == NULL || resp == NULL)
 +                      goto error;
 +
 +              if (wpas_p2p_service_add_bonjour(wpa_s, query, resp) < 0)
 +                      goto error;
 +              query = NULL;
 +              resp = NULL;
 +      } else
 +              goto error;
 +
 +      os_free(service);
 +      return reply;
 +error_clear:
 +      wpa_dbus_dict_entry_clear(&entry);
 +error:
 +      os_free(service);
 +      wpabuf_free(query);
 +      wpabuf_free(resp);
 +      return wpas_dbus_error_invalid_args(message, NULL);
 +}
 +
 +
 +DBusMessage * wpas_dbus_handler_p2p_delete_service(
 +      DBusMessage *message, struct wpa_supplicant *wpa_s)
 +{
 +      DBusMessageIter iter_dict;
 +      DBusMessage *reply = NULL;
 +      DBusMessageIter iter;
 +      struct wpa_dbus_dict_entry entry;
 +      int upnp = 0;
 +      int bonjour = 0;
 +      int ret = 0;
 +      char *service = NULL;
 +      struct wpabuf *query = NULL;
 +      u8 version = 0;
 +
 +      dbus_message_iter_init(message, &iter);
 +
 +      if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL))
 +              goto error;
 +
 +      if (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
 +              if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
 +                      goto error;
 +
 +              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") == 0)
 +                              bonjour = 1;
 +                      else
 +                              goto error_clear;
 +                      wpa_dbus_dict_entry_clear(&entry);
 +              }
 +      }
 +      if (upnp == 1) {
 +              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") == 0 &&
 +                          entry.type == DBUS_TYPE_INT32)
 +                              version = entry.uint32_value;
 +                      else if (os_strcmp(entry.key, "service") == 0 &&
 +                               entry.type == DBUS_TYPE_STRING) {
 +                              os_free(service);
 +                              service = os_strdup(entry.str_value);
 +                      } else
 +                              goto error_clear;
 +
 +                      wpa_dbus_dict_entry_clear(&entry);
 +              }
 +
 +              if (version <= 0 || service == NULL)
 +                      goto error;
 +
 +              ret = wpas_p2p_service_del_upnp(wpa_s, version, service);
 +              if (ret != 0)
 +                      goto error;
 +      } else if (bonjour == 1) {
 +              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, "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);
 +                      } else
 +                              goto error_clear;
 +
 +                      wpa_dbus_dict_entry_clear(&entry);
 +              }
 +
 +              if (query == NULL)
 +                      goto error;
 +
 +              ret = wpas_p2p_service_del_bonjour(wpa_s, query);
 +              if (ret != 0)
 +                      goto error;
 +      } 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);
 +}
 +
 +
 +DBusMessage * wpas_dbus_handler_p2p_flush_service(DBusMessage *message,
 +                                                struct wpa_supplicant *wpa_s)
 +{
 +      wpas_p2p_service_flush(wpa_s);
 +      return NULL;
 +}
 +
 +
 +DBusMessage * wpas_dbus_handler_p2p_service_sd_req(
 +      DBusMessage *message, struct wpa_supplicant *wpa_s)
 +{
 +      DBusMessageIter iter_dict;
 +      DBusMessage *reply = NULL;
 +      DBusMessageIter iter;
 +      struct wpa_dbus_dict_entry entry;
 +      int upnp = 0;
 +      char *service = NULL;
 +      char *peer_object_path = NULL;
 +      struct wpabuf *tlv = NULL;
 +      u8 version = 0;
 +      u64 ref = 0;
 +      u8 addr_buf[ETH_ALEN], *addr;
 +
 +      dbus_message_iter_init(message, &iter);
 +
 +      if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL))
 +              goto error;
 +
 +      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") == 0 &&
 +                  entry.type == DBUS_TYPE_OBJECT_PATH) {
 +                      peer_object_path = os_strdup(entry.str_value);
 +              } else if (os_strcmp(entry.key, "service_type") == 0 &&
 +                         entry.type == DBUS_TYPE_STRING) {
 +                      if (os_strcmp(entry.str_value, "upnp") == 0)
 +                              upnp = 1;
 +                      else
 +                              goto error_clear;
 +              } else if (os_strcmp(entry.key, "version") == 0 &&
 +                         entry.type == DBUS_TYPE_INT32) {
 +                      version = entry.uint32_value;
 +              } 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") == 0) {
 +                      if (entry.type != DBUS_TYPE_ARRAY ||
 +                          entry.array_type != DBUS_TYPE_BYTE)
 +                              goto error_clear;
 +                      tlv = wpabuf_alloc_copy(entry.bytearray_value,
 +                                              entry.array_len);
 +              } else
 +                      goto error_clear;
 +
 +              wpa_dbus_dict_entry_clear(&entry);
 +      }
 +
 +      if (!peer_object_path) {
 +              addr = NULL;
 +      } else {
 +              if (parse_peer_object_path(peer_object_path, addr_buf) < 0 ||
 +                  !p2p_peer_known(wpa_s->global->p2p, addr_buf))
 +                      goto error;
 +
 +              addr = addr_buf;
 +      }
 +
 +      if (upnp == 1) {
 +              if (version <= 0 || service == NULL)
 +                      goto error;
 +
 +              ref = wpas_p2p_sd_request_upnp(wpa_s, addr, version, service);
 +      } else {
 +              if (tlv == NULL)
 +                      goto error;
 +              ref = wpas_p2p_sd_request(wpa_s, addr, tlv);
 +              wpabuf_free(tlv);
 +      }
 +
 +      if (ref != 0) {
 +              reply = dbus_message_new_method_return(message);
 +              dbus_message_append_args(reply, DBUS_TYPE_UINT64,
 +                                       &ref, DBUS_TYPE_INVALID);
 +      } else {
 +              reply = wpas_dbus_error_unknown_error(
 +                      message, "Unable to send SD request");
 +      }
 +out:
 +      os_free(service);
 +      os_free(peer_object_path);
 +      return reply;
 +error_clear:
 +      wpa_dbus_dict_entry_clear(&entry);
 +error:
 +      if (tlv)
 +              wpabuf_free(tlv);
 +      reply = wpas_dbus_error_invalid_args(message, NULL);
 +      goto out;
 +}
 +
 +
 +DBusMessage * wpas_dbus_handler_p2p_service_sd_res(
 +      DBusMessage *message, struct wpa_supplicant *wpa_s)
 +{
 +      DBusMessageIter iter_dict;
 +      DBusMessage *reply = NULL;
 +      DBusMessageIter iter;
 +      struct wpa_dbus_dict_entry entry;
 +      char *peer_object_path = NULL;
 +      struct wpabuf *tlv = NULL;
 +      int freq = 0;
 +      int dlg_tok = 0;
 +      u8 addr[ETH_ALEN];
 +
 +      dbus_message_iter_init(message, &iter);
 +
 +      if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL))
 +              goto error;
 +
 +      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") == 0 &&
 +                  entry.type == DBUS_TYPE_OBJECT_PATH) {
 +                      peer_object_path = os_strdup(entry.str_value);
 +              } 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") == 0 &&
 +                         (entry.type == DBUS_TYPE_UINT32 ||
 +                          entry.type == DBUS_TYPE_INT32)) {
 +                      dlg_tok = entry.uint32_value;
 +              } else if (os_strcmp(entry.key, "tlvs") == 0) {
 +                      if (entry.type != DBUS_TYPE_ARRAY ||
 +                          entry.array_type != DBUS_TYPE_BYTE)
 +                              goto error_clear;
 +                      tlv = wpabuf_alloc_copy(entry.bytearray_value,
 +                                              entry.array_len);
 +              } else
 +                      goto error_clear;
 +
 +              wpa_dbus_dict_entry_clear(&entry);
 +      }
 +      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);
 +      wpabuf_free(tlv);
 +out:
 +      os_free(peer_object_path);
 +      return reply;
 +error_clear:
 +      wpa_dbus_dict_entry_clear(&entry);
 +error:
 +      reply = wpas_dbus_error_invalid_args(message, NULL);
 +      goto out;
 +}
 +
 +
 +DBusMessage * wpas_dbus_handler_p2p_service_sd_cancel_req(
 +      DBusMessage *message, struct wpa_supplicant *wpa_s)
 +{
 +      DBusMessageIter iter;
 +      u64 req = 0;
 +
 +      dbus_message_iter_init(message, &iter);
 +      dbus_message_iter_get_basic(&iter, &req);
 +
 +      if (req == 0)
 +              goto error;
 +
 +      if (wpas_p2p_sd_cancel_request(wpa_s, req) < 0)
 +              goto error;
 +
 +      return NULL;
 +error:
 +      return wpas_dbus_error_invalid_args(message, NULL);
 +}
 +
 +
 +DBusMessage * wpas_dbus_handler_p2p_service_update(
 +      DBusMessage *message, struct wpa_supplicant *wpa_s)
 +{
 +      wpas_p2p_sd_service_update(wpa_s);
 +      return NULL;
 +}
 +
 +
 +DBusMessage * wpas_dbus_handler_p2p_serv_disc_external(
 +      DBusMessage *message, struct wpa_supplicant *wpa_s)
 +{
 +      DBusMessageIter iter;
 +      int ext = 0;
 +
 +      dbus_message_iter_init(message, &iter);
 +      dbus_message_iter_get_basic(&iter, &ext);
 +
 +      wpa_s->p2p_sd_over_ctrl_iface = ext;
 +
 +      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 */
index fdaccbafb1437b736129cce90926622f8995551f,0000000000000000000000000000000000000000..2aecbbe465070e97f4dc1e5efe3122565d1d83fa
mode 100644,000000..100644
--- /dev/null
@@@ -1,225 -1,0 +1,247 @@@
 +/*
 + * WPA Supplicant / dbus-based control interface for p2p
 + * Copyright (c) 2011-2012, Intel Corporation
 + *
 + * This software may be distributed under the terms of the BSD license.
 + * See README for more details.
 + */
 +
 +#ifndef DBUS_NEW_HANDLERS_P2P_H
 +#define DBUS_NEW_HANDLERS_P2P_H
 +
 +struct peer_handler_args {
 +      struct wpa_supplicant *wpa_s;
 +      u8 p2p_device_addr[ETH_ALEN];
 +};
 +
 +/*
 + * P2P Device methods
 + */
 +
 +DBusMessage *wpas_dbus_handler_p2p_find(
 +      DBusMessage *message, struct wpa_supplicant *wpa_s);
 +
 +DBusMessage *wpas_dbus_handler_p2p_stop_find(
 +      DBusMessage *message, struct wpa_supplicant *wpa_s);
 +
 +DBusMessage *wpas_dbus_handler_p2p_rejectpeer(
 +      DBusMessage *message, struct wpa_supplicant *wpa_s);
 +
 +DBusMessage *wpas_dbus_handler_p2p_listen(
 +      DBusMessage *message, struct wpa_supplicant *wpa_s);
 +
 +DBusMessage *wpas_dbus_handler_p2p_extendedlisten(
 +      DBusMessage *message, struct wpa_supplicant *wpa_s);
 +
 +DBusMessage *wpas_dbus_handler_p2p_presence_request(
 +      DBusMessage *message, struct wpa_supplicant *wpa_s);
 +
 +DBusMessage *wpas_dbus_handler_p2p_prov_disc_req(
 +      DBusMessage *message, struct wpa_supplicant *wpa_s);
 +
 +DBusMessage *wpas_dbus_handler_p2p_group_add(
 +      DBusMessage *message, struct wpa_supplicant *wpa_s);
 +
 +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);
 +
 +DBusMessage *wpas_dbus_handler_p2p_disconnect(
 +      DBusMessage *message, struct wpa_supplicant *wpa_s);
 +
++DBusMessage * wpas_dbus_handler_p2p_remove_client(
++      DBusMessage *message, struct wpa_supplicant *wpa_s);
++
 +DBusMessage *wpas_dbus_handler_p2p_flush(
 +      DBusMessage *message, struct wpa_supplicant *wpa_s);
 +
 +DBusMessage *wpas_dbus_handler_p2p_add_service(
 +      DBusMessage *message, struct wpa_supplicant *wpa_s);
 +
 +DBusMessage *wpas_dbus_handler_p2p_delete_service(
 +      DBusMessage *message, struct wpa_supplicant *wpa_s);
 +
 +DBusMessage *wpas_dbus_handler_p2p_flush_service(
 +      DBusMessage *message, struct wpa_supplicant *wpa_s);
 +
 +DBusMessage *wpas_dbus_handler_p2p_service_sd_req(
 +      DBusMessage *message, struct wpa_supplicant *wpa_s);
 +
 +DBusMessage *wpas_dbus_handler_p2p_service_sd_res(
 +      DBusMessage *message, struct wpa_supplicant *wpa_s);
 +
 +DBusMessage *wpas_dbus_handler_p2p_service_sd_cancel_req(
 +      DBusMessage *message, struct wpa_supplicant *wpa_s);
 +
 +DBusMessage *wpas_dbus_handler_p2p_service_update(
 +      DBusMessage *message, struct wpa_supplicant *wpa_s);
 +
 +DBusMessage *wpas_dbus_handler_p2p_serv_disc_external(
 +      DBusMessage *message, struct wpa_supplicant *wpa_s);
 +
 +/*
 + * P2P Device property accessor methods.
 + */
 +dbus_bool_t wpas_dbus_setter_p2p_device_config(DBusMessageIter *iter,
 +                                             DBusError *error,
 +                                             void *user_data);
 +
 +dbus_bool_t wpas_dbus_getter_p2p_device_config(DBusMessageIter *iter,
 +                                             DBusError *error,
 +                                             void *user_data);
 +
 +dbus_bool_t wpas_dbus_getter_p2p_peers(DBusMessageIter *iter, DBusError *error,
 +                                     void *user_data);
 +
 +dbus_bool_t wpas_dbus_getter_p2p_role(DBusMessageIter *iter, DBusError *error,
 +                                    void *user_data);
 +
 +dbus_bool_t wpas_dbus_getter_p2p_group(DBusMessageIter *iter, DBusError *error,
 +                                     void *user_data);
 +
 +dbus_bool_t wpas_dbus_getter_p2p_peergo(DBusMessageIter *iter,
 +                                      DBusError *error,
 +                                      void *user_data);
 +
 +/*
 + * P2P Peer properties.
 + */
 +
 +dbus_bool_t wpas_dbus_getter_p2p_peer_device_name(DBusMessageIter *iter,
 +                                                DBusError *error,
 +                                                void *user_data);
 +
++dbus_bool_t wpas_dbus_getter_p2p_peer_manufacturer(DBusMessageIter *iter,
++                                                 DBusError *error,
++                                                 void *user_data);
++
++dbus_bool_t wpas_dbus_getter_p2p_peer_modelname(DBusMessageIter *iter,
++                                              DBusError *error,
++                                              void *user_data);
++
++dbus_bool_t wpas_dbus_getter_p2p_peer_modelnumber(DBusMessageIter *iter,
++                                                DBusError *error,
++                                                void *user_data);
++
++dbus_bool_t wpas_dbus_getter_p2p_peer_serialnumber(DBusMessageIter *iter,
++                                                 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);
 +
 +dbus_bool_t wpas_dbus_getter_p2p_peer_level(DBusMessageIter *iter,
 +                                          DBusError *error,
 +                                          void *user_data);
 +
 +dbus_bool_t wpas_dbus_getter_p2p_peer_device_capability(DBusMessageIter *iter,
 +                                                      DBusError *error,
 +                                                      void *user_data);
 +
 +dbus_bool_t wpas_dbus_getter_p2p_peer_group_capability(DBusMessageIter *iter,
 +                                                     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);
 +
 +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_groups(DBusMessageIter *iter,
 +                                           DBusError *error,
 +                                           void *user_data);
 +
 +/*
 + * P2P Group properties
 + */
 +
 +dbus_bool_t wpas_dbus_getter_p2p_group_members(DBusMessageIter *iter,
 +                                             DBusError *error,
 +                                             void *user_data);
 +
 +dbus_bool_t wpas_dbus_getter_p2p_group_ssid(DBusMessageIter *iter,
 +                                          DBusError *error,
 +                                          void *user_data);
 +
 +dbus_bool_t wpas_dbus_getter_p2p_group_bssid(DBusMessageIter *iter,
 +                                           DBusError *error,
 +                                           void *user_data);
 +
 +dbus_bool_t wpas_dbus_getter_p2p_group_frequency(DBusMessageIter *iter,
 +                                               DBusError *error,
 +                                               void *user_data);
 +
 +dbus_bool_t wpas_dbus_getter_p2p_group_passphrase(DBusMessageIter *iter,
 +                                                DBusError *error,
 +                                                void *user_data);
 +
 +dbus_bool_t wpas_dbus_getter_p2p_group_psk(DBusMessageIter *iter,
 +                                         DBusError *error,
 +                                         void *user_data);
 +
 +dbus_bool_t wpas_dbus_getter_p2p_group_vendor_ext(DBusMessageIter *iter,
 +                                                DBusError *error,
 +                                                void *user_data);
 +
 +dbus_bool_t wpas_dbus_setter_p2p_group_vendor_ext(DBusMessageIter *iter,
 +                                                DBusError *error,
 +                                                void *user_data);
 +
 +/*
 + * P2P Persistent Groups and properties
 + */
 +
 +dbus_bool_t wpas_dbus_getter_persistent_groups(DBusMessageIter *iter,
 +                                             DBusError *error,
 +                                             void *user_data);
 +
 +dbus_bool_t wpas_dbus_getter_persistent_group_properties(DBusMessageIter *iter,
 +      DBusError *error, void *user_data);
 +
 +dbus_bool_t wpas_dbus_setter_persistent_group_properties(DBusMessageIter *iter,
 +                                                       DBusError *error,
 +                                                       void *user_data);
 +
 +DBusMessage * wpas_dbus_handler_add_persistent_group(
 +      DBusMessage *message, struct wpa_supplicant *wpa_s);
 +
 +DBusMessage * wpas_dbus_handler_remove_persistent_group(
 +      DBusMessage *message, struct wpa_supplicant *wpa_s);
 +
 +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 */
index a94a0e51fc294c3118d58717f1277762dee80fe4,0000000000000000000000000000000000000000..b2251baa3fe5cbca27dd53ac8befa47ad622e40d
mode 100644,000000..100644
--- /dev/null
@@@ -1,434 -1,0 +1,456 @@@
-               wpa_printf(MSG_DEBUG, "dbus: WPS.Start - Uknown role %s", val);
 +/*
 + * WPA Supplicant / dbus-based control interface (WPS)
 + * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc.
 + * Copyright (c) 2009, Witold Sowa <witold.sowa@gmail.com>
 + *
 + * This software may be distributed under the terms of the BSD license.
 + * See README for more details.
 + */
 +
 +#include "includes.h"
 +
 +#include "common.h"
 +#include "../config.h"
 +#include "../wpa_supplicant_i.h"
 +#include "../wps_supplicant.h"
 +#include "../driver_i.h"
 +#include "../ap.h"
 +#include "dbus_new_helpers.h"
 +#include "dbus_new.h"
 +#include "dbus_new_handlers.h"
 +#include "dbus_dict_helpers.h"
 +
 +
 +struct wps_start_params {
 +      int role; /* 0 - not set, 1 - enrollee, 2 - registrar */
 +      int type; /* 0 - not set, 1 - pin,      2 - pbc       */
 +      u8 *bssid;
 +      char *pin;
 +      u8 *p2p_dev_addr;
 +};
 +
 +
 +static int wpas_dbus_handler_wps_role(DBusMessage *message,
 +                                    DBusMessageIter *entry_iter,
 +                                    struct wps_start_params *params,
 +                                    DBusMessage **reply)
 +{
 +      DBusMessageIter variant_iter;
 +      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 Role type, string required");
 +              *reply = wpas_dbus_error_invalid_args(message,
 +                                                    "Role must be a string");
 +              return -1;
 +      }
 +      dbus_message_iter_get_basic(&variant_iter, &val);
 +      if (os_strcmp(val, "enrollee") == 0)
 +              params->role = 1;
 +      else if (os_strcmp(val, "registrar") == 0)
 +              params->role = 2;
 +      else {
-               wpa_printf(MSG_DEBUG, "dbus: WPS.Stsrt - Wrong Bssid length %d",
++              wpa_printf(MSG_DEBUG, "dbus: WPS.Start - Unknown role %s", val);
 +              *reply = wpas_dbus_error_invalid_args(message, val);
 +              return -1;
 +      }
 +      return 0;
 +}
 +
 +
 +static int wpas_dbus_handler_wps_type(DBusMessage *message,
 +                                    DBusMessageIter *entry_iter,
 +                                    struct wps_start_params *params,
 +                                    DBusMessage **reply)
 +{
 +      DBusMessageIter variant_iter;
 +      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");
 +              *reply = wpas_dbus_error_invalid_args(message,
 +                                                    "Type must be a string");
 +              return -1;
 +      }
 +      dbus_message_iter_get_basic(&variant_iter, &val);
 +      if (os_strcmp(val, "pin") == 0)
 +              params->type = 1;
 +      else if (os_strcmp(val, "pbc") == 0)
 +              params->type = 2;
 +      else {
 +              wpa_printf(MSG_DEBUG, "dbus: WPS.Start - Unknown type %s",
 +                         val);
 +              *reply = wpas_dbus_error_invalid_args(message, val);
 +              return -1;
 +      }
 +      return 0;
 +}
 +
 +
 +static int wpas_dbus_handler_wps_bssid(DBusMessage *message,
 +                                     DBusMessageIter *entry_iter,
 +                                     struct wps_start_params *params,
 +                                     DBusMessage **reply)
 +{
 +      DBusMessageIter variant_iter, array_iter;
 +      int len;
 +
 +      dbus_message_iter_recurse(entry_iter, &variant_iter);
 +      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");
 +              *reply = wpas_dbus_error_invalid_args(
 +                      message, "Bssid must be a byte array");
 +              return -1;
 +      }
 +      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.Start - Wrong Bssid length %d",
 +                         len);
 +              *reply = wpas_dbus_error_invalid_args(message,
 +                                                    "Bssid is wrong length");
 +              return -1;
 +      }
 +      return 0;
 +}
 +
 +
 +static int wpas_dbus_handler_wps_pin(DBusMessage *message,
 +                                   DBusMessageIter *entry_iter,
 +                                   struct wps_start_params *params,
 +                                   DBusMessage **reply)
 +{
 +      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");
 +              *reply = wpas_dbus_error_invalid_args(message,
 +                                                    "Pin must be a string");
 +              return -1;
 +      }
 +      dbus_message_iter_get_basic(&variant_iter, &params->pin);
 +      return 0;
 +}
 +
 +
 +#ifdef CONFIG_P2P
 +static int wpas_dbus_handler_wps_p2p_dev_addr(DBusMessage *message,
 +                                            DBusMessageIter *entry_iter,
 +                                            struct wps_start_params *params,
 +                                            DBusMessage **reply)
 +{
 +      DBusMessageIter variant_iter, array_iter;
 +      int len;
 +
 +      dbus_message_iter_recurse(entry_iter, &variant_iter);
 +      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");
 +              *reply = wpas_dbus_error_invalid_args(
 +                      message, "P2PDeviceAddress must be a byte array");
 +              return -1;
 +      }
 +      dbus_message_iter_recurse(&variant_iter, &array_iter);
 +      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");
 +              return -1;
 +      }
 +      return 0;
 +}
 +#endif /* CONFIG_P2P */
 +
 +
 +static int wpas_dbus_handler_wps_start_entry(DBusMessage *message, char *key,
 +                                           DBusMessageIter *entry_iter,
 +                                           struct wps_start_params *params,
 +                                           DBusMessage **reply)
 +{
 +      if (os_strcmp(key, "Role") == 0)
 +              return wpas_dbus_handler_wps_role(message, entry_iter,
 +                                                params, reply);
 +      else if (os_strcmp(key, "Type") == 0)
 +              return wpas_dbus_handler_wps_type(message, entry_iter,
 +                                                params, reply);
 +      else if (os_strcmp(key, "Bssid") == 0)
 +              return wpas_dbus_handler_wps_bssid(message, entry_iter,
 +                                                 params, reply);
 +      else if (os_strcmp(key, "Pin") == 0)
 +              return wpas_dbus_handler_wps_pin(message, entry_iter,
 +                                               params, reply);
 +#ifdef CONFIG_P2P
 +      else if (os_strcmp(key, "P2PDeviceAddress") == 0)
 +              return wpas_dbus_handler_wps_p2p_dev_addr(message, entry_iter,
 +                                                        params, reply);
 +#endif /* CONFIG_P2P */
 +
 +      wpa_printf(MSG_DEBUG, "dbus: WPS.Start - unknown key %s", key);
 +      *reply = wpas_dbus_error_invalid_args(message, key);
 +      return -1;
 +}
 +
 +
 +/**
 + * wpas_dbus_handler_wps_start - Start WPS configuration
 + * @message: Pointer to incoming dbus message
 + * @wpa_s: %wpa_supplicant data structure
 + * Returns: DBus message dictionary on success or DBus error on failure
 + *
 + * Handler for "Start" method call. DBus dictionary argument contains
 + * information about role (enrollee or registrar), authorization method
 + * (pin or push button) and optionally pin and bssid. Returned message
 + * has a dictionary argument which may contain newly generated pin (optional).
 + */
 +DBusMessage * wpas_dbus_handler_wps_start(DBusMessage *message,
 +                                        struct wpa_supplicant *wpa_s)
 +{
 +      DBusMessage *reply = NULL;
 +      DBusMessageIter iter, dict_iter, entry_iter;
 +      struct wps_start_params params;
 +      char *key;
 +      char npin[9] = { '\0' };
 +      int ret;
 +
 +      os_memset(&params, 0, sizeof(params));
 +      dbus_message_iter_init(message, &iter);
 +
 +      dbus_message_iter_recurse(&iter, &dict_iter);
 +      while (dbus_message_iter_get_arg_type(&dict_iter) ==
 +             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);
 +
 +              if (wpas_dbus_handler_wps_start_entry(message, key,
 +                                                    &entry_iter,
 +                                                    &params, &reply))
 +                      return reply;
 +
 +              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 == 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.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 {
 +              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",
 +                         (params.role == 1 ? "enrollee" : "registrar"),
 +                         (params.type == 0 ? "" :
 +                          (params.type == 1 ? "pin" : "pbc")));
 +              return wpas_dbus_error_unknown_error(message,
 +                                                   "WPS start failed");
 +      }
 +
 +      reply = dbus_message_new_method_return(message);
 +      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) ||
 +          (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 wpas_dbus_error_no_memory(message);
 +      }
 +
 +      return reply;
 +}
 +
 +
++/**
++ * wpas_dbus_handler_wps_cancel - Cancel ongoing WPS configuration
++ * @message: Pointer to incoming dbus message
++ * @wpa_s: %wpa_supplicant data structure
++ * Returns: NULL on success or DBus error on failure
++ *
++ * Handler for "Cancel" method call. Returns NULL if WPS cancel successfull
++ * or DBus error on WPS cancel failure
++ */
++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;
++}
++
++
 +/**
 + * wpas_dbus_getter_process_credentials - Check if credentials are processed
 + * @message: Pointer to incoming dbus message
 + * @wpa_s: %wpa_supplicant data structure
 + * Returns: TRUE on success, FALSE on failure
 + *
 + * Getter for "ProcessCredentials" property. Returns returned boolean will be
 + * true if wps_cred_processing configuration field is not equal to 1 or false
 + * if otherwise.
 + */
 +dbus_bool_t wpas_dbus_getter_process_credentials(DBusMessageIter *iter,
 +                                               DBusError *error,
 +                                               void *user_data)
 +{
 +      struct wpa_supplicant *wpa_s = user_data;
 +      dbus_bool_t process = wpa_s->conf->wps_cred_processing != 1;
 +
 +      return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_BOOLEAN,
 +                                              &process, error);
 +}
 +
 +
 +/**
 + * wpas_dbus_setter_process_credentials - Set credentials_processed conf param
 + * @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 "ProcessCredentials" property. Sets credentials_processed on 2
 + * if boolean argument is true or on 1 if otherwise.
 + */
 +dbus_bool_t wpas_dbus_setter_process_credentials(DBusMessageIter *iter,
 +                                               DBusError *error,
 +                                               void *user_data)
 +{
 +      struct wpa_supplicant *wpa_s = user_data;
 +      dbus_bool_t process_credentials, old_pc;
 +
++      if (!wpa_s->dbus_new_path)
++              return FALSE;
 +      if (!wpas_dbus_simple_property_setter(iter, error, DBUS_TYPE_BOOLEAN,
 +                                            &process_credentials))
 +              return FALSE;
 +
 +      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)
 +              wpa_dbus_mark_property_changed(wpa_s->global->dbus,
 +                                             wpa_s->dbus_new_path,
 +                                             WPAS_DBUS_NEW_IFACE_WPS,
 +                                             "ProcessCredentials");
 +
 +      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;
 +}
index 15b090141c978f2574fc1d944b478b081cb00b0b,0000000000000000000000000000000000000000..45623f34646557cc2acafb5fe82b468091366e6f
mode 100644,000000..100644
--- /dev/null
@@@ -1,1017 -1,0 +1,1022 @@@
-               if (!dsc->getter(&entry_iter, error, user_data))
 +/*
 + * WPA Supplicant / dbus-based control interface
 + * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc.
 + * Copyright (c) 2009, Witold Sowa <witold.sowa@gmail.com>
 + *
 + * 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 "dbus_common.h"
 +#include "dbus_common_i.h"
 +#include "dbus_new.h"
 +#include "dbus_new_helpers.h"
 +#include "dbus_new_handlers.h"
 +#include "dbus_dict_helpers.h"
 +
 +
 +static dbus_bool_t fill_dict_with_properties(
 +      DBusMessageIter *dict_iter,
 +      const struct wpa_dbus_property_desc *props,
 +      const char *interface, void *user_data, DBusError *error)
 +{
 +      DBusMessageIter entry_iter;
 +      const struct wpa_dbus_property_desc *dsc;
 +
 +      for (dsc = props; dsc && dsc->dbus_property; dsc++) {
 +              /* Only return properties for the requested D-Bus interface */
 +              if (os_strncmp(dsc->dbus_interface, interface,
 +                             WPAS_DBUS_INTERFACE_MAX) != 0)
 +                      continue;
 +
 +              /* Skip write-only properties */
 +              if (dsc->getter == NULL)
 +                      continue;
 +
 +              if (!dbus_message_iter_open_container(dict_iter,
 +                                                    DBUS_TYPE_DICT_ENTRY,
 +                                                    NULL, &entry_iter) ||
 +                  !dbus_message_iter_append_basic(&entry_iter,
 +                                                  DBUS_TYPE_STRING,
 +                                                  &dsc->dbus_property))
 +                      goto error;
 +
 +              /* An error getting a property fails the request entirely */
++              if (!dsc->getter(&entry_iter, error, user_data)) {
++                      wpa_printf(MSG_INFO,
++                                 "dbus: %s dbus_interface=%s dbus_property=%s getter failed",
++                                 __func__, dsc->dbus_interface,
++                                 dsc->dbus_property);
 +                      return FALSE;
++              }
 +
 +              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;
 +}
 +
 +
 +/**
 + * get_all_properties - Responds for GetAll properties calls on object
 + * @message: Message with GetAll call
 + * @interface: interface name which properties will be returned
 + * @property_dsc: list of object's properties
 + * Returns: Message with dict of variants as argument with properties values
 + *
 + * Iterates over all properties registered with object and execute getters
 + * of those, which are readable and which interface matches interface
 + * specified as argument. Returned message contains one dict argument
 + * 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)
 +{
 +      DBusMessage *reply;
 +      DBusMessageIter iter, dict_iter;
 +      DBusError error;
 +
 +      reply = dbus_message_new_method_return(message);
 +      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)) {
 +              dbus_message_unref(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)) {
 +              dbus_message_unref(reply);
 +              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;
 +      }
 +
 +      if (!wpa_dbus_dict_close_write(&iter, &dict_iter)) {
 +              dbus_message_unref(reply);
 +              return wpas_dbus_error_no_memory(message);
 +      }
 +
 +      return reply;
 +}
 +
 +
 +static int is_signature_correct(DBusMessage *message,
 +                              const struct wpa_dbus_method_desc *method_dsc)
 +{
 +      /* According to DBus documentation max length of signature is 255 */
 +#define MAX_SIG_LEN 256
 +      char registered_sig[MAX_SIG_LEN], *pos;
 +      const char *sig = dbus_message_get_signature(message);
 +      int ret;
 +      const struct wpa_dbus_argument *arg;
 +
 +      pos = registered_sig;
 +      *pos = '\0';
 +
 +      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 (os_snprintf_error(blen, ret))
 +                              return 0;
 +                      pos += ret;
 +              }
 +      }
 +
 +      return !os_strncmp(registered_sig, sig, MAX_SIG_LEN);
 +}
 +
 +
 +static DBusMessage * properties_get_all(DBusMessage *message, char *interface,
 +                                      struct wpa_dbus_object_desc *obj_dsc)
 +{
 +      if (os_strcmp(dbus_message_get_signature(message), "s") != 0)
 +              return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
 +                                            NULL);
 +
 +      return get_all_properties(message, interface, obj_dsc);
 +}
 +
 +
 +static DBusMessage * properties_get(DBusMessage *message,
 +                                  const struct wpa_dbus_property_desc *dsc,
 +                                  void *user_data)
 +{
 +      DBusMessage *reply;
 +      DBusMessageIter iter;
 +      DBusError error;
 +
 +      if (os_strcmp(dbus_message_get_signature(message), "ss")) {
 +              return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
 +                                            NULL);
 +      }
 +
 +      if (dsc->getter == NULL) {
 +              return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
 +                                            "Property is write-only");
 +      }
 +
 +      reply = dbus_message_new_method_return(message);
 +      dbus_message_iter_init_append(reply, &iter);
 +
 +      dbus_error_init(&error);
 +      if (dsc->getter(&iter, &error, user_data) == FALSE) {
 +              dbus_message_unref(reply);
 +              reply = wpas_dbus_reply_new_from_error(
 +                      message, &error, DBUS_ERROR_FAILED,
 +                      "Failed to read property");
 +              dbus_error_free(&error);
 +      }
 +
 +      return reply;
 +}
 +
 +
 +static DBusMessage * properties_set(DBusMessage *message,
 +                                  const struct wpa_dbus_property_desc *dsc,
 +                                  void *user_data)
 +{
 +      DBusMessage *reply;
 +      DBusMessageIter iter;
 +      DBusError error;
 +
 +      if (os_strcmp(dbus_message_get_signature(message), "ssv")) {
 +              return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
 +                                            NULL);
 +      }
 +
 +      if (dsc->setter == NULL) {
 +              return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
 +                                            "Property is read-only");
 +      }
 +
 +      dbus_message_iter_init(message, &iter);
 +      /* Skip the interface name and the property name */
 +      dbus_message_iter_next(&iter);
 +      dbus_message_iter_next(&iter);
 +
 +      /* Iter will now point to the property's new value */
 +      dbus_error_init(&error);
 +      if (dsc->setter(&iter, &error, user_data) == TRUE) {
 +              /* Success */
 +              reply = dbus_message_new_method_return(message);
 +      } else {
 +              reply = wpas_dbus_reply_new_from_error(
 +                      message, &error, DBUS_ERROR_FAILED,
 +                      "Failed to set property");
 +              dbus_error_free(&error);
 +      }
 +
 +      return reply;
 +}
 +
 +
 +static DBusMessage *
 +properties_get_or_set(DBusMessage *message, DBusMessageIter *iter,
 +                    char *interface,
 +                    struct wpa_dbus_object_desc *obj_dsc)
 +{
 +      const struct wpa_dbus_property_desc *property_dsc;
 +      char *property;
 +      const char *method;
 +
 +      method = dbus_message_get_member(message);
 +      property_dsc = obj_dsc->properties;
 +
 +      /* Second argument: property name (DBUS_TYPE_STRING) */
 +      if (!dbus_message_iter_next(iter) ||
 +          dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING) {
 +              return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
 +                                            NULL);
 +      }
 +      dbus_message_iter_get_basic(iter, &property);
 +
 +      while (property_dsc && property_dsc->dbus_property) {
 +              /* compare property names and
 +               * interfaces */
 +              if (!os_strncmp(property_dsc->dbus_property, property,
 +                              WPAS_DBUS_METHOD_SIGNAL_PROP_MAX) &&
 +                  !os_strncmp(property_dsc->dbus_interface, interface,
 +                              WPAS_DBUS_INTERFACE_MAX))
 +                      break;
 +
 +              property_dsc++;
 +      }
 +      if (property_dsc == NULL || property_dsc->dbus_property == NULL) {
 +              wpa_printf(MSG_DEBUG, "no property handler for %s.%s on %s",
 +                         interface, property,
 +                         dbus_message_get_path(message));
 +              return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
 +                                            "No such property");
 +      }
 +
 +      if (os_strncmp(WPA_DBUS_PROPERTIES_GET, method,
 +                     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);
 +}
 +
 +
 +static DBusMessage * properties_handler(DBusMessage *message,
 +                                      struct wpa_dbus_object_desc *obj_dsc)
 +{
 +      DBusMessageIter iter;
 +      char *interface;
 +      const char *method;
 +
 +      method = dbus_message_get_member(message);
 +      dbus_message_iter_init(message, &iter);
 +
 +      if (!os_strncmp(WPA_DBUS_PROPERTIES_GET, method,
 +                      WPAS_DBUS_METHOD_SIGNAL_PROP_MAX) ||
 +          !os_strncmp(WPA_DBUS_PROPERTIES_SET, method,
 +                      WPAS_DBUS_METHOD_SIGNAL_PROP_MAX) ||
 +          !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) {
 +                      return dbus_message_new_error(message,
 +                                                    DBUS_ERROR_INVALID_ARGS,
 +                                                    NULL);
 +              }
 +
 +              dbus_message_iter_get_basic(&iter, &interface);
 +
 +              if (!os_strncmp(WPA_DBUS_PROPERTIES_GETALL, method,
 +                              WPAS_DBUS_METHOD_SIGNAL_PROP_MAX)) {
 +                      /* GetAll */
 +                      return properties_get_all(message, interface, obj_dsc);
 +              }
 +              /* Get or Set */
 +              return properties_get_or_set(message, &iter, interface,
 +                                           obj_dsc);
 +      }
 +      return dbus_message_new_error(message, DBUS_ERROR_UNKNOWN_METHOD,
 +                                    NULL);
 +}
 +
 +
 +static DBusMessage * msg_method_handler(DBusMessage *message,
 +                                      struct wpa_dbus_object_desc *obj_dsc)
 +{
 +      const struct wpa_dbus_method_desc *method_dsc = obj_dsc->methods;
 +      const char *method;
 +      const char *msg_interface;
 +
 +      method = dbus_message_get_member(message);
 +      msg_interface = dbus_message_get_interface(message);
 +
 +      /* try match call to any registered method */
 +      while (method_dsc && method_dsc->dbus_method) {
 +              /* compare method names and interfaces */
 +              if (!os_strncmp(method_dsc->dbus_method, method,
 +                              WPAS_DBUS_METHOD_SIGNAL_PROP_MAX) &&
 +                  !os_strncmp(method_dsc->dbus_interface, msg_interface,
 +                              WPAS_DBUS_INTERFACE_MAX))
 +                      break;
 +
 +              method_dsc++;
 +      }
 +      if (method_dsc == NULL || method_dsc->dbus_method == NULL) {
 +              wpa_printf(MSG_DEBUG, "no method handler for %s.%s on %s",
 +                         msg_interface, method,
 +                         dbus_message_get_path(message));
 +              return dbus_message_new_error(message,
 +                                            DBUS_ERROR_UNKNOWN_METHOD, NULL);
 +      }
 +
 +      if (!is_signature_correct(message, method_dsc)) {
 +              return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
 +                                            NULL);
 +      }
 +
 +      return method_dsc->method_handler(message, obj_dsc->user_data);
 +}
 +
 +
 +/**
 + * message_handler - Handles incoming DBus messages
 + * @connection: DBus connection on which message was received
 + * @message: Received message
 + * @user_data: pointer to description of object to which message was sent
 + * Returns: Returns information whether message was handled or not
 + *
 + * Reads message interface and method name, then checks if they matches one
 + * of the special cases i.e. introspection call or properties get/getall/set
 + * methods and handles it. Else it iterates over registered methods list
 + * and tries to match method's name and interface to those read from message
 + * If appropriate method was found its handler function is called and
 + * response is sent. Otherwise, the DBUS_ERROR_UNKNOWN_METHOD error message
 + * will be sent.
 + */
 +static DBusHandlerResult message_handler(DBusConnection *connection,
 +                                       DBusMessage *message, void *user_data)
 +{
 +      struct wpa_dbus_object_desc *obj_dsc = user_data;
 +      const char *method;
 +      const char *path;
 +      const char *msg_interface;
 +      DBusMessage *reply;
 +
 +      /* get method, interface and path the message is addressed to */
 +      method = dbus_message_get_member(message);
 +      path = dbus_message_get_path(message);
 +      msg_interface = dbus_message_get_interface(message);
 +      if (!method || !path || !msg_interface)
 +              return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
 +
 +      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,
 +                      WPAS_DBUS_METHOD_SIGNAL_PROP_MAX) &&
 +          !os_strncmp(WPA_DBUS_INTROSPECTION_INTERFACE, msg_interface,
 +                      WPAS_DBUS_INTERFACE_MAX)) {
 +#ifdef CONFIG_CTRL_IFACE_DBUS_INTRO
 +              reply = wpa_dbus_introspect(message, obj_dsc);
 +#else /* CONFIG_CTRL_IFACE_DBUS_INTRO */
 +              reply = dbus_message_new_error(
 +                      message, DBUS_ERROR_UNKNOWN_METHOD,
 +                      "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)) {
 +              /* if message is properties method call */
 +              reply = properties_handler(message, obj_dsc);
 +      } else {
 +              reply = msg_method_handler(message, obj_dsc);
 +      }
 +
 +      /* If handler succeed returning NULL, reply empty message */
 +      if (!reply)
 +              reply = dbus_message_new_method_return(message);
 +      if (reply) {
 +              if (!dbus_message_get_no_reply(message))
 +                      dbus_connection_send(connection, reply, NULL);
 +              dbus_message_unref(reply);
 +      }
 +
 +      wpa_dbus_flush_all_changed_properties(connection);
 +
 +      return DBUS_HANDLER_RESULT_HANDLED;
 +}
 +
 +
 +/**
 + * free_dbus_object_desc - Frees object description data structure
 + * @connection: DBus connection
 + * @obj_dsc: Object description to free
 + *
 + * Frees each of properties, methods and signals description lists and
 + * the object description structure itself.
 + */
 +void free_dbus_object_desc(struct wpa_dbus_object_desc *obj_dsc)
 +{
 +      if (!obj_dsc)
 +              return;
 +
 +      /* free handler's argument */
 +      if (obj_dsc->user_data_free_func)
 +              obj_dsc->user_data_free_func(obj_dsc->user_data);
 +
 +      os_free(obj_dsc->path);
 +      os_free(obj_dsc->prop_changed_flags);
 +      os_free(obj_dsc);
 +}
 +
 +
 +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
 + * @dbus_path: DBus path to interface object
 + * @dbus_service: DBus service name to register with
 + * @messageHandler: a pointer to function which will handle dbus messages
 + * coming on interface
 + * Returns: 0 on success, -1 on failure
 + *
 + * Initialize the dbus control interface and start receiving commands from
 + * external programs over the bus.
 + */
 +int wpa_dbus_ctrl_iface_init(struct wpas_dbus_priv *iface,
 +                           char *dbus_path, char *dbus_service,
 +                           struct wpa_dbus_object_desc *obj_desc)
 +{
 +      DBusError error;
 +      int ret = -1;
 +      DBusObjectPathVTable wpa_vtable = {
 +              &free_dbus_object_desc_cb, &message_handler,
 +              NULL, NULL, NULL, NULL
 +      };
 +
 +      obj_desc->connection = iface->con;
 +      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");
 +              return -1;
 +      }
 +
 +      /* Register our service with the message bus */
 +      dbus_error_init(&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");
 +              break;
 +      default:
 +              wpa_printf(MSG_ERROR,
 +                         "dbus: Could not request service name: %s %s",
 +                         error.name, error.message);
 +              break;
 +      }
 +      dbus_error_free(&error);
 +
 +      if (ret != 0)
 +              return -1;
 +
 +      wpa_printf(MSG_DEBUG, "Providing DBus service '%s'.", dbus_service);
 +
 +      return 0;
 +}
 +
 +
 +/**
 + * wpa_dbus_register_object_per_iface - Register a new object with dbus
 + * @ctrl_iface: pointer to dbus private data
 + * @path: DBus path to object
 + * @ifname: interface name
 + * @obj_desc: description of object's methods, signals and properties
 + * Returns: 0 on success, -1 on error
 + *
 + * 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)
 +{
 +      DBusConnection *con;
 +      DBusError error;
 +      DBusObjectPathVTable vtable = {
 +              &free_dbus_object_desc_cb, &message_handler,
 +              NULL, NULL, NULL, NULL
 +      };
 +
 +      /* Do nothing if the control interface is not turned on */
 +      if (ctrl_iface == NULL)
 +              return 0;
 +
 +      con = ctrl_iface->con;
 +      obj_desc->connection = con;
 +      obj_desc->path = os_strdup(path);
 +
 +      dbus_error_init(&error);
 +      /* 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) == 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 (error: %s message: %s)",
 +                                 ifname, path, error.name, error.message);
 +              }
 +              dbus_error_free(&error);
 +              return -1;
 +      }
 +
 +      dbus_error_free(&error);
 +      return 0;
 +}
 +
 +
 +static void flush_object_timeout_handler(void *eloop_ctx, void *timeout_ctx);
 +
 +
 +/**
 + * wpa_dbus_unregister_object_per_iface - Unregisters DBus object
 + * @ctrl_iface: Pointer to dbus private data
 + * @path: DBus path to object which will be unregistered
 + * Returns: Zero on success and -1 on failure
 + *
 + * Unregisters DBus object given by its path
 + */
 +int wpa_dbus_unregister_object_per_iface(
 +      struct wpas_dbus_priv *ctrl_iface, const char *path)
 +{
 +      DBusConnection *con = ctrl_iface->con;
 +      struct wpa_dbus_object_desc *obj_desc = NULL;
 +
 +      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);
 +              return 0;
 +      }
 +
 +      eloop_cancel_timeout(flush_object_timeout_handler, con, obj_desc);
 +
 +      if (!dbus_connection_unregister_object_path(con, path))
 +              return -1;
 +
 +      return 0;
 +}
 +
 +
 +static dbus_bool_t put_changed_properties(
 +      const struct wpa_dbus_object_desc *obj_dsc, const char *interface,
 +      DBusMessageIter *dict_iter, int clear_changed)
 +{
 +      DBusMessageIter entry_iter;
 +      const struct wpa_dbus_property_desc *dsc;
 +      int i;
 +      DBusError error;
 +
 +      for (dsc = obj_dsc->properties, i = 0; dsc && dsc->dbus_property;
 +           dsc++, i++) {
 +              if (obj_dsc->prop_changed_flags == NULL ||
 +                  !obj_dsc->prop_changed_flags[i])
 +                      continue;
 +              if (os_strcmp(dsc->dbus_interface, interface) != 0)
 +                      continue;
 +              if (clear_changed)
 +                      obj_dsc->prop_changed_flags[i] = 0;
 +
 +              if (!dbus_message_iter_open_container(dict_iter,
 +                                                    DBUS_TYPE_DICT_ENTRY,
 +                                                    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);
 +                      } else {
 +                              wpa_printf(MSG_ERROR,
 +                                         "dbus: %s: Cannot get new value of property %s",
 +                                         __func__, dsc->dbus_property);
 +                      }
 +                      dbus_error_free(&error);
 +                      return FALSE;
 +              }
 +
 +              if (!dbus_message_iter_close_container(dict_iter, &entry_iter))
 +                      return FALSE;
 +      }
 +
 +      return TRUE;
 +}
 +
 +
 +static void do_send_prop_changed_signal(
 +      DBusConnection *con, const char *path, const char *interface,
 +      const struct wpa_dbus_object_desc *obj_dsc)
 +{
 +      DBusMessage *msg;
 +      DBusMessageIter signal_iter, dict_iter;
 +
 +      msg = dbus_message_new_signal(path, DBUS_INTERFACE_PROPERTIES,
 +                                    "PropertiesChanged");
 +      if (msg == NULL)
 +              return;
 +
 +      dbus_message_iter_init_append(msg, &signal_iter);
 +
 +      if (!dbus_message_iter_append_basic(&signal_iter, DBUS_TYPE_STRING,
 +                                          &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);
 +      }
 +
 +      dbus_message_unref(msg);
 +}
 +
 +
 +static void do_send_deprecated_prop_changed_signal(
 +      DBusConnection *con, const char *path, const char *interface,
 +      const struct wpa_dbus_object_desc *obj_dsc)
 +{
 +      DBusMessage *msg;
 +      DBusMessageIter signal_iter, dict_iter;
 +
 +      msg = dbus_message_new_signal(path, interface, "PropertiesChanged");
 +      if (msg == NULL)
 +              return;
 +
 +      dbus_message_iter_init_append(msg, &signal_iter);
 +
 +      if (!dbus_message_iter_open_container(&signal_iter, DBUS_TYPE_ARRAY,
 +                                            "{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);
 +      }
 +
 +      dbus_message_unref(msg);
 +}
 +
 +
 +static void send_prop_changed_signal(
 +      DBusConnection *con, const char *path, const char *interface,
 +      const struct wpa_dbus_object_desc *obj_dsc)
 +{
 +      /*
 +       * First, send property change notification on the standardized
 +       * org.freedesktop.DBus.Properties interface. This call will not
 +       * clear the property change bits, so that they are preserved for
 +       * the call that follows.
 +       */
 +      do_send_prop_changed_signal(con, path, interface, obj_dsc);
 +
 +      /*
 +       * Now send PropertiesChanged on our own interface for backwards
 +       * compatibility. This is deprecated and will be removed in a future
 +       * release.
 +       */
 +      do_send_deprecated_prop_changed_signal(con, path, interface, obj_dsc);
 +
 +      /* Property change bits have now been cleared. */
 +}
 +
 +
 +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_dbus_flush_object_changed_properties(con, obj_desc->path);
 +}
 +
 +
 +static void recursive_flush_changed_properties(DBusConnection *con,
 +                                             const char *path)
 +{
 +      char **objects = NULL;
 +      char subobj_path[WPAS_DBUS_OBJECT_PATH_MAX];
 +      int i;
 +
 +      wpa_dbus_flush_object_changed_properties(con, path);
 +
 +      if (!dbus_connection_list_registered(con, path, &objects))
 +              goto out;
 +
 +      for (i = 0; objects[i]; i++) {
 +              os_snprintf(subobj_path, WPAS_DBUS_OBJECT_PATH_MAX,
 +                          "%s/%s", path, objects[i]);
 +              recursive_flush_changed_properties(con, subobj_path);
 +      }
 +
 +out:
 +      dbus_free_string_array(objects);
 +}
 +
 +
 +/**
 + * wpa_dbus_flush_all_changed_properties - Send all PropertiesChanged signals
 + * @con: DBus connection
 + *
 + * Traverses through all registered objects and sends PropertiesChanged for
 + * each properties.
 + */
 +void wpa_dbus_flush_all_changed_properties(DBusConnection *con)
 +{
 +      recursive_flush_changed_properties(con, WPAS_DBUS_NEW_PATH);
 +}
 +
 +
 +/**
 + * wpa_dbus_flush_object_changed_properties - Send PropertiesChanged for object
 + * @con: DBus connection
 + * @path: path to a DBus object for which PropertiesChanged will be sent.
 + *
 + * Iterates over all properties registered with object and for each interface
 + * containing properties marked as changed, sends a PropertiesChanged signal
 + * containing names and new values of properties that have changed.
 + *
 + * You need to call this function after wpa_dbus_mark_property_changed()
 + * if you want to send PropertiesChanged signal immediately (i.e., without
 + * waiting timeout to expire). PropertiesChanged signal for an object is sent
 + * automatically short time after first marking property as changed. All
 + * PropertiesChanged signals are sent automatically after responding on DBus
 + * message, so if you marked a property changed as a result of DBus call
 + * (e.g., param setter), you usually do not need to call this function.
 + */
 +void wpa_dbus_flush_object_changed_properties(DBusConnection *con,
 +                                            const char *path)
 +{
 +      struct wpa_dbus_object_desc *obj_desc = NULL;
 +      const struct wpa_dbus_property_desc *dsc;
 +      int i;
 +
 +      dbus_connection_get_object_path_data(con, path, (void **) &obj_desc);
 +      if (!obj_desc)
 +              return;
 +      eloop_cancel_timeout(flush_object_timeout_handler, con, obj_desc);
 +
 +      for (dsc = obj_desc->properties, i = 0; dsc && dsc->dbus_property;
 +           dsc++, i++) {
 +              if (obj_desc->prop_changed_flags == NULL ||
 +                  !obj_desc->prop_changed_flags[i])
 +                      continue;
 +              send_prop_changed_signal(con, path, dsc->dbus_interface,
 +                                       obj_desc);
 +      }
 +}
 +
 +
 +#define WPA_DBUS_SEND_PROP_CHANGED_TIMEOUT 5000
 +
 +
 +/**
 + * wpa_dbus_mark_property_changed - Mark a property as changed and
 + * @iface: dbus priv struct
 + * @path: path to DBus object which property has changed
 + * @interface: interface containing changed property
 + * @property: property name which has changed
 + *
 + * Iterates over all properties registered with an object and marks the one
 + * given in parameters as changed. All parameters registered for an object
 + * within a single interface will be aggregated together and sent in one
 + * PropertiesChanged signal when function
 + * wpa_dbus_flush_object_changed_properties() is called.
 + */
 +void wpa_dbus_mark_property_changed(struct wpas_dbus_priv *iface,
 +                                  const char *path, const char *interface,
 +                                  const char *property)
 +{
 +      struct wpa_dbus_object_desc *obj_desc = NULL;
 +      const struct wpa_dbus_property_desc *dsc;
 +      int i = 0;
 +
 +      if (iface == NULL)
 +              return;
 +
 +      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);
 +              return;
 +      }
 +
 +      for (dsc = obj_desc->properties; dsc && dsc->dbus_property; dsc++, i++)
 +              if (os_strcmp(property, dsc->dbus_property) == 0 &&
 +                  os_strcmp(interface, dsc->dbus_interface) == 0) {
 +                      if (obj_desc->prop_changed_flags)
 +                              obj_desc->prop_changed_flags[i] = 1;
 +                      break;
 +              }
 +
 +      if (!dsc || !dsc->dbus_property) {
 +              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)) {
 +              eloop_register_timeout(0, WPA_DBUS_SEND_PROP_CHANGED_TIMEOUT,
 +                                     flush_object_timeout_handler,
 +                                     iface->con, obj_desc);
 +      }
 +}
 +
 +
 +/**
 + * wpa_dbus_get_object_properties - Put object's properties into dictionary
 + * @iface: dbus priv struct
 + * @path: path to DBus object which properties will be obtained
 + * @interface: interface name which properties will be obtained
 + * @iter: DBus message iter at which to append property dictionary.
 + *
 + * Iterates over all properties registered with object and execute getters
 + * of those, which are readable and which interface matches interface
 + * specified as argument. Obtained properties values are stored in
 + * dict_iter dictionary.
 + */
 +dbus_bool_t wpa_dbus_get_object_properties(struct wpas_dbus_priv *iface,
 +                                         const char *path,
 +                                         const char *interface,
 +                                         DBusMessageIter *iter)
 +{
 +      struct wpa_dbus_object_desc *obj_desc = NULL;
 +      DBusMessageIter dict_iter;
 +      DBusError error;
 +
 +      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);
 +              return FALSE;
 +      }
 +
 +      if (!wpa_dbus_dict_open_write(iter, &dict_iter)) {
 +              wpa_printf(MSG_ERROR, "dbus: %s: failed to open message dict",
 +                         __func__);
 +              return FALSE;
 +      }
 +
 +      dbus_error_init(&error);
 +      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");
 +              dbus_error_free(&error);
 +              return FALSE;
 +      }
 +
 +      return wpa_dbus_dict_close_write(iter, &dict_iter);
 +}
 +
 +/**
 + * wpas_dbus_new_decompose_object_path - Decompose an interface object path into parts
 + * @path: The dbus object path
 + * @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 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, const char *sep,
 +                                         char **item)
 +{
 +      const unsigned int dev_path_prefix_len =
 +              os_strlen(WPAS_DBUS_NEW_PATH_INTERFACES "/");
 +      char *obj_path_only;
 +      char *pos;
 +      size_t sep_len;
 +
 +      *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')
 +              return NULL;
 +
 +      obj_path_only = os_strdup(path);
 +      if (obj_path_only == NULL)
 +              return 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 */
 +
 +       /* 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;
 +}
 +
 +
 +/**
 + * wpas_dbus_reply_new_from_error - Create a new D-Bus error message from a
 + *   dbus error structure
 + * @message: The original request message for which the error is a reply
 + * @error: The error containing a name and a descriptive error cause
 + * @fallback_name: A generic error name if @error was not set
 + * @fallback_string: A generic error string if @error was not set
 + * Returns: A new D-Bus error message
 + *
 + * Given a DBusMessage structure, creates a new D-Bus error message using
 + * the error name and string contained in that structure.
 + */
 +DBusMessage * wpas_dbus_reply_new_from_error(DBusMessage *message,
 +                                           DBusError *error,
 +                                           const char *fallback_name,
 +                                           const char *fallback_string)
 +{
 +      if (error && error->name && error->message) {
 +              return dbus_message_new_error(message, error->name,
 +                                            error->message);
 +      }
 +      if (fallback_name && fallback_string) {
 +              return dbus_message_new_error(message, fallback_name,
 +                                            fallback_string);
 +      }
 +      return NULL;
 +}
index 6209c67856bbead8b4a86eb24fb1fd95a7360169,0000000000000000000000000000000000000000..fba57e6361ae5c24656a3c54943349467ed49ec4
mode 100644,000000..100644
--- /dev/null
@@@ -1,286 -1,0 +1,286 @@@
-       xml = wpabuf_alloc(10000);
 +/*
 + * wpa_supplicant - D-Bus introspection
 + * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc.
 + * Copyright (c) 2009, Witold Sowa <witold.sowa@gmail.com>
 + * Copyright (c) 2010, Jouni Malinen <j@w1.fi>
 + *
 + * This software may be distributed under the terms of the BSD license.
 + * See README for more details.
 + */
 +
 +#include "utils/includes.h"
 +
 +#include "utils/common.h"
 +#include "utils/list.h"
 +#include "utils/wpabuf.h"
 +#include "dbus_common_i.h"
 +#include "dbus_new_helpers.h"
 +
 +
 +struct interfaces {
 +      struct dl_list list;
 +      char *dbus_interface;
 +      struct wpabuf *xml;
 +};
 +
 +
 +static struct interfaces * add_interface(struct dl_list *list,
 +                                       const char *dbus_interface)
 +{
 +      struct interfaces *iface;
 +
 +      dl_list_for_each(iface, list, struct interfaces, list) {
 +              if (os_strcmp(iface->dbus_interface, dbus_interface) == 0)
 +                      return iface; /* already in the 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->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);
 +      return iface;
 +}
 +
 +
 +static void add_arg(struct wpabuf *xml, const char *name, const char *type,
 +                  const char *direction)
 +{
 +      wpabuf_printf(xml, "<arg name=\"%s\"", name);
 +      if (type)
 +              wpabuf_printf(xml, " type=\"%s\"", type);
 +      if (direction)
 +              wpabuf_printf(xml, " direction=\"%s\"", direction);
 +      wpabuf_put_str(xml, "/>");
 +}
 +
 +
 +static void add_entry(struct wpabuf *xml, const char *type, const char *name,
 +                    const struct wpa_dbus_argument *args, int include_dir)
 +{
 +      const struct wpa_dbus_argument *arg;
 +
 +      if (args == NULL || args->name == NULL) {
 +              wpabuf_printf(xml, "<%s name=\"%s\"/>", type, name);
 +              return;
 +      }
 +      wpabuf_printf(xml, "<%s name=\"%s\">", type, name);
 +      for (arg = args; arg && arg->name; arg++) {
 +              add_arg(xml, arg->name, arg->type,
 +                      include_dir ? (arg->dir == ARG_IN ? "in" : "out") :
 +                      NULL);
 +      }
 +      wpabuf_printf(xml, "</%s>", type);
 +}
 +
 +
 +static void add_property(struct wpabuf *xml,
 +                       const struct wpa_dbus_property_desc *dsc)
 +{
 +      wpabuf_printf(xml, "<property name=\"%s\" type=\"%s\" "
 +                    "access=\"%s%s\"/>",
 +                    dsc->dbus_property, dsc->type,
 +                    dsc->getter ? "read" : "",
 +                    dsc->setter ? "write" : "");
 +}
 +
 +
 +static void extract_interfaces_methods(
 +      struct dl_list *list, const struct wpa_dbus_method_desc *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)
 +                      add_entry(iface->xml, "method", dsc->dbus_method,
 +                                dsc->args, 1);
 +      }
 +}
 +
 +
 +static void extract_interfaces_signals(
 +      struct dl_list *list, const struct wpa_dbus_signal_desc *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)
 +                      add_entry(iface->xml, "signal", dsc->dbus_signal,
 +                                dsc->args, 0);
 +      }
 +}
 +
 +
 +static void extract_interfaces_properties(
 +      struct dl_list *list, const struct wpa_dbus_property_desc *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)
 +                      add_property(iface->xml, dsc);
 +      }
 +}
 +
 +
 +/**
 + * extract_interfaces - Extract interfaces from methods, signals and props
 + * @list: Interface list to be filled
 + * @obj_dsc: Description of object from which interfaces will be extracted
 + *
 + * Iterates over all methods, signals, and properties registered with an
 + * object and collects all declared DBus interfaces and create interfaces'
 + * node in XML root node for each. Returned list elements contain interface
 + * name and XML node of corresponding interface.
 + */
 +static void extract_interfaces(struct dl_list *list,
 +                             struct wpa_dbus_object_desc *obj_dsc)
 +{
 +      extract_interfaces_methods(list, obj_dsc->methods);
 +      extract_interfaces_signals(list, obj_dsc->signals);
 +      extract_interfaces_properties(list, obj_dsc->properties);
 +}
 +
 +
 +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",
 +                                 (unsigned int) wpabuf_tailroom(xml),
 +                                 (unsigned int) wpabuf_len(iface->xml));
 +              }
 +              dl_list_del(&iface->list);
 +              wpabuf_free(iface->xml);
 +              os_free(iface->dbus_interface);
 +              os_free(iface);
 +      }
 +}
 +
 +
 +static void add_child_nodes(struct wpabuf *xml, DBusConnection *con,
 +                          const char *path)
 +{
 +      char **children;
 +      int i;
 +
 +      /* add child nodes to introspection tree */
 +      dbus_connection_list_registered(con, path, &children);
 +      for (i = 0; children[i]; i++)
 +              wpabuf_printf(xml, "<node name=\"%s\"/>", children[i]);
 +      dbus_free_string_array(children);
 +}
 +
 +
 +static void add_introspectable_interface(struct wpabuf *xml)
 +{
 +      wpabuf_printf(xml, "<interface name=\"%s\">"
 +                    "<method name=\"%s\">"
 +                    "<arg name=\"data\" type=\"s\" direction=\"out\"/>"
 +                    "</method>"
 +                    "</interface>",
 +                    WPA_DBUS_INTROSPECTION_INTERFACE,
 +                    WPA_DBUS_INTROSPECTION_METHOD);
 +}
 +
 +
 +static void add_properties_interface(struct wpabuf *xml)
 +{
 +      wpabuf_printf(xml, "<interface name=\"%s\">",
 +                    WPA_DBUS_PROPERTIES_INTERFACE);
 +
 +      wpabuf_printf(xml, "<method name=\"%s\">", WPA_DBUS_PROPERTIES_GET);
 +      add_arg(xml, "interface", "s", "in");
 +      add_arg(xml, "propname", "s", "in");
 +      add_arg(xml, "value", "v", "out");
 +      wpabuf_put_str(xml, "</method>");
 +
 +      wpabuf_printf(xml, "<method name=\"%s\">", WPA_DBUS_PROPERTIES_GETALL);
 +      add_arg(xml, "interface", "s", "in");
 +      add_arg(xml, "props", "a{sv}", "out");
 +      wpabuf_put_str(xml, "</method>");
 +
 +      wpabuf_printf(xml, "<method name=\"%s\">", WPA_DBUS_PROPERTIES_SET);
 +      add_arg(xml, "interface", "s", "in");
 +      add_arg(xml, "propname", "s", "in");
 +      add_arg(xml, "value", "v", "in");
 +      wpabuf_put_str(xml, "</method>");
 +
 +      wpabuf_put_str(xml, "</interface>");
 +}
 +
 +
 +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);
 +}
 +
 +
 +/**
 + * wpa_dbus_introspect - Responds for Introspect calls on object
 + * @message: Message with Introspect call
 + * @obj_dsc: Object description on which Introspect was called
 + * Returns: Message with introspection result XML string as only argument
 + *
 + * Iterates over all methods, signals and properties registered with
 + * object and generates introspection data for the object as XML string.
 + */
 +DBusMessage * wpa_dbus_introspect(DBusMessage *message,
 +                                struct wpa_dbus_object_desc *obj_dsc)
 +{
 +
 +      DBusMessage *reply;
 +      struct wpabuf *xml;
 +
++      xml = wpabuf_alloc(15000);
 +      if (xml == NULL)
 +              return NULL;
 +
 +      wpabuf_put_str(xml, "<?xml version=\"1.0\"?>\n");
 +      wpabuf_put_str(xml, DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE);
 +      wpabuf_put_str(xml, "<node>");
 +
 +      add_introspectable_interface(xml);
 +      add_properties_interface(xml);
 +      add_wpas_interfaces(xml, obj_dsc);
 +      add_child_nodes(xml, obj_dsc->connection,
 +                      dbus_message_get_path(message));
 +
 +      wpabuf_put_str(xml, "</node>\n");
 +
 +      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);
 +      }
 +      wpabuf_free(xml);
 +
 +      return reply;
 +}
index 45bb4022702fe2f16a4b885e95d6feee822243c9,0000000000000000000000000000000000000000..88227af7c03b8a1240e2cf946de0ecbbf3dc63e6
mode 100644,000000..100644
--- /dev/null
@@@ -1,745 -1,0 +1,745 @@@
-       if (iface == NULL)
 +/*
 + * WPA Supplicant / dbus-based control interface
 + * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc.
 + *
 + * This software may be distributed under the terms of the BSD license.
 + * See README for more details.
 + */
 +
 +#include "includes.h"
 +#include <dbus/dbus.h>
 +
 +#include "common.h"
 +#include "eloop.h"
 +#include "wps/wps.h"
 +#include "../config.h"
 +#include "../wpa_supplicant_i.h"
 +#include "../bss.h"
 +#include "dbus_old.h"
 +#include "dbus_old_handlers.h"
 +#include "dbus_common_i.h"
 +
 +
 +/**
 + * wpas_dbus_decompose_object_path - Decompose an interface object path into parts
 + * @path: The dbus object path
 + * @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
 + *
 + * For a given object path, decomposes the object path into object id, network,
 + * and BSSID parts, if those parts exist.
 + */
 +char * wpas_dbus_decompose_object_path(const char *path, char **network,
 +                                     char **bssid)
 +{
 +      const unsigned int dev_path_prefix_len =
 +              strlen(WPAS_DBUS_PATH_INTERFACES "/");
 +      char *obj_path_only;
 +      char *next_sep;
 +
 +      /* Be a bit paranoid about path */
 +      if (!path || strncmp(path, WPAS_DBUS_PATH_INTERFACES "/",
 +                           dev_path_prefix_len))
 +              return NULL;
 +
 +      /* Ensure there's something at the end of the path */
 +      if ((path + dev_path_prefix_len)[0] == '\0')
 +              return NULL;
 +
 +      obj_path_only = os_strdup(path);
 +      if (obj_path_only == NULL)
 +              return NULL;
 +
 +      next_sep = strchr(obj_path_only + dev_path_prefix_len, '/');
 +      if (next_sep != NULL) {
 +              const char *net_part = strstr(next_sep,
 +                                            WPAS_DBUS_NETWORKS_PART "/");
 +              const char *bssid_part = strstr(next_sep,
 +                                              WPAS_DBUS_BSSIDS_PART "/");
 +
 +              if (network && net_part) {
 +                      /* Deal with a request for a configured network */
 +                      const char *net_name = net_part +
 +                              strlen(WPAS_DBUS_NETWORKS_PART "/");
 +                      *network = NULL;
 +                      if (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 +
 +                              strlen(WPAS_DBUS_BSSIDS_PART "/");
 +                      if (strlen(bssid_name))
 +                              *bssid = os_strdup(bssid_name);
 +                      else
 +                              *bssid = NULL;
 +              }
 +
 +              /* Cut off interface object path before "/" */
 +              *next_sep = '\0';
 +      }
 +
 +      return obj_path_only;
 +}
 +
 +
 +/**
 + * wpas_dbus_new_invalid_iface_error - Return a new invalid interface error message
 + * @message: Pointer to incoming dbus message this error refers to
 + * Returns: A dbus error message
 + *
 + * Convenience function to create and return an invalid interface error
 + */
 +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.");
 +}
 +
 +
 +/**
 + * wpas_dbus_new_invalid_network_error - Return a new invalid network error message
 + * @message: Pointer to incoming dbus message this error refers to
 + * Returns: a dbus error message
 + *
 + * Convenience function to create and return an invalid network error
 + */
 +DBusMessage * wpas_dbus_new_invalid_network_error(DBusMessage *message)
 +{
 +      return dbus_message_new_error(message, WPAS_ERROR_INVALID_NETWORK,
 +                                    "The requested network does not exist.");
 +}
 +
 +
 +/**
 + * wpas_dbus_new_invalid_bssid_error - Return a new invalid bssid error message
 + * @message: Pointer to incoming dbus message this error refers to
 + * Returns: a dbus error message
 + *
 + * Convenience function to create and return an invalid bssid error
 + */
 +static DBusMessage * wpas_dbus_new_invalid_bssid_error(DBusMessage *message)
 +{
 +      return dbus_message_new_error(message, WPAS_ERROR_INVALID_BSSID,
 +                                    "The BSSID requested was invalid.");
 +}
 +
 +
 +/**
 + * wpas_dispatch_network_method - dispatch messages for configured networks
 + * @message: the incoming dbus message
 + * @wpa_s: a network interface's data
 + * @network_id: id of the configured network we're interested in
 + * Returns: a reply dbus message, or a dbus error message
 + *
 + * This function dispatches all incoming dbus messages for configured networks.
 + */
 +static DBusMessage * wpas_dispatch_network_method(DBusMessage *message,
 +                                                struct wpa_supplicant *wpa_s,
 +                                                int network_id)
 +{
 +      DBusMessage *reply = NULL;
 +      const char *method = dbus_message_get_member(message);
 +      struct wpa_ssid *ssid;
 +
 +      ssid = wpa_config_get_network(wpa_s->conf, network_id);
 +      if (ssid == NULL)
 +              return wpas_dbus_new_invalid_network_error(message);
 +
 +      if (!strcmp(method, "set"))
 +              reply = wpas_dbus_iface_set_network(message, wpa_s, ssid);
 +      else if (!strcmp(method, "enable"))
 +              reply = wpas_dbus_iface_enable_network(message, wpa_s, ssid);
 +      else if (!strcmp(method, "disable"))
 +              reply = wpas_dbus_iface_disable_network(message, wpa_s, ssid);
 +
 +      return reply;
 +}
 +
 +
 +/**
 + * wpas_dispatch_bssid_method - dispatch messages for scanned networks
 + * @message: the incoming dbus message
 + * @wpa_s: a network interface's data
 + * @bssid: bssid of the scanned network we're interested in
 + * Returns: a reply dbus message, or a dbus error message
 + *
 + * This function dispatches all incoming dbus messages for scanned networks.
 + */
 +static DBusMessage * wpas_dispatch_bssid_method(DBusMessage *message,
 +                                              struct wpa_supplicant *wpa_s,
 +                                              const char *bssid_txt)
 +{
 +      u8 bssid[ETH_ALEN];
 +      struct wpa_bss *bss;
 +
 +      if (hexstr2bin(bssid_txt, bssid, ETH_ALEN) < 0)
 +              return wpas_dbus_new_invalid_bssid_error(message);
 +
 +      bss = wpa_bss_get_bssid(wpa_s, bssid);
 +      if (bss == NULL)
 +              return wpas_dbus_new_invalid_bssid_error(message);
 +
 +      /* Dispatch the method call against the scanned bssid */
 +      if (os_strcmp(dbus_message_get_member(message), "properties") == 0)
 +              return wpas_dbus_bssid_properties(message, wpa_s, bss);
 +
 +      return NULL;
 +}
 +
 +
 +/**
 + * wpas_iface_message_handler - Dispatch messages for interfaces or networks
 + * @connection: Connection to the system message bus
 + * @message: An incoming dbus message
 + * @user_data: A pointer to a dbus control interface data structure
 + * Returns: Whether or not the message was handled
 + *
 + * This function dispatches all incoming dbus messages for network interfaces,
 + * or objects owned by them, such as scanned BSSIDs and configured networks.
 + */
 +static DBusHandlerResult wpas_iface_message_handler(DBusConnection *connection,
 +                                                  DBusMessage *message,
 +                                                  void *user_data)
 +{
 +      struct wpa_supplicant *wpa_s = user_data;
 +      const char *method = dbus_message_get_member(message);
 +      const char *path = dbus_message_get_path(message);
 +      const char *msg_interface = dbus_message_get_interface(message);
 +      char *iface_obj_path = NULL;
 +      char *network = NULL;
 +      char *bssid = NULL;
 +      DBusMessage *reply = NULL;
 +
 +      /* Caller must specify a message interface */
 +      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);
 +      if (iface_obj_path == NULL) {
 +              reply = wpas_dbus_new_invalid_iface_error(message);
 +              goto out;
 +      }
 +
 +      /* Make sure the message's object path actually refers to the
 +       * 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) {
 +              reply = wpas_dbus_new_invalid_iface_error(message);
 +              goto out;
 +      }
 +
 +      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);
 +              else
 +                      reply = wpas_dbus_new_invalid_network_error(message);
 +      } else if (bssid && !strcmp(msg_interface, WPAS_DBUS_IFACE_BSSID)) {
 +              /* A method for one of this interface's scanned BSSIDs */
 +              reply = wpas_dispatch_bssid_method(message, wpa_s, bssid);
 +      } else if (!strcmp(msg_interface, WPAS_DBUS_IFACE_INTERFACE)) {
 +              /* A method for an interface only. */
 +              if (!strcmp(method, "scan"))
 +                      reply = wpas_dbus_iface_scan(message, wpa_s);
 +              else if (!strcmp(method, "scanResults"))
 +                      reply = wpas_dbus_iface_scan_results(message, wpa_s);
 +              else if (!strcmp(method, "addNetwork"))
 +                      reply = wpas_dbus_iface_add_network(message, wpa_s);
 +              else if (!strcmp(method, "removeNetwork"))
 +                      reply = wpas_dbus_iface_remove_network(message, wpa_s);
 +              else if (!strcmp(method, "selectNetwork"))
 +                      reply = wpas_dbus_iface_select_network(message, wpa_s);
 +              else if (!strcmp(method, "capabilities"))
 +                      reply = wpas_dbus_iface_capabilities(message, wpa_s);
 +              else if (!strcmp(method, "disconnect"))
 +                      reply = wpas_dbus_iface_disconnect(message, wpa_s);
 +              else if (!strcmp(method, "setAPScan"))
 +                      reply = wpas_dbus_iface_set_ap_scan(message, wpa_s);
 +              else if (!strcmp(method, "setSmartcardModules"))
 +                      reply = wpas_dbus_iface_set_smartcard_modules(message,
 +                                                                    wpa_s);
 +              else if (!strcmp(method, "state"))
 +                      reply = wpas_dbus_iface_get_state(message, wpa_s);
 +              else if (!strcmp(method, "scanning"))
 +                      reply = wpas_dbus_iface_get_scanning(message, wpa_s);
 +#ifndef CONFIG_NO_CONFIG_BLOBS
 +              else if (!strcmp(method, "setBlobs"))
 +                      reply = wpas_dbus_iface_set_blobs(message, wpa_s);
 +              else if (!strcmp(method, "removeBlobs"))
 +                      reply = wpas_dbus_iface_remove_blobs(message, wpa_s);
 +#endif /* CONFIG_NO_CONFIG_BLOBS */
 +#ifdef CONFIG_WPS
 +              else if (os_strcmp(method, "wpsPbc") == 0)
 +                      reply = wpas_dbus_iface_wps_pbc(message, wpa_s);
 +              else if (os_strcmp(method, "wpsPin") == 0)
 +                      reply = wpas_dbus_iface_wps_pin(message, wpa_s);
 +              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") == 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);
 +      }
 +
 +      os_free(iface_obj_path);
 +      os_free(network);
 +      os_free(bssid);
 +      return reply ? DBUS_HANDLER_RESULT_HANDLED :
 +              DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
 +}
 +
 +
 +/**
 + * wpas_message_handler - dispatch incoming dbus messages
 + * @connection: connection to the system message bus
 + * @message: an incoming dbus message
 + * @user_data: a pointer to a dbus control interface data structure
 + * Returns: whether or not the message was handled
 + *
 + * This function dispatches all incoming dbus messages to the correct
 + * handlers, depending on what the message's target object path is,
 + * and what the method call is.
 + */
 +static DBusHandlerResult wpas_message_handler(DBusConnection *connection,
 +      DBusMessage *message, void *user_data)
 +{
 +      struct wpas_dbus_priv *ctrl_iface = user_data;
 +      const char *method;
 +      const char *path;
 +      const char *msg_interface;
 +      DBusMessage *reply = NULL;
 +
 +      method = dbus_message_get_member(message);
 +      path = dbus_message_get_path(message);
 +      msg_interface = dbus_message_get_interface(message);
 +      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;
 +
 +      if (!strcmp(path, WPAS_DBUS_PATH)) {
 +              /* dispatch methods against our global dbus interface here */
 +              if (!strcmp(method, "addInterface")) {
 +                      reply = wpas_dbus_global_add_interface(
 +                              message, ctrl_iface->global);
 +              } else if (!strcmp(method, "removeInterface")) {
 +                      reply = wpas_dbus_global_remove_interface(
 +                              message, ctrl_iface->global);
 +              } else if (!strcmp(method, "getInterface")) {
 +                      reply = wpas_dbus_global_get_interface(
 +                              message, ctrl_iface->global);
 +              } else if (!strcmp(method, "setDebugParams")) {
 +                      reply = wpas_dbus_global_set_debugparams(
 +                              message, ctrl_iface->global);
 +              }
 +      }
 +
 +      /* If the message was handled, send back the reply */
 +      if (reply) {
 +              if (!dbus_message_get_no_reply(message))
 +                      dbus_connection_send(connection, reply, NULL);
 +              dbus_message_unref(reply);
 +      }
 +
 +      return reply ? DBUS_HANDLER_RESULT_HANDLED :
 +              DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
 +}
 +
 +
 +/**
 + * wpa_supplicant_dbus_notify_scan_results - Send a scan results signal
 + * @wpa_s: %wpa_supplicant network interface data
 + * Returns: 0 on success, -1 on failure
 + *
 + * Notify listeners that this interface has updated scan results.
 + */
 +void wpa_supplicant_dbus_notify_scan_results(struct wpa_supplicant *wpa_s)
 +{
 +      struct wpas_dbus_priv *iface = wpa_s->global->dbus;
 +      DBusMessage *_signal;
 +
 +      /* Do nothing if the control interface is not turned on */
-       if (iface == NULL)
++      if (iface == NULL || !wpa_s->dbus_path)
 +              return;
 +
 +      _signal = dbus_message_new_signal(wpa_s->dbus_path,
 +                                        WPAS_DBUS_IFACE_INTERFACE,
 +                                        "ScanResultsAvailable");
 +      if (_signal == NULL) {
 +              wpa_printf(MSG_ERROR,
 +                         "dbus: Not enough memory to send scan results signal");
 +              return;
 +      }
 +      dbus_connection_send(iface->con, _signal, NULL);
 +      dbus_message_unref(_signal);
 +}
 +
 +
 +/**
 + * wpa_supplicant_dbus_notify_state_change - Send a state change signal
 + * @wpa_s: %wpa_supplicant network interface data
 + * @new_state: new state wpa_supplicant is entering
 + * @old_state: old state wpa_supplicant is leaving
 + * Returns: 0 on success, -1 on failure
 + *
 + * Notify listeners that wpa_supplicant has changed state
 + */
 +void wpa_supplicant_dbus_notify_state_change(struct wpa_supplicant *wpa_s,
 +                                           enum wpa_states new_state,
 +                                           enum wpa_states old_state)
 +{
 +      struct wpas_dbus_priv *iface;
 +      DBusMessage *_signal = NULL;
 +      const char *new_state_str, *old_state_str;
 +
 +      if (wpa_s->dbus_path == NULL)
 +              return; /* Skip signal since D-Bus setup is not yet ready */
 +
 +      /* Do nothing if the control interface is not turned on */
 +      if (wpa_s->global == NULL)
 +              return;
 +      iface = wpa_s->global->dbus;
 +      if (iface == NULL)
 +              return;
 +
 +      /* Only send signal if state really changed */
 +      if (new_state == old_state)
 +              return;
 +
 +      _signal = dbus_message_new_signal(wpa_s->dbus_path,
 +                                        WPAS_DBUS_IFACE_INTERFACE,
 +                                        "StateChange");
 +      if (_signal == NULL) {
 +              wpa_printf(MSG_ERROR,
 +                         "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 (!dbus_message_append_args(_signal,
 +                                    DBUS_TYPE_STRING, &new_state_str,
 +                                    DBUS_TYPE_STRING, &old_state_str,
 +                                    DBUS_TYPE_INVALID)) {
 +              wpa_printf(MSG_ERROR,
 +                         "dbus: %s: Not enough memory to construct state change signal",
 +                         __func__);
 +              goto out;
 +      }
 +
 +      dbus_connection_send(iface->con, _signal, NULL);
 +
 +out:
 +      dbus_message_unref(_signal);
 +}
 +
 +
 +/**
 + * wpa_supplicant_dbus_notify_scanning - send scanning status
 + * @wpa_s: %wpa_supplicant network interface data
 + * Returns: 0 on success, -1 on failure
 + *
 + * Notify listeners of interface scanning state changes
 + */
 +void wpa_supplicant_dbus_notify_scanning(struct wpa_supplicant *wpa_s)
 +{
 +      struct wpas_dbus_priv *iface = wpa_s->global->dbus;
 +      DBusMessage *_signal;
 +      dbus_bool_t scanning = wpa_s->scanning ? TRUE : FALSE;
 +
 +      /* Do nothing if the control interface is not turned on */
-       if (iface == NULL)
++      if (iface == NULL || !wpa_s->dbus_path)
 +              return;
 +
 +      _signal = dbus_message_new_signal(wpa_s->dbus_path,
 +                                        WPAS_DBUS_IFACE_INTERFACE,
 +                                        "Scanning");
 +      if (_signal == NULL) {
 +              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_connection_send(iface->con, _signal, NULL);
 +      } else {
 +              wpa_printf(MSG_ERROR,
 +                         "dbus: Not enough memory to construct signal");
 +      }
 +      dbus_message_unref(_signal);
 +}
 +
 +
 +#ifdef CONFIG_WPS
 +void wpa_supplicant_dbus_notify_wps_cred(struct wpa_supplicant *wpa_s,
 +                                       const struct wps_credential *cred)
 +{
 +      struct wpas_dbus_priv *iface;
 +      DBusMessage *_signal = NULL;
 +
 +      /* Do nothing if the control interface is not turned on */
 +      if (wpa_s->global == NULL)
 +              return;
 +      iface = wpa_s->global->dbus;
-       if (iface == NULL)
++      if (iface == NULL || !wpa_s->dbus_path)
 +              return;
 +
 +      _signal = dbus_message_new_signal(wpa_s->dbus_path,
 +                                        WPAS_DBUS_IFACE_INTERFACE,
 +                                        "WpsCred");
 +      if (_signal == NULL) {
 +              wpa_printf(MSG_ERROR,
 +                         "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,
 +                                    &cred->cred_attr, cred->cred_attr_len,
 +                                    DBUS_TYPE_INVALID)) {
 +              wpa_printf(MSG_ERROR,
 +                         "dbus: %s: Not enough memory to construct signal",
 +                         __func__);
 +              goto out;
 +      }
 +
 +      dbus_connection_send(iface->con, _signal, NULL);
 +
 +out:
 +      dbus_message_unref(_signal);
 +}
 +#else /* CONFIG_WPS */
 +void wpa_supplicant_dbus_notify_wps_cred(struct wpa_supplicant *wpa_s,
 +                                       const struct wps_credential *cred)
 +{
 +}
 +#endif /* CONFIG_WPS */
 +
 +void wpa_supplicant_dbus_notify_certification(struct wpa_supplicant *wpa_s,
 +                                            int depth, const char *subject,
 +                                            const char *cert_hash,
 +                                            const struct wpabuf *cert)
 +{
 +      struct wpas_dbus_priv *iface;
 +      DBusMessage *_signal = NULL;
 +      const char *hash;
 +      const char *cert_hex;
 +      int cert_hex_len;
 +
 +      /* Do nothing if the control interface is not turned on */
 +      if (wpa_s->global == NULL)
 +              return;
 +      iface = wpa_s->global->dbus;
-               if (strcmp(wpa_s->dbus_path, path) == 0)
++      if (iface == NULL || !wpa_s->dbus_path)
 +              return;
 +
 +      _signal = dbus_message_new_signal(wpa_s->dbus_path,
 +                                        WPAS_DBUS_IFACE_INTERFACE,
 +                                        "Certification");
 +      if (_signal == NULL) {
 +              wpa_printf(MSG_ERROR,
 +                         "dbus: %s: Could not create dbus signal; likely out of memory",
 +                         __func__);
 +              return;
 +      }
 +
 +      hash = cert_hash ? cert_hash : "";
 +      cert_hex = cert ? wpabuf_head(cert) : "";
 +      cert_hex_len = cert ? wpabuf_len(cert) : 0;
 +
 +      if (!dbus_message_append_args(_signal,
 +                                    DBUS_TYPE_INT32, &depth,
 +                                    DBUS_TYPE_STRING, &subject,
 +                                    DBUS_TYPE_STRING, &hash,
 +                                    DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE,
 +                                    &cert_hex, cert_hex_len,
 +                                    DBUS_TYPE_INVALID)) {
 +              wpa_printf(MSG_ERROR,
 +                         "dbus: %s: Not enough memory to construct signal",
 +                         __func__);
 +              goto out;
 +      }
 +
 +      dbus_connection_send(iface->con, _signal, NULL);
 +
 +out:
 +      dbus_message_unref(_signal);
 +
 +}
 +
 +
 +/**
 + * wpa_supplicant_dbus_ctrl_iface_init - Initialize dbus control interface
 + * @global: Pointer to global data from wpa_supplicant_init()
 + * Returns: 0 on success, -1 on failure
 + *
 + * Initialize the dbus control interface and start receiving commands from
 + * external programs over the bus.
 + */
 +int wpa_supplicant_dbus_ctrl_iface_init(struct wpas_dbus_priv *iface)
 +{
 +      DBusError error;
 +      int ret = -1;
 +      DBusObjectPathVTable wpas_vtable = {
 +              NULL, &wpas_message_handler, NULL, NULL, NULL, NULL
 +      };
 +
 +      /* Register the message handler for the global dbus interface */
 +      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");
 +              return -1;
 +      }
 +
 +      /* Register our service with the message bus */
 +      dbus_error_init(&error);
 +      switch (dbus_bus_request_name(iface->con, WPAS_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");
 +              break;
 +      default:
 +              wpa_printf(MSG_ERROR,
 +                         "dbus: Could not request service name: %s %s",
 +                         error.name, error.message);
 +              break;
 +      }
 +      dbus_error_free(&error);
 +
 +      if (ret != 0)
 +              return -1;
 +
 +      wpa_printf(MSG_DEBUG, "Providing DBus service '" WPAS_DBUS_SERVICE
 +                 "'.");
 +
 +      return 0;
 +}
 +
 +
 +/**
 + * wpas_dbus_register_new_iface - Register a new interface with dbus
 + * @wpa_s: %wpa_supplicant interface description structure to register
 + * Returns: 0 on success, -1 on error
 + *
 + * Registers a new interface with dbus and assigns it a dbus object path.
 + */
 +int wpas_dbus_register_iface(struct wpa_supplicant *wpa_s)
 +{
 +      struct wpas_dbus_priv *ctrl_iface = wpa_s->global->dbus;
 +      DBusConnection * con;
 +      u32 next;
 +      DBusObjectPathVTable vtable = {
 +              NULL, &wpas_iface_message_handler, NULL, NULL, NULL, NULL
 +      };
 +
 +      /* Do nothing if the control interface is not turned on */
 +      if (ctrl_iface == NULL)
 +              return 0;
 +
 +      con = ctrl_iface->con;
 +      next = ctrl_iface->next_objid++;
 +
 +      /* Create and set the interface's object path */
 +      wpa_s->dbus_path = os_zalloc(WPAS_DBUS_OBJECT_PATH_MAX);
 +      if (wpa_s->dbus_path == NULL)
 +              return -1;
 +      os_snprintf(wpa_s->dbus_path, WPAS_DBUS_OBJECT_PATH_MAX,
 +                  WPAS_DBUS_PATH_INTERFACES "/%u",
 +                  next);
 +
 +      /* 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);
 +              return -1;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +/**
 + * wpas_dbus_unregister_iface - Unregister an interface from dbus
 + * @wpa_s: wpa_supplicant interface structure
 + * Returns: 0 on success, -1 on failure
 + *
 + * Unregisters the interface with dbus
 + */
 +int wpas_dbus_unregister_iface(struct wpa_supplicant *wpa_s)
 +{
 +      struct wpas_dbus_priv *ctrl_iface;
 +      DBusConnection *con;
 +
 +      /* Do nothing if the control interface is not turned on */
 +      if (wpa_s == NULL || wpa_s->global == NULL)
 +              return 0;
 +      ctrl_iface = wpa_s->global->dbus;
 +      if (ctrl_iface == NULL || wpa_s->dbus_path == NULL)
 +              return 0;
 +
 +      con = ctrl_iface->con;
 +      if (!dbus_connection_unregister_object_path(con, wpa_s->dbus_path))
 +              return -1;
 +
 +      os_free(wpa_s->dbus_path);
 +      wpa_s->dbus_path = NULL;
 +
 +      return 0;
 +}
 +
 +
 +/**
 + * wpa_supplicant_get_iface_by_dbus_path - Get a new network interface
 + * @global: Pointer to global data from wpa_supplicant_init()
 + * @path: Pointer to a dbus object path representing an interface
 + * Returns: Pointer to the interface or %NULL if not found
 + */
 +struct wpa_supplicant * wpa_supplicant_get_iface_by_dbus_path(
 +      struct wpa_global *global, const char *path)
 +{
 +      struct wpa_supplicant *wpa_s;
 +
 +      for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
++              if (wpa_s->dbus_path && strcmp(wpa_s->dbus_path, path) == 0)
 +                      return wpa_s;
 +      }
 +      return NULL;
 +}
index 773ee8b49a2d3eca63681b050b42d678d970323f,0000000000000000000000000000000000000000..e8f62ef6bdc3f06b6e9075af8ff18153b9c49b84
mode 100644,000000..100644
--- /dev/null
@@@ -1,1398 -1,0 +1,1404 @@@
-               if (wpa_s) {
 +/*
 + * WPA Supplicant / dbus-based control interface
 + * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc.
 + *
 + * This software may be distributed under the terms of the BSD license.
 + * See README for more details.
 + */
 +
 +#include "includes.h"
 +#include <dbus/dbus.h>
 +
 +#include "common.h"
 +#include "eap_peer/eap_methods.h"
 +#include "common/ieee802_11_defs.h"
 +#include "eapol_supp/eapol_supp_sm.h"
 +#include "rsn_supp/wpa.h"
 +#include "../config.h"
 +#include "../wpa_supplicant_i.h"
 +#include "../driver_i.h"
 +#include "../notify.h"
 +#include "../wpas_glue.h"
 +#include "../bss.h"
 +#include "../scan.h"
 +#include "dbus_old.h"
 +#include "dbus_old_handlers.h"
 +#include "dbus_dict_helpers.h"
 +
 +/**
 + * wpas_dbus_new_invalid_opts_error - Return a new invalid options error message
 + * @message: Pointer to incoming dbus message this error refers to
 + * Returns: a dbus error message
 + *
 + * Convenience function to create and return an invalid options error
 + */
 +DBusMessage * wpas_dbus_new_invalid_opts_error(DBusMessage *message,
 +                                             const char *arg)
 +{
 +      DBusMessage *reply;
 +
 +      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);
 +
 +      return reply;
 +}
 +
 +
 +/**
 + * wpas_dbus_new_success_reply - Return a new success reply message
 + * @message: Pointer to incoming dbus message this reply refers to
 + * Returns: a dbus message containing a single UINT32 that indicates
 + *          success (ie, a value of 1)
 + *
 + * Convenience function to create and return a success reply message
 + */
 +DBusMessage * wpas_dbus_new_success_reply(DBusMessage *message)
 +{
 +      DBusMessage *reply;
 +      unsigned int success = 1;
 +
 +      reply = dbus_message_new_method_return(message);
 +      dbus_message_append_args(reply, DBUS_TYPE_UINT32, &success,
 +                               DBUS_TYPE_INVALID);
 +      return reply;
 +}
 +
 +
 +/**
 + * wpas_dbus_global_add_interface - Request registration of a network interface
 + * @message: Pointer to incoming dbus message
 + * @global: %wpa_supplicant global data structure
 + * Returns: The object path of the new interface object,
 + *          or a dbus error message with more information
 + *
 + * Handler function for "addInterface" method call. Handles requests
 + * by dbus clients to register a network interface that wpa_supplicant
 + * will manage.
 + */
 +DBusMessage * wpas_dbus_global_add_interface(DBusMessage *message,
 +                                           struct wpa_global *global)
 +{
 +      char *ifname = NULL;
 +      char *driver = NULL;
 +      char *driver_param = NULL;
 +      char *confname = NULL;
 +      char *bridge_ifname = NULL;
 +      DBusMessage *reply = NULL;
 +      DBusMessageIter iter;
 +
 +      dbus_message_iter_init(message, &iter);
 +
 +      /* First argument: interface name (DBUS_TYPE_STRING)
 +       *    Required; must be non-zero length
 +       */
 +      if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
 +              goto error;
 +      dbus_message_iter_get_basic(&iter, &ifname);
 +      if (!os_strlen(ifname))
 +              goto error;
 +
 +      /* Second argument: dict of options */
 +      if (dbus_message_iter_next(&iter)) {
 +              DBusMessageIter iter_dict;
 +              struct wpa_dbus_dict_entry entry;
 +
 +              if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL))
 +                      goto error;
 +              while (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
 +                      if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
 +                              goto error;
 +                      if (!strcmp(entry.key, "driver") &&
 +                          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) {
 +                              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) {
 +                              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) {
 +                              os_free(bridge_ifname);
 +                              bridge_ifname = os_strdup(entry.str_value);
 +                              wpa_dbus_dict_entry_clear(&entry);
 +                              if (bridge_ifname == NULL)
 +                                      goto error;
 +                      } else {
 +                              wpa_dbus_dict_entry_clear(&entry);
 +                              goto error;
 +                      }
 +              }
 +      }
 +
 +      /*
 +       * 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_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;
 +              iface.driver_param = driver_param;
 +              iface.confname = confname;
 +              iface.bridge_ifname = bridge_ifname;
 +              /* Otherwise, have wpa_supplicant attach to it. */
 +              wpa_s = wpa_supplicant_add_iface(global, &iface, NULL);
-       if (wpa_s == NULL) {
++              if (wpa_s && wpa_s->dbus_path) {
 +                      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);
 +              } else {
 +                      reply = dbus_message_new_error(
 +                              message, WPAS_ERROR_ADD_ERROR,
 +                              "wpa_supplicant couldn't grab this interface.");
 +              }
 +      }
 +
 +out:
 +      os_free(driver);
 +      os_free(driver_param);
 +      os_free(confname);
 +      os_free(bridge_ifname);
 +      return reply;
 +
 +error:
 +      reply = wpas_dbus_new_invalid_opts_error(message, NULL);
 +      goto out;
 +}
 +
 +
 +/**
 + * wpas_dbus_global_remove_interface - Request deregistration of an interface
 + * @message: Pointer to incoming dbus message
 + * @global: wpa_supplicant global data structure
 + * Returns: a dbus message containing a UINT32 indicating success (1) or
 + *          failure (0), or returns a dbus error message with more information
 + *
 + * Handler function for "removeInterface" method call.  Handles requests
 + * by dbus clients to deregister a network interface that wpa_supplicant
 + * currently manages.
 + */
 +DBusMessage * wpas_dbus_global_remove_interface(DBusMessage *message,
 +                                              struct wpa_global *global)
 +{
 +      struct wpa_supplicant *wpa_s;
 +      char *path;
 +      DBusMessage *reply = NULL;
 +
 +      if (!dbus_message_get_args(message, NULL,
 +                                 DBUS_TYPE_OBJECT_PATH, &path,
 +                                 DBUS_TYPE_INVALID)) {
 +              reply = wpas_dbus_new_invalid_opts_error(message, NULL);
 +              goto out;
 +      }
 +
 +      wpa_s = wpa_supplicant_get_iface_by_dbus_path(global, path);
 +      if (wpa_s == NULL) {
 +              reply = wpas_dbus_new_invalid_iface_error(message);
 +              goto out;
 +      }
 +
 +      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.");
 +      }
 +
 +out:
 +      return reply;
 +}
 +
 +
 +/**
 + * wpas_dbus_global_get_interface - Get the object path for an interface name
 + * @message: Pointer to incoming dbus message
 + * @global: %wpa_supplicant global data structure
 + * Returns: The object path of the interface object,
 + *          or a dbus error message with more information
 + *
 + * Handler function for "getInterface" method call. Handles requests
 + * by dbus clients for the object path of an specific network interface.
 + */
 +DBusMessage * wpas_dbus_global_get_interface(DBusMessage *message,
 +                                           struct wpa_global *global)
 +{
 +      DBusMessage *reply = NULL;
 +      const char *ifname;
 +      const char *path;
 +      struct wpa_supplicant *wpa_s;
 +
 +      if (!dbus_message_get_args(message, NULL,
 +                                 DBUS_TYPE_STRING, &ifname,
 +                                 DBUS_TYPE_INVALID)) {
 +              reply = wpas_dbus_new_invalid_opts_error(message, NULL);
 +              goto out;
 +      }
 +
 +      wpa_s = wpa_supplicant_get_iface(global, ifname);
-               dbus_bool_t success = FALSE;
++      if (wpa_s == NULL || !wpa_s->dbus_path) {
 +              reply = wpas_dbus_new_invalid_iface_error(message);
 +              goto out;
 +      }
 +
 +      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);
 +
 +out:
 +      return reply;
 +}
 +
 +
 +/**
 + * wpas_dbus_global_set_debugparams- Set the debug params
 + * @message: Pointer to incoming dbus message
 + * @global: %wpa_supplicant global data structure
 + * Returns: a dbus message containing a UINT32 indicating success (1) or
 + *          failure (0), or returns a dbus error message with more information
 + *
 + * Handler function for "setDebugParams" method call. Handles requests
 + * by dbus clients for the object path of an specific network interface.
 + */
 +DBusMessage * wpas_dbus_global_set_debugparams(DBusMessage *message,
 +                                             struct wpa_global *global)
 +{
 +      DBusMessage *reply = NULL;
 +      int debug_level;
 +      dbus_bool_t debug_timestamp;
 +      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)) {
 +              return wpas_dbus_new_invalid_opts_error(message, NULL);
 +      }
 +
 +      if (wpa_supplicant_set_debug_params(global, debug_level,
 +                                          debug_timestamp ? 1 : 0,
 +                                          debug_show_keys ? 1 : 0)) {
 +              return wpas_dbus_new_invalid_opts_error(message, NULL);
 +      }
 +
 +      reply = wpas_dbus_new_success_reply(message);
 +
 +      return reply;
 +}
 +
 +
 +/**
 + * wpas_dbus_iface_scan - Request a wireless scan on an interface
 + * @message: Pointer to incoming dbus message
 + * @wpa_s: wpa_supplicant structure for a network interface
 + * Returns: a dbus message containing a UINT32 indicating success (1) or
 + *          failure (0)
 + *
 + * Handler function for "scan" method call of a network device. Requests
 + * that wpa_supplicant perform a wireless scan as soon as possible
 + * on a particular wireless interface.
 + */
 +DBusMessage * wpas_dbus_iface_scan(DBusMessage *message,
 +                                 struct wpa_supplicant *wpa_s)
 +{
 +      wpa_s->scan_req = MANUAL_SCAN_REQ;
 +      wpa_supplicant_req_scan(wpa_s, 0, 0);
 +      return wpas_dbus_new_success_reply(message);
 +}
 +
 +
 +/**
 + * wpas_dbus_iface_scan_results - Get the results of a recent scan request
 + * @message: Pointer to incoming dbus message
 + * @wpa_s: wpa_supplicant structure for a network interface
 + * Returns: a dbus message containing a dbus array of objects paths, or returns
 + *          a dbus error message if not scan results could be found
 + *
 + * Handler function for "scanResults" method call of a network device. Returns
 + * a dbus message containing the object paths of wireless networks found.
 + */
 +DBusMessage * wpas_dbus_iface_scan_results(DBusMessage *message,
 +                                         struct wpa_supplicant *wpa_s)
 +{
 +      DBusMessage *reply;
 +      DBusMessageIter iter;
 +      DBusMessageIter sub_iter;
 +      struct wpa_bss *bss;
 +
++      if (!wpa_s->dbus_path)
++              return dbus_message_new_error(message,
++                                            WPAS_ERROR_INTERNAL_ERROR,
++                                            "no D-Bus interface available");
++
 +      /* Create and initialize the return message */
 +      reply = dbus_message_new_method_return(message);
 +      dbus_message_iter_init_append(reply, &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) {
 +              char path_buf[WPAS_DBUS_OBJECT_PATH_MAX];
 +              char *path = path_buf;
 +
 +              /* Construct the object path for this network.  Note that ':'
 +               * is not a valid character in dbus object paths.
 +               */
 +              os_snprintf(path, WPAS_DBUS_OBJECT_PATH_MAX,
 +                          "%s/" WPAS_DBUS_BSSIDS_PART "/"
 +                          WPAS_DBUS_BSSID_FORMAT,
 +                          wpa_s->dbus_path, MAC2STR(bss->bssid));
 +              if (!dbus_message_iter_append_basic(&sub_iter,
 +                                                  DBUS_TYPE_OBJECT_PATH,
 +                                                  &path))
 +                      goto error;
 +      }
 +
 +      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");
 +}
 +
 +
 +/**
 + * wpas_dbus_bssid_properties - Return the properties of a scanned network
 + * @message: Pointer to incoming dbus message
 + * @wpa_s: wpa_supplicant structure for a network interface
 + * @res: wpa_supplicant scan result for which to get properties
 + * Returns: a dbus message containing the properties for the requested network
 + *
 + * Handler function for "properties" method call of a scanned network.
 + * Returns a dbus message containing the the properties.
 + */
 +DBusMessage * wpas_dbus_bssid_properties(DBusMessage *message,
 +                                       struct wpa_supplicant *wpa_s,
 +                                       struct wpa_bss *bss)
 +{
 +      DBusMessage *reply;
 +      DBusMessageIter iter, iter_dict;
 +      const u8 *wpa_ie, *rsn_ie, *wps_ie;
 +
 +      /* Dump the properties into a dbus message */
 +      reply = dbus_message_new_method_return(message);
 +
 +      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);
 +
 +      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) ||
 +          !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.");
 +      }
 +
 +      return reply;
 +}
 +
 +
 +/**
 + * wpas_dbus_iface_capabilities - Return interface capabilities
 + * @message: Pointer to incoming dbus message
 + * @wpa_s: wpa_supplicant structure for a network interface
 + * Returns: A dbus message containing a dict of strings
 + *
 + * Handler function for "capabilities" method call of an interface.
 + */
 +DBusMessage * wpas_dbus_iface_capabilities(DBusMessage *message,
 +                                         struct wpa_supplicant *wpa_s)
 +{
 +      DBusMessage *reply = NULL;
 +      struct wpa_driver_capa capa;
 +      int res;
 +      DBusMessageIter iter, iter_dict;
 +      char **eap_methods;
 +      size_t num_items;
 +      dbus_bool_t strict = FALSE;
 +      DBusMessageIter iter_dict_entry, iter_dict_val, iter_array;
 +
 +      if (!dbus_message_get_args(message, NULL,
 +                                 DBUS_TYPE_BOOLEAN, &strict,
 +                                 DBUS_TYPE_INVALID))
 +              strict = FALSE;
 +
 +      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;
 +
 +      /* EAP methods */
 +      eap_methods = eap_get_names_as_string_array(&num_items);
 +      if (eap_methods) {
-       struct wpa_ssid *ssid;
++              dbus_bool_t success;
 +              size_t i = 0;
 +
 +              success = wpa_dbus_dict_append_string_array(
 +                      &iter_dict, "eap", (const char **) eap_methods,
 +                      num_items);
 +
 +              /* free returned method array */
 +              while (eap_methods[i])
 +                      os_free(eap_methods[i++]);
 +              os_free(eap_methods);
 +
 +              if (!success)
 +                      goto error;
 +      }
 +
 +      res = wpa_drv_get_capa(wpa_s, &capa);
 +
 +      /***** pairwise cipher */
 +      if (res < 0) {
 +              if (!strict) {
 +                      const char *args[] = {"CCMP", "TKIP", "NONE"};
 +
 +                      if (!wpa_dbus_dict_append_string_array(
 +                                  &iter_dict, "pairwise", args,
 +                                  ARRAY_SIZE(args)))
 +                              goto error;
 +              }
 +      } 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) &&
 +                   !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))
 +                      goto error;
 +      }
 +
 +      /***** group cipher */
 +      if (res < 0) {
 +              if (!strict) {
 +                      const char *args[] = {
 +                              "CCMP", "TKIP", "WEP104", "WEP40"
 +                      };
 +
 +                      if (!wpa_dbus_dict_append_string_array(
 +                                  &iter_dict, "group", args,
 +                                  ARRAY_SIZE(args)))
 +                              goto error;
 +              }
 +      } else {
 +              if (!wpa_dbus_dict_begin_string_array(&iter_dict, "group",
 +                                                    &iter_dict_entry,
 +                                                    &iter_dict_val,
 +                                                    &iter_array))
 +                      goto error;
 +
 +              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))
 +                      goto error;
 +      }
 +
 +      /***** key management */
 +      if (res < 0) {
 +              if (!strict) {
 +                      const char *args[] = {
 +                              "WPA-PSK", "WPA-EAP", "IEEE8021X", "WPA-NONE",
 +                              "NONE"
 +                      };
 +                      if (!wpa_dbus_dict_append_string_array(
 +                                  &iter_dict, "key_mgmt", args,
 +                                  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) ||
 +                  !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))
 +                      goto error;
 +      }
 +
 +      /***** WPA protocol */
 +      if (res < 0) {
 +              if (!strict) {
 +                      const char *args[] = { "RSN", "WPA" };
 +
 +                      if (!wpa_dbus_dict_append_string_array(
 +                                  &iter_dict, "proto", args,
 +                                  ARRAY_SIZE(args)))
 +                              goto error;
 +              }
 +      } else {
 +              if (!wpa_dbus_dict_begin_string_array(&iter_dict, "proto",
 +                                                    &iter_dict_entry,
 +                                                    &iter_dict_val,
 +                                                    &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))
 +                      goto error;
 +      }
 +
 +      /***** auth alg */
 +      if (res < 0) {
 +              if (!strict) {
 +                      const char *args[] = { "OPEN", "SHARED", "LEAP" };
 +
 +                      if (!wpa_dbus_dict_append_string_array(
 +                                  &iter_dict, "auth_alg", args,
 +                                  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) ||
 +                  ((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))
 +                      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 interface capabilities.");
 +}
 +
 +
 +/**
 + * wpas_dbus_iface_add_network - Add a new configured network
 + * @message: Pointer to incoming dbus message
 + * @wpa_s: wpa_supplicant structure for a network interface
 + * Returns: A dbus message containing the object path of the new network
 + *
 + * Handler function for "addNetwork" method call of a network interface.
 + */
 +DBusMessage * wpas_dbus_iface_add_network(DBusMessage *message,
 +                                        struct wpa_supplicant *wpa_s)
 +{
 +      DBusMessage *reply = NULL;
-       ssid = wpa_config_add_network(wpa_s->conf);
++      struct wpa_ssid *ssid = NULL;
 +      char path_buf[WPAS_DBUS_OBJECT_PATH_MAX], *path = path_buf;
 +
-       if (os_strcmp(iface, wpa_s->dbus_path) != 0) {
++      if (wpa_s->dbus_path)
++              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.");
 +              goto out;
 +      }
 +      wpas_notify_network_added(wpa_s, ssid);
 +      ssid->disabled = 1;
 +      wpa_config_set_network_defaults(ssid);
 +
 +      /* Construct the object path for this network. */
 +      os_snprintf(path, WPAS_DBUS_OBJECT_PATH_MAX,
 +                  "%s/" WPAS_DBUS_NETWORKS_PART "/%d",
 +                  wpa_s->dbus_path, ssid->id);
 +
 +      reply = dbus_message_new_method_return(message);
 +      dbus_message_append_args(reply, DBUS_TYPE_OBJECT_PATH,
 +                               &path, DBUS_TYPE_INVALID);
 +
 +out:
 +      return reply;
 +}
 +
 +
 +/**
 + * wpas_dbus_iface_remove_network - Remove a configured network
 + * @message: Pointer to incoming dbus message
 + * @wpa_s: wpa_supplicant structure for a network interface
 + * Returns: A dbus message containing a UINT32 indicating success (1) or
 + *          failure (0)
 + *
 + * Handler function for "removeNetwork" method call of a network interface.
 + */
 +DBusMessage * wpas_dbus_iface_remove_network(DBusMessage *message,
 +                                           struct wpa_supplicant *wpa_s)
 +{
 +      DBusMessage *reply = NULL;
 +      const char *op;
 +      char *iface = NULL, *net_id = NULL;
 +      int id;
 +      struct wpa_ssid *ssid;
 +
 +      if (!dbus_message_get_args(message, NULL,
 +                                 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 || net_id == NULL) {
 +              reply = wpas_dbus_new_invalid_network_error(message);
 +              goto out;
 +      }
 +
 +      /* Ensure the network is actually a child of this interface */
- static const char  const *dont_quote[] = {
++      if (!wpa_s->dbus_path || os_strcmp(iface, wpa_s->dbus_path) != 0) {
 +              reply = wpas_dbus_new_invalid_network_error(message);
 +              goto out;
 +      }
 +
 +      id = strtoul(net_id, NULL, 10);
 +      ssid = wpa_config_get_network(wpa_s->conf, id);
 +      if (ssid == NULL) {
 +              reply = wpas_dbus_new_invalid_network_error(message);
 +              goto out;
 +      }
 +
 +      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.");
 +              goto out;
 +      }
 +
 +      reply = wpas_dbus_new_success_reply(message);
 +
 +out:
 +      os_free(iface);
 +      os_free(net_id);
 +      return reply;
 +}
 +
 +
-       "bssid", NULL
++static const char * const dont_quote[] = {
 +      "key_mgmt", "proto", "pairwise", "auth_alg", "group", "eap",
 +      "opensc_engine_path", "pkcs11_engine_path", "pkcs11_module_path",
-                               if (size <= 0)
++      "bssid", "scan_freq", "freq_list", NULL
 +};
 +
 +
 +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;
 +              i++;
 +      }
 +      return TRUE;
 +}
 +
 +
 +/**
 + * wpas_dbus_iface_set_network - Set options for a configured network
 + * @message: Pointer to incoming dbus message
 + * @wpa_s: wpa_supplicant structure for a network interface
 + * @ssid: wpa_ssid structure for a configured network
 + * Returns: a dbus message containing a UINT32 indicating success (1) or
 + *          failure (0)
 + *
 + * Handler function for "set" method call of a configured network.
 + */
 +DBusMessage * wpas_dbus_iface_set_network(DBusMessage *message,
 +                                        struct wpa_supplicant *wpa_s,
 +                                        struct wpa_ssid *ssid)
 +{
 +      DBusMessage *reply = NULL;
 +      struct wpa_dbus_dict_entry entry = { .type = DBUS_TYPE_STRING };
 +      DBusMessageIter iter, iter_dict;
 +
 +      dbus_message_iter_init(message, &iter);
 +
 +      if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL)) {
 +              reply = wpas_dbus_new_invalid_opts_error(message, NULL);
 +              goto out;
 +      }
 +
 +      while (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
 +              char *value = NULL;
 +              size_t size = 50;
 +              int ret;
 +
 +              if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) {
 +                      reply = wpas_dbus_new_invalid_opts_error(message,
 +                                                               NULL);
 +                      goto out;
 +              }
 +
 +              /* Type conversions, since wpa_supplicant wants strings */
 +              if (entry.type == DBUS_TYPE_ARRAY &&
 +                  entry.array_type == DBUS_TYPE_BYTE) {
 +                      if (entry.array_len <= 0)
 +                              goto error;
 +
 +                      size = entry.array_len * 2 + 1;
 +                      value = os_zalloc(size);
 +                      if (value == NULL)
 +                              goto error;
 +                      ret = wpa_snprintf_hex(value, size,
 +                                             (u8 *) entry.bytearray_value,
 +                                             entry.array_len);
 +                      if (ret <= 0)
 +                              goto error;
 +              } else if (entry.type == DBUS_TYPE_STRING) {
 +                      if (should_quote_opt(entry.key)) {
 +                              size = os_strlen(entry.str_value);
 +                              /* Zero-length option check */
-               if (network == NULL ||
++                              if (size == 0)
 +                                      goto error;
 +                              size += 3;  /* For quotes and terminator */
 +                              value = os_zalloc(size);
 +                              if (value == NULL)
 +                                      goto error;
 +                              ret = os_snprintf(value, size, "\"%s\"",
 +                                                entry.str_value);
 +                              if (os_snprintf_error(size, ret))
 +                                      goto error;
 +                      } else {
 +                              value = os_strdup(entry.str_value);
 +                              if (value == NULL)
 +                                      goto error;
 +                      }
 +              } else if (entry.type == DBUS_TYPE_UINT32) {
 +                      value = os_zalloc(size);
 +                      if (value == NULL)
 +                              goto error;
 +                      ret = os_snprintf(value, size, "%u",
 +                                        entry.uint32_value);
 +                      if (os_snprintf_error(size, ret))
 +                              goto error;
 +              } else if (entry.type == DBUS_TYPE_INT32) {
 +                      value = os_zalloc(size);
 +                      if (value == NULL)
 +                              goto error;
 +                      ret = os_snprintf(value, size, "%d",
 +                                        entry.int32_value);
 +                      if (os_snprintf_error(size, ret))
 +                              goto error;
 +              } else
 +                      goto error;
 +
 +              if (wpa_config_set(ssid, entry.key, value, 0) < 0)
 +                      goto error;
 +
 +              if ((os_strcmp(entry.key, "psk") == 0 &&
 +                   value[0] == '"' && ssid->ssid_len) ||
 +                  (os_strcmp(entry.key, "ssid") == 0 && ssid->passphrase))
 +                      wpa_config_update_psk(ssid);
 +              else if (os_strcmp(entry.key, "priority") == 0)
 +                      wpa_config_update_prio_list(wpa_s->conf);
 +
 +              os_free(value);
 +              wpa_dbus_dict_entry_clear(&entry);
 +              continue;
 +
 +      error:
 +              os_free(value);
 +              reply = wpas_dbus_new_invalid_opts_error(message, entry.key);
 +              wpa_dbus_dict_entry_clear(&entry);
 +              break;
 +      }
 +
 +      if (!reply)
 +              reply = wpas_dbus_new_success_reply(message);
 +
 +out:
 +      return reply;
 +}
 +
 +
 +/**
 + * wpas_dbus_iface_enable_network - Mark a configured network as enabled
 + * @message: Pointer to incoming dbus message
 + * @wpa_s: wpa_supplicant structure for a network interface
 + * @ssid: wpa_ssid structure for a configured network
 + * Returns: A dbus message containing a UINT32 indicating success (1) or
 + *          failure (0)
 + *
 + * Handler function for "enable" method call of a configured network.
 + */
 +DBusMessage * wpas_dbus_iface_enable_network(DBusMessage *message,
 +                                           struct wpa_supplicant *wpa_s,
 +                                           struct wpa_ssid *ssid)
 +{
 +      wpa_supplicant_enable_network(wpa_s, ssid);
 +      return wpas_dbus_new_success_reply(message);
 +}
 +
 +
 +/**
 + * wpas_dbus_iface_disable_network - Mark a configured network as disabled
 + * @message: Pointer to incoming dbus message
 + * @wpa_s: wpa_supplicant structure for a network interface
 + * @ssid: wpa_ssid structure for a configured network
 + * Returns: A dbus message containing a UINT32 indicating success (1) or
 + *          failure (0)
 + *
 + * Handler function for "disable" method call of a configured network.
 + */
 +DBusMessage * wpas_dbus_iface_disable_network(DBusMessage *message,
 +                                            struct wpa_supplicant *wpa_s,
 +                                            struct wpa_ssid *ssid)
 +{
 +      wpa_supplicant_disable_network(wpa_s, ssid);
 +      return wpas_dbus_new_success_reply(message);
 +}
 +
 +
 +/**
 + * wpas_dbus_iface_select_network - Attempt association with a configured network
 + * @message: Pointer to incoming dbus message
 + * @wpa_s: wpa_supplicant structure for a network interface
 + * Returns: A dbus message containing a UINT32 indicating success (1) or
 + *          failure (0)
 + *
 + * Handler function for "selectNetwork" method call of network interface.
 + */
 +DBusMessage * wpas_dbus_iface_select_network(DBusMessage *message,
 +                                           struct wpa_supplicant *wpa_s)
 +{
 +      DBusMessage *reply = NULL;
 +      const char *op;
 +      struct wpa_ssid *ssid;
 +      char *iface_obj_path = NULL;
 +      char *network = NULL;
 +
 +      if (os_strlen(dbus_message_get_signature(message)) == 0) {
 +              /* Any network */
 +              ssid = NULL;
 +      } else {
 +              int nid;
 +
 +              if (!dbus_message_get_args(message, NULL,
 +                                         DBUS_TYPE_OBJECT_PATH, &op,
 +                                         DBUS_TYPE_INVALID)) {
 +                      reply = wpas_dbus_new_invalid_opts_error(message,
 +                                                               NULL);
 +                      goto out;
 +              }
 +
 +              /* Extract the network number */
 +              iface_obj_path = wpas_dbus_decompose_object_path(op,
 +                                                               &network,
 +                                                               NULL);
 +              if (iface_obj_path == NULL) {
 +                      reply = wpas_dbus_new_invalid_iface_error(message);
 +                      goto out;
 +              }
 +              /* Ensure the object path really points to this interface */
++              if (network == NULL || !wpa_s->dbus_path ||
 +                  os_strcmp(iface_obj_path, wpa_s->dbus_path) != 0) {
 +                      reply = wpas_dbus_new_invalid_network_error(message);
 +                      goto out;
 +              }
 +
 +              nid = strtoul(network, NULL, 10);
 +              if (errno == EINVAL) {
 +                      reply = wpas_dbus_new_invalid_network_error(message);
 +                      goto out;
 +              }
 +
 +              ssid = wpa_config_get_network(wpa_s->conf, nid);
 +              if (ssid == NULL) {
 +                      reply = wpas_dbus_new_invalid_network_error(message);
 +                      goto out;
 +              }
 +      }
 +
 +      /* Finally, associate with the network */
 +      wpa_supplicant_select_network(wpa_s, ssid);
 +
 +      reply = wpas_dbus_new_success_reply(message);
 +
 +out:
 +      os_free(iface_obj_path);
 +      os_free(network);
 +      return reply;
 +}
 +
 +
 +/**
 + * wpas_dbus_iface_disconnect - Terminate the current connection
 + * @message: Pointer to incoming dbus message
 + * @wpa_s: wpa_supplicant structure for a network interface
 + * Returns: A dbus message containing a UINT32 indicating success (1) or
 + *          failure (0)
 + *
 + * Handler function for "disconnect" method call of network interface.
 + */
 +DBusMessage * wpas_dbus_iface_disconnect(DBusMessage *message,
 +                                       struct wpa_supplicant *wpa_s)
 +{
 +      wpa_s->disconnected = 1;
 +      wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING);
 +
 +      return wpas_dbus_new_success_reply(message);
 +}
 +
 +
 +/**
 + * wpas_dbus_iface_set_ap_scan - Control roaming mode
 + * @message: Pointer to incoming dbus message
 + * @wpa_s: wpa_supplicant structure for a network interface
 + * Returns: A dbus message containing a UINT32 indicating success (1) or
 + *          failure (0)
 + *
 + * Handler function for "setAPScan" method call.
 + */
 +DBusMessage * wpas_dbus_iface_set_ap_scan(DBusMessage *message,
 +                                        struct wpa_supplicant *wpa_s)
 +{
 +      DBusMessage *reply = NULL;
 +      dbus_uint32_t ap_scan = 1;
 +
 +      if (!dbus_message_get_args(message, NULL, DBUS_TYPE_UINT32, &ap_scan,
 +                                 DBUS_TYPE_INVALID)) {
 +              reply = wpas_dbus_new_invalid_opts_error(message, NULL);
 +              goto out;
 +      }
 +
 +      if (wpa_supplicant_set_ap_scan(wpa_s, ap_scan)) {
 +              reply = wpas_dbus_new_invalid_opts_error(message, NULL);
 +              goto out;
 +      }
 +
 +      reply = wpas_dbus_new_success_reply(message);
 +
 +out:
 +      return reply;
 +}
 +
 +
 +/**
 + * wpas_dbus_iface_set_smartcard_modules - Set smartcard related module paths
 + * @message: Pointer to incoming dbus message
 + * @wpa_s: wpa_supplicant structure for a network interface
 + * Returns: A dbus message containing a UINT32 indicating success (1) or
 + *          failure (0)
 + *
 + * Handler function for "setSmartcardModules" method call.
 + */
 +DBusMessage * wpas_dbus_iface_set_smartcard_modules(
 +      DBusMessage *message, struct wpa_supplicant *wpa_s)
 +{
 +      DBusMessageIter iter, iter_dict;
 +      char *opensc_engine_path = NULL;
 +      char *pkcs11_engine_path = NULL;
 +      char *pkcs11_module_path = NULL;
 +      struct wpa_dbus_dict_entry entry;
 +
 +      if (!dbus_message_iter_init(message, &iter))
 +              goto error;
 +
 +      if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL))
 +              goto error;
 +
 +      while (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
 +              if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
 +                      goto error;
 +              if (!strcmp(entry.key, "opensc_engine_path") &&
 +                  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) {
 +                      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) {
 +                      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;
 +              }
 +      }
 +
 +      os_free(wpa_s->conf->opensc_engine_path);
 +      wpa_s->conf->opensc_engine_path = opensc_engine_path;
 +      os_free(wpa_s->conf->pkcs11_engine_path);
 +      wpa_s->conf->pkcs11_engine_path = pkcs11_engine_path;
 +      os_free(wpa_s->conf->pkcs11_module_path);
 +      wpa_s->conf->pkcs11_module_path = pkcs11_module_path;
 +
 +      wpa_sm_set_eapol(wpa_s->wpa, NULL);
 +      eapol_sm_deinit(wpa_s->eapol);
 +      wpa_s->eapol = NULL;
 +      wpa_supplicant_init_eapol(wpa_s);
 +      wpa_sm_set_eapol(wpa_s->wpa, wpa_s->eapol);
 +
 +      return wpas_dbus_new_success_reply(message);
 +
 +error:
 +      os_free(opensc_engine_path);
 +      os_free(pkcs11_engine_path);
 +      os_free(pkcs11_module_path);
 +      return wpas_dbus_new_invalid_opts_error(message, NULL);
 +}
 +
 +
 +/**
 + * wpas_dbus_iface_get_state - Get interface state
 + * @message: Pointer to incoming dbus message
 + * @wpa_s: wpa_supplicant structure for a network interface
 + * Returns: A dbus message containing a STRING representing the current
 + *          interface state
 + *
 + * Handler function for "state" method call.
 + */
 +DBusMessage * wpas_dbus_iface_get_state(DBusMessage *message,
 +                                      struct wpa_supplicant *wpa_s)
 +{
 +      DBusMessage *reply = NULL;
 +      const char *str_state;
 +
 +      reply = dbus_message_new_method_return(message);
 +      if (reply != NULL) {
 +              str_state = wpa_supplicant_state_txt(wpa_s->wpa_state);
 +              dbus_message_append_args(reply, DBUS_TYPE_STRING, &str_state,
 +                                       DBUS_TYPE_INVALID);
 +      }
 +
 +      return reply;
 +}
 +
 +
 +/**
 + * wpas_dbus_iface_get_scanning - Get interface scanning state
 + * @message: Pointer to incoming dbus message
 + * @wpa_s: wpa_supplicant structure for a network interface
 + * Returns: A dbus message containing whether the interface is scanning
 + *
 + * Handler function for "scanning" method call.
 + */
 +DBusMessage * wpas_dbus_iface_get_scanning(DBusMessage *message,
 +                                         struct wpa_supplicant *wpa_s)
 +{
 +      DBusMessage *reply = NULL;
 +      dbus_bool_t scanning = wpa_s->scanning ? TRUE : FALSE;
 +
 +      reply = dbus_message_new_method_return(message);
 +      if (reply != NULL) {
 +              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");
 +      }
 +
 +      return reply;
 +}
 +
 +
 +#ifndef CONFIG_NO_CONFIG_BLOBS
 +
 +/**
 + * wpas_dbus_iface_set_blobs - Store named binary blobs (ie, for certificates)
 + * @message: Pointer to incoming dbus message
 + * @wpa_s: %wpa_supplicant data structure
 + * Returns: A dbus message containing a UINT32 indicating success (1) or
 + *          failure (0)
 + *
 + * Asks wpa_supplicant to internally store a one or more binary blobs.
 + */
 +DBusMessage * wpas_dbus_iface_set_blobs(DBusMessage *message,
 +                                      struct wpa_supplicant *wpa_s)
 +{
 +      DBusMessage *reply = NULL;
 +      struct wpa_dbus_dict_entry entry = { .type = DBUS_TYPE_STRING };
 +      DBusMessageIter iter, iter_dict;
 +
 +      dbus_message_iter_init(message, &iter);
 +
 +      if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL))
 +              return wpas_dbus_new_invalid_opts_error(message, NULL);
 +
 +      while (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
 +              struct wpa_config_blob *blob;
 +
 +              if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) {
 +                      reply = wpas_dbus_new_invalid_opts_error(message,
 +                                                               NULL);
 +                      break;
 +              }
 +
 +              if (entry.type != DBUS_TYPE_ARRAY ||
 +                  entry.array_type != DBUS_TYPE_BYTE) {
 +                      reply = wpas_dbus_new_invalid_opts_error(
 +                              message, "Byte array expected.");
 +                      break;
 +              }
 +
 +              if ((entry.array_len <= 0) || (entry.array_len > 65536) ||
 +                  !strlen(entry.key)) {
 +                      reply = wpas_dbus_new_invalid_opts_error(
 +                              message, "Invalid array size.");
 +                      break;
 +              }
 +
 +              blob = os_zalloc(sizeof(*blob));
 +              if (blob == NULL) {
 +                      reply = dbus_message_new_error(
 +                              message, WPAS_ERROR_ADD_ERROR,
 +                              "Not enough memory to add blob.");
 +                      break;
 +              }
 +              blob->data = os_zalloc(entry.array_len);
 +              if (blob->data == NULL) {
 +                      reply = dbus_message_new_error(
 +                              message, WPAS_ERROR_ADD_ERROR,
 +                              "Not enough memory to add blob data.");
 +                      os_free(blob);
 +                      break;
 +              }
 +
 +              blob->name = os_strdup(entry.key);
 +              blob->len = entry.array_len;
 +              os_memcpy(blob->data, (u8 *) entry.bytearray_value,
 +                              entry.array_len);
 +              if (blob->name == NULL) {
 +                      wpa_config_free_blob(blob);
 +                      reply = dbus_message_new_error(
 +                              message, WPAS_ERROR_ADD_ERROR,
 +                              "Error adding blob.");
 +                      break;
 +              }
 +
 +              /* Success */
 +              if (!wpa_config_remove_blob(wpa_s->conf, blob->name))
 +                      wpas_notify_blob_removed(wpa_s, blob->name);
 +              wpa_config_set_blob(wpa_s->conf, blob);
 +              wpas_notify_blob_added(wpa_s, blob->name);
 +
 +              wpa_dbus_dict_entry_clear(&entry);
 +      }
 +      wpa_dbus_dict_entry_clear(&entry);
 +
 +      return reply ? reply : wpas_dbus_new_success_reply(message);
 +}
 +
 +
 +/**
 + * wpas_dbus_iface_remove_blob - Remove named binary blobs
 + * @message: Pointer to incoming dbus message
 + * @wpa_s: %wpa_supplicant data structure
 + * Returns: A dbus message containing a UINT32 indicating success (1) or
 + *          failure (0)
 + *
 + * Asks wpa_supplicant to remove one or more previously stored binary blobs.
 + */
 +DBusMessage * wpas_dbus_iface_remove_blobs(DBusMessage *message,
 +                                         struct wpa_supplicant *wpa_s)
 +{
 +      DBusMessageIter iter, array;
 +      char *err_msg = NULL;
 +
 +      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)
 +              return wpas_dbus_new_invalid_opts_error(message, NULL);
 +
 +      dbus_message_iter_recurse(&iter, &array);
 +      while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_STRING) {
 +              const char *name;
 +
 +              dbus_message_iter_get_basic(&array, &name);
 +              if (!os_strlen(name))
 +                      err_msg = "Invalid blob name.";
 +              else if (wpa_config_remove_blob(wpa_s->conf, name) != 0)
 +                      err_msg = "Error removing blob.";
 +              else
 +                      wpas_notify_blob_removed(wpa_s, name);
 +              dbus_message_iter_next(&array);
 +      }
 +
 +      if (err_msg)
 +              return dbus_message_new_error(message, WPAS_ERROR_REMOVE_ERROR,
 +                                            err_msg);
 +
 +      return wpas_dbus_new_success_reply(message);
 +}
 +
 +#endif /* CONFIG_NO_CONFIG_BLOBS */
 +
 +
 +/**
 + * wpas_dbus_iface_flush - Clear BSS of old or all inactive entries
 + * @message: Pointer to incoming dbus message
 + * @wpa_s: %wpa_supplicant data structure
 + * Returns: a dbus message containing a UINT32 indicating success (1) or
 + *          failure (0), or returns a dbus error message with more information
 + *
 + * Handler function for "flush" method call. Handles requests for an
 + * interface with an optional "age" parameter that specifies the minimum
 + * age of a BSS to be flushed.
 + */
 +DBusMessage * wpas_dbus_iface_flush(DBusMessage *message,
 +                                  struct wpa_supplicant *wpa_s)
 +{
 +      int flush_age = 0;
 +
 +      if (os_strlen(dbus_message_get_signature(message)) != 0 &&
 +          !dbus_message_get_args(message, NULL,
 +                                 DBUS_TYPE_INT32, &flush_age,
 +                                 DBUS_TYPE_INVALID)) {
 +              return wpas_dbus_new_invalid_opts_error(message, NULL);
 +      }
 +
 +      if (flush_age == 0)
 +              wpa_bss_flush(wpa_s);
 +      else
 +              wpa_bss_flush_by_age(wpa_s, flush_age);
 +
 +      return wpas_dbus_new_success_reply(message);
 +}
index 7f627fdd6146fa38348de74408e1c8be667a85cf,0000000000000000000000000000000000000000..01a8c2ccb00b74b6432f0151218767b56ee84ce9
mode 100644,000000..100644
--- /dev/null
@@@ -1,497 -1,0 +1,506 @@@
 +# Example wpa_supplicant 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
 +# lines must be commented out complete, if they are not to be included, i.e.,
 +# 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
 +# to override previous values of the variables.
 +
 +
 +# 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
 +
 +# 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
 +
 +# 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
 +#LIBS += -L/usr/local/lib
 +#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 wired Ethernet drivers
 +CONFIG_DRIVER_WIRED=y
 +
 +# Driver interface for the Broadcom RoboSwitch family
 +#CONFIG_DRIVER_ROBOSWITCH=y
 +
 +# Driver interface for no driver (e.g., WPS ER only)
 +#CONFIG_DRIVER_NONE=y
 +
 +# Solaris libraries
 +#LIBS += -lsocket -ldlpi -lnsl
 +#LIBS_c += -lsocket
 +
 +# Enable IEEE 802.1X Supplicant (automatically included if any EAP method is
 +# included)
 +CONFIG_IEEE8021X_EAPOL=y
 +
 +# EAP-MD5
 +CONFIG_EAP_MD5=y
 +
 +# EAP-MSCHAPv2
 +CONFIG_EAP_MSCHAPV2=y
 +
 +# EAP-TLS
 +CONFIG_EAP_TLS=y
 +
 +# EAL-PEAP
 +CONFIG_EAP_PEAP=y
 +
 +# EAP-TTLS
 +CONFIG_EAP_TTLS=y
 +
 +# EAP-FAST
 +# 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
 +
 +# EAP-OTP
 +CONFIG_EAP_OTP=y
 +
 +# EAP-SIM (enable CONFIG_PCSC, if EAP-SIM is used)
 +#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
 +
 +# LEAP
 +CONFIG_EAP_LEAP=y
 +
 +# EAP-AKA (enable CONFIG_PCSC, if EAP-AKA is used)
 +#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
 +
 +# Enable USIM simulator (Milenage) for EAP-AKA
 +#CONFIG_USIM_SIMULATOR=y
 +
 +# EAP-SAKE
 +#CONFIG_EAP_SAKE=y
 +
 +# EAP-GPSK
 +#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
 +
 +# Wi-Fi Protected Setup (WPS)
 +#CONFIG_WPS=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
 +
 +# 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
 +
 +# 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.
 +CONFIG_CTRL_IFACE=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
 +
 +# 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
 +
 +# 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
 +
 +# Remove WPA support, e.g., for wired-only IEEE 802.1X supplicant, to save
 +# 35-50 kB in code size.
 +#CONFIG_NO_WPA=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
 +
 +# 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
 +
 +# 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 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 configuration blobs to reduce code size by about 1.5 kB.
 +#CONFIG_NO_CONFIG_BLOBS=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
 +
 +# Select wrapper for operating system and C library specific functions
 +# unix = UNIX/POSIX like systems (default)
 +# win32 = Windows systems
 +# none = Empty template
 +#CONFIG_OS=unix
 +
 +# Select event loop implementation
 +# eloop = select() loop (default)
 +# eloop_win = Windows events and WaitForMultipleObject() loop
 +#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
 +# freebsd = FreeBSD libpcap
 +# winpcap = WinPcap with receive thread
 +# ndis = Windows NDISUIO (note: requires CONFIG_USE_NDISUIO=y)
 +# none = Empty template
 +#CONFIG_L2_PACKET=linux
 +
 +# PeerKey handshake for Station to Station Link (IEEE 802.11e DLS)
 +CONFIG_PEERKEY=y
 +
 +# IEEE 802.11w (management frame protection), also known as PMF
 +# Driver support is also needed for IEEE 802.11w.
 +#CONFIG_IEEE80211W=y
 +
 +# Select TLS implementation
 +# openssl = OpenSSL (default)
 +# gnutls = GnuTLS
 +# internal = Internal TLSv1 implementation (experimental)
 +# none = Empty template
 +#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)
 +#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)
 +#CONFIG_TLSV12=y
 +
 +# If CONFIG_TLS=internal is used, additional library and include paths are
 +# needed for LibTomMath. Alternatively, an integrated, minimal version of
 +# LibTomMath can be used. See beginning of libtommath.c for details on benefits
 +# and drawbacks of this option.
 +#CONFIG_INTERNAL_LIBTOMMATH=y
 +#ifndef CONFIG_INTERNAL_LIBTOMMATH
 +#LTM_PATH=/usr/src/libtommath-0.39
 +#CFLAGS += -I$(LTM_PATH)
 +#LIBS += -L$(LTM_PATH)
 +#LIBS_p += -L$(LTM_PATH)
 +#endif
 +# At the cost of about 4 kB of additional binary size, the internal LibTomMath
 +# can be configured to include faster routines for exptmod, sqr, and div to
 +# 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.
 +#
 +# 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
 +
 +# IEEE Std 802.11r-2008 (Fast BSS Transition)
 +#CONFIG_IEEE80211R=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
 +
 +# 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
 +
 +# 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, uncomment these.
 +#LIBS += -lexecinfo
 +#LIBS_p += -lexecinfo
 +#LIBS_c += -lexecinfo
 +
 +# 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, uncomment 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
 +
 +# 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
 +
 +# 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
++
++# Enable Fast Session Transfer (FST)
++#CONFIG_FST=y
++
++# Enable CLI commands for FST testing
++#CONFIG_FST_TEST=y
++
++# OS X builds. This is only for building eapol_test.
++#CONFIG_OSX=y
index 65b430d4a6c0cc87f83009864412feb936b0c296,0000000000000000000000000000000000000000..73768c756f0a31704524be72e0a1d633f19f44f3
mode 100644,000000..100644
--- /dev/null
@@@ -1,893 -1,0 +1,915 @@@
-                                   const u8 *data, size_t data_len, int noack)
 +/*
 + * wpa_supplicant - Internal driver interface wrappers
 + * 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.
 + */
 +
 +#ifndef DRIVER_I_H
 +#define DRIVER_I_H
 +
 +#include "drivers/driver.h"
 +
 +/* driver_ops */
 +static inline void * wpa_drv_init(struct wpa_supplicant *wpa_s,
 +                                const char *ifname)
 +{
 +      if (wpa_s->driver->init2)
 +              return wpa_s->driver->init2(wpa_s, ifname,
 +                                          wpa_s->global_drv_priv);
 +      if (wpa_s->driver->init) {
 +              return wpa_s->driver->init(wpa_s, ifname);
 +      }
 +      return NULL;
 +}
 +
 +static inline void wpa_drv_deinit(struct wpa_supplicant *wpa_s)
 +{
 +      if (wpa_s->driver->deinit)
 +              wpa_s->driver->deinit(wpa_s->drv_priv);
 +}
 +
 +static inline int wpa_drv_set_param(struct wpa_supplicant *wpa_s,
 +                                  const char *param)
 +{
 +      if (wpa_s->driver->set_param)
 +              return wpa_s->driver->set_param(wpa_s->drv_priv, param);
 +      return 0;
 +}
 +
 +static inline int wpa_drv_set_countermeasures(struct wpa_supplicant *wpa_s,
 +                                            int enabled)
 +{
 +      if (wpa_s->driver->set_countermeasures) {
 +              return wpa_s->driver->set_countermeasures(wpa_s->drv_priv,
 +                                                        enabled);
 +      }
 +      return -1;
 +}
 +
 +static inline int wpa_drv_authenticate(struct wpa_supplicant *wpa_s,
 +                                     struct wpa_driver_auth_params *params)
 +{
 +      if (wpa_s->driver->authenticate)
 +              return wpa_s->driver->authenticate(wpa_s->drv_priv, params);
 +      return -1;
 +}
 +
 +static inline int wpa_drv_associate(struct wpa_supplicant *wpa_s,
 +                                  struct wpa_driver_associate_params *params)
 +{
 +      if (wpa_s->driver->associate) {
 +              return wpa_s->driver->associate(wpa_s->drv_priv, params);
 +      }
 +      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;
 +}
 +
 +static inline int wpa_drv_sched_scan(struct wpa_supplicant *wpa_s,
 +                                   struct wpa_driver_scan_params *params,
 +                                   u32 interval)
 +{
 +      if (wpa_s->driver->sched_scan)
 +              return wpa_s->driver->sched_scan(wpa_s->drv_priv,
 +                                               params, interval);
 +      return -1;
 +}
 +
 +static inline int wpa_drv_stop_sched_scan(struct wpa_supplicant *wpa_s)
 +{
 +      if (wpa_s->driver->stop_sched_scan)
 +              return wpa_s->driver->stop_sched_scan(wpa_s->drv_priv);
 +      return -1;
 +}
 +
 +static inline struct wpa_scan_results * wpa_drv_get_scan_results2(
 +      struct wpa_supplicant *wpa_s)
 +{
 +      if (wpa_s->driver->get_scan_results2)
 +              return wpa_s->driver->get_scan_results2(wpa_s->drv_priv);
 +      return NULL;
 +}
 +
 +static inline int wpa_drv_get_bssid(struct wpa_supplicant *wpa_s, u8 *bssid)
 +{
 +      if (wpa_s->driver->get_bssid) {
 +              return wpa_s->driver->get_bssid(wpa_s->drv_priv, bssid);
 +      }
 +      return -1;
 +}
 +
 +static inline int wpa_drv_get_ssid(struct wpa_supplicant *wpa_s, u8 *ssid)
 +{
 +      if (wpa_s->driver->get_ssid) {
 +              return wpa_s->driver->get_ssid(wpa_s->drv_priv, ssid);
 +      }
 +      return -1;
 +}
 +
 +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)
 +{
 +      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) {
 +              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);
 +      }
 +      return -1;
 +}
 +
 +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,
 +                                               wpa_s->own_addr, addr,
 +                                               reason_code);
 +      }
 +      return -1;
 +}
 +
 +static inline int wpa_drv_deauthenticate(struct wpa_supplicant *wpa_s,
 +                                       const u8 *addr, int reason_code)
 +{
 +      if (wpa_s->driver->deauthenticate) {
 +              return wpa_s->driver->deauthenticate(wpa_s->drv_priv, addr,
 +                                                   reason_code);
 +      }
 +      return -1;
 +}
 +
 +static inline int wpa_drv_add_pmkid(struct wpa_supplicant *wpa_s,
 +                                  const u8 *bssid, const u8 *pmkid)
 +{
 +      if (wpa_s->driver->add_pmkid) {
 +              return wpa_s->driver->add_pmkid(wpa_s->drv_priv, bssid, pmkid);
 +      }
 +      return -1;
 +}
 +
 +static inline int wpa_drv_remove_pmkid(struct wpa_supplicant *wpa_s,
 +                                     const u8 *bssid, const u8 *pmkid)
 +{
 +      if (wpa_s->driver->remove_pmkid) {
 +              return wpa_s->driver->remove_pmkid(wpa_s->drv_priv, bssid,
 +                                                 pmkid);
 +      }
 +      return -1;
 +}
 +
 +static inline int wpa_drv_flush_pmkid(struct wpa_supplicant *wpa_s)
 +{
 +      if (wpa_s->driver->flush_pmkid) {
 +              return wpa_s->driver->flush_pmkid(wpa_s->drv_priv);
 +      }
 +      return -1;
 +}
 +
 +static inline int wpa_drv_get_capa(struct wpa_supplicant *wpa_s,
 +                                 struct wpa_driver_capa *capa)
 +{
 +      if (wpa_s->driver->get_capa) {
 +              return wpa_s->driver->get_capa(wpa_s->drv_priv, capa);
 +      }
 +      return -1;
 +}
 +
 +static inline void wpa_drv_poll(struct wpa_supplicant *wpa_s)
 +{
 +      if (wpa_s->driver->poll) {
 +              wpa_s->driver->poll(wpa_s->drv_priv);
 +      }
 +}
 +
 +static inline const char * wpa_drv_get_ifname(struct wpa_supplicant *wpa_s)
 +{
 +      if (wpa_s->driver->get_ifname) {
 +              return wpa_s->driver->get_ifname(wpa_s->drv_priv);
 +      }
 +      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) {
 +              return wpa_s->driver->get_mac_addr(wpa_s->drv_priv);
 +      }
 +      return NULL;
 +}
 +
 +static inline int wpa_drv_set_operstate(struct wpa_supplicant *wpa_s,
 +                                      int state)
 +{
 +      if (wpa_s->driver->set_operstate)
 +              return wpa_s->driver->set_operstate(wpa_s->drv_priv, state);
 +      return 0;
 +}
 +
 +static inline int wpa_drv_mlme_setprotection(struct wpa_supplicant *wpa_s,
 +                                           const u8 *addr, int protect_type,
 +                                           int key_type)
 +{
 +      if (wpa_s->driver->mlme_setprotection)
 +              return wpa_s->driver->mlme_setprotection(wpa_s->drv_priv, addr,
 +                                                       protect_type,
 +                                                       key_type);
 +      return 0;
 +}
 +
 +static inline struct hostapd_hw_modes *
 +wpa_drv_get_hw_feature_data(struct wpa_supplicant *wpa_s, u16 *num_modes,
 +                          u16 *flags)
 +{
 +      if (wpa_s->driver->get_hw_feature_data)
 +              return wpa_s->driver->get_hw_feature_data(wpa_s->drv_priv,
 +                                                        num_modes, flags);
 +      return NULL;
 +}
 +
 +static inline int wpa_drv_set_country(struct wpa_supplicant *wpa_s,
 +                                    const char *alpha2)
 +{
 +      if (wpa_s->driver->set_country)
 +              return wpa_s->driver->set_country(wpa_s->drv_priv, alpha2);
 +      return 0;
 +}
 +
 +static inline int wpa_drv_send_mlme(struct wpa_supplicant *wpa_s,
-                                               data, data_len, noack);
++                                  const u8 *data, size_t data_len, int noack,
++                                  unsigned int freq)
 +{
 +      if (wpa_s->driver->send_mlme)
 +              return wpa_s->driver->send_mlme(wpa_s->drv_priv,
- static inline int wpa_drv_shared_freq(struct wpa_supplicant *wpa_s)
- {
-       if (!wpa_s->driver->shared_freq)
-               return -1;
-       return wpa_s->driver->shared_freq(wpa_s->drv_priv);
- }
++                                              data, data_len, noack,
++                                              freq);
 +      return -1;
 +}
 +
 +static inline int wpa_drv_update_ft_ies(struct wpa_supplicant *wpa_s,
 +                                      const u8 *md,
 +                                      const u8 *ies, size_t ies_len)
 +{
 +      if (wpa_s->driver->update_ft_ies)
 +              return wpa_s->driver->update_ft_ies(wpa_s->drv_priv, md,
 +                                                  ies, ies_len);
 +      return -1;
 +}
 +
 +static inline int wpa_drv_set_ap(struct wpa_supplicant *wpa_s,
 +                               struct wpa_driver_ap_params *params)
 +{
 +      if (wpa_s->driver->set_ap)
 +              return wpa_s->driver->set_ap(wpa_s->drv_priv, params);
 +      return -1;
 +}
 +
 +static inline int wpa_drv_sta_add(struct wpa_supplicant *wpa_s,
 +                                struct hostapd_sta_add_params *params)
 +{
 +      if (wpa_s->driver->sta_add)
 +              return wpa_s->driver->sta_add(wpa_s->drv_priv, params);
 +      return -1;
 +}
 +
 +static inline int wpa_drv_sta_remove(struct wpa_supplicant *wpa_s,
 +                                   const u8 *addr)
 +{
 +      if (wpa_s->driver->sta_remove)
 +              return wpa_s->driver->sta_remove(wpa_s->drv_priv, addr);
 +      return -1;
 +}
 +
 +static inline int wpa_drv_hapd_send_eapol(struct wpa_supplicant *wpa_s,
 +                                        const u8 *addr, const u8 *data,
 +                                        size_t data_len, int encrypt,
 +                                        const u8 *own_addr, u32 flags)
 +{
 +      if (wpa_s->driver->hapd_send_eapol)
 +              return wpa_s->driver->hapd_send_eapol(wpa_s->drv_priv, addr,
 +                                                    data, data_len, encrypt,
 +                                                    own_addr, flags);
 +      return -1;
 +}
 +
 +static inline int wpa_drv_sta_set_flags(struct wpa_supplicant *wpa_s,
 +                                      const u8 *addr, int total_flags,
 +                                      int flags_or, int flags_and)
 +{
 +      if (wpa_s->driver->sta_set_flags)
 +              return wpa_s->driver->sta_set_flags(wpa_s->drv_priv, addr,
 +                                                  total_flags, flags_or,
 +                                                  flags_and);
 +      return -1;
 +}
 +
 +static inline int wpa_drv_set_supp_port(struct wpa_supplicant *wpa_s,
 +                                      int authorized)
 +{
 +      if (wpa_s->driver->set_supp_port) {
 +              return wpa_s->driver->set_supp_port(wpa_s->drv_priv,
 +                                                  authorized);
 +      }
 +      return 0;
 +}
 +
 +static inline int wpa_drv_send_action(struct wpa_supplicant *wpa_s,
 +                                    unsigned int freq,
 +                                    unsigned int wait,
 +                                    const u8 *dst, const u8 *src,
 +                                    const u8 *bssid,
 +                                    const u8 *data, size_t data_len,
 +                                    int no_cck)
 +{
 +      if (wpa_s->driver->send_action)
 +              return wpa_s->driver->send_action(wpa_s->drv_priv, freq,
 +                                                wait, dst, src, bssid,
 +                                                data, data_len, no_cck);
 +      return -1;
 +}
 +
 +static inline void wpa_drv_send_action_cancel_wait(struct wpa_supplicant *wpa_s)
 +{
 +      if (wpa_s->driver->send_action_cancel_wait)
 +              wpa_s->driver->send_action_cancel_wait(wpa_s->drv_priv);
 +}
 +
 +static inline int wpa_drv_set_freq(struct wpa_supplicant *wpa_s,
 +                                 struct hostapd_freq_params *freq)
 +{
 +      if (wpa_s->driver->set_freq)
 +              return wpa_s->driver->set_freq(wpa_s->drv_priv, freq);
 +      return -1;
 +}
 +
 +static inline int wpa_drv_if_add(struct wpa_supplicant *wpa_s,
 +                               enum wpa_driver_if_type type,
 +                               const char *ifname, const u8 *addr,
 +                               void *bss_ctx, char *force_ifname,
 +                               u8 *if_addr, const char *bridge)
 +{
 +      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, 0);
 +      return -1;
 +}
 +
 +static inline int wpa_drv_if_remove(struct wpa_supplicant *wpa_s,
 +                                  enum wpa_driver_if_type type,
 +                                  const char *ifname)
 +{
 +      if (wpa_s->driver->if_remove)
 +              return wpa_s->driver->if_remove(wpa_s->drv_priv, type, ifname);
 +      return -1;
 +}
 +
 +static inline int wpa_drv_remain_on_channel(struct wpa_supplicant *wpa_s,
 +                                          unsigned int freq,
 +                                          unsigned int duration)
 +{
 +      if (wpa_s->driver->remain_on_channel)
 +              return wpa_s->driver->remain_on_channel(wpa_s->drv_priv, freq,
 +                                                      duration);
 +      return -1;
 +}
 +
 +static inline int wpa_drv_cancel_remain_on_channel(
 +      struct wpa_supplicant *wpa_s)
 +{
 +      if (wpa_s->driver->cancel_remain_on_channel)
 +              return wpa_s->driver->cancel_remain_on_channel(
 +                      wpa_s->drv_priv);
 +      return -1;
 +}
 +
 +static inline int wpa_drv_probe_req_report(struct wpa_supplicant *wpa_s,
 +                                         int report)
 +{
 +      if (wpa_s->driver->probe_req_report)
 +              return wpa_s->driver->probe_req_report(wpa_s->drv_priv,
 +                                                     report);
 +      return -1;
 +}
 +
 +static inline int wpa_drv_deinit_ap(struct wpa_supplicant *wpa_s)
 +{
 +      if (wpa_s->driver->deinit_ap)
 +              return wpa_s->driver->deinit_ap(wpa_s->drv_priv);
 +      return 0;
 +}
 +
 +static inline int wpa_drv_deinit_p2p_cli(struct wpa_supplicant *wpa_s)
 +{
 +      if (wpa_s->driver->deinit_p2p_cli)
 +              return wpa_s->driver->deinit_p2p_cli(wpa_s->drv_priv);
 +      return 0;
 +}
 +
 +static inline void wpa_drv_suspend(struct wpa_supplicant *wpa_s)
 +{
 +      if (wpa_s->driver->suspend)
 +              wpa_s->driver->suspend(wpa_s->drv_priv);
 +}
 +
 +static inline void wpa_drv_resume(struct wpa_supplicant *wpa_s)
 +{
 +      if (wpa_s->driver->resume)
 +              wpa_s->driver->resume(wpa_s->drv_priv);
 +}
 +
 +static inline int wpa_drv_signal_monitor(struct wpa_supplicant *wpa_s,
 +                                       int threshold, int hysteresis)
 +{
 +      if (wpa_s->driver->signal_monitor)
 +              return wpa_s->driver->signal_monitor(wpa_s->drv_priv,
 +                                                   threshold, hysteresis);
 +      return -1;
 +}
 +
 +static inline int wpa_drv_signal_poll(struct wpa_supplicant *wpa_s,
 +                                    struct wpa_signal_info *si)
 +{
 +      if (wpa_s->driver->signal_poll)
 +              return wpa_s->driver->signal_poll(wpa_s->drv_priv, si);
 +      return -1;
 +}
 +
 +static inline int wpa_drv_pktcnt_poll(struct wpa_supplicant *wpa_s,
 +                                    struct hostap_sta_driver_data *sta)
 +{
 +      if (wpa_s->driver->read_sta_data)
 +              return wpa_s->driver->read_sta_data(wpa_s->drv_priv, sta,
 +                                                  wpa_s->bssid);
 +      return -1;
 +}
 +
 +static inline int wpa_drv_set_ap_wps_ie(struct wpa_supplicant *wpa_s,
 +                                      const struct wpabuf *beacon,
 +                                      const struct wpabuf *proberesp,
 +                                      const struct wpabuf *assocresp)
 +{
 +      if (!wpa_s->driver->set_ap_wps_ie)
 +              return -1;
 +      return wpa_s->driver->set_ap_wps_ie(wpa_s->drv_priv, beacon,
 +                                          proberesp, assocresp);
 +}
 +
 +static inline int wpa_drv_get_noa(struct wpa_supplicant *wpa_s,
 +                                u8 *buf, size_t buf_len)
 +{
 +      if (!wpa_s->driver->get_noa)
 +              return -1;
 +      return wpa_s->driver->get_noa(wpa_s->drv_priv, buf, buf_len);
 +}
 +
 +static inline int wpa_drv_set_p2p_powersave(struct wpa_supplicant *wpa_s,
 +                                          int legacy_ps, int opp_ps,
 +                                          int ctwindow)
 +{
 +      if (!wpa_s->driver->set_p2p_powersave)
 +              return -1;
 +      return wpa_s->driver->set_p2p_powersave(wpa_s->drv_priv, legacy_ps,
 +                                              opp_ps, ctwindow);
 +}
 +
 +static inline int wpa_drv_ampdu(struct wpa_supplicant *wpa_s, int ampdu)
 +{
 +      if (!wpa_s->driver->ampdu)
 +              return -1;
 +      return wpa_s->driver->ampdu(wpa_s->drv_priv, ampdu);
 +}
 +
 +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->radio_disable)
 +              return -1;
 +      return wpa_s->driver->radio_disable(wpa_s->drv_priv, disabled);
 +}
 +
 +static inline int wpa_drv_switch_channel(struct wpa_supplicant *wpa_s,
 +                                       struct csa_settings *settings)
 +{
 +      if (!wpa_s->driver->switch_channel)
 +              return -1;
 +      return wpa_s->driver->switch_channel(wpa_s->drv_priv, settings);
 +}
 +
 +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->add_tx_ts)
 +              return -1;
 +      return wpa_s->driver->add_tx_ts(wpa_s->drv_priv, tsid, address,
 +                                      user_priority, admitted_time);
 +}
 +
 +static inline int wpa_drv_del_ts(struct wpa_supplicant *wpa_s, u8 tid,
 +                               const u8 *address)
 +{
 +      if (!wpa_s->driver->del_tx_ts)
 +              return -1;
 +      return wpa_s->driver->del_tx_ts(wpa_s->drv_priv, tid, address);
 +}
 +
 +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->tdls_enable_channel_switch)
 +              return -1;
 +      return wpa_s->driver->tdls_enable_channel_switch(wpa_s->drv_priv, addr,
 +                                                       oper_class,
 +                                                       freq_params);
 +}
 +
 +static inline int
 +wpa_drv_tdls_disable_channel_switch(struct wpa_supplicant *wpa_s,
 +                                  const u8 *addr)
 +{
 +      if (!wpa_s->driver->tdls_disable_channel_switch)
 +              return -1;
 +      return wpa_s->driver->tdls_disable_channel_switch(wpa_s->drv_priv,
 +                                                        addr);
 +}
 +
 +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->wnm_oper)
 +              return -1;
 +      return wpa_s->driver->wnm_oper(wpa_s->drv_priv, oper, peer, buf,
 +                                     buf_len);
 +}
 +
 +static inline int wpa_drv_status(struct wpa_supplicant *wpa_s,
 +                               char *buf, size_t buflen)
 +{
 +      if (!wpa_s->driver->status)
 +              return -1;
 +      return wpa_s->driver->status(wpa_s->drv_priv, buf, buflen);
 +}
 +
 +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->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_wowlan(struct wpa_supplicant *wpa_s,
 +                               const struct wowlan_triggers *triggers)
 +{
 +      if (!wpa_s->driver->set_wowlan)
 +              return -1;
 +      return wpa_s->driver->set_wowlan(wpa_s->drv_priv, triggers);
 +}
 +
 +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->vendor_cmd)
 +              return -1;
 +      return wpa_s->driver->vendor_cmd(wpa_s->drv_priv, vendor_id, subcmd,
 +                                       data, data_len, buf);
 +}
 +
 +static inline int wpa_drv_roaming(struct wpa_supplicant *wpa_s, int allowed,
 +                                const u8 *bssid)
 +{
 +      if (!wpa_s->driver->roaming)
 +              return -1;
 +      return wpa_s->driver->roaming(wpa_s->drv_priv, allowed, bssid);
 +}
 +
 +static inline int wpa_drv_set_mac_addr(struct wpa_supplicant *wpa_s,
 +                                     const u8 *addr)
 +{
 +      if (!wpa_s->driver->set_mac_addr)
 +              return -1;
 +      return wpa_s->driver->set_mac_addr(wpa_s->drv_priv, addr);
 +}
 +
 +
 +#ifdef CONFIG_MACSEC
 +
 +static inline int wpa_drv_macsec_init(struct wpa_supplicant *wpa_s,
 +                                    struct macsec_init_params *params)
 +{
 +      if (!wpa_s->driver->macsec_init)
 +              return -1;
 +      return wpa_s->driver->macsec_init(wpa_s->drv_priv, params);
 +}
 +
 +static inline int wpa_drv_macsec_deinit(struct wpa_supplicant *wpa_s)
 +{
 +      if (!wpa_s->driver->macsec_deinit)
 +              return -1;
 +      return wpa_s->driver->macsec_deinit(wpa_s->drv_priv);
 +}
 +
 +static inline int wpa_drv_enable_protect_frames(struct wpa_supplicant *wpa_s,
 +                                              Boolean enabled)
 +{
 +      if (!wpa_s->driver->enable_protect_frames)
 +              return -1;
 +      return wpa_s->driver->enable_protect_frames(wpa_s->drv_priv, enabled);
 +}
 +
 +static inline int wpa_drv_set_replay_protect(struct wpa_supplicant *wpa_s,
 +                                           Boolean enabled, u32 window)
 +{
 +      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_set_current_cipher_suite(struct wpa_supplicant *wpa_s,
 +                                                 const u8 *cs, size_t cs_len)
 +{
 +      if (!wpa_s->driver->set_current_cipher_suite)
 +              return -1;
 +      return wpa_s->driver->set_current_cipher_suite(wpa_s->drv_priv, cs,
 +                                                     cs_len);
 +}
 +
 +static inline int wpa_drv_enable_controlled_port(struct wpa_supplicant *wpa_s,
 +                                               Boolean enabled)
 +{
 +      if (!wpa_s->driver->enable_controlled_port)
 +              return -1;
 +      return wpa_s->driver->enable_controlled_port(wpa_s->drv_priv, enabled);
 +}
 +
 +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->get_receive_lowest_pn)
 +              return -1;
 +      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 */
 +
++static inline int wpa_drv_setband(struct wpa_supplicant *wpa_s,
++                                enum set_band band)
++{
++      if (!wpa_s->driver->set_band)
++              return -1;
++      return wpa_s->driver->set_band(wpa_s->drv_priv, band);
++}
++
++static inline int wpa_drv_get_pref_freq_list(struct wpa_supplicant *wpa_s,
++                                           enum wpa_driver_if_type if_type,
++                                           unsigned int *num,
++                                           unsigned int *freq_list)
++{
++      if (!wpa_s->driver->get_pref_freq_list)
++              return -1;
++      return wpa_s->driver->get_pref_freq_list(wpa_s->drv_priv, if_type,
++                                               num, freq_list);
++}
++
++static inline int wpa_drv_set_prob_oper_freq(struct wpa_supplicant *wpa_s,
++                                           unsigned int freq)
++{
++      if (!wpa_s->driver->set_prob_oper_freq)
++              return 0;
++      return wpa_s->driver->set_prob_oper_freq(wpa_s->drv_priv, freq);
++}
++
 +#endif /* DRIVER_I_H */
index 9b7af30550bd4ecbcb0d327d9bdc07528c3c551b,0000000000000000000000000000000000000000..dce7d1fadd44e25ac1fc4edb040acb6464150027
mode 100644,000000..100644
--- /dev/null
@@@ -1,1448 -1,0 +1,1547 @@@
- struct wpa_driver_ops *wpa_drivers[] = { NULL };
 +/*
 + * WPA Supplicant - test code
 + * 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.
 + *
 + * IEEE 802.1X Supplicant test code (to be used in place of wpa_supplicant.c.
 + * Not used in production version.
 + */
 +
 +#include "includes.h"
 +#include <assert.h>
 +
 +#include "common.h"
 +#include "utils/ext_password.h"
 +#include "config.h"
 +#include "eapol_supp/eapol_supp_sm.h"
 +#include "eap_peer/eap.h"
 +#include "eap_server/eap_methods.h"
 +#include "eloop.h"
 +#include "utils/base64.h"
 +#include "rsn_supp/wpa.h"
 +#include "wpa_supplicant_i.h"
 +#include "radius/radius.h"
 +#include "radius/radius_client.h"
 +#include "common/wpa_ctrl.h"
 +#include "ctrl_iface.h"
 +#include "pcsc_funcs.h"
 +#include "wpas_glue.h"
 +
 +
-               eloop_terminate();
++const struct wpa_driver_ops *const wpa_drivers[] = { NULL };
 +
 +
 +struct extra_radius_attr {
 +      u8 type;
 +      char syntax;
 +      char *data;
 +      struct extra_radius_attr *next;
 +};
 +
 +struct eapol_test_data {
 +      struct wpa_supplicant *wpa_s;
 +
 +      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;
 +      struct in_addr own_ip_addr;
 +      struct radius_client_data *radius;
 +      struct hostapd_radius_servers *radius_conf;
 +
 +       /* last received EAP Response from Authentication Server */
 +      struct wpabuf *last_eap_radius;
 +
 +      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;
 +
 +      u8 *eap_identity;
 +      size_t eap_identity_len;
 +
 +      char *connect_info;
 +      u8 own_addr[ETH_ALEN];
 +      struct extra_radius_attr *extra_attrs;
 +
 +      FILE *server_cert_file;
 +
 +      const char *pcsc_reader;
 +      const char *pcsc_pin;
++
++      unsigned int ctrl_iface:1;
++      unsigned int id_req_sent:1;
 +};
 +
 +static struct eapol_test_data eapol_test;
 +
 +
 +static void send_eap_request_identity(void *eloop_ctx, void *timeout_ctx);
 +
 +
 +static void hostapd_logger_cb(void *ctx, const u8 *addr, unsigned int module,
 +                            int level, const char *txt, size_t len)
 +{
 +      if (addr)
 +              wpa_printf(MSG_DEBUG, "STA " MACSTR ": %s\n",
 +                         MAC2STR(addr), txt);
 +      else
 +              wpa_printf(MSG_DEBUG, "%s", txt);
 +}
 +
 +
 +static int add_extra_attr(struct radius_msg *msg,
 +                        struct extra_radius_attr *attr)
 +{
 +      size_t len;
 +      char *pos;
 +      u32 val;
 +      char buf[RADIUS_MAX_ATTR_LEN + 1];
 +
 +      switch (attr->syntax) {
 +      case 's':
 +              os_snprintf(buf, sizeof(buf), "%s", attr->data);
 +              len = os_strlen(buf);
 +              break;
 +      case 'n':
 +              buf[0] = '\0';
 +              len = 1;
 +              break;
 +      case 'x':
 +              pos = attr->data;
 +              if (pos[0] == '0' && pos[1] == 'x')
 +                      pos += 2;
 +              len = os_strlen(pos);
 +              if ((len & 1) || (len / 2) > RADIUS_MAX_ATTR_LEN) {
 +                      printf("Invalid extra attribute hexstring\n");
 +                      return -1;
 +              }
 +              len /= 2;
 +              if (hexstr2bin(pos, (u8 *) buf, len) < 0) {
 +                      printf("Invalid extra attribute hexstring\n");
 +                      return -1;
 +              }
 +              break;
 +      case 'd':
 +              val = htonl(atoi(attr->data));
 +              os_memcpy(buf, &val, 4);
 +              len = 4;
 +              break;
 +      default:
 +              printf("Incorrect extra attribute syntax specification\n");
 +              return -1;
 +      }
 +
 +      if (!radius_msg_add_attr(msg, attr->type, (u8 *) buf, len)) {
 +              printf("Could not add attribute %d\n", attr->type);
 +              return -1;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +static int add_extra_attrs(struct radius_msg *msg,
 +                         struct extra_radius_attr *attrs)
 +{
 +      struct extra_radius_attr *p;
 +      for (p = attrs; p; p = p->next) {
 +              if (add_extra_attr(msg, p) < 0)
 +                      return -1;
 +      }
 +      return 0;
 +}
 +
 +
 +static struct extra_radius_attr *
 +find_extra_attr(struct extra_radius_attr *attrs, u8 type)
 +{
 +      struct extra_radius_attr *p;
 +      for (p = attrs; p; p = p->next) {
 +              if (p->type == type)
 +                      return p;
 +      }
 +      return NULL;
 +}
 +
 +
 +static void ieee802_1x_encapsulate_radius(struct eapol_test_data *e,
 +                                        const u8 *eap, size_t len)
 +{
 +      struct radius_msg *msg;
 +      char buf[RADIUS_MAX_ATTR_LEN + 1];
 +      const struct eap_hdr *hdr;
 +      const u8 *pos;
 +
 +      wpa_printf(MSG_DEBUG, "Encapsulating EAP message into a RADIUS "
 +                 "packet");
 +
 +      e->radius_identifier = radius_client_get_id(e->radius);
 +      msg = radius_msg_new(RADIUS_CODE_ACCESS_REQUEST,
 +                           e->radius_identifier);
 +      if (msg == NULL) {
 +              printf("Could not create net RADIUS packet\n");
 +              return;
 +      }
 +
 +      radius_msg_make_authenticator(msg, (u8 *) e, sizeof(*e));
 +
 +      hdr = (const struct eap_hdr *) eap;
 +      pos = (const u8 *) (hdr + 1);
 +      if (len > sizeof(*hdr) && hdr->code == EAP_CODE_RESPONSE &&
 +          pos[0] == EAP_TYPE_IDENTITY) {
 +              pos++;
 +              os_free(e->eap_identity);
 +              e->eap_identity_len = len - sizeof(*hdr) - 1;
 +              e->eap_identity = os_malloc(e->eap_identity_len);
 +              if (e->eap_identity) {
 +                      os_memcpy(e->eap_identity, pos, e->eap_identity_len);
 +                      wpa_hexdump(MSG_DEBUG, "Learned identity from "
 +                                  "EAP-Response-Identity",
 +                                  e->eap_identity, e->eap_identity_len);
 +              }
 +      }
 +
 +      if (e->eap_identity &&
 +          !radius_msg_add_attr(msg, RADIUS_ATTR_USER_NAME,
 +                               e->eap_identity, e->eap_identity_len)) {
 +              printf("Could not add User-Name\n");
 +              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)) {
 +              printf("Could not add NAS-IP-Address\n");
 +              goto fail;
 +      }
 +
 +      os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT,
 +                  MAC2STR(e->wpa_s->own_addr));
 +      if (!find_extra_attr(e->extra_attrs, RADIUS_ATTR_CALLING_STATION_ID)
 +          &&
 +          !radius_msg_add_attr(msg, RADIUS_ATTR_CALLING_STATION_ID,
 +                               (u8 *) buf, os_strlen(buf))) {
 +              printf("Could not add Calling-Station-Id\n");
 +              goto fail;
 +      }
 +
 +      /* TODO: should probably check MTU from driver config; 2304 is max for
 +       * IEEE 802.11, but use 1400 to avoid problems with too large packets
 +       */
 +      if (!find_extra_attr(e->extra_attrs, RADIUS_ATTR_FRAMED_MTU) &&
 +          !radius_msg_add_attr_int32(msg, RADIUS_ATTR_FRAMED_MTU, 1400)) {
 +              printf("Could not add Framed-MTU\n");
 +              goto fail;
 +      }
 +
 +      if (!find_extra_attr(e->extra_attrs, RADIUS_ATTR_NAS_PORT_TYPE) &&
 +          !radius_msg_add_attr_int32(msg, RADIUS_ATTR_NAS_PORT_TYPE,
 +                                     RADIUS_NAS_PORT_TYPE_IEEE_802_11)) {
 +              printf("Could not add NAS-Port-Type\n");
 +              goto fail;
 +      }
 +
 +      os_snprintf(buf, sizeof(buf), "%s", e->connect_info);
 +      if (!find_extra_attr(e->extra_attrs, RADIUS_ATTR_CONNECT_INFO) &&
 +          !radius_msg_add_attr(msg, RADIUS_ATTR_CONNECT_INFO,
 +                               (u8 *) buf, os_strlen(buf))) {
 +              printf("Could not add Connect-Info\n");
 +              goto fail;
 +      }
 +
 +      if (add_extra_attrs(msg, e->extra_attrs) < 0)
 +              goto fail;
 +
 +      if (eap && !radius_msg_add_eap(msg, eap, len)) {
 +              printf("Could not add EAP-Message\n");
 +              goto fail;
 +      }
 +
 +      /* State attribute must be copied if and only if this packet is
 +       * Access-Request reply to the previous Access-Challenge */
 +      if (e->last_recv_radius &&
 +          radius_msg_get_hdr(e->last_recv_radius)->code ==
 +          RADIUS_CODE_ACCESS_CHALLENGE) {
 +              int res = radius_msg_copy_attr(msg, e->last_recv_radius,
 +                                             RADIUS_ATTR_STATE);
 +              if (res < 0) {
 +                      printf("Could not copy State attribute from previous "
 +                             "Access-Challenge\n");
 +                      goto fail;
 +              }
 +              if (res > 0) {
 +                      wpa_printf(MSG_DEBUG, "  Copied RADIUS State "
 +                                 "Attribute");
 +              }
 +      }
 +
 +      if (radius_client_send(e->radius, msg, RADIUS_AUTH, e->wpa_s->own_addr)
 +          < 0)
 +              goto fail;
 +      return;
 +
 + fail:
 +      radius_msg_free(msg);
 +}
 +
 +
 +static int eapol_test_eapol_send(void *ctx, int type, const u8 *buf,
 +                               size_t len)
 +{
 +      printf("WPA: eapol_test_eapol_send(type=%d len=%lu)\n",
 +             type, (unsigned long) len);
 +      if (type == IEEE802_1X_TYPE_EAP_PACKET) {
 +              wpa_hexdump(MSG_DEBUG, "TX EAP -> RADIUS", buf, len);
 +              ieee802_1x_encapsulate_radius(&eapol_test, buf, len);
 +      }
 +      return 0;
 +}
 +
 +
 +static void eapol_test_set_config_blob(void *ctx,
 +                                     struct wpa_config_blob *blob)
 +{
 +      struct eapol_test_data *e = ctx;
 +      wpa_config_set_blob(e->wpa_s->conf, blob);
 +}
 +
 +
 +static const struct wpa_config_blob *
 +eapol_test_get_config_blob(void *ctx, const char *name)
 +{
 +      struct eapol_test_data *e = ctx;
 +      return wpa_config_get_blob(e->wpa_s->conf, name);
 +}
 +
 +
 +static void eapol_test_eapol_done_cb(void *ctx)
 +{
++      struct eapol_test_data *e = ctx;
++
 +      printf("WPA: EAPOL processing complete\n");
++      wpa_supplicant_cancel_auth_timeout(e->wpa_s);
++      wpa_supplicant_set_state(e->wpa_s, WPA_COMPLETED);
 +}
 +
 +
 +static void eapol_sm_reauth(void *eloop_ctx, void *timeout_ctx)
 +{
 +      struct eapol_test_data *e = eloop_ctx;
 +      printf("\n\n\n\n\neapol_test: Triggering EAP reauthentication\n\n");
 +      e->radius_access_accept_received = 0;
 +      send_eap_request_identity(e->wpa_s, NULL);
 +}
 +
 +
 +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);
 +              if (os_memcmp(pmk, e->authenticator_pmk, PMK_LEN) != 0) {
 +                      printf("WARNING: PMK mismatch\n");
 +                      wpa_hexdump(MSG_DEBUG, "PMK from AS",
 +                                  e->authenticator_pmk, PMK_LEN);
 +              } else if (e->radius_access_accept_received)
 +                      ret = 0;
 +      } else if (e->authenticator_pmk_len == 16 &&
 +                 eapol_sm_get_key(e->wpa_s->eapol, pmk, 16) == 0) {
 +              wpa_hexdump(MSG_DEBUG, "LEAP PMK from EAPOL", pmk, 16);
 +              if (os_memcmp(pmk, e->authenticator_pmk, 16) != 0) {
 +                      printf("WARNING: PMK mismatch\n");
 +                      wpa_hexdump(MSG_DEBUG, "PMK from AS",
 +                                  e->authenticator_pmk, 16);
 +              } else if (e->radius_access_accept_received)
 +                      ret = 0;
 +      } else if (e->radius_access_accept_received && e->no_mppe_keys) {
 +              /* No keying material expected */
 +              ret = 0;
 +      }
 +
 +      if (ret && !e->no_mppe_keys)
 +              e->num_mppe_mismatch++;
 +      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, enum eapol_supp_result result,
 +                      void *ctx)
 +{
 +      struct eapol_test_data *e = ctx;
 +      printf("eapol_sm_cb: result=%d\n", result);
++      e->id_req_sent = 0;
++      if (e->ctrl_iface)
++              return;
 +      e->eapol_test_num_reauths--;
 +      if (e->eapol_test_num_reauths < 0)
 +              eloop_terminate();
 +      else {
 +              eapol_test_compare_pmk(e);
 +              eloop_register_timeout(0, 100000, eapol_sm_reauth, e, NULL);
 +      }
 +}
 +
 +
 +static void eapol_test_write_cert(FILE *f, const char *subject,
 +                                const struct wpabuf *cert)
 +{
 +      unsigned char *encoded;
 +
 +      encoded = base64_encode(wpabuf_head(cert), wpabuf_len(cert), NULL);
 +      if (encoded == NULL)
 +              return;
 +      fprintf(f, "%s\n-----BEGIN CERTIFICATE-----\n%s"
 +              "-----END CERTIFICATE-----\n\n", subject, encoded);
 +      os_free(encoded);
 +}
 +
 +
 +#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)
 +{
 +      struct eapol_test_data *e = ctx;
 +
 +      wpa_msg(e->wpa_s, MSG_INFO, WPA_EVENT_EAP_PEER_CERT
 +              "depth=%d subject='%s'%s%s",
 +              depth, subject,
 +              cert_hash ? " hash=" : "",
 +              cert_hash ? cert_hash : "");
 +
 +      if (cert) {
 +              char *cert_hex;
 +              size_t len = wpabuf_len(cert) * 2 + 1;
 +              cert_hex = os_malloc(len);
 +              if (cert_hex) {
 +                      wpa_snprintf_hex(cert_hex, len, wpabuf_head(cert),
 +                                       wpabuf_len(cert));
 +                      wpa_msg_ctrl(e->wpa_s, MSG_INFO,
 +                                   WPA_EVENT_EAP_PEER_CERT
 +                                   "depth=%d subject='%s' cert=%s",
 +                                   depth, subject, cert_hex);
 +                      os_free(cert_hex);
 +              }
 +
 +              if (e->server_cert_file)
 +                      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]);
 +      }
 +}
 +
 +
 +static void eapol_test_set_anon_id(void *ctx, const u8 *id, size_t len)
 +{
 +      struct eapol_test_data *e = ctx;
 +      struct wpa_supplicant *wpa_s = e->wpa_s;
 +      char *str;
 +      int res;
 +
 +      wpa_hexdump_ascii(MSG_DEBUG, "EAP method updated anonymous_identity",
 +                        id, len);
 +
 +      if (wpa_s->current_ssid == NULL)
 +              return;
 +
 +      if (id == NULL) {
 +              if (wpa_config_set(wpa_s->current_ssid, "anonymous_identity",
 +                                 "NULL", 0) < 0)
 +                      return;
 +      } else {
 +              str = os_malloc(len * 2 + 1);
 +              if (str == NULL)
 +                      return;
 +              wpa_snprintf_hex(str, len * 2 + 1, id, len);
 +              res = wpa_config_set(wpa_s->current_ssid, "anonymous_identity",
 +                                   str, 0);
 +              os_free(str);
 +              if (res < 0)
 +                      return;
 +      }
 +}
 +
 +
++static enum wpa_states eapol_test_get_state(void *ctx)
++{
++      struct eapol_test_data *e = ctx;
++      struct wpa_supplicant *wpa_s = e->wpa_s;
++
++      return wpa_s->wpa_state;
++}
++
++
 +static int test_eapol(struct eapol_test_data *e, struct wpa_supplicant *wpa_s,
 +                    struct wpa_ssid *ssid)
 +{
 +      struct eapol_config eapol_conf;
 +      struct eapol_ctx *ctx;
++      struct wpa_sm_ctx *wctx;
 +
 +      ctx = os_zalloc(sizeof(*ctx));
 +      if (ctx == NULL) {
 +              printf("Failed to allocate EAPOL context.\n");
 +              return -1;
 +      }
 +      ctx->ctx = e;
 +      ctx->msg_ctx = wpa_s;
 +      ctx->scard_ctx = wpa_s->scard;
 +      ctx->cb = eapol_sm_cb;
 +      ctx->cb_ctx = e;
 +      ctx->eapol_send_ctx = wpa_s;
 +      ctx->preauth = 0;
 +      ctx->eapol_done_cb = eapol_test_eapol_done_cb;
 +      ctx->eapol_send = eapol_test_eapol_send;
 +      ctx->set_config_blob = eapol_test_set_config_blob;
 +      ctx->get_config_blob = eapol_test_get_config_blob;
 +      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;
 +
 +      wpa_s->eapol = eapol_sm_init(ctx);
 +      if (wpa_s->eapol == NULL) {
 +              os_free(ctx);
 +              printf("Failed to initialize EAPOL state machines.\n");
 +              return -1;
 +      }
 +
++      wpa_s->key_mgmt = WPA_KEY_MGMT_IEEE8021X_NO_WPA;
++      wctx = os_zalloc(sizeof(*wctx));
++      if (wctx == NULL) {
++              os_free(ctx);
++              return -1;
++      }
++      wctx->ctx = e;
++      wctx->msg_ctx = wpa_s;
++      wctx->get_state = eapol_test_get_state;
++      wpa_s->wpa = wpa_sm_init(wctx);
++      if (!wpa_s->wpa) {
++              os_free(ctx);
++              os_free(wctx);
++              return -1;
++      }
++
++      if (!ssid)
++              return 0;
++
 +      wpa_s->current_ssid = ssid;
 +      os_memset(&eapol_conf, 0, sizeof(eapol_conf));
 +      eapol_conf.accept_802_1x_keys = 1;
 +      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);
 +
 +
 +      eapol_sm_notify_portValid(wpa_s->eapol, FALSE);
 +      /* 802.1X::portControl = Auto */
 +      eapol_sm_notify_portEnabled(wpa_s->eapol, TRUE);
 +
 +      return 0;
 +}
 +
 +
 +static void test_eapol_clean(struct eapol_test_data *e,
 +                           struct wpa_supplicant *wpa_s)
 +{
 +      struct extra_radius_attr *p, *prev;
 +
++      wpa_sm_deinit(wpa_s->wpa);
++      wpa_s->wpa = NULL;
 +      radius_client_deinit(e->radius);
 +      wpabuf_free(e->last_eap_radius);
 +      radius_msg_free(e->last_recv_radius);
 +      e->last_recv_radius = NULL;
 +      os_free(e->eap_identity);
 +      e->eap_identity = NULL;
 +      eapol_sm_deinit(wpa_s->eapol);
 +      wpa_s->eapol = NULL;
 +      if (e->radius_conf && e->radius_conf->auth_server) {
 +              os_free(e->radius_conf->auth_server->shared_secret);
 +              os_free(e->radius_conf->auth_server);
 +      }
 +      os_free(e->radius_conf);
 +      e->radius_conf = NULL;
 +      scard_deinit(wpa_s->scard);
 +      if (wpa_s->ctrl_iface) {
 +              wpa_supplicant_ctrl_iface_deinit(wpa_s->ctrl_iface);
 +              wpa_s->ctrl_iface = NULL;
 +      }
 +
 +      ext_password_deinit(wpa_s->ext_pw);
 +      wpa_s->ext_pw = NULL;
 +
 +      wpa_config_free(wpa_s->conf);
 +
 +      p = e->extra_attrs;
 +      while (p) {
 +              prev = p;
 +              p = p->next;
 +              os_free(prev);
 +      }
 +}
 +
 +
 +static void send_eap_request_identity(void *eloop_ctx, void *timeout_ctx)
 +{
 +      struct wpa_supplicant *wpa_s = eloop_ctx;
 +      u8 buf[100], *pos;
 +      struct ieee802_1x_hdr *hdr;
 +      struct eap_hdr *eap;
 +
 +      hdr = (struct ieee802_1x_hdr *) buf;
 +      hdr->version = EAPOL_VERSION;
 +      hdr->type = IEEE802_1X_TYPE_EAP_PACKET;
 +      hdr->length = htons(5);
 +
 +      eap = (struct eap_hdr *) (hdr + 1);
 +      eap->code = EAP_CODE_REQUEST;
 +      eap->identifier = 0;
 +      eap->length = htons(5);
 +      pos = (u8 *) (eap + 1);
 +      *pos = EAP_TYPE_IDENTITY;
 +
 +      printf("Sending fake EAP-Request-Identity\n");
 +      eapol_sm_rx_eapol(wpa_s->eapol, wpa_s->bssid, buf,
 +                        sizeof(*hdr) + 5);
 +}
 +
 +
 +static void eapol_test_timeout(void *eloop_ctx, void *timeout_ctx)
 +{
 +      struct eapol_test_data *e = eloop_ctx;
 +      printf("EAPOL test timed out\n");
 +      e->auth_timed_out = 1;
 +      eloop_terminate();
 +}
 +
 +
 +static char *eap_type_text(u8 type)
 +{
 +      switch (type) {
 +      case EAP_TYPE_IDENTITY: return "Identity";
 +      case EAP_TYPE_NOTIFICATION: return "Notification";
 +      case EAP_TYPE_NAK: return "Nak";
 +      case EAP_TYPE_TLS: return "TLS";
 +      case EAP_TYPE_TTLS: return "TTLS";
 +      case EAP_TYPE_PEAP: return "PEAP";
 +      case EAP_TYPE_SIM: return "SIM";
 +      case EAP_TYPE_GTC: return "GTC";
 +      case EAP_TYPE_MD5: return "MD5";
 +      case EAP_TYPE_OTP: return "OTP";
 +      case EAP_TYPE_FAST: return "FAST";
 +      case EAP_TYPE_SAKE: return "SAKE";
 +      case EAP_TYPE_PSK: return "PSK";
 +      default: return "Unknown";
 +      }
 +}
 +
 +
 +static void ieee802_1x_decapsulate_radius(struct eapol_test_data *e)
 +{
 +      struct wpabuf *eap;
 +      const struct eap_hdr *hdr;
 +      int eap_type = -1;
 +      char buf[64];
 +      struct radius_msg *msg;
 +
 +      if (e->last_recv_radius == NULL)
 +              return;
 +
 +      msg = e->last_recv_radius;
 +
 +      eap = radius_msg_get_eap(msg);
 +      if (eap == NULL) {
 +              /* draft-aboba-radius-rfc2869bis-20.txt, Chap. 2.6.3:
 +               * RADIUS server SHOULD NOT send Access-Reject/no EAP-Message
 +               * attribute */
 +              wpa_printf(MSG_DEBUG, "could not extract "
 +                             "EAP-Message from RADIUS message");
 +              wpabuf_free(e->last_eap_radius);
 +              e->last_eap_radius = NULL;
 +              return;
 +      }
 +
 +      if (wpabuf_len(eap) < sizeof(*hdr)) {
 +              wpa_printf(MSG_DEBUG, "too short EAP packet "
 +                             "received from authentication server");
 +              wpabuf_free(eap);
 +              return;
 +      }
 +
 +      if (wpabuf_len(eap) > sizeof(*hdr))
 +              eap_type = (wpabuf_head_u8(eap))[sizeof(*hdr)];
 +
 +      hdr = wpabuf_head(eap);
 +      switch (hdr->code) {
 +      case EAP_CODE_REQUEST:
 +              os_snprintf(buf, sizeof(buf), "EAP-Request-%s (%d)",
 +                          eap_type >= 0 ? eap_type_text(eap_type) : "??",
 +                          eap_type);
 +              break;
 +      case EAP_CODE_RESPONSE:
 +              os_snprintf(buf, sizeof(buf), "EAP Response-%s (%d)",
 +                          eap_type >= 0 ? eap_type_text(eap_type) : "??",
 +                          eap_type);
 +              break;
 +      case EAP_CODE_SUCCESS:
 +              os_strlcpy(buf, "EAP Success", sizeof(buf));
 +              /* LEAP uses EAP Success within an authentication, so must not
 +               * stop here with eloop_terminate(); */
 +              break;
 +      case EAP_CODE_FAILURE:
 +              os_strlcpy(buf, "EAP Failure", sizeof(buf));
++              if (e->ctrl_iface)
++                      break;
 +              eloop_terminate();
 +              break;
 +      default:
 +              os_strlcpy(buf, "unknown EAP code", sizeof(buf));
 +              wpa_hexdump_buf(MSG_DEBUG, "Decapsulated EAP packet", eap);
 +              break;
 +      }
 +      wpa_printf(MSG_DEBUG, "decapsulated EAP packet (code=%d "
 +                     "id=%d len=%d) from RADIUS server: %s",
 +                    hdr->code, hdr->identifier, ntohs(hdr->length), buf);
 +
 +      /* sta->eapol_sm->be_auth.idFromServer = hdr->identifier; */
 +
 +      wpabuf_free(e->last_eap_radius);
 +      e->last_eap_radius = eap;
 +
 +      {
 +              struct ieee802_1x_hdr *dot1x;
 +              dot1x = os_malloc(sizeof(*dot1x) + wpabuf_len(eap));
 +              assert(dot1x != NULL);
 +              dot1x->version = EAPOL_VERSION;
 +              dot1x->type = IEEE802_1X_TYPE_EAP_PACKET;
 +              dot1x->length = htons(wpabuf_len(eap));
 +              os_memcpy((u8 *) (dot1x + 1), wpabuf_head(eap),
 +                        wpabuf_len(eap));
 +              eapol_sm_rx_eapol(e->wpa_s->eapol, e->wpa_s->bssid,
 +                                (u8 *) dot1x,
 +                                sizeof(*dot1x) + wpabuf_len(eap));
 +              os_free(dot1x);
 +      }
 +}
 +
 +
 +static void ieee802_1x_get_keys(struct eapol_test_data *e,
 +                              struct radius_msg *msg, struct radius_msg *req,
 +                              const u8 *shared_secret,
 +                              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);
 +      if (keys && keys->send == NULL && keys->recv == NULL) {
 +              os_free(keys);
 +              keys = radius_msg_get_cisco_keys(msg, req, shared_secret,
 +                                               shared_secret_len);
 +      }
 +
 +      if (keys) {
 +              if (keys->send) {
 +                      wpa_hexdump(MSG_DEBUG, "MS-MPPE-Send-Key (sign)",
 +                                  keys->send, keys->send_len);
 +              }
 +              if (keys->recv) {
 +                      wpa_hexdump(MSG_DEBUG, "MS-MPPE-Recv-Key (crypt)",
 +                                  keys->recv, keys->recv_len);
 +                      e->authenticator_pmk_len =
 +                              keys->recv_len > PMK_LEN ? PMK_LEN :
 +                              keys->recv_len;
 +                      os_memcpy(e->authenticator_pmk, keys->recv,
 +                                e->authenticator_pmk_len);
 +                      if (e->authenticator_pmk_len == 16 && keys->send &&
 +                          keys->send_len == 16) {
 +                              /* MS-CHAP-v2 derives 16 octet keys */
 +                              wpa_printf(MSG_DEBUG, "Use MS-MPPE-Send-Key "
 +                                         "to extend PMK to 32 octets");
 +                              os_memcpy(e->authenticator_pmk +
 +                                        e->authenticator_pmk_len,
 +                                        keys->send, keys->send_len);
 +                              e->authenticator_pmk_len += keys->send_len;
 +                      }
 +              }
 +
 +              os_free(keys->send);
 +              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;
 +      }
 +}
 +
 +
 +/* Process the RADIUS frames from Authentication Server */
 +static RadiusRxResult
 +ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req,
 +                      const u8 *shared_secret, size_t shared_secret_len,
 +                      void *data)
 +{
 +      struct eapol_test_data *e = data;
 +      struct radius_hdr *hdr = radius_msg_get_hdr(msg);
 +
 +      /* RFC 2869, Ch. 5.13: valid Message-Authenticator attribute MUST be
 +       * present when packet contains an EAP-Message attribute */
 +      if (hdr->code == RADIUS_CODE_ACCESS_REJECT &&
 +          radius_msg_get_attr(msg, RADIUS_ATTR_MESSAGE_AUTHENTICATOR, NULL,
 +                              0) < 0 &&
 +          radius_msg_get_attr(msg, RADIUS_ATTR_EAP_MESSAGE, NULL, 0) < 0) {
 +              wpa_printf(MSG_DEBUG, "Allowing RADIUS "
 +                            "Access-Reject without Message-Authenticator "
 +                            "since it does not include EAP-Message\n");
 +      } 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");
 +              return RADIUS_RX_UNKNOWN;
 +      }
 +
 +      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");
 +              return RADIUS_RX_UNKNOWN;
 +      }
 +
 +      e->radius_identifier = -1;
 +      wpa_printf(MSG_DEBUG, "RADIUS packet matching with station");
 +
 +      radius_msg_free(e->last_recv_radius);
 +      e->last_recv_radius = msg;
 +
 +      switch (hdr->code) {
 +      case RADIUS_CODE_ACCESS_ACCEPT:
 +              e->radius_access_accept_received = 1;
 +              ieee802_1x_get_keys(e, msg, req, shared_secret,
 +                                  shared_secret_len);
 +              break;
 +      case RADIUS_CODE_ACCESS_REJECT:
 +              e->radius_access_reject_received = 1;
 +              break;
 +      }
 +
 +      ieee802_1x_decapsulate_radius(e);
 +
 +      if ((hdr->code == RADIUS_CODE_ACCESS_ACCEPT &&
 +           e->eapol_test_num_reauths < 0) ||
 +          hdr->code == RADIUS_CODE_ACCESS_REJECT) {
-                         const char *cli_addr)
++              if (!e->ctrl_iface)
++                      eloop_terminate();
 +      }
 +
 +      return RADIUS_RX_QUEUED;
 +}
 +
 +
++static int driver_get_ssid(void *priv, u8 *ssid)
++{
++      ssid[0] = 0;
++      return 0;
++}
++
++
++static int driver_get_bssid(void *priv, u8 *bssid)
++{
++      struct eapol_test_data *e = priv;
++
++      if (e->ctrl_iface && !e->id_req_sent) {
++              eloop_register_timeout(0, 0, send_eap_request_identity,
++                                     e->wpa_s, NULL);
++              e->id_req_sent = 1;
++      }
++
++      os_memset(bssid, 0, ETH_ALEN);
++      bssid[5] = 1;
++      return 0;
++}
++
++
++static int driver_get_capa(void *priv, struct wpa_driver_capa *capa)
++{
++      os_memset(capa, 0, sizeof(*capa));
++      capa->flags = WPA_DRIVER_FLAGS_WIRED;
++      return 0;
++}
++
++
++struct wpa_driver_ops eapol_test_drv_ops = {
++      .name = "test",
++      .get_ssid = driver_get_ssid,
++      .get_bssid = driver_get_bssid,
++      .get_capa = driver_get_capa,
++};
++
 +static void wpa_init_conf(struct eapol_test_data *e,
 +                        struct wpa_supplicant *wpa_s, const char *authsrv,
 +                        int port, const char *secret,
-       os_strlcpy(wpa_s->ifname, "test", sizeof(wpa_s->ifname));
++                        const char *cli_addr, const char *ifname)
 +{
 +      struct hostapd_radius_server *as;
 +      int res;
 +
++      wpa_s->driver = &eapol_test_drv_ops;
++      wpa_s->drv_priv = e;
 +      wpa_s->bssid[5] = 1;
 +      os_memcpy(wpa_s->own_addr, e->own_addr, ETH_ALEN);
 +      e->own_ip_addr.s_addr = htonl((127 << 24) | 1);
-       if (inet_aton(authsrv, &as->addr.u.v4) < 0) {
++      os_strlcpy(wpa_s->ifname, ifname, sizeof(wpa_s->ifname));
 +
 +      e->radius_conf = os_zalloc(sizeof(struct hostapd_radius_servers));
 +      assert(e->radius_conf != NULL);
 +      e->radius_conf->num_auth_servers = 1;
 +      as = os_zalloc(sizeof(struct hostapd_radius_server));
 +      assert(as != NULL);
 +#if defined(CONFIG_NATIVE_WINDOWS) || defined(CONFIG_ANSI_C_EXTRA)
 +      {
 +              int a[4];
 +              u8 *pos;
 +              sscanf(authsrv, "%d.%d.%d.%d", &a[0], &a[1], &a[2], &a[3]);
 +              pos = (u8 *) &as->addr.u.v4;
 +              *pos++ = a[0];
 +              *pos++ = a[1];
 +              *pos++ = a[2];
 +              *pos++ = a[3];
 +      }
 +#else /* CONFIG_NATIVE_WINDOWS or CONFIG_ANSI_C_EXTRA */
-       as->addr.af = AF_INET;
++      if (hostapd_parse_ip_addr(authsrv, &as->addr) < 0) {
 +              wpa_printf(MSG_ERROR, "Invalid IP address '%s'",
 +                         authsrv);
 +              assert(0);
 +      }
 +#endif /* CONFIG_NATIVE_WINDOWS or CONFIG_ANSI_C_EXTRA */
-              "           [-A<client IP>]\n"
 +      as->port = port;
 +      as->shared_secret = (u8 *) os_strdup(secret);
 +      as->shared_secret_len = os_strlen(secret);
 +      e->radius_conf->auth_server = as;
 +      e->radius_conf->auth_servers = as;
 +      e->radius_conf->msg_dumps = 1;
 +      if (cli_addr) {
 +              if (hostapd_parse_ip_addr(cli_addr,
 +                                        &e->radius_conf->client_addr) == 0)
 +                      e->radius_conf->force_client_addr = 1;
 +              else {
 +                      wpa_printf(MSG_ERROR, "Invalid IP address '%s'",
 +                                 cli_addr);
 +                      assert(0);
 +              }
 +      }
 +
 +      e->radius = radius_client_init(wpa_s, e->radius_conf);
 +      assert(e->radius != NULL);
 +
 +      res = radius_client_register(e->radius, RADIUS_AUTH,
 +                                   ieee802_1x_receive_auth, e);
 +      assert(res == 0);
 +}
 +
 +
 +static int scard_test(struct eapol_test_data *e)
 +{
 +      struct scard_data *scard;
 +      size_t len;
 +      char imsi[20];
 +      unsigned char _rand[16];
 +#ifdef PCSC_FUNCS
 +      unsigned char sres[4];
 +      unsigned char kc[8];
 +#endif /* PCSC_FUNCS */
 +#define num_triplets 5
 +      unsigned char rand_[num_triplets][16];
 +      unsigned char sres_[num_triplets][4];
 +      unsigned char kc_[num_triplets][8];
 +      int i, res;
 +      size_t j;
 +
 +#define AKA_RAND_LEN 16
 +#define AKA_AUTN_LEN 16
 +#define AKA_AUTS_LEN 14
 +#define RES_MAX_LEN 16
 +#define IK_LEN 16
 +#define CK_LEN 16
 +      unsigned char aka_rand[AKA_RAND_LEN];
 +      unsigned char aka_autn[AKA_AUTN_LEN];
 +      unsigned char aka_auts[AKA_AUTS_LEN];
 +      unsigned char aka_res[RES_MAX_LEN];
 +      size_t aka_res_len;
 +      unsigned char aka_ik[IK_LEN];
 +      unsigned char aka_ck[CK_LEN];
 +
 +      scard = scard_init(e->pcsc_reader);
 +      if (scard == NULL)
 +              return -1;
 +      if (scard_set_pin(scard, e->pcsc_pin)) {
 +              wpa_printf(MSG_WARNING, "PIN validation failed");
 +              scard_deinit(scard);
 +              return -1;
 +      }
 +
 +      len = sizeof(imsi);
 +      if (scard_get_imsi(scard, imsi, &len))
 +              goto failed;
 +      wpa_hexdump_ascii(MSG_DEBUG, "SCARD: IMSI", (u8 *) imsi, len);
 +      /* NOTE: Permanent Username: 1 | IMSI */
 +
 +      wpa_printf(MSG_DEBUG, "SCARD: MNC length %d",
 +                 scard_get_mnc_len(scard));
 +
 +      os_memset(_rand, 0, sizeof(_rand));
 +      if (scard_gsm_auth(scard, _rand, sres, kc))
 +              goto failed;
 +
 +      os_memset(_rand, 0xff, sizeof(_rand));
 +      if (scard_gsm_auth(scard, _rand, sres, kc))
 +              goto failed;
 +
 +      for (i = 0; i < num_triplets; i++) {
 +              os_memset(rand_[i], i, sizeof(rand_[i]));
 +              if (scard_gsm_auth(scard, rand_[i], sres_[i], kc_[i]))
 +                      goto failed;
 +      }
 +
 +      for (i = 0; i < num_triplets; i++) {
 +              printf("1");
 +              for (j = 0; j < len; j++)
 +                      printf("%c", imsi[j]);
 +              printf(",");
 +              for (j = 0; j < 16; j++)
 +                      printf("%02X", rand_[i][j]);
 +              printf(",");
 +              for (j = 0; j < 4; j++)
 +                      printf("%02X", sres_[i][j]);
 +              printf(",");
 +              for (j = 0; j < 8; j++)
 +                      printf("%02X", kc_[i][j]);
 +              printf("\n");
 +      }
 +
 +      wpa_printf(MSG_DEBUG, "Trying to use UMTS authentication");
 +
 +      /* seq 39 (0x28) */
 +      os_memset(aka_rand, 0xaa, 16);
 +      os_memcpy(aka_autn, "\x86\x71\x31\xcb\xa2\xfc\x61\xdf"
 +                "\xa3\xb3\x97\x9d\x07\x32\xa2\x12", 16);
 +
 +      res = scard_umts_auth(scard, aka_rand, aka_autn, aka_res, &aka_res_len,
 +                            aka_ik, aka_ck, aka_auts);
 +      if (res == 0) {
 +              wpa_printf(MSG_DEBUG, "UMTS auth completed successfully");
 +              wpa_hexdump(MSG_DEBUG, "RES", aka_res, aka_res_len);
 +              wpa_hexdump(MSG_DEBUG, "IK", aka_ik, IK_LEN);
 +              wpa_hexdump(MSG_DEBUG, "CK", aka_ck, CK_LEN);
 +      } else if (res == -2) {
 +              wpa_printf(MSG_DEBUG, "UMTS auth resulted in synchronization "
 +                         "failure");
 +              wpa_hexdump(MSG_DEBUG, "AUTS", aka_auts, AKA_AUTS_LEN);
 +      } else {
 +              wpa_printf(MSG_DEBUG, "UMTS auth failed");
 +      }
 +
 +failed:
 +      scard_deinit(scard);
 +
 +      return 0;
 +#undef num_triplets
 +}
 +
 +
 +static int scard_get_triplets(struct eapol_test_data *e, int argc, char *argv[])
 +{
 +      struct scard_data *scard;
 +      size_t len;
 +      char imsi[20];
 +      unsigned char _rand[16];
 +      unsigned char sres[4];
 +      unsigned char kc[8];
 +      int num_triplets;
 +      int i;
 +      size_t j;
 +
 +      if (argc < 2 || ((num_triplets = atoi(argv[1])) <= 0)) {
 +              printf("invalid parameters for sim command\n");
 +              return -1;
 +      }
 +
 +      if (argc <= 2 || os_strcmp(argv[2], "debug") != 0) {
 +              /* disable debug output */
 +              wpa_debug_level = 99;
 +      }
 +
 +      scard = scard_init(e->pcsc_reader);
 +      if (scard == NULL) {
 +              printf("Failed to open smartcard connection\n");
 +              return -1;
 +      }
 +      if (scard_set_pin(scard, argv[0])) {
 +              wpa_printf(MSG_WARNING, "PIN validation failed");
 +              scard_deinit(scard);
 +              return -1;
 +      }
 +
 +      len = sizeof(imsi);
 +      if (scard_get_imsi(scard, imsi, &len)) {
 +              scard_deinit(scard);
 +              return -1;
 +      }
 +
 +      for (i = 0; i < num_triplets; i++) {
 +              os_memset(_rand, i, sizeof(_rand));
 +              if (scard_gsm_auth(scard, _rand, sres, kc))
 +                      break;
 +
 +              /* IMSI:Kc:SRES:RAND */
 +              for (j = 0; j < len; j++)
 +                      printf("%c", imsi[j]);
 +              printf(":");
 +              for (j = 0; j < 8; j++)
 +                      printf("%02X", kc[j]);
 +              printf(":");
 +              for (j = 0; j < 4; j++)
 +                      printf("%02X", sres[j]);
 +              printf(":");
 +              for (j = 0; j < 16; j++)
 +                      printf("%02X", _rand[j]);
 +              printf("\n");
 +      }
 +
 +      scard_deinit(scard);
 +
 +      return 0;
 +}
 +
 +
 +static void eapol_test_terminate(int sig, void *signal_ctx)
 +{
 +      struct wpa_supplicant *wpa_s = signal_ctx;
 +      wpa_msg(wpa_s, MSG_INFO, "Signal %d received - terminating", sig);
 +      eloop_terminate();
 +}
 +
 +
 +static void usage(void)
 +{
 +      printf("usage:\n"
 +             "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>] [-R<PC/SC reader>] "
 +             "[-P<PC/SC PIN>] \\\n"
-               c = getopt(argc, argv, "a:A:c:C:eM:nN:o:p:P:r:R:s:St:W");
++             "           [-A<client IP>] [-i<ifname>] [-T<ctrl_iface>]\n"
 +             "eapol_test scard\n"
 +             "eapol_test sim <PIN> <num triplets> [debug]\n"
 +             "\n");
 +      printf("options:\n"
 +             "  -c<conf> = configuration file\n"
 +             "  -a<AS IP> = IP address of the authentication server, "
 +             "default 127.0.0.1\n"
 +             "  -p<AS port> = UDP port of the authentication server, "
 +             "default 1812\n"
 +             "  -s<AS secret> = shared secret with the authentication "
 +             "server, default 'radius'\n"
 +             "  -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"
 +             "  -t<timeout> = sets timeout in seconds (default: 30 s)\n"
 +             "  -C<Connect-Info> = RADIUS Connect-Info (default: "
 +             "CONNECT 11Mbps 802.11b)\n"
 +             "  -M<client MAC address> = Set own MAC address "
 +             "(Calling-Station-Id,\n"
 +             "                           default: 02:00:00:00:00:01)\n"
 +             "  -o<server cert file> = Write received server certificate\n"
 +             "                         chain to the specified file\n"
 +             "  -N<attr spec> = send arbitrary attribute specified by:\n"
 +             "                  attr_id:syntax:value or attr_id\n"
 +             "                  attr_id - number id of the attribute\n"
 +             "                  syntax - one of: s, d, x\n"
 +             "                     s = string\n"
 +             "                     d = integer\n"
 +             "                     x = octet string\n"
 +             "                  value - attribute value.\n"
 +             "       When only attr_id is specified, NULL will be used as "
 +             "value.\n"
 +             "       Multiple attributes can be specified by using the "
 +             "option several times.\n");
 +}
 +
 +
 +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";
 +      int as_port = 1812;
 +      char *as_secret = "radius";
 +      char *cli_addr = NULL;
 +      char *conf = NULL;
 +      int timeout = 30;
 +      char *pos;
 +      struct extra_radius_attr *p = NULL, *p1;
++      const char *ifname = "test";
++      const char *ctrl_iface = NULL;
 +
 +      if (os_program_init())
 +              return -1;
 +
 +      hostapd_logger_register_cb(hostapd_logger_cb);
 +
 +      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 (;;) {
-       if (conf == NULL) {
++              c = getopt(argc, argv, "a:A:c:C:ei:M:nN:o:p:P:r:R:s:St:T:W");
 +              if (c < 0)
 +                      break;
 +              switch (c) {
 +              case 'a':
 +                      as_addr = optarg;
 +                      break;
 +              case 'A':
 +                      cli_addr = optarg;
 +                      break;
 +              case 'c':
 +                      conf = optarg;
 +                      break;
 +              case 'C':
 +                      eapol_test.connect_info = optarg;
 +                      break;
 +              case 'e':
 +                      eapol_test.req_eap_key_name = 1;
 +                      break;
++              case 'i':
++                      ifname = optarg;
++                      break;
 +              case 'M':
 +                      if (hwaddr_aton(optarg, eapol_test.own_addr)) {
 +                              usage();
 +                              return -1;
 +                      }
 +                      break;
 +              case 'n':
 +                      eapol_test.no_mppe_keys++;
 +                      break;
 +              case 'o':
 +                      if (eapol_test.server_cert_file)
 +                              fclose(eapol_test.server_cert_file);
 +                      eapol_test.server_cert_file = fopen(optarg, "w");
 +                      if (eapol_test.server_cert_file == NULL) {
 +                              printf("Could not open '%s' for writing\n",
 +                                     optarg);
 +                              return -1;
 +                      }
 +                      break;
 +              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;
 +              case 'S':
 +                      save_config++;
 +                      break;
 +              case 't':
 +                      timeout = atoi(optarg);
 +                      break;
++              case 'T':
++                      ctrl_iface = optarg;
++                      eapol_test.ctrl_iface = 1;
++                      break;
 +              case 'W':
 +                      wait_for_monitor++;
 +                      break;
 +              case 'N':
 +                      p1 = os_zalloc(sizeof(*p1));
 +                      if (p1 == NULL)
 +                              break;
 +                      if (!p)
 +                              eapol_test.extra_attrs = p1;
 +                      else
 +                              p->next = p1;
 +                      p = p1;
 +
 +                      p->type = atoi(optarg);
 +                      pos = os_strchr(optarg, ':');
 +                      if (pos == NULL) {
 +                              p->syntax = 'n';
 +                              p->data = NULL;
 +                              break;
 +                      }
 +
 +                      pos++;
 +                      if (pos[0] == '\0' || pos[1] != ':') {
 +                              printf("Incorrect format of attribute "
 +                                     "specification\n");
 +                              break;
 +                      }
 +
 +                      p->syntax = pos[0];
 +                      p->data = pos + 2;
 +                      break;
 +              default:
 +                      usage();
 +                      return -1;
 +              }
 +      }
 +
 +      if (argc > optind && os_strcmp(argv[optind], "scard") == 0) {
 +              return scard_test(&eapol_test);
 +      }
 +
 +      if (argc > optind && os_strcmp(argv[optind], "sim") == 0) {
 +              return scard_get_triplets(&eapol_test, argc - optind - 1,
 +                                        &argv[optind + 1]);
 +      }
 +
-       wpa_s.conf = wpa_config_read(conf, NULL);
++      if (conf == NULL && !ctrl_iface) {
 +              usage();
 +              printf("Configuration file is required.\n");
 +              return -1;
 +      }
 +
 +      if (eap_register_methods()) {
 +              wpa_printf(MSG_ERROR, "Failed to register EAP methods");
 +              return -1;
 +      }
 +
 +      if (eloop_init()) {
 +              wpa_printf(MSG_ERROR, "Failed to initialize event loop");
 +              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);
-       if (wpa_s.conf->ssid == NULL) {
++      if (conf)
++              wpa_s.conf = wpa_config_read(conf, NULL);
++      else
++              wpa_s.conf = wpa_config_alloc_empty(ctrl_iface, NULL);
 +      if (wpa_s.conf == NULL) {
 +              printf("Failed to parse configuration file '%s'.\n", conf);
 +              return -1;
 +      }
-                     cli_addr);
++      if (!ctrl_iface && wpa_s.conf->ssid == NULL) {
 +              printf("No networks defined.\n");
 +              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,
-       if (wpa_supplicant_scard_init(&wpa_s, wpa_s.conf->ssid))
++                    cli_addr, ifname);
 +      wpa_s.ctrl_iface = wpa_supplicant_ctrl_iface_init(&wpa_s);
 +      if (wpa_s.ctrl_iface == NULL) {
 +              printf("Failed to initialize control interface '%s'.\n"
 +                     "You may have another eapol_test process already "
 +                     "running or the file was\n"
 +                     "left by an unclean termination of eapol_test in "
 +                     "which case you will need\n"
 +                     "to manually remove this file before starting "
 +                     "eapol_test again.\n",
 +                     wpa_s.conf->ctrl_interface);
 +              return -1;
 +      }
-       eloop_register_timeout(timeout, 0, eapol_test_timeout, &eapol_test,
-                              NULL);
-       eloop_register_timeout(0, 0, send_eap_request_identity, &wpa_s, NULL);
++      if (wpa_s.conf->ssid &&
++          wpa_supplicant_scard_init(&wpa_s, wpa_s.conf->ssid))
 +              return -1;
 +
 +      if (test_eapol(&eapol_test, &wpa_s, wpa_s.conf->ssid))
 +              return -1;
 +
 +      if (wpas_init_ext_pw(&wpa_s) < 0)
 +              return -1;
 +
 +      if (wait_for_monitor)
 +              wpa_supplicant_ctrl_iface_wait(wpa_s.ctrl_iface);
 +
++      if (!ctrl_iface) {
++              eloop_register_timeout(timeout, 0, eapol_test_timeout,
++                                     &eapol_test, NULL);
++              eloop_register_timeout(0, 0, send_eap_request_identity, &wpa_s,
++                                     NULL);
++      }
 +      eloop_register_signal_terminate(eapol_test_terminate, &wpa_s);
 +      eloop_register_signal_reconfig(eapol_test_terminate, &wpa_s);
 +      eloop_run();
 +
 +      eloop_cancel_timeout(eapol_test_timeout, &eapol_test, NULL);
 +      eloop_cancel_timeout(eapol_sm_reauth, &eapol_test, NULL);
 +
 +      if (eapol_test_compare_pmk(&eapol_test) == 0 ||
 +          eapol_test.no_mppe_keys)
 +              ret = 0;
 +      if (eapol_test.auth_timed_out)
 +              ret = -2;
 +      if (eapol_test.radius_access_reject_received)
 +              ret = -3;
 +
 +      if (save_config)
 +              wpa_config_write(conf, wpa_s.conf);
 +
 +      test_eapol_clean(&eapol_test, &wpa_s);
 +
 +      eap_peer_unregister_methods();
 +#ifdef CONFIG_AP
 +      eap_server_unregister_methods();
 +#endif /* CONFIG_AP */
 +
 +      eloop_destroy();
 +
 +      if (eapol_test.server_cert_file)
 +              fclose(eapol_test.server_cert_file);
 +
 +      printf("MPPE keys OK: %d  mismatch: %d\n",
 +             eapol_test.num_mppe_ok, eapol_test.num_mppe_mismatch);
 +      if (eapol_test.num_mppe_mismatch)
 +              ret = -4;
 +      if (ret)
 +              printf("FAILURE\n");
 +      else
 +              printf("SUCCESS\n");
 +
 +      os_program_deinit();
 +
 +      return ret;
 +}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..80e7dfcf531dc391bb6fa376e5f83daeb7044a54
new file mode 100755 (executable)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,142 @@@
++#!/usr/bin/env python2
++#
++# eapol_test controller
++# Copyright (c) 2015, Jouni Malinen <j@w1.fi>
++#
++# This software may be distributed under the terms of the BSD license.
++# See README for more details.
++
++import argparse
++import logging
++import os
++import Queue
++import sys
++import threading
++
++logger = logging.getLogger()
++dir = os.path.dirname(os.path.realpath(sys.modules[__name__].__file__))
++sys.path.append(os.path.join(dir, '..', 'wpaspy'))
++import wpaspy
++wpas_ctrl = '/tmp/eapol_test'
++
++class eapol_test:
++    def __init__(self, ifname):
++        self.ifname = ifname
++        self.ctrl = wpaspy.Ctrl(os.path.join(wpas_ctrl, ifname))
++        if "PONG" not in self.ctrl.request("PING"):
++            raise Exception("Failed to connect to eapol_test (%s)" % ifname)
++        self.mon = wpaspy.Ctrl(os.path.join(wpas_ctrl, ifname))
++        self.mon.attach()
++
++    def add_network(self):
++        id = self.request("ADD_NETWORK")
++        if "FAIL" in id:
++            raise Exception("ADD_NETWORK failed")
++        return int(id)
++
++    def remove_network(self, id):
++        id = self.request("REMOVE_NETWORK " + str(id))
++        if "FAIL" in id:
++            raise Exception("REMOVE_NETWORK failed")
++        return None
++
++    def set_network(self, id, field, value):
++        res = self.request("SET_NETWORK " + str(id) + " " + field + " " + value)
++        if "FAIL" in res:
++            raise Exception("SET_NETWORK failed")
++        return None
++
++    def set_network_quoted(self, id, field, value):
++        res = self.request("SET_NETWORK " + str(id) + " " + field + ' "' + value + '"')
++        if "FAIL" in res:
++            raise Exception("SET_NETWORK failed")
++        return None
++
++    def request(self, cmd, timeout=10):
++        return self.ctrl.request(cmd, timeout=timeout)
++
++    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 run(ifname, count, no_fast_reauth, res):
++    et = eapol_test(ifname)
++
++    et.request("AP_SCAN 0")
++    if no_fast_reauth:
++        et.request("SET fast_reauth 0")
++    else:
++        et.request("SET fast_reauth 1")
++    id = et.add_network()
++    et.set_network(id, "key_mgmt", "IEEE8021X")
++    et.set_network(id, "eapol_flags", "0")
++    et.set_network(id, "eap", "TLS")
++    et.set_network_quoted(id, "identity", "user")
++    et.set_network_quoted(id, "ca_cert", 'ca.pem')
++    et.set_network_quoted(id, "client_cert", 'client.pem')
++    et.set_network_quoted(id, "private_key", 'client.key')
++    et.set_network_quoted(id, "private_key_passwd", 'whatever')
++    et.set_network(id, "disabled", "0")
++
++    fail = False
++    for i in range(count):
++        et.request("REASSOCIATE")
++        ev = et.wait_event(["CTRL-EVENT-CONNECTED", "CTRL-EVENT-EAP-FAILURE"])
++        if ev is None or "CTRL-EVENT-CONNECTED" not in ev:
++            fail = True
++            break
++
++    et.remove_network(id)
++
++    if fail:
++        res.put("FAIL (%d OK)" % i)
++    else:
++        res.put("PASS %d" % (i + 1))
++
++def main():
++    parser = argparse.ArgumentParser(description='eapol_test controller')
++    parser.add_argument('--ctrl', help='control interface directory')
++    parser.add_argument('--num', help='number of processes')
++    parser.add_argument('--iter', help='number of iterations')
++    parser.add_argument('--no-fast-reauth', action='store_true',
++                        dest='no_fast_reauth',
++                        help='disable TLS session resumption')
++    args = parser.parse_args()
++
++    num = int(args.num)
++    iter = int(args.iter)
++    if args.ctrl:
++        global wpas_ctrl
++        wpas_ctrl = args.ctrl
++
++    t = {}
++    res = {}
++    for i in range(num):
++        res[i] = Queue.Queue()
++        t[i] = threading.Thread(target=run, args=(str(i), iter,
++                                                  args.no_fast_reauth, res[i]))
++    for i in range(num):
++        t[i].start()
++    for i in range(num):
++        t[i].join()
++        try:
++            results = res[i].get(False)
++        except:
++            results = "N/A"
++        print "%d: %s" % (i, results)
++
++if __name__ == "__main__":
++    main()
index d275ca424e6ee74d4e51a2313dc13d1423ee2467,0000000000000000000000000000000000000000..3af1c7d89c64901ba4663199c88af0e2edd4a19b
mode 100644,000000..100644
--- /dev/null
@@@ -1,3664 -1,0 +1,3827 @@@
-               return 0;
 +/*
 + * WPA Supplicant - Driver event processing
 + * 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 "includes.h"
 +
 +#include "common.h"
 +#include "eapol_supp/eapol_supp_sm.h"
 +#include "rsn_supp/wpa.h"
 +#include "eloop.h"
 +#include "config.h"
 +#include "l2_packet/l2_packet.h"
 +#include "wpa_supplicant_i.h"
 +#include "driver_i.h"
 +#include "pcsc_funcs.h"
 +#include "rsn_supp/preauth.h"
 +#include "rsn_supp/pmksa_cache.h"
 +#include "common/wpa_ctrl.h"
 +#include "eap_peer/eap.h"
 +#include "ap/hostapd.h"
 +#include "p2p/p2p.h"
++#include "fst/fst.h"
 +#include "wnm_sta.h"
 +#include "notify.h"
 +#include "common/ieee802_11_defs.h"
 +#include "common/ieee802_11_common.h"
 +#include "crypto/random.h"
 +#include "blacklist.h"
 +#include "wpas_glue.h"
 +#include "wps_supplicant.h"
 +#include "ibss_rsn.h"
 +#include "sme.h"
 +#include "gas_query.h"
 +#include "p2p_supplicant.h"
 +#include "bgscan.h"
 +#include "autoscan.h"
 +#include "ap.h"
 +#include "bss.h"
 +#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 own_request);
 +#endif /* CONFIG_NO_SCAN_PROCESSING */
 +
 +
 +static int wpas_temp_disabled(struct wpa_supplicant *wpa_s,
 +                            struct wpa_ssid *ssid)
 +{
 +      struct os_reltime now;
 +
 +      if (ssid == NULL || ssid->disabled_until.sec == 0)
 +              return 0;
 +
 +      os_get_reltime(&now);
 +      if (ssid->disabled_until.sec > now.sec)
 +              return ssid->disabled_until.sec - now.sec;
 +
 +      wpas_clear_temp_disabled(wpa_s, ssid, 0);
 +
 +      return 0;
 +}
 +
 +
++/**
++ * wpas_reenabled_network_time - Time until first network is re-enabled
++ * @wpa_s: Pointer to wpa_supplicant data
++ * Returns: If all enabled networks are temporarily disabled, returns the time
++ *    (in sec) until the first network is re-enabled. Otherwise returns 0.
++ *
++ * This function is used in case all enabled networks are temporarily disabled,
++ * in which case it returns the time (in sec) that the first network will be
++ * re-enabled. The function assumes that at least one network is enabled.
++ */
++static int wpas_reenabled_network_time(struct wpa_supplicant *wpa_s)
++{
++      struct wpa_ssid *ssid;
++      int disabled_for, res = 0;
++
++#ifdef CONFIG_INTERWORKING
++      if (wpa_s->conf->auto_interworking && wpa_s->conf->interworking &&
++          wpa_s->conf->cred)
++              return 0;
++#endif /* CONFIG_INTERWORKING */
++
++      for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
++              if (ssid->disabled)
++                      continue;
++
++              disabled_for = wpas_temp_disabled(wpa_s, ssid);
++              if (!disabled_for)
++                      return 0;
++
++              if (!res || disabled_for < res)
++                      res = disabled_for;
++      }
++
++      return res;
++}
++
++
++void wpas_network_reenabled(void *eloop_ctx, void *timeout_ctx)
++{
++      struct wpa_supplicant *wpa_s = eloop_ctx;
++
++      if (wpa_s->disconnected || wpa_s->wpa_state != WPA_SCANNING)
++              return;
++
++      wpa_dbg(wpa_s, MSG_DEBUG,
++              "Try to associate due to network getting re-enabled");
++      if (wpa_supplicant_fast_associate(wpa_s) != 1) {
++              wpa_supplicant_cancel_sched_scan(wpa_s);
++              wpa_supplicant_req_scan(wpa_s, 0, 0);
++      }
++}
++
++
 +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;
++      u8 drv_ssid[SSID_MAX_LEN];
++      size_t drv_ssid_len;
 +      int res;
 +
 +      if (wpa_s->conf->ap_scan == 1 && wpa_s->current_ssid) {
 +              wpa_supplicant_update_current_bss(wpa_s);
- #ifdef CONFIG_P2P
-       os_memset(wpa_s->go_dev_addr, 0, ETH_ALEN);
- #endif /* CONFIG_P2P */
++
++              if (wpa_s->current_ssid->ssid_len == 0)
++                      return 0; /* current profile still in use */
++              res = wpa_drv_get_ssid(wpa_s, drv_ssid);
++              if (res < 0) {
++                      wpa_msg(wpa_s, MSG_INFO,
++                              "Failed to read SSID from driver");
++                      return 0; /* try to use current profile */
++              }
++              drv_ssid_len = res;
++
++              if (drv_ssid_len == wpa_s->current_ssid->ssid_len &&
++                  os_memcmp(drv_ssid, wpa_s->current_ssid->ssid,
++                            drv_ssid_len) == 0)
++                      return 0; /* current profile still in use */
++
++              wpa_msg(wpa_s, MSG_DEBUG,
++                      "Driver-initiated BSS selection changed the SSID to %s",
++                      wpa_ssid_txt(drv_ssid, drv_ssid_len));
++              /* continue selecting a new network profile */
 +      }
 +
 +      wpa_dbg(wpa_s, MSG_DEBUG, "Select network based on association "
 +              "information");
 +      ssid = wpa_supplicant_get_ssid(wpa_s);
 +      if (ssid == NULL) {
 +              wpa_msg(wpa_s, MSG_INFO,
 +                      "No network configuration found for the current AP");
 +              return -1;
 +      }
 +
 +      if (wpas_network_disabled(wpa_s, ssid)) {
 +              wpa_dbg(wpa_s, MSG_DEBUG, "Selected network is disabled");
 +              return -1;
 +      }
 +
 +      if (disallowed_bssid(wpa_s, wpa_s->bssid) ||
 +          disallowed_ssid(wpa_s, ssid->ssid, ssid->ssid_len)) {
 +              wpa_dbg(wpa_s, MSG_DEBUG, "Selected BSS is disallowed");
 +              return -1;
 +      }
 +
 +      res = wpas_temp_disabled(wpa_s, ssid);
 +      if (res > 0) {
 +              wpa_dbg(wpa_s, MSG_DEBUG, "Selected network is temporarily "
 +                      "disabled for %d second(s)", res);
 +              return -1;
 +      }
 +
 +      wpa_dbg(wpa_s, MSG_DEBUG, "Network configuration found for the "
 +              "current AP");
 +      if (wpa_key_mgmt_wpa_any(ssid->key_mgmt)) {
 +              u8 wpa_ie[80];
 +              size_t wpa_ie_len = sizeof(wpa_ie);
 +              if (wpa_supplicant_set_suites(wpa_s, NULL, ssid,
 +                                            wpa_ie, &wpa_ie_len) < 0)
 +                      wpa_dbg(wpa_s, MSG_DEBUG, "Could not set WPA suites");
 +      } else {
 +              wpa_supplicant_set_non_wpa_policy(wpa_s, ssid);
 +      }
 +
 +      if (wpa_s->current_ssid && wpa_s->current_ssid != ssid)
 +              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)
 +              wpas_notify_network_changed(wpa_s);
 +
 +      return 0;
 +}
 +
 +
 +void wpa_supplicant_stop_countermeasures(void *eloop_ctx, void *sock_ctx)
 +{
 +      struct wpa_supplicant *wpa_s = eloop_ctx;
 +
 +      if (wpa_s->countermeasures) {
 +              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);
 +      }
 +}
 +
 +
 +void wpa_supplicant_mark_disassoc(struct wpa_supplicant *wpa_s)
 +{
 +      int bssid_changed;
 +
 +      wnm_bss_keep_alive_deinit(wpa_s);
 +
 +#ifdef CONFIG_IBSS_RSN
 +      ibss_rsn_deinit(wpa_s->ibss_rsn);
 +      wpa_s->ibss_rsn = NULL;
 +#endif /* CONFIG_IBSS_RSN */
 +
 +#ifdef CONFIG_AP
 +      wpa_supplicant_ap_deinit(wpa_s);
 +#endif /* CONFIG_AP */
 +
 +      if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED)
 +              return;
 +
 +      wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
 +      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);
 +      sme_clear_on_disassoc(wpa_s);
-               "wpa_ie_len=%u rsn_ie_len=%u caps=0x%x level=%d%s%s%s",
 +      wpa_s->current_bss = NULL;
 +      wpa_s->assoc_freq = 0;
 +
 +      if (bssid_changed)
 +              wpas_notify_bssid_changed(wpa_s);
 +
 +      eapol_sm_notify_portEnabled(wpa_s->eapol, FALSE);
 +      eapol_sm_notify_portValid(wpa_s->eapol, FALSE);
 +      if (wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt))
 +              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);
 +}
 +
 +
 +static void wpa_find_assoc_pmkid(struct wpa_supplicant *wpa_s)
 +{
 +      struct wpa_ie_data ie;
 +      int pmksa_set = -1;
 +      size_t i;
 +
 +      if (wpa_sm_parse_own_wpa_ie(wpa_s->wpa, &ie) < 0 ||
 +          ie.pmkid == NULL)
 +              return;
 +
 +      for (i = 0; i < ie.num_pmkid; i++) {
 +              pmksa_set = pmksa_cache_set_current(wpa_s->wpa,
 +                                                  ie.pmkid + i * PMKID_LEN,
 +                                                  NULL, NULL, 0);
 +              if (pmksa_set == 0) {
 +                      eapol_sm_notify_pmkid_attempt(wpa_s->eapol);
 +                      break;
 +              }
 +      }
 +
 +      wpa_dbg(wpa_s, MSG_DEBUG, "RSN: PMKID from assoc IE %sfound from "
 +              "PMKSA cache", pmksa_set == 0 ? "" : "not ");
 +}
 +
 +
 +static void wpa_supplicant_event_pmkid_candidate(struct wpa_supplicant *wpa_s,
 +                                               union wpa_event_data *data)
 +{
 +      if (data == NULL) {
 +              wpa_dbg(wpa_s, MSG_DEBUG, "RSN: No data in PMKID candidate "
 +                      "event");
 +              return;
 +      }
 +      wpa_dbg(wpa_s, MSG_DEBUG, "RSN: PMKID candidate event - bssid=" MACSTR
 +              " index=%d preauth=%d",
 +              MAC2STR(data->pmkid_candidate.bssid),
 +              data->pmkid_candidate.index,
 +              data->pmkid_candidate.preauth);
 +
 +      pmksa_candidate_add(wpa_s->wpa, data->pmkid_candidate.bssid,
 +                          data->pmkid_candidate.index,
 +                          data->pmkid_candidate.preauth);
 +}
 +
 +
 +static int wpa_supplicant_dynamic_keys(struct wpa_supplicant *wpa_s)
 +{
 +      if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE ||
 +          wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE)
 +              return 0;
 +
 +#ifdef IEEE8021X_EAPOL
 +      if (wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA &&
 +          wpa_s->current_ssid &&
 +          !(wpa_s->current_ssid->eapol_flags &
 +            (EAPOL_FLAG_REQUIRE_KEY_UNICAST |
 +             EAPOL_FLAG_REQUIRE_KEY_BROADCAST))) {
 +              /* IEEE 802.1X, but not using dynamic WEP keys (i.e., either
 +               * plaintext or static WEP keys). */
 +              return 0;
 +      }
 +#endif /* IEEE8021X_EAPOL */
 +
 +      return 1;
 +}
 +
 +
 +/**
 + * wpa_supplicant_scard_init - Initialize SIM/USIM access with PC/SC
 + * @wpa_s: pointer to wpa_supplicant data
 + * @ssid: Configuration data for the network
 + * Returns: 0 on success, -1 on failure
 + *
 + * This function is called when starting authentication with a network that is
 + * configured to use PC/SC for SIM/USIM access (EAP-SIM or EAP-AKA).
 + */
 +int wpa_supplicant_scard_init(struct wpa_supplicant *wpa_s,
 +                            struct wpa_ssid *ssid)
 +{
 +#ifdef IEEE8021X_EAPOL
 +#ifdef PCSC_FUNCS
 +      int aka = 0, sim = 0;
 +
 +      if ((ssid != NULL && ssid->eap.pcsc == NULL) ||
 +          wpa_s->scard != NULL || wpa_s->conf->external_sim)
 +              return 0;
 +
 +      if (ssid == NULL || ssid->eap.eap_methods == NULL) {
 +              sim = 1;
 +              aka = 1;
 +      } else {
 +              struct eap_method_type *eap = ssid->eap.eap_methods;
 +              while (eap->vendor != EAP_VENDOR_IETF ||
 +                     eap->method != EAP_TYPE_NONE) {
 +                      if (eap->vendor == EAP_VENDOR_IETF) {
 +                              if (eap->method == EAP_TYPE_SIM)
 +                                      sim = 1;
 +                              else if (eap->method == EAP_TYPE_AKA ||
 +                                       eap->method == EAP_TYPE_AKA_PRIME)
 +                                      aka = 1;
 +                      }
 +                      eap++;
 +              }
 +      }
 +
 +      if (eap_peer_get_eap_method(EAP_VENDOR_IETF, EAP_TYPE_SIM) == NULL)
 +              sim = 0;
 +      if (eap_peer_get_eap_method(EAP_VENDOR_IETF, EAP_TYPE_AKA) == NULL &&
 +          eap_peer_get_eap_method(EAP_VENDOR_IETF, EAP_TYPE_AKA_PRIME) ==
 +          NULL)
 +              aka = 0;
 +
 +      if (!sim && !aka) {
 +              wpa_dbg(wpa_s, MSG_DEBUG, "Selected network is configured to "
 +                      "use SIM, but neither EAP-SIM nor EAP-AKA are "
 +                      "enabled");
 +              return 0;
 +      }
 +
 +      wpa_dbg(wpa_s, MSG_DEBUG, "Selected network is configured to use SIM "
 +              "(sim=%d aka=%d) - initialize PCSC", sim, aka);
 +
 +      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)");
 +              return -1;
 +      }
 +      wpa_sm_set_scard_ctx(wpa_s->wpa, wpa_s->scard);
 +      eapol_sm_register_scard_ctx(wpa_s->eapol, wpa_s->scard);
 +#endif /* PCSC_FUNCS */
 +#endif /* IEEE8021X_EAPOL */
 +
 +      return 0;
 +}
 +
 +
 +#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 privacy = 0;
 +
 +      if (ssid->mixed_cell)
 +              return 1;
 +
 +#ifdef CONFIG_WPS
 +      if (ssid->key_mgmt & WPA_KEY_MGMT_WPS)
 +              return 1;
 +#endif /* CONFIG_WPS */
 +
 +      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 |
 +                               EAPOL_FLAG_REQUIRE_KEY_BROADCAST))
 +              privacy = 1;
 +#endif /* IEEE8021X_EAPOL */
 +
 +      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;
 +}
 +
 +
 +static int wpa_supplicant_ssid_bss_match(struct wpa_supplicant *wpa_s,
 +                                       struct wpa_ssid *ssid,
 +                                       struct wpa_bss *bss)
 +{
 +      struct wpa_ie_data ie;
 +      int proto_match = 0;
 +      const u8 *rsn_ie, *wpa_ie;
 +      int ret;
 +      int wep_ok;
 +
 +      ret = wpas_wps_ssid_bss_match(wpa_s, ssid, bss);
 +      if (ret >= 0)
 +              return ret;
 +
 +      /* Allow TSN if local configuration accepts WEP use without WPA/WPA2 */
 +      wep_ok = !wpa_key_mgmt_wpa(ssid->key_mgmt) &&
 +              (((ssid->key_mgmt & WPA_KEY_MGMT_NONE) &&
 +                ssid->wep_key_len[ssid->wep_tx_keyidx] > 0) ||
 +               (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA));
 +
 +      rsn_ie = wpa_bss_get_ie(bss, WLAN_EID_RSN);
 +      while ((ssid->proto & WPA_PROTO_RSN) && rsn_ie) {
 +              proto_match++;
 +
 +              if (wpa_parse_wpa_ie(rsn_ie, 2 + rsn_ie[1], &ie)) {
 +                      wpa_dbg(wpa_s, MSG_DEBUG, "   skip RSN IE - parse "
 +                              "failed");
 +                      break;
 +              }
 +
 +              if (wep_ok &&
 +                  (ie.group_cipher & (WPA_CIPHER_WEP40 | WPA_CIPHER_WEP104)))
 +              {
 +                      wpa_dbg(wpa_s, MSG_DEBUG, "   selected based on TSN "
 +                              "in RSN IE");
 +                      return 1;
 +              }
 +
 +              if (!(ie.proto & ssid->proto)) {
 +                      wpa_dbg(wpa_s, MSG_DEBUG, "   skip RSN IE - proto "
 +                              "mismatch");
 +                      break;
 +              }
 +
 +              if (!(ie.pairwise_cipher & ssid->pairwise_cipher)) {
 +                      wpa_dbg(wpa_s, MSG_DEBUG, "   skip RSN IE - PTK "
 +                              "cipher mismatch");
 +                      break;
 +              }
 +
 +              if (!(ie.group_cipher & ssid->group_cipher)) {
 +                      wpa_dbg(wpa_s, MSG_DEBUG, "   skip RSN IE - GTK "
 +                              "cipher mismatch");
 +                      break;
 +              }
 +
 +              if (!(ie.key_mgmt & ssid->key_mgmt)) {
 +                      wpa_dbg(wpa_s, MSG_DEBUG, "   skip RSN IE - key mgmt "
 +                              "mismatch");
 +                      break;
 +              }
 +
 +#ifdef CONFIG_IEEE80211W
 +              if (!(ie.capabilities & WPA_CAPABILITY_MFPC) &&
 +                  wpas_get_ssid_pmf(wpa_s, ssid) ==
 +                  MGMT_FRAME_PROTECTION_REQUIRED) {
 +                      wpa_dbg(wpa_s, MSG_DEBUG, "   skip RSN IE - no mgmt "
 +                              "frame protection");
 +                      break;
 +              }
 +#endif /* CONFIG_IEEE80211W */
 +
 +              wpa_dbg(wpa_s, MSG_DEBUG, "   selected based on RSN IE");
 +              return 1;
 +      }
 +
 +      wpa_ie = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE);
 +      while ((ssid->proto & WPA_PROTO_WPA) && wpa_ie) {
 +              proto_match++;
 +
 +              if (wpa_parse_wpa_ie(wpa_ie, 2 + wpa_ie[1], &ie)) {
 +                      wpa_dbg(wpa_s, MSG_DEBUG, "   skip WPA IE - parse "
 +                              "failed");
 +                      break;
 +              }
 +
 +              if (wep_ok &&
 +                  (ie.group_cipher & (WPA_CIPHER_WEP40 | WPA_CIPHER_WEP104)))
 +              {
 +                      wpa_dbg(wpa_s, MSG_DEBUG, "   selected based on TSN "
 +                              "in WPA IE");
 +                      return 1;
 +              }
 +
 +              if (!(ie.proto & ssid->proto)) {
 +                      wpa_dbg(wpa_s, MSG_DEBUG, "   skip WPA IE - proto "
 +                              "mismatch");
 +                      break;
 +              }
 +
 +              if (!(ie.pairwise_cipher & ssid->pairwise_cipher)) {
 +                      wpa_dbg(wpa_s, MSG_DEBUG, "   skip WPA IE - PTK "
 +                              "cipher mismatch");
 +                      break;
 +              }
 +
 +              if (!(ie.group_cipher & ssid->group_cipher)) {
 +                      wpa_dbg(wpa_s, MSG_DEBUG, "   skip WPA IE - GTK "
 +                              "cipher mismatch");
 +                      break;
 +              }
 +
 +              if (!(ie.key_mgmt & ssid->key_mgmt)) {
 +                      wpa_dbg(wpa_s, MSG_DEBUG, "   skip WPA IE - key mgmt "
 +                              "mismatch");
 +                      break;
 +              }
 +
 +              wpa_dbg(wpa_s, MSG_DEBUG, "   selected based on WPA IE");
 +              return 1;
 +      }
 +
 +      if ((ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) && !wpa_ie &&
 +          !rsn_ie) {
 +              wpa_dbg(wpa_s, MSG_DEBUG, "   allow for non-WPA IEEE 802.1X");
 +              return 1;
 +      }
 +
 +      if ((ssid->proto & (WPA_PROTO_WPA | WPA_PROTO_RSN)) &&
 +          wpa_key_mgmt_wpa(ssid->key_mgmt) && proto_match == 0) {
 +              wpa_dbg(wpa_s, MSG_DEBUG, "   skip - no WPA/RSN proto match");
 +              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;
 +      }
 +
 +      wpa_dbg(wpa_s, MSG_DEBUG, "   reject due to mismatch with "
 +              "WPA/WPA2");
 +
 +      return 0;
 +}
 +
 +
 +static int freq_allowed(int *freqs, int freq)
 +{
 +      int i;
 +
 +      if (freqs == NULL)
 +              return 1;
 +
 +      for (i = 0; freqs[i]; i++)
 +              if (freqs[i] == freq)
 +                      return 1;
 +      return 0;
 +}
 +
 +
 +static int rate_match(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
 +{
 +      const struct hostapd_hw_modes *mode = NULL, *modes;
 +      const u8 scan_ie[2] = { WLAN_EID_SUPP_RATES, WLAN_EID_EXT_SUPP_RATES };
 +      const u8 *rate_ie;
 +      int i, j, k;
 +
 +      if (bss->freq == 0)
 +              return 1; /* Cannot do matching without knowing band */
 +
 +      modes = wpa_s->hw.modes;
 +      if (modes == NULL) {
 +              /*
 +               * The driver does not provide any additional information
 +               * about the utilized hardware, so allow the connection attempt
 +               * to continue.
 +               */
 +              return 1;
 +      }
 +
 +      for (i = 0; i < wpa_s->hw.num_modes; i++) {
 +              for (j = 0; j < modes[i].num_channels; j++) {
 +                      int freq = modes[i].channels[j].freq;
 +                      if (freq == bss->freq) {
 +                              if (mode &&
 +                                  mode->mode == HOSTAPD_MODE_IEEE80211G)
 +                                      break; /* do not allow 802.11b replace
 +                                              * 802.11g */
 +                              mode = &modes[i];
 +                              break;
 +                      }
 +              }
 +      }
 +
 +      if (mode == NULL)
 +              return 0;
 +
 +      for (i = 0; i < (int) sizeof(scan_ie); i++) {
 +              rate_ie = wpa_bss_get_ie(bss, scan_ie[i]);
 +              if (rate_ie == NULL)
 +                      continue;
 +
 +              for (j = 2; j < rate_ie[1] + 2; j++) {
 +                      int flagged = !!(rate_ie[j] & 0x80);
 +                      int r = (rate_ie[j] & 0x7f) * 5;
 +
 +                      /*
 +                       * IEEE Std 802.11n-2009 7.3.2.2:
 +                       * The new BSS Membership selector value is encoded
 +                       * like a legacy basic rate, but it is not a rate and
 +                       * only indicates if the BSS members are required to
 +                       * support the mandatory features of Clause 20 [HT PHY]
 +                       * in order to join the BSS.
 +                       */
 +                      if (flagged && ((rate_ie[j] & 0x7f) ==
 +                                      BSS_MEMBERSHIP_SELECTOR_HT_PHY)) {
 +                              if (!ht_supported(mode)) {
 +                                      wpa_dbg(wpa_s, MSG_DEBUG,
 +                                              "   hardware does not support "
 +                                              "HT PHY");
 +                                      return 0;
 +                              }
 +                              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;
 +
 +                      /* check for legacy basic rates */
 +                      for (k = 0; k < mode->num_rates; k++) {
 +                              if (mode->rates[k] == r)
 +                                      break;
 +                      }
 +                      if (k == mode->num_rates) {
 +                              /*
 +                               * IEEE Std 802.11-2007 7.3.2.2 demands that in
 +                               * 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 (freq=%d mode==%d num_rates=%d)",
 +                                      r / 10, r % 10,
 +                                      bss->freq, mode->mode, mode->num_rates);
 +                              return 0;
 +                      }
 +              }
 +      }
 +
 +      return 1;
 +}
 +
 +
 +/*
 + * Test whether BSS is in an ESS.
 + * This is done differently in DMG (60 GHz) and non-DMG bands
 + */
 +static int bss_is_ess(struct wpa_bss *bss)
 +{
 +      if (bss_is_dmg(bss)) {
 +              return (bss->caps & IEEE80211_CAP_DMG_MASK) ==
 +                      IEEE80211_CAP_DMG_AP;
 +      }
 +
 +      return ((bss->caps & (IEEE80211_CAP_ESS | IEEE80211_CAP_IBSS)) ==
 +              IEEE80211_CAP_ESS);
 +}
 +
 +
 +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,
 +                                          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;
 +
 +      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, rsn_ie_len, bss->caps, bss->level,
++              "wpa_ie_len=%u rsn_ie_len=%u caps=0x%x level=%d freq=%d %s%s%s",
 +              i, MAC2STR(bss->bssid), wpa_ssid_txt(bss->ssid, bss->ssid_len),
-               struct wpa_ssid *ssid;
++              wpa_ie_len, rsn_ie_len, bss->caps, bss->level, bss->freq,
 +              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) {
 +              int limit = 1;
 +              if (wpa_supplicant_enabled_networks(wpa_s) == 1) {
 +                      /*
 +                       * When only a single network is enabled, we can
 +                       * trigger blacklisting on the first failure. This
 +                       * should not be done with multiple enabled networks to
 +                       * avoid getting forced to move into a worse ESS on
 +                       * single error if there are no other BSSes of the
 +                       * current ESS.
 +                       */
 +                      limit = 0;
 +              }
 +              if (e->count > limit) {
 +                      wpa_dbg(wpa_s, MSG_DEBUG, "   skip - blacklisted "
 +                              "(count=%d limit=%d)", e->count, limit);
 +                      return NULL;
 +              }
 +      }
 +
 +      if (bss->ssid_len == 0) {
 +              wpa_dbg(wpa_s, MSG_DEBUG, "   skip - SSID not known");
 +              return NULL;
 +      }
 +
 +      if (disallowed_bssid(wpa_s, bss->bssid)) {
 +              wpa_dbg(wpa_s, MSG_DEBUG, "   skip - BSSID disallowed");
 +              return NULL;
 +      }
 +
 +      if (disallowed_ssid(wpa_s, bss->ssid, bss->ssid_len)) {
 +              wpa_dbg(wpa_s, MSG_DEBUG, "   skip - SSID disallowed");
 +              return NULL;
 +      }
 +
 +      wpa = wpa_ie_len > 0 || rsn_ie_len > 0;
 +
 +      for (ssid = group; ssid; ssid = only_first_ssid ? NULL : ssid->pnext) {
 +              int check_ssid = wpa ? 1 : (ssid->ssid_len != 0);
 +              int res;
 +
 +              if (wpas_network_disabled(wpa_s, ssid)) {
 +                      wpa_dbg(wpa_s, MSG_DEBUG, "   skip - disabled");
 +                      continue;
 +              }
 +
 +              res = wpas_temp_disabled(wpa_s, ssid);
 +              if (res > 0) {
 +                      wpa_dbg(wpa_s, MSG_DEBUG, "   skip - disabled "
 +                              "temporarily for %d second(s)", res);
 +                      continue;
 +              }
 +
 +#ifdef CONFIG_WPS
 +              if ((ssid->key_mgmt & WPA_KEY_MGMT_WPS) && e && e->count > 0) {
 +                      wpa_dbg(wpa_s, MSG_DEBUG, "   skip - blacklisted "
 +                              "(WPS)");
 +                      continue;
 +              }
 +
 +              if (wpa && ssid->ssid_len == 0 &&
 +                  wpas_wps_ssid_wildcard_ok(wpa_s, ssid, bss))
 +                      check_ssid = 0;
 +
 +              if (!wpa && (ssid->key_mgmt & WPA_KEY_MGMT_WPS)) {
 +                      /* Only allow wildcard SSID match if an AP
 +                       * advertises active WPS operation that matches
 +                       * with our mode. */
 +                      check_ssid = 1;
 +                      if (ssid->ssid_len == 0 &&
 +                          wpas_wps_ssid_wildcard_ok(wpa_s, ssid, bss))
 +                              check_ssid = 0;
 +              }
 +#endif /* CONFIG_WPS */
 +
 +              if (ssid->bssid_set && ssid->ssid_len == 0 &&
 +                  os_memcmp(bss->bssid, ssid->bssid, ETH_ALEN) == 0)
 +                      check_ssid = 0;
 +
 +              if (check_ssid &&
 +                  (bss->ssid_len != ssid->ssid_len ||
 +                   os_memcmp(bss->ssid, ssid->ssid, bss->ssid_len) != 0)) {
 +                      wpa_dbg(wpa_s, MSG_DEBUG, "   skip - SSID mismatch");
 +                      continue;
 +              }
 +
 +              if (ssid->bssid_set &&
 +                  os_memcmp(bss->bssid, ssid->bssid, ETH_ALEN) != 0) {
 +                      wpa_dbg(wpa_s, MSG_DEBUG, "   skip - BSSID mismatch");
 +                      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 (!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)) {
 +                      wpa_dbg(wpa_s, MSG_DEBUG, "   skip - non-WPA network "
 +                              "not allowed");
 +                      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");
 +                      continue;
 +              }
 +
 +              if (!bss_is_ess(bss)) {
 +                      wpa_dbg(wpa_s, MSG_DEBUG, "   skip - not ESS network");
 +                      continue;
 +              }
 +
 +              if (!freq_allowed(ssid->freq_list, bss->freq)) {
 +                      wpa_dbg(wpa_s, MSG_DEBUG, "   skip - frequency not "
 +                              "allowed");
 +                      continue;
 +              }
 +
 +              if (!rate_match(wpa_s, bss)) {
 +                      wpa_dbg(wpa_s, MSG_DEBUG, "   skip - rate sets do "
 +                              "not match");
 +                      continue;
 +              }
 +
 +#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
 +               * are not in Group Formation with that device.
 +               */
 +#endif /* CONFIG_P2P */
 +
++              if (os_reltime_before(&bss->last_update, &wpa_s->scan_min_time))
++              {
++                      struct os_reltime diff;
++
++                      os_reltime_sub(&wpa_s->scan_min_time,
++                                     &bss->last_update, &diff);
++                      wpa_dbg(wpa_s, MSG_DEBUG,
++                              "   skip - scan result not recent enough (%u.%06u seconds too old)",
++                              (unsigned int) diff.sec,
++                              (unsigned int) diff.usec);
++                      continue;
++              }
++
 +              /* Matching configuration found */
 +              return ssid;
 +      }
 +
 +      /* No matching configuration found */
 +      return NULL;
 +}
 +
 +
 +static struct wpa_bss *
 +wpa_supplicant_select_bss(struct wpa_supplicant *wpa_s,
 +                        struct wpa_ssid *group,
 +                        struct wpa_ssid **selected_ssid,
 +                        int only_first_ssid)
 +{
 +      unsigned int i;
 +
 +      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,
 +                                                  only_first_ssid);
 +              if (!*selected_ssid)
 +                      continue;
 +              wpa_dbg(wpa_s, MSG_DEBUG, "   selected BSS " MACSTR
 +                      " ssid='%s'",
 +                      MAC2STR(bss->bssid),
 +                      wpa_ssid_txt(bss->ssid, bss->ssid_len));
 +              return bss;
 +      }
 +
 +      return NULL;
 +}
 +
 +
 +struct wpa_bss * wpa_supplicant_pick_network(struct wpa_supplicant *wpa_s,
 +                                           struct wpa_ssid **selected_ssid)
 +{
 +      struct wpa_bss *selected = NULL;
 +      int prio;
 +      struct wpa_ssid *next_ssid = NULL;
++      struct wpa_ssid *ssid;
 +
 +      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) {
-       if (wpa_s->last_scan_res_used <= 0)
 +              /* 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, 0);
 +                      if (selected)
 +                              break;
 +              }
 +
 +              if (selected == NULL && wpa_s->blacklist &&
 +                  !wpa_s->countermeasures) {
 +                      wpa_dbg(wpa_s, MSG_DEBUG, "No APs found - clear "
 +                              "blacklist and try again");
 +                      wpa_blacklist_clear(wpa_s);
 +                      wpa_s->blacklist_cleared++;
 +              } else if (selected == NULL)
 +                      break;
 +      }
 +
++      ssid = *selected_ssid;
++      if (selected && ssid && ssid->mem_only_psk && !ssid->psk_set &&
++          !ssid->passphrase && !ssid->ext_psk) {
++              const char *field_name, *txt = NULL;
++
++              wpa_dbg(wpa_s, MSG_DEBUG,
++                      "PSK/passphrase not yet available for the selected network");
++
++              wpas_notify_network_request(wpa_s, ssid,
++                                          WPA_CTRL_REQ_PSK_PASSPHRASE, NULL);
++
++              field_name = wpa_supplicant_ctrl_req_to_string(
++                      WPA_CTRL_REQ_PSK_PASSPHRASE, NULL, &txt);
++              if (field_name == NULL)
++                      return NULL;
++
++              wpas_send_ctrl_req(wpa_s, ssid, field_name, txt);
++
++              selected = NULL;
++      }
++
 +      return selected;
 +}
 +
 +
 +static void wpa_supplicant_req_new_scan(struct wpa_supplicant *wpa_s,
 +                                      int timeout_sec, int timeout_usec)
 +{
 +      if (!wpa_supplicant_enabled_networks(wpa_s)) {
 +              /*
 +               * No networks are enabled; short-circuit request so
 +               * we don't wait timeout seconds before transitioning
 +               * to INACTIVE state.
 +               */
 +              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);
 +              return;
 +      }
 +
 +      wpa_s->scan_for_connection = 1;
 +      wpa_supplicant_req_scan(wpa_s, timeout_sec, timeout_usec);
 +}
 +
 +
 +int wpa_supplicant_connect(struct wpa_supplicant *wpa_s,
 +                         struct wpa_bss *selected,
 +                         struct wpa_ssid *ssid)
 +{
 +      if (wpas_wps_scan_pbc_overlap(wpa_s, selected, ssid)) {
 +              wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_OVERLAP
 +                      "PBC session overlap");
++              wpas_notify_wps_event_pbc_overlap(wpa_s);
 +#ifdef CONFIG_P2P
 +              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
++              wpas_wps_pbc_overlap(wpa_s);
 +              wpas_wps_cancel(wpa_s);
 +#endif /* CONFIG_WPS */
 +              return -1;
 +      }
 +
 +      wpa_msg(wpa_s, MSG_DEBUG,
 +              "Considering connect request: reassociate: %d  selected: "
 +              MACSTR "  bssid: " MACSTR "  pending: " MACSTR
 +              "  wpa_state: %s  ssid=%p  current_ssid=%p",
 +              wpa_s->reassociate, MAC2STR(selected->bssid),
 +              MAC2STR(wpa_s->bssid), MAC2STR(wpa_s->pending_bssid),
 +              wpa_supplicant_state_txt(wpa_s->wpa_state),
 +              ssid, wpa_s->current_ssid);
 +
 +      /*
 +       * Do not trigger new association unless the BSSID has changed or if
 +       * reassociation is requested. If we are in process of associating with
 +       * the selected BSSID, do not trigger new attempt.
 +       */
 +      if (wpa_s->reassociate ||
 +          (os_memcmp(selected->bssid, wpa_s->bssid, ETH_ALEN) != 0 &&
 +           ((wpa_s->wpa_state != WPA_ASSOCIATING &&
 +             wpa_s->wpa_state != WPA_AUTHENTICATING) ||
 +            (!is_zero_ether_addr(wpa_s->pending_bssid) &&
 +             os_memcmp(selected->bssid, wpa_s->pending_bssid, ETH_ALEN) !=
 +             0) ||
 +            (is_zero_ether_addr(wpa_s->pending_bssid) &&
 +             ssid != wpa_s->current_ssid)))) {
 +              if (wpa_supplicant_scard_init(wpa_s, ssid)) {
 +                      wpa_supplicant_req_new_scan(wpa_s, 10, 0);
 +                      return 0;
 +              }
 +              wpa_msg(wpa_s, MSG_DEBUG, "Request association with " MACSTR,
 +                      MAC2STR(selected->bssid));
 +              wpa_supplicant_associate(wpa_s, selected, ssid);
 +      } else {
 +              wpa_dbg(wpa_s, MSG_DEBUG, "Already associated or trying to "
 +                      "connect with the selected AP");
 +      }
 +
 +      return 0;
 +}
 +
 +
 +static struct wpa_ssid *
 +wpa_supplicant_pick_new_network(struct wpa_supplicant *wpa_s)
 +{
 +      int prio;
 +      struct wpa_ssid *ssid;
 +
 +      for (prio = 0; prio < wpa_s->conf->num_prio; prio++) {
 +              for (ssid = wpa_s->conf->pssid[prio]; ssid; ssid = ssid->pnext)
 +              {
 +                      if (wpas_network_disabled(wpa_s, ssid))
 +                              continue;
 +                      if (ssid->mode == IEEE80211_MODE_IBSS ||
 +                          ssid->mode == IEEE80211_MODE_AP ||
 +                          ssid->mode == IEEE80211_MODE_MESH)
 +                              return ssid;
 +              }
 +      }
 +      return NULL;
 +}
 +
 +
 +/* TODO: move the rsn_preauth_scan_result*() to be called from notify.c based
 + * on BSS added and BSS changed events */
 +static void wpa_supplicant_rsn_preauth_scan_results(
 +      struct wpa_supplicant *wpa_s)
 +{
 +      struct wpa_bss *bss;
 +
 +      if (rsn_preauth_scan_results(wpa_s->wpa) < 0)
 +              return;
 +
 +      dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
 +              const u8 *ssid, *rsn;
 +
 +              ssid = wpa_bss_get_ie(bss, WLAN_EID_SSID);
 +              if (ssid == NULL)
 +                      continue;
 +
 +              rsn = wpa_bss_get_ie(bss, WLAN_EID_RSN);
 +              if (rsn == NULL)
 +                      continue;
 +
 +              rsn_preauth_scan_result(wpa_s->wpa, bss->bssid, ssid, rsn);
 +      }
 +
 +}
 +
 +
 +static int wpa_supplicant_need_to_roam(struct wpa_supplicant *wpa_s,
 +                                     struct wpa_bss *selected,
 +                                     struct wpa_ssid *ssid)
 +{
 +      struct wpa_bss *current_bss = NULL;
++#ifndef CONFIG_NO_ROAMING
 +      int min_diff;
++#endif /* CONFIG_NO_ROAMING */
 +
 +      if (wpa_s->reassociate)
 +              return 1; /* explicit request to reassociate */
 +      if (wpa_s->wpa_state < WPA_ASSOCIATED)
 +              return 1; /* we are not associated; continue */
 +      if (wpa_s->current_ssid == NULL)
 +              return 1; /* unknown current SSID */
 +      if (wpa_s->current_ssid != ssid)
 +              return 1; /* different network block */
 +
 +      if (wpas_driver_bss_selection(wpa_s))
 +              return 0; /* Driver-based roaming */
 +
 +      if (wpa_s->current_ssid->ssid)
 +              current_bss = wpa_bss_get(wpa_s, wpa_s->bssid,
 +                                        wpa_s->current_ssid->ssid,
 +                                        wpa_s->current_ssid->ssid_len);
 +      if (!current_bss)
 +              current_bss = wpa_bss_get_bssid(wpa_s, wpa_s->bssid);
 +
 +      if (!current_bss)
 +              return 1; /* current BSS not seen in scan results */
 +
 +      if (current_bss == selected)
 +              return 0;
 +
 +      if (selected->last_update_idx > current_bss->last_update_idx)
 +              return 1; /* current BSS not seen in the last scan */
 +
 +#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 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) ==
 +          0) {
 +              wpa_dbg(wpa_s, MSG_DEBUG, "Allow reassociation - selected BSS "
 +                      "has preferred BSSID");
 +              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");
 +              return 0;
 +      }
 +
 +      min_diff = 2;
 +      if (current_bss->level < 0) {
 +              if (current_bss->level < -85)
 +                      min_diff = 1;
 +              else if (current_bss->level < -80)
 +                      min_diff = 2;
 +              else if (current_bss->level < -75)
 +                      min_diff = 3;
 +              else if (current_bss->level < -70)
 +                      min_diff = 4;
 +              else
 +                      min_diff = 5;
 +      }
 +      if (abs(current_bss->level - selected->level) < min_diff) {
 +              wpa_dbg(wpa_s, MSG_DEBUG, "Skip roam - too small difference "
 +                      "in signal level");
 +              return 0;
 +      }
 +
 +      return 1;
 +#else /* CONFIG_NO_ROAMING */
 +      return 0;
 +#endif /* CONFIG_NO_ROAMING */
 +}
 +
 +
 +/* Return != 0 if no scan results could be fetched or if scan results should not
 + * be shared with other virtual interfaces. */
 +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 = NULL;
 +      int ret = 0;
 +      int ap = 0;
 +#ifndef CONFIG_NO_RANDOM_POOL
 +      size_t i, num;
 +#endif /* CONFIG_NO_RANDOM_POOL */
 +
 +#ifdef CONFIG_AP
 +      if (wpa_s->ap_iface)
 +              ap = 1;
 +#endif /* CONFIG_AP */
 +
 +      wpa_supplicant_notify_scanning(wpa_s, 0);
 +
 +      scan_res = wpa_supplicant_get_scan_results(wpa_s,
 +                                                 data ? &data->scan_info :
 +                                                 NULL, 1);
 +      if (scan_res == NULL) {
 +              if (wpa_s->conf->ap_scan == 2 || ap ||
 +                  wpa_s->scan_res_handler == scan_only_handler)
 +                      return -1;
 +              if (!own_request)
 +                      return -1;
 +              wpa_dbg(wpa_s, MSG_DEBUG, "Failed to get scan results - try "
 +                      "scanning again");
 +              wpa_supplicant_req_new_scan(wpa_s, 1, 0);
 +              ret = -1;
 +              goto scan_work_done;
 +      }
 +
 +#ifndef CONFIG_NO_RANDOM_POOL
 +      num = scan_res->num;
 +      if (num > 10)
 +              num = 10;
 +      for (i = 0; i < num; i++) {
 +              u8 buf[5];
 +              struct wpa_scan_res *res = scan_res->res[i];
 +              buf[0] = res->bssid[5];
 +              buf[1] = res->qual & 0xff;
 +              buf[2] = res->noise & 0xff;
 +              buf[3] = res->level & 0xff;
 +              buf[4] = res->tsf & 0xff;
 +              random_add_randomness(buf, sizeof(buf));
 +      }
 +#endif /* CONFIG_NO_RANDOM_POOL */
 +
 +      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);
 +              ret = -2;
 +              goto scan_work_done;
 +      }
 +
 +      if (ap) {
 +              wpa_dbg(wpa_s, MSG_DEBUG, "Ignore scan results in AP mode");
 +#ifdef CONFIG_AP
 +              if (wpa_s->ap_iface->scan_cb)
 +                      wpa_s->ap_iface->scan_cb(wpa_s->ap_iface);
 +#endif /* CONFIG_AP */
 +              goto scan_work_done;
 +      }
 +
 +      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 (!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 (wnm_scan_process(wpa_s, 1) > 0)
 +              goto scan_work_done;
 +
 +      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);
 +              goto scan_work_done;
 +      }
 +
 +      if (!wpas_driver_bss_selection(wpa_s) &&
 +          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);
 +
 +      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 own_request)
 +{
 +      struct wpa_bss *selected;
 +      struct wpa_ssid *ssid = NULL;
++      int time_to_reenable = wpas_reenabled_network_time(wpa_s);
++
++      if (time_to_reenable > 0) {
++              wpa_dbg(wpa_s, MSG_DEBUG,
++                      "Postpone network selection by %d seconds since all networks are disabled",
++                      time_to_reenable);
++              eloop_cancel_timeout(wpas_network_reenabled, wpa_s, NULL);
++              eloop_register_timeout(time_to_reenable, 0,
++                                     wpas_network_reenabled, wpa_s, NULL);
++              return 0;
++      }
 +
 +      if (wpa_s->p2p_mgmt)
 +              return 0; /* no normal connection on p2p_mgmt interface */
 +
 +      selected = wpa_supplicant_pick_network(wpa_s, &ssid);
 +
 +      if (selected) {
 +              int skip;
 +              skip = !wpa_supplicant_need_to_roam(wpa_s, selected, ssid);
 +              if (skip) {
 +                      if (new_scan)
 +                              wpa_supplicant_rsn_preauth_scan_results(wpa_s);
 +                      return 0;
 +              }
 +
 +              if (wpa_supplicant_connect(wpa_s, selected, ssid) < 0) {
 +                      wpa_dbg(wpa_s, MSG_DEBUG, "Connect failed");
 +                      return -1;
 +              }
 +              if (new_scan)
 +                      wpa_supplicant_rsn_preauth_scan_results(wpa_s);
 +              /*
 +               * Do not notify other virtual radios of scan results since we do not
 +               * want them to start other associations at the same time.
 +               */
 +              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) {
 +                      wpa_dbg(wpa_s, MSG_DEBUG, "Setup a new network");
 +                      wpa_supplicant_associate(wpa_s, NULL, ssid);
 +                      if (new_scan)
 +                              wpa_supplicant_rsn_preauth_scan_results(wpa_s);
 +              } 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
 +                      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 ||
 +                          wpa_s->show_group_started ||
 +                          wpa_s->p2p_in_invitation) {
 +                              /*
 +                               * Use shorter wait during P2P Provisioning
 +                               * state and during P2P join-a-group operation
 +                               * to speed up group formation.
 +                               */
 +                              timeout_sec = 0;
 +                              timeout_usec = 250000;
 +                              wpa_supplicant_req_new_scan(wpa_s, timeout_sec,
 +                                                          timeout_usec);
 +                              return 0;
 +                      }
 +#endif /* CONFIG_P2P */
 +#ifdef CONFIG_INTERWORKING
 +                      if (wpa_s->conf->auto_interworking &&
 +                          wpa_s->conf->interworking &&
 +                          wpa_s->conf->cred) {
 +                              wpa_dbg(wpa_s, MSG_DEBUG, "Interworking: "
 +                                      "start ANQP fetch since no matching "
 +                                      "networks found");
 +                              wpa_s->network_select = 1;
 +                              wpa_s->auto_network_select = 1;
 +                              interworking_start_fetch_anqp(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);
++
++                      wpa_msg_ctrl(wpa_s, MSG_INFO,
++                                   WPA_EVENT_NETWORK_NOT_FOUND);
 +              }
 +      }
 +      return 0;
 +}
 +
 +
 +static int wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s,
 +                                           union wpa_event_data *data)
 +{
 +      struct wpa_supplicant *ifs;
 +      int res;
 +
 +      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
 +               * this scan. Similarly, if scan results started a new operation on this
 +               * interface, do not notify other interfaces to avoid concurrent
 +               * operations during a connection attempt.
 +               */
 +              return 0;
 +      }
 +
 +      /*
 +       * Check other interfaces to see if they share the same radio. If
 +       * so, they get updated with this same scan 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 scan results from "
 +                                 "sibling", ifs->ifname);
 +                      _wpa_supplicant_event_scan_results(ifs, data, 0);
 +              }
 +      }
 +
 +      return 0;
 +}
 +
 +#endif /* CONFIG_NO_SCAN_PROCESSING */
 +
 +
 +int wpa_supplicant_fast_associate(struct wpa_supplicant *wpa_s)
 +{
 +#ifdef CONFIG_NO_SCAN_PROCESSING
 +      return -1;
 +#else /* CONFIG_NO_SCAN_PROCESSING */
 +      struct os_reltime now;
 +
-               if (wpa_s->conf->ap_scan == 1 &&
-                   wpa_s->drv_flags & WPA_DRIVER_FLAGS_BSS_SELECTION) {
-                       if (wpa_supplicant_assoc_update_ie(wpa_s) < 0)
-                               wpa_msg(wpa_s, MSG_WARNING,
-                                       "WPA/RSN IEs not updated");
-               }
++      if (wpa_s->last_scan_res_used == 0)
 +              return -1;
 +
 +      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, 1);
 +#endif /* CONFIG_NO_SCAN_PROCESSING */
 +}
 +
 +#ifdef CONFIG_WNM
 +
 +static void wnm_bss_keep_alive(void *eloop_ctx, void *sock_ctx)
 +{
 +      struct wpa_supplicant *wpa_s = eloop_ctx;
 +
 +      if (wpa_s->wpa_state < WPA_ASSOCIATED)
 +              return;
 +
 +      if (!wpa_s->no_keep_alive) {
 +              wpa_printf(MSG_DEBUG, "WNM: Send keep-alive to AP " MACSTR,
 +                         MAC2STR(wpa_s->bssid));
 +              /* TODO: could skip this if normal data traffic has been sent */
 +              /* TODO: Consider using some more appropriate data frame for
 +               * this */
 +              if (wpa_s->l2)
 +                      l2_packet_send(wpa_s->l2, wpa_s->bssid, 0x0800,
 +                                     (u8 *) "", 0);
 +      }
 +
 +#ifdef CONFIG_SME
 +      if (wpa_s->sme.bss_max_idle_period) {
 +              unsigned int msec;
 +              msec = wpa_s->sme.bss_max_idle_period * 1024; /* times 1000 */
 +              if (msec > 100)
 +                      msec -= 100;
 +              eloop_register_timeout(msec / 1000, msec % 1000 * 1000,
 +                                     wnm_bss_keep_alive, wpa_s, NULL);
 +      }
 +#endif /* CONFIG_SME */
 +}
 +
 +
 +static void wnm_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;
 +
 +#ifdef CONFIG_SME
 +      if (elems.bss_max_idle_period) {
 +              unsigned int msec;
 +              wpa_s->sme.bss_max_idle_period =
 +                      WPA_GET_LE16(elems.bss_max_idle_period);
 +              wpa_printf(MSG_DEBUG, "WNM: BSS Max Idle Period: %u (* 1000 "
 +                         "TU)%s", wpa_s->sme.bss_max_idle_period,
 +                         (elems.bss_max_idle_period[2] & 0x01) ?
 +                         " (protected keep-live required)" : "");
 +              if (wpa_s->sme.bss_max_idle_period == 0)
 +                      wpa_s->sme.bss_max_idle_period = 1;
 +              if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) {
 +                      eloop_cancel_timeout(wnm_bss_keep_alive, wpa_s, NULL);
 +                       /* msec times 1000 */
 +                      msec = wpa_s->sme.bss_max_idle_period * 1024;
 +                      if (msec > 100)
 +                              msec -= 100;
 +                      eloop_register_timeout(msec / 1000, msec % 1000 * 1000,
 +                                             wnm_bss_keep_alive, wpa_s,
 +                                             NULL);
 +              }
 +      }
 +#endif /* CONFIG_SME */
 +}
 +
 +#endif /* CONFIG_WNM */
 +
 +
 +void wnm_bss_keep_alive_deinit(struct wpa_supplicant *wpa_s)
 +{
 +#ifdef CONFIG_WNM
 +      eloop_cancel_timeout(wnm_bss_keep_alive, wpa_s, NULL);
 +#endif /* CONFIG_WNM */
 +}
 +
 +
 +#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)
 +{
 +      int l, len, found = 0, wpa_found, rsn_found;
 +      const u8 *p;
 +#ifdef CONFIG_IEEE80211R
 +      u8 bssid[ETH_ALEN];
 +#endif /* CONFIG_IEEE80211R */
 +
 +      wpa_dbg(wpa_s, MSG_DEBUG, "Association info event");
 +      if (data->assoc_info.req_ies)
 +              wpa_hexdump(MSG_DEBUG, "req_ies", data->assoc_info.req_ies,
 +                          data->assoc_info.req_ies_len);
 +      if (data->assoc_info.resp_ies) {
 +              wpa_hexdump(MSG_DEBUG, "resp_ies", data->assoc_info.resp_ies,
 +                          data->assoc_info.resp_ies_len);
 +#ifdef CONFIG_TDLS
 +              wpa_tdls_assoc_resp_ies(wpa_s->wpa, data->assoc_info.resp_ies,
 +                                      data->assoc_info.resp_ies_len);
 +#endif /* CONFIG_TDLS */
 +#ifdef CONFIG_WNM
 +              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",
 +                          data->assoc_info.beacon_ies,
 +                          data->assoc_info.beacon_ies_len);
 +      if (data->assoc_info.freq)
 +              wpa_dbg(wpa_s, MSG_DEBUG, "freq=%u MHz",
 +                      data->assoc_info.freq);
 +
 +      p = data->assoc_info.req_ies;
 +      l = data->assoc_info.req_ies_len;
 +
 +      /* Go through the IEs and make a copy of the WPA/RSN IE, if present. */
 +      while (p && l >= 2) {
 +              len = p[1] + 2;
 +              if (len > l) {
 +                      wpa_hexdump(MSG_DEBUG, "Truncated IE in assoc_info",
 +                                  p, l);
 +                      break;
 +              }
 +              if ((p[0] == WLAN_EID_VENDOR_SPECIFIC && p[1] >= 6 &&
 +                   (os_memcmp(&p[2], "\x00\x50\xF2\x01\x01\x00", 6) == 0)) ||
 +                  (p[0] == WLAN_EID_RSN && p[1] >= 2)) {
 +                      if (wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, p, len))
 +                              break;
 +                      found = 1;
 +                      wpa_find_assoc_pmkid(wpa_s);
 +                      break;
 +              }
 +              l -= len;
 +              p += len;
 +      }
 +      if (!found && data->assoc_info.req_ies)
 +              wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, NULL, 0);
 +
 +#ifdef CONFIG_IEEE80211R
 +#ifdef CONFIG_SME
 +      if (wpa_s->sme.auth_alg == WPA_AUTH_ALG_FT) {
 +              if (wpa_drv_get_bssid(wpa_s, bssid) < 0 ||
 +                  wpa_ft_validate_reassoc_resp(wpa_s->wpa,
 +                                               data->assoc_info.resp_ies,
 +                                               data->assoc_info.resp_ies_len,
 +                                               bssid) < 0) {
 +                      wpa_dbg(wpa_s, MSG_DEBUG, "FT: Validation of "
 +                              "Reassociation Response failed");
 +                      wpa_supplicant_deauthenticate(
 +                              wpa_s, WLAN_REASON_INVALID_IE);
 +                      return -1;
 +              }
 +      }
 +
 +      p = data->assoc_info.resp_ies;
 +      l = data->assoc_info.resp_ies_len;
 +
 +#ifdef CONFIG_WPS_STRICT
 +      if (p && wpa_s->current_ssid &&
 +          wpa_s->current_ssid->key_mgmt == WPA_KEY_MGMT_WPS) {
 +              struct wpabuf *wps;
 +              wps = ieee802_11_vendor_ie_concat(p, l, WPS_IE_VENDOR_TYPE);
 +              if (wps == NULL) {
 +                      wpa_msg(wpa_s, MSG_INFO, "WPS-STRICT: AP did not "
 +                              "include WPS IE in (Re)Association Response");
 +                      return -1;
 +              }
 +
 +              if (wps_validate_assoc_resp(wps) < 0) {
 +                      wpabuf_free(wps);
 +                      wpa_supplicant_deauthenticate(
 +                              wpa_s, WLAN_REASON_INVALID_IE);
 +                      return -1;
 +              }
 +              wpabuf_free(wps);
 +      }
 +#endif /* CONFIG_WPS_STRICT */
 +
 +      /* Go through the IEs and make a copy of the MDIE, if present. */
 +      while (p && l >= 2) {
 +              len = p[1] + 2;
 +              if (len > l) {
 +                      wpa_hexdump(MSG_DEBUG, "Truncated IE in assoc_info",
 +                                  p, l);
 +                      break;
 +              }
 +              if (p[0] == WLAN_EID_MOBILITY_DOMAIN &&
 +                  p[1] >= MOBILITY_DOMAIN_ID_LEN) {
 +                      wpa_s->sme.ft_used = 1;
 +                      os_memcpy(wpa_s->sme.mobility_domain, p + 2,
 +                                MOBILITY_DOMAIN_ID_LEN);
 +                      break;
 +              }
 +              l -= len;
 +              p += len;
 +      }
 +#endif /* CONFIG_SME */
 +
 +      /* Process FT when SME is in the driver */
 +      if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) &&
 +          wpa_ft_is_completed(wpa_s->wpa)) {
 +              if (wpa_drv_get_bssid(wpa_s, bssid) < 0 ||
 +                  wpa_ft_validate_reassoc_resp(wpa_s->wpa,
 +                                               data->assoc_info.resp_ies,
 +                                               data->assoc_info.resp_ies_len,
 +                                               bssid) < 0) {
 +                      wpa_dbg(wpa_s, MSG_DEBUG, "FT: Validation of "
 +                              "Reassociation Response failed");
 +                      wpa_supplicant_deauthenticate(
 +                              wpa_s, WLAN_REASON_INVALID_IE);
 +                      return -1;
 +              }
 +              wpa_dbg(wpa_s, MSG_DEBUG, "FT: Reassociation Response done");
 +      }
 +
 +      wpa_sm_set_ft_params(wpa_s->wpa, data->assoc_info.resp_ies,
 +                           data->assoc_info.resp_ies_len);
 +#endif /* CONFIG_IEEE80211R */
 +
 +      /* WPA/RSN IE from Beacon/ProbeResp */
 +      p = data->assoc_info.beacon_ies;
 +      l = data->assoc_info.beacon_ies_len;
 +
 +      /* Go through the IEs and make a copy of the WPA/RSN IEs, if present.
 +       */
 +      wpa_found = rsn_found = 0;
 +      while (p && l >= 2) {
 +              len = p[1] + 2;
 +              if (len > l) {
 +                      wpa_hexdump(MSG_DEBUG, "Truncated IE in beacon_ies",
 +                                  p, l);
 +                      break;
 +              }
 +              if (!wpa_found &&
 +                  p[0] == WLAN_EID_VENDOR_SPECIFIC && p[1] >= 6 &&
 +                  os_memcmp(&p[2], "\x00\x50\xF2\x01\x01\x00", 6) == 0) {
 +                      wpa_found = 1;
 +                      wpa_sm_set_ap_wpa_ie(wpa_s->wpa, p, len);
 +              }
 +
 +              if (!rsn_found &&
 +                  p[0] == WLAN_EID_RSN && p[1] >= 2) {
 +                      rsn_found = 1;
 +                      wpa_sm_set_ap_rsn_ie(wpa_s->wpa, p, len);
 +              }
 +
 +              l -= len;
 +              p += len;
 +      }
 +
 +      if (!wpa_found && data->assoc_info.beacon_ies)
 +              wpa_sm_set_ap_wpa_ie(wpa_s->wpa, NULL, 0);
 +      if (!rsn_found && data->assoc_info.beacon_ies)
 +              wpa_sm_set_ap_rsn_ie(wpa_s->wpa, NULL, 0);
 +      if (wpa_found || rsn_found)
 +              wpa_s->ap_ies_from_associnfo = 1;
 +
++#ifdef CONFIG_FST
++      wpabuf_free(wpa_s->received_mb_ies);
++      wpa_s->received_mb_ies = NULL;
++      if (wpa_s->fst) {
++              struct mb_ies_info mb_ies;
++
++              wpa_printf(MSG_DEBUG, "Looking for MB IE");
++              if (!mb_ies_info_by_ies(&mb_ies, data->assoc_info.resp_ies,
++                                      data->assoc_info.resp_ies_len))
++                      wpa_s->received_mb_ies = mb_ies_by_info(&mb_ies);
++      }
++#endif /* CONFIG_FST */
++
 +      if (wpa_s->assoc_freq && data->assoc_info.freq &&
 +          wpa_s->assoc_freq != data->assoc_info.freq) {
 +              wpa_printf(MSG_DEBUG, "Operating frequency changed from "
 +                         "%u to %u MHz",
 +                         wpa_s->assoc_freq, data->assoc_info.freq);
 +              wpa_supplicant_update_scan_results(wpa_s);
 +      }
 +
 +      wpa_s->assoc_freq = data->assoc_info.freq;
 +
 +      return 0;
 +}
 +
 +
 +static int wpa_supplicant_assoc_update_ie(struct wpa_supplicant *wpa_s)
 +{
 +      const u8 *bss_wpa = NULL, *bss_rsn = NULL;
 +
 +      if (!wpa_s->current_bss || !wpa_s->current_ssid)
 +              return -1;
 +
 +      if (!wpa_key_mgmt_wpa_any(wpa_s->current_ssid->key_mgmt))
 +              return 0;
 +
 +      bss_wpa = wpa_bss_get_vendor_ie(wpa_s->current_bss,
 +                                      WPA_IE_VENDOR_TYPE);
 +      bss_rsn = wpa_bss_get_ie(wpa_s->current_bss, WLAN_EID_RSN);
 +
 +      if (wpa_sm_set_ap_wpa_ie(wpa_s->wpa, bss_wpa,
 +                               bss_wpa ? 2 + bss_wpa[1] : 0) ||
 +          wpa_sm_set_ap_rsn_ie(wpa_s->wpa, bss_rsn,
 +                               bss_rsn ? 2 + bss_rsn[1] : 0))
 +              return -1;
 +
 +      return 0;
 +}
 +
 +
 +static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s,
 +                                     union wpa_event_data *data)
 +{
 +      u8 bssid[ETH_ALEN];
 +      int ft_completed;
++      int new_bss = 0;
 +
 +#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,
 +                                  data->assoc_info.req_ies_len,
 +                                  data->assoc_info.reassoc);
 +              return;
 +      }
 +#endif /* CONFIG_AP */
 +
++      eloop_cancel_timeout(wpas_network_reenabled, wpa_s, NULL);
++
 +      ft_completed = wpa_ft_is_completed(wpa_s->wpa);
 +      if (data && wpa_supplicant_event_associnfo(wpa_s, data) < 0)
 +              return;
 +
 +      if (wpa_drv_get_bssid(wpa_s, bssid) < 0) {
 +              wpa_dbg(wpa_s, MSG_ERROR, "Failed to get BSSID");
 +              wpa_supplicant_deauthenticate(
 +                      wpa_s, WLAN_REASON_DEAUTH_LEAVING);
 +              return;
 +      }
 +
 +      wpa_supplicant_set_state(wpa_s, WPA_ASSOCIATED);
 +      if (os_memcmp(bssid, wpa_s->bssid, ETH_ALEN) != 0) {
 +              wpa_dbg(wpa_s, MSG_DEBUG, "Associated to a new BSS: BSSID="
 +                      MACSTR, MAC2STR(bssid));
++              new_bss = 1;
 +              random_add_randomness(bssid, ETH_ALEN);
 +              os_memcpy(wpa_s->bssid, bssid, ETH_ALEN);
 +              os_memset(wpa_s->pending_bssid, 0, ETH_ALEN);
 +              wpas_notify_bssid_changed(wpa_s);
 +
 +              if (wpa_supplicant_dynamic_keys(wpa_s) && !ft_completed) {
 +                      wpa_clear_keys(wpa_s, bssid);
 +              }
 +              if (wpa_supplicant_select_config(wpa_s) < 0) {
 +                      wpa_supplicant_deauthenticate(
 +                              wpa_s, WLAN_REASON_DEAUTH_LEAVING);
 +                      return;
 +              }
++      }
 +
-               wpa_supplicant_cancel_sched_scan(wpa_s);
++      if (wpa_s->conf->ap_scan == 1 &&
++          wpa_s->drv_flags & WPA_DRIVER_FLAGS_BSS_SELECTION) {
++              if (wpa_supplicant_assoc_update_ie(wpa_s) < 0 && new_bss)
++                      wpa_msg(wpa_s, MSG_WARNING,
++                              "WPA/RSN IEs not updated");
 +      }
 +
 +#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));
 +      if (wpa_s->current_ssid) {
 +              /* When using scanning (ap_scan=1), SIM PC/SC interface can be
 +               * initialized before association, but for other modes,
 +               * initialize PC/SC here, if the current configuration needs
 +               * smartcard or SIM/USIM. */
 +              wpa_supplicant_scard_init(wpa_s, wpa_s->current_ssid);
 +      }
 +      wpa_sm_notify_assoc(wpa_s->wpa, bssid);
 +      if (wpa_s->l2)
 +              l2_packet_notify_auth_start(wpa_s->l2);
 +
 +      /*
 +       * Set portEnabled first to FALSE in order to get EAP state machine out
 +       * of the SUCCESS state and eapSuccess cleared. Without this, EAPOL PAE
 +       * state machine may transit to AUTHENTICATING state based on obsolete
 +       * eapSuccess and then trigger BE_AUTH to SUCCESS and PAE to
 +       * AUTHENTICATED without ever giving chance to EAP state machine to
 +       * reset the state.
 +       */
 +      if (!ft_completed) {
 +              eapol_sm_notify_portEnabled(wpa_s->eapol, FALSE);
 +              eapol_sm_notify_portValid(wpa_s->eapol, FALSE);
 +      }
 +      if (wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt) || ft_completed)
 +              eapol_sm_notify_eap_success(wpa_s->eapol, FALSE);
 +      /* 802.1X::portControl = Auto */
 +      eapol_sm_notify_portEnabled(wpa_s->eapol, TRUE);
 +      wpa_s->eapol_received = 0;
 +      if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE ||
 +          wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE ||
 +          (wpa_s->current_ssid &&
 +           wpa_s->current_ssid->mode == IEEE80211_MODE_IBSS)) {
 +              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)) {
 +                      /*
 +                       * Set the key after having received joined-IBSS event
 +                       * from the driver.
 +                       */
 +                      wpa_supplicant_set_wpa_none_key(wpa_s,
 +                                                      wpa_s->current_ssid);
 +              }
 +              wpa_supplicant_cancel_auth_timeout(wpa_s);
 +              wpa_supplicant_set_state(wpa_s, WPA_COMPLETED);
 +      } else if (!ft_completed) {
 +              /* Timeout for receiving the first EAPOL packet */
 +              wpa_supplicant_req_auth_timeout(wpa_s, 10, 0);
 +      }
 +      wpa_supplicant_cancel_scan(wpa_s);
 +
 +      if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE) &&
 +          wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt)) {
 +              /*
 +               * We are done; the driver will take care of RSN 4-way
 +               * handshake.
 +               */
 +              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);
 +      } else if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE) &&
 +                 wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt)) {
 +              /*
 +               * The driver will take care of RSN 4-way handshake, so we need
 +               * to allow EAPOL supplicant to complete its work without
 +               * waiting for WPA supplicant.
 +               */
 +              eapol_sm_notify_portValid(wpa_s->eapol, TRUE);
 +      } else if (ft_completed) {
 +              /*
 +               * FT protocol completed - make sure EAPOL state machine ends
 +               * up in authenticated.
 +               */
 +              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_s->last_eapol_matches_bssid = 0;
 +
 +      if (wpa_s->pending_eapol_rx) {
 +              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) {
 +                      wpa_dbg(wpa_s, MSG_DEBUG, "Process pending EAPOL "
 +                              "frame that was received just before "
 +                              "association notification");
 +                      wpa_supplicant_rx_eapol(
 +                              wpa_s, wpa_s->pending_eapol_rx_src,
 +                              wpabuf_head(wpa_s->pending_eapol_rx),
 +                              wpabuf_len(wpa_s->pending_eapol_rx));
 +              }
 +              wpabuf_free(wpa_s->pending_eapol_rx);
 +              wpa_s->pending_eapol_rx = NULL;
 +      }
 +
 +      if ((wpa_s->key_mgmt == WPA_KEY_MGMT_NONE ||
 +           wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) &&
 +          wpa_s->current_ssid &&
 +          (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC_DONE)) {
 +              /* Set static WEP keys again */
 +              wpa_set_wep_keys(wpa_s, wpa_s->current_ssid);
 +      }
 +
 +#ifdef CONFIG_IBSS_RSN
 +      if (wpa_s->current_ssid &&
 +          wpa_s->current_ssid->mode == WPAS_MODE_IBSS &&
 +          wpa_s->key_mgmt != WPA_KEY_MGMT_NONE &&
 +          wpa_s->key_mgmt != WPA_KEY_MGMT_WPA_NONE &&
 +          wpa_s->ibss_rsn == NULL) {
 +              wpa_s->ibss_rsn = ibss_rsn_init(wpa_s);
 +              if (!wpa_s->ibss_rsn) {
 +                      wpa_msg(wpa_s, MSG_INFO, "Failed to init IBSS RSN");
 +                      wpa_supplicant_deauthenticate(
 +                              wpa_s, WLAN_REASON_DEAUTH_LEAVING);
 +                      return;
 +              }
 +
 +              ibss_rsn_set_psk(wpa_s->ibss_rsn, wpa_s->current_ssid->psk);
 +      }
 +#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);
 +      }
 +}
 +
 +
 +static int disconnect_reason_recoverable(u16 reason_code)
 +{
 +      return reason_code == WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY ||
 +              reason_code == WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA ||
 +              reason_code == WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA;
 +}
 +
 +
 +static void wpa_supplicant_event_disassoc(struct wpa_supplicant *wpa_s,
 +                                        u16 reason_code,
 +                                        int locally_generated)
 +{
 +      const u8 *bssid;
 +
 +      if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE) {
 +              /*
 +               * At least Host AP driver and a Prism3 card seemed to be
 +               * generating streams of disconnected events when configuring
 +               * IBSS for WPA-None. Ignore them for now.
 +               */
 +              return;
 +      }
 +
 +      bssid = wpa_s->bssid;
 +      if (is_zero_ether_addr(bssid))
 +              bssid = wpa_s->pending_bssid;
 +
 +      if (!is_zero_ether_addr(bssid) ||
 +          wpa_s->wpa_state >= WPA_AUTHENTICATING) {
 +              wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DISCONNECTED "bssid=" MACSTR
 +                      " reason=%d%s",
 +                      MAC2STR(bssid), reason_code,
 +                      locally_generated ? " locally_generated=1" : "");
 +      }
 +}
 +
 +
 +static int could_be_psk_mismatch(struct wpa_supplicant *wpa_s, u16 reason_code,
 +                               int locally_generated)
 +{
 +      if (wpa_s->wpa_state != WPA_4WAY_HANDSHAKE ||
 +          !wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt))
 +              return 0; /* Not in 4-way handshake with PSK */
 +
 +      /*
 +       * It looks like connection was lost while trying to go through PSK
 +       * 4-way handshake. Filter out known disconnection cases that are caused
 +       * by something else than PSK mismatch to avoid confusing reports.
 +       */
 +
 +      if (locally_generated) {
 +              if (reason_code == WLAN_REASON_IE_IN_4WAY_DIFFERS)
 +                      return 0;
 +      }
 +
 +      return 1;
 +}
 +
 +
 +static void wpa_supplicant_event_disassoc_finish(struct wpa_supplicant *wpa_s,
 +                                               u16 reason_code,
 +                                               int locally_generated)
 +{
 +      const u8 *bssid;
 +      int authenticating;
 +      u8 prev_pending_bssid[ETH_ALEN];
 +      struct wpa_bss *fast_reconnect = NULL;
 +      struct wpa_ssid *fast_reconnect_ssid = NULL;
 +      struct wpa_ssid *last_ssid;
 +
 +      authenticating = wpa_s->wpa_state == WPA_AUTHENTICATING;
 +      os_memcpy(prev_pending_bssid, wpa_s->pending_bssid, ETH_ALEN);
 +
 +      if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE) {
 +              /*
 +               * At least Host AP driver and a Prism3 card seemed to be
 +               * generating streams of disconnected events when configuring
 +               * IBSS for WPA-None. Ignore them for now.
 +               */
 +              wpa_dbg(wpa_s, MSG_DEBUG, "Disconnect event - ignore in "
 +                      "IBSS/WPA-None mode");
 +              return;
 +      }
 +
 +      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");
 +              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 ||
 +           wpas_wps_searching(wpa_s))) {
 +              wpa_dbg(wpa_s, MSG_DEBUG, "Auto connect enabled: try to "
 +                      "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 &&
 +                  wpa_s->current_ssid->mode == WPAS_MODE_INFRA &&
 +                  !locally_generated &&
 +                  disconnect_reason_recoverable(reason_code)) {
 +                      /*
 +                       * It looks like the AP has dropped association with
 +                       * us, but could allow us to get back in. Try to
 +                       * reconnect to the same BSS without full scan to save
 +                       * time for some common cases.
 +                       */
 +                      fast_reconnect = wpa_s->current_bss;
 +                      fast_reconnect_ssid = wpa_s->current_ssid;
 +              } else if (wpa_s->wpa_state >= WPA_ASSOCIATING)
 +                      wpa_supplicant_req_scan(wpa_s, 0, 100000);
 +              else
 +                      wpa_dbg(wpa_s, MSG_DEBUG, "Do not request new "
 +                              "immediate scan");
 +      } else {
 +              wpa_dbg(wpa_s, MSG_DEBUG, "Auto connect disabled: do not "
 +                      "try to re-connect");
 +              wpa_s->reassociate = 0;
 +              wpa_s->disconnected = 1;
-       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.
-        */
++              if (!wpa_s->pno)
++                      wpa_supplicant_cancel_sched_scan(wpa_s);
 +      }
 +      bssid = wpa_s->bssid;
 +      if (is_zero_ether_addr(bssid))
 +              bssid = wpa_s->pending_bssid;
 +      if (wpa_s->wpa_state >= WPA_AUTHENTICATING)
 +              wpas_connection_failed(wpa_s, bssid);
 +      wpa_sm_notify_disassoc(wpa_s->wpa);
 +      if (locally_generated)
 +              wpa_s->disconnect_reason = -reason_code;
 +      else
 +              wpa_s->disconnect_reason = reason_code;
 +      wpas_notify_disconnect_reason(wpa_s);
 +      if (wpa_supplicant_dynamic_keys(wpa_s)) {
 +              wpa_dbg(wpa_s, MSG_DEBUG, "Disconnect event - remove keys");
 +              wpa_clear_keys(wpa_s, wpa_s->bssid);
 +      }
 +      last_ssid = wpa_s->current_ssid;
 +      wpa_supplicant_mark_disassoc(wpa_s);
 +
 +      if (authenticating && (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME)) {
 +              sme_disassoc_while_authenticating(wpa_s, prev_pending_bssid);
 +              wpa_s->current_ssid = last_ssid;
 +      }
 +
 +      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,
 +                                         fast_reconnect_ssid) < 0) {
 +                      /* Recover through full scan */
 +                      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);
 +      }
 +}
 +
 +
 +#ifdef CONFIG_DELAYED_MIC_ERROR_REPORT
 +void wpa_supplicant_delayed_mic_error_report(void *eloop_ctx, void *sock_ctx)
 +{
 +      struct wpa_supplicant *wpa_s = eloop_ctx;
 +
 +      if (!wpa_s->pending_mic_error_report)
 +              return;
 +
 +      wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Sending pending MIC error report");
 +      wpa_sm_key_request(wpa_s->wpa, 1, wpa_s->pending_mic_error_pairwise);
 +      wpa_s->pending_mic_error_report = 0;
 +}
 +#endif /* CONFIG_DELAYED_MIC_ERROR_REPORT */
 +
 +
 +static void
 +wpa_supplicant_event_michael_mic_failure(struct wpa_supplicant *wpa_s,
 +                                       union wpa_event_data *data)
 +{
 +      int pairwise;
 +      struct os_reltime t;
 +
 +      wpa_msg(wpa_s, MSG_WARNING, "Michael MIC failure detected");
 +      pairwise = (data && data->michael_mic_failure.unicast);
 +      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) {
 +                      /*
 +                       * Send the pending MIC error report immediately since
 +                       * we are going to start countermeasures and AP better
 +                       * do the same.
 +                       */
 +                      wpa_sm_key_request(wpa_s->wpa, 1,
 +                                         wpa_s->pending_mic_error_pairwise);
 +              }
 +
 +              /* Send the new MIC error report immediately since we are going
 +               * to start countermeasures and AP better do the same.
 +               */
 +              wpa_sm_key_request(wpa_s->wpa, 1, pairwise);
 +
 +              /* initialize countermeasures */
 +              wpa_s->countermeasures = 1;
 +
 +              wpa_blacklist_add(wpa_s, wpa_s->bssid);
 +
 +              wpa_msg(wpa_s, MSG_WARNING, "TKIP countermeasures started");
 +
 +              /*
 +               * Need to wait for completion of request frame. We do not get
 +               * any callback for the message completion, so just wait a
 +               * short while and hope for the best. */
 +              os_sleep(0, 10000);
 +
 +              wpa_drv_set_countermeasures(wpa_s, 1);
 +              wpa_supplicant_deauthenticate(wpa_s,
 +                                            WLAN_REASON_MICHAEL_MIC_FAILURE);
 +              eloop_cancel_timeout(wpa_supplicant_stop_countermeasures,
 +                                   wpa_s, NULL);
 +              eloop_register_timeout(60, 0,
 +                                     wpa_supplicant_stop_countermeasures,
 +                                     wpa_s, NULL);
 +              /* TODO: mark the AP rejected for 60 second. STA is
 +               * allowed to associate with another AP.. */
 +      } else {
 +#ifdef CONFIG_DELAYED_MIC_ERROR_REPORT
 +              if (wpa_s->mic_errors_seen) {
 +                      /*
 +                       * Reduce the effectiveness of Michael MIC error
 +                       * reports as a means for attacking against TKIP if
 +                       * more than one MIC failure is noticed with the same
 +                       * PTK. We delay the transmission of the reports by a
 +                       * random time between 0 and 60 seconds in order to
 +                       * force the attacker wait 60 seconds before getting
 +                       * the information on whether a frame resulted in a MIC
 +                       * failure.
 +                       */
 +                      u8 rval[4];
 +                      int sec;
 +
 +                      if (os_get_random(rval, sizeof(rval)) < 0)
 +                              sec = os_random() % 60;
 +                      else
 +                              sec = WPA_GET_BE32(rval) % 60;
 +                      wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Delay MIC error "
 +                              "report %d seconds", sec);
 +                      wpa_s->pending_mic_error_report = 1;
 +                      wpa_s->pending_mic_error_pairwise = pairwise;
 +                      eloop_cancel_timeout(
 +                              wpa_supplicant_delayed_mic_error_report,
 +                              wpa_s, NULL);
 +                      eloop_register_timeout(
 +                              sec, os_random() % 1000000,
 +                              wpa_supplicant_delayed_mic_error_report,
 +                              wpa_s, NULL);
 +              } else {
 +                      wpa_sm_key_request(wpa_s->wpa, 1, pairwise);
 +              }
 +#else /* CONFIG_DELAYED_MIC_ERROR_REPORT */
 +              wpa_sm_key_request(wpa_s->wpa, 1, pairwise);
 +#endif /* CONFIG_DELAYED_MIC_ERROR_REPORT */
 +      }
 +      wpa_s->last_michael_mic_error = t;
 +      wpa_s->mic_errors_seen++;
 +}
 +
 +
 +#ifdef CONFIG_TERMINATE_ONLASTIF
 +static int any_interfaces(struct wpa_supplicant *head)
 +{
 +      struct wpa_supplicant *wpa_s;
 +
 +      for (wpa_s = head; wpa_s != NULL; wpa_s = wpa_s->next)
 +              if (!wpa_s->interface_removed)
 +                      return 1;
 +      return 0;
 +}
 +#endif /* CONFIG_TERMINATE_ONLASTIF */
 +
 +
 +static void
 +wpa_supplicant_event_interface_status(struct wpa_supplicant *wpa_s,
 +                                    union wpa_event_data *data)
 +{
 +      if (os_strcmp(wpa_s->ifname, data->interface_status.ifname) != 0)
 +              return;
 +
 +      switch (data->interface_status.ievent) {
 +      case EVENT_INTERFACE_ADDED:
 +              if (!wpa_s->interface_removed)
 +                      break;
 +              wpa_s->interface_removed = 0;
 +              wpa_dbg(wpa_s, MSG_DEBUG, "Configured interface was added");
 +              if (wpa_supplicant_driver_init(wpa_s) < 0) {
 +                      wpa_msg(wpa_s, MSG_INFO, "Failed to initialize the "
 +                              "driver after interface was added");
 +              }
++
++#ifdef CONFIG_P2P
++              if (!wpa_s->global->p2p &&
++                  !wpa_s->global->p2p_disabled &&
++                  !wpa_s->conf->p2p_disabled &&
++                  (wpa_s->drv_flags &
++                   WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE) &&
++                  wpas_p2p_add_p2pdev_interface(
++                          wpa_s, wpa_s->global->params.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 */
++
 +              break;
 +      case EVENT_INTERFACE_REMOVED:
 +              wpa_dbg(wpa_s, MSG_DEBUG, "Configured interface was removed");
 +              wpa_s->interface_removed = 1;
 +              wpa_supplicant_mark_disassoc(wpa_s);
 +              wpa_supplicant_set_state(wpa_s, WPA_INTERFACE_DISABLED);
 +              l2_packet_deinit(wpa_s->l2);
 +              wpa_s->l2 = NULL;
++
++#ifdef CONFIG_P2P
++              if (wpa_s->global->p2p &&
++                  wpa_s->global->p2p_init_wpa_s->parent == wpa_s &&
++                  (wpa_s->drv_flags &
++                   WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE)) {
++                      wpa_dbg(wpa_s, MSG_DEBUG,
++                              "Removing P2P Device interface");
++                      wpa_supplicant_remove_iface(
++                              wpa_s->global, wpa_s->global->p2p_init_wpa_s,
++                              0);
++                      wpa_s->global->p2p_init_wpa_s = NULL;
++              }
++#endif /* CONFIG_P2P */
++
 +#ifdef CONFIG_TERMINATE_ONLASTIF
 +              /* check if last interface */
 +              if (!any_interfaces(wpa_s->global->ifaces))
 +                      eloop_terminate();
 +#endif /* CONFIG_TERMINATE_ONLASTIF */
 +              break;
 +      }
 +}
 +
 +
 +#ifdef CONFIG_PEERKEY
 +static void
 +wpa_supplicant_event_stkstart(struct wpa_supplicant *wpa_s,
 +                            union wpa_event_data *data)
 +{
 +      if (data == NULL)
 +              return;
 +      wpa_sm_stkstart(wpa_s->wpa, data->stkstart.peer);
 +}
 +#endif /* CONFIG_PEERKEY */
 +
 +
 +#ifdef CONFIG_TDLS
 +static void wpa_supplicant_event_tdls(struct wpa_supplicant *wpa_s,
 +                                    union wpa_event_data *data)
 +{
 +      if (data == NULL)
 +              return;
 +      switch (data->tdls.oper) {
 +      case TDLS_REQUEST_SETUP:
 +              wpa_tdls_remove(wpa_s->wpa, data->tdls.peer);
 +              if (wpa_tdls_is_external_setup(wpa_s->wpa))
 +                      wpa_tdls_start(wpa_s->wpa, data->tdls.peer);
 +              else
 +                      wpa_drv_tdls_oper(wpa_s, TDLS_SETUP, data->tdls.peer);
 +              break;
 +      case TDLS_REQUEST_TEARDOWN:
 +              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;
 +      }
 +}
 +#endif /* CONFIG_TDLS */
 +
 +
 +#ifdef CONFIG_WNM
 +static void wpa_supplicant_event_wnm(struct wpa_supplicant *wpa_s,
 +                                   union wpa_event_data *data)
 +{
 +      if (data == NULL)
 +              return;
 +      switch (data->wnm.oper) {
 +      case WNM_OPER_SLEEP:
 +              wpa_printf(MSG_DEBUG, "Start sending WNM-Sleep Request "
 +                         "(action=%d, intval=%d)",
 +                         data->wnm.sleep_action, data->wnm.sleep_intval);
 +              ieee802_11_send_wnmsleep_req(wpa_s, data->wnm.sleep_action,
 +                                           data->wnm.sleep_intval, NULL);
 +              break;
 +      }
 +}
 +#endif /* CONFIG_WNM */
 +
 +
 +#ifdef CONFIG_IEEE80211R
 +static void
 +wpa_supplicant_event_ft_response(struct wpa_supplicant *wpa_s,
 +                               union wpa_event_data *data)
 +{
 +      if (data == NULL)
 +              return;
 +
 +      if (wpa_ft_process_response(wpa_s->wpa, data->ft_ies.ies,
 +                                  data->ft_ies.ies_len,
 +                                  data->ft_ies.ft_action,
 +                                  data->ft_ies.target_ap,
 +                                  data->ft_ies.ric_ies,
 +                                  data->ft_ies.ric_ies_len) < 0) {
 +              /* TODO: prevent MLME/driver from trying to associate? */
 +      }
 +}
 +#endif /* CONFIG_IEEE80211R */
 +
 +
 +#ifdef CONFIG_IBSS_RSN
 +static void wpa_supplicant_event_ibss_rsn_start(struct wpa_supplicant *wpa_s,
 +                                              union wpa_event_data *data)
 +{
 +      struct wpa_ssid *ssid;
 +      if (wpa_s->wpa_state < WPA_ASSOCIATED)
 +              return;
 +      if (data == NULL)
 +              return;
 +      ssid = wpa_s->current_ssid;
 +      if (ssid == NULL)
 +              return;
 +      if (ssid->mode != WPAS_MODE_IBSS || !wpa_key_mgmt_wpa(ssid->key_mgmt))
 +              return;
 +
 +      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 */
 +
 +
 +#ifdef CONFIG_IEEE80211R
 +static void ft_rx_action(struct wpa_supplicant *wpa_s, const u8 *data,
 +                       size_t len)
 +{
 +      const u8 *sta_addr, *target_ap_addr;
 +      u16 status;
 +
 +      wpa_hexdump(MSG_MSGDUMP, "FT: RX Action", data, len);
 +      if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME))
 +              return; /* only SME case supported for now */
 +      if (len < 1 + 2 * ETH_ALEN + 2)
 +              return;
 +      if (data[0] != 2)
 +              return; /* Only FT Action Response is supported for now */
 +      sta_addr = data + 1;
 +      target_ap_addr = data + 1 + ETH_ALEN;
 +      status = WPA_GET_LE16(data + 1 + 2 * ETH_ALEN);
 +      wpa_dbg(wpa_s, MSG_DEBUG, "FT: Received FT Action Response: STA "
 +              MACSTR " TargetAP " MACSTR " status %u",
 +              MAC2STR(sta_addr), MAC2STR(target_ap_addr), status);
 +
 +      if (os_memcmp(sta_addr, wpa_s->own_addr, ETH_ALEN) != 0) {
 +              wpa_dbg(wpa_s, MSG_DEBUG, "FT: Foreign STA Address " MACSTR
 +                      " in FT Action Response", MAC2STR(sta_addr));
 +              return;
 +      }
 +
 +      if (status) {
 +              wpa_dbg(wpa_s, MSG_DEBUG, "FT: FT Action Response indicates "
 +                      "failure (status code %d)", status);
 +              /* TODO: report error to FT code(?) */
 +              return;
 +      }
 +
 +      if (wpa_ft_process_response(wpa_s->wpa, data + 1 + 2 * ETH_ALEN + 2,
 +                                  len - (1 + 2 * ETH_ALEN + 2), 1,
 +                                  target_ap_addr, NULL, 0) < 0)
 +              return;
 +
 +#ifdef CONFIG_SME
 +      {
 +              struct wpa_bss *bss;
 +              bss = wpa_bss_get_bssid(wpa_s, target_ap_addr);
 +              if (bss)
 +                      wpa_s->sme.freq = bss->freq;
 +              wpa_s->sme.auth_alg = WPA_AUTH_ALG_FT;
 +              sme_associate(wpa_s, WPAS_MODE_INFRA, target_ap_addr,
 +                            WLAN_AUTH_FT);
 +      }
 +#endif /* CONFIG_SME */
 +}
 +#endif /* CONFIG_IEEE80211R */
 +
 +
 +static void wpa_supplicant_event_unprot_deauth(struct wpa_supplicant *wpa_s,
 +                                             struct unprot_deauth *e)
 +{
 +#ifdef CONFIG_IEEE80211W
 +      wpa_printf(MSG_DEBUG, "Unprotected Deauthentication frame "
 +                 "dropped: " MACSTR " -> " MACSTR
 +                 " (reason code %u)",
 +                 MAC2STR(e->sa), MAC2STR(e->da), e->reason_code);
 +      sme_event_unprot_disconnect(wpa_s, e->sa, e->da, e->reason_code);
 +#endif /* CONFIG_IEEE80211W */
 +}
 +
 +
 +static void wpa_supplicant_event_unprot_disassoc(struct wpa_supplicant *wpa_s,
 +                                               struct unprot_disassoc *e)
 +{
 +#ifdef CONFIG_IEEE80211W
 +      wpa_printf(MSG_DEBUG, "Unprotected Disassociation frame "
 +                 "dropped: " MACSTR " -> " MACSTR
 +                 " (reason code %u)",
 +                 MAC2STR(e->sa), MAC2STR(e->da), e->reason_code);
 +      sme_event_unprot_disconnect(wpa_s, e->sa, e->da, e->reason_code);
 +#endif /* CONFIG_IEEE80211W */
 +}
 +
 +
 +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 */
 +
-               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);
-               }
 +      dl_list_for_each(ifs, &wpa_s->radio->ifaces, struct wpa_supplicant,
 +                       radio_list) {
- #ifdef CONFIG_P2P
-       struct wpa_supplicant *ifs;
- #endif /* CONFIG_P2P */
++              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);
++      }
++
++      /* Restart sched_scan with updated channel list */
++      if (wpa_s->sched_scanning) {
++              wpa_dbg(wpa_s, MSG_DEBUG,
++                      "Channel list changed restart sched scan.");
++              wpa_supplicant_cancel_sched_scan(wpa_s);
++              wpa_supplicant_req_scan(wpa_s, 0, 0);
 +      }
++
++      wpas_p2p_update_channel_list(wpa_s, WPAS_P2P_CHANNEL_UPDATE_DRIVER);
 +}
 +
 +
 +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;
 +      }
 +
++#ifdef CONFIG_FST
++      if (mgmt->u.action.category == WLAN_ACTION_FST && wpa_s->fst) {
++              fst_rx_action(wpa_s->fst, mgmt, len);
++              return;
++      }
++#endif /* CONFIG_FST */
++
 +      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)
 +{
-               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;
 +      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");
-               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);
 +
++              /*
++               * The update channel flow will also take care of moving a GO
++               * from the unsafe frequency if needed.
++               */
++              wpas_p2p_update_channel_list(wpa_s,
++                                           WPAS_P2P_CHANNEL_UPDATE_AVOID);
 +      }
 +#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;
++      int resched;
 +
 +      if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED &&
 +          event != EVENT_INTERFACE_ENABLED &&
 +          event != EVENT_INTERFACE_STATUS &&
 +          event != EVENT_SCHED_SCAN_STOPPED) {
 +              wpa_dbg(wpa_s, MSG_DEBUG,
 +                      "Ignore event %s (%d) while interface is disabled",
 +                      event_to_string(event), event);
 +              return;
 +      }
 +
 +#ifndef CONFIG_NO_STDOUT_DEBUG
 +{
 +      int level = MSG_DEBUG;
 +
 +      if (event == EVENT_RX_MGMT && data->rx_mgmt.frame_len >= 24) {
 +              const struct ieee80211_hdr *hdr;
 +              u16 fc;
 +              hdr = (const struct ieee80211_hdr *) data->rx_mgmt.frame;
 +              fc = le_to_host16(hdr->frame_control);
 +              if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT &&
 +                  WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_BEACON)
 +                      level = MSG_EXCESSIVE;
 +      }
 +
 +      wpa_dbg(wpa_s, level, "Event %s (%d) received",
 +              event_to_string(event), event);
 +}
 +#endif /* CONFIG_NO_STDOUT_DEBUG */
 +
 +      switch (event) {
 +      case EVENT_AUTH:
 +              sme_event_auth(wpa_s, data);
 +              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:
 +              wpas_event_disassoc(wpa_s,
 +                                  data ? &data->disassoc_info : NULL);
 +              break;
 +      case EVENT_DEAUTH:
 +              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:
 +              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:
 +              wpa_supplicant_event_associnfo(wpa_s, data);
 +              break;
 +      case EVENT_INTERFACE_STATUS:
 +              wpa_supplicant_event_interface_status(wpa_s, data);
 +              break;
 +      case EVENT_PMKID_CANDIDATE:
 +              wpa_supplicant_event_pmkid_candidate(wpa_s, data);
 +              break;
 +#ifdef CONFIG_PEERKEY
 +      case EVENT_STKSTART:
 +              wpa_supplicant_event_stkstart(wpa_s, data);
 +              break;
 +#endif /* CONFIG_PEERKEY */
 +#ifdef CONFIG_TDLS
 +      case EVENT_TDLS:
 +              wpa_supplicant_event_tdls(wpa_s, data);
 +              break;
 +#endif /* CONFIG_TDLS */
 +#ifdef CONFIG_WNM
 +      case EVENT_WNM:
 +              wpa_supplicant_event_wnm(wpa_s, data);
 +              break;
 +#endif /* CONFIG_WNM */
 +#ifdef CONFIG_IEEE80211R
 +      case EVENT_FT_RESPONSE:
 +              wpa_supplicant_event_ft_response(wpa_s, data);
 +              break;
 +#endif /* CONFIG_IEEE80211R */
 +#ifdef CONFIG_IBSS_RSN
 +      case EVENT_IBSS_RSN_START:
 +              wpa_supplicant_event_ibss_rsn_start(wpa_s, data);
 +              break;
 +#endif /* CONFIG_IBSS_RSN */
 +      case EVENT_ASSOC_REJECT:
 +              if (data->assoc_reject.bssid)
 +                      wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_ASSOC_REJECT
 +                              "bssid=" MACSTR " status_code=%u",
 +                              MAC2STR(data->assoc_reject.bssid),
 +                              data->assoc_reject.status_code);
 +              else
 +                      wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_ASSOC_REJECT
 +                              "status_code=%u",
 +                              data->assoc_reject.status_code);
 +              if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME)
 +                      sme_event_assoc_reject(wpa_s, data);
 +              else {
 +                      const u8 *bssid = data->assoc_reject.bssid;
 +                      if (bssid == NULL || is_zero_ether_addr(bssid))
 +                              bssid = wpa_s->pending_bssid;
 +                      wpas_connection_failed(wpa_s, bssid);
 +                      wpa_supplicant_mark_disassoc(wpa_s);
 +              }
 +              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;
 +      case EVENT_TX_STATUS:
 +              wpa_dbg(wpa_s, MSG_DEBUG, "EVENT_TX_STATUS dst=" MACSTR
 +                      " type=%d stype=%d",
 +                      MAC2STR(data->tx_status.dst),
 +                      data->tx_status.type, data->tx_status.stype);
 +#ifdef CONFIG_AP
 +              if (wpa_s->ap_iface == NULL) {
 +#ifdef CONFIG_OFFCHANNEL
 +                      if (data->tx_status.type == WLAN_FC_TYPE_MGMT &&
 +                          data->tx_status.stype == WLAN_FC_STYPE_ACTION)
 +                              offchannel_send_action_tx_status(
 +                                      wpa_s, data->tx_status.dst,
 +                                      data->tx_status.data,
 +                                      data->tx_status.data_len,
 +                                      data->tx_status.ack ?
 +                                      OFFCHANNEL_SEND_ACTION_SUCCESS :
 +                                      OFFCHANNEL_SEND_ACTION_NO_ACK);
 +#endif /* CONFIG_OFFCHANNEL */
 +                      break;
 +              }
 +#endif /* CONFIG_AP */
 +#ifdef CONFIG_OFFCHANNEL
 +              wpa_dbg(wpa_s, MSG_DEBUG, "EVENT_TX_STATUS pending_dst="
 +                      MACSTR, MAC2STR(wpa_s->parent->pending_action_dst));
 +              /*
 +               * Catch TX status events for Action frames we sent via group
 +               * interface in GO mode.
 +               */
 +              if (data->tx_status.type == WLAN_FC_TYPE_MGMT &&
 +                  data->tx_status.stype == WLAN_FC_STYPE_ACTION &&
 +                  os_memcmp(wpa_s->parent->pending_action_dst,
 +                            data->tx_status.dst, ETH_ALEN) == 0) {
 +                      offchannel_send_action_tx_status(
 +                              wpa_s->parent, data->tx_status.dst,
 +                              data->tx_status.data,
 +                              data->tx_status.data_len,
 +                              data->tx_status.ack ?
 +                              OFFCHANNEL_SEND_ACTION_SUCCESS :
 +                              OFFCHANNEL_SEND_ACTION_NO_ACK);
 +                      break;
 +              }
 +#endif /* CONFIG_OFFCHANNEL */
 +#ifdef CONFIG_AP
 +              switch (data->tx_status.type) {
 +              case WLAN_FC_TYPE_MGMT:
 +                      ap_mgmt_tx_cb(wpa_s, data->tx_status.data,
 +                                    data->tx_status.data_len,
 +                                    data->tx_status.stype,
 +                                    data->tx_status.ack);
 +                      break;
 +              case WLAN_FC_TYPE_DATA:
 +                      ap_tx_status(wpa_s, data->tx_status.dst,
 +                                   data->tx_status.data,
 +                                   data->tx_status.data_len,
 +                                   data->tx_status.ack);
 +                      break;
 +              }
 +#endif /* CONFIG_AP */
 +              break;
 +#ifdef CONFIG_AP
 +      case EVENT_EAPOL_TX_STATUS:
 +              ap_eapol_tx_status(wpa_s, data->eapol_tx_status.dst,
 +                                 data->eapol_tx_status.data,
 +                                 data->eapol_tx_status.data_len,
 +                                 data->eapol_tx_status.ack);
 +              break;
 +      case EVENT_DRIVER_CLIENT_POLL_OK:
 +              ap_client_poll_ok(wpa_s, data->client_poll.addr);
 +              break;
 +      case EVENT_RX_FROM_UNKNOWN:
 +              if (wpa_s->ap_iface == NULL)
 +                      break;
 +              ap_rx_from_unknown_sta(wpa_s, data->rx_from_unknown.addr,
 +                                     data->rx_from_unknown.wds);
 +              break;
 +      case EVENT_CH_SWITCH:
 +              if (!data)
 +                      break;
 +              if (!wpa_s->ap_iface) {
 +                      wpa_dbg(wpa_s, MSG_DEBUG, "AP: Ignore channel switch "
 +                              "event in non-AP mode");
 +                      break;
 +              }
 +
 +              wpas_ap_ch_switch(wpa_s, data->ch_switch.freq,
 +                                data->ch_switch.ht_enabled,
 +                                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) {
 +                              const u8 *src = mgmt->sa;
 +                              const u8 *ie = mgmt->u.probe_req.variable;
 +                              size_t ie_len = data->rx_mgmt.frame_len -
 +                                      (mgmt->u.probe_req.variable -
 +                                       data->rx_mgmt.frame);
 +                              wpas_p2p_probe_req_rx(
 +                                      wpa_s, src, mgmt->da,
 +                                      mgmt->bssid, ie, ie_len,
++                                      data->rx_mgmt.freq,
 +                                      data->rx_mgmt.ssi_signal);
 +                              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 &&
 +                  data->rx_mgmt.frame_len > 24) {
 +                      const u8 *ie = mgmt->u.probe_req.variable;
 +                      size_t ie_len = data->rx_mgmt.frame_len -
 +                              (mgmt->u.probe_req.variable -
 +                               data->rx_mgmt.frame);
 +
 +                      wpas_notify_preq(wpa_s, mgmt->sa, mgmt->da,
 +                                       mgmt->bssid, ie, ie_len,
 +                                       data->rx_mgmt.ssi_signal);
 +              }
 +
 +              ap_mgmt_rx(wpa_s, &data->rx_mgmt);
 +#endif /* CONFIG_AP */
 +              break;
 +              }
 +      case EVENT_RX_PROBE_REQ:
 +              if (data->rx_probe_req.sa == NULL ||
 +                  data->rx_probe_req.ie == NULL)
 +                      break;
 +#ifdef CONFIG_AP
 +              if (wpa_s->ap_iface) {
 +                      hostapd_probe_req_rx(wpa_s->ap_iface->bss[0],
 +                                           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);
 +                      break;
 +              }
 +#endif /* CONFIG_AP */
 +              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,
++                                    0,
 +                                    data->rx_probe_req.ssi_signal);
 +              break;
 +      case EVENT_REMAIN_ON_CHANNEL:
 +#ifdef CONFIG_OFFCHANNEL
 +              offchannel_remain_on_channel_cb(
 +                      wpa_s, data->remain_on_channel.freq,
 +                      data->remain_on_channel.duration);
 +#endif /* CONFIG_OFFCHANNEL */
 +              wpas_p2p_remain_on_channel_cb(
 +                      wpa_s, data->remain_on_channel.freq,
 +                      data->remain_on_channel.duration);
 +              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 */
 +              wpas_p2p_cancel_remain_on_channel_cb(
 +                      wpa_s, data->remain_on_channel.freq);
 +              break;
 +      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,
 +                      data->signal_change.current_noise,
 +                      data->signal_change.current_txrate);
 +              break;
 +      case EVENT_INTERFACE_ENABLED:
 +              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,
 +                                                       WPA_COMPLETED);
 +#else /* CONFIG_AP */
 +                      wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
 +                      wpa_supplicant_req_scan(wpa_s, 0, 0);
 +#endif /* CONFIG_AP */
 +              }
 +              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:
 +              wpa_supplicant_update_channel_list(
 +                      wpa_s, &data->channel_list_changed);
 +              break;
 +      case EVENT_INTERFACE_UNAVAILABLE:
 +              wpas_p2p_interface_unavailable(wpa_s);
 +              break;
 +      case EVENT_BEST_CHANNEL:
 +              wpa_dbg(wpa_s, MSG_DEBUG, "Best channel event received "
 +                      "(%d %d %d)",
 +                      data->best_chan.freq_24, data->best_chan.freq_5,
 +                      data->best_chan.freq_overall);
 +              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;
 +              wpas_p2p_update_best_channels(wpa_s, data->best_chan.freq_24,
 +                                            data->best_chan.freq_5,
 +                                            data->best_chan.freq_overall);
 +              break;
 +      case EVENT_UNPROT_DEAUTH:
 +              wpa_supplicant_event_unprot_deauth(wpa_s,
 +                                                 &data->unprot_deauth);
 +              break;
 +      case EVENT_UNPROT_DISASSOC:
 +              wpa_supplicant_event_unprot_disassoc(wpa_s,
 +                                                   &data->unprot_disassoc);
 +              break;
 +      case EVENT_STATION_LOW_ACK:
 +#ifdef CONFIG_AP
 +              if (wpa_s->ap_iface && data)
 +                      hostapd_event_sta_low_ack(wpa_s->ap_iface->bss[0],
 +                                                data->low_ack.addr);
 +#endif /* CONFIG_AP */
 +#ifdef CONFIG_TDLS
 +              if (data)
 +                      wpa_tdls_disable_unreachable_link(wpa_s->wpa,
 +                                                        data->low_ack.addr);
 +#endif /* CONFIG_TDLS */
 +              break;
 +      case EVENT_IBSS_PEER_LOST:
 +#ifdef CONFIG_IBSS_RSN
 +              ibss_rsn_stop(wpa_s->ibss_rsn, data->ibss_peer_lost.peer);
 +#endif /* CONFIG_IBSS_RSN */
 +              break;
 +      case EVENT_DRIVER_GTK_REKEY:
 +              if (os_memcmp(data->driver_gtk_rekey.bssid,
 +                            wpa_s->bssid, ETH_ALEN))
 +                      break;
 +              if (!wpa_s->wpa)
 +                      break;
 +              wpa_sm_update_replay_ctr(wpa_s->wpa,
 +                                       data->driver_gtk_rekey.replay_ctr);
 +              break;
 +      case EVENT_SCHED_SCAN_STOPPED:
 +              wpa_s->pno = 0;
 +              wpa_s->sched_scanning = 0;
++              resched = wpa_s->scanning;
 +              wpa_supplicant_notify_scanning(wpa_s, 0);
 +
 +              if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED)
 +                      break;
 +
 +              /*
 +               * 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) {
 +                      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);
++              } else if (resched) {
++                      wpa_supplicant_req_scan(wpa_s, 0, 0);
 +              }
 +
 +              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)
 +                      break;
 +              hostapd_event_connect_failed_reason(
 +                      wpa_s->ap_iface->bss[0],
 +                      data->connect_failed_reason.addr,
 +                      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 b9cd68193b2d8ac467e6c4cf28c8b7fab47d39d6,0000000000000000000000000000000000000000..a1afc85ff9bb693a7eb28d0741ce315fc9fb98ad
mode 100644,000000..100644
--- /dev/null
@@@ -1,1007 -1,0 +1,1009 @@@
-       u8 osu_ssid[32];
 +/*
 + * Copyright (c) 2009, Atheros Communications, 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_common.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"
 +
 +
 +#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];
-       freq = wpa_s->assoc_freq;
++      u8 osu_ssid[SSID_MAX_LEN];
 +      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, pps_mo_id >= 0 ? 7 : 5);
 +      wpabuf_put_be24(buf, OUI_WFA);
 +      wpabuf_put_u8(buf, HS20_INDICATION_OUI_TYPE);
 +      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);
 +}
 +
 +
 +int is_hs20_network(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
 +                  struct wpa_bss *bss)
 +{
 +      if (!wpa_s->conf->hs20 || !ssid)
 +              return 0;
 +
 +      if (ssid->parent_cred)
 +              return 1;
 +
 +      if (bss && !wpa_bss_get_vendor_ie(bss, HS20_IE_VENDOR_TYPE))
 +              return 0;
 +
 +      /*
 +       * This may catch some non-Hotspot 2.0 cases, but it is safer to do that
 +       * than cause Hotspot 2.0 connections without indication element getting
 +       * added. Non-Hotspot 2.0 APs should ignore the unknown vendor element.
 +       */
 +
 +      if (!(ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X))
 +              return 0;
 +      if (!(ssid->pairwise_cipher & WPA_CIPHER_CCMP))
 +              return 0;
 +      if (ssid->proto != WPA_PROTO_RSN)
 +              return 0;
 +
 +      return 1;
 +}
 +
 +
 +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)
 +{
 +      u8 *len_pos;
 +
 +      if (buf == NULL)
 +              return;
 +
 +      len_pos = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
 +      wpabuf_put_be24(buf, OUI_WFA);
 +      wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
 +      if (stypes == BIT(HS20_STYPE_NAI_HOME_REALM_QUERY)) {
 +              wpabuf_put_u8(buf, HS20_STYPE_NAI_HOME_REALM_QUERY);
 +              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);
 +              wpabuf_put_u8(buf, 0); /* Reserved */
 +              for (i = 0; i < 32; i++) {
 +                      if (stypes & BIT(i))
 +                              wpabuf_put_u8(buf, i);
 +              }
 +      }
 +      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;
 +}
 +
 +
 +int hs20_anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst, u32 stypes,
 +                     const u8 *payload, size_t payload_len)
 +{
 +      struct wpabuf *buf;
 +      int ret = 0;
 +      int freq;
 +      struct wpa_bss *bss;
 +      int res;
 +
-       if (bss) {
-               wpa_bss_anqp_unshare_alloc(bss);
-               freq = bss->freq;
-       }
-       if (freq <= 0)
 +      bss = wpa_bss_get_bssid(wpa_s, dst);
-               if (osu_ssid_len > 32) {
++      if (!bss) {
++              wpa_printf(MSG_WARNING,
++                         "ANQP: Cannot send query to unknown BSS "
++                         MACSTR, MAC2STR(dst));
 +              return -1;
++      }
++
++      wpa_bss_anqp_unshare_alloc(bss);
++      freq = bss->freq;
 +
 +      wpa_printf(MSG_DEBUG, "HS20: ANQP Query Request to " MACSTR " for "
 +                 "subtypes 0x%x", MAC2STR(dst), stypes);
 +
 +      buf = hs20_build_anqp_req(stypes, payload, payload_len);
 +      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");
 +              wpabuf_free(buf);
 +              ret = -1;
 +      } else
 +              wpa_printf(MSG_DEBUG, "ANQP: Query started with dialog token "
 +                         "%u", res);
 +
 +      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,
 +                                struct wpa_bss *bss, const u8 *sa,
 +                                const u8 *data, size_t slen)
 +{
 +      const u8 *pos = data;
 +      u8 subtype;
 +      struct wpa_bss_anqp *anqp = NULL;
 +      int ret;
 +
 +      if (slen < 2)
 +              return;
 +
 +      if (bss)
 +              anqp = bss->anqp;
 +
 +      subtype = *pos++;
 +      slen--;
 +
 +      pos++; /* Reserved */
 +      slen--;
 +
 +      switch (subtype) {
 +      case HS20_STYPE_CAPABILITY_LIST:
 +              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
 +                      " Operator Friendly Name", MAC2STR(sa));
 +              wpa_hexdump_ascii(MSG_DEBUG, "oper friendly name", pos, slen);
 +              if (anqp) {
 +                      wpabuf_free(anqp->hs20_operator_friendly_name);
 +                      anqp->hs20_operator_friendly_name =
 +                              wpabuf_alloc_copy(pos, slen);
 +              }
 +              break;
 +      case HS20_STYPE_WAN_METRICS:
 +              wpa_hexdump(MSG_DEBUG, "WAN Metrics", pos, slen);
 +              if (slen < 13) {
 +                      wpa_dbg(wpa_s, MSG_DEBUG, "HS 2.0: Too short WAN "
 +                              "Metrics value from " MACSTR, MAC2STR(sa));
 +                      break;
 +              }
 +              wpa_msg(wpa_s, MSG_INFO, "RX-HS20-ANQP " MACSTR
 +                      " WAN Metrics %02x:%u:%u:%u:%u:%u", MAC2STR(sa),
 +                      pos[0], WPA_GET_LE32(pos + 1), WPA_GET_LE32(pos + 5),
 +                      pos[9], pos[10], WPA_GET_LE16(pos + 11));
 +              if (anqp) {
 +                      wpabuf_free(anqp->hs20_wan_metrics);
 +                      anqp->hs20_wan_metrics = wpabuf_alloc_copy(pos, slen);
 +              }
 +              break;
 +      case HS20_STYPE_CONNECTION_CAPABILITY:
 +              wpa_msg(wpa_s, MSG_INFO, "RX-HS20-ANQP " MACSTR
 +                      " Connection Capability", MAC2STR(sa));
 +              wpa_hexdump_ascii(MSG_DEBUG, "conn capability", pos, slen);
 +              if (anqp) {
 +                      wpabuf_free(anqp->hs20_connection_capability);
 +                      anqp->hs20_connection_capability =
 +                              wpabuf_alloc_copy(pos, slen);
 +              }
 +              break;
 +      case HS20_STYPE_OPERATING_CLASS:
 +              wpa_msg(wpa_s, MSG_INFO, "RX-HS20-ANQP " MACSTR
 +                      " Operating Class", MAC2STR(sa));
 +              wpa_hexdump_ascii(MSG_DEBUG, "Operating Class", pos, slen);
 +              if (anqp) {
 +                      wpabuf_free(anqp->hs20_operating_class);
 +                      anqp->hs20_operating_class =
 +                              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 > SSID_MAX_LEN) {
 +                      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);
 +}
index d0ae135bdf7253d7276f531cd79090afcade5898,0000000000000000000000000000000000000000..d9d0ae7f10dd63c25a04175d32d3682bac5c3410
mode 100644,000000..100644
--- /dev/null
@@@ -1,916 -1,0 +1,920 @@@
-       wpa_deinit(ibss_rsn->auth_group);
 +/*
 + * wpa_supplicant - IBSS RSN
 + * 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.
 + */
 +
 +#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)
 +{
 +      struct ibss_rsn_peer *peer;
 +
 +      for (peer = ibss_rsn->peers; peer; peer = peer->next)
 +              if (os_memcmp(addr, peer->addr, ETH_ALEN) == 0)
 +                      break;
 +      return peer;
 +}
 +
 +
 +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);
 +}
 +
 +
 +static void supp_set_state(void *ctx, enum wpa_states state)
 +{
 +      struct ibss_rsn_peer *peer = ctx;
 +      peer->supp_state = state;
 +}
 +
 +
 +static enum wpa_states supp_get_state(void *ctx)
 +{
 +      struct ibss_rsn_peer *peer = ctx;
 +      return peer->supp_state;
 +}
 +
 +
 +static int supp_ether_send(void *ctx, const u8 *dest, u16 proto, const u8 *buf,
 +                         size_t len)
 +{
 +      struct ibss_rsn_peer *peer = ctx;
 +      struct wpa_supplicant *wpa_s = peer->ibss_rsn->wpa_s;
 +
 +      wpa_printf(MSG_DEBUG, "SUPP: %s(dest=" MACSTR " proto=0x%04x "
 +                 "len=%lu)",
 +                 __func__, MAC2STR(dest), proto, (unsigned long) len);
 +
 +      if (wpa_s->l2)
 +              return l2_packet_send(wpa_s->l2, dest, proto, buf, len);
 +
 +      return -1;
 +}
 +
 +
 +static u8 * supp_alloc_eapol(void *ctx, u8 type, const void *data,
 +                           u16 data_len, size_t *msg_len, void **data_pos)
 +{
 +      struct ieee802_1x_hdr *hdr;
 +
 +      wpa_printf(MSG_DEBUG, "SUPP: %s(type=%d data_len=%d)",
 +                 __func__, type, data_len);
 +
 +      *msg_len = sizeof(*hdr) + data_len;
 +      hdr = os_malloc(*msg_len);
 +      if (hdr == NULL)
 +              return NULL;
 +
 +      hdr->version = 2;
 +      hdr->type = type;
 +      hdr->length = host_to_be16(data_len);
 +
 +      if (data)
 +              os_memcpy(hdr + 1, data, data_len);
 +      else
 +              os_memset(hdr + 1, 0, data_len);
 +
 +      if (data_pos)
 +              *data_pos = hdr + 1;
 +
 +      return (u8 *) hdr;
 +}
 +
 +
 +static int supp_get_beacon_ie(void *ctx)
 +{
 +      struct ibss_rsn_peer *peer = ctx;
 +
 +      wpa_printf(MSG_DEBUG, "SUPP: %s", __func__);
 +      /* TODO: get correct RSN IE */
 +      return wpa_sm_set_ap_rsn_ie(peer->supp,
 +                                  (u8 *) "\x30\x14\x01\x00"
 +                                  "\x00\x0f\xac\x04"
 +                                  "\x01\x00\x00\x0f\xac\x04"
 +                                  "\x01\x00\x00\x0f\xac\x02"
 +                                  "\x00\x00", 22);
 +}
 +
 +
 +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,
 +                      const u8 *key, size_t key_len)
 +{
 +      struct ibss_rsn_peer *peer = ctx;
 +
 +      wpa_printf(MSG_DEBUG, "SUPP: %s(alg=%d addr=" MACSTR " key_idx=%d "
 +                 "set_tx=%d)",
 +                 __func__, alg, MAC2STR(addr), key_idx, set_tx);
 +      wpa_hexdump(MSG_DEBUG, "SUPP: set_key - seq", seq, seq_len);
 +      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.
 +               */
 +              if (os_memcmp(peer->ibss_rsn->wpa_s->own_addr, peer->addr,
 +                            ETH_ALEN) > 0) {
 +                      wpa_printf(MSG_DEBUG, "SUPP: Do not use this PTK");
 +                      return 0;
 +              }
 +      }
 +
 +      if (is_broadcast_ether_addr(addr))
 +              addr = peer->addr;
 +      return wpa_drv_set_key(peer->ibss_rsn->wpa_s, alg, addr, key_idx,
 +                             set_tx, seq, seq_len, key, key_len);
 +}
 +
 +
 +static void * supp_get_network_ctx(void *ctx)
 +{
 +      struct ibss_rsn_peer *peer = ctx;
 +      return wpa_supplicant_get_ssid(peer->ibss_rsn->wpa_s);
 +}
 +
 +
 +static int supp_mlme_setprotection(void *ctx, const u8 *addr,
 +                                 int protection_type, int key_type)
 +{
 +      wpa_printf(MSG_DEBUG, "SUPP: %s(addr=" MACSTR " protection_type=%d "
 +                 "key_type=%d)",
 +                 __func__, MAC2STR(addr), protection_type, key_type);
 +      return 0;
 +}
 +
 +
 +static void supp_cancel_auth_timeout(void *ctx)
 +{
 +      wpa_printf(MSG_DEBUG, "SUPP: %s", __func__);
 +}
 +
 +
 +static void supp_deauthenticate(void * ctx, int reason_code)
 +{
 +      wpa_printf(MSG_DEBUG, "SUPP: %s (TODO)", __func__);
 +}
 +
 +
 +static int ibss_rsn_supp_init(struct ibss_rsn_peer *peer, const u8 *own_addr,
 +                            const u8 *psk)
 +{
 +      struct wpa_sm_ctx *ctx = os_zalloc(sizeof(*ctx));
 +      if (ctx == NULL)
 +              return -1;
 +
 +      ctx->ctx = peer;
 +      ctx->msg_ctx = peer->ibss_rsn->wpa_s;
 +      ctx->set_state = supp_set_state;
 +      ctx->get_state = supp_get_state;
 +      ctx->ether_send = supp_ether_send;
 +      ctx->get_beacon_ie = supp_get_beacon_ie;
 +      ctx->alloc_eapol = supp_alloc_eapol;
 +      ctx->set_key = supp_set_key;
 +      ctx->get_network_ctx = supp_get_network_ctx;
 +      ctx->mlme_setprotection = supp_mlme_setprotection;
 +      ctx->cancel_auth_timeout = supp_cancel_auth_timeout;
 +      ctx->deauthenticate = supp_deauthenticate;
 +      peer->supp = wpa_sm_init(ctx);
 +      if (peer->supp == NULL) {
 +              wpa_printf(MSG_DEBUG, "SUPP: wpa_sm_init() failed");
 +              return -1;
 +      }
 +
 +      wpa_sm_set_own_addr(peer->supp, own_addr);
 +      wpa_sm_set_param(peer->supp, WPA_PARAM_RSN_ENABLED, 1);
 +      wpa_sm_set_param(peer->supp, WPA_PARAM_PROTO, WPA_PROTO_RSN);
 +      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, NULL);
 +
 +      peer->supp_ie_len = sizeof(peer->supp_ie);
 +      if (wpa_sm_set_assoc_wpa_ie_default(peer->supp, peer->supp_ie,
 +                                          &peer->supp_ie_len) < 0) {
 +              wpa_printf(MSG_DEBUG, "SUPP: wpa_sm_set_assoc_wpa_ie_default()"
 +                         " failed");
 +              return -1;
 +      }
 +
 +      wpa_sm_notify_assoc(peer->supp, peer->addr);
 +
 +      return 0;
 +}
 +
 +
 +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 ibss_rsn *ibss_rsn = ctx;
 +      wpa_printf(MSG_DEBUG, "AUTH: %s (addr=" MACSTR " prev_psk=%p)",
 +                 __func__, MAC2STR(addr), prev_psk);
 +      if (prev_psk)
 +              return NULL;
 +      return ibss_rsn->psk;
 +}
 +
 +
 +static int auth_send_eapol(void *ctx, const u8 *addr, const u8 *data,
 +                         size_t data_len, int encrypt)
 +{
 +      struct ibss_rsn *ibss_rsn = ctx;
 +      struct wpa_supplicant *wpa_s = ibss_rsn->wpa_s;
 +
 +      wpa_printf(MSG_DEBUG, "AUTH: %s(addr=" MACSTR " data_len=%lu "
 +                 "encrypt=%d)",
 +                 __func__, MAC2STR(addr), (unsigned long) data_len, encrypt);
 +
 +      if (wpa_s->l2)
 +              return l2_packet_send(wpa_s->l2, addr, ETH_P_EAPOL, data,
 +                                    data_len);
 +
 +      return -1;
 +}
 +
 +
 +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 ibss_rsn *ibss_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);
 +
 +      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.
 +               */
 +              if (addr == NULL ||
 +                  os_memcmp(ibss_rsn->wpa_s->own_addr, addr, ETH_ALEN) < 0) {
 +                      wpa_printf(MSG_DEBUG, "AUTH: Do not use this PTK");
 +                      return 0;
 +              }
 +      }
 +
 +      return wpa_drv_set_key(ibss_rsn->wpa_s, alg, addr, idx,
 +                             1, seq, 6, key, key_len);
 +}
 +
 +
 +static void ibss_rsn_disconnect(void *ctx, const u8 *addr, u16 reason)
 +{
 +      struct ibss_rsn *ibss_rsn = ctx;
 +      wpa_drv_sta_deauth(ibss_rsn->wpa_s, addr, reason);
 +}
 +
 +
 +static int auth_for_each_sta(void *ctx, int (*cb)(struct wpa_state_machine *sm,
 +                                                void *ctx),
 +                           void *cb_ctx)
 +{
 +      struct ibss_rsn *ibss_rsn = ctx;
 +      struct ibss_rsn_peer *peer;
 +
 +      wpa_printf(MSG_DEBUG, "AUTH: for_each_sta");
 +
 +      for (peer = ibss_rsn->peers; peer; peer = peer->next) {
 +              if (peer->auth && cb(peer->auth, cb_ctx))
 +                      return 1;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +static void ibss_set_sta_authorized(struct ibss_rsn *ibss_rsn,
 +                                  struct ibss_rsn_peer *peer, int authorized)
 +{
 +      int res;
 +
 +      if (authorized) {
 +              res = wpa_drv_sta_set_flags(ibss_rsn->wpa_s, peer->addr,
 +                                          WPA_STA_AUTHORIZED,
 +                                          WPA_STA_AUTHORIZED, ~0);
 +              wpa_printf(MSG_DEBUG, "AUTH: " MACSTR " authorizing port",
 +                         MAC2STR(peer->addr));
 +      } else {
 +              res = wpa_drv_sta_set_flags(ibss_rsn->wpa_s, peer->addr,
 +                                          0, 0, ~WPA_STA_AUTHORIZED);
 +              wpa_printf(MSG_DEBUG, "AUTH: " MACSTR " unauthorizing port",
 +                         MAC2STR(peer->addr));
 +      }
 +
 +      if (res && errno != ENOENT) {
 +              wpa_printf(MSG_DEBUG, "Could not set station " MACSTR " flags "
 +                         "for kernel driver (errno=%d)",
 +                         MAC2STR(peer->addr), errno);
 +      }
 +}
 +
 +
 +static void auth_set_eapol(void *ctx, const u8 *addr,
 +                                     wpa_eapol_variable var, int value)
 +{
 +      struct ibss_rsn *ibss_rsn = ctx;
 +      struct ibss_rsn_peer *peer = ibss_rsn_get_peer(ibss_rsn, addr);
 +
 +      if (peer == NULL)
 +              return;
 +
 +      switch (var) {
 +      case WPA_EAPOL_authorized:
 +              ibss_set_sta_authorized(ibss_rsn, peer, value);
 +              break;
 +      default:
 +              /* do not handle any other event */
 +              wpa_printf(MSG_DEBUG, "AUTH: eapol event not handled %d", var);
 +              break;
 +      }
 +}
 +
 +
 +static int ibss_rsn_auth_init_group(struct ibss_rsn *ibss_rsn,
 +                                  const u8 *own_addr)
 +{
 +      struct wpa_auth_config conf;
 +      struct wpa_auth_callbacks cb;
 +
 +      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_PSK;
 +      conf.wpa_pairwise = WPA_CIPHER_CCMP;
 +      conf.rsn_pairwise = WPA_CIPHER_CCMP;
 +      conf.wpa_group = WPA_CIPHER_CCMP;
 +      conf.eapol_version = 2;
 +      conf.wpa_group_rekey = 600;
 +
 +      os_memset(&cb, 0, sizeof(cb));
 +      cb.ctx = ibss_rsn;
 +      cb.logger = auth_logger;
 +      cb.set_eapol = auth_set_eapol;
 +      cb.send_eapol = auth_send_eapol;
 +      cb.get_psk = auth_get_psk;
 +      cb.set_key = auth_set_key;
 +      cb.for_each_sta = auth_for_each_sta;
 +      cb.disconnect = ibss_rsn_disconnect;
 +
 +      ibss_rsn->auth_group = wpa_init(own_addr, &conf, &cb);
 +      if (ibss_rsn->auth_group == NULL) {
 +              wpa_printf(MSG_DEBUG, "AUTH: wpa_init() failed");
 +              return -1;
 +      }
 +
 +      wpa_init_keys(ibss_rsn->auth_group);
 +
 +      return 0;
 +}
 +
 +
 +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, NULL);
 +      if (peer->auth == NULL) {
 +              wpa_printf(MSG_DEBUG, "AUTH: wpa_auth_sta_init() failed");
 +              return -1;
 +      }
 +
 +      /* TODO: get peer RSN IE with Probe Request */
 +      if (wpa_validate_wpa_ie(ibss_rsn->auth_group, peer->auth,
 +                              (u8 *) "\x30\x14\x01\x00"
 +                              "\x00\x0f\xac\x04"
 +                              "\x01\x00\x00\x0f\xac\x04"
 +                              "\x01\x00\x00\x0f\xac\x02"
 +                              "\x00\x00", 22, NULL, 0) !=
 +          WPA_IE_OK) {
 +              wpa_printf(MSG_DEBUG, "AUTH: wpa_validate_wpa_ie() failed");
 +              return -1;
 +      }
 +
 +      if (wpa_auth_sm_event(peer->auth, WPA_ASSOC))
 +              return -1;
 +
 +      if (wpa_auth_sta_associated(ibss_rsn->auth_group, peer->auth))
 +              return -1;
 +
 +      return 0;
 +}
 +
 +
 +static int ibss_rsn_send_auth(struct ibss_rsn *ibss_rsn, const u8 *da, int seq)
 +{
 +      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 (wpa_s->driver->send_frame == NULL)
 +              return -1;
 +
 +      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 Supplicant for peer "MACSTR,
 +                 MAC2STR(addr));
 +
 +      peer = os_zalloc(sizeof(*peer));
 +      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) {
 +              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 (!ibss_rsn)
++              return -1;
++
 +      /* 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);
 +      }
 +
 +      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;
 +      }
 +
 +      wpa_printf(MSG_DEBUG, "RSN: Starting IBSS Authenticator "
 +                 "for now-authenticated peer " MACSTR, MAC2STR(peer->addr));
 +
 +      return ibss_rsn_auth_init(ibss_rsn, peer);
 +}
 +
 +
 +void ibss_rsn_stop(struct ibss_rsn *ibss_rsn, const u8 *peermac)
 +{
 +      struct ibss_rsn_peer *peer, *prev;
 +
 +      if (ibss_rsn == NULL)
 +              return;
 +
 +      if (peermac == NULL) {
 +              /* remove all peers */
 +              wpa_printf(MSG_DEBUG, "%s: Remove all peers", __func__);
 +              peer = ibss_rsn->peers;
 +              while (peer) {
 +                      prev = peer;
 +                      peer = peer->next;
 +                      ibss_rsn_free(prev);
 +                      ibss_rsn->peers = peer;
 +              }
 +      } else {
 +              /* remove specific peer */
 +              wpa_printf(MSG_DEBUG, "%s: Remove specific peer " MACSTR,
 +                         __func__, MAC2STR(peermac));
 +
 +              for (prev = NULL, peer = ibss_rsn->peers; peer != NULL;
 +                   prev = peer, peer = peer->next) {
 +                      if (os_memcmp(peermac, peer->addr, ETH_ALEN) == 0) {
 +                              if (prev == NULL)
 +                                      ibss_rsn->peers = peer->next;
 +                              else
 +                                      prev->next = peer->next;
 +                              ibss_rsn_free(peer);
 +                              wpa_printf(MSG_DEBUG, "%s: Successfully "
 +                                         "removed a specific peer",
 +                                         __func__);
 +                              break;
 +                      }
 +              }
 +      }
 +}
 +
 +
 +struct ibss_rsn * ibss_rsn_init(struct wpa_supplicant *wpa_s)
 +{
 +      struct ibss_rsn *ibss_rsn;
 +
 +      ibss_rsn = os_zalloc(sizeof(*ibss_rsn));
 +      if (ibss_rsn == NULL)
 +              return NULL;
 +      ibss_rsn->wpa_s = wpa_s;
 +
 +      if (ibss_rsn_auth_init_group(ibss_rsn, wpa_s->own_addr) < 0) {
 +              ibss_rsn_deinit(ibss_rsn);
 +              return NULL;
 +      }
 +
 +      return ibss_rsn;
 +}
 +
 +
 +void ibss_rsn_deinit(struct ibss_rsn *ibss_rsn)
 +{
 +      struct ibss_rsn_peer *peer, *prev;
 +
 +      if (ibss_rsn == NULL)
 +              return;
 +
 +      peer = ibss_rsn->peers;
 +      while (peer) {
 +              prev = peer;
 +              peer = peer->next;
 +              ibss_rsn_free(prev);
 +      }
 +
++      if (ibss_rsn->auth_group)
++              wpa_deinit(ibss_rsn->auth_group);
 +      os_free(ibss_rsn);
 +
 +}
 +
 +
 +static int ibss_rsn_eapol_dst_supp(const u8 *buf, size_t len)
 +{
 +      const struct ieee802_1x_hdr *hdr;
 +      const struct wpa_eapol_key *key;
 +      u16 key_info;
 +      size_t plen;
 +
 +      /* TODO: Support other EAPOL packets than just EAPOL-Key */
 +
 +      if (len < sizeof(*hdr) + sizeof(*key))
 +              return -1;
 +
 +      hdr = (const struct ieee802_1x_hdr *) buf;
 +      key = (const struct wpa_eapol_key *) (hdr + 1);
 +      plen = be_to_host16(hdr->length);
 +
 +      if (hdr->version < EAPOL_VERSION) {
 +              /* TODO: backwards compatibility */
 +      }
 +      if (hdr->type != IEEE802_1X_TYPE_EAPOL_KEY) {
 +              wpa_printf(MSG_DEBUG, "RSN: EAPOL frame (type %u) discarded, "
 +                      "not a Key frame", hdr->type);
 +              return -1;
 +      }
 +      if (plen > len - sizeof(*hdr) || plen < sizeof(*key)) {
 +              wpa_printf(MSG_DEBUG, "RSN: EAPOL frame payload size %lu "
 +                         "invalid (frame size %lu)",
 +                         (unsigned long) plen, (unsigned long) len);
 +              return -1;
 +      }
 +
 +      if (key->type != EAPOL_KEY_TYPE_RSN) {
 +              wpa_printf(MSG_DEBUG, "RSN: EAPOL-Key type (%d) unknown, "
 +                         "discarded", key->type);
 +              return -1;
 +      }
 +
 +      key_info = WPA_GET_BE16(key->key_info);
 +
 +      return !!(key_info & WPA_KEY_INFO_ACK);
 +}
 +
 +
 +static int ibss_rsn_process_rx_eapol(struct ibss_rsn *ibss_rsn,
 +                                   struct ibss_rsn_peer *peer,
 +                                   const u8 *buf, size_t len)
 +{
 +      int supp;
 +      u8 *tmp;
 +
 +      supp = ibss_rsn_eapol_dst_supp(buf, len);
 +      if (supp < 0)
 +              return -1;
 +
 +      tmp = os_malloc(len);
 +      if (tmp == NULL)
 +              return -1;
 +      os_memcpy(tmp, buf, len);
 +      if (supp) {
 +              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 {
 +              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);
 +
 +      return 1;
 +}
 +
 +
 +int ibss_rsn_rx_eapol(struct ibss_rsn *ibss_rsn, const u8 *src_addr,
 +                    const u8 *buf, size_t len)
 +{
 +      struct ibss_rsn_peer *peer;
 +
 +      if (ibss_rsn == NULL)
 +              return -1;
 +
 +      peer = ibss_rsn_get_peer(ibss_rsn, src_addr);
 +      if (peer)
 +              return ibss_rsn_process_rx_eapol(ibss_rsn, peer, buf, len);
 +
 +      if (ibss_rsn_eapol_dst_supp(buf, len) > 0) {
 +              /*
 +               * Create new IBSS peer based on an EAPOL message from the peer
 +               * Authenticator.
 +               */
 +              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);
 +      }
 +
 +      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;
 +      }
 +}
index 4a396654487e68244fc48e995a784bf21245b4b1,0000000000000000000000000000000000000000..fd47c179ea4b17af3dbeef1b2a5257971aa37a97
mode 100644,000000..100644
--- /dev/null
@@@ -1,3055 -1,0 +1,3057 @@@
-       int excluded1, excluded2;
 +/*
 + * Interworking (IEEE 802.11u)
 + * 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 "includes.h"
 +
 +#include "common.h"
 +#include "common/ieee802_11_defs.h"
 +#include "common/gas.h"
 +#include "common/wpa_ctrl.h"
 +#include "utils/pcsc_funcs.h"
 +#include "utils/eloop.h"
 +#include "drivers/driver.h"
 +#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"
 +
 +
 +#if defined(EAP_SIM) | defined(EAP_SIM_DYNAMIC)
 +#define INTERWORKING_3GPP
 +#else
 +#if defined(EAP_AKA) | defined(EAP_AKA_DYNAMIC)
 +#define INTERWORKING_3GPP
 +#else
 +#if defined(EAP_AKA_PRIME) | defined(EAP_AKA_PRIME_DYNAMIC)
 +#define INTERWORKING_3GPP
 +#endif
 +#endif
 +#endif
 +
 +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, int ignore_bw,
 +      int *excluded);
 +static struct wpa_cred * interworking_credentials_available_3gpp(
 +      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 (!tried && wpa_supplicant_fast_associate(wpa_s) >= 0)
 +              return;
 +
 +      wpa_s->interworking_fast_assoc_tried = 0;
 +      wpa_supplicant_req_scan(wpa_s, 0, 0);
 +}
 +
 +
 +static struct wpabuf * anqp_build_req(u16 info_ids[], size_t num_ids,
 +                                    struct wpabuf *extra)
 +{
 +      struct wpabuf *buf;
 +      size_t i;
 +      u8 *len_pos;
 +
 +      buf = gas_anqp_build_initial_req(0, 4 + num_ids * 2 +
 +                                       (extra ? wpabuf_len(extra) : 0));
 +      if (buf == NULL)
 +              return NULL;
 +
 +      len_pos = gas_anqp_add_element(buf, ANQP_QUERY_LIST);
 +      for (i = 0; i < num_ids; i++)
 +              wpabuf_put_le16(buf, info_ids[i]);
 +      gas_anqp_set_element_len(buf, len_pos);
 +      if (extra)
 +              wpabuf_put_buf(buf, extra);
 +
 +      gas_anqp_set_len(buf);
 +
 +      return buf;
 +}
 +
 +
 +static void interworking_anqp_resp_cb(void *ctx, const u8 *dst,
 +                                    u8 dialog_token,
 +                                    enum gas_query_result result,
 +                                    const struct wpabuf *adv_proto,
 +                                    const struct wpabuf *resp,
 +                                    u16 status_code)
 +{
 +      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);
 +}
 +
 +
 +static int cred_with_roaming_consortium(struct wpa_supplicant *wpa_s)
 +{
 +      struct wpa_cred *cred;
 +
 +      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;
 +}
 +
 +
 +static int cred_with_3gpp(struct wpa_supplicant *wpa_s)
 +{
 +      struct wpa_cred *cred;
 +
 +      for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
 +              if (cred->pcsc || cred->imsi)
 +                      return 1;
 +      }
 +      return 0;
 +}
 +
 +
 +static int cred_with_nai_realm(struct wpa_supplicant *wpa_s)
 +{
 +      struct wpa_cred *cred;
 +
 +      for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
 +              if (cred->pcsc || cred->imsi)
 +                      continue;
 +              if (!cred->eap_method)
 +                      return 1;
 +              if (cred->realm && cred->roaming_consortium_len == 0)
 +                      return 1;
 +      }
 +      return 0;
 +}
 +
 +
 +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 ||
 +                  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;
 +      ie = wpa_bss_get_ie(bss, WLAN_EID_ROAMING_CONSORTIUM);
 +      if (ie == NULL || ie[1] == 0)
 +              return 0;
 +      return ie[2]; /* Number of ANQP OIs */
 +}
 +
 +
 +static void interworking_continue_anqp(void *eloop_ctx, void *sock_ctx)
 +{
 +      struct wpa_supplicant *wpa_s = eloop_ctx;
 +      interworking_next_anqp_fetch(wpa_s);
 +}
 +
 +
 +static int interworking_anqp_send_req(struct wpa_supplicant *wpa_s,
 +                                    struct wpa_bss *bss)
 +{
 +      struct wpabuf *buf;
 +      int ret = 0;
 +      int res;
 +      u16 info_ids[8];
 +      size_t num_info_ids = 0;
 +      struct wpabuf *extra = NULL;
 +      int all = wpa_s->fetch_all_anqp;
 +
 +      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;
 +      if (all) {
 +              info_ids[num_info_ids++] = ANQP_VENUE_NAME;
 +              info_ids[num_info_ids++] = ANQP_NETWORK_AUTH_TYPE;
 +      }
 +      if (all || (cred_with_roaming_consortium(wpa_s) &&
 +                  additional_roaming_consortiums(bss)))
 +              info_ids[num_info_ids++] = ANQP_ROAMING_CONSORTIUM;
 +      if (all)
 +              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)) {
 +              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",
 +                  (u8 *) info_ids, num_info_ids * 2);
 +
 +#ifdef CONFIG_HS20
 +      if (wpa_bss_get_vendor_ie(bss, HS20_IE_VENDOR_TYPE)) {
 +              u8 *len_pos;
 +
 +              extra = wpabuf_alloc(100);
 +              if (!extra)
 +                      return -1;
 +
 +              len_pos = gas_anqp_add_element(extra, ANQP_VENDOR_SPECIFIC);
 +              wpabuf_put_be24(extra, OUI_WFA);
 +              wpabuf_put_u8(extra, HS20_ANQP_OUI_TYPE);
 +              wpabuf_put_u8(extra, HS20_STYPE_QUERY_LIST);
 +              wpabuf_put_u8(extra, 0); /* Reserved */
 +              wpabuf_put_u8(extra, HS20_STYPE_CAPABILITY_LIST);
 +              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 */
 +
 +      buf = anqp_build_req(info_ids, num_info_ids, extra);
 +      wpabuf_free(extra);
 +      if (buf == NULL)
 +              return -1;
 +
 +      res = gas_query_req(wpa_s->gas, bss->bssid, bss->freq, buf,
 +                          interworking_anqp_resp_cb, wpa_s);
 +      if (res < 0) {
 +              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_msg(wpa_s, MSG_DEBUG,
 +                      "ANQP: Query started with dialog token %u", res);
 +
 +      return ret;
 +}
 +
 +
 +struct nai_realm_eap {
 +      u8 method;
 +      u8 inner_method;
 +      enum nai_realm_eap_auth_inner_non_eap inner_non_eap;
 +      u8 cred_type;
 +      u8 tunneled_cred_type;
 +};
 +
 +struct nai_realm {
 +      u8 encoding;
 +      char *realm;
 +      u8 eap_count;
 +      struct nai_realm_eap *eap;
 +};
 +
 +
 +static void nai_realm_free(struct nai_realm *realms, u16 count)
 +{
 +      u16 i;
 +
 +      if (realms == NULL)
 +              return;
 +      for (i = 0; i < count; i++) {
 +              os_free(realms[i].eap);
 +              os_free(realms[i].realm);
 +      }
 +      os_free(realms);
 +}
 +
 +
 +static const u8 * nai_realm_parse_eap(struct nai_realm_eap *e, const u8 *pos,
 +                                    const u8 *end)
 +{
 +      u8 elen, auth_count, a;
 +      const u8 *e_end;
 +
 +      if (pos + 3 > end) {
 +              wpa_printf(MSG_DEBUG, "No room for EAP Method fixed fields");
 +              return NULL;
 +      }
 +
 +      elen = *pos++;
 +      if (pos + elen > end || elen < 2) {
 +              wpa_printf(MSG_DEBUG, "No room for EAP Method subfield");
 +              return NULL;
 +      }
 +      e_end = pos + elen;
 +      e->method = *pos++;
 +      auth_count = *pos++;
 +      wpa_printf(MSG_DEBUG, "EAP Method: len=%u method=%u auth_count=%u",
 +                 elen, e->method, auth_count);
 +
 +      for (a = 0; a < auth_count; a++) {
 +              u8 id, len;
 +
 +              if (pos + 2 > end || pos + 2 + pos[1] > end) {
 +                      wpa_printf(MSG_DEBUG, "No room for Authentication "
 +                                 "Parameter subfield");
 +                      return NULL;
 +              }
 +
 +              id = *pos++;
 +              len = *pos++;
 +
 +              switch (id) {
 +              case NAI_REALM_EAP_AUTH_NON_EAP_INNER_AUTH:
 +                      if (len < 1)
 +                              break;
 +                      e->inner_non_eap = *pos;
 +                      if (e->method != EAP_TYPE_TTLS)
 +                              break;
 +                      switch (*pos) {
 +                      case NAI_REALM_INNER_NON_EAP_PAP:
 +                              wpa_printf(MSG_DEBUG, "EAP-TTLS/PAP");
 +                              break;
 +                      case NAI_REALM_INNER_NON_EAP_CHAP:
 +                              wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP");
 +                              break;
 +                      case NAI_REALM_INNER_NON_EAP_MSCHAP:
 +                              wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP");
 +                              break;
 +                      case NAI_REALM_INNER_NON_EAP_MSCHAPV2:
 +                              wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2");
 +                              break;
 +                      }
 +                      break;
 +              case NAI_REALM_EAP_AUTH_INNER_AUTH_EAP_METHOD:
 +                      if (len < 1)
 +                              break;
 +                      e->inner_method = *pos;
 +                      wpa_printf(MSG_DEBUG, "Inner EAP method: %u",
 +                                 e->inner_method);
 +                      break;
 +              case NAI_REALM_EAP_AUTH_CRED_TYPE:
 +                      if (len < 1)
 +                              break;
 +                      e->cred_type = *pos;
 +                      wpa_printf(MSG_DEBUG, "Credential Type: %u",
 +                                 e->cred_type);
 +                      break;
 +              case NAI_REALM_EAP_AUTH_TUNNELED_CRED_TYPE:
 +                      if (len < 1)
 +                              break;
 +                      e->tunneled_cred_type = *pos;
 +                      wpa_printf(MSG_DEBUG, "Tunneled EAP Method Credential "
 +                                 "Type: %u", e->tunneled_cred_type);
 +                      break;
 +              default:
 +                      wpa_printf(MSG_DEBUG, "Unsupported Authentication "
 +                                 "Parameter: id=%u len=%u", id, len);
 +                      wpa_hexdump(MSG_DEBUG, "Authentication Parameter "
 +                                  "Value", pos, len);
 +                      break;
 +              }
 +
 +              pos += len;
 +      }
 +
 +      return e_end;
 +}
 +
 +
 +static const u8 * nai_realm_parse_realm(struct nai_realm *r, const u8 *pos,
 +                                      const u8 *end)
 +{
 +      u16 len;
 +      const u8 *f_end;
 +      u8 realm_len, e;
 +
 +      if (end - pos < 4) {
 +              wpa_printf(MSG_DEBUG, "No room for NAI Realm Data "
 +                         "fixed fields");
 +              return NULL;
 +      }
 +
 +      len = WPA_GET_LE16(pos); /* NAI Realm Data field Length */
 +      pos += 2;
 +      if (pos + len > end || len < 3) {
 +              wpa_printf(MSG_DEBUG, "No room for NAI Realm Data "
 +                         "(len=%u; left=%u)",
 +                         len, (unsigned int) (end - pos));
 +              return NULL;
 +      }
 +      f_end = pos + len;
 +
 +      r->encoding = *pos++;
 +      realm_len = *pos++;
 +      if (pos + realm_len > f_end) {
 +              wpa_printf(MSG_DEBUG, "No room for NAI Realm "
 +                         "(len=%u; left=%u)",
 +                         realm_len, (unsigned int) (f_end - pos));
 +              return NULL;
 +      }
 +      wpa_hexdump_ascii(MSG_DEBUG, "NAI Realm", pos, realm_len);
 +      r->realm = dup_binstr(pos, realm_len);
 +      if (r->realm == NULL)
 +              return NULL;
 +      pos += realm_len;
 +
 +      if (pos + 1 > f_end) {
 +              wpa_printf(MSG_DEBUG, "No room for EAP Method Count");
 +              return NULL;
 +      }
 +      r->eap_count = *pos++;
 +      wpa_printf(MSG_DEBUG, "EAP Count: %u", r->eap_count);
 +      if (pos + r->eap_count * 3 > f_end) {
 +              wpa_printf(MSG_DEBUG, "No room for EAP Methods");
 +              return NULL;
 +      }
 +      r->eap = os_calloc(r->eap_count, sizeof(struct nai_realm_eap));
 +      if (r->eap == NULL)
 +              return NULL;
 +
 +      for (e = 0; e < r->eap_count; e++) {
 +              pos = nai_realm_parse_eap(&r->eap[e], pos, f_end);
 +              if (pos == NULL)
 +                      return NULL;
 +      }
 +
 +      return f_end;
 +}
 +
 +
 +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)
 +              return NULL;
 +      left = wpabuf_len(anqp);
 +      if (left < 2)
 +              return NULL;
 +
 +      pos = wpabuf_head_u8(anqp);
 +      end = pos + left;
 +      num = WPA_GET_LE16(pos);
 +      wpa_printf(MSG_DEBUG, "NAI Realm Count: %u", num);
 +      pos += 2;
 +      left -= 2;
 +
 +      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) left);
 +              return NULL;
 +      }
 +
 +      realm = os_calloc(num, sizeof(struct nai_realm));
 +      if (realm == NULL)
 +              return NULL;
 +
 +      for (i = 0; i < num; i++) {
 +              pos = nai_realm_parse_realm(&realm[i], pos, end);
 +              if (pos == NULL) {
 +                      nai_realm_free(realm, num);
 +                      return NULL;
 +              }
 +      }
 +
 +      *count = num;
 +      return realm;
 +}
 +
 +
 +static int nai_realm_match(struct nai_realm *realm, const char *home_realm)
 +{
 +      char *tmp, *pos, *end;
 +      int match = 0;
 +
 +      if (realm->realm == NULL || home_realm == NULL)
 +              return 0;
 +
 +      if (os_strchr(realm->realm, ';') == NULL)
 +              return os_strcasecmp(realm->realm, home_realm) == 0;
 +
 +      tmp = os_strdup(realm->realm);
 +      if (tmp == NULL)
 +              return 0;
 +
 +      pos = tmp;
 +      while (*pos) {
 +              end = os_strchr(pos, ';');
 +              if (end)
 +                      *end = '\0';
 +              if (os_strcasecmp(pos, home_realm) == 0) {
 +                      match = 1;
 +                      break;
 +              }
 +              if (end == NULL)
 +                      break;
 +              pos = end + 1;
 +      }
 +
 +      os_free(tmp);
 +
 +      return match;
 +}
 +
 +
 +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) {
 +              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 &&
 +          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 || eap->method == EAP_TYPE_FAST) {
 +              if (eap->inner_method &&
 +                  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) {
 +                      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) {
 +                      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) {
 +                      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) {
 +              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 wpa_supplicant *wpa_s,
 +                             struct nai_realm_eap *eap)
 +{
 +      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;
 +      }
 +
 +      return 1;
 +}
 +
 +
 +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->username == NULL ||
 +          cred->username[0] == '\0' ||
 +          ((cred->password == NULL ||
 +            cred->password[0] == '\0') &&
 +           (cred->private_key == NULL ||
 +            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(wpa_s, eap))
 +                      return eap;
 +              if (cred->private_key && cred->private_key[0] &&
 +                  nai_realm_cred_cert(wpa_s, eap))
 +                      return eap;
 +      }
 +
 +      return NULL;
 +}
 +
 +
 +#ifdef INTERWORKING_3GPP
 +
 +static int plmn_id_match(struct wpabuf *anqp, const char *imsi, int mnc_len)
 +{
 +      u8 plmn[3], plmn2[3];
 +      const u8 *pos, *end;
 +      u8 udhl;
 +
 +      /*
 +       * 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') | ((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;
 +      pos = wpabuf_head_u8(anqp);
 +      end = pos + wpabuf_len(anqp);
 +      if (pos + 2 > end)
 +              return 0;
 +      if (*pos != 0) {
 +              wpa_printf(MSG_DEBUG, "Unsupported GUD version 0x%x", *pos);
 +              return 0;
 +      }
 +      pos++;
 +      udhl = *pos++;
 +      if (pos + udhl > end) {
 +              wpa_printf(MSG_DEBUG, "Invalid UDHL");
 +              return 0;
 +      }
 +      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;
 +              iei = *pos++;
 +              len = *pos++ & 0x7f;
 +              if (pos + len > end)
 +                      break;
 +              l_end = pos + 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 > l_end)
 +                                      break;
 +                              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;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +static int build_root_nai(char *nai, size_t nai_len, const char *imsi,
 +                        size_t mnc_len, char prefix)
 +{
 +      const char *sep, *msin;
 +      char *end, *pos;
 +      size_t msin_len, plmn_len;
 +
 +      /*
 +       * TS 23.003, Clause 14 (3GPP to WLAN Interworking)
 +       * Root NAI:
 +       * <aka:0|sim:1><IMSI>@wlan.mnc<MNC>.mcc<MCC>.3gppnetwork.org
 +       * <MNC> is zero-padded to three digits in case two-digit MNC is used
 +       */
 +
 +      if (imsi == NULL || os_strlen(imsi) > 16) {
 +              wpa_printf(MSG_DEBUG, "No valid IMSI available");
 +              return -1;
 +      }
 +      sep = os_strchr(imsi, '-');
 +      if (sep) {
 +              plmn_len = sep - imsi;
 +              msin = sep + 1;
 +      } else if (mnc_len && os_strlen(imsi) >= 3 + mnc_len) {
 +              plmn_len = 3 + mnc_len;
 +              msin = imsi + plmn_len;
 +      } else
 +              return -1;
 +      if (plmn_len != 5 && plmn_len != 6)
 +              return -1;
 +      msin_len = os_strlen(msin);
 +
 +      pos = nai;
 +      end = nai + nai_len;
 +      if (prefix)
 +              *pos++ = prefix;
 +      os_memcpy(pos, imsi, plmn_len);
 +      pos += plmn_len;
 +      os_memcpy(pos, msin, msin_len);
 +      pos += msin_len;
 +      pos += os_snprintf(pos, end - pos, "@wlan.mnc");
 +      if (plmn_len == 5) {
 +              *pos++ = '0';
 +              *pos++ = imsi[3];
 +              *pos++ = imsi[4];
 +      } else {
 +              *pos++ = imsi[3];
 +              *pos++ = imsi[4];
 +              *pos++ = imsi[5];
 +      }
 +      os_snprintf(pos, end - pos, ".mcc%c%c%c.3gppnetwork.org",
 +                  imsi[0], imsi[1], imsi[2]);
 +
 +      return 0;
 +}
 +
 +
 +static int set_root_nai(struct wpa_ssid *ssid, const char *imsi, char prefix)
 +{
 +      char nai[100];
 +      if (build_root_nai(nai, sizeof(nai), imsi, 0, prefix) < 0)
 +              return -1;
 +      return wpa_config_set_quoted(ssid, "identity", nai);
 +}
 +
 +#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)
 +{
 +      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;
 +      if (wpa_config_set(ssid, "pairwise", "CCMP", 0) < 0)
 +              return -1;
 +      return 0;
 +}
 +
 +
 +static int interworking_connect_3gpp(struct wpa_supplicant *wpa_s,
 +                                   struct wpa_cred *cred,
 +                                   struct wpa_bss *bss, int only_add)
 +{
 +#ifdef INTERWORKING_3GPP
 +      struct wpa_ssid *ssid;
 +      int eap_type;
 +      int res;
 +      char prefix;
 +
 +      if (bss->anqp == NULL || bss->anqp->anqp_3gpp == NULL)
 +              return -1;
 +
 +      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)
 +              return -1;
 +      ssid->parent_cred = cred;
 +
 +      wpas_notify_network_added(wpa_s, ssid);
 +      wpa_config_set_network_defaults(ssid);
 +      ssid->priority = cred->priority;
 +      ssid->temporary = 1;
 +      ssid->ssid = os_zalloc(bss->ssid_len + 1);
 +      if (ssid->ssid == NULL)
 +              goto fail;
 +      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;
 +
 +      eap_type = EAP_TYPE_SIM;
 +      if (cred->pcsc && wpa_s->scard && scard_supports_umts(wpa_s->scard))
 +              eap_type = EAP_TYPE_AKA;
 +      if (cred->eap_method && cred->eap_method[0].vendor == EAP_VENDOR_IETF) {
 +              if (cred->eap_method[0].method == EAP_TYPE_SIM ||
 +                  cred->eap_method[0].method == EAP_TYPE_AKA ||
 +                  cred->eap_method[0].method == EAP_TYPE_AKA_PRIME)
 +                      eap_type = cred->eap_method[0].method;
 +      }
 +
 +      switch (eap_type) {
 +      case EAP_TYPE_SIM:
 +              prefix = '1';
 +              res = wpa_config_set(ssid, "eap", "SIM", 0);
 +              break;
 +      case EAP_TYPE_AKA:
 +              prefix = '0';
 +              res = wpa_config_set(ssid, "eap", "AKA", 0);
 +              break;
 +      case EAP_TYPE_AKA_PRIME:
 +              prefix = '6';
 +              res = wpa_config_set(ssid, "eap", "AKA'", 0);
 +              break;
 +      default:
 +              res = -1;
 +              break;
 +      }
 +      if (res < 0) {
 +              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_msg(wpa_s, MSG_DEBUG, "Failed to set Root NAI");
 +              goto fail;
 +      }
 +
 +      if (cred->milenage && cred->milenage[0]) {
 +              if (wpa_config_set_quoted(ssid, "password",
 +                                        cred->milenage) < 0)
 +                      goto fail;
 +      } else if (cred->pcsc) {
 +              if (wpa_config_set_quoted(ssid, "pcsc", "") < 0)
 +                      goto fail;
 +              if (wpa_s->conf->pcsc_pin &&
 +                  wpa_config_set_quoted(ssid, "pin", wpa_s->conf->pcsc_pin)
 +                  < 0)
 +                      goto fail;
 +      }
 +
 +      wpa_s->next_ssid = ssid;
 +      wpa_config_update_prio_list(wpa_s->conf);
 +      if (!only_add)
 +              interworking_reconnect(wpa_s);
 +
 +      return ssid->id;
 +
 +fail:
 +      wpas_notify_network_removed(wpa_s, ssid);
 +      wpa_config_remove_network(wpa_s->conf, ssid->id);
 +#endif /* INTERWORKING_3GPP */
 +      return -1;
 +}
 +
 +
 +static int roaming_consortium_element_match(const u8 *ie, const u8 *rc_id,
 +                                          size_t rc_len)
 +{
 +      const u8 *pos, *end;
 +      u8 lens;
 +
 +      if (ie == NULL)
 +              return 0;
 +
 +      pos = ie + 2;
 +      end = ie + 2 + ie[1];
 +
 +      /* Roaming Consortium element:
 +       * Number of ANQP OIs
 +       * OI #1 and #2 lengths
 +       * OI #1, [OI #2], [OI #3]
 +       */
 +
 +      if (pos + 2 > end)
 +              return 0;
 +
 +      pos++; /* skip Number of ANQP OIs */
 +      lens = *pos++;
 +      if (pos + (lens & 0x0f) + (lens >> 4) > end)
 +              return 0;
 +
 +      if ((lens & 0x0f) == rc_len && os_memcmp(pos, rc_id, rc_len) == 0)
 +              return 1;
 +      pos += lens & 0x0f;
 +
 +      if ((lens >> 4) == rc_len && os_memcmp(pos, rc_id, rc_len) == 0)
 +              return 1;
 +      pos += lens >> 4;
 +
 +      if (pos < end && (size_t) (end - pos) == rc_len &&
 +          os_memcmp(pos, rc_id, rc_len) == 0)
 +              return 1;
 +
 +      return 0;
 +}
 +
 +
 +static int roaming_consortium_anqp_match(const struct wpabuf *anqp,
 +                                       const u8 *rc_id, size_t rc_len)
 +{
 +      const u8 *pos, *end;
 +      u8 len;
 +
 +      if (anqp == NULL)
 +              return 0;
 +
 +      pos = wpabuf_head(anqp);
 +      end = pos + wpabuf_len(anqp);
 +
 +      /* Set of <OI Length, OI> duples */
 +      while (pos < end) {
 +              len = *pos++;
 +              if (pos + len > end)
 +                      break;
 +              if (len == rc_len && os_memcmp(pos, rc_id, rc_len) == 0)
 +                      return 1;
 +              pos += len;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +static int roaming_consortium_match(const u8 *ie, const struct wpabuf *anqp,
 +                                  const u8 *rc_id, size_t rc_len)
 +{
 +      return roaming_consortium_element_match(ie, rc_id, rc_len) ||
 +              roaming_consortium_anqp_match(anqp, rc_id, rc_len);
 +}
 +
 +
 +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;
 +
 +      if (!cred->excluded_ssid)
 +              return 0;
 +
 +      for (i = 0; i < cred->num_excluded_ssid; i++) {
 +              struct excluded_ssid *e = &cred->excluded_ssid[i];
 +              if (bss->ssid_len == e->ssid_len &&
 +                  os_memcmp(bss->ssid, e->ssid, e->ssid_len) == 0)
 +                      return 1;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +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, 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);
 +
 +      if (ie == NULL &&
 +          (bss->anqp == NULL || bss->anqp->roaming_consortium == NULL))
 +              return NULL;
 +
 +      if (wpa_s->conf->cred == NULL)
 +              return NULL;
 +
 +      for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
 +              if (cred->roaming_consortium_len == 0)
 +                      continue;
 +
 +              if (!roaming_consortium_match(ie,
 +                                            bss->anqp ?
 +                                            bss->anqp->roaming_consortium :
 +                                            NULL,
 +                                            cred->roaming_consortium,
 +                                            cred->roaming_consortium_len))
 +                      continue;
 +
 +              if (cred_no_required_oi_match(cred, bss))
 +                      continue;
 +              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;
 +}
 +
 +
 +static int interworking_set_eap_params(struct wpa_ssid *ssid,
 +                                     struct wpa_cred *cred, int ttls)
 +{
 +      if (cred->eap_method) {
 +              ttls = cred->eap_method->vendor == EAP_VENDOR_IETF &&
 +                      cred->eap_method->method == EAP_TYPE_TTLS;
 +
 +              os_free(ssid->eap.eap_methods);
 +              ssid->eap.eap_methods =
 +                      os_malloc(sizeof(struct eap_method_type) * 2);
 +              if (ssid->eap.eap_methods == NULL)
 +                      return -1;
 +              os_memcpy(ssid->eap.eap_methods, cred->eap_method,
 +                        sizeof(*cred->eap_method));
 +              ssid->eap.eap_methods[1].vendor = EAP_VENDOR_IETF;
 +              ssid->eap.eap_methods[1].method = EAP_TYPE_NONE;
 +      }
 +
 +      if (ttls && cred->username && cred->username[0]) {
 +              const char *pos;
 +              char *anon;
 +              /* Use anonymous NAI in Phase 1 */
 +              pos = os_strchr(cred->username, '@');
 +              if (pos) {
 +                      size_t buflen = 9 + os_strlen(pos) + 1;
 +                      anon = os_malloc(buflen);
 +                      if (anon == NULL)
 +                              return -1;
 +                      os_snprintf(anon, buflen, "anonymous%s", pos);
 +              } else if (cred->realm) {
 +                      size_t buflen = 10 + os_strlen(cred->realm) + 1;
 +                      anon = os_malloc(buflen);
 +                      if (anon == NULL)
 +                              return -1;
 +                      os_snprintf(anon, buflen, "anonymous@%s", cred->realm);
 +              } else {
 +                      anon = os_strdup("anonymous");
 +                      if (anon == NULL)
 +                              return -1;
 +              }
 +              if (wpa_config_set_quoted(ssid, "anonymous_identity", anon) <
 +                  0) {
 +                      os_free(anon);
 +                      return -1;
 +              }
 +              os_free(anon);
 +      }
 +
 +      if (cred->username && cred->username[0] &&
 +          wpa_config_set_quoted(ssid, "identity", cred->username) < 0)
 +              return -1;
 +
 +      if (cred->password && cred->password[0]) {
 +              if (cred->ext_password &&
 +                  wpa_config_set(ssid, "password", cred->password, 0) < 0)
 +                      return -1;
 +              if (!cred->ext_password &&
 +                  wpa_config_set_quoted(ssid, "password", cred->password) <
 +                  0)
 +                      return -1;
 +      }
 +
 +      if (cred->client_cert && cred->client_cert[0] &&
 +          wpa_config_set_quoted(ssid, "client_cert", cred->client_cert) < 0)
 +              return -1;
 +
 +#ifdef ANDROID
 +      if (cred->private_key &&
 +          os_strncmp(cred->private_key, "keystore://", 11) == 0) {
 +              /* Use OpenSSL engine configuration for Android keystore */
 +              if (wpa_config_set_quoted(ssid, "engine_id", "keystore") < 0 ||
 +                  wpa_config_set_quoted(ssid, "key_id",
 +                                        cred->private_key + 11) < 0 ||
 +                  wpa_config_set(ssid, "engine", "1", 0) < 0)
 +                      return -1;
 +      } else
 +#endif /* ANDROID */
 +      if (cred->private_key && cred->private_key[0] &&
 +          wpa_config_set_quoted(ssid, "private_key", cred->private_key) < 0)
 +              return -1;
 +
 +      if (cred->private_key_passwd && cred->private_key_passwd[0] &&
 +          wpa_config_set_quoted(ssid, "private_key_passwd",
 +                                cred->private_key_passwd) < 0)
 +              return -1;
 +
 +      if (cred->phase1) {
 +              os_free(ssid->eap.phase1);
 +              ssid->eap.phase1 = os_strdup(cred->phase1);
 +      }
 +      if (cred->phase2) {
 +              os_free(ssid->eap.phase2);
 +              ssid->eap.phase2 = os_strdup(cred->phase2);
 +      }
 +
 +      if (cred->ca_cert && cred->ca_cert[0] &&
 +          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, int only_add)
 +{
 +      struct wpa_ssid *ssid;
 +
 +      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)
 +              return -1;
 +      ssid->parent_cred = cred;
 +      wpas_notify_network_added(wpa_s, ssid);
 +      wpa_config_set_network_defaults(ssid);
 +      ssid->priority = cred->priority;
 +      ssid->temporary = 1;
 +      ssid->ssid = os_zalloc(bss->ssid_len + 1);
 +      if (ssid->ssid == NULL)
 +              goto fail;
 +      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_msg(wpa_s, MSG_DEBUG,
 +                      "Interworking: No EAP method set for credential using roaming consortium");
 +              goto fail;
 +      }
 +
 +      if (interworking_set_eap_params(
 +                  ssid, cred,
 +                  cred->eap_method->vendor == EAP_VENDOR_IETF &&
 +                  cred->eap_method->method == EAP_TYPE_TTLS) < 0)
 +              goto fail;
 +
 +      wpa_s->next_ssid = ssid;
 +      wpa_config_update_prio_list(wpa_s->conf);
 +      if (!only_add)
 +              interworking_reconnect(wpa_s);
 +
 +      return ssid->id;
 +
 +fail:
 +      wpas_notify_network_removed(wpa_s, ssid);
 +      wpa_config_remove_network(wpa_s->conf, ssid->id);
 +      return -1;
 +}
 +
 +
 +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;
 +      struct nai_realm *realm;
 +      struct nai_realm_eap *eap = NULL;
 +      u16 count, i;
 +      char buf[100];
 +      int excluded = 0, *excl = allow_excluded ? &excluded : NULL;
 +      const char *name;
 +
 +      if (wpa_s->conf->cred == NULL || bss == NULL)
 +              return -1;
 +      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_msg(wpa_s, MSG_DEBUG,
 +                      "Interworking: Network does not use RSN");
 +              return -1;
 +      }
 +
 +      cred_rc = interworking_credentials_available_roaming_consortium(
 +              wpa_s, bss, 0, excl);
 +      if (cred_rc) {
 +              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, 0, excl);
 +      if (cred) {
 +              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, 0,
 +                                                          excl);
 +      if (cred_3gpp) {
 +              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_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, only_add);
 +
 +      if (cred_3gpp &&
 +          (cred == NULL || cred_prio_cmp(cred_3gpp, cred) >= 0)) {
 +              return interworking_connect_3gpp(wpa_s, cred_3gpp, bss,
 +                                               only_add);
 +      }
 +
 +      if (cred == NULL) {
 +              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_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(wpa_s, cred, &realm[i]);
 +              if (eap)
 +                      break;
 +      }
 +
 +      if (!eap) {
 +              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_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) {
 +              nai_realm_free(realm, count);
 +              return -1;
 +      }
 +      ssid->parent_cred = cred;
 +      wpas_notify_network_added(wpa_s, ssid);
 +      wpa_config_set_network_defaults(ssid);
 +      ssid->priority = cred->priority;
 +      ssid->temporary = 1;
 +      ssid->ssid = os_zalloc(bss->ssid_len + 1);
 +      if (ssid->ssid == NULL)
 +              goto fail;
 +      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 (wpa_config_set(ssid, "eap", eap_get_name(EAP_VENDOR_IETF,
 +                                                   eap->method), 0) < 0)
 +              goto fail;
 +
 +      switch (eap->method) {
 +      case EAP_TYPE_TTLS:
 +              if (eap->inner_method) {
 +                      os_snprintf(buf, sizeof(buf), "\"autheap=%s\"",
 +                                  eap_get_name(EAP_VENDOR_IETF,
 +                                               eap->inner_method));
 +                      if (wpa_config_set(ssid, "phase2", buf, 0) < 0)
 +                              goto fail;
 +                      break;
 +              }
 +              switch (eap->inner_non_eap) {
 +              case NAI_REALM_INNER_NON_EAP_PAP:
 +                      if (wpa_config_set(ssid, "phase2", "\"auth=PAP\"", 0) <
 +                          0)
 +                              goto fail;
 +                      break;
 +              case NAI_REALM_INNER_NON_EAP_CHAP:
 +                      if (wpa_config_set(ssid, "phase2", "\"auth=CHAP\"", 0)
 +                          < 0)
 +                              goto fail;
 +                      break;
 +              case NAI_REALM_INNER_NON_EAP_MSCHAP:
 +                      if (wpa_config_set(ssid, "phase2", "\"auth=MSCHAP\"",
 +                                         0) < 0)
 +                              goto fail;
 +                      break;
 +              case NAI_REALM_INNER_NON_EAP_MSCHAPV2:
 +                      if (wpa_config_set(ssid, "phase2", "\"auth=MSCHAPV2\"",
 +                                         0) < 0)
 +                              goto fail;
 +                      break;
 +              default:
 +                      /* EAP params were not set - assume TTLS/MSCHAPv2 */
 +                      if (wpa_config_set(ssid, "phase2", "\"auth=MSCHAPV2\"",
 +                                         0) < 0)
 +                              goto fail;
 +                      break;
 +              }
 +              break;
 +      case EAP_TYPE_PEAP:
 +      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;
 +      case EAP_TYPE_TLS:
 +              break;
 +      }
 +
 +      if (interworking_set_eap_params(ssid, cred,
 +                                      eap->method == EAP_TYPE_TTLS) < 0)
 +              goto fail;
 +
 +      nai_realm_free(realm, count);
 +
 +      wpa_s->next_ssid = ssid;
 +      wpa_config_update_prio_list(wpa_s->conf);
 +      if (!only_add)
 +              interworking_reconnect(wpa_s);
 +
 +      return ssid->id;
 +
 +fail:
 +      wpas_notify_network_removed(wpa_s, ssid);
 +      wpa_config_remove_network(wpa_s->conf, ssid->id);
 +      nai_realm_free(realm, count);
 +      return -1;
 +}
 +
 +
 +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, 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) {
 +              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->scard) {
 +                      if (interworking_pcsc_read_imsi(wpa_s) < 0)
 +                              continue;
 +                      imsi = wpa_s->imsi;
 +                      mnc_len = wpa_s->mnc_len;
 +                      goto compare;
 +              }
 +#endif /* PCSC_FUNCS */
 +#ifdef CONFIG_EAP_PROXY
 +              if (cred->pcsc && wpa_s->mnc_len > 0 && wpa_s->imsi[0]) {
 +                      imsi = wpa_s->imsi;
 +                      mnc_len = wpa_s->mnc_len;
 +                      goto compare;
 +              }
 +#endif /* CONFIG_EAP_PROXY */
 +
 +              if (cred->imsi == NULL || !cred->imsi[0] ||
 +                  (!wpa_s->conf->external_sim &&
 +                   (cred->milenage == NULL || !cred->milenage[0])))
 +                      continue;
 +
 +              sep = os_strchr(cred->imsi, '-');
 +              if (sep == NULL ||
 +                  (sep - cred->imsi != 5 && sep - cred->imsi != 6))
 +                      continue;
 +              mnc_len = sep - cred->imsi - 3;
 +              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_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_msg(wpa_s, MSG_DEBUG, "PLMN match %sfound",
 +                      ret ? "" : "not ");
 +              if (ret) {
 +                      if (cred_no_required_oi_match(cred, bss))
 +                              continue;
 +                      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, 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;
 +
 +      if (wpa_s->conf->cred == NULL)
 +              return NULL;
 +
 +      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_msg(wpa_s, MSG_DEBUG,
 +                      "Interworking: Could not parse NAI Realm list from "
 +                      MACSTR, MAC2STR(bss->bssid));
 +              return NULL;
 +      }
 +
 +      for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
 +              if (cred->realm == NULL)
 +                      continue;
 +
 +              for (i = 0; i < count; i++) {
 +                      if (!nai_realm_match(&realm[i], cred->realm))
 +                              continue;
 +                      if (nai_realm_find_eap(wpa_s, cred, &realm[i])) {
 +                              if (cred_no_required_oi_match(cred, bss))
 +                                      continue;
 +                              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_helper(
 +      struct wpa_supplicant *wpa_s, struct wpa_bss *bss, int ignore_bw,
 +      int *excluded)
 +{
 +      struct wpa_cred *cred, *cred2;
-       freq = wpa_s->assoc_freq;
++      int excluded1, excluded2 = 0;
 +
 +      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, 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;
 +              excluded1 = excluded2;
 +      }
 +      if (!cred) {
 +              cred = cred2;
 +              excluded1 = excluded2;
 +      }
 +
 +      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;
 +              excluded1 = excluded2;
 +      }
 +      if (!cred) {
 +              cred = cred2;
 +              excluded1 = excluded2;
 +      }
 +
 +      if (excluded)
 +              *excluded = excluded1;
 +      return cred;
 +}
 +
 +
 +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;
 +
 +      len = os_strlen(domain);
 +      pos = wpabuf_head(domain_names);
 +      end = pos + wpabuf_len(domain_names);
 +
 +      while (pos + 1 < end) {
 +              if (pos + 1 + pos[0] > end)
 +                      break;
 +
 +              wpa_hexdump_ascii(MSG_DEBUG, "Interworking: AP domain name",
 +                                pos + 1, pos[0]);
 +              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];
 +      }
 +
 +      return 0;
 +}
 +
 +
 +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;
 +
 +      char *imsi = NULL;
 +      int mnc_len = 0;
 +      if (cred->imsi)
 +              imsi = cred->imsi;
 +#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 /* 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_msg(wpa_s, MSG_DEBUG,
 +                      "Interworking: Search for match with SIM/USIM domain %s",
 +                      realm);
 +              if (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 ret;
 +
 +      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;
 +}
 +
 +
 +static int interworking_home_sp(struct wpa_supplicant *wpa_s,
 +                              struct wpabuf *domain_names)
 +{
 +      struct wpa_cred *cred;
 +
 +      if (domain_names == NULL || wpa_s->conf->cred == NULL)
 +              return -1;
 +
 +      for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
 +              int res = interworking_home_sp_cred(wpa_s, cred, domain_names);
 +              if (res)
 +                      return res;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +static int interworking_find_network_match(struct wpa_supplicant *wpa_s)
 +{
 +      struct wpa_bss *bss;
 +      struct wpa_ssid *ssid;
 +
 +      dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
 +              for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
 +                      if (wpas_network_disabled(wpa_s, ssid) ||
 +                          ssid->mode != WPAS_MODE_INFRA)
 +                              continue;
 +                      if (ssid->ssid_len != bss->ssid_len ||
 +                          os_memcmp(ssid->ssid, bss->ssid, ssid->ssid_len) !=
 +                          0)
 +                              continue;
 +                      /*
 +                       * TODO: Consider more accurate matching of security
 +                       * configuration similarly to what is done in events.c
 +                       */
 +                      return 1;
 +              }
 +      }
 +
 +      return 0;
 +}
 +
 +
 +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;
 +      struct wpa_bss *selected2 = NULL, *selected2_home = NULL;
 +      unsigned int count = 0;
 +      const char *type;
 +      int res;
 +      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) {
 +              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_msg(wpa_s, MSG_DEBUG,
 +                              "Interworking: Credential match with " MACSTR
 +                              " but network does not use RSN",
 +                              MAC2STR(bss->bssid));
 +                      continue;
 +              }
 +              if (!excluded)
 +                      count++;
 +              res = interworking_home_sp(wpa_s, bss->anqp ?
 +                                         bss->anqp->domain_name : NULL);
 +              if (res > 0)
 +                      type = "home";
 +              else if (res == 0)
 +                      type = "roaming";
 +              else
 +                      type = "unknown";
 +              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 (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_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) {
 +              /*
 +               * No matching network was found based on configured
 +               * credentials. Check whether any of the enabled network blocks
 +               * have matching APs.
 +               */
 +              if (interworking_find_network_match(wpa_s)) {
 +                      wpa_msg(wpa_s, MSG_DEBUG,
 +                              "Interworking: Possible BSS match for enabled network configurations");
 +                      if (wpa_s->auto_select) {
 +                              interworking_reconnect(wpa_s);
 +                              return;
 +                      }
 +              }
 +
 +              if (wpa_s->auto_network_select) {
 +                      wpa_msg(wpa_s, MSG_DEBUG,
 +                              "Interworking: Continue scanning after ANQP fetch");
 +                      wpa_supplicant_req_scan(wpa_s, wpa_s->scan_interval,
 +                                              0);
 +                      return;
 +              }
 +
 +              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);
 +      }
 +}
 +
 +
 +static struct wpa_bss_anqp *
 +interworking_match_anqp_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
 +{
 +      struct wpa_bss *other;
 +
 +      if (is_zero_ether_addr(bss->hessid))
 +              return NULL; /* Cannot be in the same homegenous ESS */
 +
 +      dl_list_for_each(other, &wpa_s->bss, struct wpa_bss, list) {
 +              if (other == bss)
 +                      continue;
 +              if (other->anqp == NULL)
 +                      continue;
 +              if (other->anqp->roaming_consortium == NULL &&
 +                  other->anqp->nai_realm == NULL &&
 +                  other->anqp->anqp_3gpp == NULL &&
 +                  other->anqp->domain_name == NULL)
 +                      continue;
 +              if (!(other->flags & WPA_BSS_ANQP_FETCH_TRIED))
 +                      continue;
 +              if (os_memcmp(bss->hessid, other->hessid, ETH_ALEN) != 0)
 +                      continue;
 +              if (bss->ssid_len != other->ssid_len ||
 +                  os_memcmp(bss->ssid, other->ssid, bss->ssid_len) != 0)
 +                      continue;
 +
 +              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;
 +      }
 +
 +      return NULL;
 +}
 +
 +
 +static void interworking_next_anqp_fetch(struct wpa_supplicant *wpa_s)
 +{
 +      struct wpa_bss *bss;
 +      int found = 0;
 +      const u8 *ie;
 +
 +      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))
 +                      continue;
 +              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) {
 +                              bss->anqp = interworking_match_anqp_info(wpa_s,
 +                                                                       bss);
 +                              if (bss->anqp) {
 +                                      /* Shared data already fetched */
 +                                      continue;
 +                              }
 +                              bss->anqp = wpa_bss_anqp_alloc();
 +                              if (bss->anqp == NULL)
 +                                      break;
 +                      }
 +                      found++;
 +                      bss->flags |= WPA_BSS_ANQP_FETCH_TRIED;
 +                      wpa_msg(wpa_s, MSG_INFO, "Starting ANQP fetch for "
 +                              MACSTR, MAC2STR(bss->bssid));
 +                      interworking_anqp_send_req(wpa_s, bss);
 +                      break;
 +              }
 +      }
 +
 +      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)
 +                      interworking_select_network(wpa_s);
 +      }
 +}
 +
 +
 +void interworking_start_fetch_anqp(struct wpa_supplicant *wpa_s)
 +{
 +      struct wpa_bss *bss;
 +
 +      dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list)
 +              bss->flags &= ~WPA_BSS_ANQP_FETCH_TRIED;
 +
 +      wpa_s->fetch_anqp_in_progress = 1;
 +
 +      /*
 +       * 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);
 +}
 +
 +
 +int interworking_fetch_anqp(struct wpa_supplicant *wpa_s)
 +{
 +      if (wpa_s->fetch_anqp_in_progress || wpa_s->network_select)
 +              return 0;
 +
 +      wpa_s->network_select = 0;
 +      wpa_s->fetch_all_anqp = 1;
 +      wpa_s->fetch_osu_info = 0;
 +
 +      interworking_start_fetch_anqp(wpa_s);
 +
 +      return 0;
 +}
 +
 +
 +void interworking_stop_fetch_anqp(struct wpa_supplicant *wpa_s)
 +{
 +      if (!wpa_s->fetch_anqp_in_progress)
 +              return;
 +
 +      wpa_s->fetch_anqp_in_progress = 0;
 +}
 +
 +
 +int anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst,
 +                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;
 +      int res;
 +
-       if (bss) {
-               wpa_bss_anqp_unshare_alloc(bss);
-               freq = bss->freq;
-       }
-       if (freq <= 0)
 +      bss = wpa_bss_get_bssid(wpa_s, dst);
++      if (!bss) {
++              wpa_printf(MSG_WARNING,
++                         "ANQP: Cannot send query to unknown BSS "
++                         MACSTR, MAC2STR(dst));
 +              return -1;
++      }
++
++      wpa_bss_anqp_unshare_alloc(bss);
++      freq = bss->freq;
 +
 +      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, 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_msg(wpa_s, MSG_DEBUG, "ANQP: Failed to send Query Request");
 +              wpabuf_free(buf);
 +              ret = -1;
 +      } else {
 +              wpa_msg(wpa_s, MSG_DEBUG,
 +                      "ANQP: Query started with dialog token %u", res);
 +      }
 +
 +      return ret;
 +}
 +
 +
 +static void interworking_parse_rx_anqp_resp(struct wpa_supplicant *wpa_s,
 +                                          struct wpa_bss *bss, const u8 *sa,
 +                                          u16 info_id,
 +                                          const u8 *data, size_t slen)
 +{
 +      const u8 *pos = data;
 +      struct wpa_bss_anqp *anqp = NULL;
 +#ifdef CONFIG_HS20
 +      u8 type;
 +#endif /* CONFIG_HS20 */
 +
 +      if (bss)
 +              anqp = bss->anqp;
 +
 +      switch (info_id) {
 +      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
 +                      " Venue Name", MAC2STR(sa));
 +              wpa_hexdump_ascii(MSG_DEBUG, "ANQP: Venue Name", pos, slen);
 +              if (anqp) {
 +                      wpabuf_free(anqp->venue_name);
 +                      anqp->venue_name = wpabuf_alloc_copy(pos, slen);
 +              }
 +              break;
 +      case ANQP_NETWORK_AUTH_TYPE:
 +              wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
 +                      " Network Authentication Type information",
 +                      MAC2STR(sa));
 +              wpa_hexdump_ascii(MSG_DEBUG, "ANQP: Network Authentication "
 +                                "Type", pos, slen);
 +              if (anqp) {
 +                      wpabuf_free(anqp->network_auth_type);
 +                      anqp->network_auth_type = wpabuf_alloc_copy(pos, slen);
 +              }
 +              break;
 +      case ANQP_ROAMING_CONSORTIUM:
 +              wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
 +                      " Roaming Consortium list", MAC2STR(sa));
 +              wpa_hexdump_ascii(MSG_DEBUG, "ANQP: Roaming Consortium",
 +                                pos, slen);
 +              if (anqp) {
 +                      wpabuf_free(anqp->roaming_consortium);
 +                      anqp->roaming_consortium = wpabuf_alloc_copy(pos, slen);
 +              }
 +              break;
 +      case ANQP_IP_ADDR_TYPE_AVAILABILITY:
 +              wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
 +                      " IP Address Type Availability information",
 +                      MAC2STR(sa));
 +              wpa_hexdump(MSG_MSGDUMP, "ANQP: IP Address Availability",
 +                          pos, slen);
 +              if (anqp) {
 +                      wpabuf_free(anqp->ip_addr_type_availability);
 +                      anqp->ip_addr_type_availability =
 +                              wpabuf_alloc_copy(pos, slen);
 +              }
 +              break;
 +      case ANQP_NAI_REALM:
 +              wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
 +                      " NAI Realm list", MAC2STR(sa));
 +              wpa_hexdump_ascii(MSG_DEBUG, "ANQP: NAI Realm", pos, slen);
 +              if (anqp) {
 +                      wpabuf_free(anqp->nai_realm);
 +                      anqp->nai_realm = wpabuf_alloc_copy(pos, slen);
 +              }
 +              break;
 +      case ANQP_3GPP_CELLULAR_NETWORK:
 +              wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
 +                      " 3GPP Cellular Network information", MAC2STR(sa));
 +              wpa_hexdump_ascii(MSG_DEBUG, "ANQP: 3GPP Cellular Network",
 +                                pos, slen);
 +              if (anqp) {
 +                      wpabuf_free(anqp->anqp_3gpp);
 +                      anqp->anqp_3gpp = wpabuf_alloc_copy(pos, slen);
 +              }
 +              break;
 +      case ANQP_DOMAIN_NAME:
 +              wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
 +                      " Domain Name list", MAC2STR(sa));
 +              wpa_hexdump_ascii(MSG_MSGDUMP, "ANQP: Domain Name", pos, slen);
 +              if (anqp) {
 +                      wpabuf_free(anqp->domain_name);
 +                      anqp->domain_name = wpabuf_alloc_copy(pos, slen);
 +              }
 +              break;
 +      case ANQP_VENDOR_SPECIFIC:
 +              if (slen < 3)
 +                      return;
 +
 +              switch (WPA_GET_BE24(pos)) {
 +#ifdef CONFIG_HS20
 +              case OUI_WFA:
 +                      pos += 3;
 +                      slen -= 3;
 +
 +                      if (slen < 1)
 +                              return;
 +                      type = *pos++;
 +                      slen--;
 +
 +                      switch (type) {
 +                      case HS20_ANQP_OUI_TYPE:
 +                              hs20_parse_rx_hs20_anqp_resp(wpa_s, bss, sa,
 +                                                           pos, slen);
 +                              break;
 +                      default:
 +                              wpa_msg(wpa_s, MSG_DEBUG,
 +                                      "HS20: Unsupported ANQP vendor type %u",
 +                                      type);
 +                              break;
 +                      }
 +                      break;
 +#endif /* CONFIG_HS20 */
 +              default:
 +                      wpa_msg(wpa_s, MSG_DEBUG,
 +                              "Interworking: Unsupported vendor-specific ANQP OUI %06x",
 +                              WPA_GET_BE24(pos));
 +                      return;
 +              }
 +              break;
 +      default:
 +              wpa_msg(wpa_s, MSG_DEBUG,
 +                      "Interworking: Unsupported ANQP Info ID %u", info_id);
 +              break;
 +      }
 +}
 +
 +
 +void anqp_resp_cb(void *ctx, const u8 *dst, u8 dialog_token,
 +                enum gas_query_result result,
 +                const struct wpabuf *adv_proto,
 +                const struct wpabuf *resp, u16 status_code)
 +{
 +      struct wpa_supplicant *wpa_s = ctx;
 +      const u8 *pos;
 +      const u8 *end;
 +      u16 info_id;
 +      u16 slen;
 +      struct wpa_bss *bss = NULL, *tmp;
 +      const char *anqp_result = "SUCCESS";
 +
 +      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_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;
 +      }
 +
 +      /*
 +       * If possible, select the BSS entry based on which BSS entry was used
 +       * for the request. This can help in cases where multiple BSS entries
 +       * may exist for the same AP.
 +       */
 +      dl_list_for_each_reverse(tmp, &wpa_s->bss, struct wpa_bss, list) {
 +              if (tmp == wpa_s->interworking_gas_bss &&
 +                  os_memcmp(tmp->bssid, dst, ETH_ALEN) == 0) {
 +                      bss = tmp;
 +                      break;
 +              }
 +      }
 +      if (bss == NULL)
 +              bss = wpa_bss_get_bssid(wpa_s, dst);
 +
 +      pos = wpabuf_head(resp);
 +      end = pos + wpabuf_len(resp);
 +
 +      while (pos < end) {
 +              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;
 +              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_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 *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_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;
 +}
 +
 +
 +static void gas_resp_cb(void *ctx, const u8 *addr, u8 dialog_token,
 +                      enum gas_query_result result,
 +                      const struct wpabuf *adv_proto,
 +                      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",
 +              MAC2STR(addr), dialog_token, status_code,
 +              resp ? (int) wpabuf_len(resp) : -1);
 +      if (!resp)
 +              return;
 +
 +      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;
 +}
 +
 +
 +int gas_send_request(struct wpa_supplicant *wpa_s, const u8 *dst,
 +                   const struct wpabuf *adv_proto,
 +                   const struct wpabuf *query)
 +{
 +      struct wpabuf *buf;
 +      int ret = 0;
 +      int freq;
 +      struct wpa_bss *bss;
 +      int res;
 +      size_t len;
 +      u8 query_resp_len_limit = 0;
 +
 +      freq = wpa_s->assoc_freq;
 +      bss = wpa_bss_get_bssid(wpa_s, dst);
 +      if (bss)
 +              freq = bss->freq;
 +      if (freq <= 0)
 +              return -1;
 +
 +      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);
 +
 +      len = 3 + wpabuf_len(adv_proto) + 2;
 +      if (query)
 +              len += wpabuf_len(query);
 +      buf = gas_build_initial_req(0, len);
 +      if (buf == NULL)
 +              return -1;
 +
 +      /* 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);
 +      wpabuf_put_buf(buf, adv_proto);
 +
 +      /* GAS Query */
 +      if (query) {
 +              wpabuf_put_le16(buf, wpabuf_len(query));
 +              wpabuf_put_buf(buf, query);
 +      } else
 +              wpabuf_put_le16(buf, 0);
 +
 +      res = gas_query_req(wpa_s->gas, dst, freq, buf, gas_resp_cb, wpa_s);
 +      if (res < 0) {
 +              wpa_msg(wpa_s, MSG_DEBUG, "GAS: Failed to send Query Request");
 +              wpabuf_free(buf);
 +              ret = -1;
 +      } else
 +              wpa_msg(wpa_s, MSG_DEBUG,
 +                      "GAS: Query started with dialog token %u", res);
 +
 +      return ret;
 +}
index 22827479c64354bead240e91d3556a586a5ccddc,0000000000000000000000000000000000000000..d5d47e1d77b2485ce962177b3c0146429493245f
mode 100644,000000..100644
--- /dev/null
@@@ -1,345 -1,0 +1,359 @@@
-                       iface->conf_p2p_dev = optarg;
 +/*
 + * WPA Supplicant / main() function for UNIX like OSes and MinGW
 + * 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 "includes.h"
 +#ifdef __linux__
 +#include <fcntl.h>
 +#endif /* __linux__ */
 +
 +#include "common.h"
++#include "fst/fst.h"
 +#include "wpa_supplicant_i.h"
 +#include "driver_i.h"
 +#include "p2p_supplicant.h"
 +
 +
 +static void usage(void)
 +{
 +      int i;
 +      printf("%s\n\n%s\n"
 +             "usage:\n"
 +             "  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>] [-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"
 +             "drivers:\n",
 +             wpa_supplicant_version, wpa_supplicant_license);
 +
 +      for (i = 0; wpa_drivers[i]; i++) {
 +              printf("  %s = %s\n",
 +                     wpa_drivers[i]->name,
 +                     wpa_drivers[i]->desc);
 +      }
 +
 +#ifndef CONFIG_NO_STDOUT_DEBUG
 +      printf("options:\n"
 +             "  -b = optional bridge interface name\n"
 +             "  -B = run daemon in the background\n"
 +             "  -c = Configuration file\n"
 +             "  -C = ctrl_interface parameter (only used if -c is not)\n"
 +             "  -i = interface name\n"
 +             "  -I = additional configuration file\n"
 +             "  -d = increase debugging verbosity (-dd even more)\n"
 +             "  -D = driver name (can be multiple drivers: nl80211,wext)\n"
 +             "  -e = entropy file\n");
 +#ifdef CONFIG_DEBUG_FILE
 +      printf("  -f = log output to debug file instead of stdout\n");
 +#endif /* CONFIG_DEBUG_FILE */
 +      printf("  -g = global ctrl_interface\n"
 +             "  -G = global ctrl_interface group\n"
 +             "  -K = include keys (passwords, etc.) in debug output\n");
 +#ifdef CONFIG_DEBUG_SYSLOG
 +      printf("  -s = log output to syslog instead of stdout\n");
 +#endif /* CONFIG_DEBUG_SYSLOG */
 +#ifdef CONFIG_DEBUG_LINUX_TRACING
 +      printf("  -T = record to Linux tracing in addition to logging\n");
 +      printf("       (records all messages regardless of debug verbosity)\n");
 +#endif /* CONFIG_DEBUG_LINUX_TRACING */
 +      printf("  -t = include timestamp in debug messages\n"
 +             "  -h = show this help text\n"
 +             "  -L = show license (BSD)\n"
 +             "  -o = override driver parameter for new interfaces\n"
 +             "  -O = override ctrl_interface parameter for new interfaces\n"
 +             "  -p = driver parameters\n"
 +             "  -P = PID file\n"
 +             "  -q = decrease debugging verbosity (-qq even less)\n");
 +#ifdef CONFIG_DBUS
 +      printf("  -u = enable DBus control interface\n");
 +#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"
 +             "  wpa_supplicant -D%s -iwlan0 -c/etc/wpa_supplicant.conf\n",
 +             wpa_drivers[0] ? wpa_drivers[0]->name : "nl80211");
 +#endif /* CONFIG_NO_STDOUT_DEBUG */
 +}
 +
 +
 +static void license(void)
 +{
 +#ifndef CONFIG_NO_STDOUT_DEBUG
 +      printf("%s\n\n%s%s%s%s%s\n",
 +             wpa_supplicant_version,
 +             wpa_supplicant_full_license1,
 +             wpa_supplicant_full_license2,
 +             wpa_supplicant_full_license3,
 +             wpa_supplicant_full_license4,
 +             wpa_supplicant_full_license5);
 +#endif /* CONFIG_NO_STDOUT_DEBUG */
 +}
 +
 +
 +static void wpa_supplicant_fd_workaround(int start)
 +{
 +#ifdef __linux__
 +      static int fd[3] = { -1, -1, -1 };
 +      int i;
 +      /* When started from pcmcia-cs scripts, wpa_supplicant might start with
 +       * fd 0, 1, and 2 closed. This will cause some issues because many
 +       * places in wpa_supplicant are still printing out to stdout. As a
 +       * workaround, make sure that fd's 0, 1, and 2 are not used for other
 +       * sockets. */
 +      if (start) {
 +              for (i = 0; i < 3; i++) {
 +                      fd[i] = open("/dev/null", O_RDWR);
 +                      if (fd[i] > 2) {
 +                              close(fd[i]);
 +                              fd[i] = -1;
 +                              break;
 +                      }
 +              }
 +      } else {
 +              for (i = 0; i < 3; i++) {
 +                      if (fd[i] >= 0) {
 +                              close(fd[i]);
 +                              fd[i] = -1;
 +                      }
 +              }
 +      }
 +#endif /* __linux__ */
 +}
 +
 +
 +int main(int argc, char *argv[])
 +{
 +      int c, i;
 +      struct wpa_interface *ifaces, *iface;
 +      int iface_count, exitcode = -1;
 +      struct wpa_params params;
 +      struct wpa_global *global;
 +
 +      if (os_program_init())
 +              return -1;
 +
 +      os_memset(&params, 0, sizeof(params));
 +      params.wpa_debug_level = MSG_INFO;
 +
 +      iface = ifaces = os_zalloc(sizeof(struct wpa_interface));
 +      if (ifaces == NULL)
 +              return -1;
 +      iface_count = 1;
 +
 +      wpa_supplicant_fd_workaround(1);
 +
 +      for (;;) {
 +              c = getopt(argc, argv,
 +                         "b:Bc:C:D:de:f:g:G:hi:I:KLm:No:O:p:P:qsTtuvW");
 +              if (c < 0)
 +                      break;
 +              switch (c) {
 +              case 'b':
 +                      iface->bridge_ifname = optarg;
 +                      break;
 +              case 'B':
 +                      params.daemonize++;
 +                      break;
 +              case 'c':
 +                      iface->confname = optarg;
 +                      break;
 +              case 'C':
 +                      iface->ctrl_interface = optarg;
 +                      break;
 +              case 'D':
 +                      iface->driver = optarg;
 +                      break;
 +              case 'd':
 +#ifdef CONFIG_NO_STDOUT_DEBUG
 +                      printf("Debugging disabled with "
 +                             "CONFIG_NO_STDOUT_DEBUG=y build time "
 +                             "option.\n");
 +                      goto out;
 +#else /* CONFIG_NO_STDOUT_DEBUG */
 +                      params.wpa_debug_level--;
 +                      break;
 +#endif /* CONFIG_NO_STDOUT_DEBUG */
 +              case 'e':
 +                      params.entropy_file = optarg;
 +                      break;
 +#ifdef CONFIG_DEBUG_FILE
 +              case 'f':
 +                      params.wpa_debug_file_path = optarg;
 +                      break;
 +#endif /* CONFIG_DEBUG_FILE */
 +              case 'g':
 +                      params.ctrl_interface = optarg;
 +                      break;
 +              case 'G':
 +                      params.ctrl_interface_group = optarg;
 +                      break;
 +              case 'h':
 +                      usage();
 +                      exitcode = 0;
 +                      goto out;
 +              case 'i':
 +                      iface->ifname = optarg;
 +                      break;
 +              case 'I':
 +                      iface->confanother = optarg;
 +                      break;
 +              case 'K':
 +                      params.wpa_debug_show_keys++;
 +                      break;
 +              case 'L':
 +                      license();
 +                      exitcode = 0;
 +                      goto out;
 +#ifdef CONFIG_P2P
 +              case 'm':
-                       iface = &ifaces[iface_count - 1]; 
++                      params.conf_p2p_dev = optarg;
 +                      break;
 +#endif /* CONFIG_P2P */
 +              case 'o':
 +                      params.override_driver = optarg;
 +                      break;
 +              case 'O':
 +                      params.override_ctrl_interface = optarg;
 +                      break;
 +              case 'p':
 +                      iface->driver_param = optarg;
 +                      break;
 +              case 'P':
 +                      os_free(params.pid_file);
 +                      params.pid_file = os_rel2abs_path(optarg);
 +                      break;
 +              case 'q':
 +                      params.wpa_debug_level++;
 +                      break;
 +#ifdef CONFIG_DEBUG_SYSLOG
 +              case 's':
 +                      params.wpa_debug_syslog++;
 +                      break;
 +#endif /* CONFIG_DEBUG_SYSLOG */
 +#ifdef CONFIG_DEBUG_LINUX_TRACING
 +              case 'T':
 +                      params.wpa_debug_tracing++;
 +                      break;
 +#endif /* CONFIG_DEBUG_LINUX_TRACING */
 +              case 't':
 +                      params.wpa_debug_timestamp++;
 +                      break;
 +#ifdef CONFIG_DBUS
 +              case 'u':
 +                      params.dbus_ctrl_interface = 1;
 +                      break;
 +#endif /* CONFIG_DBUS */
 +              case 'v':
 +                      printf("%s\n", wpa_supplicant_version);
 +                      exitcode = 0;
 +                      goto out;
 +              case 'W':
 +                      params.wait_for_monitor++;
 +                      break;
 +              case 'N':
 +                      iface_count++;
 +                      iface = os_realloc_array(ifaces, iface_count,
 +                                               sizeof(struct wpa_interface));
 +                      if (iface == NULL)
 +                              goto out;
 +                      ifaces = iface;
++                      iface = &ifaces[iface_count - 1];
 +                      os_memset(iface, 0, sizeof(*iface));
 +                      break;
 +              default:
 +                      usage();
 +                      exitcode = 0;
 +                      goto out;
 +              }
 +      }
 +
 +      exitcode = 0;
 +      global = wpa_supplicant_init(&params);
 +      if (global == NULL) {
 +              wpa_printf(MSG_ERROR, "Failed to initialize wpa_supplicant");
 +              exitcode = -1;
 +              goto out;
 +      } else {
 +              wpa_printf(MSG_INFO, "Successfully initialized "
 +                         "wpa_supplicant");
 +      }
 +
++      if (fst_global_init()) {
++              wpa_printf(MSG_ERROR, "Failed to initialize FST");
++              exitcode = -1;
++              goto out;
++      }
++
++#if defined(CONFIG_FST) && defined(CONFIG_CTRL_IFACE)
++      if (!fst_global_add_ctrl(fst_ctrl_cli))
++              wpa_printf(MSG_WARNING, "Failed to add CLI FST ctrl");
++#endif
++
 +      for (i = 0; exitcode == 0 && i < iface_count; i++) {
 +              struct wpa_supplicant *wpa_s;
 +
 +              if ((ifaces[i].confname == NULL &&
 +                   ifaces[i].ctrl_interface == NULL) ||
 +                  ifaces[i].ifname == NULL) {
 +                      if (iface_count == 1 && (params.ctrl_interface ||
 +                                               params.dbus_ctrl_interface))
 +                              break;
 +                      usage();
 +                      exitcode = -1;
 +                      break;
 +              }
 +              wpa_s = wpa_supplicant_add_iface(global, &ifaces[i], NULL);
 +              if (wpa_s == NULL) {
 +                      exitcode = -1;
 +                      break;
 +              }
 +      }
 +
 +      if (exitcode == 0)
 +              exitcode = wpa_supplicant_run(global);
 +
 +      wpa_supplicant_deinit(global);
 +
++      fst_global_deinit();
++
 +out:
 +      wpa_supplicant_fd_workaround(0);
 +      os_free(ifaces);
 +      os_free(params.pid_file);
 +
 +      os_program_deinit();
 +
 +      return exitcode;
 +}
index 33b4af38faa86f868655581bf185d988794f9cfb,0000000000000000000000000000000000000000..77f708b42daa9690b572ad7018abc6f8bc3cf2ce
mode 100644,000000..100644
--- /dev/null
@@@ -1,540 -1,0 +1,543 @@@
-               if (ifmsh->mconf->ies) {
-                       ifmsh->mconf->ies = NULL;
 +/*
 + * 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);
-               params.ies = wpa_s->ifmsh->mconf->ies;
-               params.ie_len = wpa_s->ifmsh->mconf->ie_len;
++              if (ifmsh->mconf->rsn_ie) {
++                      ifmsh->mconf->rsn_ie = 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;
++      ifmsh->bss[0]->dot11RSNASAERetransPeriod =
++              wpa_s->conf->dot11RSNASAERetransPeriod;
 +      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) {
-                       return pos - buf;
++              params.ies = wpa_s->ifmsh->mconf->rsn_ie;
++              params.ie_len = wpa_s->ifmsh->mconf->rsn_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;
++                      goto fail;
 +              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;
++                              goto fail;
 +                      pos += ret;
 +              }
 +
 +              ret = os_snprintf(pos, end - pos, "\n");
 +              if (os_snprintf_error(end - pos, ret))
++                      goto fail;
 +              pos += ret;
 +      }
++fail:
 +      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;
 +}
index 1d6f2be2b50bb1d15af9999b87a206eaedcbca07,0000000000000000000000000000000000000000..f81b88c89401b6e1ccd38109536a11f4c035c44e
mode 100644,000000..100644
--- /dev/null
@@@ -1,1059 -1,0 +1,1064 @@@
-       copy_sta_ht_capab(data, sta, elems->ht_capabilities,
-                       elems->ht_capabilities_len);
 +/*
 + * 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 */
++      if (type != PLINK_CLOSE)
++              buf_len += conf->rsn_ie_len; /* RSN IE */
++
 +      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: RSN IE */
++              wpabuf_put_data(buf, conf->rsn_ie, conf->rsn_ie_len);
++
 +              /* 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);
 +      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);
 +}
index 936002d954ec1a2fa0a1927b61a6093fa888a4d9,0000000000000000000000000000000000000000..747f1ae6968b4e5d08083a011898bc68604d3da7
mode 100644,000000..100644
--- /dev/null
@@@ -1,574 -1,0 +1,576 @@@
-       wpa_deinit(rsn->auth);
 +/*
 + * 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));
-       conf->ies = (u8 *) ie;
-       conf->ie_len = ie_len;
++      if (rsn->auth)
++              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);
++              os_free(mesh_rsn);
 +              return NULL;
 +      }
 +
 +      bss->wpa_auth = mesh_rsn->auth;
 +
 +      ie = wpa_auth_get_wpa_ie(mesh_rsn->auth, &ie_len);
++      conf->rsn_ie = (u8 *) ie;
++      conf->rsn_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;
 +}
index ea7dbdb15bf2475770d67242402ea46e5116201b,0000000000000000000000000000000000000000..45d06bf3574464adcb98a90826d66b9777e1c1a9
mode 100644,000000..100644
--- /dev/null
@@@ -1,785 -1,0 +1,831 @@@
-       if (wpa_s->p2p_mgmt)
-               return;
 +/*
 + * wpa_supplicant - Event notifications
 + * Copyright (c) 2009-2010, Jouni Malinen <j@w1.fi>
 + *
 + * This software may be distributed under the terms of the BSD license.
 + * See README for more details.
 + */
 +
 +#include "utils/includes.h"
 +
 +#include "utils/common.h"
 +#include "common/wpa_ctrl.h"
 +#include "config.h"
 +#include "wpa_supplicant_i.h"
 +#include "wps_supplicant.h"
 +#include "dbus/dbus_common.h"
 +#include "dbus/dbus_old.h"
 +#include "dbus/dbus_new.h"
 +#include "rsn_supp/wpa.h"
++#include "fst/fst.h"
 +#include "driver_i.h"
 +#include "scan.h"
 +#include "p2p_supplicant.h"
 +#include "sme.h"
 +#include "notify.h"
 +
 +int wpas_notify_supplicant_initialized(struct wpa_global *global)
 +{
 +#ifdef CONFIG_DBUS
 +      if (global->params.dbus_ctrl_interface) {
 +              global->dbus = wpas_dbus_init(global);
 +              if (global->dbus == NULL)
 +                      return -1;
 +      }
 +#endif /* CONFIG_DBUS */
 +
 +      return 0;
 +}
 +
 +
 +void wpas_notify_supplicant_deinitialized(struct wpa_global *global)
 +{
 +#ifdef CONFIG_DBUS
 +      if (global->dbus)
 +              wpas_dbus_deinit(global->dbus);
 +#endif /* CONFIG_DBUS */
 +}
 +
 +
 +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;
 +
 +      if (wpas_dbus_register_interface(wpa_s))
 +              return -1;
 +
 +      return 0;
 +}
 +
 +
 +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);
 +
 +      /* unregister interface in new DBus ctrl iface */
 +      wpas_dbus_unregister_interface(wpa_s);
 +}
 +
 +
 +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);
 +
 +      /* notify the new DBus API */
 +      wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_STATE);
 +
++#ifdef CONFIG_FST
++      if (wpa_s->fst && !is_zero_ether_addr(wpa_s->bssid)) {
++              if (new_state == WPA_COMPLETED)
++                      fst_notify_peer_connected(wpa_s->fst, wpa_s->bssid);
++              else if (old_state >= WPA_ASSOCIATED &&
++                       new_state < WPA_ASSOCIATED)
++                      fst_notify_peer_disconnected(wpa_s->fst, wpa_s->bssid);
++      }
++#endif /* CONFIG_FST */
++
 +      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);
 +
 +      sme_state_changed(wpa_s);
 +
 +#ifdef ANDROID
 +      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,
 +                   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);
 +}
 +
 +
 +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);
 +}
 +
 +
 +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);
 +}
 +
 +
 +void wpas_notify_network_request(struct wpa_supplicant *wpa_s,
 +                               struct wpa_ssid *ssid,
 +                               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);
 +
 +      /* notify the new DBus API */
 +      wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_SCANNING);
 +}
 +
 +
 +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);
 +
 +      wpas_wps_notify_scan_results(wpa_s);
 +}
 +
 +
 +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);
 +      /* notify the new DBus API */
 +      wpas_dbus_signal_wps_cred(wpa_s, cred);
 +#endif /* CONFIG_WPS */
 +}
 +
 +
 +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 */
 +}
 +
 +
 +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 */
 +}
 +
 +
 +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 */
 +}
 +
++void wpas_notify_wps_event_pbc_overlap(struct wpa_supplicant *wpa_s)
++{
++      if (wpa_s->p2p_mgmt)
++              return;
++
++#ifdef CONFIG_WPS
++      wpas_dbus_signal_wps_event_pbc_overlap(wpa_s);
++#endif /* CONFIG_WPS */
++}
++
 +
 +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 (!ssid->p2p_group && wpa_s->global->p2p_group_formation != wpa_s)
 +              wpas_dbus_register_network(wpa_s, ssid);
 +}
 +
 +
 +void wpas_notify_persistent_group_added(struct wpa_supplicant *wpa_s,
 +                                      struct wpa_ssid *ssid)
 +{
 +#ifdef CONFIG_P2P
 +      wpas_dbus_register_persistent_group(wpa_s, ssid);
 +#endif /* CONFIG_P2P */
 +}
 +
 +
 +void wpas_notify_persistent_group_removed(struct wpa_supplicant *wpa_s,
 +                                        struct wpa_ssid *ssid)
 +{
 +#ifdef CONFIG_P2P
 +      wpas_dbus_unregister_persistent_group(wpa_s, ssid->id);
 +#endif /* CONFIG_P2P */
 +}
 +
 +
 +void wpas_notify_network_removed(struct wpa_supplicant *wpa_s,
 +                               struct wpa_ssid *ssid)
 +{
-       if (!ssid->p2p_group && wpa_s->global->p2p_group_formation != wpa_s)
 +      if (wpa_s->next_ssid == ssid)
 +              wpa_s->next_ssid = NULL;
 +      if (wpa_s->wpa)
 +              wpa_sm_pmksa_cache_flush(wpa_s->wpa, ssid);
-                               const u8 *src, u16 dev_passwd_id)
++      if (!ssid->p2p_group && wpa_s->global->p2p_group_formation != wpa_s &&
++          !wpa_s->p2p_mgmt)
 +              wpas_dbus_unregister_network(wpa_s, ssid->id);
 +      if (network_is_persistent_group(ssid))
 +              wpas_notify_persistent_group_removed(wpa_s, ssid);
 +
 +      wpas_p2p_network_removed(wpa_s, ssid);
 +}
 +
 +
 +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));
 +}
 +
 +
 +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));
 +}
 +
 +
 +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);
 +}
 +
 +
 +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);
 +}
 +
 +
 +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);
 +}
 +
 +
 +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);
 +}
 +
 +
 +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);
 +}
 +
 +
 +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);
 +}
 +
 +
 +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 */
 +}
 +
 +
 +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);
 +}
 +
 +
 +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);
 +}
 +
 +
 +void wpas_notify_debug_level_changed(struct wpa_global *global)
 +{
 +      wpas_dbus_signal_debug_level_changed(global);
 +}
 +
 +
 +void wpas_notify_debug_timestamp_changed(struct wpa_global *global)
 +{
 +      wpas_dbus_signal_debug_timestamp_changed(global);
 +}
 +
 +
 +void wpas_notify_debug_show_keys_changed(struct wpa_global *global)
 +{
 +      wpas_dbus_signal_debug_show_keys_changed(global);
 +}
 +
 +
 +void wpas_notify_suspend(struct wpa_global *global)
 +{
 +      struct wpa_supplicant *wpa_s;
 +
 +      os_get_time(&global->suspend_time);
 +      wpa_printf(MSG_DEBUG, "System suspend notification");
 +      for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next)
 +              wpa_drv_suspend(wpa_s);
 +}
 +
 +
 +void wpas_notify_resume(struct wpa_global *global)
 +{
 +      struct os_time now;
 +      int slept;
 +      struct wpa_supplicant *wpa_s;
 +
 +      if (global->suspend_time.sec == 0)
 +              slept = -1;
 +      else {
 +              os_get_time(&now);
 +              slept = now.sec - global->suspend_time.sec;
 +      }
 +      wpa_printf(MSG_DEBUG, "System resume notification (slept %d seconds)",
 +                 slept);
 +
 +      for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
 +              wpa_drv_resume(wpa_s);
 +              if (wpa_s->wpa_state == WPA_DISCONNECTED)
 +                      wpa_supplicant_req_scan(wpa_s, 0, 100000);
 +      }
 +}
 +
 +
 +#ifdef CONFIG_P2P
 +
++void wpas_notify_p2p_find_stopped(struct wpa_supplicant *wpa_s)
++{
++      /* Notify P2P find has stopped */
++      wpas_dbus_signal_p2p_find_stopped(wpa_s);
++}
++
++
 +void wpas_notify_p2p_device_found(struct wpa_supplicant *wpa_s,
 +                                const u8 *dev_addr, int new_device)
 +{
 +      if (new_device) {
 +              /* Create the new peer object */
 +              wpas_dbus_register_peer(wpa_s, dev_addr);
 +      }
 +
 +      /* Notify a new peer has been detected*/
 +      wpas_dbus_signal_peer_device_found(wpa_s, dev_addr);
 +}
 +
 +
 +void wpas_notify_p2p_device_lost(struct wpa_supplicant *wpa_s,
 +                               const u8 *dev_addr)
 +{
 +      wpas_dbus_unregister_peer(wpa_s, dev_addr);
 +
 +      /* Create signal on interface object*/
 +      wpas_dbus_signal_peer_device_lost(wpa_s, dev_addr);
 +}
 +
 +
 +void wpas_notify_p2p_group_removed(struct wpa_supplicant *wpa_s,
 +                                 const struct wpa_ssid *ssid,
 +                                 const char *role)
 +{
 +      wpas_dbus_signal_p2p_group_removed(wpa_s, role);
 +
 +      wpas_dbus_unregister_p2p_group(wpa_s, ssid);
 +}
 +
 +
 +void wpas_notify_p2p_go_neg_req(struct wpa_supplicant *wpa_s,
-       wpas_dbus_signal_p2p_go_neg_req(wpa_s, src, dev_passwd_id);
++                              const u8 *src, u16 dev_passwd_id, u8 go_intent)
 +{
++      wpas_dbus_signal_p2p_go_neg_req(wpa_s, src, dev_passwd_id, go_intent);
 +}
 +
 +
 +void wpas_notify_p2p_go_neg_completed(struct wpa_supplicant *wpa_s,
 +                                    struct p2p_go_neg_results *res)
 +{
 +      wpas_dbus_signal_p2p_go_neg_resp(wpa_s, res);
 +}
 +
 +
 +void wpas_notify_p2p_invitation_result(struct wpa_supplicant *wpa_s,
 +                                     int status, const u8 *bssid)
 +{
 +      wpas_dbus_signal_p2p_invitation_result(wpa_s, status, bssid);
 +}
 +
 +
 +void wpas_notify_p2p_sd_request(struct wpa_supplicant *wpa_s,
 +                              int freq, const u8 *sa, u8 dialog_token,
 +                              u16 update_indic, const u8 *tlvs,
 +                              size_t tlvs_len)
 +{
 +      wpas_dbus_signal_p2p_sd_request(wpa_s, freq, sa, dialog_token,
 +                                      update_indic, tlvs, tlvs_len);
 +}
 +
 +
 +void wpas_notify_p2p_sd_response(struct wpa_supplicant *wpa_s,
 +                               const u8 *sa, u16 update_indic,
 +                               const u8 *tlvs, size_t tlvs_len)
 +{
 +      wpas_dbus_signal_p2p_sd_response(wpa_s, sa, update_indic,
 +                                       tlvs, tlvs_len);
 +}
 +
 +
 +/**
 + * wpas_notify_p2p_provision_discovery - Notification of provision discovery
 + * @dev_addr: Who sent the request or responded to our request.
 + * @request: Will be 1 if request, 0 for response.
 + * @status: Valid only in case of response (0 in case of success)
 + * @config_methods: WPS config methods
 + * @generated_pin: PIN to be displayed in case of WPS_CONFIG_DISPLAY method
 + *
 + * This can be used to notify:
 + * - Requests or responses
 + * - Various config methods
 + * - Failure condition in case of response
 + */
 +void wpas_notify_p2p_provision_discovery(struct wpa_supplicant *wpa_s,
 +                                       const u8 *dev_addr, int request,
 +                                       enum p2p_prov_disc_status status,
 +                                       u16 config_methods,
 +                                       unsigned int generated_pin)
 +{
 +      wpas_dbus_signal_p2p_provision_discovery(wpa_s, dev_addr, request,
 +                                               status, config_methods,
 +                                               generated_pin);
 +}
 +
 +
 +void wpas_notify_p2p_group_started(struct wpa_supplicant *wpa_s,
 +                                 struct wpa_ssid *ssid, int network_id,
 +                                 int client)
 +{
 +      /* Notify a group has been started */
 +      wpas_dbus_register_p2p_group(wpa_s, ssid);
 +
 +      wpas_dbus_signal_p2p_group_started(wpa_s, ssid, client, network_id);
 +}
 +
 +
++void wpas_notify_p2p_group_formation_failure(struct wpa_supplicant *wpa_s,
++                                           const char *reason)
++{
++      /* Notify a group formation failed */
++      wpas_dbus_signal_p2p_group_formation_failure(wpa_s, reason);
++}
++
++
 +void wpas_notify_p2p_wps_failed(struct wpa_supplicant *wpa_s,
 +                              struct wps_event_fail *fail)
 +{
 +      wpas_dbus_signal_p2p_wps_failed(wpa_s, fail);
 +}
 +
++
++void wpas_notify_p2p_invitation_received(struct wpa_supplicant *wpa_s,
++                                       const u8 *sa, const u8 *go_dev_addr,
++                                       const u8 *bssid, int id, int op_freq)
++{
++      /* Notify a P2P Invitation Request */
++      wpas_dbus_signal_p2p_invitation_received(wpa_s, sa, go_dev_addr, bssid,
++                                               id, op_freq);
++}
++
 +#endif /* CONFIG_P2P */
 +
 +
 +static void wpas_notify_ap_sta_authorized(struct wpa_supplicant *wpa_s,
 +                                        const u8 *sta,
 +                                        const u8 *p2p_dev_addr)
 +{
 +#ifdef CONFIG_P2P
 +      wpas_p2p_notify_ap_sta_authorized(wpa_s, p2p_dev_addr);
 +
 +      /*
 +       * Create 'peer-joined' signal on group object -- will also
 +       * check P2P itself.
 +       */
 +      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 */
 +      wpas_dbus_signal_sta_authorized(wpa_s, sta);
 +}
 +
 +
 +static void wpas_notify_ap_sta_deauthorized(struct wpa_supplicant *wpa_s,
 +                                          const u8 *sta,
 +                                          const u8 *p2p_dev_addr)
 +{
 +#ifdef CONFIG_P2P
 +      /*
 +       * Create 'peer-disconnected' signal on group object if this
 +       * is a P2P group.
 +       */
 +      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 */
 +      wpas_dbus_signal_sta_deauthorized(wpa_s, sta);
 +}
 +
 +
 +void wpas_notify_sta_authorized(struct wpa_supplicant *wpa_s,
 +                              const u8 *mac_addr, int authorized,
 +                              const u8 *p2p_dev_addr)
 +{
 +      if (authorized)
 +              wpas_notify_ap_sta_authorized(wpa_s, mac_addr, p2p_dev_addr);
 +      else
 +              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 *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=" : "",
 +              cert_hash ? cert_hash : "");
 +
 +      if (cert) {
 +              char *cert_hex;
 +              size_t len = wpabuf_len(cert) * 2 + 1;
 +              cert_hex = os_malloc(len);
 +              if (cert_hex) {
 +                      wpa_snprintf_hex(cert_hex, len, wpabuf_head(cert),
 +                                       wpabuf_len(cert));
 +                      wpa_msg_ctrl(wpa_s, MSG_INFO,
 +                                   WPA_EVENT_EAP_PEER_CERT
 +                                   "depth=%d subject='%s' cert=%s",
 +                                   depth, subject, cert_hex);
 +                      os_free(cert_hex);
 +              }
 +      }
 +
 +      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, altsubject,
 +                                     num_altsubject, cert_hash, 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)
 +{
 +#ifdef CONFIG_AP
 +      wpas_dbus_signal_preq(wpa_s, addr, dst, bssid, ie, ie_len, ssi_signal);
 +#endif /* CONFIG_AP */
 +}
 +
 +
 +void wpas_notify_eap_status(struct wpa_supplicant *wpa_s, const char *status,
 +                          const char *parameter)
 +{
 +      wpas_dbus_signal_eap_status(wpa_s, status, parameter);
 +      wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_EAP_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;
++              ssid->p2p_persistent_group = 1;
 +              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);
++              ssid->p2p_persistent_group = 0;
 +              wpas_dbus_register_network(wpa_s, ssid);
 +      }
 +#endif /* CONFIG_P2P */
 +}
index b268332ffc3361e3d64ff7a22c47f2ddacb1fec6,0000000000000000000000000000000000000000..d9f0f5a967323ea59d736d5392125337149cb42b
mode 100644,000000..100644
--- /dev/null
@@@ -1,137 -1,0 +1,144 @@@
-                               const u8 *src, u16 dev_passwd_id);
 +/*
 + * wpa_supplicant - Event notifications
 + * Copyright (c) 2009-2010, Jouni Malinen <j@w1.fi>
 + *
 + * This software may be distributed under the terms of the BSD license.
 + * See README for more details.
 + */
 +
 +#ifndef NOTIFY_H
 +#define NOTIFY_H
 +
 +#include "p2p/p2p.h"
 +
 +struct wps_credential;
 +struct wps_event_m2d;
 +struct wps_event_fail;
 +
 +int wpas_notify_supplicant_initialized(struct wpa_global *global);
 +void wpas_notify_supplicant_deinitialized(struct wpa_global *global);
 +int wpas_notify_iface_added(struct wpa_supplicant *wpa_s);
 +void wpas_notify_iface_removed(struct wpa_supplicant *wpa_s);
 +void wpas_notify_state_changed(struct wpa_supplicant *wpa_s,
 +                             enum wpa_states new_state,
 +                             enum wpa_states old_state);
 +void wpas_notify_disconnect_reason(struct wpa_supplicant *wpa_s);
 +void wpas_notify_network_changed(struct wpa_supplicant *wpa_s);
 +void wpas_notify_ap_scan_changed(struct wpa_supplicant *wpa_s);
 +void wpas_notify_bssid_changed(struct wpa_supplicant *wpa_s);
 +void wpas_notify_auth_changed(struct wpa_supplicant *wpa_s);
 +void wpas_notify_network_enabled_changed(struct wpa_supplicant *wpa_s,
 +                                       struct wpa_ssid *ssid);
 +void wpas_notify_network_selected(struct wpa_supplicant *wpa_s,
 +                                struct wpa_ssid *ssid);
 +void wpas_notify_network_request(struct wpa_supplicant *wpa_s,
 +                               struct wpa_ssid *ssid,
 +                               enum wpa_ctrl_req_type rtype,
 +                               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_scan_results(struct wpa_supplicant *wpa_s);
 +void wpas_notify_wps_credential(struct wpa_supplicant *wpa_s,
 +                              const struct wps_credential *cred);
 +void wpas_notify_wps_event_m2d(struct wpa_supplicant *wpa_s,
 +                             struct wps_event_m2d *m2d);
 +void wpas_notify_wps_event_fail(struct wpa_supplicant *wpa_s,
 +                              struct wps_event_fail *fail);
 +void wpas_notify_wps_event_success(struct wpa_supplicant *wpa_s);
++void wpas_notify_wps_event_pbc_overlap(struct wpa_supplicant *wpa_s);
 +void wpas_notify_network_added(struct wpa_supplicant *wpa_s,
 +                             struct wpa_ssid *ssid);
 +void wpas_notify_network_removed(struct wpa_supplicant *wpa_s,
 +                               struct wpa_ssid *ssid);
 +void wpas_notify_bss_added(struct wpa_supplicant *wpa_s, u8 bssid[],
 +                         unsigned int id);
 +void wpas_notify_bss_removed(struct wpa_supplicant *wpa_s, u8 bssid[],
 +                           unsigned int id);
 +void wpas_notify_bss_freq_changed(struct wpa_supplicant *wpa_s,
 +                                unsigned int id);
 +void wpas_notify_bss_signal_changed(struct wpa_supplicant *wpa_s,
 +                                  unsigned int id);
 +void wpas_notify_bss_privacy_changed(struct wpa_supplicant *wpa_s,
 +                                   unsigned int id);
 +void wpas_notify_bss_mode_changed(struct wpa_supplicant *wpa_s,
 +                                unsigned int id);
 +void wpas_notify_bss_wpaie_changed(struct wpa_supplicant *wpa_s,
 +                                 unsigned int id);
 +void wpas_notify_bss_rsnie_changed(struct wpa_supplicant *wpa_s,
 +                                 unsigned int id);
 +void wpas_notify_bss_wps_changed(struct wpa_supplicant *wpa_s,
 +                               unsigned int id);
 +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);
 +
 +void wpas_notify_debug_level_changed(struct wpa_global *global);
 +void wpas_notify_debug_timestamp_changed(struct wpa_global *global);
 +void wpas_notify_debug_show_keys_changed(struct wpa_global *global);
 +void wpas_notify_suspend(struct wpa_global *global);
 +void wpas_notify_resume(struct wpa_global *global);
 +
 +void wpas_notify_sta_authorized(struct wpa_supplicant *wpa_s,
 +                              const u8 *mac_addr, int authorized,
 +                              const u8 *p2p_dev_addr);
++void wpas_notify_p2p_find_stopped(struct wpa_supplicant *wpa_s);
 +void wpas_notify_p2p_device_found(struct wpa_supplicant *wpa_s,
 +                                const u8 *dev_addr, int new_device);
 +void wpas_notify_p2p_device_lost(struct wpa_supplicant *wpa_s,
 +                               const u8 *dev_addr);
 +void wpas_notify_p2p_group_removed(struct wpa_supplicant *wpa_s,
 +                                 const struct wpa_ssid *ssid,
 +                                 const char *role);
 +void wpas_notify_p2p_go_neg_req(struct wpa_supplicant *wpa_s,
++                              const u8 *src, u16 dev_passwd_id, u8 go_intent);
 +void wpas_notify_p2p_go_neg_completed(struct wpa_supplicant *wpa_s,
 +                                    struct p2p_go_neg_results *res);
 +void wpas_notify_p2p_invitation_result(struct wpa_supplicant *wpa_s,
 +                                     int status, const u8 *bssid);
 +void wpas_notify_p2p_sd_request(struct wpa_supplicant *wpa_s,
 +                              int freq, const u8 *sa, u8 dialog_token,
 +                              u16 update_indic, const u8 *tlvs,
 +                              size_t tlvs_len);
 +void wpas_notify_p2p_sd_response(struct wpa_supplicant *wpa_s,
 +                               const u8 *sa, u16 update_indic,
 +                               const u8 *tlvs, size_t tlvs_len);
 +void wpas_notify_p2p_provision_discovery(struct wpa_supplicant *wpa_s,
 +                                       const u8 *dev_addr, int request,
 +                                       enum p2p_prov_disc_status status,
 +                                       u16 config_methods,
 +                                       unsigned int generated_pin);
 +void wpas_notify_p2p_group_started(struct wpa_supplicant *wpa_s,
 +                                 struct wpa_ssid *ssid, int network_id,
 +                                 int client);
++void wpas_notify_p2p_group_formation_failure(struct wpa_supplicant *wpa_s,
++                                           const char *reason);
 +void wpas_notify_persistent_group_added(struct wpa_supplicant *wpa_s,
 +                                      struct wpa_ssid *ssid);
 +void wpas_notify_persistent_group_removed(struct wpa_supplicant *wpa_s,
 +                                        struct wpa_ssid *ssid);
 +
 +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 *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_invitation_received(struct wpa_supplicant *wpa_s,
++                                       const u8 *sa, const u8 *go_dev_addr,
++                                       const u8 *bssid, int id, int op_freq);
 +
 +#endif /* NOTIFY_H */
index b200ca01026274d166aaa149403a6cf448b2a575,0000000000000000000000000000000000000000..78bdd0837e8be4302135c2bc695f4cadea0089c0
mode 100644,000000..100644
--- /dev/null
@@@ -1,9298 -1,0 +1,8728 @@@
-       P2P_GROUP_REMOVAL_FREQ_CONFLICT
 +/*
 + * 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 "includes.h"
 +
 +#include "common.h"
 +#include "eloop.h"
 +#include "common/ieee802_11_common.h"
 +#include "common/ieee802_11_defs.h"
 +#include "common/wpa_ctrl.h"
 +#include "wps/wps_i.h"
 +#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"
 +#include "driver_i.h"
 +#include "ap.h"
 +#include "config_ssid.h"
 +#include "config.h"
 +#include "notify.h"
 +#include "scan.h"
 +#include "bss.h"
 +#include "offchannel.h"
 +#include "wps_supplicant.h"
 +#include "p2p_supplicant.h"
 +#include "wifi_display.h"
 +
 +
 +/*
 + * How many times to try to scan to find the GO before giving up on join
 + * request.
 + */
 +#define P2P_MAX_JOIN_SCAN_ATTEMPTS 10
 +
 +#define P2P_AUTO_PD_SCAN_ATTEMPTS 5
 +
++/**
++ * Defines time interval in seconds when a GO needs to evacuate a frequency that
++ * it is currently using, but is no longer valid for P2P use cases.
++ */
++#define P2P_GO_FREQ_CHANGE_TIME 5
++
 +#ifndef P2P_MAX_CLIENT_IDLE
 +/*
 + * How many seconds to try to reconnect to the GO when connection in P2P client
 + * role has been lost.
 + */
 +#define P2P_MAX_CLIENT_IDLE 10
 +#endif /* P2P_MAX_CLIENT_IDLE */
 +
 +#ifndef P2P_MAX_INITIAL_CONN_WAIT
 +/*
 + * How many seconds to wait for initial 4-way handshake to get completed after
 + * 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_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-"
 +
++/*
++ * How many seconds to wait to re-attempt to move GOs, in case previous attempt
++ * was not possible.
++ */
++#define P2P_RECONSIDER_GO_MOVE_DELAY 30
++
 +enum p2p_group_removal_reason {
 +      P2P_GROUP_REMOVAL_UNKNOWN,
 +      P2P_GROUP_REMOVAL_SILENT,
 +      P2P_GROUP_REMOVAL_FORMATION_FAILED,
 +      P2P_GROUP_REMOVAL_REQUESTED,
 +      P2P_GROUP_REMOVAL_IDLE_TIMEOUT,
 +      P2P_GROUP_REMOVAL_UNAVAILABLE,
 +      P2P_GROUP_REMOVAL_GO_ENDING_SESSION,
 +      P2P_GROUP_REMOVAL_PSK_FAILURE,
-       if (wpa_s->parent->conf->p2p_ignore_shared_freq &&
++      P2P_GROUP_REMOVAL_FREQ_CONFLICT,
++      P2P_GROUP_REMOVAL_GO_LEAVE_CHANNEL
 +};
 +
 +
 +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, 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 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_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);
++static void wpas_p2p_group_formation_failed(struct wpa_supplicant *wpa_s,
++                                          int already_deleted);
++static void wpas_p2p_optimize_listen_channel(struct wpa_supplicant *wpa_s,
++                                           struct wpa_used_freq_data *freqs,
++                                           unsigned int num);
++static void wpas_p2p_move_go(void *eloop_ctx, void *timeout_ctx);
++static int wpas_p2p_go_is_peer_freq(struct wpa_supplicant *wpa_s, int freq);
++static void
++wpas_p2p_consider_moving_gos(struct wpa_supplicant *wpa_s,
++                           struct wpa_used_freq_data *freqs, unsigned int num,
++                           enum wpas_p2p_channel_update_trig trig);
++static void wpas_p2p_reconsider_moving_go(void *eloop_ctx, void *timeout_ctx);
 +
 +
 +/*
 + * 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,
 +                                           int freq)
 +{
 +      if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
 +              return;
-       p2p_no_group_iface = wpa_s->conf->p2p_no_group_iface;
++
++      /* Use the wpa_s used to control the P2P Device operation */
++      wpa_s = wpa_s->global->p2p_init_wpa_s;
++
++      if (wpa_s->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);
 +}
 +
 +
 +static void wpas_p2p_scan_res_handler(struct wpa_supplicant *wpa_s,
 +                                    struct wpa_scan_results *scan_res)
 +{
 +      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;
 +
 +      wpa_printf(MSG_DEBUG, "P2P: Scan results received (%d BSS)",
 +                 (int) scan_res->num);
 +
 +      for (i = 0; i < scan_res->num; i++) {
 +              struct wpa_scan_res *bss = scan_res->res[i];
 +              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_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,
 +                                       ies, ies_len) > 0)
 +                      break;
 +      }
 +
 +      p2p_scan_res_handled(wpa_s->global->p2p);
 +}
 +
 +
 +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_driver_scan_params *params = NULL;
 +      struct wpabuf *wps_ie, *ies;
 +      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;
 +
 +      if (wpa_s->p2p_scan_work) {
 +              wpa_dbg(wpa_s, MSG_INFO, "P2P: Reject scan trigger since one is already pending");
 +              return -1;
 +      }
 +
 +      params = os_zalloc(sizeof(*params));
 +      if (params == NULL)
 +              return -1;
 +
 +      /* P2P Wildcard SSID */
 +      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)
 +              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);
 +              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;
 +      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 = 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:
 +              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;
 +      }
 +
 +      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;
 +
 +fail:
 +      wpa_scan_free_params(params);
 +      return -1;
 +}
 +
 +
 +static enum wpa_driver_if_type wpas_p2p_if_type(int p2p_group_interface)
 +{
 +      switch (p2p_group_interface) {
 +      case P2P_GROUP_INTERFACE_PENDING:
 +              return WPA_IF_P2P_GROUP;
 +      case P2P_GROUP_INTERFACE_GO:
 +              return WPA_IF_P2P_GO;
 +      case P2P_GROUP_INTERFACE_CLIENT:
 +              return WPA_IF_P2P_CLIENT;
 +      }
 +
 +      return WPA_IF_P2P_GROUP;
 +}
 +
 +
 +static struct wpa_supplicant * wpas_get_p2p_group(struct wpa_supplicant *wpa_s,
 +                                                const u8 *ssid,
 +                                                size_t ssid_len, int *go)
 +{
 +      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) {
 +                      if (s->disabled != 0 || !s->p2p_group ||
 +                          s->ssid_len != ssid_len ||
 +                          os_memcmp(ssid, s->ssid, ssid_len) != 0)
 +                              continue;
 +                      if (s->mode == WPAS_MODE_P2P_GO &&
 +                          s != wpa_s->current_ssid)
 +                              continue;
 +                      if (go)
 +                              *go = s->mode == WPAS_MODE_P2P_GO;
 +                      return wpa_s;
 +              }
 +      }
 +
 +      return NULL;
 +}
 +
 +
 +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);
-       for (s = wpa_s->parent->conf->ssid; s; s = s->next) {
++      p2p_no_group_iface = !wpas_p2p_create_iface(wpa_s);
 +
 +      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)
 +{
 +      struct wpa_ssid *ssid;
 +      char *gtype;
 +      const char *reason;
 +
 +      ssid = wpa_s->current_ssid;
 +      if (ssid == NULL) {
 +              /*
 +               * The current SSID was not known, but there may still be a
 +               * pending P2P group interface waiting for provisioning or a
 +               * P2P group that is trying to reconnect.
 +               */
 +              ssid = wpa_s->conf->ssid;
 +              while (ssid) {
 +                      if (ssid->p2p_group && ssid->disabled != 2)
 +                              break;
 +                      ssid = ssid->next;
 +              }
 +              if (ssid == NULL &&
 +                      wpa_s->p2p_group_interface == NOT_P2P_GROUP_INTERFACE)
 +              {
 +                      wpa_printf(MSG_ERROR, "P2P: P2P group interface "
 +                                 "not found");
 +                      return -1;
 +              }
 +      }
 +      if (wpa_s->p2p_group_interface == P2P_GROUP_INTERFACE_GO)
 +              gtype = "GO";
 +      else if (wpa_s->p2p_group_interface == P2P_GROUP_INTERFACE_CLIENT ||
 +               (ssid && ssid->mode == WPAS_MODE_INFRA)) {
 +              wpa_s->reassociate = 0;
 +              wpa_s->disconnected = 1;
 +              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,
 +                             P2P_EVENT_CROSS_CONNECT_DISABLE "%s %s",
 +                             wpa_s->ifname, wpa_s->cross_connect_uplink);
 +      }
 +      switch (removal_reason) {
 +      case P2P_GROUP_REMOVAL_REQUESTED:
 +              reason = " reason=REQUESTED";
 +              break;
 +      case P2P_GROUP_REMOVAL_FORMATION_FAILED:
 +              reason = " reason=FORMATION_FAILED";
 +              break;
 +      case P2P_GROUP_REMOVAL_IDLE_TIMEOUT:
 +              reason = " reason=IDLE";
 +              break;
 +      case P2P_GROUP_REMOVAL_UNAVAILABLE:
 +              reason = " reason=UNAVAILABLE";
 +              break;
 +      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;
 +      }
 +      if (removal_reason != P2P_GROUP_REMOVAL_SILENT) {
 +              wpa_msg_global(wpa_s->parent, MSG_INFO,
 +                             P2P_EVENT_GROUP_REMOVED "%s %s%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,
 +                               wpa_s->parent, NULL) > 0) {
 +              wpa_printf(MSG_DEBUG, "P2P: Cancelled P2P group formation "
 +                         "timeout");
 +              wpa_s->p2p_in_provisioning = 0;
++              wpas_p2p_group_formation_failed(wpa_s, 1);
 +      }
 +
 +      wpa_s->p2p_in_invitation = 0;
++      eloop_cancel_timeout(wpas_p2p_move_go, wpa_s, NULL);
++      eloop_cancel_timeout(wpas_p2p_reconsider_moving_go, wpa_s, NULL);
 +
 +      /*
 +       * 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;
 +
 +      if (wpa_s->p2p_group_interface != NOT_P2P_GROUP_INTERFACE) {
 +              struct wpa_global *global;
 +              char *ifname;
 +              enum wpa_driver_if_type type;
 +              wpa_printf(MSG_DEBUG, "P2P: Remove group interface %s",
 +                      wpa_s->ifname);
 +              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)
 +                      wpa_drv_if_remove(wpa_s, type, ifname);
 +              os_free(ifname);
 +              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 ||
 +                   (ssid->key_mgmt & WPA_KEY_MGMT_WPS))) {
 +              int id = ssid->id;
 +              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->current_ssid = NULL;
 +              }
 +              /*
 +               * Networks objects created during any P2P activities are not
 +               * exposed out as they might/will confuse certain non-P2P aware
 +               * applications since these network objects won't behave like
 +               * regular ones.
 +               *
 +               * Likewise, we don't send out network removed signals for such
 +               * network objects.
 +               */
 +              wpa_config_remove_network(wpa_s->conf, id);
 +              wpa_supplicant_clear_status(wpa_s);
 +              wpa_supplicant_cancel_sched_scan(wpa_s);
 +      } else {
 +              wpa_printf(MSG_DEBUG, "P2P: Temporary group network not "
 +                         "found");
 +      }
 +      if (wpa_s->ap_iface)
 +              wpa_supplicant_ap_deinit(wpa_s);
 +      else
 +              wpa_drv_deinit_p2p_cli(wpa_s);
 +
++      os_memset(wpa_s->go_dev_addr, 0, ETH_ALEN);
++
 +      return 0;
 +}
 +
 +
 +static int wpas_p2p_persistent_group(struct wpa_supplicant *wpa_s,
 +                                   u8 *go_dev_addr,
 +                                   const u8 *ssid, size_t ssid_len)
 +{
 +      struct wpa_bss *bss;
 +      const u8 *bssid;
 +      struct wpabuf *p2p;
 +      u8 group_capab;
 +      const u8 *addr;
 +
 +      if (wpa_s->go_params)
 +              bssid = wpa_s->go_params->peer_interface_addr;
 +      else
 +              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,
 +                                         iface_addr) == 0)
 +                      bss = wpa_bss_get(wpa_s, iface_addr, ssid, ssid_len);
 +      }
 +      if (bss == NULL) {
 +              wpa_printf(MSG_DEBUG, "P2P: Could not figure out whether "
 +                         "group is persistent - BSS " MACSTR " not found",
 +                         MAC2STR(bssid));
 +              return 0;
 +      }
 +
 +      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
 +                         " did not include P2P IE", MAC2STR(bssid));
 +              wpa_hexdump(MSG_DEBUG, "P2P: Probe Response IEs",
 +                          (u8 *) (bss + 1), bss->ie_len);
 +              wpa_hexdump(MSG_DEBUG, "P2P: Beacon IEs",
 +                          ((u8 *) bss + 1) + bss->ie_len,
 +                          bss->beacon_ie_len);
 +              return 0;
 +      }
 +
 +      group_capab = p2p_get_group_capab(p2p);
 +      addr = p2p_get_go_dev_addr(p2p);
 +      wpa_printf(MSG_DEBUG, "P2P: Checking whether group is persistent: "
 +                 "group_capab=0x%x", group_capab);
 +      if (addr) {
 +              os_memcpy(go_dev_addr, addr, ETH_ALEN);
 +              wpa_printf(MSG_DEBUG, "P2P: GO Device Address " MACSTR,
 +                         MAC2STR(addr));
 +      } else
 +              os_memset(go_dev_addr, 0, ETH_ALEN);
 +      wpabuf_free(p2p);
 +
 +      wpa_printf(MSG_DEBUG, "P2P: BSS " MACSTR " group_capab=0x%x "
 +                 "go_dev_addr=" MACSTR,
 +                 MAC2STR(bssid), group_capab, MAC2STR(go_dev_addr));
 +
 +      return group_capab & P2P_GROUP_CAPAB_PERSISTENT_GROUP;
 +}
 +
 +
 +static int wpas_p2p_store_persistent_group(struct wpa_supplicant *wpa_s,
 +                                         struct wpa_ssid *ssid,
 +                                         const u8 *go_dev_addr)
 +{
 +      struct wpa_ssid *s;
 +      int changed = 0;
 +
 +      wpa_printf(MSG_DEBUG, "P2P: Storing credentials for a persistent "
 +                 "group (GO Dev Addr " MACSTR ")", MAC2STR(go_dev_addr));
 +      for (s = wpa_s->conf->ssid; s; s = s->next) {
 +              if (s->disabled == 2 &&
 +                  os_memcmp(go_dev_addr, s->bssid, ETH_ALEN) == 0 &&
 +                  s->ssid_len == ssid->ssid_len &&
 +                  os_memcmp(ssid->ssid, s->ssid, ssid->ssid_len) == 0)
 +                      break;
 +      }
 +
 +      if (s) {
 +              wpa_printf(MSG_DEBUG, "P2P: Update existing persistent group "
 +                         "entry");
 +              if (ssid->passphrase && !s->passphrase)
 +                      changed = 1;
 +              else if (ssid->passphrase && s->passphrase &&
 +                       os_strcmp(ssid->passphrase, s->passphrase) != 0)
 +                      changed = 1;
 +      } else {
 +              wpa_printf(MSG_DEBUG, "P2P: Create a new persistent group "
 +                         "entry");
 +              changed = 1;
 +              s = wpa_config_add_network(wpa_s->conf);
 +              if (s == NULL)
 +                      return -1;
 +
 +              /*
 +               * Instead of network_added we emit persistent_group_added
 +               * notification. Also to keep the defense checks in
 +               * persistent_group obj registration method, we set the
 +               * relevant flags in s to designate it as a persistent group.
 +               */
 +              s->p2p_group = 1;
 +              s->p2p_persistent_group = 1;
 +              wpas_notify_persistent_group_added(wpa_s, s);
 +              wpa_config_set_network_defaults(s);
 +      }
 +
 +      s->p2p_group = 1;
 +      s->p2p_persistent_group = 1;
 +      s->disabled = 2;
 +      s->bssid_set = 1;
 +      os_memcpy(s->bssid, go_dev_addr, ETH_ALEN);
 +      s->mode = ssid->mode;
 +      s->auth_alg = WPA_AUTH_ALG_OPEN;
 +      s->key_mgmt = WPA_KEY_MGMT_PSK;
 +      s->proto = WPA_PROTO_RSN;
 +      s->pairwise_cipher = WPA_CIPHER_CCMP;
 +      s->export_keys = 1;
 +      if (ssid->passphrase) {
 +              os_free(s->passphrase);
 +              s->passphrase = os_strdup(ssid->passphrase);
 +      }
 +      if (ssid->psk_set) {
 +              s->psk_set = 1;
 +              os_memcpy(s->psk, ssid->psk, 32);
 +      }
 +      if (s->passphrase && !s->psk_set)
 +              wpa_config_update_psk(s);
 +      if (s->ssid == NULL || s->ssid_len < ssid->ssid_len) {
 +              os_free(s->ssid);
 +              s->ssid = os_malloc(ssid->ssid_len);
 +      }
 +      if (s->ssid) {
 +              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;
 +      }
 +
 +      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");
 +      }
 +
 +      return s->id;
 +}
 +
 +
 +static void wpas_p2p_add_persistent_group_client(struct wpa_supplicant *wpa_s,
 +                                               const u8 *addr)
 +{
 +      struct wpa_ssid *ssid, *s;
 +      u8 *n;
 +      size_t i;
 +      int found = 0;
++      struct wpa_supplicant *p2p_wpa_s = wpa_s->global->p2p_init_wpa_s;
 +
 +      ssid = wpa_s->current_ssid;
 +      if (ssid == NULL || ssid->mode != WPAS_MODE_P2P_GO ||
 +          !ssid->p2p_persistent_group)
 +              return;
 +
-       if (wpa_s->parent->conf->update_config &&
-           wpa_config_write(wpa_s->parent->confname, wpa_s->parent->conf))
++      for (s = p2p_wpa_s->conf->ssid; s; s = s->next) {
 +              if (s->disabled != 2 || s->mode != WPAS_MODE_P2P_GO)
 +                      continue;
 +
 +              if (s->ssid_len == ssid->ssid_len &&
 +                  os_memcmp(s->ssid, ssid->ssid, s->ssid_len) == 0)
 +                      break;
 +      }
 +
 +      if (s == NULL)
 +              return;
 +
 +      for (i = 0; s->p2p_client_list && i < s->num_p2p_clients; i++) {
 +              if (os_memcmp(s->p2p_client_list + i * 2 * ETH_ALEN, addr,
 +                            ETH_ALEN) != 0)
 +                      continue;
 +
 +              if (i == s->num_p2p_clients - 1)
 +                      return; /* already the most recent entry */
 +
 +              /* move the entry to mark it most recent */
 +              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) * 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, 2 * ETH_ALEN);
 +              if (n == NULL)
 +                      return;
 +              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 && 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 + 2 * ETH_ALEN,
 +                         (s->num_p2p_clients - 1) * 2 * ETH_ALEN);
 +              os_memcpy(s->p2p_client_list +
 +                        (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);
 +      }
 +
-                                          int success)
++      if (p2p_wpa_s->conf->update_config &&
++          wpa_config_write(p2p_wpa_s->confname, p2p_wpa_s->conf))
 +              wpa_printf(MSG_DEBUG, "P2P: Failed to update configuration");
 +}
 +
 +
 +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);
 +}
 +
 +
 +static void wpas_group_formation_completed(struct wpa_supplicant *wpa_s,
-               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;
++                                         int success, int already_deleted)
 +{
 +      struct wpa_ssid *ssid;
 +      int client;
 +      int persistent;
 +      u8 go_dev_addr[ETH_ALEN];
 +      int network_id = -1;
 +
 +      /*
 +       * This callback is likely called for the main interface. Update wpa_s
 +       * to use the group interface if a new interface was created for the
 +       * group.
 +       */
 +      if (wpa_s->global->p2p_group_formation)
 +              wpa_s = wpa_s->global->p2p_group_formation;
 +      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);
++              wpas_notify_p2p_group_formation_failure(wpa_s, "");
++              if (already_deleted)
++                      return;
 +              wpas_p2p_group_delete(wpa_s,
 +                                    P2P_GROUP_REMOVAL_FORMATION_FAILED);
 +              return;
 +      }
 +
 +      wpa_msg_global(wpa_s->parent, MSG_INFO,
 +                     P2P_EVENT_GROUP_FORMATION_SUCCESS);
 +
 +      ssid = wpa_s->current_ssid;
 +      if (ssid && ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION) {
 +              ssid->mode = WPAS_MODE_P2P_GO;
 +              p2p_group_notif_formation_done(wpa_s->p2p_group);
 +              wpa_supplicant_ap_mac_addr_filter(wpa_s, NULL);
 +      }
 +
 +      persistent = 0;
 +      if (ssid) {
 +              client = ssid->mode == WPAS_MODE_INFRA;
 +              if (ssid->mode == WPAS_MODE_P2P_GO) {
 +                      persistent = ssid->p2p_persistent_group;
 +                      os_memcpy(go_dev_addr, wpa_s->global->p2p_dev_addr,
 +                                ETH_ALEN);
 +              } else
 +                      persistent = wpas_p2p_persistent_group(wpa_s,
 +                                                             go_dev_addr,
 +                                                             ssid->ssid,
 +                                                             ssid->ssid_len);
 +      } else {
 +              client = wpa_s->p2p_group_interface ==
 +                      P2P_GROUP_INTERFACE_CLIENT;
 +              os_memset(go_dev_addr, 0, ETH_ALEN);
 +      }
 +
 +      wpa_s->show_group_started = 0;
 +      if (client) {
 +              /*
 +               * Indicate event only after successfully completed 4-way
 +               * handshake, i.e., when the interface is ready for data
 +               * packets.
 +               */
 +              wpa_s->show_group_started = 1;
 +      } else {
 +              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);
 +      }
 +
 +      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) {
 +              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);
 +              }
 +      }
 +}
 +
 +
 +static void wpas_p2p_send_action_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)
 +{
 +      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;
 +
 +      switch (result) {
 +      case OFFCHANNEL_SEND_ACTION_SUCCESS:
 +              res = P2P_SEND_ACTION_SUCCESS;
 +              break;
 +      case OFFCHANNEL_SEND_ACTION_NO_ACK:
 +              res = P2P_SEND_ACTION_NO_ACK;
 +              break;
 +      case OFFCHANNEL_SEND_ACTION_FAILED:
 +              res = P2P_SEND_ACTION_FAILED;
 +              break;
 +      }
 +
 +      p2p_send_action_cb(wpa_s->global->p2p, freq, dst, src, bssid, res);
 +
 +      if (result != OFFCHANNEL_SEND_ACTION_SUCCESS &&
 +          wpa_s->pending_pd_before_join &&
 +          (os_memcmp(dst, wpa_s->pending_join_dev_addr, ETH_ALEN) == 0 ||
 +           os_memcmp(dst, wpa_s->pending_join_iface_addr, ETH_ALEN) == 0) &&
 +          wpa_s->p2p_fallback_to_go_neg) {
 +              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);
 +}
 +
 +
 +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);
 +}
 +
 +
 +static int wpas_copy_go_neg_results(struct wpa_supplicant *wpa_s,
 +                                  struct p2p_go_neg_results *params)
 +{
 +      if (wpa_s->go_params == NULL) {
 +              wpa_s->go_params = os_malloc(sizeof(*params));
 +              if (wpa_s->go_params == NULL)
 +                      return -1;
 +      }
 +      os_memcpy(wpa_s->go_params, params, sizeof(*params));
 +      return 0;
 +}
 +
 +
 +static void wpas_start_wps_enrollee(struct wpa_supplicant *wpa_s,
 +                                  struct p2p_go_neg_results *res)
 +{
 +      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) {
 +              wpas_wps_start_pbc(wpa_s, res->peer_interface_addr, 1);
 +#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,
 +                                 wpa_s->p2p_pin, 1, dev_pw_id);
 +      }
 +}
 +
 +
 +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;
 +      struct p2p_go_neg_results *params = 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;
 +              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;
 +
-       wpas_p2p_group_formation_failed(wpa_s);
++              if (wpa_s->parent->p2ps_method_config_any) {
++                      if (is_zero_ether_addr(wpa_s->parent->p2ps_join_addr)) {
++                              wpa_dbg(wpa_s, MSG_DEBUG,
++                                      "P2PS: Setting default PIN for ANY");
++                              wpa_supplicant_ap_wps_pin(wpa_s, NULL,
++                                                        "12345670", NULL, 0,
++                                                        0);
++                      } else {
++                              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_method_config_any = 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;
 +      }
 +
 +      wpa_printf(MSG_DEBUG, "P2P: Setting up WPS for GO provisioning");
 +      if (wpa_supplicant_ap_mac_addr_filter(wpa_s,
 +                                            params->peer_interface_addr)) {
 +              wpa_printf(MSG_DEBUG, "P2P: Failed to setup MAC address "
 +                         "filtering");
 +              return;
 +      }
 +      if (params->wps_method == WPS_PBC) {
 +              wpa_supplicant_ap_wps_pbc(wpa_s, params->peer_interface_addr,
 +                                        params->peer_device_addr);
 +#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);
 +      wpa_s->go_params = NULL;
 +}
 +
 +
 +static void wpas_start_wps_go(struct wpa_supplicant *wpa_s,
 +                            struct p2p_go_neg_results *params,
 +                            int group_formation)
 +{
 +      struct wpa_ssid *ssid;
 +
 +      wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Starting GO");
 +      if (wpas_copy_go_neg_results(wpa_s, params) < 0) {
 +              wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Could not copy GO Negotiation "
 +                      "results");
 +              return;
 +      }
 +
 +      ssid = wpa_config_add_network(wpa_s->conf);
 +      if (ssid == NULL) {
 +              wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Could not add network for GO");
 +              return;
 +      }
 +
 +      wpa_s->show_group_started = 0;
 +      wpa_s->p2p_go_group_formation_completed = 0;
 +      wpa_s->group_formation_reported = 0;
++      os_memset(wpa_s->go_dev_addr, 0, ETH_ALEN);
 +
 +      wpa_config_set_network_defaults(ssid);
 +      ssid->temporary = 1;
 +      ssid->p2p_group = 1;
 +      ssid->p2p_persistent_group = params->persistent_group;
 +      ssid->mode = group_formation ? WPAS_MODE_P2P_GROUP_FORMATION :
 +              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);
 +              ssid->ssid_len = params->ssid_len;
 +      }
 +      ssid->auth_alg = WPA_AUTH_ALG_OPEN;
 +      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) {
 +                      wpa_msg_global(wpa_s, MSG_ERROR,
 +                                     "P2P: Failed to copy passphrase for GO");
 +                      wpa_config_remove_network(wpa_s->conf, ssid->id);
 +                      return;
 +              }
 +      } else
 +              ssid->passphrase = NULL;
 +      ssid->psk_set = params->psk_set;
 +      if (ssid->psk_set)
 +              os_memcpy(ssid->psk, params->psk, sizeof(ssid->psk));
 +      else if (ssid->passphrase)
 +              wpa_config_update_psk(ssid);
 +      ssid->ap_max_inactivity = wpa_s->parent->conf->p2p_go_max_inactivity;
 +
 +      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;
 +      wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Request scan (that will be skipped) to "
 +              "start GO)");
 +      wpa_supplicant_req_scan(wpa_s, 0, 0);
 +}
 +
 +
 +static void wpas_p2p_clone_config(struct wpa_supplicant *dst,
 +                                const struct wpa_supplicant *src)
 +{
 +      struct wpa_config *d;
 +      const struct wpa_config *s;
 +
 +      d = dst->conf;
 +      s = src->conf;
 +
 +#define C(n) if (s->n) d->n = os_strdup(s->n)
 +      C(device_name);
 +      C(manufacturer);
 +      C(model_name);
 +      C(model_number);
 +      C(serial_number);
 +      C(config_methods);
 +#undef C
 +
 +      os_memcpy(d->device_type, s->device_type, WPS_DEV_TYPE_LEN);
 +      os_memcpy(d->sec_device_type, s->sec_device_type,
 +                sizeof(d->sec_device_type));
 +      d->num_sec_device_types = s->num_sec_device_types;
 +
 +      d->p2p_group_idle = s->p2p_group_idle;
++      d->p2p_go_freq_change_policy = s->p2p_go_freq_change_policy;
 +      d->p2p_intra_bss = s->p2p_intra_bss;
 +      d->persistent_reconnect = s->persistent_reconnect;
 +      d->max_num_sta = s->max_num_sta;
 +      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);
 +      }
++      d->p2p_cli_probe = s->p2p_cli_probe;
 +}
 +
 +
 +static void wpas_p2p_get_group_ifname(struct wpa_supplicant *wpa_s,
 +                                    char *ifname, size_t len)
 +{
 +      char *ifname_ptr = wpa_s->ifname;
 +
 +      if (os_strncmp(wpa_s->ifname, P2P_MGMT_DEVICE_PREFIX,
 +                     os_strlen(P2P_MGMT_DEVICE_PREFIX)) == 0) {
 +              ifname_ptr = os_strrchr(wpa_s->ifname, '-') + 1;
 +      }
 +
 +      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 */
 +              res = os_snprintf(ifname, len, "p2p-%d", wpa_s->p2p_group_idx);
 +              if (os_snprintf_error(len, res) && len)
 +                      ifname[len - 1] = '\0';
 +      }
 +}
 +
 +
 +static int wpas_p2p_add_group_interface(struct wpa_supplicant *wpa_s,
 +                                      enum wpa_driver_if_type type)
 +{
 +      char ifname[120], force_ifname[120];
 +
 +      if (wpa_s->pending_interface_name[0]) {
 +              wpa_printf(MSG_DEBUG, "P2P: Pending virtual interface exists "
 +                         "- skip creation of a new one");
 +              if (is_zero_ether_addr(wpa_s->pending_interface_addr)) {
 +                      wpa_printf(MSG_DEBUG, "P2P: Pending virtual address "
 +                                 "unknown?! ifname='%s'",
 +                                 wpa_s->pending_interface_name);
 +                      return -1;
 +              }
 +              return 0;
 +      }
 +
 +      wpas_p2p_get_group_ifname(wpa_s, ifname, sizeof(ifname));
 +      force_ifname[0] = '\0';
 +
 +      wpa_printf(MSG_DEBUG, "P2P: Create a new interface %s for the group",
 +                 ifname);
 +      wpa_s->p2p_group_idx++;
 +
 +      wpa_s->pending_interface_type = type;
 +      if (wpa_drv_if_add(wpa_s, type, ifname, NULL, NULL, force_ifname,
 +                         wpa_s->pending_interface_addr, NULL) < 0) {
 +              wpa_printf(MSG_ERROR, "P2P: Failed to create new group "
 +                         "interface");
 +              return -1;
 +      }
 +
 +      if (force_ifname[0]) {
 +              wpa_printf(MSG_DEBUG, "P2P: Driver forced interface name %s",
 +                         force_ifname);
 +              os_strlcpy(wpa_s->pending_interface_name, force_ifname,
 +                         sizeof(wpa_s->pending_interface_name));
 +      } else
 +              os_strlcpy(wpa_s->pending_interface_name, ifname,
 +                         sizeof(wpa_s->pending_interface_name));
 +      wpa_printf(MSG_DEBUG, "P2P: Created pending virtual interface %s addr "
 +                 MACSTR, wpa_s->pending_interface_name,
 +                 MAC2STR(wpa_s->pending_interface_addr));
 +
 +      return 0;
 +}
 +
 +
 +static void wpas_p2p_remove_pending_group_interface(
 +      struct wpa_supplicant *wpa_s)
 +{
 +      if (!wpa_s->pending_interface_name[0] ||
 +          is_zero_ether_addr(wpa_s->pending_interface_addr))
 +              return; /* No pending virtual interface */
 +
 +      wpa_printf(MSG_DEBUG, "P2P: Removing pending group interface %s",
 +                 wpa_s->pending_interface_name);
 +      wpa_drv_if_remove(wpa_s, wpa_s->pending_interface_type,
 +                        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;
 +}
 +
 +
 +static struct wpa_supplicant *
 +wpas_p2p_init_group_interface(struct wpa_supplicant *wpa_s, int go)
 +{
 +      struct wpa_interface iface;
 +      struct wpa_supplicant *group_wpa_s;
 +
 +      if (!wpa_s->pending_interface_name[0]) {
 +              wpa_printf(MSG_ERROR, "P2P: No pending group interface");
 +              if (!wpas_p2p_create_iface(wpa_s))
 +                      return NULL;
 +              /*
 +               * Something has forced us to remove the pending interface; try
 +               * to create a new one and hope for the best that we will get
 +               * the same local address.
 +               */
 +              if (wpas_p2p_add_group_interface(wpa_s, go ? WPA_IF_P2P_GO :
 +                                               WPA_IF_P2P_CLIENT) < 0)
 +                      return NULL;
 +      }
 +
 +      os_memset(&iface, 0, sizeof(iface));
 +      iface.ifname = wpa_s->pending_interface_name;
 +      iface.driver = wpa_s->driver->name;
 +      if (wpa_s->conf->ctrl_interface == NULL &&
 +          wpa_s->parent != wpa_s &&
 +          wpa_s->p2p_mgmt &&
 +          (wpa_s->drv_flags & WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE))
 +              iface.ctrl_interface = wpa_s->parent->conf->ctrl_interface;
 +      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, 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->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);
 +
 +      return group_wpa_s;
 +}
 +
 +
 +static void wpas_p2p_group_formation_timeout(void *eloop_ctx,
 +                                           void *timeout_ctx)
 +{
 +      struct wpa_supplicant *wpa_s = eloop_ctx;
 +      wpa_printf(MSG_DEBUG, "P2P: Group Formation timed out");
- void wpas_p2p_group_formation_failed(struct wpa_supplicant *wpa_s)
++      wpas_p2p_group_formation_failed(wpa_s, 0);
 +}
 +
 +
-       wpas_group_formation_completed(wpa_s, 0);
++static void wpas_p2p_group_formation_failed(struct wpa_supplicant *wpa_s,
++                                          int already_deleted)
 +{
 +      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);
-                       wpas_p2p_group_formation_failed(wpa_s);
++      wpas_group_formation_completed(wpa_s, 0, already_deleted);
 +}
 +
 +
 +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;
 +
 +      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;
 +      }
 +
 +      if (res->status) {
 +              wpa_msg_global(wpa_s, MSG_INFO,
 +                             P2P_EVENT_GO_NEG_FAILURE "status=%d",
 +                             res->status);
 +              wpas_notify_p2p_go_neg_completed(wpa_s, res);
 +              wpas_p2p_remove_pending_group_interface(wpa_s);
 +              return;
 +      }
 +
++      if (!res->role_go) {
++              /* Inform driver of the operating channel of GO. */
++              wpa_drv_set_prob_oper_freq(wpa_s, res->freq);
++      }
++
 +      if (wpa_s->p2p_go_ht40)
 +              res->ht40 = 1;
 +      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) {
 +              struct wpa_ssid *ssid;
 +              ssid = wpa_config_get_network(wpa_s->conf,
 +                                            wpa_s->p2p_persistent_id);
 +              if (ssid && ssid->disabled == 2 &&
 +                  ssid->mode == WPAS_MODE_P2P_GO && ssid->passphrase) {
 +                      size_t len = os_strlen(ssid->passphrase);
 +                      wpa_printf(MSG_DEBUG, "P2P: Override passphrase based "
 +                                 "on requested persistent group");
 +                      os_memcpy(res->passphrase, ssid->passphrase, len);
 +                      res->passphrase[len] = '\0';
 +              }
 +      }
 +
 +      if (wpa_s->create_p2p_iface) {
 +              struct wpa_supplicant *group_wpa_s =
 +                      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);
-               if (res->role_go)
++                      wpas_p2p_group_formation_failed(wpa_s, 1);
 +                      return;
 +              }
 +              if (group_wpa_s != wpa_s) {
 +                      os_memcpy(group_wpa_s->p2p_pin, wpa_s->p2p_pin,
 +                                sizeof(group_wpa_s->p2p_pin));
 +                      group_wpa_s->p2p_wps_method = wpa_s->p2p_wps_method;
 +              }
 +              os_memset(wpa_s->pending_interface_addr, 0, ETH_ALEN);
 +              wpa_s->pending_interface_name[0] = '\0';
 +              group_wpa_s->p2p_in_provisioning = 1;
 +
-               else
++              if (res->role_go) {
 +                      wpas_start_wps_go(group_wpa_s, res, 1);
-               if (res->role_go)
++              } else {
++                      os_get_reltime(&group_wpa_s->scan_min_time);
 +                      wpas_start_wps_enrollee(group_wpa_s, res);
++              }
 +      } else {
 +              wpa_s->p2p_in_provisioning = 1;
 +              wpa_s->global->p2p_group_formation = wpa_s;
 +
-               else
++              if (res->role_go) {
 +                      wpas_start_wps_go(wpa_s, res, 1);
- static void wpas_go_neg_req_rx(void *ctx, const u8 *src, u16 dev_passwd_id)
++              } else {
++                      os_get_reltime(&wpa_s->scan_min_time);
 +                      wpas_start_wps_enrollee(ctx, res);
++              }
 +      }
 +
 +      wpa_s->p2p_long_listen = 0;
 +      eloop_cancel_timeout(wpas_p2p_long_listen_timeout, wpa_s, NULL);
 +
 +      eloop_cancel_timeout(wpas_p2p_group_formation_timeout, wpa_s, NULL);
 +      eloop_register_timeout(15 + res->peer_config_timeout / 100,
 +                             (res->peer_config_timeout % 100) * 10000,
 +                             wpas_p2p_group_formation_timeout, wpa_s, NULL);
 +}
 +
 +
-                      " dev_passwd_id=%u", MAC2STR(src), dev_passwd_id);
++static void wpas_go_neg_req_rx(void *ctx, const u8 *src, u16 dev_passwd_id,
++                             u8 go_intent)
 +{
 +      struct wpa_supplicant *wpa_s = ctx;
 +      wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_GO_NEG_REQUEST MACSTR
-       wpas_notify_p2p_go_neg_req(wpa_s, src, dev_passwd_id);
++                     " dev_passwd_id=%u go_intent=%u", MAC2STR(src),
++                     dev_passwd_id, go_intent);
 +
- static void wpas_find_stopped(void *ctx)
- {
-       struct wpa_supplicant *wpa_s = ctx;
-       wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_FIND_STOPPED);
- }
- 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)
- {
-       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_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");
-               wpas_p2p_listen_work_done(wpa_s);
-               return;
-       }
-       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, lwork->freq, duration) < 0) {
-               wpa_printf(MSG_DEBUG, "P2P: Failed to request the driver "
-                          "to remain on channel (%u MHz) for Listen "
-                          "state", lwork->freq);
-               wpas_p2p_listen_work_done(wpa_s);
-               wpa_s->pending_listen_freq = 0;
-               return;
-       }
-       wpa_s->off_channel_freq = 0;
-       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;
- }
- static void wpas_stop_listen(void *ctx)
- {
-       struct wpa_supplicant *wpa_s = ctx;
-       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;
-       }
-       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);
- }
- static int wpas_send_probe_resp(void *ctx, const struct wpabuf *buf)
- {
-       struct wpa_supplicant *wpa_s = ctx;
-       return wpa_drv_send_mlme(wpa_s, wpabuf_head(buf), wpabuf_len(buf), 1);
- }
- /*
-  * DNS Header section is used only to calculate compression pointers, so the
-  * contents of this data does not matter, but the length needs to be reserved
-  * in the virtual packet.
-  */
- #define DNS_HEADER_LEN 12
- /*
-  * 27-octet in-memory packet from P2P specification containing two implied
-  * queries for _tcp.lcoal. PTR IN and _udp.local. PTR IN
-  */
- #define P2P_SD_IN_MEMORY_LEN 27
- static int p2p_sd_dns_uncompress_label(char **upos, char *uend, u8 *start,
-                                      u8 **spos, const u8 *end)
- {
-       while (*spos < end) {
-               u8 val = ((*spos)[0] & 0xc0) >> 6;
-               int len;
-               if (val == 1 || val == 2) {
-                       /* These are reserved values in RFC 1035 */
-                       wpa_printf(MSG_DEBUG, "P2P: Invalid domain name "
-                                  "sequence starting with 0x%x", val);
-                       return -1;
-               }
-               if (val == 3) {
-                       u16 offset;
-                       u8 *spos_tmp;
-                       /* Offset */
-                       if (*spos + 2 > end) {
-                               wpa_printf(MSG_DEBUG, "P2P: No room for full "
-                                          "DNS offset field");
-                               return -1;
-                       }
-                       offset = (((*spos)[0] & 0x3f) << 8) | (*spos)[1];
-                       if (offset >= *spos - start) {
-                               wpa_printf(MSG_DEBUG, "P2P: Invalid DNS "
-                                          "pointer offset %u", offset);
-                               return -1;
-                       }
-                       (*spos) += 2;
-                       spos_tmp = start + offset;
-                       return p2p_sd_dns_uncompress_label(upos, uend, start,
-                                                          &spos_tmp,
-                                                          *spos - 2);
-               }
-               /* Label */
-               len = (*spos)[0] & 0x3f;
-               if (len == 0)
-                       return 0;
-               (*spos)++;
-               if (*spos + len > end) {
-                       wpa_printf(MSG_DEBUG, "P2P: Invalid domain name "
-                                  "sequence - no room for label with length "
-                                  "%u", len);
-                       return -1;
-               }
-               if (*upos + len + 2 > uend)
-                       return -2;
-               os_memcpy(*upos, *spos, len);
-               *spos += len;
-               *upos += len;
-               (*upos)[0] = '.';
-               (*upos)++;
-               (*upos)[0] = '\0';
-       }
-       return 0;
- }
- /* Uncompress domain names per RFC 1035 using the P2P SD in-memory packet.
-  * Returns -1 on parsing error (invalid input sequence), -2 if output buffer is
-  * not large enough */
- static int p2p_sd_dns_uncompress(char *buf, size_t buf_len, const u8 *msg,
-                                size_t msg_len, size_t offset)
- {
-       /* 27-octet in-memory packet from P2P specification */
-       const char *prefix = "\x04_tcp\x05local\x00\x00\x0C\x00\x01"
-               "\x04_udp\xC0\x11\x00\x0C\x00\x01";
-       u8 *tmp, *end, *spos;
-       char *upos, *uend;
-       int ret = 0;
-       if (buf_len < 2)
-               return -1;
-       if (offset > msg_len)
-               return -1;
-       tmp = os_malloc(DNS_HEADER_LEN + P2P_SD_IN_MEMORY_LEN + msg_len);
-       if (tmp == NULL)
-               return -1;
-       spos = tmp + DNS_HEADER_LEN + P2P_SD_IN_MEMORY_LEN;
-       end = spos + msg_len;
-       spos += offset;
-       os_memset(tmp, 0, DNS_HEADER_LEN);
-       os_memcpy(tmp + DNS_HEADER_LEN, prefix, P2P_SD_IN_MEMORY_LEN);
-       os_memcpy(tmp + DNS_HEADER_LEN + P2P_SD_IN_MEMORY_LEN, msg, msg_len);
-       upos = buf;
-       uend = buf + buf_len;
-       ret = p2p_sd_dns_uncompress_label(&upos, uend, tmp, &spos, end);
-       if (ret) {
-               os_free(tmp);
-               return ret;
-       }
-       if (upos == buf) {
-               upos[0] = '.';
-               upos[1] = '\0';
-       } else if (upos[-1] == '.')
-               upos[-1] = '\0';
-       os_free(tmp);
-       return 0;
- }
- static struct p2p_srv_bonjour *
- wpas_p2p_service_get_bonjour(struct wpa_supplicant *wpa_s,
-                            const struct wpabuf *query)
- {
-       struct p2p_srv_bonjour *bsrv;
-       size_t len;
-       len = wpabuf_len(query);
-       dl_list_for_each(bsrv, &wpa_s->global->p2p_srv_bonjour,
-                        struct p2p_srv_bonjour, list) {
-               if (len == wpabuf_len(bsrv->query) &&
-                   os_memcmp(wpabuf_head(query), wpabuf_head(bsrv->query),
-                             len) == 0)
-                       return bsrv;
-       }
-       return NULL;
- }
- static struct p2p_srv_upnp *
- wpas_p2p_service_get_upnp(struct wpa_supplicant *wpa_s, u8 version,
-                         const char *service)
- {
-       struct p2p_srv_upnp *usrv;
-       dl_list_for_each(usrv, &wpa_s->global->p2p_srv_upnp,
-                        struct p2p_srv_upnp, list) {
-               if (version == usrv->version &&
-                   os_strcmp(service, usrv->service) == 0)
-                       return usrv;
-       }
-       return NULL;
- }
- static void wpas_sd_add_empty(struct wpabuf *resp, u8 srv_proto,
-                             u8 srv_trans_id, u8 status)
- {
-       u8 *len_pos;
-       if (wpabuf_tailroom(resp) < 5)
-               return;
-       /* Length (to be filled) */
-       len_pos = wpabuf_put(resp, 2);
-       wpabuf_put_u8(resp, srv_proto);
-       wpabuf_put_u8(resp, srv_trans_id);
-       /* Status Code */
-       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)
- {
-       struct p2p_srv_bonjour *bsrv;
-       u8 *len_pos;
-       wpa_printf(MSG_DEBUG, "P2P: SD Request for all Bonjour services");
-       if (dl_list_empty(&wpa_s->global->p2p_srv_bonjour)) {
-               wpa_printf(MSG_DEBUG, "P2P: Bonjour protocol not available");
-               return;
-       }
-       dl_list_for_each(bsrv, &wpa_s->global->p2p_srv_bonjour,
-                        struct p2p_srv_bonjour, list) {
-               if (wpabuf_tailroom(resp) <
-                   5 + wpabuf_len(bsrv->query) + wpabuf_len(bsrv->resp))
-                       return;
-               /* Length (to be filled) */
-               len_pos = wpabuf_put(resp, 2);
-               wpabuf_put_u8(resp, P2P_SERV_BONJOUR);
-               wpabuf_put_u8(resp, srv_trans_id);
-               /* Status Code */
-               wpabuf_put_u8(resp, P2P_SD_SUCCESS);
-               wpa_hexdump_ascii(MSG_DEBUG, "P2P: Matching Bonjour service",
-                                 wpabuf_head(bsrv->resp),
-                                 wpabuf_len(bsrv->resp));
-               /* Response Data */
-               wpabuf_put_buf(resp, bsrv->query); /* Key */
-               wpabuf_put_buf(resp, bsrv->resp); /* Value */
-               WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos -
-                            2);
-       }
- }
- static int match_bonjour_query(struct p2p_srv_bonjour *bsrv, const u8 *query,
-                              size_t query_len)
- {
-       char str_rx[256], str_srv[256];
-       if (query_len < 3 || wpabuf_len(bsrv->query) < 3)
-               return 0; /* Too short to include DNS Type and Version */
-       if (os_memcmp(query + query_len - 3,
-                     wpabuf_head_u8(bsrv->query) + wpabuf_len(bsrv->query) - 3,
-                     3) != 0)
-               return 0; /* Mismatch in DNS Type or Version */
-       if (query_len == wpabuf_len(bsrv->query) &&
-           os_memcmp(query, wpabuf_head(bsrv->query), query_len - 3) == 0)
-               return 1; /* Binary match */
-       if (p2p_sd_dns_uncompress(str_rx, sizeof(str_rx), query, query_len - 3,
-                                 0))
-               return 0; /* Failed to uncompress query */
-       if (p2p_sd_dns_uncompress(str_srv, sizeof(str_srv),
-                                 wpabuf_head(bsrv->query),
-                                 wpabuf_len(bsrv->query) - 3, 0))
-               return 0; /* Failed to uncompress service */
-       return os_strcmp(str_rx, str_srv) == 0;
- }
- static void wpas_sd_req_bonjour(struct wpa_supplicant *wpa_s,
-                               struct wpabuf *resp, u8 srv_trans_id,
-                               const u8 *query, size_t query_len)
- {
-       struct p2p_srv_bonjour *bsrv;
-       u8 *len_pos;
-       int matches = 0;
-       wpa_hexdump_ascii(MSG_DEBUG, "P2P: SD Request for Bonjour",
-                         query, query_len);
-       if (dl_list_empty(&wpa_s->global->p2p_srv_bonjour)) {
-               wpa_printf(MSG_DEBUG, "P2P: Bonjour protocol not available");
-               wpas_sd_add_proto_not_avail(resp, P2P_SERV_BONJOUR,
-                                           srv_trans_id);
-               return;
-       }
-       if (query_len == 0) {
-               wpas_sd_all_bonjour(wpa_s, resp, srv_trans_id);
-               return;
-       }
-       dl_list_for_each(bsrv, &wpa_s->global->p2p_srv_bonjour,
-                        struct p2p_srv_bonjour, list) {
-               if (!match_bonjour_query(bsrv, query, query_len))
-                       continue;
-               if (wpabuf_tailroom(resp) <
-                   5 + query_len + wpabuf_len(bsrv->resp))
-                       return;
-               matches++;
-               /* Length (to be filled) */
-               len_pos = wpabuf_put(resp, 2);
-               wpabuf_put_u8(resp, P2P_SERV_BONJOUR);
-               wpabuf_put_u8(resp, srv_trans_id);
-               /* Status Code */
-               wpabuf_put_u8(resp, P2P_SD_SUCCESS);
-               wpa_hexdump_ascii(MSG_DEBUG, "P2P: Matching Bonjour service",
-                                 wpabuf_head(bsrv->resp),
-                                 wpabuf_len(bsrv->resp));
-               /* Response Data */
-               wpabuf_put_data(resp, query, query_len); /* Key */
-               wpabuf_put_buf(resp, bsrv->resp); /* Value */
-               WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - 2);
-       }
-       if (matches == 0) {
-               wpa_printf(MSG_DEBUG, "P2P: Requested Bonjour service not "
-                          "available");
-               if (wpabuf_tailroom(resp) < 5)
-                       return;
-               /* Length (to be filled) */
-               len_pos = wpabuf_put(resp, 2);
-               wpabuf_put_u8(resp, P2P_SERV_BONJOUR);
-               wpabuf_put_u8(resp, srv_trans_id);
-               /* Status Code */
-               wpabuf_put_u8(resp, P2P_SD_REQUESTED_INFO_NOT_AVAILABLE);
-               /* Response Data: empty */
-               WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos -
-                            2);
-       }
- }
- static void wpas_sd_all_upnp(struct wpa_supplicant *wpa_s,
-                            struct wpabuf *resp, u8 srv_trans_id)
- {
-       struct p2p_srv_upnp *usrv;
-       u8 *len_pos;
-       wpa_printf(MSG_DEBUG, "P2P: SD Request for all UPnP services");
-       if (dl_list_empty(&wpa_s->global->p2p_srv_upnp)) {
-               wpa_printf(MSG_DEBUG, "P2P: UPnP protocol not available");
-               return;
-       }
-       dl_list_for_each(usrv, &wpa_s->global->p2p_srv_upnp,
-                        struct p2p_srv_upnp, list) {
-               if (wpabuf_tailroom(resp) < 5 + 1 + os_strlen(usrv->service))
-                       return;
-               /* Length (to be filled) */
-               len_pos = wpabuf_put(resp, 2);
-               wpabuf_put_u8(resp, P2P_SERV_UPNP);
-               wpabuf_put_u8(resp, srv_trans_id);
-               /* Status Code */
-               wpabuf_put_u8(resp, P2P_SD_SUCCESS);
-               /* Response Data */
-               wpabuf_put_u8(resp, usrv->version);
-               wpa_printf(MSG_DEBUG, "P2P: Matching UPnP Service: %s",
-                          usrv->service);
-               wpabuf_put_str(resp, usrv->service);
-               WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos -
-                            2);
-       }
- }
- static void wpas_sd_req_upnp(struct wpa_supplicant *wpa_s,
-                            struct wpabuf *resp, u8 srv_trans_id,
-                            const u8 *query, size_t query_len)
- {
-       struct p2p_srv_upnp *usrv;
-       u8 *len_pos;
-       u8 version;
-       char *str;
-       int count = 0;
-       wpa_hexdump_ascii(MSG_DEBUG, "P2P: SD Request for UPnP",
-                         query, query_len);
-       if (dl_list_empty(&wpa_s->global->p2p_srv_upnp)) {
-               wpa_printf(MSG_DEBUG, "P2P: UPnP protocol not available");
-               wpas_sd_add_proto_not_avail(resp, P2P_SERV_UPNP,
-                                           srv_trans_id);
-               return;
-       }
-       if (query_len == 0) {
-               wpas_sd_all_upnp(wpa_s, resp, srv_trans_id);
-               return;
-       }
-       if (wpabuf_tailroom(resp) < 5)
-               return;
-       /* Length (to be filled) */
-       len_pos = wpabuf_put(resp, 2);
-       wpabuf_put_u8(resp, P2P_SERV_UPNP);
-       wpabuf_put_u8(resp, srv_trans_id);
-       version = query[0];
-       str = os_malloc(query_len);
-       if (str == NULL)
-               return;
-       os_memcpy(str, query + 1, query_len - 1);
-       str[query_len - 1] = '\0';
-       dl_list_for_each(usrv, &wpa_s->global->p2p_srv_upnp,
-                        struct p2p_srv_upnp, list) {
-               if (version != usrv->version)
-                       continue;
-               if (os_strcmp(str, "ssdp:all") != 0 &&
-                   os_strstr(usrv->service, str) == NULL)
-                       continue;
-               if (wpabuf_tailroom(resp) < 2)
-                       break;
-               if (count == 0) {
-                       /* Status Code */
-                       wpabuf_put_u8(resp, P2P_SD_SUCCESS);
-                       /* Response Data */
-                       wpabuf_put_u8(resp, version);
-               } else
-                       wpabuf_put_u8(resp, ',');
-               count++;
-               wpa_printf(MSG_DEBUG, "P2P: Matching UPnP Service: %s",
-                          usrv->service);
-               if (wpabuf_tailroom(resp) < os_strlen(usrv->service))
-                       break;
-               wpabuf_put_str(resp, usrv->service);
-       }
-       os_free(str);
-       if (count == 0) {
-               wpa_printf(MSG_DEBUG, "P2P: Requested UPnP service not "
-                          "available");
-               /* Status Code */
-               wpabuf_put_u8(resp, P2P_SD_REQUESTED_INFO_NOT_AVAILABLE);
-               /* Response Data: empty */
-       }
-       WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - 2);
- }
- #ifdef CONFIG_WIFI_DISPLAY
- static void wpas_sd_req_wfd(struct wpa_supplicant *wpa_s,
-                           struct wpabuf *resp, u8 srv_trans_id,
-                           const u8 *query, size_t query_len)
- {
-       const u8 *pos;
-       u8 role;
-       u8 *len_pos;
-       wpa_hexdump(MSG_DEBUG, "P2P: SD Request for WFD", query, query_len);
-       if (!wpa_s->global->wifi_display) {
-               wpa_printf(MSG_DEBUG, "P2P: WFD protocol not available");
-               wpas_sd_add_proto_not_avail(resp, P2P_SERV_WIFI_DISPLAY,
-                                           srv_trans_id);
-               return;
-       }
-       if (query_len < 1) {
-               wpa_printf(MSG_DEBUG, "P2P: Missing WFD Requested Device "
-                          "Role");
-               return;
-       }
-       if (wpabuf_tailroom(resp) < 5)
-               return;
-       pos = query;
-       role = *pos++;
-       wpa_printf(MSG_DEBUG, "P2P: WSD for device role 0x%x", role);
-       /* TODO: role specific handling */
-       /* Length (to be filled) */
-       len_pos = wpabuf_put(resp, 2);
-       wpabuf_put_u8(resp, P2P_SERV_WIFI_DISPLAY);
-       wpabuf_put_u8(resp, srv_trans_id);
-       wpabuf_put_u8(resp, P2P_SD_SUCCESS); /* Status Code */
-       while (pos < query + query_len) {
-               if (*pos < MAX_WFD_SUBELEMS &&
-                   wpa_s->global->wfd_subelem[*pos] &&
-                   wpabuf_tailroom(resp) >=
-                   wpabuf_len(wpa_s->global->wfd_subelem[*pos])) {
-                       wpa_printf(MSG_DEBUG, "P2P: Add WSD response "
-                                  "subelement %u", *pos);
-                       wpabuf_put_buf(resp, wpa_s->global->wfd_subelem[*pos]);
-               }
-               pos++;
-       }
-       WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - 2);
- }
- #endif /* CONFIG_WIFI_DISPLAY */
- static int find_p2ps_substr(struct p2ps_advertisement *adv_data,
-                           const u8 *needle, size_t needle_len)
- {
-       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;
-       size_t buf_len;
-       char *buf;
-       wpa_hexdump(MSG_MSGDUMP, "P2P: Service Discovery Request TLVs",
-                   tlvs, tlvs_len);
-       buf_len = 2 * tlvs_len + 1;
-       buf = os_malloc(buf_len);
-       if (buf) {
-               wpa_snprintf_hex(buf, buf_len, tlvs, tlvs_len);
-               wpa_msg_ctrl(wpa_s, MSG_INFO, P2P_EVENT_SERV_DISC_REQ "%d "
-                            MACSTR " %u %u %s",
-                            freq, MAC2STR(sa), dialog_token, update_indic,
-                            buf);
-               os_free(buf);
-       }
-       if (wpa_s->p2p_sd_over_ctrl_iface) {
-               wpas_notify_p2p_sd_request(wpa_s, freq, sa, dialog_token,
-                                          update_indic, tlvs, tlvs_len);
-               return; /* to be processed by an external program */
-       }
-       resp = wpabuf_alloc(10000);
-       if (resp == NULL)
-               return;
-       while (pos + 1 < end) {
-               wpa_printf(MSG_DEBUG, "P2P: Service Request TLV");
-               slen = WPA_GET_LE16(pos);
-               pos += 2;
-               if (pos + slen > end || slen < 2) {
-                       wpa_printf(MSG_DEBUG, "P2P: Unexpected Query Data "
-                                  "length");
-                       wpabuf_free(resp);
-                       return;
-               }
-               tlv_end = pos + slen;
-               srv_proto = *pos++;
-               wpa_printf(MSG_DEBUG, "P2P: Service Protocol Type %u",
-                          srv_proto);
-               srv_trans_id = *pos++;
-               wpa_printf(MSG_DEBUG, "P2P: Service Transaction ID %u",
-                          srv_trans_id);
-               wpa_hexdump(MSG_MSGDUMP, "P2P: Query Data",
-                           pos, tlv_end - pos);
-               if (wpa_s->force_long_sd) {
-                       wpa_printf(MSG_DEBUG, "P2P: SD test - force long "
-                                  "response");
-                       wpas_sd_all_bonjour(wpa_s, resp, srv_trans_id);
-                       wpas_sd_all_upnp(wpa_s, resp, srv_trans_id);
-                       goto done;
-               }
-               switch (srv_proto) {
-               case P2P_SERV_ALL_SERVICES:
-                       wpa_printf(MSG_DEBUG, "P2P: Service Discovery Request "
-                                  "for all services");
-                       if (dl_list_empty(&wpa_s->global->p2p_srv_upnp) &&
-                           dl_list_empty(&wpa_s->global->p2p_srv_bonjour)) {
-                               wpa_printf(MSG_DEBUG, "P2P: No service "
-                                          "discovery protocols available");
-                               wpas_sd_add_proto_not_avail(
-                                       resp, P2P_SERV_ALL_SERVICES,
-                                       srv_trans_id);
-                               break;
-                       }
-                       wpas_sd_all_bonjour(wpa_s, resp, srv_trans_id);
-                       wpas_sd_all_upnp(wpa_s, resp, srv_trans_id);
-                       break;
-               case P2P_SERV_BONJOUR:
-                       wpas_sd_req_bonjour(wpa_s, resp, srv_trans_id,
-                                           pos, tlv_end - pos);
-                       break;
-               case P2P_SERV_UPNP:
-                       wpas_sd_req_upnp(wpa_s, resp, srv_trans_id,
-                                        pos, tlv_end - pos);
-                       break;
- #ifdef CONFIG_WIFI_DISPLAY
-               case P2P_SERV_WIFI_DISPLAY:
-                       wpas_sd_req_wfd(wpa_s, resp, srv_trans_id,
-                                       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);
-                       wpas_sd_add_proto_not_avail(resp, srv_proto,
-                                                   srv_trans_id);
-                       break;
-               }
-               pos = tlv_end;
-       }
- done:
-       wpas_notify_p2p_sd_request(wpa_s, freq, sa, dialog_token,
-                                  update_indic, tlvs, tlvs_len);
-       wpas_p2p_sd_response(wpa_s, freq, sa, dialog_token, resp);
-       wpabuf_free(resp);
- }
- 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;
-       const u8 *end = tlvs + tlvs_len;
-       const u8 *tlv_end;
-       u16 slen;
-       size_t buf_len;
-       char *buf;
-       wpa_hexdump(MSG_MSGDUMP, "P2P: Service Discovery Response TLVs",
-                   tlvs, tlvs_len);
-       if (tlvs_len > 1500) {
-               /* TODO: better way for handling this */
-               wpa_msg_ctrl(wpa_s, MSG_INFO,
-                            P2P_EVENT_SERV_DISC_RESP MACSTR
-                            " %u <long response: %u bytes>",
-                            MAC2STR(sa), update_indic,
-                            (unsigned int) tlvs_len);
-       } else {
-               buf_len = 2 * tlvs_len + 1;
-               buf = os_malloc(buf_len);
-               if (buf) {
-                       wpa_snprintf_hex(buf, buf_len, tlvs, tlvs_len);
-                       wpa_msg_ctrl(wpa_s, MSG_INFO,
-                                    P2P_EVENT_SERV_DISC_RESP MACSTR " %u %s",
-                                    MAC2STR(sa), update_indic, buf);
-                       os_free(buf);
-               }
-       }
-       while (pos < end) {
-               u8 srv_proto, srv_trans_id, status;
-               wpa_printf(MSG_DEBUG, "P2P: Service Response TLV");
-               slen = WPA_GET_LE16(pos);
-               pos += 2;
-               if (pos + slen > end || slen < 3) {
-                       wpa_printf(MSG_DEBUG, "P2P: Unexpected Response Data "
-                                  "length");
-                       return;
-               }
-               tlv_end = pos + slen;
-               srv_proto = *pos++;
-               wpa_printf(MSG_DEBUG, "P2P: Service Protocol Type %u",
-                          srv_proto);
-               srv_trans_id = *pos++;
-               wpa_printf(MSG_DEBUG, "P2P: Service Transaction ID %u",
-                          srv_trans_id);
-               status = *pos++;
-               wpa_printf(MSG_DEBUG, "P2P: Status Code ID %u",
-                          status);
-               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;
-       }
-       wpas_notify_p2p_sd_response(wpa_s, sa, update_indic, tlvs, tlvs_len);
- }
- u64 wpas_p2p_sd_request(struct wpa_supplicant *wpa_s, const u8 *dst,
-                       const struct wpabuf *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);
- }
- u64 wpas_p2p_sd_request_upnp(struct wpa_supplicant *wpa_s, const u8 *dst,
-                            u8 version, const char *query)
- {
-       struct wpabuf *tlvs;
-       u64 ret;
-       tlvs = wpabuf_alloc(2 + 1 + 1 + 1 + os_strlen(query));
-       if (tlvs == NULL)
-               return 0;
-       wpabuf_put_le16(tlvs, 1 + 1 + 1 + os_strlen(query));
-       wpabuf_put_u8(tlvs, P2P_SERV_UPNP); /* Service Protocol Type */
-       wpabuf_put_u8(tlvs, 1); /* Service Transaction ID */
-       wpabuf_put_u8(tlvs, version);
-       wpabuf_put_str(tlvs, query);
-       ret = wpas_p2p_sd_request(wpa_s, dst, tlvs);
-       wpabuf_free(tlvs);
-       return ret;
- }
- 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->global->p2p_disabled || wpa_s->global->p2p == NULL)
-               return 0;
-       return (uintptr_t) p2p_sd_request_wfd(wpa_s->global->p2p, dst, tlvs);
- }
- #define MAX_WFD_SD_SUBELEMS 20
- static void wfd_add_sd_req_role(struct wpabuf *tlvs, u8 id, u8 role,
-                               const char *subelems)
- {
-       u8 *len;
-       const char *pos;
-       int val;
-       int count = 0;
-       len = wpabuf_put(tlvs, 2);
-       wpabuf_put_u8(tlvs, P2P_SERV_WIFI_DISPLAY); /* Service Protocol Type */
-       wpabuf_put_u8(tlvs, id); /* Service Transaction ID */
-       wpabuf_put_u8(tlvs, role);
-       pos = subelems;
-       while (*pos) {
-               val = atoi(pos);
-               if (val >= 0 && val < 256) {
-                       wpabuf_put_u8(tlvs, val);
-                       count++;
-                       if (count == MAX_WFD_SD_SUBELEMS)
-                               break;
-               }
-               pos = os_strchr(pos + 1, ',');
-               if (pos == NULL)
-                       break;
-               pos++;
-       }
-       WPA_PUT_LE16(len, (u8 *) wpabuf_put(tlvs, 0) - len - 2);
- }
- u64 wpas_p2p_sd_request_wifi_display(struct wpa_supplicant *wpa_s,
-                                    const u8 *dst, const char *role)
- {
-       struct wpabuf *tlvs;
-       u64 ret;
-       const char *subelems;
-       u8 id = 1;
-       subelems = os_strchr(role, ' ');
-       if (subelems == NULL)
-               return 0;
-       subelems++;
-       tlvs = wpabuf_alloc(4 * (2 + 1 + 1 + 1 + MAX_WFD_SD_SUBELEMS));
-       if (tlvs == NULL)
-               return 0;
-       if (os_strstr(role, "[source]"))
-               wfd_add_sd_req_role(tlvs, id++, 0x00, subelems);
-       if (os_strstr(role, "[pri-sink]"))
-               wfd_add_sd_req_role(tlvs, id++, 0x01, subelems);
-       if (os_strstr(role, "[sec-sink]"))
-               wfd_add_sd_req_role(tlvs, id++, 0x02, subelems);
-       if (os_strstr(role, "[source+sink]"))
-               wfd_add_sd_req_role(tlvs, id++, 0x03, subelems);
-       ret = wpas_p2p_sd_request_wfd(wpa_s, dst, tlvs);
-       wpabuf_free(tlvs);
-       return ret;
- }
- #endif /* CONFIG_WIFI_DISPLAY */
- int wpas_p2p_sd_cancel_request(struct wpa_supplicant *wpa_s, u64 req)
- {
-       if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
-               return -1;
-       return p2p_sd_cancel_request(wpa_s->global->p2p,
-                                    (void *) (uintptr_t) req);
- }
- 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->global->p2p_disabled || wpa_s->global->p2p == NULL)
-               return;
-       p2p_sd_response(wpa_s->global->p2p, freq, dst, dialog_token,
-                       resp_tlvs);
- }
- void wpas_p2p_sd_service_update(struct wpa_supplicant *wpa_s)
++      wpas_notify_p2p_go_neg_req(wpa_s, src, dev_passwd_id, go_intent);
 +}
 +
 +
 +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;
 +      }
 +
 +      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%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,
 +                     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);
 +}
 +
 +
 +static void wpas_dev_lost(void *ctx, const u8 *dev_addr)
 +{
 +      struct wpa_supplicant *wpa_s = ctx;
 +
 +      wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_DEVICE_LOST
 +                     "p2p_dev_addr=" MACSTR, MAC2STR(dev_addr));
 +
 +      wpas_notify_p2p_device_lost(wpa_s, dev_addr);
 +}
 +
 +
-       if (wpa_s->global->p2p)
-               p2p_sd_service_update(wpa_s->global->p2p);
++static void wpas_find_stopped(void *ctx)
 +{
- static void wpas_p2p_srv_bonjour_free(struct p2p_srv_bonjour *bsrv)
- {
-       dl_list_del(&bsrv->list);
-       wpabuf_free(bsrv->query);
-       wpabuf_free(bsrv->resp);
-       os_free(bsrv);
- }
++      struct wpa_supplicant *wpa_s = ctx;
++      wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_FIND_STOPPED);
++      wpas_notify_p2p_find_stopped(wpa_s);
 +}
 +
 +
- static void wpas_p2p_srv_upnp_free(struct p2p_srv_upnp *usrv)
++struct wpas_p2p_listen_work {
++      unsigned int freq;
++      unsigned int duration;
++      struct wpabuf *probe_resp_ie;
++};
 +
 +
-       dl_list_del(&usrv->list);
-       os_free(usrv->service);
-       os_free(usrv);
++static void wpas_p2p_listen_work_free(struct wpas_p2p_listen_work *lwork)
 +{
- void wpas_p2p_service_flush(struct wpa_supplicant *wpa_s)
++      if (lwork == NULL)
++              return;
++      wpabuf_free(lwork->probe_resp_ie);
++      os_free(lwork);
 +}
 +
 +
-       struct p2p_srv_bonjour *bsrv, *bn;
-       struct p2p_srv_upnp *usrv, *un;
-       dl_list_for_each_safe(bsrv, bn, &wpa_s->global->p2p_srv_bonjour,
-                             struct p2p_srv_bonjour, list)
-               wpas_p2p_srv_bonjour_free(bsrv);
++static void wpas_p2p_listen_work_done(struct wpa_supplicant *wpa_s)
 +{
-       dl_list_for_each_safe(usrv, un, &wpa_s->global->p2p_srv_upnp,
-                             struct p2p_srv_upnp, list)
-               wpas_p2p_srv_upnp_free(usrv);
++      struct wpas_p2p_listen_work *lwork;
 +
-       wpas_p2p_sd_service_update(wpa_s);
++      if (!wpa_s->p2p_listen_work)
++              return;
 +
- int wpas_p2p_service_p2ps_id_exists(struct wpa_supplicant *wpa_s, u32 adv_id)
++      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;
 +}
 +
 +
-       if (adv_id == 0)
-               return 1;
++static void wpas_start_listen_cb(struct wpa_radio_work *work, int deinit)
 +{
-       if (p2p_service_p2ps_id(wpa_s->global->p2p, adv_id))
-               return 1;
++      struct wpa_supplicant *wpa_s = work->wpa_s;
++      struct wpas_p2p_listen_work *lwork = work->ctx;
++      unsigned int duration;
 +
-       return 0;
- }
++      if (deinit) {
++              if (work->started) {
++                      wpa_s->p2p_listen_work = NULL;
++                      wpas_stop_listen(wpa_s);
++              }
++              wpas_p2p_listen_work_free(lwork);
++              return;
++      }
 +
- 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);
- }
++      wpa_s->p2p_listen_work = work;
 +
++      wpa_drv_set_ap_wps_ie(wpa_s, NULL, lwork->probe_resp_ie, NULL);
 +
- 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);
++      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");
++              wpas_p2p_listen_work_done(wpa_s);
++              return;
++      }
 +
++      wpa_s->pending_listen_freq = lwork->freq;
++      wpa_s->pending_listen_duration = lwork->duration;
 +
- int wpas_p2p_service_add_bonjour(struct wpa_supplicant *wpa_s,
-                                struct wpabuf *query, struct wpabuf *resp)
++      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, lwork->freq, duration) < 0) {
++              wpa_printf(MSG_DEBUG, "P2P: Failed to request the driver "
++                         "to remain on channel (%u MHz) for Listen "
++                         "state", lwork->freq);
++              wpas_p2p_listen_work_done(wpa_s);
++              wpa_s->pending_listen_freq = 0;
++              return;
++      }
++      wpa_s->off_channel_freq = 0;
++      wpa_s->roc_waiting_drv_freq = lwork->freq;
 +}
 +
 +
-       struct p2p_srv_bonjour *bsrv;
++static int wpas_start_listen(void *ctx, unsigned int freq,
++                           unsigned int duration,
++                           const struct wpabuf *probe_resp_ie)
 +{
-       bsrv = os_zalloc(sizeof(*bsrv));
-       if (bsrv == NULL)
++      struct wpa_supplicant *wpa_s = ctx;
++      struct wpas_p2p_listen_work *lwork;
 +
-       bsrv->query = query;
-       bsrv->resp = resp;
-       dl_list_add(&wpa_s->global->p2p_srv_bonjour, &bsrv->list);
-       wpas_p2p_sd_service_update(wpa_s);
-       return 0;
- }
++      if (wpa_s->p2p_listen_work) {
++              wpa_printf(MSG_DEBUG, "P2P: Reject start_listen since p2p_listen_work already exists");
 +              return -1;
- int wpas_p2p_service_del_bonjour(struct wpa_supplicant *wpa_s,
-                                const struct wpabuf *query)
- {
-       struct p2p_srv_bonjour *bsrv;
++      }
 +
-       bsrv = wpas_p2p_service_get_bonjour(wpa_s, query);
-       if (bsrv == NULL)
++      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;
++              }
++      }
 +
-       wpas_p2p_srv_bonjour_free(bsrv);
-       wpas_p2p_sd_service_update(wpa_s);
++      if (radio_add_work(wpa_s, freq, "p2p-listen", 0, wpas_start_listen_cb,
++                         lwork) < 0) {
++              wpas_p2p_listen_work_free(lwork);
 +              return -1;
- int wpas_p2p_service_add_upnp(struct wpa_supplicant *wpa_s, u8 version,
-                             const char *service)
++      }
++
 +      return 0;
 +}
 +
 +
-       struct p2p_srv_upnp *usrv;
-       if (wpas_p2p_service_get_upnp(wpa_s, version, service))
-               return 0; /* Already listed */
-       usrv = os_zalloc(sizeof(*usrv));
-       if (usrv == NULL)
-               return -1;
-       usrv->version = version;
-       usrv->service = os_strdup(service);
-       if (usrv->service == NULL) {
-               os_free(usrv);
-               return -1;
++static void wpas_stop_listen(void *ctx)
 +{
-       dl_list_add(&wpa_s->global->p2p_srv_upnp, &usrv->list);
++      struct wpa_supplicant *wpa_s = ctx;
++      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;
 +      }
-       wpas_p2p_sd_service_update(wpa_s);
-       return 0;
++      wpa_drv_set_ap_wps_ie(wpa_s, NULL, NULL, NULL);
 +
- int wpas_p2p_service_del_upnp(struct wpa_supplicant *wpa_s, u8 version,
-                             const char *service)
++      /*
++       * Don't cancel Probe Request RX reporting for a connected P2P Client
++       * handling Probe Request frames.
++       */
++      if (!wpa_s->p2p_cli_probe)
++              wpa_drv_probe_req_report(wpa_s, 0);
++
++      wpas_p2p_listen_work_done(wpa_s);
 +}
 +
 +
-       struct p2p_srv_upnp *usrv;
-       usrv = wpas_p2p_service_get_upnp(wpa_s, version, service);
-       if (usrv == NULL)
-               return -1;
-       wpas_p2p_srv_upnp_free(usrv);
-       wpas_p2p_sd_service_update(wpa_s);
-       return 0;
++static int wpas_send_probe_resp(void *ctx, const struct wpabuf *buf,
++                              unsigned int freq)
 +{
- static int freq_included(const struct p2p_channels *channels, unsigned int freq)
++      struct wpa_supplicant *wpa_s = ctx;
++      return wpa_drv_send_mlme(wpa_s, wpabuf_head(buf), wpabuf_len(buf), 1,
++                               freq);
 +}
 +
 +
 +static void wpas_prov_disc_local_display(struct wpa_supplicant *wpa_s,
 +                                       const u8 *peer, const char *params,
 +                                       unsigned int generated_pin)
 +{
 +      wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_SHOW_PIN MACSTR
 +                     " %08d%s", MAC2STR(peer), generated_pin, params);
 +}
 +
 +
 +static void wpas_prov_disc_local_keypad(struct wpa_supplicant *wpa_s,
 +                                      const u8 *peer, const char *params)
 +{
 +      wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_ENTER_PIN MACSTR
 +                     "%s", MAC2STR(peer), params);
 +}
 +
 +
 +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];
 +      char params[300];
 +      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)
 +              {
 +                      struct wpa_ssid *s = group->current_ssid;
 +                      if (s != NULL &&
 +                          s->mode == WPAS_MODE_P2P_GO &&
 +                          group_id_len - ETH_ALEN == s->ssid_len &&
 +                          os_memcmp(group_id + ETH_ALEN, s->ssid,
 +                                    s->ssid_len) == 0)
 +                              break;
 +              }
 +      }
 +
 +      if (pri_dev_type == NULL) {
 +              os_memset(empty_dev_type, 0, sizeof(empty_dev_type));
 +              pri_dev_type = empty_dev_type;
 +      }
 +      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) {
 +              generated_pin = wps_generate_pin();
 +              wpas_prov_disc_local_display(wpa_s, peer, params,
 +                                           generated_pin);
 +      } else if (config_methods & WPS_CONFIG_KEYPAD)
 +              wpas_prov_disc_local_keypad(wpa_s, peer, params);
 +      else if (config_methods & WPS_CONFIG_PUSHBUTTON)
 +              wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_PBC_REQ
 +                             MACSTR "%s", MAC2STR(peer), params);
 +
 +      wpas_notify_p2p_provision_discovery(wpa_s, peer, 1 /* request */,
 +                                          P2P_PROV_DISC_SUCCESS,
 +                                          config_methods, generated_pin);
 +}
 +
 +
 +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;
 +      char params[20];
 +
 +      if (wpa_s->pending_pd_before_join &&
 +          (os_memcmp(peer, wpa_s->pending_join_dev_addr, ETH_ALEN) == 0 ||
 +           os_memcmp(peer, wpa_s->pending_join_iface_addr, ETH_ALEN) == 0)) {
 +              wpa_s->pending_pd_before_join = 0;
 +              wpa_printf(MSG_DEBUG, "P2P: Starting pending "
 +                         "join-existing-group operation");
 +              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) {
 +              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)
 +              wpas_prov_disc_local_keypad(wpa_s, peer, params);
 +      else if (config_methods & WPS_CONFIG_KEYPAD) {
 +              generated_pin = wps_generate_pin();
 +              wpas_prov_disc_local_display(wpa_s, peer, params,
 +                                           generated_pin);
 +      } else if (config_methods & WPS_CONFIG_PUSHBUTTON)
 +              wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_PBC_RESP
 +                             MACSTR "%s", MAC2STR(peer), params);
 +
 +      wpas_notify_p2p_provision_discovery(wpa_s, peer, 0 /* response */,
 +                                          P2P_PROV_DISC_SUCCESS,
 +                                          config_methods, generated_pin);
 +}
 +
 +
 +static void wpas_prov_disc_fail(void *ctx, const u8 *peer,
 +                              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;
 +      }
 +
 +      if (status == P2P_PROV_DISC_TIMEOUT_JOIN) {
 +              wpa_s->pending_pd_before_join = 0;
 +              wpa_printf(MSG_DEBUG, "P2P: Starting pending "
 +                         "join-existing-group operation (no ACK for PD "
 +                         "Req attempts)");
 +              wpas_p2p_join_start(wpa_s, 0, NULL, 0);
 +              return;
 +      }
 +
 +      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);
 +}
 +
 +
-       if (channels == NULL)
-               return 1; /* Assume no restrictions */
-       return p2p_channels_includes_freq(channels, freq);
++static int freq_included(struct wpa_supplicant *wpa_s,
++                       const struct p2p_channels *channels,
++                       unsigned int freq)
++{
++      if ((channels == NULL || p2p_channels_includes_freq(channels, freq)) &&
++          wpas_p2p_go_is_peer_freq(wpa_s, freq))
++              return 1;
++      return 0;
++}
++
++
++static void wpas_p2p_go_update_common_freqs(struct wpa_supplicant *wpa_s)
++{
++      unsigned int num = P2P_MAX_CHANNELS;
++      int *common_freqs;
++      int ret;
++
++      p2p_go_dump_common_freqs(wpa_s);
++      common_freqs = os_calloc(num, sizeof(int));
++      if (!common_freqs)
++              return;
++
++      ret = p2p_group_get_common_freqs(wpa_s->p2p_group, common_freqs, &num);
++      if (ret < 0) {
++              wpa_dbg(wpa_s, MSG_DEBUG,
++                      "P2P: Failed to get group common freqs");
++              os_free(common_freqs);
++              return;
++      }
++
++      os_free(wpa_s->p2p_group_common_freqs);
++      wpa_s->p2p_group_common_freqs = common_freqs;
++      wpa_s->p2p_group_common_freqs_num = num;
++      p2p_go_dump_common_freqs(wpa_s);
++}
++
++
++/*
++ * Check if the given frequency is one of the possible operating frequencies
++ * set after the completion of the GO Negotiation.
++ */
++static int wpas_p2p_go_is_peer_freq(struct wpa_supplicant *wpa_s, int freq)
 +{
-               } else if (!freq_included(channels, *force_freq)) {
++      unsigned int i;
++
++      p2p_go_dump_common_freqs(wpa_s);
++
++      /* assume no restrictions */
++      if (!wpa_s->p2p_group_common_freqs_num)
++              return 1;
 +
++      for (i = 0; i < wpa_s->p2p_group_common_freqs_num; i++) {
++              if (wpa_s->p2p_group_common_freqs[i] == freq)
++                      return 1;
++      }
++      return 0;
 +}
 +
 +
 +/**
 + * 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,
 +                                int dev_pw_id)
 +{
 +      struct wpa_supplicant *wpa_s = ctx;
 +      struct wpa_ssid *s;
 +      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 (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 ||
 +                   os_memcmp(sa, wpa_s->p2p_auth_invite, ETH_ALEN) == 0)) {
 +                      wpa_printf(MSG_DEBUG, "P2P: Accept previously "
 +                                 "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.
 +               */
 +              return P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE;
 +      }
 +
 +      grp = wpas_get_p2p_group(wpa_s, ssid, ssid_len, go);
 +      if (grp) {
 +              wpa_printf(MSG_DEBUG, "P2P: Accept invitation to already "
 +                         "running persistent group");
 +              if (*go)
 +                      os_memcpy(group_bssid, grp->own_addr, ETH_ALEN);
 +              goto accept_inv;
 +      }
 +
 +      if (!is_zero_ether_addr(wpa_s->p2p_auth_invite) &&
 +          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;
 +
 +      for (s = wpa_s->conf->ssid; s; s = s->next) {
 +              if (s->disabled == 2 &&
 +                  os_memcmp(s->bssid, go_dev_addr, ETH_ALEN) == 0 &&
 +                  s->ssid_len == ssid_len &&
 +                  os_memcmp(ssid, s->ssid, ssid_len) == 0)
 +                      break;
 +      }
 +
 +      if (!s) {
 +              wpa_printf(MSG_DEBUG, "P2P: Invitation from " MACSTR
 +                         " requested reinvocation of an unknown group",
 +                         MAC2STR(sa));
 +              return P2P_SC_FAIL_UNKNOWN_GROUP;
 +      }
 +
 +      if (s->mode == WPAS_MODE_P2P_GO && !wpas_p2p_create_iface(wpa_s)) {
 +              *go = 1;
 +              if (wpa_s->wpa_state >= WPA_AUTHENTICATING) {
 +                      wpa_printf(MSG_DEBUG, "P2P: The only available "
 +                                 "interface is already in use - reject "
 +                                 "invitation");
 +                      return P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE;
 +              }
 +              os_memcpy(group_bssid, wpa_s->own_addr, ETH_ALEN);
 +      } else if (s->mode == WPAS_MODE_P2P_GO) {
 +              *go = 1;
 +              if (wpas_p2p_add_group_interface(wpa_s, WPA_IF_P2P_GO) < 0)
 +              {
 +                      wpa_printf(MSG_ERROR, "P2P: Failed to allocate a new "
 +                                 "interface address for the group");
 +                      return P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE;
 +              }
 +              os_memcpy(group_bssid, wpa_s->pending_interface_addr,
 +                        ETH_ALEN);
 +      }
 +
 +accept_inv:
 +      wpas_p2p_set_own_freq_preference(wpa_s, 0);
 +
 +      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->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 "
 +                                 "running a GO but we are capable of MCC, "
 +                                 "figure out the best channel to use");
 +                      *force_freq = 0;
-                               go ? P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE : 0);
++              } else if (!freq_included(wpa_s, channels, *force_freq)) {
 +                      /* We are the GO, and *force_freq is not in the
 +                       * intersection */
 +                      wpa_printf(MSG_DEBUG, "P2P: Forced GO freq %d MHz not "
 +                                 "in intersection but we are capable of MCC, "
 +                                 "figure out the best channel to use",
 +                                 *force_freq);
 +                      *force_freq = 0;
 +              }
 +      }
 +
 +      return P2P_SC_SUCCESS;
 +}
 +
 +
 +static void wpas_invitation_received(void *ctx, const u8 *sa, const u8 *bssid,
 +                                   const u8 *ssid, size_t ssid_len,
 +                                   const u8 *go_dev_addr, u8 status,
 +                                   int op_freq)
 +{
 +      struct wpa_supplicant *wpa_s = ctx;
 +      struct wpa_ssid *s;
 +
 +      for (s = wpa_s->conf->ssid; s; s = s->next) {
 +              if (s->disabled == 2 &&
 +                  s->ssid_len == ssid_len &&
 +                  os_memcmp(ssid, s->ssid, ssid_len) == 0)
 +                      break;
 +      }
 +
 +      if (status == P2P_SC_SUCCESS) {
 +              wpa_printf(MSG_DEBUG, "P2P: Invitation from peer " MACSTR
 +                         " 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, 0, op_freq, 0, 0, NULL,
-       if (wpa_s->parent->conf->update_config &&
-           wpa_config_write(wpa_s->parent->confname, wpa_s->parent->conf))
++                              go ? P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE : 0,
++                              1);
 +              } else if (bssid) {
 +                      wpa_s->user_initiated_pd = 0;
 +                      wpas_p2p_join(wpa_s, bssid, go_dev_addr,
 +                                    wpa_s->p2p_wps_method, 0, op_freq,
 +                                    ssid, ssid_len);
 +              }
 +              return;
 +      }
 +
 +      if (status != P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) {
 +              wpa_printf(MSG_DEBUG, "P2P: Invitation from peer " MACSTR
 +                         " was rejected (status %u)", MAC2STR(sa), status);
 +              return;
 +      }
 +
 +      if (!s) {
 +              if (bssid) {
 +                      wpa_msg_global(wpa_s, MSG_INFO,
 +                                     P2P_EVENT_INVITATION_RECEIVED
 +                                     "sa=" MACSTR " go_dev_addr=" MACSTR
 +                                     " bssid=" MACSTR " unknown-network",
 +                                     MAC2STR(sa), MAC2STR(go_dev_addr),
 +                                     MAC2STR(bssid));
 +              } else {
 +                      wpa_msg_global(wpa_s, MSG_INFO,
 +                                     P2P_EVENT_INVITATION_RECEIVED
 +                                     "sa=" MACSTR " go_dev_addr=" MACSTR
 +                                     " unknown-network",
 +                                     MAC2STR(sa), MAC2STR(go_dev_addr));
 +              }
++              wpas_notify_p2p_invitation_received(wpa_s, sa, go_dev_addr,
++                                                  bssid, 0, op_freq);
 +              return;
 +      }
 +
 +      if (s->mode == WPAS_MODE_P2P_GO && op_freq) {
 +              wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_INVITATION_RECEIVED
 +                             "sa=" MACSTR " persistent=%d freq=%d",
 +                             MAC2STR(sa), s->id, op_freq);
 +      } else {
 +              wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_INVITATION_RECEIVED
 +                             "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, int inv)
 +{
 +      size_t i;
++      struct wpa_supplicant *p2p_wpa_s = wpa_s->global->p2p_init_wpa_s;
 +
 +      if (ssid == NULL)
 +              return;
 +
 +      for (i = 0; ssid->p2p_client_list && i < ssid->num_p2p_clients; i++) {
 +              if (os_memcmp(ssid->p2p_client_list + i * 2 * ETH_ALEN, peer,
 +                            ETH_ALEN) == 0)
 +                      break;
 +      }
 +      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 "
 +                                 "due to invitation result", ssid->id);
 +                      wpas_notify_network_removed(wpa_s, ssid);
 +                      wpa_config_remove_network(wpa_s->conf, ssid->id);
 +                      return;
 +              }
 +              return; /* Peer not found in client list */
 +      }
 +
 +      wpa_printf(MSG_DEBUG, "P2P: Remove peer " MACSTR " from persistent "
 +                 "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--;
-           freq_included(channels, neg_freq))
++      if (p2p_wpa_s->conf->update_config &&
++          wpa_config_write(p2p_wpa_s->confname, p2p_wpa_s->conf))
 +              wpa_printf(MSG_DEBUG, "P2P: Failed to update configuration");
 +}
 +
 +
 +static void wpas_remove_persistent_client(struct wpa_supplicant *wpa_s,
 +                                        const u8 *peer)
 +{
 +      struct wpa_ssid *ssid;
 +
 +      wpa_s = wpa_s->global->p2p_invite_group;
 +      if (wpa_s == NULL)
 +              return; /* No known invitation group */
 +      ssid = wpa_s->current_ssid;
 +      if (ssid == NULL || ssid->mode != WPAS_MODE_P2P_GO ||
 +          !ssid->p2p_persistent_group)
 +              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, 1);
 +}
 +
 +
 +static void wpas_invitation_result(void *ctx, int status, const u8 *bssid,
 +                                 const struct p2p_channels *channels,
 +                                 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
 +                             "status=%d " MACSTR,
 +                             status, MAC2STR(bssid));
 +      } else {
 +              wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_INVITATION_RESULT
 +                             "status=%d ", status);
 +      }
 +      wpas_notify_p2p_invitation_result(wpa_s, status, bssid);
 +
 +      wpa_printf(MSG_DEBUG, "P2P: Invitation result - status=%d peer=" MACSTR,
 +                 status, MAC2STR(peer));
 +      if (wpa_s->pending_invite_ssid_id == -1) {
 +              if (status == P2P_SC_FAIL_UNKNOWN_GROUP)
 +                      wpas_remove_persistent_client(wpa_s, peer);
 +              return; /* Invitation to active group */
 +      }
 +
 +      if (status == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) {
 +              wpa_printf(MSG_DEBUG, "P2P: Waiting for peer to start another "
 +                         "invitation exchange to indicate readiness for "
 +                         "re-invocation");
 +      }
 +
 +      if (status != P2P_SC_SUCCESS) {
 +              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, 1);
 +              }
 +              wpas_p2p_remove_pending_group_interface(wpa_s);
 +              return;
 +      }
 +
 +      ssid = wpa_config_get_network(wpa_s->conf,
 +                                    wpa_s->pending_invite_ssid_id);
 +      if (ssid == NULL) {
 +              wpa_printf(MSG_ERROR, "P2P: Could not find persistent group "
 +                         "data matching with invitation");
 +              return;
 +      }
 +
 +      /*
 +       * The peer could have missed our ctrl::ack frame for Invitation
 +       * Response and continue retransmitting the frame. To reduce the
 +       * likelihood of the peer not getting successful TX status for the
 +       * Invitation Response frame, wait a short time here before starting
 +       * the persistent group so that we will remain on the current channel to
 +       * acknowledge any possible retransmission from the peer.
 +       */
 +      wpa_dbg(wpa_s, MSG_DEBUG, "P2P: 50 ms wait on current channel before "
 +              "starting persistent group");
 +      os_sleep(0, 50000);
 +
 +      if (neg_freq > 0 && ssid->mode == WPAS_MODE_P2P_GO &&
-                freq_included(channels, peer_oper_freq))
++          freq_included(wpa_s, channels, neg_freq))
 +              freq = neg_freq;
 +      else if (peer_oper_freq > 0 && ssid->mode != WPAS_MODE_P2P_GO &&
-                                     0);
++               freq_included(wpa_s, 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,
 +                                    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 :
- static struct p2p_oper_class_map op_class[] = {
++                                    0, 1);
 +}
 +
 +
 +static int wpas_p2p_disallowed_freq(struct wpa_global *global,
 +                                  unsigned int freq)
 +{
 +      if (freq_range_list_includes(&global->p2p_go_avoid_freq, freq))
 +              return 1;
 +      return freq_range_list_includes(&global->p2p_disallow_freq, freq);
 +}
 +
 +
 +static void wpas_p2p_add_chan(struct p2p_reg_class *reg, u8 chan)
 +{
 +      reg->channel[reg->channels] = chan;
 +      reg->channels++;
 +}
 +
 +
 +static int wpas_p2p_default_channels(struct wpa_supplicant *wpa_s,
 +                                   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");
 +
 +      /* Operating class 81 - 2.4 GHz band channels 1..13 */
 +      chan->reg_class[cla].reg_class = 81;
 +      chan->reg_class[cla].channels = 0;
 +      for (i = 0; i < 11; i++) {
 +              if (!wpas_p2p_disallowed_freq(wpa_s->global, 2412 + i * 5))
 +                      wpas_p2p_add_chan(&chan->reg_class[cla], i + 1);
 +      }
 +      if (chan->reg_class[cla].channels)
 +              cla++;
 +
 +      wpa_printf(MSG_DEBUG, "P2P: Enable operating classes for lower 5 GHz "
 +                 "band");
 +
 +      /* Operating class 115 - 5 GHz, channels 36-48 */
 +      chan->reg_class[cla].reg_class = 115;
 +      chan->reg_class[cla].channels = 0;
 +      if (!wpas_p2p_disallowed_freq(wpa_s->global, 5000 + 36 * 5))
 +              wpas_p2p_add_chan(&chan->reg_class[cla], 36);
 +      if (!wpas_p2p_disallowed_freq(wpa_s->global, 5000 + 40 * 5))
 +              wpas_p2p_add_chan(&chan->reg_class[cla], 40);
 +      if (!wpas_p2p_disallowed_freq(wpa_s->global, 5000 + 44 * 5))
 +              wpas_p2p_add_chan(&chan->reg_class[cla], 44);
 +      if (!wpas_p2p_disallowed_freq(wpa_s->global, 5000 + 48 * 5))
 +              wpas_p2p_add_chan(&chan->reg_class[cla], 48);
 +      if (chan->reg_class[cla].channels)
 +              cla++;
 +
 +      wpa_printf(MSG_DEBUG, "P2P: Enable operating classes for higher 5 GHz "
 +                 "band");
 +
 +      /* Operating class 124 - 5 GHz, channels 149,153,157,161 */
 +      chan->reg_class[cla].reg_class = 124;
 +      chan->reg_class[cla].channels = 0;
 +      if (!wpas_p2p_disallowed_freq(wpa_s->global, 5000 + 149 * 5))
 +              wpas_p2p_add_chan(&chan->reg_class[cla], 149);
 +      if (!wpas_p2p_disallowed_freq(wpa_s->global, 5000 + 153 * 5))
 +              wpas_p2p_add_chan(&chan->reg_class[cla], 153);
 +      if (!wpas_p2p_disallowed_freq(wpa_s->global, 5000 + 156 * 5))
 +              wpas_p2p_add_chan(&chan->reg_class[cla], 157);
 +      if (!wpas_p2p_disallowed_freq(wpa_s->global, 5000 + 161 * 5))
 +              wpas_p2p_add_chan(&chan->reg_class[cla], 161);
 +      if (chan->reg_class[cla].channels)
 +              cla++;
 +
 +      chan->reg_classes = cla;
 +      return 0;
 +}
 +
 +
 +static struct hostapd_hw_modes * get_mode(struct hostapd_hw_modes *modes,
 +                                        u16 num_modes,
 +                                        enum hostapd_hw_mode mode)
 +{
 +      u16 i;
 +
 +      for (i = 0; i < num_modes; i++) {
 +              if (modes[i].mode == mode)
 +                      return &modes[i];
 +      }
 +
 +      return NULL;
 +}
 +
 +
 +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)
 +{
 +      int i;
 +      unsigned int freq;
 +
 +      freq = (mode->mode == HOSTAPD_MODE_IEEE80211A ? 5000 : 2407) +
 +              chan * 5;
 +      if (wpas_p2p_disallowed_freq(global, freq))
 +              return NOT_ALLOWED;
 +
 +      for (i = 0; i < mode->num_channels; i++) {
 +              if (mode->channels[i].chan == chan) {
 +                      if (flags)
 +                              *flags = mode->channels[i].flag;
 +                      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 NOT_ALLOWED;
 +}
 +
 +
 +struct p2p_oper_class_map {
 +      enum hostapd_hw_mode mode;
 +      u8 op_class;
 +      u8 min_chan;
 +      u8 max_chan;
 +      u8 inc;
 +      enum { BW20, BW40PLUS, BW40MINUS, BW80, BW2160 } bw;
 +};
 +
-               struct p2p_oper_class_map *o = &op_class[op];
++static const struct p2p_oper_class_map op_class[] = {
 +      { HOSTAPD_MODE_IEEE80211G, 81, 1, 13, 1, BW20 },
 +#if 0 /* Do not enable HT40 on 2 GHz for now */
 +      { HOSTAPD_MODE_IEEE80211G, 83, 1, 9, 1, BW40PLUS },
 +      { HOSTAPD_MODE_IEEE80211G, 84, 5, 13, 1, BW40MINUS },
 +#endif
 +      { HOSTAPD_MODE_IEEE80211A, 115, 36, 48, 4, BW20 },
 +      { HOSTAPD_MODE_IEEE80211A, 124, 149, 161, 4, BW20 },
++      { HOSTAPD_MODE_IEEE80211A, 125, 149, 169, 4, BW20 },
 +      { HOSTAPD_MODE_IEEE80211A, 116, 36, 44, 8, BW40PLUS },
 +      { 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_get_center_80mhz(struct wpa_supplicant *wpa_s,
 +                                   struct hostapd_hw_modes *mode,
 +                                   u8 channel)
 +{
 +      u8 center_channels[] = { 42, 58, 106, 122, 138, 155 };
 +      unsigned int i;
 +
 +      if (mode->mode != HOSTAPD_MODE_IEEE80211A)
 +              return 0;
 +
 +      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 *cli_chan)
 +{
 +      struct hostapd_hw_modes *mode;
 +      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, cli_chan);
 +      }
 +
 +      cla = cli_cla = 0;
 +
 +      for (op = 0; op_class[op].op_class; op++) {
-               struct p2p_oper_class_map *o = &op_class[op];
++              const struct p2p_oper_class_map *o = &op_class[op];
 +              u8 ch;
 +              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) {
 +                      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++;
 +                      }
 +              }
 +              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;
 +}
 +
 +
 +int wpas_p2p_get_ht40_mode(struct wpa_supplicant *wpa_s,
 +                         struct hostapd_hw_modes *mode, u8 channel)
 +{
 +      int op;
 +      enum chan_allowed ret;
 +
 +      for (op = 0; op_class[op].op_class; op++) {
-                           o->bw == BW20 || ch != channel)
++              const struct p2p_oper_class_map *o = &op_class[op];
 +              u8 ch;
 +
 +              for (ch = o->min_chan; ch <= o->max_chan; ch += o->inc) {
 +                      if (o->mode != HOSTAPD_MODE_IEEE80211A ||
-               if (ssid == NULL)
-                       continue;
-               if (ssid->mode != WPAS_MODE_INFRA)
-                       continue;
-               if (wpa_s->wpa_state != WPA_COMPLETED &&
-                   wpa_s->wpa_state != WPA_GROUP_HANDSHAKE)
++                          (o->bw != BW40PLUS && o->bw != BW40MINUS) ||
++                          ch != channel)
 +                              continue;
 +                      ret = wpas_p2p_verify_channel(wpa_s, mode, ch, o->bw);
 +                      if (ret == ALLOWED)
 +                              return (o->bw == BW40MINUS) ? -1 : 1;
 +              }
 +      }
 +      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)
 +{
 +      struct wpa_supplicant *wpa_s = ctx;
 +
 +      for (wpa_s = wpa_s->global->ifaces; wpa_s; wpa_s = wpa_s->next) {
 +              if (os_memcmp(wpa_s->own_addr, interface_addr, ETH_ALEN) == 0)
 +                      break;
 +      }
 +      if (wpa_s == NULL)
 +              return -1;
 +
 +      return wpa_drv_get_noa(wpa_s, buf, buf_len);
 +}
 +
 +
 +struct wpa_supplicant * wpas_get_p2p_go_iface(struct wpa_supplicant *wpa_s,
 +                                            const u8 *ssid, size_t ssid_len)
 +{
 +      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;
-       iface.conf_p2p_dev = NULL;
++              if (ssid && (ssid->mode != WPAS_MODE_INFRA || !ssid->p2p_group))
 +                      continue;
 +              if (os_memcmp(wpa_s->go_dev_addr, peer_dev_addr, ETH_ALEN) == 0)
 +                      return wpa_s;
 +      }
 +
 +      return NULL;
 +}
 +
 +
 +static int wpas_go_connected(void *ctx, const u8 *dev_addr)
 +{
 +      struct wpa_supplicant *wpa_s = ctx;
 +
 +      return wpas_get_p2p_client_iface(wpa_s, dev_addr) != NULL;
 +}
 +
 +
 +static int wpas_is_concurrent_session_active(void *ctx)
 +{
 +      struct wpa_supplicant *wpa_s = ctx;
 +      struct wpa_supplicant *ifs;
 +
 +      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) {
 +              wpa_printf(MSG_DEBUG, "P2P: Failed to create P2P Device interface");
 +              return ret;
 +      }
 +      os_strlcpy(wpa_s->pending_interface_name, ifname,
 +                 sizeof(wpa_s->pending_interface_name));
 +
 +      os_memset(&iface, 0, sizeof(iface));
 +      iface.p2p_mgmt = 1;
 +      iface.ifname = wpa_s->pending_interface_name;
 +      iface.driver = wpa_s->driver->name;
 +      iface.driver_param = wpa_s->conf->driver_param;
 +
 +      /*
 +       * 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;
 +      }
-       wpa_s->p2p_dev = p2pdev_wpa_s;
 +
 +      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;
 +      }
-                                    u8 *ret_ssid, size_t *ret_ssid_len)
 +
 +      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,
-       *group_iface = wpas_p2p_create_iface(wpa_s);
-       if (!s)
-               return 0;
++                                   u8 *ret_ssid, size_t *ret_ssid_len,
++                                   u8 *intended_iface_addr)
 +{
 +      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);
++
++              if (s->mode != WPAS_MODE_P2P_GO) {
++                      os_memset(intended_iface_addr, 0, ETH_ALEN);
++              } else if (wpas_p2p_create_iface(wpa_s)) {
++                      if (wpas_p2p_add_group_interface(wpa_s, WPA_IF_P2P_GO))
++                              return 0;
++
++                      os_memcpy(intended_iface_addr,
++                                wpa_s->pending_interface_addr, ETH_ALEN);
++              } else {
++                      os_memcpy(intended_iface_addr, wpa_s->own_addr,
++                                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];
 +
++      /*
++       * group_iface will be set to 1 only if a dedicated interface for P2P
++       * role is required. First, we try to reuse an active GO. However,
++       * if it is not present, we will try to reactivate an existing
++       * persistent group and set group_iface to 1, so the caller will know
++       * that the pending interface should be used.
++       */
++      *group_iface = 0;
 +      s = wpas_p2p_group_go_ssid(wpa_s, bssid);
 +      if (!s) {
 +              s = wpas_p2p_get_persistent_go(wpa_s);
++              *group_iface = wpas_p2p_create_iface(wpa_s);
 +              if (s)
 +                      os_memcpy(bssid, s->bssid, ETH_ALEN);
++              else
++                      return 0;
 +      }
 +
-                                   int prov_start, const char *session_info)
 +      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_get_feat_cap_str(char *buf, size_t buf_len,
++                                     const u8 *feat_cap, size_t feat_cap_len)
++{
++      static const char pref[] = " feature_cap=";
++      int ret;
++
++      buf[0] = '\0';
++
++      /*
++       * We expect a feature capability to contain at least one byte to be
++       * reported. The string buffer provided by the caller function is
++       * expected to be big enough to contain all bytes of the attribute for
++       * known specifications. This function truncates the reported bytes if
++       * the feature capability data exceeds the string buffer size.
++       */
++      if (!feat_cap || !feat_cap_len || buf_len < sizeof(pref) + 2)
++              return;
++
++      os_memcpy(buf, pref, sizeof(pref));
++      ret = wpa_snprintf_hex(&buf[sizeof(pref) - 1],
++                             buf_len - sizeof(pref) + 1,
++                             feat_cap, feat_cap_len);
++
++      if (ret != (2 * (int) feat_cap_len))
++              wpa_printf(MSG_WARNING, "P2PS feature_cap bytes truncated");
++}
++
++
 +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,
-                                      " dev_passwd_id=%d",
++                                  int prov_start, const char *session_info,
++                                  const u8 *feat_cap, size_t feat_cap_len)
 +{
 +      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;
++      char feat_cap_str[256];
 +
 +      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;
 +
++      wpas_p2ps_get_feat_cap_str(feat_cap_str, sizeof(feat_cap_str),
++                                 feat_cap, feat_cap_len);
++
 +      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
-                                      passwd_id);
++                                     " dev_passwd_id=%d%s",
 +                                     MAC2STR(dev), adv_id, conncap,
 +                                     MAC2STR(adv_mac),
 +                                     ses_id, MAC2STR(ses_mac),
-                                      " dev_passwd_id=%d info='%s'",
++                                     passwd_id, feat_cap_str);
 +              } 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
-                                      passwd_id, session_info);
++                                     " dev_passwd_id=%d info='%s'%s",
 +                                     MAC2STR(dev), adv_id, conncap,
 +                                     MAC2STR(adv_mac),
 +                                     ses_id, MAC2STR(ses_mac),
-                              " session=%x mac=" MACSTR,
++                                     passwd_id, session_info, feat_cap_str);
 +              }
 +              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
-                              ses_id, MAC2STR(ses_mac));
++                             " session=%x mac=" MACSTR "%s",
 +                             MAC2STR(dev), status,
 +                             adv_id, MAC2STR(adv_mac),
-                              " persist=%d",
++                             ses_id, MAC2STR(ses_mac), feat_cap_str);
 +              return;
 +      }
 +
 +      /* Clean up stale persistent groups with this device */
 +      s = wpas_p2p_get_persistent(wpa_s, dev, persist_ssid,
 +                                  persist_ssid_size);
++
++      if (persist_ssid && s && s->mode != WPAS_MODE_P2P_GO &&
++          is_zero_ether_addr(grp_mac)) {
++              wpa_dbg(wpa_s, MSG_ERROR,
++                      "P2P: Peer device is a GO in a persistent group, but it did not provide the intended MAC address");
++              return;
++      }
++
 +      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
-                              ses_id, MAC2STR(ses_mac), s->id);
++                             " persist=%d%s",
 +                             MAC2STR(dev), status,
 +                             adv_id, MAC2STR(adv_mac),
-               const char *go_ifname = NULL;
++                             ses_id, MAC2STR(ses_mac), s->id, feat_cap_str);
 +              return;
 +      }
 +
 +      if (conncap == P2PS_SETUP_GROUP_OWNER) {
-                       if (wpa_s->conf->p2p_no_group_iface)
-                               go_ifname = wpa_s->ifname;
++              /*
++               * We need to copy the interface name. Simply saving a
++               * pointer isn't enough, since if we use pending_interface_name
++               * it will be overwritten when the group is added.
++               */
++              char go_ifname[100];
++
++              go_ifname[0] = '\0';
 +              if (!go_wpa_s) {
 +                      wpa_s->global->pending_p2ps_group = 1;
 +
-                               go_ifname = wpa_s->pending_interface_name;
++                      if (!wpas_p2p_create_iface(wpa_s))
++                              os_memcpy(go_ifname, wpa_s->ifname,
++                                        sizeof(go_ifname));
 +                      else if (wpa_s->pending_interface_name[0])
-                       if (!go_ifname) {
++                              os_memcpy(go_ifname,
++                                        wpa_s->pending_interface_name,
++                                        sizeof(go_ifname));
 +
-                                       NULL, adv_id, ses_id, 0, 0,
-                                       NULL, 0, 0, 0, NULL);
++                      if (!go_ifname[0]) {
 +                              wpas_p2ps_prov_complete(
 +                                      wpa_s, P2P_SC_FAIL_UNKNOWN_GROUP,
 +                                      dev, adv_mac, ses_mac,
-                                       0);
++                                      grp_mac, adv_id, ses_id, 0, 0,
++                                      NULL, 0, 0, 0, NULL, NULL, 0);
 +                              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 :
-                               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));
++                                      0, 0);
 +                      } else if (response_done) {
 +                              wpas_p2p_group_add(wpa_s, 1, 0, 0, 0);
 +                      }
 +
 +                      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, grp_mac,
++                                        ETH_ALEN);
++                              wpa_s->p2ps_method_config_any = 1;
 +                      }
 +              } else 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));
++                      os_memcpy(go_ifname, go_wpa_s->ifname,
++                                sizeof(go_ifname));
++
++                      if (is_zero_ether_addr(grp_mac)) {
++                              wpa_dbg(go_wpa_s, MSG_DEBUG,
++                                      "P2P: Setting PIN-1 for ANY");
++                              wpa_supplicant_ap_wps_pin(go_wpa_s, NULL,
++                                                        "12345670", NULL, 0,
++                                                        0);
++                      } else {
++                              wpa_dbg(go_wpa_s, MSG_DEBUG,
++                                      "P2P: Setting PIN-1 for " MACSTR,
++                                      MAC2STR(grp_mac));
++                              wpa_supplicant_ap_wps_pin(go_wpa_s, grp_mac,
++                                                        "12345670", NULL, 0,
++                                                        0);
++                      }
 +
-                              " dev_passwd_id=%d go=%s",
++                      os_memcpy(wpa_s->p2ps_join_addr, grp_mac, ETH_ALEN);
++                      wpa_s->p2ps_method_config_any = 1;
 +              }
 +
 +              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
-                              passwd_id, go_ifname);
++                             " dev_passwd_id=%d go=%s%s",
 +                             MAC2STR(dev), status, conncap,
 +                             adv_id, MAC2STR(adv_mac),
 +                             ses_id, MAC2STR(ses_mac),
-                              " dev_passwd_id=%d join=" MACSTR,
++                             passwd_id, go_ifname, feat_cap_str);
 +              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
-                              passwd_id, MAC2STR(grp_mac));
++                             " dev_passwd_id=%d join=" MACSTR "%s",
 +                             MAC2STR(dev), status, conncap,
 +                             adv_id, MAC2STR(adv_mac),
 +                             ses_id, MAC2STR(ses_mac),
-                              " dev_passwd_id=%d",
++                             passwd_id, MAC2STR(grp_mac), feat_cap_str);
 +      } 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
-                              passwd_id);
++                             " dev_passwd_id=%d%s",
 +                             MAC2STR(dev), status, conncap,
 +                             adv_id, MAC2STR(adv_mac),
 +                             ses_id, MAC2STR(ses_mac),
-                       P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE : 0);
++                             passwd_id, feat_cap_str);
 +      }
 +}
 +
 +
 +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 ?
-                       wpa_printf(MSG_ERROR,
-                                  "P2P: Failed to select random social channel as listen channel");
-                       return -1;
++                      P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE : 0, 0);
 +      } else {
 +              wpas_p2p_group_add(wpa_s, 1, 0, 0, 0);
 +      }
 +
 +      return 1;
 +}
 +
 +
++static int wpas_p2p_get_pref_freq_list(void *ctx, int go,
++                                     unsigned int *len,
++                                     unsigned int *freq_list)
++{
++      struct wpa_supplicant *wpa_s = ctx;
++
++      return wpa_drv_get_pref_freq_list(wpa_s, go ? WPA_IF_P2P_GO :
++                                        WPA_IF_P2P_CLIENT, len, freq_list);
++}
++
++
 +/**
 + * wpas_p2p_init - Initialize P2P module for %wpa_supplicant
 + * @global: Pointer to global data from wpa_supplicant_init()
 + * @wpa_s: Pointer to wpa_supplicant data from wpa_supplicant_add_iface()
 + * Returns: 0 on success, -1 on failure
 + */
 +int wpas_p2p_init(struct wpa_global *global, struct wpa_supplicant *wpa_s)
 +{
 +      struct p2p_config p2p;
 +      int i;
 +
 +      if (wpa_s->conf->p2p_disabled)
 +              return 0;
 +
 +      if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_CAPABLE))
 +              return 0;
 +
 +      if (global->p2p)
 +              return 0;
 +
 +      os_memset(&p2p, 0, sizeof(p2p));
 +      p2p.cb_ctx = wpa_s;
 +      p2p.debug_print = wpas_p2p_debug_print;
 +      p2p.p2p_scan = wpas_p2p_scan;
 +      p2p.send_action = wpas_send_action;
 +      p2p.send_action_done = wpas_send_action_done;
 +      p2p.go_neg_completed = wpas_go_neg_completed;
 +      p2p.go_neg_req_rx = wpas_go_neg_req_rx;
 +      p2p.dev_found = wpas_dev_found;
 +      p2p.dev_lost = wpas_dev_lost;
 +      p2p.find_stopped = wpas_find_stopped;
 +      p2p.start_listen = wpas_start_listen;
 +      p2p.stop_listen = wpas_stop_listen;
 +      p2p.send_probe_resp = wpas_send_probe_resp;
 +      p2p.sd_request = wpas_sd_request;
 +      p2p.sd_response = wpas_sd_response;
 +      p2p.prov_disc_req = wpas_prov_disc_req;
 +      p2p.prov_disc_resp = wpas_prov_disc_resp;
 +      p2p.prov_disc_fail = wpas_prov_disc_fail;
 +      p2p.invitation_process = wpas_invitation_process;
 +      p2p.invitation_received = wpas_invitation_received;
 +      p2p.invitation_result = wpas_invitation_result;
 +      p2p.get_noa = wpas_get_noa;
 +      p2p.go_connected = wpas_go_connected;
 +      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;
++      p2p.get_pref_freq_list = wpas_p2p_get_pref_freq_list;
 +
 +      os_memcpy(wpa_s->global->p2p_dev_addr, wpa_s->own_addr, ETH_ALEN);
 +      os_memcpy(p2p.dev_addr, wpa_s->global->p2p_dev_addr, ETH_ALEN);
 +      p2p.dev_name = wpa_s->conf->device_name;
 +      p2p.manufacturer = wpa_s->conf->manufacturer;
 +      p2p.model_name = wpa_s->conf->model_name;
 +      p2p.model_number = wpa_s->conf->model_number;
 +      p2p.serial_number = wpa_s->conf->serial_number;
 +      if (wpa_s->wps) {
 +              os_memcpy(p2p.uuid, wpa_s->wps->uuid, 16);
 +              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 {
 +              /*
 +               * Pick one of the social channels randomly as the listen
 +               * channel.
 +               */
 +              if (p2p_config_get_random_social(&p2p, &p2p.reg_class,
 +                                               &p2p.channel) != 0) {
-                               int *force_freq, int *pref_freq, int go)
++                      wpa_printf(MSG_INFO,
++                                 "P2P: No social channels supported by the driver - do not enable P2P");
++                      return 0;
 +              }
 +              p2p.channel_forced = 0;
 +      }
 +      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) {
 +              p2p.op_reg_class = wpa_s->conf->p2p_oper_reg_class;
 +              p2p.op_channel = wpa_s->conf->p2p_oper_channel;
 +              p2p.cfg_op_channel = 1;
 +              wpa_printf(MSG_DEBUG, "P2P: Configured operating channel: "
 +                         "%d:%d", p2p.op_reg_class, p2p.op_channel);
 +
 +      } else {
 +              /*
 +               * 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.
 +               */
 +              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);
 +      }
 +
 +      if (wpa_s->conf->p2p_pref_chan && wpa_s->conf->num_p2p_pref_chan) {
 +              p2p.pref_chan = wpa_s->conf->p2p_pref_chan;
 +              p2p.num_pref_chan = wpa_s->conf->num_p2p_pref_chan;
 +      }
 +
 +      if (wpa_s->conf->country[0] && wpa_s->conf->country[1]) {
 +              os_memcpy(p2p.country, wpa_s->conf->country, 2);
 +              p2p.country[2] = 0x04;
 +      } else
 +              os_memcpy(p2p.country, "XX\x04", 3);
 +
 +      os_memcpy(p2p.pri_dev_type, wpa_s->conf->device_type,
 +                WPS_DEV_TYPE_LEN);
 +
 +      p2p.num_sec_dev_types = wpa_s->conf->num_sec_device_types;
 +      os_memcpy(p2p.sec_dev_type, wpa_s->conf->sec_device_type,
 +                p2p.num_sec_dev_types * WPS_DEV_TYPE_LEN);
 +
 +      p2p.concurrent_operations = !!(wpa_s->drv_flags &
 +                                     WPA_DRIVER_FLAGS_P2P_CONCURRENT);
 +
 +      p2p.max_peers = 100;
 +
 +      if (wpa_s->conf->p2p_ssid_postfix) {
 +              p2p.ssid_postfix_len =
 +                      os_strlen(wpa_s->conf->p2p_ssid_postfix);
 +              if (p2p.ssid_postfix_len > sizeof(p2p.ssid_postfix))
 +                      p2p.ssid_postfix_len = sizeof(p2p.ssid_postfix);
 +              os_memcpy(p2p.ssid_postfix, wpa_s->conf->p2p_ssid_postfix,
 +                        p2p.ssid_postfix_len);
 +      }
 +
 +      p2p.p2p_intra_bss = wpa_s->conf->p2p_intra_bss;
 +
 +      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;
 +      global->p2p_init_wpa_s = wpa_s;
 +
 +      for (i = 0; i < MAX_WPS_VENDOR_EXT; i++) {
 +              if (wpa_s->conf->wps_vendor_ext[i] == NULL)
 +                      continue;
 +              p2p_add_wps_vendor_extension(
 +                      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;
 +}
 +
 +
 +/**
 + * wpas_p2p_deinit - Deinitialize per-interface P2P data
 + * @wpa_s: Pointer to wpa_supplicant data from wpa_supplicant_add_iface()
 + *
 + * This function deinitialize per-interface P2P data.
 + */
 +void wpas_p2p_deinit(struct wpa_supplicant *wpa_s)
 +{
 +      if (wpa_s->driver && wpa_s->drv_priv)
 +              wpa_drv_probe_req_report(wpa_s, 0);
 +
 +      if (wpa_s->go_params) {
 +              /* Clear any stored provisioning info */
 +              p2p_clear_provisioning_info(
 +                      wpa_s->global->p2p,
 +                      wpa_s->go_params->peer_device_addr);
 +      }
 +
 +      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 */
 +}
 +
 +
 +/**
 + * wpas_p2p_deinit_global - Deinitialize global P2P module
 + * @global: Pointer to global data from wpa_supplicant_init()
 + *
 + * This function deinitializes the global (per device) P2P module.
 + */
 +static void wpas_p2p_deinit_global(struct wpa_global *global)
 +{
 +      struct wpa_supplicant *wpa_s, *tmp;
 +
 +      wpa_s = global->ifaces;
 +
 +      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)
 +              wpa_s = wpa_s->next;
 +      while (wpa_s) {
 +              tmp = global->ifaces;
 +              while (tmp &&
 +                     (tmp == wpa_s ||
 +                      tmp->p2p_group_interface == NOT_P2P_GROUP_INTERFACE)) {
 +                      tmp = tmp->next;
 +              }
 +              if (tmp == NULL)
 +                      break;
 +              /* Disconnect from the P2P group and deinit the interface */
 +              wpas_p2p_disconnect(tmp);
 +      }
 +
 +      /*
 +       * Deinit GO data on any possibly remaining interface (if main
 +       * interface is used as GO).
 +       */
 +      for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
 +              if (wpa_s->ap_iface)
 +                      wpas_p2p_group_deinit(wpa_s);
 +      }
 +
 +      p2p_deinit(global->p2p);
 +      global->p2p = NULL;
 +      global->p2p_init_wpa_s = NULL;
 +}
 +
 +
 +static int wpas_p2p_create_iface(struct wpa_supplicant *wpa_s)
 +{
 +      if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE) &&
 +          wpa_s->conf->p2p_no_group_iface)
 +              return 0; /* separate interface disabled per configuration */
 +      if (wpa_s->drv_flags &
 +          (WPA_DRIVER_FLAGS_P2P_DEDICATED_INTERFACE |
 +           WPA_DRIVER_FLAGS_P2P_MGMT_AND_NON_P2P))
 +              return 1; /* P2P group requires a new interface in every case
 +                         */
 +      if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_CONCURRENT))
 +              return 0; /* driver does not support concurrent operations */
 +      if (wpa_s->global->ifaces->next)
 +              return 1; /* more that one interface already in use */
 +      if (wpa_s->wpa_state >= WPA_AUTHENTICATING)
 +              return 1; /* this interface is already in use */
 +      return 0;
 +}
 +
 +
 +static int wpas_p2p_start_go_neg(struct wpa_supplicant *wpa_s,
 +                               const u8 *peer_addr,
 +                               enum p2p_wps_method wps_method,
 +                               int go_intent, const u8 *own_interface_addr,
 +                               unsigned int force_freq, int persistent_group,
 +                               struct wpa_ssid *ssid, unsigned int pref_freq)
 +{
 +      if (persistent_group && wpa_s->conf->persistent_reconnect)
 +              persistent_group = 2;
 +
 +      /*
 +       * 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.
 +       */
 +      p2p_set_config_timeout(wpa_s->global->p2p,
 +                             wpa_s->p2p_go_ht40 ? 255 : 100, 20);
 +
 +      return p2p_connect(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,
 +                         wpa_s->p2p_pd_before_go_neg, pref_freq,
 +                         wps_method == WPS_NFC ? wpa_s->p2p_oob_dev_pw_id :
 +                         0);
 +}
 +
 +
 +static int wpas_p2p_auth_go_neg(struct wpa_supplicant *wpa_s,
 +                              const u8 *peer_addr,
 +                              enum p2p_wps_method wps_method,
 +                              int go_intent, const u8 *own_interface_addr,
 +                              unsigned int force_freq, int persistent_group,
 +                              struct wpa_ssid *ssid, unsigned int pref_freq)
 +{
 +      if (persistent_group && wpa_s->conf->persistent_reconnect)
 +              persistent_group = 2;
 +
 +      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,
 +                           wps_method == WPS_NFC ? wpa_s->p2p_oob_dev_pw_id :
 +                           0);
 +}
 +
 +
 +static void wpas_p2p_check_join_scan_limit(struct wpa_supplicant *wpa_s)
 +{
 +      wpa_s->p2p_join_scan_count++;
 +      wpa_printf(MSG_DEBUG, "P2P: Join scan attempt %d",
 +                 wpa_s->p2p_join_scan_count);
 +      if (wpa_s->p2p_join_scan_count > P2P_MAX_JOIN_SCAN_ATTEMPTS) {
 +              wpa_printf(MSG_DEBUG, "P2P: Failed to find GO " MACSTR
 +                         " for join operationg - stop join attempt",
 +                         MAC2STR(wpa_s->pending_join_iface_addr));
 +              eloop_cancel_timeout(wpas_p2p_join_scan, wpa_s, NULL);
 +              if (wpa_s->p2p_auto_pd) {
 +                      wpa_s->p2p_auto_pd = 0;
 +                      wpa_msg_global(wpa_s, MSG_INFO,
 +                                     P2P_EVENT_PROV_DISC_FAILURE
 +                                     " p2p_dev_addr=" MACSTR " status=N/A",
 +                                     MAC2STR(wpa_s->pending_join_dev_addr));
 +                      return;
 +              }
 +              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)
 +{
 +      int res;
 +      unsigned int num, i;
 +      struct wpa_used_freq_data *freqs;
 +
 +      if (wpas_p2p_num_unused_channels(wpa_s) > 0) {
 +              /* Multiple channels are supported and not all are in use */
 +              return 0;
 +      }
 +
 +      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);
 +
 +      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;
 +              }
 +      }
 +
 +      wpa_printf(MSG_DEBUG, "P2P: No valid operating frequencies");
 +      res = 1;
 +
 +exit_free:
 +      os_free(freqs);
 +      return res;
 +}
 +
 +
 +static int wpas_p2p_peer_go(struct wpa_supplicant *wpa_s,
 +                          const u8 *peer_dev_addr)
 +{
 +      struct wpa_bss *bss;
 +      int updated;
 +
 +      bss = wpa_bss_get_p2p_dev_addr(wpa_s, peer_dev_addr);
 +      if (bss == NULL)
 +              return -1;
 +      if (bss->last_update_idx < wpa_s->bss_update_idx) {
 +              wpa_printf(MSG_DEBUG, "P2P: Peer BSS entry not updated in the "
 +                         "last scan");
 +              return 0;
 +      }
 +
 +      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,
 +                 updated ? "": "not ");
 +
 +      return updated;
 +}
 +
 +
 +static void wpas_p2p_scan_res_join(struct wpa_supplicant *wpa_s,
 +                                 struct wpa_scan_results *scan_res)
 +{
 +      struct wpa_bss *bss = NULL;
 +      int freq;
 +      u8 iface_addr[ETH_ALEN];
 +
 +      eloop_cancel_timeout(wpas_p2p_join_scan, wpa_s, NULL);
 +
 +      if (wpa_s->global->p2p_disabled)
 +              return;
 +
 +      wpa_printf(MSG_DEBUG, "P2P: Scan results received (%d BSS) for %sjoin",
 +                 scan_res ? (int) scan_res->num : -1,
 +                 wpa_s->p2p_auto_join ? "auto_" : "");
 +
 +      if (scan_res)
 +              wpas_p2p_scan_res_handler(wpa_s, scan_res);
 +
 +      if (wpa_s->p2p_auto_pd) {
 +              int join = wpas_p2p_peer_go(wpa_s,
 +                                          wpa_s->pending_join_dev_addr);
 +              if (join == 0 &&
 +                  wpa_s->auto_pd_scan_retry < P2P_AUTO_PD_SCAN_ATTEMPTS) {
 +                      wpa_s->auto_pd_scan_retry++;
 +                      bss = wpa_bss_get_bssid_latest(
 +                              wpa_s, wpa_s->pending_join_dev_addr);
 +                      if (bss) {
 +                              freq = bss->freq;
 +                              wpa_printf(MSG_DEBUG, "P2P: Scan retry %d for "
 +                                         "the peer " MACSTR " at %d MHz",
 +                                         wpa_s->auto_pd_scan_retry,
 +                                         MAC2STR(wpa_s->
 +                                                 pending_join_dev_addr),
 +                                         freq);
 +                              wpas_p2p_join_scan_req(wpa_s, freq, NULL, 0);
 +                              return;
 +                      }
 +              }
 +
 +              if (join < 0)
 +                      join = 0;
 +
 +              wpa_s->p2p_auto_pd = 0;
 +              wpa_s->pending_pd_use = join ? AUTO_PD_JOIN : AUTO_PD_GO_NEG;
 +              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, NULL,
 +                                    wpa_s->pending_pd_config_methods, join,
 +                                    0, wpa_s->user_initiated_pd) < 0) {
 +                      wpa_s->p2p_auto_pd = 0;
 +                      wpa_msg_global(wpa_s, MSG_INFO,
 +                                     P2P_EVENT_PROV_DISC_FAILURE
 +                                     " p2p_dev_addr=" MACSTR " status=N/A",
 +                                     MAC2STR(wpa_s->pending_join_dev_addr));
 +              }
 +              return;
 +      }
 +
 +      if (wpa_s->p2p_auto_join) {
 +              int join = wpas_p2p_peer_go(wpa_s,
 +                                          wpa_s->pending_join_dev_addr);
 +              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,
 +                                       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_vht);
 +                      return;
 +              }
 +
 +              wpa_printf(MSG_DEBUG, "P2P: Peer was found running GO%s -> "
 +                         "try to join the group", join ? "" :
 +                         " in older scan");
 +              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,
 +                               wpa_s->pending_join_iface_addr);
 +      if (freq < 0 &&
 +          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
 +          && !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",
 +                         MAC2STR(wpa_s->pending_join_iface_addr),
 +                         MAC2STR(iface_addr));
 +              os_memcpy(wpa_s->pending_join_iface_addr, iface_addr,
 +                        ETH_ALEN);
 +
 +              freq = p2p_get_oper_freq(wpa_s->global->p2p,
 +                                       wpa_s->pending_join_iface_addr);
 +      }
 +      if (freq >= 0) {
 +              wpa_printf(MSG_DEBUG, "P2P: Target GO operating frequency "
 +                         "from P2P peer table: %d MHz", freq);
 +      }
 +      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) {
++              u8 dev_addr[ETH_ALEN];
++
 +              freq = bss->freq;
 +              wpa_printf(MSG_DEBUG, "P2P: Target GO operating frequency "
 +                         "from BSS table: %d MHz (SSID %s)", freq,
 +                         wpa_ssid_txt(bss->ssid, bss->ssid_len));
++              if (p2p_parse_dev_addr((const u8 *) (bss + 1), bss->ie_len,
++                                     dev_addr) == 0 &&
++                  os_memcmp(wpa_s->pending_join_dev_addr,
++                            wpa_s->pending_join_iface_addr, ETH_ALEN) == 0 &&
++                  os_memcmp(dev_addr, wpa_s->pending_join_dev_addr,
++                            ETH_ALEN) != 0) {
++                      wpa_printf(MSG_DEBUG,
++                                 "P2P: Update target GO device address based on BSS entry: " MACSTR " (was " MACSTR ")",
++                                 MAC2STR(dev_addr),
++                                 MAC2STR(wpa_s->pending_join_dev_addr));
++                      os_memcpy(wpa_s->pending_join_dev_addr, dev_addr,
++                                ETH_ALEN);
++              }
 +      }
 +      if (freq > 0) {
 +              u16 method;
 +
 +              if (wpas_check_freq_conflict(wpa_s, freq) > 0) {
 +                      wpa_msg_global(wpa_s->parent, MSG_INFO,
 +                                     P2P_EVENT_GROUP_FORMATION_FAILURE
 +                                     "reason=FREQ_CONFLICT");
++                      wpas_notify_p2p_group_formation_failure(
++                              wpa_s, "FREQ_CONFLICT");
 +                      return;
 +              }
 +
 +              wpa_printf(MSG_DEBUG, "P2P: Send Provision Discovery Request "
 +                         "prior to joining an existing group (GO " MACSTR
 +                         " freq=%u MHz)",
 +                         MAC2STR(wpa_s->pending_join_dev_addr), freq);
 +              wpa_s->pending_pd_before_join = 1;
 +
 +              switch (wpa_s->pending_join_wps_method) {
 +              case WPS_PIN_DISPLAY:
 +                      method = WPS_CONFIG_KEYPAD;
 +                      break;
 +              case WPS_PIN_KEYPAD:
 +                      method = WPS_CONFIG_DISPLAY;
 +                      break;
 +              case WPS_PBC:
 +                      method = WPS_CONFIG_PUSHBUTTON;
 +                      break;
++              case WPS_P2PS:
++                      method = WPS_CONFIG_P2PS;
++                      break;
 +              default:
 +                      method = 0;
 +                      break;
 +              }
 +
 +              if ((p2p_get_provisioning_info(wpa_s->global->p2p,
 +                                             wpa_s->pending_join_dev_addr) ==
 +                   method)) {
 +                      /*
 +                       * We have already performed provision discovery for
 +                       * joining the group. Proceed directly to join
 +                       * operation without duplicated provision discovery. */
 +                      wpa_printf(MSG_DEBUG, "P2P: Provision discovery "
 +                                 "with " MACSTR " already done - proceed to "
 +                                 "join",
 +                                 MAC2STR(wpa_s->pending_join_dev_addr));
 +                      wpa_s->pending_pd_before_join = 0;
 +                      goto start;
 +              }
 +
 +              if (p2p_prov_disc_req(wpa_s->global->p2p,
 +                                    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 "
 +                                 "existing group");
 +                      wpa_s->pending_pd_before_join = 0;
 +                      goto start;
 +              }
 +              return;
 +      }
 +
 +      wpa_printf(MSG_DEBUG, "P2P: Failed to find BSS/GO - try again later");
 +      eloop_cancel_timeout(wpas_p2p_join_scan, wpa_s, NULL);
 +      eloop_register_timeout(1, 0, wpas_p2p_join_scan, wpa_s, NULL);
 +      wpas_p2p_check_join_scan_limit(wpa_s);
 +      return;
 +
 +start:
 +      /* Start join operation immediately */
 +      wpas_p2p_join_start(wpa_s, 0, NULL, 0);
 +}
 +
 +
 +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;
 +      struct wpabuf *wps_ie, *ies;
 +      size_t ielen;
 +      int freqs[2] = { 0, 0 };
 +
 +      os_memset(&params, 0, sizeof(params));
 +
 +      /* P2P Wildcard SSID */
 +      params.num_ssids = 1;
 +      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,
 +                                      wpa_s->wps->uuid, WPS_REQ_ENROLLEE, 0,
 +                                      NULL);
 +      if (wps_ie == NULL) {
 +              wpas_p2p_scan_res_join(wpa_s, NULL);
 +              return;
 +      }
 +
 +      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);
 +              wpas_p2p_scan_res_join(wpa_s, NULL);
 +              return;
 +      }
 +      wpabuf_put_buf(ies, wps_ie);
 +      wpabuf_free(wps_ie);
 +
 +      p2p_scan_ie(wpa_s->global->p2p, ies, NULL);
 +
 +      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;
 +      }
 +
 +      /*
 +       * Run a scan to update BSS table and start Provision Discovery once
 +       * the new scan results become available.
 +       */
 +      ret = wpa_drv_scan(wpa_s, &params);
 +      if (!ret) {
 +              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);
 +
 +      if (ret) {
 +              wpa_printf(MSG_DEBUG, "P2P: Failed to start scan for join - "
 +                         "try again later");
 +              eloop_cancel_timeout(wpas_p2p_join_scan, wpa_s, NULL);
 +              eloop_register_timeout(1, 0, wpas_p2p_join_scan, wpa_s, NULL);
 +              wpas_p2p_check_join_scan_limit(wpa_s);
 +      }
 +}
 +
 +
 +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, 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 op_freq,
 +                       const u8 *ssid, size_t ssid_len)
 +{
 +      wpa_printf(MSG_DEBUG, "P2P: Request to join existing group (iface "
 +                 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;
 +      os_memcpy(wpa_s->pending_join_iface_addr, iface_addr, ETH_ALEN);
 +      os_memcpy(wpa_s->pending_join_dev_addr, dev_addr, ETH_ALEN);
 +      wpa_s->pending_join_wps_method = wps_method;
 +
 +      /* Make sure we are not running find during connection establishment */
 +      wpas_p2p_stop_find(wpa_s);
 +
 +      wpa_s->p2p_join_scan_count = 0;
 +      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, int freq,
 +                             const u8 *ssid, size_t ssid_len)
 +{
 +      struct wpa_supplicant *group;
 +      struct p2p_go_neg_results res;
 +      struct wpa_bss *bss;
 +
 +      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;
 +      } else {
 +              /*
 +               * Need to mark the current interface for p2p_group_formation
 +               * when a separate group interface is not used. This is needed
 +               * to allow p2p_cancel stop a pending p2p_connect-join.
 +               * wpas_p2p_init_group_interface() addresses this for the case
 +               * where a separate group interface is used.
 +               */
 +              wpa_s->global->p2p_group_formation = wpa_s;
 +      }
 +
 +      group->p2p_in_provisioning = 1;
 +      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;
 +      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) {
 +              wpa_printf(MSG_DEBUG, "P2P: Cancel remain-on-channel prior to "
 +                         "starting client");
 +              wpa_drv_cancel_remain_on_channel(wpa_s);
 +              wpa_s->off_channel_freq = 0;
 +              wpa_s->roc_waiting_drv_freq = 0;
 +      }
 +      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;
 +}
 +
 +
 +static int wpas_p2p_setup_freqs(struct wpa_supplicant *wpa_s, int freq,
-       unsigned int freq_in_use = 0, num, i;
++                              int *force_freq, int *pref_freq, int go,
++                              unsigned int *pref_freq_list,
++                              unsigned int *num_pref_freq)
 +{
 +      struct wpa_used_freq_data *freqs;
 +      int res, best_freq, num_unused;
-                                  go_intent == 15);
++      unsigned int freq_in_use = 0, num, i, max_pref_freq;
++
++      max_pref_freq = *num_pref_freq;
++      *num_pref_freq = 0;
 +
 +      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);
 +
 +      /*
 +       * 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);
 +
++      if (!wpa_s->conf->num_p2p_pref_chan && *pref_freq == 0) {
++              enum wpa_driver_if_type iface_type;
++
++              if (go)
++                      iface_type = WPA_IF_P2P_GO;
++              else
++                      iface_type = WPA_IF_P2P_CLIENT;
++
++              wpa_printf(MSG_DEBUG, "P2P: best_freq=%d, go=%d",
++                         best_freq, go);
++
++              res = wpa_drv_get_pref_freq_list(wpa_s, iface_type,
++                                               &max_pref_freq,
++                                               pref_freq_list);
++              if (!res && max_pref_freq > 0) {
++                      *num_pref_freq = max_pref_freq;
++                      i = 0;
++                      while (wpas_p2p_disallowed_freq(wpa_s->global,
++                                                      pref_freq_list[i]) &&
++                             i < *num_pref_freq) {
++                              wpa_printf(MSG_DEBUG,
++                                         "P2P: preferred_freq_list[%d]=%d is disallowed",
++                                         i, pref_freq_list[i]);
++                              i++;
++                      }
++                      if (i != *num_pref_freq) {
++                              best_freq = pref_freq_list[i];
++                              wpa_printf(MSG_DEBUG,
++                                         "P2P: Using preferred_freq_list[%d]=%d",
++                                         i, best_freq);
++                      } else {
++                              wpa_printf(MSG_DEBUG,
++                                         "P2P: All driver preferred frequencies are disallowed for P2P use");
++                              *num_pref_freq = 0;
++                      }
++              } else {
++                      wpa_printf(MSG_DEBUG,
++                                 "P2P: No preferred frequency list available");
++              }
++      }
++
 +      /* 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;
 +}
 +
 +
 +/**
 + * wpas_p2p_connect - Request P2P Group Formation to be started
 + * @wpa_s: Pointer to wpa_supplicant data from wpa_supplicant_add_iface()
 + * @peer_addr: Address of the peer P2P Device
 + * @pin: PIN to use during provisioning or %NULL to indicate PBC mode
 + * @persistent_group: Whether to create a persistent group
 + * @auto_join: Whether to select join vs. GO Negotiation automatically
 + * @join: Whether to join an existing group (as a client) instead of starting
 + *    Group Owner negotiation; @peer_addr is BSSID in that case
 + * @auth: Whether to only authorize the connection instead of doing that and
 + *    initiating Group Owner negotiation
 + * @go_intent: GO Intent or -1 to use default
 + * @freq: Frequency for the group or 0 for auto-selection
 + * @persistent_id: Persistent group credentials to use for forcing GO
 + *    parameters or -1 to generate new values (SSID/passphrase)
 + * @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
 + */
 +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 vht)
 +{
 +      int force_freq = 0, pref_freq = 0;
 +      int ret = 0, res;
 +      enum wpa_driver_if_type iftype;
 +      const u8 *if_addr;
 +      struct wpa_ssid *ssid = NULL;
++      unsigned int pref_freq_list[P2P_MAX_PREF_CHANNELS], size;
 +
 +      if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
 +              return -1;
 +
 +      if (persistent_id >= 0) {
 +              ssid = wpa_config_get_network(wpa_s->conf, persistent_id);
 +              if (ssid == NULL || ssid->disabled != 2 ||
 +                  ssid->mode != WPAS_MODE_P2P_GO)
 +                      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;
++      wpa_s->p2ps_method_config_any = 0;
 +
 +      if (go_intent < 0)
 +              go_intent = wpa_s->conf->p2p_go_intent;
 +
 +      if (!auth)
 +              wpa_s->p2p_long_listen = 0;
 +
 +      wpa_s->p2p_wps_method = wps_method;
 +      wpa_s->p2p_persistent_group = !!persistent_group;
 +      wpa_s->p2p_persistent_id = persistent_id;
 +      wpa_s->p2p_go_intent = go_intent;
 +      wpa_s->p2p_connect_freq = freq;
 +      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();
 +              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
 +              wpa_s->p2p_pin[0] = '\0';
 +
 +      if (join || auto_join) {
 +              u8 iface_addr[ETH_ALEN], dev_addr[ETH_ALEN];
 +              if (auth) {
 +                      wpa_printf(MSG_DEBUG, "P2P: Authorize invitation to "
 +                                 "connect a running group from " MACSTR,
 +                                 MAC2STR(peer_addr));
 +                      os_memcpy(wpa_s->p2p_auth_invite, peer_addr, ETH_ALEN);
 +                      return ret;
 +              }
 +              os_memcpy(dev_addr, peer_addr, ETH_ALEN);
 +              if (p2p_get_interface_addr(wpa_s->global->p2p, peer_addr,
 +                                         iface_addr) < 0) {
 +                      os_memcpy(iface_addr, peer_addr, ETH_ALEN);
 +                      p2p_get_dev_addr(wpa_s->global->p2p, peer_addr,
 +                                       dev_addr);
 +              }
 +              if (auto_join) {
 +                      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,
 +                                 wpa_s->p2p_auto_started.usec);
 +              }
 +              wpa_s->user_initiated_pd = 1;
 +              if (wpas_p2p_join(wpa_s, iface_addr, dev_addr, wps_method,
 +                                auto_join, freq, NULL, 0) < 0)
 +                      return -1;
 +              return ret;
 +      }
 +
++      size = P2P_MAX_PREF_CHANNELS;
 +      res = wpas_p2p_setup_freqs(wpa_s, freq, &force_freq, &pref_freq,
-       } else
++                                 go_intent == 15, pref_freq_list, &size);
 +      if (res)
 +              return res;
 +      wpas_p2p_set_own_freq_preference(wpa_s,
 +                                       force_freq ? force_freq : pref_freq);
 +
++      p2p_set_own_pref_freq_list(wpa_s->global->p2p, pref_freq_list, size);
++
 +      wpa_s->create_p2p_iface = wpas_p2p_create_iface(wpa_s);
 +
 +      if (wpa_s->create_p2p_iface) {
 +              /* 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;
- static int wpas_p2p_select_freq_no_pref(struct wpa_supplicant *wpa_s,
-                                       struct p2p_go_neg_results *params,
-                                       const struct p2p_channels *channels)
++      } else {
 +              if_addr = wpa_s->own_addr;
++              os_memset(wpa_s->go_dev_addr, 0, ETH_ALEN);
++      }
 +
 +      if (auth) {
 +              if (wpas_p2p_auth_go_neg(wpa_s, peer_addr, wps_method,
 +                                       go_intent, if_addr,
 +                                       force_freq, persistent_group, ssid,
 +                                       pref_freq) < 0)
 +                      return -1;
 +              return ret;
 +      }
 +
 +      if (wpas_p2p_start_go_neg(wpa_s, peer_addr, wps_method,
 +                                go_intent, if_addr, force_freq,
 +                                persistent_group, ssid, pref_freq) < 0) {
 +              if (wpa_s->create_p2p_iface)
 +                      wpas_p2p_remove_pending_group_interface(wpa_s);
 +              return -1;
 +      }
 +      return ret;
 +}
 +
 +
 +/**
 + * wpas_p2p_remain_on_channel_cb - Indication of remain-on-channel start
 + * @wpa_s: Pointer to wpa_supplicant data from wpa_supplicant_add_iface()
 + * @freq: Frequency of the channel in MHz
 + * @duration: Duration of the stay on the channel in milliseconds
 + *
 + * This callback is called when the driver indicates that it has started the
 + * requested remain-on-channel duration.
 + */
 +void wpas_p2p_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
 +                                 unsigned int freq, unsigned int duration)
 +{
 +      if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
 +              return;
 +      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);
 +      }
 +}
 +
 +
 +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;
 +
 +      return p2p_listen(wpa_s->global->p2p, timeout);
 +}
 +
 +
 +/**
 + * wpas_p2p_cancel_remain_on_channel_cb - Remain-on-channel timeout
 + * @wpa_s: Pointer to wpa_supplicant data from wpa_supplicant_add_iface()
 + * @freq: Frequency of the channel in MHz
 + *
 + * This callback is called when the driver indicates that a remain-on-channel
 + * operation has been completed, i.e., the duration on the requested channel
 + * has timed out.
 + */
 +void wpas_p2p_cancel_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
 +                                        unsigned int freq)
 +{
 +      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_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);
 +      }
 +}
 +
 +
 +/**
 + * wpas_p2p_group_remove - Remove a P2P group
 + * @wpa_s: Pointer to wpa_supplicant data from wpa_supplicant_add_iface()
 + * @ifname: Network interface name of the group interface or "*" to remove all
 + *    groups
 + * Returns: 0 on success, -1 on failure
 + *
 + * This function is used to remove a P2P group. This can be used to disconnect
 + * from a group in which the local end is a P2P Client or to end a P2P Group in
 + * case the local end is the Group Owner. If a virtual network interface was
 + * created for this group, that interface will be removed. Otherwise, only the
 + * configured P2P group network will be removed from the interface.
 + */
 +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;
 +              wpa_s = global->ifaces;
 +              while (wpa_s) {
 +                      prev = wpa_s;
 +                      wpa_s = wpa_s->next;
 +                      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;
 +      }
 +
 +      for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
 +              if (os_strcmp(wpa_s->ifname, ifname) == 0)
 +                      break;
 +      }
 +
 +      return wpas_p2p_disconnect_safely(wpa_s, calling_wpa_s);
 +}
 +
 +
 +static int wpas_p2p_select_go_freq(struct wpa_supplicant *wpa_s, int freq)
 +{
 +      unsigned int r;
 +
++      if (!wpa_s->conf->num_p2p_pref_chan && !freq) {
++              unsigned int i, size = P2P_MAX_PREF_CHANNELS;
++              unsigned int pref_freq_list[P2P_MAX_PREF_CHANNELS];
++              int res;
++
++              res = wpa_drv_get_pref_freq_list(wpa_s, WPA_IF_P2P_GO,
++                                               &size, pref_freq_list);
++              if (!res && size > 0) {
++                      i = 0;
++                      while (wpas_p2p_disallowed_freq(wpa_s->global,
++                                                      pref_freq_list[i]) &&
++                             i < size) {
++                              wpa_printf(MSG_DEBUG,
++                                         "P2P: preferred_freq_list[%d]=%d is disallowed",
++                                         i, pref_freq_list[i]);
++                              i++;
++                      }
++                      if (i != size) {
++                              freq = pref_freq_list[i];
++                              wpa_printf(MSG_DEBUG,
++                                         "P2P: Using preferred_freq_list[%d]=%d",
++                                         i, freq);
++                      } else {
++                              wpa_printf(MSG_DEBUG,
++                                         "P2P: All driver preferred frequencies are disallowed for P2P use");
++                      }
++              } else {
++                      wpa_printf(MSG_DEBUG,
++                                 "P2P: No preferred frequency list available");
++              }
++      }
++
 +      if (freq == 2) {
 +              wpa_printf(MSG_DEBUG, "P2P: Request to start GO on 2.4 GHz "
 +                         "band");
 +              if (wpa_s->best_24_freq > 0 &&
 +                  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 {
 +                      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);
 +              }
 +      }
 +
 +      if (freq == 5) {
 +              wpa_printf(MSG_DEBUG, "P2P: Request to start GO on 5 GHz "
 +                         "band");
 +              if (wpa_s->best_5_freq > 0 &&
 +                  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 {
 +                      if (os_get_random((u8 *) &r, sizeof(r)) < 0)
 +                              return -1;
 +                      freq = 5180 + (r % 4) * 20;
 +                      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;
 +                      }
 +                      wpa_printf(MSG_DEBUG, "P2P: Use random 5 GHz band "
 +                                 "channel: %d MHz", 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);
 +              return -1;
 +      }
 +
 +      return freq;
 +}
 +
 +
-               return -1;
++static int wpas_p2p_supported_freq_go(struct wpa_supplicant *wpa_s,
++                                    const struct p2p_channels *channels,
++                                    int freq)
++{
++      if (!wpas_p2p_disallowed_freq(wpa_s->global, freq) &&
++          p2p_supported_freq_go(wpa_s->global->p2p, freq) &&
++          freq_included(wpa_s, channels, freq))
++              return 1;
++      return 0;
++}
++
++
++static void wpas_p2p_select_go_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)
-               if (!wpas_p2p_disallowed_freq(wpa_s->global, params->freq) &&
-                   freq_included(channels, params->freq) &&
-                   p2p_supported_freq(wpa_s->global->p2p, params->freq))
++              return;
 +
 +      for (i = 0; i < 3; i++) {
 +              params->freq = 2412 + ((r + i) % 3) * 25;
-       /* try all channels in reg. class 81 */
++              if (wpas_p2p_supported_freq_go(wpa_s, channels, params->freq))
 +                      goto out;
 +      }
 +
-               if (!wpas_p2p_disallowed_freq(wpa_s->global, params->freq) &&
-                   freq_included(channels, params->freq) &&
-                   p2p_supported_freq(wpa_s->global->p2p, params->freq))
++      /* try all other channels in operating class 81 */
 +      for (i = 0; i < 11; i++) {
 +              params->freq = 2412 + i * 5;
-                   freq_included(channels, params->freq) &&
++
++              /* skip social channels; covered in the previous loop */
++              if (params->freq == 2412 ||
++                  params->freq == 2437 ||
++                  params->freq == 2462)
++                      continue;
++
++              if (wpas_p2p_supported_freq_go(wpa_s, channels, 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) &&
++                  freq_included(wpa_s, 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) &&
++                  freq_included(wpa_s, 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) &&
++          freq_included(wpa_s, 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) &&
-       return -1;
++                  freq_included(wpa_s, channels, params->freq) &&
 +                  p2p_supported_freq(wpa_s->global->p2p, params->freq))
 +                      goto out;
 +      }
 +
++      params->freq = 0;
 +      wpa_printf(MSG_DEBUG, "P2P: No 2.4, 5, or 60 GHz channel allowed");
-       return 0;
++      return;
 +out:
 +      wpa_printf(MSG_DEBUG, "P2P: Set GO freq %d MHz (no preference known)",
 +                 params->freq);
-       unsigned int pref_freq, cand_freq;
 +}
 +
 +
 +static int wpas_p2p_init_go_params(struct wpa_supplicant *wpa_s,
 +                                 struct p2p_go_neg_results *params,
 +                                 int freq, int ht40, int vht,
 +                                 const struct p2p_channels *channels)
 +{
 +      struct wpa_used_freq_data *freqs;
-               if (!freq_included(channels, freq)) {
-                       wpa_printf(MSG_DEBUG, "P2P: Forced GO freq %d MHz not "
-                                  "accepted", freq);
-                       return -1;
++      unsigned int cand;
 +      unsigned int num, i;
 +
 +      os_memset(params, 0, sizeof(*params));
 +      params->role_go = 1;
 +      params->ht40 = ht40;
 +      params->vht = vht;
++
++      if (wpa_s->p2p_group_common_freqs_num)
++              wpa_printf(MSG_DEBUG, "P2P: %s called for an active GO",
++                         __func__);
++
++      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);
++
++      /* try using the forced freq */
 +      if (freq) {
-               wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on forced "
-                          "frequency %d MHz", freq);
++              if (!wpas_p2p_supported_freq_go(wpa_s, channels, freq)) {
++                      wpa_printf(MSG_DEBUG,
++                                 "P2P: Forced GO freq %d MHz not accepted",
++                                 freq);
++                      goto fail;
++              }
++
++              for (i = 0; i < num; i++) {
++                      if (freqs[i].freq == freq) {
++                              wpa_printf(MSG_DEBUG,
++                                         "P2P: forced freq (%d MHz) is also shared",
++                                         freq);
++                              params->freq = freq;
++                              goto success;
++                      }
++              }
++
++              if (wpas_p2p_num_unused_channels(wpa_s) <= 0) {
++                      wpa_printf(MSG_DEBUG,
++                                 "P2P: Cannot force GO on freq (%d MHz) as all the channels are in use",
++                                 freq);
++                      goto fail;
 +              }
-       } else if (wpa_s->conf->p2p_oper_reg_class == 81 &&
-                  wpa_s->conf->p2p_oper_channel >= 1 &&
-                  wpa_s->conf->p2p_oper_channel <= 11 &&
-                  freq_included(channels,
-                                2407 + 5 * wpa_s->conf->p2p_oper_channel)) {
++
++              wpa_printf(MSG_DEBUG,
++                         "P2P: force GO freq (%d MHz) on a free channel",
++                         freq);
 +              params->freq = freq;
-       } else if ((wpa_s->conf->p2p_oper_reg_class == 115 ||
-                   wpa_s->conf->p2p_oper_reg_class == 116 ||
-                   wpa_s->conf->p2p_oper_reg_class == 117 ||
-                   wpa_s->conf->p2p_oper_reg_class == 124 ||
-                   wpa_s->conf->p2p_oper_reg_class == 126 ||
-                   wpa_s->conf->p2p_oper_reg_class == 127) &&
-                  freq_included(channels,
-                                5000 + 5 * wpa_s->conf->p2p_oper_channel)) {
++              goto success;
++      }
++
++      /* consider using one of the shared frequencies */
++      if (num) {
++              cand = wpas_p2p_pick_best_used_freq(wpa_s, freqs, num);
++              if (wpas_p2p_supported_freq_go(wpa_s, channels, cand)) {
++                      wpa_printf(MSG_DEBUG,
++                                 "P2P: Use shared freq (%d MHz) for GO",
++                                 freq);
++                      params->freq = cand;
++                      goto success;
++              }
++
++              /* try using one of the shared freqs */
++              for (i = 0; i < num; i++) {
++                      if (wpas_p2p_supported_freq_go(wpa_s, channels,
++                                                     freqs[i].freq)) {
++                              wpa_printf(MSG_DEBUG,
++                                         "P2P: Use shared freq (%d MHz) for GO",
++                                         freq);
++                              params->freq = freqs[i].freq;
++                              goto success;
++                      }
++              }
++      }
++
++      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");
++              goto fail;
++      }
++
++      /* try using the setting from the configuration file */
++      if (wpa_s->conf->p2p_oper_reg_class == 81 &&
++          wpa_s->conf->p2p_oper_channel >= 1 &&
++          wpa_s->conf->p2p_oper_channel <= 11 &&
++          wpas_p2p_supported_freq_go(
++                  wpa_s, channels,
++                  2407 + 5 * wpa_s->conf->p2p_oper_channel)) {
 +              params->freq = 2407 + 5 * wpa_s->conf->p2p_oper_channel;
 +              wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on configured "
 +                         "frequency %d MHz", params->freq);
-       } else if (wpa_s->conf->p2p_oper_channel == 0 &&
-                  wpa_s->best_overall_freq > 0 &&
-                  p2p_supported_freq_go(wpa_s->global->p2p,
-                                        wpa_s->best_overall_freq) &&
-                  freq_included(channels, wpa_s->best_overall_freq)) {
++              goto success;
++      }
++
++      if ((wpa_s->conf->p2p_oper_reg_class == 115 ||
++           wpa_s->conf->p2p_oper_reg_class == 116 ||
++           wpa_s->conf->p2p_oper_reg_class == 117 ||
++           wpa_s->conf->p2p_oper_reg_class == 124 ||
++           wpa_s->conf->p2p_oper_reg_class == 125 ||
++           wpa_s->conf->p2p_oper_reg_class == 126 ||
++           wpa_s->conf->p2p_oper_reg_class == 127) &&
++          wpas_p2p_supported_freq_go(wpa_s, channels,
++                                     5000 +
++                                     5 * wpa_s->conf->p2p_oper_channel)) {
 +              params->freq = 5000 + 5 * wpa_s->conf->p2p_oper_channel;
 +              wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on configured "
 +                         "frequency %d MHz", params->freq);
-       } else if (wpa_s->conf->p2p_oper_channel == 0 &&
-                  wpa_s->best_24_freq > 0 &&
-                  p2p_supported_freq_go(wpa_s->global->p2p,
-                                        wpa_s->best_24_freq) &&
-                  freq_included(channels, wpa_s->best_24_freq)) {
++              goto success;
++      }
++
++      /* Try using best channels */
++      if (wpa_s->conf->p2p_oper_channel == 0 &&
++          wpa_s->best_overall_freq > 0 &&
++          wpas_p2p_supported_freq_go(wpa_s, 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_5_freq > 0 &&
-                  p2p_supported_freq_go(wpa_s->global->p2p,
-                                        wpa_s->best_5_freq) &&
-                  freq_included(channels, wpa_s->best_5_freq)) {
++              goto success;
++      }
++
++      if (wpa_s->conf->p2p_oper_channel == 0 &&
++          wpa_s->best_24_freq > 0 &&
++          wpas_p2p_supported_freq_go(wpa_s, 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 ((pref_freq = p2p_get_pref_freq(wpa_s->global->p2p,
-                                                 channels))) {
-               params->freq = pref_freq;
++              goto success;
++      }
++
++      if (wpa_s->conf->p2p_oper_channel == 0 &&
++          wpa_s->best_5_freq > 0 &&
++          wpas_p2p_supported_freq_go(wpa_s, 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 "
 +                         "channel %d MHz", params->freq);
-       } else {
-               /* no preference, select some channel */
-               if (wpas_p2p_select_freq_no_pref(wpa_s, params, channels) < 0)
-                       return -1;
++              goto success;
++      }
++
++      /* try using preferred channels */
++      cand = p2p_get_pref_freq(wpa_s->global->p2p, channels);
++      if (cand && wpas_p2p_supported_freq_go(wpa_s, channels, cand)) {
++              params->freq = cand;
 +              wpa_printf(MSG_DEBUG, "P2P: Set GO freq %d MHz from preferred "
 +                         "channels", params->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;
++              goto success;
 +      }
 +
-               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 {
-               for (i = 0; i < num; i++) {
-                       if (freqs[i].freq == freq)
-                               break;
-               }
++      /* Try using one of the group common freqs */
++      if (wpa_s->p2p_group_common_freqs) {
++              for (i = 0; i < wpa_s->p2p_group_common_freqs_num; i++) {
++                      cand = wpa_s->p2p_group_common_freqs[i];
++                      if (wpas_p2p_supported_freq_go(wpa_s, channels, cand)) {
++                              params->freq = cand;
++                              wpa_printf(MSG_DEBUG,
++                                         "P2P: Use freq %d MHz common with the peer",
++                                         params->freq);
++                              goto success;
 +                      }
 +              }
++      }
 +
-               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");
-                       }
-               }
++      /* no preference, select some channel */
++      wpas_p2p_select_go_freq_no_pref(wpa_s, params, channels);
 +
-                                int freq)
++      if (params->freq == 0) {
++              wpa_printf(MSG_DEBUG, "P2P: did not find a freq for GO use");
++              goto fail;
 +      }
 +
++success:
 +      os_free(freqs);
 +      return 0;
++fail:
++      os_free(freqs);
++      return -1;
 +}
 +
 +
 +static struct wpa_supplicant *
 +wpas_p2p_get_group_iface(struct wpa_supplicant *wpa_s, int addr_allocated,
 +                       int go)
 +{
 +      struct wpa_supplicant *group_wpa_s;
 +
 +      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;
 +      }
 +
 +      if (wpas_p2p_add_group_interface(wpa_s, go ? WPA_IF_P2P_GO :
 +                                       WPA_IF_P2P_CLIENT) < 0) {
 +              wpa_msg_global(wpa_s, MSG_ERROR,
 +                             "P2P: Failed to add group interface");
 +              return NULL;
 +      }
 +      group_wpa_s = wpas_p2p_init_group_interface(wpa_s, go);
 +      if (group_wpa_s == NULL) {
 +              wpa_msg_global(wpa_s, MSG_ERROR,
 +                             "P2P: Failed to initialize group interface");
 +              wpas_p2p_remove_pending_group_interface(wpa_s);
 +              return NULL;
 +      }
 +
 +      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;
 +}
 +
 +
 +/**
 + * wpas_p2p_group_add - Add a new P2P group with local end as Group Owner
 + * @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 vht)
 +{
 +      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);
 +
 +      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, vht, NULL))
 +              return -1;
 +      if (params.freq &&
 +          !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;
 +
 +      wpa_s = wpas_p2p_get_group_iface(wpa_s, 0, 1);
 +      if (wpa_s == NULL)
 +              return -1;
 +      wpas_start_wps_go(wpa_s, &params, 0);
 +
 +      return 0;
 +}
 +
 +
 +static int wpas_start_p2p_client(struct wpa_supplicant *wpa_s,
 +                               struct wpa_ssid *params, int addr_allocated,
-                                 int connection_timeout)
++                               int freq, int force_scan)
 +{
 +      struct wpa_ssid *ssid;
 +
 +      wpa_s = wpas_p2p_get_group_iface(wpa_s, addr_allocated, 0);
 +      if (wpa_s == NULL)
 +              return -1;
++      if (force_scan)
++              os_get_reltime(&wpa_s->scan_min_time);
 +      wpa_s->p2p_last_4way_hs_fail = NULL;
 +
 +      wpa_supplicant_ap_deinit(wpa_s);
 +
 +      ssid = wpa_config_add_network(wpa_s->conf);
 +      if (ssid == NULL)
 +              return -1;
++      os_memset(wpa_s->go_dev_addr, 0, ETH_ALEN);
 +      wpa_config_set_network_defaults(ssid);
 +      ssid->temporary = 1;
 +      ssid->proto = WPA_PROTO_RSN;
 +      ssid->pairwise_cipher = WPA_CIPHER_CCMP;
 +      ssid->group_cipher = WPA_CIPHER_CCMP;
 +      ssid->key_mgmt = WPA_KEY_MGMT_PSK;
 +      ssid->ssid = os_malloc(params->ssid_len);
 +      if (ssid->ssid == NULL) {
 +              wpa_config_remove_network(wpa_s->conf, ssid->id);
 +              return -1;
 +      }
 +      os_memcpy(ssid->ssid, params->ssid, params->ssid_len);
 +      ssid->ssid_len = params->ssid_len;
 +      ssid->p2p_group = 1;
 +      ssid->export_keys = 1;
 +      if (params->psk_set) {
 +              os_memcpy(ssid->psk, params->psk, 32);
 +              ssid->psk_set = 1;
 +      }
 +      if (params->passphrase)
 +              ssid->passphrase = os_strdup(params->passphrase);
 +
 +      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;
 +}
 +
 +
 +int wpas_p2p_group_add_persistent(struct wpa_supplicant *wpa_s,
 +                                struct wpa_ssid *ssid, int addr_allocated,
 +                                int force_freq, int neg_freq, int ht40,
 +                                int vht, const struct p2p_channels *channels,
-                           (freq > 0 && !freq_included(channels, freq)))
++                                int connection_timeout, int force_scan)
 +{
 +      struct p2p_go_neg_results params;
 +      int go = 0, freq;
 +
 +      if (ssid->disabled != 2 || ssid->ssid == NULL)
 +              return -1;
 +
 +      if (wpas_get_p2p_group(wpa_s, ssid->ssid, ssid->ssid_len, &go) &&
 +          go == (ssid->mode == WPAS_MODE_P2P_GO)) {
 +              wpa_printf(MSG_DEBUG, "P2P: Requested persistent group is "
 +                         "already running");
++              if (go == 0 &&
++                  eloop_cancel_timeout(wpas_p2p_group_formation_timeout,
++                                       wpa_s->parent, NULL)) {
++                      /*
++                       * This can happen if Invitation Response frame was lost
++                       * and the peer (GO of a persistent group) tries to
++                       * invite us again. Reschedule the timeout to avoid
++                       * terminating the wait for the connection too early
++                       * since we now know that the peer is still trying to
++                       * invite us instead of having already started the GO.
++                       */
++                      wpa_printf(MSG_DEBUG,
++                                 "P2P: Reschedule group formation timeout since peer is still trying to invite us");
++                      eloop_register_timeout(P2P_MAX_INITIAL_CONN_WAIT, 0,
++                                             wpas_p2p_group_formation_timeout,
++                                             wpa_s->parent, NULL);
++              }
 +              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 ||
-       } else {
++                          (freq > 0 && !freq_included(wpa_s, channels, freq)))
 +                              freq = 0;
 +              }
-               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, freq);
++      } else if (ssid->mode == WPAS_MODE_INFRA) {
 +              freq = neg_freq;
-       if (ssid->mode != WPAS_MODE_P2P_GO)
++              if (freq <= 0 || !freq_included(wpa_s, channels, freq)) {
++                      struct os_reltime now;
++                      struct wpa_bss *bss =
++                              wpa_bss_get_p2p_dev_addr(wpa_s, ssid->bssid);
++
++                      os_get_reltime(&now);
++                      if (bss &&
++                          !os_reltime_expired(&now, &bss->last_update, 5) &&
++                          freq_included(wpa_s, channels, bss->freq))
++                              freq = bss->freq;
++                      else
++                              freq = 0;
++              }
 +
-       wpas_group_formation_completed(wpa_s, 1);
++              return wpas_start_p2p_client(wpa_s, ssid, addr_allocated, freq,
++                                           force_scan);
++      } else {
 +              return -1;
++      }
 +
 +      if (wpas_p2p_init_go_params(wpa_s, &params, freq, ht40, vht, channels))
 +              return -1;
 +
 +      params.role_go = 1;
 +      params.psk_set = ssid->psk_set;
 +      if (params.psk_set)
 +              os_memcpy(params.psk, ssid->psk, sizeof(params.psk));
 +      if (ssid->passphrase) {
 +              if (os_strlen(ssid->passphrase) >= sizeof(params.passphrase)) {
 +                      wpa_printf(MSG_ERROR, "P2P: Invalid passphrase in "
 +                                 "persistent group");
 +                      return -1;
 +              }
 +              os_strlcpy(params.passphrase, ssid->passphrase,
 +                         sizeof(params.passphrase));
 +      }
 +      os_memcpy(params.ssid, ssid->ssid, ssid->ssid_len);
 +      params.ssid_len = ssid->ssid_len;
 +      params.persistent_group = 1;
 +
 +      wpa_s = wpas_p2p_get_group_iface(wpa_s, addr_allocated, 1);
 +      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;
 +}
 +
 +
 +static void wpas_p2p_ie_update(void *ctx, struct wpabuf *beacon_ies,
 +                             struct wpabuf *proberesp_ies)
 +{
 +      struct wpa_supplicant *wpa_s = ctx;
 +      if (wpa_s->ap_iface) {
 +              struct hostapd_data *hapd = wpa_s->ap_iface->bss[0];
 +              if (!(hapd->conf->p2p & P2P_GROUP_OWNER)) {
 +                      wpabuf_free(beacon_ies);
 +                      wpabuf_free(proberesp_ies);
 +                      return;
 +              }
 +              if (beacon_ies) {
 +                      wpabuf_free(hapd->p2p_beacon_ie);
 +                      hapd->p2p_beacon_ie = beacon_ies;
 +              }
 +              wpabuf_free(hapd->p2p_probe_resp_ie);
 +              hapd->p2p_probe_resp_ie = proberesp_ies;
 +      } else {
 +              wpabuf_free(beacon_ies);
 +              wpabuf_free(proberesp_ies);
 +      }
 +      wpa_supplicant_ap_update_beacon(wpa_s);
 +}
 +
 +
 +static void wpas_p2p_idle_update(void *ctx, int idle)
 +{
 +      struct wpa_supplicant *wpa_s = ctx;
 +      if (!wpa_s->ap_iface)
 +              return;
 +      wpa_printf(MSG_DEBUG, "P2P: GO - group %sidle", idle ? "" : "not ");
 +      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
 +              eloop_cancel_timeout(wpas_p2p_group_idle_timeout, wpa_s, NULL);
 +}
 +
 +
 +struct p2p_group * wpas_p2p_group_init(struct wpa_supplicant *wpa_s,
 +                                     struct wpa_ssid *ssid)
 +{
 +      struct p2p_group *group;
 +      struct p2p_group_config *cfg;
 +
 +      if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
 +              return NULL;
 +
 +      cfg = os_zalloc(sizeof(*cfg));
 +      if (cfg == NULL)
 +              return NULL;
 +
 +      if (ssid->p2p_persistent_group && wpa_s->conf->persistent_reconnect)
 +              cfg->persistent_group = 2;
 +      else if (ssid->p2p_persistent_group)
 +              cfg->persistent_group = 1;
 +      os_memcpy(cfg->interface_addr, wpa_s->own_addr, ETH_ALEN);
 +      if (wpa_s->max_stations &&
 +          wpa_s->max_stations < wpa_s->conf->max_num_sta)
 +              cfg->max_clients = wpa_s->max_stations;
 +      else
 +              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;
 +
 +      group = p2p_group_init(wpa_s->global->p2p, cfg);
 +      if (group == NULL)
 +              os_free(cfg);
 +      if (ssid->mode != WPAS_MODE_P2P_GROUP_FORMATION)
 +              p2p_group_notif_formation_done(group);
 +      wpa_s->p2p_group = group;
 +      return group;
 +}
 +
 +
 +void wpas_p2p_wps_success(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
 +                        int registrar)
 +{
 +      struct wpa_ssid *ssid = wpa_s->current_ssid;
 +
 +      if (!wpa_s->p2p_in_provisioning) {
 +              wpa_printf(MSG_DEBUG, "P2P: Ignore WPS success event - P2P "
 +                         "provisioning not in progress");
 +              return;
 +      }
 +
 +      if (ssid && ssid->mode == WPAS_MODE_INFRA) {
 +              u8 go_dev_addr[ETH_ALEN];
 +              os_memcpy(go_dev_addr, wpa_s->bssid, ETH_ALEN);
 +              wpas_p2p_persistent_group(wpa_s, go_dev_addr, ssid->ssid,
 +                                        ssid->ssid_len);
 +              /* Clear any stored provisioning info */
 +              p2p_clear_provisioning_info(wpa_s->global->p2p, go_dev_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
 +               * complete to allow the group to be removed automatically if
 +               * 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);
-       if (wpa_s->conf->p2p_disabled)
++      wpas_group_formation_completed(wpa_s, 1, 0);
 +}
 +
 +
 +void wpas_p2p_wps_failed(struct wpa_supplicant *wpa_s,
 +                       struct wps_event_fail *fail)
 +{
 +      if (!wpa_s->p2p_in_provisioning) {
 +              wpa_printf(MSG_DEBUG, "P2P: Ignore WPS fail event - P2P "
 +                         "provisioning not in progress");
 +              return;
 +      }
 +
 +      if (wpa_s->go_params) {
 +              p2p_clear_provisioning_info(
 +                      wpa_s->global->p2p,
 +                      wpa_s->go_params->peer_device_addr);
 +      }
 +
 +      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,
 +                     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 (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;
 +      else if (os_strncmp(config_method, "pbc", 3) == 0 ||
 +               os_strncmp(config_method, "pushbutton", 10) == 0)
 +              config_methods = WPS_CONFIG_PUSHBUTTON;
 +      else {
 +              wpa_printf(MSG_DEBUG, "P2P: Unknown config method");
 +              os_free(p2ps_prov);
 +              return -1;
 +      }
 +
 +      if (use == WPAS_P2P_PD_AUTO) {
 +              os_memcpy(wpa_s->pending_join_dev_addr, peer_addr, ETH_ALEN);
 +              wpa_s->pending_pd_config_methods = config_methods;
 +              wpa_s->p2p_auto_pd = 1;
 +              wpa_s->p2p_auto_join = 0;
 +              wpa_s->pending_pd_before_join = 0;
 +              wpa_s->auto_pd_scan_retry = 0;
 +              wpas_p2p_stop_find(wpa_s);
 +              wpa_s->p2p_join_scan_count = 0;
 +              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);
 +              wpas_p2p_join_scan(wpa_s, NULL);
 +              return 0;
 +      }
 +
 +      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, p2ps_prov,
 +                               config_methods, use == WPAS_P2P_PD_FOR_JOIN,
 +                               0, 1);
 +}
 +
 +
 +int wpas_p2p_scan_result_text(const u8 *ies, size_t ies_len, char *buf,
 +                            char *end)
 +{
 +      return p2p_scan_result_text(ies, ies_len, buf, end);
 +}
 +
 +
 +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);
 +}
 +
 +
 +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,
 +                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->global->p2p_disabled || wpa_s->global->p2p == NULL ||
 +          wpa_s->p2p_in_provisioning)
 +              return -1;
 +
 +      wpa_supplicant_cancel_sched_scan(wpa_s);
 +
 +      return p2p_find(wpa_s->global->p2p, timeout, type,
 +                      num_req_dev_types, req_dev_types, dev_id,
 +                      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 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);
 +
 +      if (wpa_s->global->p2p)
 +              p2p_stop_find(wpa_s->global->p2p);
 +
 +      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)
 +{
 +      wpas_p2p_stop_find_oper(wpa_s);
 +      if (!wpa_s->global->pending_group_iface_for_p2ps)
 +              wpas_p2p_remove_pending_group_interface(wpa_s);
 +}
 +
 +
 +static void wpas_p2p_long_listen_timeout(void *eloop_ctx, void *timeout_ctx)
 +{
 +      struct wpa_supplicant *wpa_s = eloop_ctx;
 +      wpa_s->p2p_long_listen = 0;
 +}
 +
 +
 +int wpas_p2p_listen(struct wpa_supplicant *wpa_s, unsigned int timeout)
 +{
 +      int res;
 +
 +      if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
 +              return -1;
 +
 +      wpa_supplicant_cancel_sched_scan(wpa_s);
 +      wpas_p2p_clear_pending_action_tx(wpa_s);
 +
 +      if (timeout == 0) {
 +              /*
 +               * This is a request for unlimited Listen state. However, at
 +               * least for now, this is mapped to a Listen state for one
 +               * hour.
 +               */
 +              timeout = 3600;
 +      }
 +      eloop_cancel_timeout(wpas_p2p_long_listen_timeout, wpa_s, NULL);
 +      wpa_s->p2p_long_listen = 0;
 +
 +      /*
 +       * Stop previous find/listen operation to avoid trying to request a new
 +       * remain-on-channel operation while the driver is still running the
 +       * previous one.
 +       */
 +      if (wpa_s->global->p2p)
 +              p2p_stop_find(wpa_s->global->p2p);
 +
 +      res = wpas_p2p_listen_start(wpa_s, timeout * 1000);
 +      if (res == 0 && timeout * 1000 > wpa_s->max_remain_on_chan) {
 +              wpa_s->p2p_long_listen = timeout * 1000;
 +              eloop_register_timeout(timeout, 0,
 +                                     wpas_p2p_long_listen_timeout,
 +                                     wpa_s, NULL);
 +      }
 +
 +      return res;
 +}
 +
 +
 +int wpas_p2p_assoc_req_ie(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
 +                        u8 *buf, size_t len, int p2p_group)
 +{
 +      struct wpabuf *p2p_ie;
 +      int ret;
 +
 +      if (wpa_s->global->p2p_disabled)
 +              return -1;
-                         const u8 *ie, size_t ie_len, int ssi_signal)
++      /*
++       * Advertize mandatory cross connection capability even on
++       * p2p_disabled=1 interface when associating with a P2P Manager WLAN AP.
++       */
++      if (wpa_s->conf->p2p_disabled && p2p_group)
 +              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);
 +      ret = p2p_assoc_req_ie(wpa_s->global->p2p, bss->bssid, buf, len,
 +                             p2p_group, p2p_ie);
 +      wpabuf_free(p2p_ie);
 +
 +      return ret;
 +}
 +
 +
 +int wpas_p2p_probe_req_rx(struct wpa_supplicant *wpa_s, const u8 *addr,
 +                        const u8 *dst, const u8 *bssid,
-                                ie, ie_len)) {
++                        const u8 *ie, size_t ie_len,
++                        unsigned int rx_freq, int ssi_signal)
 +{
 +      if (wpa_s->global->p2p_disabled)
 +              return 0;
 +      if (wpa_s->global->p2p == NULL)
 +              return 0;
 +
 +      switch (p2p_probe_req_rx(wpa_s->global->p2p, addr, dst, bssid,
-                                  role == P2P_INVITE_ROLE_GO);
++                               ie, ie_len, rx_freq)) {
 +      case P2P_PREQ_NOT_P2P:
 +              wpas_notify_preq(wpa_s, addr, dst, bssid, ie, ie_len,
 +                               ssi_signal);
 +              /* fall through */
 +      case P2P_PREQ_MALFORMED:
 +      case P2P_PREQ_NOT_LISTEN:
 +      case P2P_PREQ_NOT_PROCESSED:
 +      default: /* make gcc happy */
 +              return 0;
 +      case P2P_PREQ_PROCESSED:
 +              return 1;
 +      }
 +}
 +
 +
 +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)
 +{
 +      if (wpa_s->global->p2p_disabled)
 +              return;
 +      if (wpa_s->global->p2p == NULL)
 +              return;
 +
 +      p2p_rx_action(wpa_s->global->p2p, da, sa, bssid, category, data, len,
 +                    freq);
 +}
 +
 +
 +void wpas_p2p_scan_ie(struct wpa_supplicant *wpa_s, struct wpabuf *ies)
 +{
 +      if (wpa_s->global->p2p_disabled)
 +              return;
 +      if (wpa_s->global->p2p == NULL)
 +              return;
 +
 +      p2p_scan_ie(wpa_s->global->p2p, ies, NULL);
 +}
 +
 +
 +static void wpas_p2p_group_deinit(struct wpa_supplicant *wpa_s)
 +{
 +      p2p_group_deinit(wpa_s->p2p_group);
 +      wpa_s->p2p_group = NULL;
 +
 +      wpa_s->ap_configured_cb = NULL;
 +      wpa_s->ap_configured_cb_ctx = NULL;
 +      wpa_s->ap_configured_cb_data = NULL;
 +      wpa_s->connect_without_scan = NULL;
 +}
 +
 +
 +int wpas_p2p_reject(struct wpa_supplicant *wpa_s, const u8 *addr)
 +{
 +      wpa_s->p2p_long_listen = 0;
 +
 +      if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
 +              return -1;
 +
 +      return p2p_reject(wpa_s->global->p2p, addr);
 +}
 +
 +
 +/* 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 vht, int pref_freq)
 +{
 +      enum p2p_invite_role role;
 +      u8 *bssid = NULL;
 +      int force_freq = 0;
 +      int res;
 +      int no_pref_freq_given = pref_freq == 0;
++      unsigned int pref_freq_list[P2P_MAX_PREF_CHANNELS], size;
 +
 +      wpa_s->global->p2p_invite_group = NULL;
 +      if (peer_addr)
 +              os_memcpy(wpa_s->p2p_auth_invite, peer_addr, ETH_ALEN);
 +      else
 +              os_memset(wpa_s->p2p_auth_invite, 0, ETH_ALEN);
 +
 +      wpa_s->p2p_persistent_go_freq = freq;
 +      wpa_s->p2p_go_ht40 = !!ht40;
 +      if (ssid->mode == WPAS_MODE_P2P_GO) {
 +              role = P2P_INVITE_ROLE_GO;
 +              if (peer_addr == NULL) {
 +                      wpa_printf(MSG_DEBUG, "P2P: Missing peer "
 +                                 "address in invitation command");
 +                      return -1;
 +              }
 +              if (wpas_p2p_create_iface(wpa_s)) {
 +                      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 -1;
 +                      }
 +                      bssid = wpa_s->pending_interface_addr;
 +              } else
 +                      bssid = wpa_s->own_addr;
 +      } else {
 +              role = P2P_INVITE_ROLE_CLIENT;
 +              peer_addr = ssid->bssid;
 +      }
 +      wpa_s->pending_invite_ssid_id = ssid->id;
 +
++      size = P2P_MAX_PREF_CHANNELS;
 +      res = wpas_p2p_setup_freqs(wpa_s, freq, &force_freq, &pref_freq,
-                                  role == P2P_INVITE_ROLE_ACTIVE_GO);
++                                 role == P2P_INVITE_ROLE_GO,
++                                 pref_freq_list, &size);
 +      if (res)
 +              return res;
++      p2p_set_own_pref_freq_list(wpa_s->global->p2p, pref_freq_list, size);
 +
 +      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);
 +}
 +
 +
 +/* Invite to join an active group */
 +int wpas_p2p_invite_group(struct wpa_supplicant *wpa_s, const char *ifname,
 +                        const u8 *peer_addr, const u8 *go_dev_addr)
 +{
 +      struct wpa_global *global = wpa_s->global;
 +      enum p2p_invite_role role;
 +      u8 *bssid = NULL;
 +      struct wpa_ssid *ssid;
 +      int persistent;
 +      int freq = 0, force_freq = 0, pref_freq = 0;
 +      int res;
++      unsigned int pref_freq_list[P2P_MAX_PREF_CHANNELS], size;
 +
 +      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)
 +                      break;
 +      }
 +      if (wpa_s == NULL) {
 +              wpa_printf(MSG_DEBUG, "P2P: Interface '%s' not found", ifname);
 +              return -1;
 +      }
 +
 +      ssid = wpa_s->current_ssid;
 +      if (ssid == NULL) {
 +              wpa_printf(MSG_DEBUG, "P2P: No current SSID to use for "
 +                         "invitation");
 +              return -1;
 +      }
 +
 +      wpa_s->global->p2p_invite_group = wpa_s;
 +      persistent = ssid->p2p_persistent_group &&
 +              wpas_p2p_get_persistent(wpa_s->parent, peer_addr,
 +                                      ssid->ssid, ssid->ssid_len);
 +
 +      if (ssid->mode == WPAS_MODE_P2P_GO) {
 +              role = P2P_INVITE_ROLE_ACTIVE_GO;
 +              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) {
 +                      wpa_printf(MSG_DEBUG, "P2P: Not associated - cannot "
 +                                 "invite to current group");
 +                      return -1;
 +              }
 +              bssid = wpa_s->bssid;
 +              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->global->p2p_disabled || wpa_s->global->p2p == NULL)
 +              return -1;
 +
++      size = P2P_MAX_PREF_CHANNELS;
 +      res = wpas_p2p_setup_freqs(wpa_s, freq, &force_freq, &pref_freq,
-       wpas_p2p_group_formation_failed(wpa_s);
++                                 role == P2P_INVITE_ROLE_ACTIVE_GO,
++                                 pref_freq_list, &size);
 +      if (res)
 +              return res;
 +      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, -1);
 +}
 +
 +
 +void wpas_p2p_completed(struct wpa_supplicant *wpa_s)
 +{
 +      struct wpa_ssid *ssid = wpa_s->current_ssid;
 +      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,
 +                                   wpa_s->parent, NULL);
 +      }
 +
 +      if (!wpa_s->show_group_started || !ssid)
 +              return;
 +
 +      wpa_s->show_group_started = 0;
 +
 +      os_memset(go_dev_addr, 0, ETH_ALEN);
 +      if (ssid->bssid_set)
 +              os_memcpy(go_dev_addr, ssid->bssid, ETH_ALEN);
 +      persistent = wpas_p2p_persistent_group(wpa_s, go_dev_addr, ssid->ssid,
 +                                             ssid->ssid_len);
 +      os_memcpy(wpa_s->go_dev_addr, go_dev_addr, ETH_ALEN);
 +
 +      if (wpa_s->global->p2p_group_formation == wpa_s)
 +              wpa_s->global->p2p_group_formation = NULL;
 +
 +      freq = wpa_s->current_bss ? wpa_s->current_bss->freq :
 +              (int) wpa_s->assoc_freq;
 +
 +      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,
 +                                                           ssid, go_dev_addr);
 +      if (network_id < 0)
 +              network_id = ssid->id;
 +      wpas_notify_p2p_group_started(wpa_s, ssid, network_id, 1);
 +}
 +
 +
 +int wpas_p2p_presence_req(struct wpa_supplicant *wpa_s, u32 duration1,
 +                        u32 interval1, u32 duration2, u32 interval2)
 +{
 +      int ret;
 +
 +      if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
 +              return -1;
 +
 +      if (wpa_s->wpa_state < WPA_ASSOCIATED ||
 +          wpa_s->current_ssid == NULL ||
 +          wpa_s->current_ssid->mode != WPAS_MODE_INFRA)
 +              return -1;
 +
 +      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->global->p2p_disabled || wpa_s->global->p2p == NULL)
 +              return -1;
 +
 +      return p2p_ext_listen(wpa_s->global->p2p, period, interval);
 +}
 +
 +
 +static int wpas_p2p_is_client(struct wpa_supplicant *wpa_s)
 +{
 +      if (wpa_s->current_ssid == NULL) {
 +              /*
 +               * current_ssid can be cleared when P2P client interface gets
 +               * disconnected, so assume this interface was used as P2P
 +               * client.
 +               */
 +              return 1;
 +      }
 +      return wpa_s->current_ssid->p2p_group &&
 +              wpa_s->current_ssid->mode == WPAS_MODE_INFRA;
 +}
 +
 +
 +static void wpas_p2p_group_idle_timeout(void *eloop_ctx, void *timeout_ctx)
 +{
 +      struct wpa_supplicant *wpa_s = eloop_ctx;
 +
 +      if (wpa_s->conf->p2p_group_idle == 0 && !wpas_p2p_is_client(wpa_s)) {
 +              wpa_printf(MSG_DEBUG, "P2P: Ignore group idle timeout - "
 +                         "disabled");
 +              return;
 +      }
 +
 +      wpa_printf(MSG_DEBUG, "P2P: Group idle timeout reached - terminate "
 +                 "group");
 +      wpas_p2p_group_delete(wpa_s, P2P_GROUP_REMOVAL_IDLE_TIMEOUT);
 +}
 +
 +
 +static void wpas_p2p_set_group_idle_timeout(struct wpa_supplicant *wpa_s)
 +{
 +      int 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 (wpa_s->current_ssid == NULL || !wpa_s->current_ssid->p2p_group)
 +              return;
 +
 +      timeout = wpa_s->conf->p2p_group_idle;
 +      if (wpa_s->current_ssid->mode == WPAS_MODE_INFRA &&
 +          (timeout == 0 || timeout > P2P_MAX_CLIENT_IDLE))
 +          timeout = P2P_MAX_CLIENT_IDLE;
 +
 +      if (timeout == 0)
 +              return;
 +
 +      if (timeout < 0) {
 +              if (wpa_s->current_ssid->mode == WPAS_MODE_INFRA)
 +                      timeout = 0; /* special client mode no-timeout */
 +              else
 +                      return;
 +      }
 +
 +      if (wpa_s->p2p_in_provisioning) {
 +              /*
 +               * Use the normal group formation timeout during the
 +               * provisioning phase to avoid terminating this process too
 +               * early due to group idle timeout.
 +               */
 +              wpa_printf(MSG_DEBUG, "P2P: Do not use P2P group idle timeout "
 +                         "during provisioning");
 +              return;
 +      }
 +
 +      if (wpa_s->show_group_started) {
 +              /*
 +               * Use the normal group formation timeout between the end of
 +               * the provisioning phase and completion of 4-way handshake to
 +               * avoid terminating this process too early due to group idle
 +               * timeout.
 +               */
 +              wpa_printf(MSG_DEBUG, "P2P: Do not use P2P group idle timeout "
 +                         "while waiting for initial 4-way handshake to "
 +                         "complete");
 +              return;
 +      }
 +
 +      wpa_printf(MSG_DEBUG, "P2P: Set P2P group idle timeout to %u seconds",
 +                 timeout);
 +      eloop_register_timeout(timeout, 0, wpas_p2p_group_idle_timeout,
 +                             wpa_s, NULL);
 +}
 +
 +
 +/* Returns 1 if the interface was removed */
 +int wpas_p2p_deauth_notif(struct wpa_supplicant *wpa_s, const u8 *bssid,
 +                        u16 reason_code, const u8 *ie, size_t ie_len,
 +                        int locally_generated)
 +{
 +      if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
 +              return 0;
 +
 +      if (!locally_generated)
 +              p2p_deauth_notif(wpa_s->global->p2p, bssid, reason_code, ie,
 +                               ie_len);
 +
 +      if (reason_code == WLAN_REASON_DEAUTH_LEAVING && !locally_generated &&
 +          wpa_s->current_ssid &&
 +          wpa_s->current_ssid->p2p_group &&
 +          wpa_s->current_ssid->mode == WPAS_MODE_INFRA) {
 +              wpa_printf(MSG_DEBUG, "P2P: GO indicated that the P2P Group "
 +                         "session is ending");
 +              if (wpas_p2p_group_delete(wpa_s,
 +                                        P2P_GROUP_REMOVAL_GO_ENDING_SESSION)
 +                  > 0)
 +                      return 1;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +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)
 +{
 +      if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
 +              return;
 +
 +      if (!locally_generated)
 +              p2p_disassoc_notif(wpa_s->global->p2p, bssid, reason_code, ie,
 +                                 ie_len);
 +}
 +
 +
 +void wpas_p2p_update_config(struct wpa_supplicant *wpa_s)
 +{
 +      struct p2p_data *p2p = wpa_s->global->p2p;
 +
 +      if (p2p == NULL)
 +              return;
 +
 +      if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_CAPABLE))
 +              return;
 +
 +      if (wpa_s->conf->changed_parameters & CFG_CHANGED_DEVICE_NAME)
 +              p2p_set_dev_name(p2p, wpa_s->conf->device_name);
 +
 +      if (wpa_s->conf->changed_parameters & CFG_CHANGED_DEVICE_TYPE)
 +              p2p_set_pri_dev_type(p2p, wpa_s->conf->device_type);
 +
 +      if (wpa_s->wps &&
 +          (wpa_s->conf->changed_parameters & CFG_CHANGED_CONFIG_METHODS))
 +              p2p_set_config_methods(p2p, wpa_s->wps->config_methods);
 +
 +      if (wpa_s->wps && (wpa_s->conf->changed_parameters & CFG_CHANGED_UUID))
 +              p2p_set_uuid(p2p, wpa_s->wps->uuid);
 +
 +      if (wpa_s->conf->changed_parameters & CFG_CHANGED_WPS_STRING) {
 +              p2p_set_manufacturer(p2p, wpa_s->conf->manufacturer);
 +              p2p_set_model_name(p2p, wpa_s->conf->model_name);
 +              p2p_set_model_number(p2p, wpa_s->conf->model_number);
 +              p2p_set_serial_number(p2p, wpa_s->conf->serial_number);
 +      }
 +
 +      if (wpa_s->conf->changed_parameters & CFG_CHANGED_SEC_DEVICE_TYPE)
 +              p2p_set_sec_dev_types(p2p,
 +                                    (void *) wpa_s->conf->sec_device_type,
 +                                    wpa_s->conf->num_sec_device_types);
 +
 +      if (wpa_s->conf->changed_parameters & CFG_CHANGED_VENDOR_EXTENSION) {
 +              int i;
 +              p2p_remove_wps_vendor_extensions(p2p);
 +              for (i = 0; i < MAX_WPS_VENDOR_EXT; i++) {
 +                      if (wpa_s->conf->wps_vendor_ext[i] == NULL)
 +                              continue;
 +                      p2p_add_wps_vendor_extension(
 +                              p2p, wpa_s->conf->wps_vendor_ext[i]);
 +              }
 +      }
 +
 +      if ((wpa_s->conf->changed_parameters & CFG_CHANGED_COUNTRY) &&
 +          wpa_s->conf->country[0] && wpa_s->conf->country[1]) {
 +              char country[3];
 +              country[0] = wpa_s->conf->country[0];
 +              country[1] = wpa_s->conf->country[1];
 +              country[2] = 0x04;
 +              p2p_set_country(p2p, country);
 +      }
 +
 +      if (wpa_s->conf->changed_parameters & CFG_CHANGED_P2P_SSID_POSTFIX) {
 +              p2p_set_ssid_postfix(p2p, (u8 *) wpa_s->conf->p2p_ssid_postfix,
 +                                   wpa_s->conf->p2p_ssid_postfix ?
 +                                   os_strlen(wpa_s->conf->p2p_ssid_postfix) :
 +                                   0);
 +      }
 +
 +      if (wpa_s->conf->changed_parameters & CFG_CHANGED_P2P_INTRA_BSS)
 +              p2p_set_intra_bss_dist(p2p, wpa_s->conf->p2p_intra_bss);
 +
 +      if (wpa_s->conf->changed_parameters & CFG_CHANGED_P2P_LISTEN_CHANNEL) {
 +              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.
 +                       */
 +                      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,
 +                                           channel_forced);
 +              if (ret)
 +                      wpa_printf(MSG_ERROR, "P2P: Own listen channel update "
 +                                 "failed: %d", ret);
 +      }
 +      if (wpa_s->conf->changed_parameters & CFG_CHANGED_P2P_OPER_CHANNEL) {
 +              u8 op_reg_class, op_channel, cfg_op_channel;
 +              int ret = 0;
 +              unsigned int r;
 +              if (wpa_s->conf->p2p_oper_reg_class &&
 +                  wpa_s->conf->p2p_oper_channel) {
 +                      op_reg_class = wpa_s->conf->p2p_oper_reg_class;
 +                      op_channel = wpa_s->conf->p2p_oper_channel;
 +                      cfg_op_channel = 1;
 +              } else {
 +                      op_reg_class = 81;
 +                      /*
 +                       * Use random operation channel from (1, 6, 11)
 +                       *if no other preference is indicated.
 +                       */
 +                      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,
 +                                         cfg_op_channel);
 +              if (ret)
 +                      wpa_printf(MSG_ERROR, "P2P: Own oper channel update "
 +                                 "failed: %d", ret);
 +      }
 +
 +      if (wpa_s->conf->changed_parameters & CFG_CHANGED_P2P_PREF_CHAN) {
 +              if (p2p_set_pref_chan(p2p, wpa_s->conf->num_p2p_pref_chan,
 +                                    wpa_s->conf->p2p_pref_chan) < 0) {
 +                      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);
 +}
 +
 +
 +int wpas_p2p_set_noa(struct wpa_supplicant *wpa_s, u8 count, int start,
 +                   int duration)
 +{
 +      if (!wpa_s->ap_iface)
 +              return -1;
 +      return hostapd_p2p_set_noa(wpa_s->ap_iface->bss[0], count, start,
 +                                 duration);
 +}
 +
 +
 +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;
 +
 +      wpa_s->global->cross_connection = enabled;
 +      p2p_set_cross_connect(wpa_s->global->p2p, enabled);
 +
 +      if (!enabled) {
 +              struct wpa_supplicant *iface;
 +
 +              for (iface = wpa_s->global->ifaces; iface; iface = iface->next)
 +              {
 +                      if (iface->cross_connect_enabled == 0)
 +                              continue;
 +
 +                      iface->cross_connect_enabled = 0;
 +                      iface->cross_connect_in_use = 0;
 +                      wpa_msg_global(iface->parent, MSG_INFO,
 +                                     P2P_EVENT_CROSS_CONNECT_DISABLE "%s %s",
 +                                     iface->ifname,
 +                                     iface->cross_connect_uplink);
 +              }
 +      }
 +
 +      return 0;
 +}
 +
 +
 +static void wpas_p2p_enable_cross_connect(struct wpa_supplicant *uplink)
 +{
 +      struct wpa_supplicant *iface;
 +
 +      if (!uplink->global->cross_connection)
 +              return;
 +
 +      for (iface = uplink->global->ifaces; iface; iface = iface->next) {
 +              if (!iface->cross_connect_enabled)
 +                      continue;
 +              if (os_strcmp(uplink->ifname, iface->cross_connect_uplink) !=
 +                  0)
 +                      continue;
 +              if (iface->ap_iface == NULL)
 +                      continue;
 +              if (iface->cross_connect_in_use)
 +                      continue;
 +
 +              iface->cross_connect_in_use = 1;
 +              wpa_msg_global(iface->parent, MSG_INFO,
 +                             P2P_EVENT_CROSS_CONNECT_ENABLE "%s %s",
 +                             iface->ifname, iface->cross_connect_uplink);
 +      }
 +}
 +
 +
 +static void wpas_p2p_disable_cross_connect(struct wpa_supplicant *uplink)
 +{
 +      struct wpa_supplicant *iface;
 +
 +      for (iface = uplink->global->ifaces; iface; iface = iface->next) {
 +              if (!iface->cross_connect_enabled)
 +                      continue;
 +              if (os_strcmp(uplink->ifname, iface->cross_connect_uplink) !=
 +                  0)
 +                      continue;
 +              if (!iface->cross_connect_in_use)
 +                      continue;
 +
 +              wpa_msg_global(iface->parent, MSG_INFO,
 +                             P2P_EVENT_CROSS_CONNECT_DISABLE "%s %s",
 +                             iface->ifname, iface->cross_connect_uplink);
 +              iface->cross_connect_in_use = 0;
 +      }
 +}
 +
 +
 +void wpas_p2p_notif_connected(struct wpa_supplicant *wpa_s)
 +{
 +      if (wpa_s->ap_iface || wpa_s->current_ssid == NULL ||
 +          wpa_s->current_ssid->mode != WPAS_MODE_INFRA ||
 +          wpa_s->cross_connect_disallowed)
 +              wpas_p2p_disable_cross_connect(wpa_s);
 +      else
 +              wpas_p2p_enable_cross_connect(wpa_s);
 +      if (!wpa_s->ap_iface &&
 +          eloop_cancel_timeout(wpas_p2p_group_idle_timeout, wpa_s, NULL) > 0)
 +              wpa_printf(MSG_DEBUG, "P2P: Cancelled P2P group idle timeout");
 +}
 +
 +
 +void wpas_p2p_notif_disconnected(struct wpa_supplicant *wpa_s)
 +{
 +      wpas_p2p_disable_cross_connect(wpa_s);
 +      if (!wpa_s->ap_iface &&
 +          !eloop_is_timeout_registered(wpas_p2p_group_idle_timeout,
 +                                       wpa_s, NULL))
 +              wpas_p2p_set_group_idle_timeout(wpa_s);
 +}
 +
 +
 +static void wpas_p2p_cross_connect_setup(struct wpa_supplicant *wpa_s)
 +{
 +      struct wpa_supplicant *iface;
 +
 +      if (!wpa_s->global->cross_connection)
 +              return;
 +
 +      for (iface = wpa_s->global->ifaces; iface; iface = iface->next) {
 +              if (iface == wpa_s)
 +                      continue;
 +              if (iface->drv_flags &
 +                  WPA_DRIVER_FLAGS_P2P_DEDICATED_INTERFACE)
 +                      continue;
 +              if ((iface->drv_flags & WPA_DRIVER_FLAGS_P2P_CAPABLE) &&
 +                  iface != wpa_s->parent)
 +                      continue;
 +
 +              wpa_s->cross_connect_enabled = 1;
 +              os_strlcpy(wpa_s->cross_connect_uplink, iface->ifname,
 +                         sizeof(wpa_s->cross_connect_uplink));
 +              wpa_printf(MSG_DEBUG, "P2P: Enable cross connection from "
 +                         "%s to %s whenever uplink is available",
 +                         wpa_s->ifname, wpa_s->cross_connect_uplink);
 +
 +              if (iface->ap_iface || iface->current_ssid == NULL ||
 +                  iface->current_ssid->mode != WPAS_MODE_INFRA ||
 +                  iface->cross_connect_disallowed ||
 +                  iface->wpa_state != WPA_COMPLETED)
 +                      break;
 +
 +              wpa_s->cross_connect_in_use = 1;
 +              wpa_msg_global(wpa_s->parent, MSG_INFO,
 +                             P2P_EVENT_CROSS_CONNECT_ENABLE "%s %s",
 +                             wpa_s->ifname, wpa_s->cross_connect_uplink);
 +              break;
 +      }
 +}
 +
 +
 +int wpas_p2p_notif_pbc_overlap(struct wpa_supplicant *wpa_s)
 +{
 +      if (wpa_s->p2p_group_interface != P2P_GROUP_INTERFACE_CLIENT &&
 +          !wpa_s->p2p_in_provisioning)
 +              return 0; /* not P2P client operation */
 +
 +      wpa_printf(MSG_DEBUG, "P2P: Terminate connection due to WPS PBC "
 +                 "session overlap");
 +      if (wpa_s != wpa_s->parent)
 +              wpa_msg_ctrl(wpa_s->parent, MSG_INFO, WPS_EVENT_OVERLAP);
- void wpas_p2p_update_channel_list(struct wpa_supplicant *wpa_s)
++      wpas_p2p_group_formation_failed(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);
 +}
 +
 +
-       struct wpa_supplicant *ifs;
++void wpas_p2p_update_channel_list(struct wpa_supplicant *wpa_s,
++                                enum wpas_p2p_channel_update_trig trig)
 +{
 +      struct p2p_channels 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;
-               }
++      struct wpa_used_freq_data *freqs = NULL;
++      unsigned int num = wpa_s->num_multichan_concurrent;
 +
 +      if (wpa_s->global == NULL || wpa_s->global->p2p == NULL)
 +              return;
 +
++      freqs = os_calloc(num, sizeof(struct wpa_used_freq_data));
++      if (!freqs)
++              return;
++
++      num = get_shared_radio_freqs_data(wpa_s, freqs, num);
++
 +      os_memset(&chan, 0, sizeof(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, &cli_chan);
 +
-               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);
-       }
++      wpas_p2p_optimize_listen_channel(wpa_s, freqs, num);
 +
-                               wpas_group_formation_completed(wpa_s, 0);
++      /*
++       * The used frequencies map changed, so it is possible that a GO is
++       * using a channel that is no longer valid for P2P use. It is also
++       * possible that due to policy consideration, it would be preferable to
++       * move it to a frequency already used by other station interfaces.
++       */
++      wpas_p2p_consider_moving_gos(wpa_s, freqs, num, trig);
++
++      os_free(freqs);
 +}
 +
 +
 +static void wpas_p2p_scan_res_ignore(struct wpa_supplicant *wpa_s,
 +                                   struct wpa_scan_results *scan_res)
 +{
 +      wpa_printf(MSG_DEBUG, "P2P: Ignore scan results");
 +}
 +
 +
 +int wpas_p2p_cancel(struct wpa_supplicant *wpa_s)
 +{
 +      struct wpa_global *global = wpa_s->global;
 +      int found = 0;
 +      const u8 *peer;
 +
 +      if (global->p2p == NULL)
 +              return -1;
 +
 +      wpa_printf(MSG_DEBUG, "P2P: Request to cancel group formation");
 +
 +      if (wpa_s->pending_interface_name[0] &&
 +          !is_zero_ether_addr(wpa_s->pending_interface_addr))
 +              found = 1;
 +
 +      peer = p2p_get_go_neg_peer(global->p2p);
 +      if (peer) {
 +              wpa_printf(MSG_DEBUG, "P2P: Unauthorize pending GO Neg peer "
 +                         MACSTR, MAC2STR(peer));
 +              p2p_unauthorize(global->p2p, peer);
 +              found = 1;
 +      }
 +
 +      if (wpa_s->scan_res_handler == wpas_p2p_scan_res_join) {
 +              wpa_printf(MSG_DEBUG, "P2P: Stop pending scan for join");
 +              wpa_s->scan_res_handler = wpas_p2p_scan_res_ignore;
 +              found = 1;
 +      }
 +
 +      if (wpa_s->pending_pd_before_join) {
 +              wpa_printf(MSG_DEBUG, "P2P: Stop pending PD before join");
 +              wpa_s->pending_pd_before_join = 0;
 +              found = 1;
 +      }
 +
 +      wpas_p2p_stop_find(wpa_s);
 +
 +      for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
 +              if (wpa_s == global->p2p_group_formation &&
 +                  (wpa_s->p2p_in_provisioning ||
 +                   wpa_s->parent->pending_interface_type ==
 +                   WPA_IF_P2P_CLIENT)) {
 +                      wpa_printf(MSG_DEBUG, "P2P: Interface %s in group "
 +                                 "formation found - cancelling",
 +                                 wpa_s->ifname);
 +                      found = 1;
 +                      eloop_cancel_timeout(wpas_p2p_group_formation_timeout,
 +                                           wpa_s->parent, NULL);
 +                      if (wpa_s->p2p_in_provisioning) {
-                       wpas_p2p_group_formation_failed(wpa_s);
++                              wpas_group_formation_completed(wpa_s, 0, 0);
 +                              break;
 +                      }
 +                      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_group_formation_completed(wpa_s, 1);
++                      wpas_p2p_group_formation_failed(wpa_s, 0);
 +              }
 +      }
 +
 +      if (!found) {
 +              wpa_printf(MSG_DEBUG, "P2P: No ongoing group formation found");
 +              return -1;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +void wpas_p2p_interface_unavailable(struct wpa_supplicant *wpa_s)
 +{
 +      if (wpa_s->current_ssid == NULL || !wpa_s->current_ssid->p2p_group)
 +              return;
 +
 +      wpa_printf(MSG_DEBUG, "P2P: Remove group due to driver resource not "
 +                 "being available anymore");
 +      wpas_p2p_group_delete(wpa_s, P2P_GROUP_REMOVAL_UNAVAILABLE);
 +}
 +
 +
 +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)
 +              return;
 +      p2p_set_best_channels(p2p, freq_24, freq_5, freq_overall);
 +}
 +
 +
 +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)
 +              return -1;
 +
 +      if (hwaddr_aton(addr, peer))
 +              return -1;
 +
 +      return p2p_unauthorize(p2p, peer);
 +}
 +
 +
 +/**
 + * wpas_p2p_disconnect - Disconnect from a P2P Group
 + * @wpa_s: Pointer to wpa_supplicant data
 + * Returns: 0 on success, -1 on failure
 + *
 + * This can be used to disconnect from a group in which the local end is a P2P
 + * Client or to end a P2P Group in case the local end is the Group Owner. If a
 + * virtual network interface was created for this group, that interface will be
 + * removed. Otherwise, only the configured P2P group network will be removed
 + * from the interface.
 + */
 +int wpas_p2p_disconnect(struct wpa_supplicant *wpa_s)
 +{
 +
 +      if (wpa_s == NULL)
 +              return -1;
 +
 +      return wpas_p2p_group_delete(wpa_s, P2P_GROUP_REMOVAL_REQUESTED) < 0 ?
 +              -1 : 0;
 +}
 +
 +
 +int wpas_p2p_in_progress(struct wpa_supplicant *wpa_s)
 +{
 +      int ret;
 +
 +      if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
 +              return 0;
 +
 +      ret = p2p_in_progress(wpa_s->global->p2p);
 +      if (ret == 0) {
 +              /*
 +               * Check whether there is an ongoing WPS provisioning step (or
 +               * other parts of group formation) on another interface since
 +               * p2p_in_progress() does not report this to avoid issues for
 +               * scans during such provisioning step.
 +               */
 +              if (wpa_s->global->p2p_group_formation &&
 +                  wpa_s->global->p2p_group_formation != wpa_s) {
 +                      wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Another interface (%s) "
 +                              "in group formation",
 +                              wpa_s->global->p2p_group_formation->ifname);
 +                      ret = 1;
 +              }
 +      }
 +
 +      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;
 +}
 +
 +
 +void wpas_p2p_network_removed(struct wpa_supplicant *wpa_s,
 +                            struct wpa_ssid *ssid)
 +{
 +      if (wpa_s->p2p_in_provisioning && ssid->p2p_group &&
 +          eloop_cancel_timeout(wpas_p2p_group_formation_timeout,
 +                               wpa_s->parent, NULL) > 0) {
 +              /**
 +               * Remove the network by scheduling the group formation
 +               * timeout to happen immediately. The teardown code
 +               * needs to be scheduled to run asynch later so that we
 +               * don't delete data from under ourselves unexpectedly.
 +               * Calling wpas_p2p_group_formation_timeout directly
 +               * causes a series of crashes in WPS failure scenarios.
 +               */
 +              wpa_printf(MSG_DEBUG, "P2P: Canceled group formation due to "
 +                         "P2P group network getting removed");
 +              eloop_register_timeout(0, 0, wpas_p2p_group_formation_timeout,
 +                                     wpa_s->parent, NULL);
 +      }
 +}
 +
 +
 +struct wpa_ssid * wpas_p2p_get_persistent(struct wpa_supplicant *wpa_s,
 +                                        const u8 *addr, const u8 *ssid,
 +                                        size_t ssid_len)
 +{
 +      struct wpa_ssid *s;
 +      size_t i;
 +
 +      for (s = wpa_s->conf->ssid; s; s = s->next) {
 +              if (s->disabled != 2)
 +                      continue;
 +              if (ssid &&
 +                  (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 * 2 * ETH_ALEN,
 +                                    addr, ETH_ALEN) == 0)
 +                              return s; /* peer is P2P client in persistent
 +                                         * group */
 +              }
 +      }
 +
 +      return NULL;
 +}
 +
 +
 +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);
-       wpa_s = wpa_s->parent;
++                      wpas_group_formation_completed(wpa_s, 1, 0);
 +              }
 +      }
 +      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 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;
-       for (s = wpa_s->parent->conf->ssid; s; s = s->next) {
++      wpa_s = wpa_s->global->p2p_init_wpa_s;
 +      offchannel_send_action_done(wpa_s);
 +      if (group_added)
 +              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_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;
 +
 +      if (wpas_p2p_peer_go(wpa_s, wpa_s->pending_join_dev_addr) > 0)
 +              return 0; /* peer operating as a GO */
 +
 +      wpa_dbg(wpa_s, MSG_DEBUG, "P2P: GO not found for p2p_connect-auto - "
 +              "fallback to GO Negotiation");
 +      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 res == 1 ? 2 : 1;
 +}
 +
 +
 +unsigned int wpas_p2p_search_delay(struct wpa_supplicant *wpa_s)
 +{
 +      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",
 +                      wpa_s->conf->p2p_search_delay);
 +              return wpa_s->conf->p2p_search_delay;
 +      }
 +
 +      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;
++      struct wpa_supplicant *p2p_wpa_s = wpa_s->global->p2p_init_wpa_s;
 +
 +      wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Remove client " MACSTR, MAC2STR(peer));
 +
 +      /* Remove from any persistent group */
-                       wpas_remove_persistent_peer(wpa_s, s, peer, 0);
-               wpas_p2p_remove_psk(wpa_s->parent, s, peer, iface_addr);
++      for (s = p2p_wpa_s->conf->ssid; s; s = s->next) {
 +              if (s->disabled != 2 || s->mode != WPAS_MODE_P2P_GO)
 +                      continue;
 +              if (!iface_addr)
- void wpas_p2p_indicate_state_change(struct wpa_supplicant *wpa_s)
++                      wpas_remove_persistent_peer(p2p_wpa_s, s, peer, 0);
++              wpas_p2p_remove_psk(p2p_wpa_s, 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;
 +
 +      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;
 +}
 +
 +
 +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 (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 */
 +
 +
 +static void wpas_p2p_optimize_listen_channel(struct wpa_supplicant *wpa_s,
 +                                           struct wpa_used_freq_data *freqs,
 +                                           unsigned int num)
 +{
 +      u8 curr_chan, cand, chan;
 +      unsigned int i;
 +
++      /*
++       * 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;
++
 +      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);
 +      }
 +}
 +
 +
-       struct wpa_used_freq_data *freqs;
-       unsigned int num = wpa_s->num_multichan_concurrent;
++static int wpas_p2p_move_go_csa(struct wpa_supplicant *wpa_s)
 +{
-       if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
++      if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_AP_CSA)) {
++              wpa_dbg(wpa_s, MSG_DEBUG, "CSA is not enabled");
++              return -1;
++      }
 +
-       /*
-        * 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)
++      /* TODO: Add CSA support */
++      wpa_dbg(wpa_s, MSG_DEBUG, "Moving GO with CSA is not implemented");
++      return -1;
++}
++
++
++static void wpas_p2p_move_go_no_csa(struct wpa_supplicant *wpa_s)
++{
++      struct p2p_go_neg_results params;
++      struct wpa_ssid *current_ssid = wpa_s->current_ssid;
++
++      wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_REMOVE_AND_REFORM_GROUP);
++
++      wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Move GO from freq=%d MHz",
++              current_ssid->frequency);
++
++      /* Stop the AP functionality */
++      /* TODO: Should do this in a way that does not indicated to possible
++       * P2P Clients in the group that the group is terminated. */
++      wpa_supplicant_ap_deinit(wpa_s);
++
++      /* Reselect the GO frequency */
++      if (wpas_p2p_init_go_params(wpa_s, &params, 0, 0, 0, NULL)) {
++              wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Failed to reselect freq");
++              wpas_p2p_group_delete(wpa_s,
++                                    P2P_GROUP_REMOVAL_GO_LEAVE_CHANNEL);
 +              return;
++      }
++      wpa_dbg(wpa_s, MSG_DEBUG, "P2P: New freq selected for the GO (%u MHz)",
++              params.freq);
 +
-       if (!wpa_s->current_ssid || wpa_s->wpa_state != WPA_COMPLETED)
++      if (params.freq &&
++          !p2p_supported_freq_go(wpa_s->global->p2p, params.freq)) {
++              wpa_printf(MSG_DEBUG,
++                         "P2P: Selected freq (%u MHz) is not valid for P2P",
++                         params.freq);
++              wpas_p2p_group_delete(wpa_s,
++                                    P2P_GROUP_REMOVAL_GO_LEAVE_CHANNEL);
 +              return;
++      }
 +
-       wpas_p2p_optimize_listen_channel(wpa_s, freqs, num);
++      /* Update the frequency */
++      current_ssid->frequency = params.freq;
++      wpa_s->connect_without_scan = current_ssid;
++      wpa_s->reassociate = 1;
++      wpa_s->disconnected = 0;
++      wpa_supplicant_req_scan(wpa_s, 0, 0);
++}
++
++
++static void wpas_p2p_move_go(void *eloop_ctx, void *timeout_ctx)
++{
++      struct wpa_supplicant *wpa_s = eloop_ctx;
++
++      if (!wpa_s->ap_iface || !wpa_s->current_ssid)
 +              return;
 +
++      wpas_p2p_go_update_common_freqs(wpa_s);
++
++      /*
++       * First, try a channel switch flow. If it is not supported or fails,
++       * take down the GO and bring it up again.
++       */
++      if (wpas_p2p_move_go_csa(wpa_s) < 0)
++              wpas_p2p_move_go_no_csa(wpa_s);
++}
++
++
++static void wpas_p2p_reconsider_moving_go(void *eloop_ctx, void *timeout_ctx)
++{
++      struct wpa_supplicant *wpa_s = eloop_ctx;
++      struct wpa_used_freq_data *freqs = NULL;
++      unsigned int num = wpa_s->num_multichan_concurrent;
++
 +      freqs = os_calloc(num, sizeof(struct wpa_used_freq_data));
 +      if (!freqs)
 +              return;
 +
 +      num = get_shared_radio_freqs_data(wpa_s, freqs, num);
 +
++      /* Previous attempt to move a GO was not possible -- try again. */
++      wpas_p2p_consider_moving_gos(wpa_s, freqs, num,
++                                   WPAS_P2P_CHANNEL_UPDATE_ANY);
++
 +      os_free(freqs);
 +}
 +
 +
++/*
++ * Consider moving a GO from its currently used frequency:
++ * 1. It is possible that due to regulatory consideration the frequency
++ *    can no longer be used and there is a need to evacuate the GO.
++ * 2. It is possible that due to MCC considerations, it would be preferable
++ *    to move the GO to a channel that is currently used by some other
++ *    station interface.
++ *
++ * In case a frequency that became invalid is once again valid, cancel a
++ * previously initiated GO frequency change.
++ */
++static void wpas_p2p_consider_moving_one_go(struct wpa_supplicant *wpa_s,
++                                          struct wpa_used_freq_data *freqs,
++                                          unsigned int num)
++{
++      unsigned int i, invalid_freq = 0, policy_move = 0, flags = 0;
++      unsigned int timeout;
++      int freq;
++
++      wpas_p2p_go_update_common_freqs(wpa_s);
++
++      freq = wpa_s->current_ssid->frequency;
++      for (i = 0, invalid_freq = 0; i < num; i++) {
++              if (freqs[i].freq == freq) {
++                      flags = freqs[i].flags;
++
++                      /* The channel is invalid, must change it */
++                      if (!p2p_supported_freq_go(wpa_s->global->p2p, freq)) {
++                              wpa_dbg(wpa_s, MSG_DEBUG,
++                                      "P2P: Freq=%d MHz no longer valid for GO",
++                                      freq);
++                              invalid_freq = 1;
++                      }
++              } else if (freqs[i].flags == 0) {
++                      /* Freq is not used by any other station interface */
++                      continue;
++              } else if (!p2p_supported_freq(wpa_s->global->p2p,
++                                             freqs[i].freq)) {
++                      /* Freq is not valid for P2P use cases */
++                      continue;
++              } else if (wpa_s->conf->p2p_go_freq_change_policy ==
++                         P2P_GO_FREQ_MOVE_SCM) {
++                      policy_move = 1;
++              } else if (wpa_s->conf->p2p_go_freq_change_policy ==
++                         P2P_GO_FREQ_MOVE_SCM_PEER_SUPPORTS &&
++                         wpas_p2p_go_is_peer_freq(wpa_s, freqs[i].freq)) {
++                      policy_move = 1;
++              }
++      }
++
++      wpa_dbg(wpa_s, MSG_DEBUG,
++              "P2P: GO move: invalid_freq=%u, policy_move=%u, flags=0x%X",
++              invalid_freq, policy_move, flags);
++
++      /*
++       * The channel is valid, or we are going to have a policy move, so
++       * cancel timeout.
++       */
++      if (!invalid_freq || policy_move) {
++              wpa_dbg(wpa_s, MSG_DEBUG,
++                      "P2P: Cancel a GO move from freq=%d MHz", freq);
++              eloop_cancel_timeout(wpas_p2p_move_go, wpa_s, NULL);
++
++              if (wpas_p2p_in_progress(wpa_s)) {
++                      wpa_dbg(wpa_s, MSG_DEBUG,
++                              "P2P: GO move: policy CS is not allowed - setting timeout to re-consider GO move");
++                      eloop_cancel_timeout(wpas_p2p_reconsider_moving_go,
++                                           wpa_s, NULL);
++                      eloop_register_timeout(P2P_RECONSIDER_GO_MOVE_DELAY, 0,
++                                             wpas_p2p_reconsider_moving_go,
++                                             wpa_s, NULL);
++                      return;
++              }
++      }
++
++      if (!invalid_freq && (!policy_move || flags != 0)) {
++              wpa_dbg(wpa_s, MSG_DEBUG,
++                      "P2P: Not initiating a GO frequency change");
++              return;
++      }
++
++      if (invalid_freq && !wpas_p2p_disallowed_freq(wpa_s->global, freq))
++              timeout = P2P_GO_FREQ_CHANGE_TIME;
++      else
++              timeout = 0;
++
++      wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Move GO from freq=%d MHz in %d secs",
++              freq, timeout);
++      eloop_cancel_timeout(wpas_p2p_move_go, wpa_s, NULL);
++      eloop_register_timeout(timeout, 0, wpas_p2p_move_go, wpa_s, NULL);
++}
++
++
++static void wpas_p2p_consider_moving_gos(struct wpa_supplicant *wpa_s,
++                                       struct wpa_used_freq_data *freqs,
++                                       unsigned int num,
++                                       enum wpas_p2p_channel_update_trig trig)
++{
++      struct wpa_supplicant *ifs;
++
++      eloop_cancel_timeout(wpas_p2p_reconsider_moving_go, ELOOP_ALL_CTX,
++                           NULL);
++
++      /*
++       * Travers all the radio interfaces, and for each GO interface, check
++       * if there is a need to move the GO from the frequency it is using,
++       * or in case the frequency is valid again, cancel the evacuation flow.
++       */
++      dl_list_for_each(ifs, &wpa_s->radio->ifaces, struct wpa_supplicant,
++                       radio_list) {
++              if (ifs->current_ssid == NULL ||
++                  ifs->current_ssid->mode != WPAS_MODE_P2P_GO)
++                      continue;
++
++              /*
++               * The GO was just started or completed channel switch, no need
++               * to move it.
++               */
++              if (wpa_s == ifs &&
++                  (trig == WPAS_P2P_CHANNEL_UPDATE_STATE_CHANGE ||
++                   trig == WPAS_P2P_CHANNEL_UPDATE_CS)) {
++                      wpa_dbg(wpa_s, MSG_DEBUG,
++                              "P2P: GO move - schedule re-consideration");
++                      eloop_register_timeout(P2P_RECONSIDER_GO_MOVE_DELAY, 0,
++                                             wpas_p2p_reconsider_moving_go,
++                                             wpa_s, NULL);
++                      continue;
++              }
++
++              wpas_p2p_consider_moving_one_go(ifs, freqs, num);
++      }
++}
++
++
++void wpas_p2p_indicate_state_change(struct wpa_supplicant *wpa_s)
++{
++      if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
++              return;
++
++      wpas_p2p_update_channel_list(wpa_s,
++                                   WPAS_P2P_CHANNEL_UPDATE_STATE_CHANGE);
++}
++
++
 +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);
 +}
index b7861786ca22d4732ea6d0955bc6a53e720ad6d2,0000000000000000000000000000000000000000..56e683498d66f60738d64adeff492ee2a139f237
mode 100644,000000..100644
--- /dev/null
@@@ -1,316 -1,0 +1,335 @@@
-                                 int connection_timeout);
 +/*
 + * wpa_supplicant - P2P
 + * Copyright (c) 2009-2010, Atheros Communications
 + *
 + * This software may be distributed under the terms of the BSD license.
 + * See README for more details.
 + */
 +
 +#ifndef P2P_SUPPLICANT_H
 +#define P2P_SUPPLICANT_H
 +
 +enum p2p_wps_method;
 +struct p2p_go_neg_results;
 +enum p2p_send_action_result;
 +struct p2p_peer_info;
 +struct p2p_channels;
 +struct wps_event_fail;
 +struct p2ps_provision;
 +
++enum wpas_p2p_channel_update_trig {
++      WPAS_P2P_CHANNEL_UPDATE_ANY,
++      WPAS_P2P_CHANNEL_UPDATE_DRIVER,
++      WPAS_P2P_CHANNEL_UPDATE_STATE_CHANGE,
++      WPAS_P2P_CHANNEL_UPDATE_AVOID,
++      WPAS_P2P_CHANNEL_UPDATE_DISALLOW,
++      WPAS_P2P_CHANNEL_UPDATE_CS,
++};
++
 +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, 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 vht);
 +int wpas_p2p_group_add_persistent(struct wpa_supplicant *wpa_s,
 +                                struct wpa_ssid *ssid, int addr_allocated,
 +                                int force_freq, int neg_freq, int ht40,
 +                                int vht, const struct p2p_channels *channels,
- void wpas_p2p_group_formation_failed(struct wpa_supplicant *wpa_s);
++                                int connection_timeout, int force_scan);
 +struct p2p_group * wpas_p2p_group_init(struct wpa_supplicant *wpa_s,
 +                                     struct wpa_ssid *ssid);
 +enum wpas_p2p_prov_disc_use {
 +      WPAS_P2P_PD_FOR_GO_NEG,
 +      WPAS_P2P_PD_FOR_JOIN,
 +      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,
 +                     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);
 +int wpas_p2p_scan_result_text(const u8 *ies, size_t ies_len, char *buf,
 +                            char *end);
 +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,
 +                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);
 +void wpas_p2p_scan_ie(struct wpa_supplicant *wpa_s, struct wpabuf *ies);
-                            u16 config_methods, const char *svc_info);
 +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,
 +                                   const u8 *dst, const char *role);
 +int wpas_p2p_sd_cancel_request(struct wpa_supplicant *wpa_s, u64 req);
 +void wpas_p2p_sd_response(struct wpa_supplicant *wpa_s, int freq,
 +                        const u8 *dst, u8 dialog_token,
 +                        const struct wpabuf *resp_tlvs);
 +void wpas_p2p_sd_service_update(struct wpa_supplicant *wpa_s);
 +void wpas_p2p_service_flush(struct wpa_supplicant *wpa_s);
 +int wpas_p2p_service_add_bonjour(struct wpa_supplicant *wpa_s,
 +                               struct wpabuf *query, struct wpabuf *resp);
 +int wpas_p2p_service_del_bonjour(struct wpa_supplicant *wpa_s,
 +                               const struct wpabuf *query);
 +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,
-                         int ssi_signal);
++                           u16 config_methods, const char *svc_info,
++                           const u8 *cpt_priority);
 +int wpas_p2p_service_del_asp(struct wpa_supplicant *wpa_s, u32 adv_id);
++void wpas_p2p_service_flush_asp(struct wpa_supplicant *wpa_s);
 +int wpas_p2p_service_p2ps_id_exists(struct wpa_supplicant *wpa_s, u32 adv_id);
++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);
 +int wpas_p2p_reject(struct wpa_supplicant *wpa_s, const u8 *addr);
 +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 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);
 +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,
 +                      unsigned int interval);
 +int wpas_p2p_deauth_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_disassoc_notif(struct wpa_supplicant *wpa_s, const u8 *bssid,
 +                           u16 reason_code, const u8 *ie, size_t ie_len,
 +                           int locally_generated);
 +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);
 +int wpas_p2p_cancel(struct wpa_supplicant *wpa_s);
 +int wpas_p2p_unauthorize(struct wpa_supplicant *wpa_s, const char *addr);
 +int wpas_p2p_disconnect(struct wpa_supplicant *wpa_s);
 +struct wpa_ssid * wpas_p2p_get_persistent(struct wpa_supplicant *wpa_s,
 +                                        const u8 *addr, const u8 *ssid,
 +                                        size_t ssid_len);
 +void wpas_p2p_notify_ap_sta_authorized(struct wpa_supplicant *wpa_s,
 +                                     const u8 *addr);
 +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
 +
 +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,
- void wpas_p2p_update_channel_list(struct wpa_supplicant *wpa_s);
++                        unsigned int rx_freq, int ssi_signal);
 +void wpas_p2p_wps_success(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
 +                        int registrar);
-                                       int ssi_signal)
++
++void wpas_p2p_update_channel_list(struct wpa_supplicant *wpa_s,
++                                enum wpas_p2p_channel_update_trig trig);
++
 +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 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,
- static inline void wpas_p2p_update_channel_list(struct wpa_supplicant *wpa_s)
++                                      unsigned int rx_freq, 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,
++                           enum wpas_p2p_channel_update_trig trig)
 +{
 +}
 +
 +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 */
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..fc07b07462f5ab05cebecfa5eb066b5dd5a11b40
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1273 @@@
++/*
++ * wpa_supplicant - P2P service discovery
++ * 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 "utils/includes.h"
++
++#include "utils/common.h"
++#include "p2p/p2p.h"
++#include "wpa_supplicant_i.h"
++#include "notify.h"
++#include "p2p_supplicant.h"
++
++
++/*
++ * DNS Header section is used only to calculate compression pointers, so the
++ * contents of this data does not matter, but the length needs to be reserved
++ * in the virtual packet.
++ */
++#define DNS_HEADER_LEN 12
++
++/*
++ * 27-octet in-memory packet from P2P specification containing two implied
++ * queries for _tcp.lcoal. PTR IN and _udp.local. PTR IN
++ */
++#define P2P_SD_IN_MEMORY_LEN 27
++
++static int p2p_sd_dns_uncompress_label(char **upos, char *uend, u8 *start,
++                                     u8 **spos, const u8 *end)
++{
++      while (*spos < end) {
++              u8 val = ((*spos)[0] & 0xc0) >> 6;
++              int len;
++
++              if (val == 1 || val == 2) {
++                      /* These are reserved values in RFC 1035 */
++                      wpa_printf(MSG_DEBUG, "P2P: Invalid domain name "
++                                 "sequence starting with 0x%x", val);
++                      return -1;
++              }
++
++              if (val == 3) {
++                      u16 offset;
++                      u8 *spos_tmp;
++
++                      /* Offset */
++                      if (*spos + 2 > end) {
++                              wpa_printf(MSG_DEBUG, "P2P: No room for full "
++                                         "DNS offset field");
++                              return -1;
++                      }
++
++                      offset = (((*spos)[0] & 0x3f) << 8) | (*spos)[1];
++                      if (offset >= *spos - start) {
++                              wpa_printf(MSG_DEBUG, "P2P: Invalid DNS "
++                                         "pointer offset %u", offset);
++                              return -1;
++                      }
++
++                      (*spos) += 2;
++                      spos_tmp = start + offset;
++                      return p2p_sd_dns_uncompress_label(upos, uend, start,
++                                                         &spos_tmp,
++                                                         *spos - 2);
++              }
++
++              /* Label */
++              len = (*spos)[0] & 0x3f;
++              if (len == 0)
++                      return 0;
++
++              (*spos)++;
++              if (*spos + len > end) {
++                      wpa_printf(MSG_DEBUG, "P2P: Invalid domain name "
++                                 "sequence - no room for label with length "
++                                 "%u", len);
++                      return -1;
++              }
++
++              if (*upos + len + 2 > uend)
++                      return -2;
++
++              os_memcpy(*upos, *spos, len);
++              *spos += len;
++              *upos += len;
++              (*upos)[0] = '.';
++              (*upos)++;
++              (*upos)[0] = '\0';
++      }
++
++      return 0;
++}
++
++
++/* Uncompress domain names per RFC 1035 using the P2P SD in-memory packet.
++ * Returns -1 on parsing error (invalid input sequence), -2 if output buffer is
++ * not large enough */
++static int p2p_sd_dns_uncompress(char *buf, size_t buf_len, const u8 *msg,
++                               size_t msg_len, size_t offset)
++{
++      /* 27-octet in-memory packet from P2P specification */
++      const char *prefix = "\x04_tcp\x05local\x00\x00\x0C\x00\x01"
++              "\x04_udp\xC0\x11\x00\x0C\x00\x01";
++      u8 *tmp, *end, *spos;
++      char *upos, *uend;
++      int ret = 0;
++
++      if (buf_len < 2)
++              return -1;
++      if (offset > msg_len)
++              return -1;
++
++      tmp = os_malloc(DNS_HEADER_LEN + P2P_SD_IN_MEMORY_LEN + msg_len);
++      if (tmp == NULL)
++              return -1;
++      spos = tmp + DNS_HEADER_LEN + P2P_SD_IN_MEMORY_LEN;
++      end = spos + msg_len;
++      spos += offset;
++
++      os_memset(tmp, 0, DNS_HEADER_LEN);
++      os_memcpy(tmp + DNS_HEADER_LEN, prefix, P2P_SD_IN_MEMORY_LEN);
++      os_memcpy(tmp + DNS_HEADER_LEN + P2P_SD_IN_MEMORY_LEN, msg, msg_len);
++
++      upos = buf;
++      uend = buf + buf_len;
++
++      ret = p2p_sd_dns_uncompress_label(&upos, uend, tmp, &spos, end);
++      if (ret) {
++              os_free(tmp);
++              return ret;
++      }
++
++      if (upos == buf) {
++              upos[0] = '.';
++              upos[1] = '\0';
++      } else if (upos[-1] == '.')
++              upos[-1] = '\0';
++
++      os_free(tmp);
++      return 0;
++}
++
++
++static struct p2p_srv_bonjour *
++wpas_p2p_service_get_bonjour(struct wpa_supplicant *wpa_s,
++                           const struct wpabuf *query)
++{
++      struct p2p_srv_bonjour *bsrv;
++      size_t len;
++
++      len = wpabuf_len(query);
++      dl_list_for_each(bsrv, &wpa_s->global->p2p_srv_bonjour,
++                       struct p2p_srv_bonjour, list) {
++              if (len == wpabuf_len(bsrv->query) &&
++                  os_memcmp(wpabuf_head(query), wpabuf_head(bsrv->query),
++                            len) == 0)
++                      return bsrv;
++      }
++      return NULL;
++}
++
++
++static struct p2p_srv_upnp *
++wpas_p2p_service_get_upnp(struct wpa_supplicant *wpa_s, u8 version,
++                        const char *service)
++{
++      struct p2p_srv_upnp *usrv;
++
++      dl_list_for_each(usrv, &wpa_s->global->p2p_srv_upnp,
++                       struct p2p_srv_upnp, list) {
++              if (version == usrv->version &&
++                  os_strcmp(service, usrv->service) == 0)
++                      return usrv;
++      }
++      return NULL;
++}
++
++
++static void wpas_sd_add_empty(struct wpabuf *resp, u8 srv_proto,
++                            u8 srv_trans_id, u8 status)
++{
++      u8 *len_pos;
++
++      if (wpabuf_tailroom(resp) < 5)
++              return;
++
++      /* Length (to be filled) */
++      len_pos = wpabuf_put(resp, 2);
++      wpabuf_put_u8(resp, srv_proto);
++      wpabuf_put_u8(resp, srv_trans_id);
++      /* Status Code */
++      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)
++{
++      struct p2p_srv_bonjour *bsrv;
++      u8 *len_pos;
++
++      wpa_printf(MSG_DEBUG, "P2P: SD Request for all Bonjour services");
++
++      if (dl_list_empty(&wpa_s->global->p2p_srv_bonjour)) {
++              wpa_printf(MSG_DEBUG, "P2P: Bonjour protocol not available");
++              return;
++      }
++
++      dl_list_for_each(bsrv, &wpa_s->global->p2p_srv_bonjour,
++                       struct p2p_srv_bonjour, list) {
++              if (wpabuf_tailroom(resp) <
++                  5 + wpabuf_len(bsrv->query) + wpabuf_len(bsrv->resp))
++                      return;
++              /* Length (to be filled) */
++              len_pos = wpabuf_put(resp, 2);
++              wpabuf_put_u8(resp, P2P_SERV_BONJOUR);
++              wpabuf_put_u8(resp, srv_trans_id);
++              /* Status Code */
++              wpabuf_put_u8(resp, P2P_SD_SUCCESS);
++              wpa_hexdump_ascii(MSG_DEBUG, "P2P: Matching Bonjour service",
++                                wpabuf_head(bsrv->resp),
++                                wpabuf_len(bsrv->resp));
++              /* Response Data */
++              wpabuf_put_buf(resp, bsrv->query); /* Key */
++              wpabuf_put_buf(resp, bsrv->resp); /* Value */
++              WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos -
++                           2);
++      }
++}
++
++
++static int match_bonjour_query(struct p2p_srv_bonjour *bsrv, const u8 *query,
++                             size_t query_len)
++{
++      char str_rx[256], str_srv[256];
++
++      if (query_len < 3 || wpabuf_len(bsrv->query) < 3)
++              return 0; /* Too short to include DNS Type and Version */
++      if (os_memcmp(query + query_len - 3,
++                    wpabuf_head_u8(bsrv->query) + wpabuf_len(bsrv->query) - 3,
++                    3) != 0)
++              return 0; /* Mismatch in DNS Type or Version */
++      if (query_len == wpabuf_len(bsrv->query) &&
++          os_memcmp(query, wpabuf_head(bsrv->query), query_len - 3) == 0)
++              return 1; /* Binary match */
++
++      if (p2p_sd_dns_uncompress(str_rx, sizeof(str_rx), query, query_len - 3,
++                                0))
++              return 0; /* Failed to uncompress query */
++      if (p2p_sd_dns_uncompress(str_srv, sizeof(str_srv),
++                                wpabuf_head(bsrv->query),
++                                wpabuf_len(bsrv->query) - 3, 0))
++              return 0; /* Failed to uncompress service */
++
++      return os_strcmp(str_rx, str_srv) == 0;
++}
++
++
++static void wpas_sd_req_bonjour(struct wpa_supplicant *wpa_s,
++                              struct wpabuf *resp, u8 srv_trans_id,
++                              const u8 *query, size_t query_len)
++{
++      struct p2p_srv_bonjour *bsrv;
++      u8 *len_pos;
++      int matches = 0;
++
++      wpa_hexdump_ascii(MSG_DEBUG, "P2P: SD Request for Bonjour",
++                        query, query_len);
++      if (dl_list_empty(&wpa_s->global->p2p_srv_bonjour)) {
++              wpa_printf(MSG_DEBUG, "P2P: Bonjour protocol not available");
++              wpas_sd_add_proto_not_avail(resp, P2P_SERV_BONJOUR,
++                                          srv_trans_id);
++              return;
++      }
++
++      if (query_len == 0) {
++              wpas_sd_all_bonjour(wpa_s, resp, srv_trans_id);
++              return;
++      }
++
++      dl_list_for_each(bsrv, &wpa_s->global->p2p_srv_bonjour,
++                       struct p2p_srv_bonjour, list) {
++              if (!match_bonjour_query(bsrv, query, query_len))
++                      continue;
++
++              if (wpabuf_tailroom(resp) <
++                  5 + query_len + wpabuf_len(bsrv->resp))
++                      return;
++
++              matches++;
++
++              /* Length (to be filled) */
++              len_pos = wpabuf_put(resp, 2);
++              wpabuf_put_u8(resp, P2P_SERV_BONJOUR);
++              wpabuf_put_u8(resp, srv_trans_id);
++
++              /* Status Code */
++              wpabuf_put_u8(resp, P2P_SD_SUCCESS);
++              wpa_hexdump_ascii(MSG_DEBUG, "P2P: Matching Bonjour service",
++                                wpabuf_head(bsrv->resp),
++                                wpabuf_len(bsrv->resp));
++
++              /* Response Data */
++              wpabuf_put_data(resp, query, query_len); /* Key */
++              wpabuf_put_buf(resp, bsrv->resp); /* Value */
++
++              WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - 2);
++      }
++
++      if (matches == 0) {
++              wpa_printf(MSG_DEBUG, "P2P: Requested Bonjour service not "
++                         "available");
++              if (wpabuf_tailroom(resp) < 5)
++                      return;
++
++              /* Length (to be filled) */
++              len_pos = wpabuf_put(resp, 2);
++              wpabuf_put_u8(resp, P2P_SERV_BONJOUR);
++              wpabuf_put_u8(resp, srv_trans_id);
++
++              /* Status Code */
++              wpabuf_put_u8(resp, P2P_SD_REQUESTED_INFO_NOT_AVAILABLE);
++              /* Response Data: empty */
++              WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos -
++                           2);
++      }
++}
++
++
++static void wpas_sd_all_upnp(struct wpa_supplicant *wpa_s,
++                           struct wpabuf *resp, u8 srv_trans_id)
++{
++      struct p2p_srv_upnp *usrv;
++      u8 *len_pos;
++
++      wpa_printf(MSG_DEBUG, "P2P: SD Request for all UPnP services");
++
++      if (dl_list_empty(&wpa_s->global->p2p_srv_upnp)) {
++              wpa_printf(MSG_DEBUG, "P2P: UPnP protocol not available");
++              return;
++      }
++
++      dl_list_for_each(usrv, &wpa_s->global->p2p_srv_upnp,
++                       struct p2p_srv_upnp, list) {
++              if (wpabuf_tailroom(resp) < 5 + 1 + os_strlen(usrv->service))
++                      return;
++
++              /* Length (to be filled) */
++              len_pos = wpabuf_put(resp, 2);
++              wpabuf_put_u8(resp, P2P_SERV_UPNP);
++              wpabuf_put_u8(resp, srv_trans_id);
++
++              /* Status Code */
++              wpabuf_put_u8(resp, P2P_SD_SUCCESS);
++              /* Response Data */
++              wpabuf_put_u8(resp, usrv->version);
++              wpa_printf(MSG_DEBUG, "P2P: Matching UPnP Service: %s",
++                         usrv->service);
++              wpabuf_put_str(resp, usrv->service);
++              WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos -
++                           2);
++      }
++}
++
++
++static void wpas_sd_req_upnp(struct wpa_supplicant *wpa_s,
++                           struct wpabuf *resp, u8 srv_trans_id,
++                           const u8 *query, size_t query_len)
++{
++      struct p2p_srv_upnp *usrv;
++      u8 *len_pos;
++      u8 version;
++      char *str;
++      int count = 0;
++
++      wpa_hexdump_ascii(MSG_DEBUG, "P2P: SD Request for UPnP",
++                        query, query_len);
++
++      if (dl_list_empty(&wpa_s->global->p2p_srv_upnp)) {
++              wpa_printf(MSG_DEBUG, "P2P: UPnP protocol not available");
++              wpas_sd_add_proto_not_avail(resp, P2P_SERV_UPNP,
++                                          srv_trans_id);
++              return;
++      }
++
++      if (query_len == 0) {
++              wpas_sd_all_upnp(wpa_s, resp, srv_trans_id);
++              return;
++      }
++
++      if (wpabuf_tailroom(resp) < 5)
++              return;
++
++      /* Length (to be filled) */
++      len_pos = wpabuf_put(resp, 2);
++      wpabuf_put_u8(resp, P2P_SERV_UPNP);
++      wpabuf_put_u8(resp, srv_trans_id);
++
++      version = query[0];
++      str = os_malloc(query_len);
++      if (str == NULL)
++              return;
++      os_memcpy(str, query + 1, query_len - 1);
++      str[query_len - 1] = '\0';
++
++      dl_list_for_each(usrv, &wpa_s->global->p2p_srv_upnp,
++                       struct p2p_srv_upnp, list) {
++              if (version != usrv->version)
++                      continue;
++
++              if (os_strcmp(str, "ssdp:all") != 0 &&
++                  os_strstr(usrv->service, str) == NULL)
++                      continue;
++
++              if (wpabuf_tailroom(resp) < 2)
++                      break;
++              if (count == 0) {
++                      /* Status Code */
++                      wpabuf_put_u8(resp, P2P_SD_SUCCESS);
++                      /* Response Data */
++                      wpabuf_put_u8(resp, version);
++              } else
++                      wpabuf_put_u8(resp, ',');
++
++              count++;
++
++              wpa_printf(MSG_DEBUG, "P2P: Matching UPnP Service: %s",
++                         usrv->service);
++              if (wpabuf_tailroom(resp) < os_strlen(usrv->service))
++                      break;
++              wpabuf_put_str(resp, usrv->service);
++      }
++      os_free(str);
++
++      if (count == 0) {
++              wpa_printf(MSG_DEBUG, "P2P: Requested UPnP service not "
++                         "available");
++              /* Status Code */
++              wpabuf_put_u8(resp, P2P_SD_REQUESTED_INFO_NOT_AVAILABLE);
++              /* Response Data: empty */
++      }
++
++      WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - 2);
++}
++
++
++#ifdef CONFIG_WIFI_DISPLAY
++static void wpas_sd_req_wfd(struct wpa_supplicant *wpa_s,
++                          struct wpabuf *resp, u8 srv_trans_id,
++                          const u8 *query, size_t query_len)
++{
++      const u8 *pos;
++      u8 role;
++      u8 *len_pos;
++
++      wpa_hexdump(MSG_DEBUG, "P2P: SD Request for WFD", query, query_len);
++
++      if (!wpa_s->global->wifi_display) {
++              wpa_printf(MSG_DEBUG, "P2P: WFD protocol not available");
++              wpas_sd_add_proto_not_avail(resp, P2P_SERV_WIFI_DISPLAY,
++                                          srv_trans_id);
++              return;
++      }
++
++      if (query_len < 1) {
++              wpa_printf(MSG_DEBUG, "P2P: Missing WFD Requested Device "
++                         "Role");
++              return;
++      }
++
++      if (wpabuf_tailroom(resp) < 5)
++              return;
++
++      pos = query;
++      role = *pos++;
++      wpa_printf(MSG_DEBUG, "P2P: WSD for device role 0x%x", role);
++
++      /* TODO: role specific handling */
++
++      /* Length (to be filled) */
++      len_pos = wpabuf_put(resp, 2);
++      wpabuf_put_u8(resp, P2P_SERV_WIFI_DISPLAY);
++      wpabuf_put_u8(resp, srv_trans_id);
++      wpabuf_put_u8(resp, P2P_SD_SUCCESS); /* Status Code */
++
++      while (pos < query + query_len) {
++              if (*pos < MAX_WFD_SUBELEMS &&
++                  wpa_s->global->wfd_subelem[*pos] &&
++                  wpabuf_tailroom(resp) >=
++                  wpabuf_len(wpa_s->global->wfd_subelem[*pos])) {
++                      wpa_printf(MSG_DEBUG, "P2P: Add WSD response "
++                                 "subelement %u", *pos);
++                      wpabuf_put_buf(resp, wpa_s->global->wfd_subelem[*pos]);
++              }
++              pos++;
++      }
++
++      WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - 2);
++}
++#endif /* CONFIG_WIFI_DISPLAY */
++
++
++static int find_p2ps_substr(struct p2ps_advertisement *adv_data,
++                          const u8 *needle, size_t needle_len)
++{
++      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_all_asp(struct wpa_supplicant *wpa_s,
++                          struct wpabuf *resp, u8 srv_trans_id)
++{
++      /* Query data to add all P2PS advertisements:
++       *  - Service name length: 1
++       *  - Service name: '*'
++       *  - Service Information Request Length: 0
++       */
++      const u8 q[] = { 1, (const u8) '*', 0 };
++
++      if (p2p_get_p2ps_adv_list(wpa_s->global->p2p))
++              wpas_sd_req_asp(wpa_s, resp, srv_trans_id, q, sizeof(q));
++}
++
++
++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;
++      size_t buf_len;
++      char *buf;
++
++      wpa_hexdump(MSG_MSGDUMP, "P2P: Service Discovery Request TLVs",
++                  tlvs, tlvs_len);
++      buf_len = 2 * tlvs_len + 1;
++      buf = os_malloc(buf_len);
++      if (buf) {
++              wpa_snprintf_hex(buf, buf_len, tlvs, tlvs_len);
++              wpa_msg_ctrl(wpa_s, MSG_INFO, P2P_EVENT_SERV_DISC_REQ "%d "
++                           MACSTR " %u %u %s",
++                           freq, MAC2STR(sa), dialog_token, update_indic,
++                           buf);
++              os_free(buf);
++      }
++
++      if (wpa_s->p2p_sd_over_ctrl_iface) {
++              wpas_notify_p2p_sd_request(wpa_s, freq, sa, dialog_token,
++                                         update_indic, tlvs, tlvs_len);
++              return; /* to be processed by an external program */
++      }
++
++      resp = wpabuf_alloc(10000);
++      if (resp == NULL)
++              return;
++
++      while (pos + 1 < end) {
++              wpa_printf(MSG_DEBUG, "P2P: Service Request TLV");
++              slen = WPA_GET_LE16(pos);
++              pos += 2;
++              if (pos + slen > end || slen < 2) {
++                      wpa_printf(MSG_DEBUG, "P2P: Unexpected Query Data "
++                                 "length");
++                      wpabuf_free(resp);
++                      return;
++              }
++              tlv_end = pos + slen;
++
++              srv_proto = *pos++;
++              wpa_printf(MSG_DEBUG, "P2P: Service Protocol Type %u",
++                         srv_proto);
++              srv_trans_id = *pos++;
++              wpa_printf(MSG_DEBUG, "P2P: Service Transaction ID %u",
++                         srv_trans_id);
++
++              wpa_hexdump(MSG_MSGDUMP, "P2P: Query Data",
++                          pos, tlv_end - pos);
++
++
++              if (wpa_s->force_long_sd) {
++                      wpa_printf(MSG_DEBUG, "P2P: SD test - force long "
++                                 "response");
++                      wpas_sd_all_bonjour(wpa_s, resp, srv_trans_id);
++                      wpas_sd_all_upnp(wpa_s, resp, srv_trans_id);
++                      wpas_sd_all_asp(wpa_s, resp, srv_trans_id);
++                      goto done;
++              }
++
++              switch (srv_proto) {
++              case P2P_SERV_ALL_SERVICES:
++                      wpa_printf(MSG_DEBUG, "P2P: Service Discovery Request "
++                                 "for all services");
++                      if (dl_list_empty(&wpa_s->global->p2p_srv_upnp) &&
++                          dl_list_empty(&wpa_s->global->p2p_srv_bonjour) &&
++                          !p2p_get_p2ps_adv_list(wpa_s->global->p2p)) {
++                              wpa_printf(MSG_DEBUG, "P2P: No service "
++                                         "discovery protocols available");
++                              wpas_sd_add_proto_not_avail(
++                                      resp, P2P_SERV_ALL_SERVICES,
++                                      srv_trans_id);
++                              break;
++                      }
++                      wpas_sd_all_bonjour(wpa_s, resp, srv_trans_id);
++                      wpas_sd_all_upnp(wpa_s, resp, srv_trans_id);
++                      wpas_sd_all_asp(wpa_s, resp, srv_trans_id);
++                      break;
++              case P2P_SERV_BONJOUR:
++                      wpas_sd_req_bonjour(wpa_s, resp, srv_trans_id,
++                                          pos, tlv_end - pos);
++                      break;
++              case P2P_SERV_UPNP:
++                      wpas_sd_req_upnp(wpa_s, resp, srv_trans_id,
++                                       pos, tlv_end - pos);
++                      break;
++#ifdef CONFIG_WIFI_DISPLAY
++              case P2P_SERV_WIFI_DISPLAY:
++                      wpas_sd_req_wfd(wpa_s, resp, srv_trans_id,
++                                      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);
++                      wpas_sd_add_proto_not_avail(resp, srv_proto,
++                                                  srv_trans_id);
++                      break;
++              }
++
++              pos = tlv_end;
++      }
++
++done:
++      wpas_notify_p2p_sd_request(wpa_s, freq, sa, dialog_token,
++                                 update_indic, tlvs, tlvs_len);
++
++      wpas_p2p_sd_response(wpa_s, freq, sa, dialog_token, resp);
++
++      wpabuf_free(resp);
++}
++
++
++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);
++              }
++      }
++}
++
++
++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;
++      const u8 *end = tlvs + tlvs_len;
++      const u8 *tlv_end;
++      u16 slen;
++      size_t buf_len;
++      char *buf;
++
++      wpa_hexdump(MSG_MSGDUMP, "P2P: Service Discovery Response TLVs",
++                  tlvs, tlvs_len);
++      if (tlvs_len > 1500) {
++              /* TODO: better way for handling this */
++              wpa_msg_ctrl(wpa_s, MSG_INFO,
++                           P2P_EVENT_SERV_DISC_RESP MACSTR
++                           " %u <long response: %u bytes>",
++                           MAC2STR(sa), update_indic,
++                           (unsigned int) tlvs_len);
++      } else {
++              buf_len = 2 * tlvs_len + 1;
++              buf = os_malloc(buf_len);
++              if (buf) {
++                      wpa_snprintf_hex(buf, buf_len, tlvs, tlvs_len);
++                      wpa_msg_ctrl(wpa_s, MSG_INFO,
++                                   P2P_EVENT_SERV_DISC_RESP MACSTR " %u %s",
++                                   MAC2STR(sa), update_indic, buf);
++                      os_free(buf);
++              }
++      }
++
++      while (pos < end) {
++              u8 srv_proto, srv_trans_id, status;
++
++              wpa_printf(MSG_DEBUG, "P2P: Service Response TLV");
++              slen = WPA_GET_LE16(pos);
++              pos += 2;
++              if (pos + slen > end || slen < 3) {
++                      wpa_printf(MSG_DEBUG, "P2P: Unexpected Response Data "
++                                 "length");
++                      return;
++              }
++              tlv_end = pos + slen;
++
++              srv_proto = *pos++;
++              wpa_printf(MSG_DEBUG, "P2P: Service Protocol Type %u",
++                         srv_proto);
++              srv_trans_id = *pos++;
++              wpa_printf(MSG_DEBUG, "P2P: Service Transaction ID %u",
++                         srv_trans_id);
++              status = *pos++;
++              wpa_printf(MSG_DEBUG, "P2P: Status Code ID %u",
++                         status);
++
++              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;
++      }
++
++      wpas_notify_p2p_sd_response(wpa_s, sa, update_indic, tlvs, tlvs_len);
++}
++
++
++u64 wpas_p2p_sd_request(struct wpa_supplicant *wpa_s, const u8 *dst,
++                      const struct wpabuf *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);
++}
++
++
++u64 wpas_p2p_sd_request_upnp(struct wpa_supplicant *wpa_s, const u8 *dst,
++                           u8 version, const char *query)
++{
++      struct wpabuf *tlvs;
++      u64 ret;
++
++      tlvs = wpabuf_alloc(2 + 1 + 1 + 1 + os_strlen(query));
++      if (tlvs == NULL)
++              return 0;
++      wpabuf_put_le16(tlvs, 1 + 1 + 1 + os_strlen(query));
++      wpabuf_put_u8(tlvs, P2P_SERV_UPNP); /* Service Protocol Type */
++      wpabuf_put_u8(tlvs, 1); /* Service Transaction ID */
++      wpabuf_put_u8(tlvs, version);
++      wpabuf_put_str(tlvs, query);
++      ret = wpas_p2p_sd_request(wpa_s, dst, tlvs);
++      wpabuf_free(tlvs);
++      return ret;
++}
++
++
++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->global->p2p_disabled || wpa_s->global->p2p == NULL)
++              return 0;
++      return (uintptr_t) p2p_sd_request_wfd(wpa_s->global->p2p, dst, tlvs);
++}
++
++
++#define MAX_WFD_SD_SUBELEMS 20
++
++static void wfd_add_sd_req_role(struct wpabuf *tlvs, u8 id, u8 role,
++                              const char *subelems)
++{
++      u8 *len;
++      const char *pos;
++      int val;
++      int count = 0;
++
++      len = wpabuf_put(tlvs, 2);
++      wpabuf_put_u8(tlvs, P2P_SERV_WIFI_DISPLAY); /* Service Protocol Type */
++      wpabuf_put_u8(tlvs, id); /* Service Transaction ID */
++
++      wpabuf_put_u8(tlvs, role);
++
++      pos = subelems;
++      while (*pos) {
++              val = atoi(pos);
++              if (val >= 0 && val < 256) {
++                      wpabuf_put_u8(tlvs, val);
++                      count++;
++                      if (count == MAX_WFD_SD_SUBELEMS)
++                              break;
++              }
++              pos = os_strchr(pos + 1, ',');
++              if (pos == NULL)
++                      break;
++              pos++;
++      }
++
++      WPA_PUT_LE16(len, (u8 *) wpabuf_put(tlvs, 0) - len - 2);
++}
++
++
++u64 wpas_p2p_sd_request_wifi_display(struct wpa_supplicant *wpa_s,
++                                   const u8 *dst, const char *role)
++{
++      struct wpabuf *tlvs;
++      u64 ret;
++      const char *subelems;
++      u8 id = 1;
++
++      subelems = os_strchr(role, ' ');
++      if (subelems == NULL)
++              return 0;
++      subelems++;
++
++      tlvs = wpabuf_alloc(4 * (2 + 1 + 1 + 1 + MAX_WFD_SD_SUBELEMS));
++      if (tlvs == NULL)
++              return 0;
++
++      if (os_strstr(role, "[source]"))
++              wfd_add_sd_req_role(tlvs, id++, 0x00, subelems);
++      if (os_strstr(role, "[pri-sink]"))
++              wfd_add_sd_req_role(tlvs, id++, 0x01, subelems);
++      if (os_strstr(role, "[sec-sink]"))
++              wfd_add_sd_req_role(tlvs, id++, 0x02, subelems);
++      if (os_strstr(role, "[source+sink]"))
++              wfd_add_sd_req_role(tlvs, id++, 0x03, subelems);
++
++      ret = wpas_p2p_sd_request_wfd(wpa_s, dst, tlvs);
++      wpabuf_free(tlvs);
++      return ret;
++}
++
++#endif /* CONFIG_WIFI_DISPLAY */
++
++
++int wpas_p2p_sd_cancel_request(struct wpa_supplicant *wpa_s, u64 req)
++{
++      if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
++              return -1;
++      return p2p_sd_cancel_request(wpa_s->global->p2p,
++                                   (void *) (uintptr_t) req);
++}
++
++
++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->global->p2p_disabled || wpa_s->global->p2p == NULL)
++              return;
++      p2p_sd_response(wpa_s->global->p2p, freq, dst, dialog_token,
++                      resp_tlvs);
++}
++
++
++void wpas_p2p_sd_service_update(struct wpa_supplicant *wpa_s)
++{
++      if (wpa_s->global->p2p)
++              p2p_sd_service_update(wpa_s->global->p2p);
++}
++
++
++static void wpas_p2p_srv_bonjour_free(struct p2p_srv_bonjour *bsrv)
++{
++      dl_list_del(&bsrv->list);
++      wpabuf_free(bsrv->query);
++      wpabuf_free(bsrv->resp);
++      os_free(bsrv);
++}
++
++
++static void wpas_p2p_srv_upnp_free(struct p2p_srv_upnp *usrv)
++{
++      dl_list_del(&usrv->list);
++      os_free(usrv->service);
++      os_free(usrv);
++}
++
++
++void wpas_p2p_service_flush(struct wpa_supplicant *wpa_s)
++{
++      struct p2p_srv_bonjour *bsrv, *bn;
++      struct p2p_srv_upnp *usrv, *un;
++
++      dl_list_for_each_safe(bsrv, bn, &wpa_s->global->p2p_srv_bonjour,
++                            struct p2p_srv_bonjour, list)
++              wpas_p2p_srv_bonjour_free(bsrv);
++
++      dl_list_for_each_safe(usrv, un, &wpa_s->global->p2p_srv_upnp,
++                            struct p2p_srv_upnp, list)
++              wpas_p2p_srv_upnp_free(usrv);
++
++      wpas_p2p_service_flush_asp(wpa_s);
++      wpas_p2p_sd_service_update(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)
++{
++      int ret;
++
++      ret = p2p_service_del_asp(wpa_s->global->p2p, adv_id);
++      if (ret == 0)
++              wpas_p2p_sd_service_update(wpa_s);
++      return ret;
++}
++
++
++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,
++                           const u8 *cpt_priority)
++{
++      int ret;
++
++      ret = p2p_service_add_asp(wpa_s->global->p2p, auto_accept, adv_id,
++                                adv_str, svc_state, config_methods,
++                                svc_info, cpt_priority);
++      if (ret == 0)
++              wpas_p2p_sd_service_update(wpa_s);
++      return ret;
++}
++
++
++void wpas_p2p_service_flush_asp(struct wpa_supplicant *wpa_s)
++{
++      p2p_service_flush_asp(wpa_s->global->p2p);
++}
++
++
++int wpas_p2p_service_add_bonjour(struct wpa_supplicant *wpa_s,
++                               struct wpabuf *query, struct wpabuf *resp)
++{
++      struct p2p_srv_bonjour *bsrv;
++
++      bsrv = os_zalloc(sizeof(*bsrv));
++      if (bsrv == NULL)
++              return -1;
++      bsrv->query = query;
++      bsrv->resp = resp;
++      dl_list_add(&wpa_s->global->p2p_srv_bonjour, &bsrv->list);
++
++      wpas_p2p_sd_service_update(wpa_s);
++      return 0;
++}
++
++
++int wpas_p2p_service_del_bonjour(struct wpa_supplicant *wpa_s,
++                               const struct wpabuf *query)
++{
++      struct p2p_srv_bonjour *bsrv;
++
++      bsrv = wpas_p2p_service_get_bonjour(wpa_s, query);
++      if (bsrv == NULL)
++              return -1;
++      wpas_p2p_srv_bonjour_free(bsrv);
++      wpas_p2p_sd_service_update(wpa_s);
++      return 0;
++}
++
++
++int wpas_p2p_service_add_upnp(struct wpa_supplicant *wpa_s, u8 version,
++                            const char *service)
++{
++      struct p2p_srv_upnp *usrv;
++
++      if (wpas_p2p_service_get_upnp(wpa_s, version, service))
++              return 0; /* Already listed */
++      usrv = os_zalloc(sizeof(*usrv));
++      if (usrv == NULL)
++              return -1;
++      usrv->version = version;
++      usrv->service = os_strdup(service);
++      if (usrv->service == NULL) {
++              os_free(usrv);
++              return -1;
++      }
++      dl_list_add(&wpa_s->global->p2p_srv_upnp, &usrv->list);
++
++      wpas_p2p_sd_service_update(wpa_s);
++      return 0;
++}
++
++
++int wpas_p2p_service_del_upnp(struct wpa_supplicant *wpa_s, u8 version,
++                            const char *service)
++{
++      struct p2p_srv_upnp *usrv;
++
++      usrv = wpas_p2p_service_get_upnp(wpa_s, version, service);
++      if (usrv == NULL)
++              return -1;
++      wpas_p2p_srv_upnp_free(usrv);
++      wpas_p2p_sd_service_update(wpa_s);
++      return 0;
++}
index ed5708585be1d740b341f6ed147766b64eb2499c,0000000000000000000000000000000000000000..f4bba98e2a8254259df0716ed7a58b252ef055b9
mode 100644,000000..100644
--- /dev/null
@@@ -1,360 -1,0 +1,360 @@@
- struct wpa_driver_ops *wpa_drivers[] = { NULL };
 +/*
 + * WPA Supplicant - test code for pre-authentication
 + * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi>
 + *
 + * This software may be distributed under the terms of the BSD license.
 + * See README for more details.
 + *
 + * IEEE 802.1X Supplicant test code (to be used in place of wpa_supplicant.c.
 + * Not used in production version.
 + */
 +
 +#include "includes.h"
 +#include <assert.h>
 +
 +#include "common.h"
 +#include "config.h"
 +#include "eapol_supp/eapol_supp_sm.h"
 +#include "eloop.h"
 +#include "rsn_supp/wpa.h"
 +#include "eap_peer/eap.h"
 +#include "wpa_supplicant_i.h"
 +#include "l2_packet/l2_packet.h"
 +#include "ctrl_iface.h"
 +#include "pcsc_funcs.h"
 +#include "rsn_supp/preauth.h"
 +#include "rsn_supp/pmksa_cache.h"
 +#include "drivers/driver.h"
 +
 +
++const struct wpa_driver_ops *const wpa_drivers[] = { NULL };
 +
 +
 +struct preauth_test_data {
 +      int auth_timed_out;
 +};
 +
 +
 +static void _wpa_supplicant_deauthenticate(void *wpa_s, int reason_code)
 +{
 +      wpa_supplicant_deauthenticate(wpa_s, reason_code);
 +}
 +
 +
 +static u8 * wpa_alloc_eapol(const struct wpa_supplicant *wpa_s, u8 type,
 +                          const void *data, u16 data_len,
 +                          size_t *msg_len, void **data_pos)
 +{
 +      struct ieee802_1x_hdr *hdr;
 +
 +      *msg_len = sizeof(*hdr) + data_len;
 +      hdr = os_malloc(*msg_len);
 +      if (hdr == NULL)
 +              return NULL;
 +
 +      hdr->version = wpa_s->conf->eapol_version;
 +      hdr->type = type;
 +      hdr->length = htons(data_len);
 +
 +      if (data)
 +              os_memcpy(hdr + 1, data, data_len);
 +      else
 +              os_memset(hdr + 1, 0, data_len);
 +
 +      if (data_pos)
 +              *data_pos = hdr + 1;
 +
 +      return (u8 *) hdr;
 +}
 +
 +
 +static u8 * _wpa_alloc_eapol(void *wpa_s, u8 type,
 +                           const void *data, u16 data_len,
 +                           size_t *msg_len, void **data_pos)
 +{
 +      return wpa_alloc_eapol(wpa_s, type, data, data_len, msg_len, data_pos);
 +}
 +
 +
 +static void _wpa_supplicant_set_state(void *ctx, enum wpa_states state)
 +{
 +      struct wpa_supplicant *wpa_s = ctx;
 +      wpa_s->wpa_state = state;
 +}
 +
 +
 +static enum wpa_states _wpa_supplicant_get_state(void *ctx)
 +{
 +      struct wpa_supplicant *wpa_s = ctx;
 +      return wpa_s->wpa_state;
 +}
 +
 +
 +static int wpa_ether_send(void *wpa_s, const u8 *dest, u16 proto,
 +                        const u8 *buf, size_t len)
 +{
 +      printf("%s - not implemented\n", __func__);
 +      return -1;
 +}
 +
 +
 +static void * wpa_supplicant_get_network_ctx(void *wpa_s)
 +{
 +      return wpa_supplicant_get_ssid(wpa_s);
 +}
 +
 +
 +static void _wpa_supplicant_cancel_auth_timeout(void *wpa_s)
 +{
 +      wpa_supplicant_cancel_auth_timeout(wpa_s);
 +}
 +
 +
 +static int wpa_supplicant_get_beacon_ie(void *wpa_s)
 +{
 +      printf("%s - not implemented\n", __func__);
 +      return -1;
 +}
 +
 +
 +static int wpa_supplicant_get_bssid(void *wpa_s, u8 *bssid)
 +{
 +      printf("%s - not implemented\n", __func__);
 +      return -1;
 +}
 +
 +
 +static int wpa_supplicant_set_key(void *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)
 +{
 +      printf("%s - not implemented\n", __func__);
 +      return -1;
 +}
 +
 +
 +static int wpa_supplicant_mlme_setprotection(void *wpa_s, const u8 *addr,
 +                                           int protection_type,
 +                                           int key_type)
 +{
 +      printf("%s - not implemented\n", __func__);
 +      return -1;
 +}
 +
 +
 +static int wpa_supplicant_add_pmkid(void *wpa_s,
 +                                  const u8 *bssid, const u8 *pmkid)
 +{
 +      printf("%s - not implemented\n", __func__);
 +      return -1;
 +}
 +
 +
 +static int wpa_supplicant_remove_pmkid(void *wpa_s,
 +                                     const u8 *bssid, const u8 *pmkid)
 +{
 +      printf("%s - not implemented\n", __func__);
 +      return -1;
 +}
 +
 +
 +static void wpa_supplicant_set_config_blob(void *ctx,
 +                                         struct wpa_config_blob *blob)
 +{
 +      struct wpa_supplicant *wpa_s = ctx;
 +      wpa_config_set_blob(wpa_s->conf, blob);
 +}
 +
 +
 +static const struct wpa_config_blob *
 +wpa_supplicant_get_config_blob(void *ctx, const char *name)
 +{
 +      struct wpa_supplicant *wpa_s = ctx;
 +      return wpa_config_get_blob(wpa_s->conf, name);
 +}
 +
 +
 +static void test_eapol_clean(struct wpa_supplicant *wpa_s)
 +{
 +      rsn_preauth_deinit(wpa_s->wpa);
 +      pmksa_candidate_free(wpa_s->wpa);
 +      wpa_sm_deinit(wpa_s->wpa);
 +      scard_deinit(wpa_s->scard);
 +      if (wpa_s->ctrl_iface) {
 +              wpa_supplicant_ctrl_iface_deinit(wpa_s->ctrl_iface);
 +              wpa_s->ctrl_iface = NULL;
 +      }
 +      wpa_config_free(wpa_s->conf);
 +}
 +
 +
 +static void eapol_test_timeout(void *eloop_ctx, void *timeout_ctx)
 +{
 +      struct preauth_test_data *p = eloop_ctx;
 +      printf("EAPOL test timed out\n");
 +      p->auth_timed_out = 1;
 +      eloop_terminate();
 +}
 +
 +
 +static void eapol_test_poll(void *eloop_ctx, void *timeout_ctx)
 +{
 +      struct wpa_supplicant *wpa_s = eloop_ctx;
 +      if (!rsn_preauth_in_progress(wpa_s->wpa))
 +              eloop_terminate();
 +      else {
 +              eloop_register_timeout(0, 100000, eapol_test_poll, eloop_ctx,
 +                                     timeout_ctx);
 +      }
 +}
 +
 +
 +static struct wpa_driver_ops dummy_driver;
 +
 +
 +static void wpa_init_conf(struct wpa_supplicant *wpa_s, const char *ifname)
 +{
 +      struct l2_packet_data *l2;
 +      struct wpa_sm_ctx *ctx;
 +
 +      os_memset(&dummy_driver, 0, sizeof(dummy_driver));
 +      wpa_s->driver = &dummy_driver;
 +
 +      ctx = os_zalloc(sizeof(*ctx));
 +      assert(ctx != NULL);
 +
 +      ctx->ctx = wpa_s;
 +      ctx->msg_ctx = wpa_s;
 +      ctx->set_state = _wpa_supplicant_set_state;
 +      ctx->get_state = _wpa_supplicant_get_state;
 +      ctx->deauthenticate = _wpa_supplicant_deauthenticate;
 +      ctx->set_key = wpa_supplicant_set_key;
 +      ctx->get_network_ctx = wpa_supplicant_get_network_ctx;
 +      ctx->get_bssid = wpa_supplicant_get_bssid;
 +      ctx->ether_send = wpa_ether_send;
 +      ctx->get_beacon_ie = wpa_supplicant_get_beacon_ie;
 +      ctx->alloc_eapol = _wpa_alloc_eapol;
 +      ctx->cancel_auth_timeout = _wpa_supplicant_cancel_auth_timeout;
 +      ctx->add_pmkid = wpa_supplicant_add_pmkid;
 +      ctx->remove_pmkid = wpa_supplicant_remove_pmkid;
 +      ctx->set_config_blob = wpa_supplicant_set_config_blob;
 +      ctx->get_config_blob = wpa_supplicant_get_config_blob;
 +      ctx->mlme_setprotection = wpa_supplicant_mlme_setprotection;
 +
 +      wpa_s->wpa = wpa_sm_init(ctx);
 +      assert(wpa_s->wpa != NULL);
 +      wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_PROTO, WPA_PROTO_RSN);
 +
 +      os_strlcpy(wpa_s->ifname, ifname, sizeof(wpa_s->ifname));
 +      wpa_sm_set_ifname(wpa_s->wpa, wpa_s->ifname, NULL);
 +
 +      l2 = l2_packet_init(wpa_s->ifname, NULL, ETH_P_RSN_PREAUTH, NULL,
 +                          NULL, 0);
 +      assert(l2 != NULL);
 +      if (l2_packet_get_own_addr(l2, wpa_s->own_addr)) {
 +              wpa_printf(MSG_WARNING, "Failed to get own L2 address\n");
 +              exit(-1);
 +      }
 +      l2_packet_deinit(l2);
 +      wpa_sm_set_own_addr(wpa_s->wpa, wpa_s->own_addr);
 +}
 +
 +
 +static void eapol_test_terminate(int sig, void *signal_ctx)
 +{
 +      struct wpa_supplicant *wpa_s = signal_ctx;
 +      wpa_msg(wpa_s, MSG_INFO, "Signal %d received - terminating", sig);
 +      eloop_terminate();
 +}
 +
 +
 +int main(int argc, char *argv[])
 +{
 +      struct wpa_supplicant wpa_s;
 +      int ret = 1;
 +      u8 bssid[ETH_ALEN];
 +      struct preauth_test_data preauth_test;
 +
 +      if (os_program_init())
 +              return -1;
 +
 +      os_memset(&preauth_test, 0, sizeof(preauth_test));
 +
 +      wpa_debug_level = 0;
 +      wpa_debug_show_keys = 1;
 +
 +      if (argc != 4) {
 +              printf("usage: preauth_test <conf> <target MAC address> "
 +                     "<ifname>\n");
 +              return -1;
 +      }
 +
 +      if (hwaddr_aton(argv[2], bssid)) {
 +              printf("Failed to parse target address '%s'.\n", argv[2]);
 +              return -1;
 +      }
 +
 +      if (eap_register_methods()) {
 +              wpa_printf(MSG_ERROR, "Failed to register EAP methods");
 +              return -1;
 +      }
 +
 +      if (eloop_init()) {
 +              wpa_printf(MSG_ERROR, "Failed to initialize event loop");
 +              return -1;
 +      }
 +
 +      os_memset(&wpa_s, 0, sizeof(wpa_s));
 +      wpa_s.conf = wpa_config_read(argv[1], NULL);
 +      if (wpa_s.conf == NULL) {
 +              printf("Failed to parse configuration file '%s'.\n", argv[1]);
 +              return -1;
 +      }
 +      if (wpa_s.conf->ssid == NULL) {
 +              printf("No networks defined.\n");
 +              return -1;
 +      }
 +
 +      wpa_init_conf(&wpa_s, argv[3]);
 +      wpa_s.ctrl_iface = wpa_supplicant_ctrl_iface_init(&wpa_s);
 +      if (wpa_s.ctrl_iface == NULL) {
 +              printf("Failed to initialize control interface '%s'.\n"
 +                     "You may have another preauth_test process already "
 +                     "running or the file was\n"
 +                     "left by an unclean termination of preauth_test in "
 +                     "which case you will need\n"
 +                     "to manually remove this file before starting "
 +                     "preauth_test again.\n",
 +                     wpa_s.conf->ctrl_interface);
 +              return -1;
 +      }
 +      if (wpa_supplicant_scard_init(&wpa_s, wpa_s.conf->ssid))
 +              return -1;
 +
 +      if (rsn_preauth_init(wpa_s.wpa, bssid, &wpa_s.conf->ssid->eap))
 +              return -1;
 +
 +      eloop_register_timeout(30, 0, eapol_test_timeout, &preauth_test, NULL);
 +      eloop_register_timeout(0, 100000, eapol_test_poll, &wpa_s, NULL);
 +      eloop_register_signal_terminate(eapol_test_terminate, &wpa_s);
 +      eloop_register_signal_reconfig(eapol_test_terminate, &wpa_s);
 +      eloop_run();
 +
 +      if (preauth_test.auth_timed_out)
 +              ret = -2;
 +      else {
 +              ret = pmksa_cache_set_current(wpa_s.wpa, NULL, bssid, NULL, 0)
 +                      ? 0 : -3;
 +      }
 +
 +      test_eapol_clean(&wpa_s);
 +
 +      eap_peer_unregister_methods();
 +
 +      eloop_destroy();
 +
 +      os_program_deinit();
 +
 +      return ret;
 +}
index 805891a88005cd39daf3b6d9dc9e9d820421594d,0000000000000000000000000000000000000000..d7049a1a8164c439ee9c1a8930c46c6d4bf547fc
mode 100644,000000..100644
--- /dev/null
@@@ -1,2428 -1,0 +1,2464 @@@
-       if (wpa_s->conf->interworking == 0)
-               return;
-       wpabuf_put_u8(buf, WLAN_EID_EXT_CAPAB);
-       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 */
 +/*
 + * WPA Supplicant - Scanning
 + * 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/includes.h"
 +
 +#include "utils/common.h"
 +#include "utils/eloop.h"
 +#include "common/ieee802_11_defs.h"
 +#include "common/wpa_ctrl.h"
 +#include "config.h"
 +#include "wpa_supplicant_i.h"
 +#include "driver_i.h"
 +#include "wps_supplicant.h"
 +#include "p2p_supplicant.h"
 +#include "p2p/p2p.h"
 +#include "hs20_supplicant.h"
 +#include "notify.h"
 +#include "bss.h"
 +#include "scan.h"
 +#include "mesh.h"
 +
 +
 +static void wpa_supplicant_gen_assoc_event(struct wpa_supplicant *wpa_s)
 +{
 +      struct wpa_ssid *ssid;
 +      union wpa_event_data data;
 +
 +      ssid = wpa_supplicant_get_ssid(wpa_s);
 +      if (ssid == NULL)
 +              return;
 +
 +      if (wpa_s->current_ssid == NULL) {
 +              wpa_s->current_ssid = ssid;
 +              if (wpa_s->current_ssid != NULL)
 +                      wpas_notify_network_changed(wpa_s);
 +      }
 +      wpa_supplicant_initiate_eapol(wpa_s);
 +      wpa_dbg(wpa_s, MSG_DEBUG, "Already associated with a configured "
 +              "network - generating associated event");
 +      os_memset(&data, 0, sizeof(data));
 +      wpa_supplicant_event(wpa_s, EVENT_ASSOC, &data);
 +}
 +
 +
 +#ifdef CONFIG_WPS
 +static int wpas_wps_in_use(struct wpa_supplicant *wpa_s,
 +                         enum wps_request_type *req_type)
 +{
 +      struct wpa_ssid *ssid;
 +      int wps = 0;
 +
 +      for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
 +              if (!(ssid->key_mgmt & WPA_KEY_MGMT_WPS))
 +                      continue;
 +
 +              wps = 1;
 +              *req_type = wpas_wps_get_req_type(ssid);
 +              if (!ssid->eap.phase1)
 +                      continue;
 +
 +              if (os_strstr(ssid->eap.phase1, "pbc=1"))
 +                      return 2;
 +      }
 +
 +#ifdef CONFIG_P2P
 +      if (!wpa_s->global->p2p_disabled && wpa_s->global->p2p &&
 +          !wpa_s->conf->p2p_disabled) {
 +              wpa_s->wps->dev.p2p = 1;
 +              if (!wps) {
 +                      wps = 1;
 +                      *req_type = WPS_REQ_ENROLLEE_INFO;
 +              }
 +      }
 +#endif /* CONFIG_P2P */
 +
 +      return wps;
 +}
 +#endif /* CONFIG_WPS */
 +
 +
 +/**
 + * wpa_supplicant_enabled_networks - Check whether there are enabled networks
 + * @wpa_s: Pointer to wpa_supplicant data
 + * Returns: 0 if no networks are enabled, >0 if networks are enabled
 + *
 + * This function is used to figure out whether any networks (or Interworking
 + * with enabled credentials and auto_interworking) are present in the current
 + * configuration.
 + */
 +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++;
 +              else
 +                      disabled++;
 +              ssid = ssid->next;
 +      }
 +      if (wpa_s->conf->cred && wpa_s->conf->interworking &&
 +          wpa_s->conf->auto_interworking)
 +              count++;
 +      if (count == 0 && disabled > 0) {
 +              wpa_dbg(wpa_s, MSG_DEBUG, "No enabled networks (%d disabled "
 +                      "networks)", disabled);
 +      }
 +      return count;
 +}
 +
 +
 +static void wpa_supplicant_assoc_try(struct wpa_supplicant *wpa_s,
 +                                   struct wpa_ssid *ssid)
 +{
 +      while (ssid) {
 +              if (!wpas_network_disabled(wpa_s, ssid))
 +                      break;
 +              ssid = ssid->next;
 +      }
 +
 +      /* ap_scan=2 mode - try to associate with each SSID. */
 +      if (ssid == NULL) {
 +              wpa_dbg(wpa_s, MSG_DEBUG, "wpa_supplicant_assoc_try: Reached "
 +                      "end of scan list - go back to beginning");
 +              wpa_s->prev_scan_ssid = WILDCARD_SSID_SCAN;
 +              wpa_supplicant_req_scan(wpa_s, 0, 0);
 +              return;
 +      }
 +      if (ssid->next) {
 +              /* Continue from the next SSID on the next attempt. */
 +              wpa_s->prev_scan_ssid = ssid;
 +      } else {
 +              /* Start from the beginning of the SSID list. */
 +              wpa_s->prev_scan_ssid = WILDCARD_SSID_SCAN;
 +      }
 +      wpa_supplicant_associate(wpa_s, NULL, ssid);
 +}
 +
 +
 +static void wpas_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_supplicant_notify_scanning(wpa_s, 0);
 +              wpas_notify_scan_done(wpa_s, 0);
 +              wpa_s->scan_work = NULL;
 +              return;
 +      }
 +
 +      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;
 +      }
 +
 +      wpa_supplicant_notify_scanning(wpa_s, 1);
 +
 +      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;
 +
 +              if (wpa_s->disconnected)
 +                      retry = 0;
 +
 +              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);
 +              }
 +              return;
 +      }
 +
 +      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;
 +}
 +
 +
 +/**
 + * wpa_supplicant_trigger_scan - Request driver to start a scan
 + * @wpa_s: Pointer to wpa_supplicant data
 + * @params: Scan parameters
 + * Returns: 0 on success, -1 on failure
 + */
 +int wpa_supplicant_trigger_scan(struct wpa_supplicant *wpa_s,
 +                              struct wpa_driver_scan_params *params)
 +{
 +      struct wpa_driver_scan_params *ctx;
 +
 +      if (wpa_s->scan_work) {
 +              wpa_dbg(wpa_s, MSG_INFO, "Reject scan trigger since one is already pending");
 +              return -1;
 +      }
 +
 +      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 0;
 +}
 +
 +
 +static void
 +wpa_supplicant_delayed_sched_scan_timeout(void *eloop_ctx, void *timeout_ctx)
 +{
 +      struct wpa_supplicant *wpa_s = eloop_ctx;
 +
 +      wpa_dbg(wpa_s, MSG_DEBUG, "Starting delayed sched scan");
 +
 +      if (wpa_supplicant_req_sched_scan(wpa_s))
 +              wpa_supplicant_req_scan(wpa_s, 0, 0);
 +}
 +
 +
 +static void
 +wpa_supplicant_sched_scan_timeout(void *eloop_ctx, void *timeout_ctx)
 +{
 +      struct wpa_supplicant *wpa_s = eloop_ctx;
 +
 +      wpa_dbg(wpa_s, MSG_DEBUG, "Sched scan timeout - stopping it");
 +
 +      wpa_s->sched_scan_timed_out = 1;
 +      wpa_supplicant_cancel_sched_scan(wpa_s);
 +}
 +
 +
 +int wpa_supplicant_start_sched_scan(struct wpa_supplicant *wpa_s,
 +                                  struct wpa_driver_scan_params *params,
 +                                  int interval)
 +{
 +      int ret;
 +
 +      wpa_supplicant_notify_scanning(wpa_s, 1);
 +      ret = wpa_drv_sched_scan(wpa_s, params, interval * 1000);
 +      if (ret)
 +              wpa_supplicant_notify_scanning(wpa_s, 0);
 +      else
 +              wpa_s->sched_scanning = 1;
 +
 +      return ret;
 +}
 +
 +
 +int wpa_supplicant_stop_sched_scan(struct wpa_supplicant *wpa_s)
 +{
 +      int ret;
 +
 +      ret = wpa_drv_stop_sched_scan(wpa_s);
 +      if (ret) {
 +              wpa_dbg(wpa_s, MSG_DEBUG, "stopping sched_scan failed!");
 +              /* TODO: what to do if stopping fails? */
 +              return -1;
 +      }
 +
 +      return ret;
 +}
 +
 +
 +static struct wpa_driver_scan_filter *
 +wpa_supplicant_build_filter_ssids(struct wpa_config *conf, size_t *num_ssids)
 +{
 +      struct wpa_driver_scan_filter *ssids;
 +      struct wpa_ssid *ssid;
 +      size_t count;
 +
 +      *num_ssids = 0;
 +      if (!conf->filter_ssids)
 +              return NULL;
 +
 +      for (count = 0, ssid = conf->ssid; ssid; ssid = ssid->next) {
 +              if (ssid->ssid && ssid->ssid_len)
 +                      count++;
 +      }
 +      if (count == 0)
 +              return NULL;
 +      ssids = os_calloc(count, sizeof(struct wpa_driver_scan_filter));
 +      if (ssids == NULL)
 +              return NULL;
 +
 +      for (ssid = conf->ssid; ssid; ssid = ssid->next) {
 +              if (!ssid->ssid || !ssid->ssid_len)
 +                      continue;
 +              os_memcpy(ssids[*num_ssids].ssid, ssid->ssid, ssid->ssid_len);
 +              ssids[*num_ssids].ssid_len = ssid->ssid_len;
 +              (*num_ssids)++;
 +      }
 +
 +      return ssids;
 +}
 +
 +
 +static void wpa_supplicant_optimize_freqs(
 +      struct wpa_supplicant *wpa_s, struct wpa_driver_scan_params *params)
 +{
 +#ifdef CONFIG_P2P
 +      if (params->freqs == NULL && wpa_s->p2p_in_provisioning &&
 +          wpa_s->go_params) {
 +              /* Optimize provisioning state scan based on GO information */
 +              if (wpa_s->p2p_in_provisioning < 5 &&
 +                  wpa_s->go_params->freq > 0) {
 +                      wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Scan only GO "
 +                              "preferred frequency %d MHz",
 +                              wpa_s->go_params->freq);
 +                      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 &&
 +                         wpa_s->go_params->freq_list[0]) {
 +                      wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Scan only common "
 +                              "channels");
 +                      int_array_concat(&params->freqs,
 +                                       wpa_s->go_params->freq_list);
 +                      if (params->freqs)
 +                              int_array_sort_unique(params->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
 +      if (params->freqs == NULL && wpa_s->after_wps && wpa_s->wps_freq) {
 +              /*
 +               * Optimize post-provisioning scan based on channel used
 +               * during provisioning.
 +               */
 +              wpa_dbg(wpa_s, MSG_DEBUG, "WPS: Scan only frequency %u MHz "
 +                      "that was used during provisioning", wpa_s->wps_freq);
 +              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_calloc(2, sizeof(int));
 +              if (params->freqs)
 +                      params->freqs[0] = wpa_s->wps_freq;
 +              wpa_s->known_wps_freq = 0; /* only do this once */
 +      }
 +#endif /* CONFIG_WPS */
 +}
 +
 +
 +#ifdef CONFIG_INTERWORKING
 +static void wpas_add_interworking_elements(struct wpa_supplicant *wpa_s,
 +                                         struct wpabuf *buf)
 +{
- #ifdef CONFIG_P2P
- ssid_list_set:
- #endif /* CONFIG_P2P */
 +      wpabuf_put_u8(buf, WLAN_EID_INTERWORKING);
 +      wpabuf_put_u8(buf, is_zero_ether_addr(wpa_s->conf->hessid) ? 1 :
 +                    1 + ETH_ALEN);
 +      wpabuf_put_u8(buf, wpa_s->conf->access_network_type);
 +      /* No Venue Info */
 +      if (!is_zero_ether_addr(wpa_s->conf->hessid))
 +              wpabuf_put_data(buf, wpa_s->conf->hessid, ETH_ALEN);
 +}
 +#endif /* CONFIG_INTERWORKING */
 +
 +
 +static struct wpabuf * wpa_supplicant_extra_ies(struct wpa_supplicant *wpa_s)
 +{
 +      struct wpabuf *extra_ie = NULL;
++      u8 ext_capab[18];
++      int ext_capab_len;
 +#ifdef CONFIG_WPS
 +      int wps = 0;
 +      enum wps_request_type req_type = WPS_REQ_ENROLLEE_INFO;
 +#endif /* CONFIG_WPS */
 +
++      ext_capab_len = wpas_build_ext_capab(wpa_s, ext_capab,
++                                           sizeof(ext_capab));
++      if (ext_capab_len > 0 &&
++          wpabuf_resize(&extra_ie, ext_capab_len) == 0)
++              wpabuf_put_data(extra_ie, ext_capab, ext_capab_len);
++
 +#ifdef CONFIG_INTERWORKING
 +      if (wpa_s->conf->interworking &&
 +          wpabuf_resize(&extra_ie, 100) == 0)
 +              wpas_add_interworking_elements(wpa_s, extra_ie);
 +#endif /* CONFIG_INTERWORKING */
 +
 +#ifdef CONFIG_WPS
 +      wps = wpas_wps_in_use(wpa_s, &req_type);
 +
 +      if (wps) {
 +              struct wpabuf *wps_ie;
 +              wps_ie = wps_build_probe_req_ie(wps == 2 ? DEV_PW_PUSHBUTTON :
 +                                              DEV_PW_DEFAULT,
 +                                              &wpa_s->wps->dev,
 +                                              wpa_s->wps->uuid, req_type,
 +                                              0, NULL);
 +              if (wps_ie) {
 +                      if (wpabuf_resize(&extra_ie, wpabuf_len(wps_ie)) == 0)
 +                              wpabuf_put_buf(extra_ie, wps_ie);
 +                      wpabuf_free(wps_ie);
 +              }
 +      }
 +
 +#ifdef CONFIG_P2P
 +      if (wps) {
 +              size_t ielen = p2p_scan_ie_buf_len(wpa_s->global->p2p);
 +              if (wpabuf_resize(&extra_ie, ielen) == 0)
 +                      wpas_p2p_scan_ie(wpa_s, extra_ie);
 +      }
 +#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, -1);
 +#endif /* CONFIG_HS20 */
 +
++#ifdef CONFIG_FST
++      if (wpa_s->fst_ies &&
++          wpabuf_resize(&extra_ie, wpabuf_len(wpa_s->fst_ies)) == 0)
++              wpabuf_put_buf(extra_ie, wpa_s->fst_ies);
++#endif /* CONFIG_FST */
++
 +      return extra_ie;
 +}
 +
 +
 +#ifdef CONFIG_P2P
 +
 +/*
 + * Check whether there are any enabled networks or credentials that could be
 + * used for a non-P2P connection.
 + */
 +static int non_p2p_network_enabled(struct wpa_supplicant *wpa_s)
 +{
 +      struct wpa_ssid *ssid;
 +
 +      for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
 +              if (wpas_network_disabled(wpa_s, ssid))
 +                      continue;
 +              if (!ssid->p2p_group)
 +                      return 1;
 +      }
 +
 +      if (wpa_s->conf->cred && wpa_s->conf->interworking &&
 +          wpa_s->conf->auto_interworking)
 +              return 1;
 +
 +      return 0;
 +}
 +
 +#endif /* CONFIG_P2P */
 +
 +
 +static struct hostapd_hw_modes * get_mode(struct hostapd_hw_modes *modes,
 +                                        u16 num_modes,
 +                                        enum hostapd_hw_mode mode)
 +{
 +      u16 i;
 +
 +      for (i = 0; i < num_modes; i++) {
 +              if (modes[i].mode == mode)
 +                      return &modes[i];
 +      }
 +
 +      return NULL;
 +}
 +
 +
 +static void wpa_setband_scan_freqs_list(struct wpa_supplicant *wpa_s,
 +                                      enum hostapd_hw_mode band,
 +                                      struct wpa_driver_scan_params *params)
 +{
 +      /* Include only supported channels for the specified band */
 +      struct hostapd_hw_modes *mode;
 +      int count, i;
 +
 +      mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes, band);
 +      if (mode == NULL) {
 +              /* No channels supported in this band - use empty list */
 +              params->freqs = os_zalloc(sizeof(int));
 +              return;
 +      }
 +
 +      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++) {
 +              if (mode->channels[i].flag & HOSTAPD_CHAN_DISABLED)
 +                      continue;
 +              params->freqs[count++] = mode->channels[i].freq;
 +      }
 +}
 +
 +
 +static void wpa_setband_scan_freqs(struct wpa_supplicant *wpa_s,
 +                                 struct wpa_driver_scan_params *params)
 +{
 +      if (wpa_s->hw.modes == NULL)
 +              return; /* unknown what channels the driver supports */
 +      if (params->freqs)
 +              return; /* already using a limited channel set */
 +      if (wpa_s->setband == WPA_SETBAND_5G)
 +              wpa_setband_scan_freqs_list(wpa_s, HOSTAPD_MODE_IEEE80211A,
 +                                          params);
 +      else if (wpa_s->setband == WPA_SETBAND_2G)
 +              wpa_setband_scan_freqs_list(wpa_s, HOSTAPD_MODE_IEEE80211G,
 +                                          params);
 +}
 +
 +
 +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 int wpa_set_ssids_from_scan_req(struct wpa_supplicant *wpa_s,
++                                     struct wpa_driver_scan_params *params,
++                                     size_t max_ssids)
++{
++      unsigned int i;
++
++      if (wpa_s->ssids_from_scan_req == NULL ||
++          wpa_s->num_ssids_from_scan_req == 0)
++              return 0;
++
++      if (wpa_s->num_ssids_from_scan_req > max_ssids) {
++              wpa_s->num_ssids_from_scan_req = max_ssids;
++              wpa_printf(MSG_DEBUG, "Over max scan SSIDs from scan req: %u",
++                         (unsigned int) max_ssids);
++      }
++
++      for (i = 0; i < wpa_s->num_ssids_from_scan_req; i++) {
++              params->ssids[i].ssid = wpa_s->ssids_from_scan_req[i].ssid;
++              params->ssids[i].ssid_len =
++                      wpa_s->ssids_from_scan_req[i].ssid_len;
++              wpa_hexdump_ascii(MSG_DEBUG, "specific SSID",
++                                params->ssids[i].ssid,
++                                params->ssids[i].ssid_len);
++      }
++
++      params->num_ssids = wpa_s->num_ssids_from_scan_req;
++      wpa_s->num_ssids_from_scan_req = 0;
++      return 1;
++}
++
++
 +static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx)
 +{
 +      struct wpa_supplicant *wpa_s = eloop_ctx;
 +      struct wpa_ssid *ssid;
 +      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;
 +      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");
 +              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);
 +              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;
 +      }
 +
 +      if (!wpa_supplicant_enabled_networks(wpa_s) &&
 +          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);
 +              return;
 +      }
 +
 +      if (wpa_s->conf->ap_scan != 0 &&
 +          (wpa_s->drv_flags & WPA_DRIVER_FLAGS_WIRED)) {
 +              wpa_dbg(wpa_s, MSG_DEBUG, "Using wired authentication - "
 +                      "overriding ap_scan configuration");
 +              wpa_s->conf->ap_scan = 0;
 +              wpas_notify_ap_scan_changed(wpa_s);
 +      }
 +
 +      if (wpa_s->conf->ap_scan == 0) {
 +              wpa_supplicant_gen_assoc_event(wpa_s);
 +              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;
 +              }
 +      }
 +
 +      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;
 +      else {
 +              max_ssids = wpa_s->max_scan_ssids;
 +              if (max_ssids > WPAS_MAX_SCAN_SSIDS)
 +                      max_ssids = WPAS_MAX_SCAN_SSIDS;
 +      }
 +
 +      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));
 +
 +      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);
 +
 +      /*
 +       * If autoscan has set its own scanning parameters
 +       */
 +      if (wpa_s->autoscan_params != NULL) {
 +              scan_params = wpa_s->autoscan_params;
 +              goto scan;
 +      }
 +
++      if (wpa_s->last_scan_req == MANUAL_SCAN_REQ &&
++          wpa_set_ssids_from_scan_req(wpa_s, &params, max_ssids)) {
++              wpa_printf(MSG_DEBUG, "Use specific SSIDs from SCAN command");
++              goto ssid_list_set;
++      }
++
 +#ifdef CONFIG_P2P
 +      if ((wpa_s->p2p_in_provisioning || wpa_s->show_group_started) &&
 +          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 */
 +      ssid = wpa_s->conf->ssid;
 +      if (wpa_s->prev_scan_ssid != WILDCARD_SSID_SCAN) {
 +              while (ssid) {
 +                      if (ssid == wpa_s->prev_scan_ssid) {
 +                              ssid = ssid->next;
 +                              break;
 +                      }
 +                      ssid = ssid->next;
 +              }
 +      }
 +
 +      if (wpa_s->last_scan_req != MANUAL_SCAN_REQ &&
++#ifdef CONFIG_AP
++          !wpa_s->ap_iface &&
++#endif /* CONFIG_AP */
 +          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);
 +              return;
 +      } else if (wpa_s->conf->ap_scan == 2) {
 +              /*
 +               * User-initiated scan request in ap_scan == 2; scan with
 +               * 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;
 +              if (ssid == NULL && max_ssids > 1)
 +                      ssid = wpa_s->conf->ssid;
 +              while (ssid) {
 +                      if (!wpas_network_disabled(wpa_s, ssid) &&
 +                          ssid->scan_ssid) {
 +                              wpa_hexdump_ascii(MSG_DEBUG, "Scan SSID",
 +                                                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++;
 +                              if (params.num_ssids + 1 >= max_ssids)
 +                                      break;
 +                      }
 +                      ssid = ssid->next;
 +                      if (ssid == start)
 +                              break;
 +                      if (ssid == NULL && max_ssids > 1 &&
 +                          start != wpa_s->conf->ssid)
 +                              ssid = wpa_s->conf->ssid;
 +              }
 +
 +              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) {
 +                              int_array_concat(&params.freqs,
 +                                               tssid->scan_freq);
 +                      } else {
 +                              os_free(params.freqs);
 +                              params.freqs = NULL;
 +                      }
 +                      freqs_set = 1;
 +              }
 +              int_array_sort_unique(params.freqs);
 +      }
 +
 +      if (ssid && max_ssids == 1) {
 +              /*
 +               * If the driver is limited to 1 SSID at a time interleave
 +               * wildcard SSID scans with specific SSID scans to avoid
 +               * waiting a long time for a wildcard scan.
 +               */
 +              if (!wpa_s->prev_scan_wildcard) {
 +                      params.ssids[0].ssid = NULL;
 +                      params.ssids[0].ssid_len = 0;
 +                      wpa_s->prev_scan_wildcard = 1;
 +                      wpa_dbg(wpa_s, MSG_DEBUG, "Starting AP scan for "
 +                              "wildcard SSID (Interleave with specific)");
 +              } else {
 +                      wpa_s->prev_scan_ssid = ssid;
 +                      wpa_s->prev_scan_wildcard = 0;
 +                      wpa_dbg(wpa_s, MSG_DEBUG,
 +                              "Starting AP scan for specific SSID: %s",
 +                              wpa_ssid_txt(ssid->ssid, ssid->ssid_len));
 +              }
 +      } else if (ssid) {
 +              /* max_ssids > 1 */
 +
 +              wpa_s->prev_scan_ssid = ssid;
 +              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++;
 +              wpa_dbg(wpa_s, MSG_DEBUG, "Starting AP scan for wildcard "
 +                      "SSID");
 +      }
-               snr_b = MIN(wa->snr, GREAT_SNR);
 +
++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");
 +              params.freqs = wpa_s->next_scan_freqs;
 +      } else
 +              os_free(wpa_s->next_scan_freqs);
 +      wpa_s->next_scan_freqs = NULL;
 +      wpa_setband_scan_freqs(wpa_s, &params);
 +
 +      /* 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);
 +      }
 +
 +      /* Use current associated channel? */
 +      if (wpa_s->conf->scan_cur_freq && !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) {
 +                              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;
 +                      }
 +              }
 +      }
 +
 +      params.filter_ssids = wpa_supplicant_build_filter_ssids(
 +              wpa_s->conf, &params.num_filter_ssids);
 +      if (extra_ie) {
 +              params.extra_ies = wpabuf_head(extra_ie);
 +              params.extra_ies_len = wpabuf_len(extra_ie);
 +      }
 +
 +#ifdef CONFIG_P2P
 +      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
 +               * explicitly request P2P probe to disable CCK rates.
 +               */
 +              params.p2p_probe = 1;
 +      }
 +#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:
 +#ifdef CONFIG_P2P
 +      /*
 +       * If the driver does not support multi-channel concurrency and a
 +       * virtual interface that shares the same radio with the wpa_s interface
 +       * is operating there may not be need to scan other channels apart from
 +       * the current operating channel on the other virtual interface. Filter
 +       * out other channels in case we are trying to find a connection for a
 +       * 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 &&
 +          wpa_s->last_scan_req == NORMAL_SCAN_REQ &&
 +          !scan_params->freqs && !params.freqs &&
 +          wpas_is_p2p_prioritized(wpa_s) &&
 +          wpa_s->p2p_group_interface == NOT_P2P_GROUP_INTERFACE &&
 +          non_p2p_network_enabled(wpa_s)) {
 +              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 (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 = 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_reltime remaining, new_int;
 +      int cancelled;
 +
 +      cancelled = eloop_cancel_timeout_one(wpa_supplicant_scan, wpa_s, NULL,
 +                                           &remaining);
 +
 +      new_int.sec = sec;
 +      new_int.usec = 0;
 +      if (cancelled && os_reltime_before(&remaining, &new_int)) {
 +              new_int.sec = remaining.sec;
 +              new_int.usec = remaining.usec;
 +      }
 +
 +      if (cancelled) {
 +              eloop_register_timeout(new_int.sec, new_int.usec,
 +                                     wpa_supplicant_scan, wpa_s, NULL);
 +      }
 +      wpa_s->scan_interval = sec;
 +}
 +
 +
 +/**
 + * wpa_supplicant_req_scan - Schedule a scan for neighboring access points
 + * @wpa_s: Pointer to wpa_supplicant data
 + * @sec: Number of seconds after which to scan
 + * @usec: Number of microseconds after which to scan
 + *
 + * This function is used to schedule a scan for neighboring access points after
 + * the specified time.
 + */
 +void wpa_supplicant_req_scan(struct wpa_supplicant *wpa_s, int sec, int usec)
 +{
 +      int res;
 +
 +      if (wpa_s->p2p_mgmt) {
 +              wpa_dbg(wpa_s, MSG_DEBUG,
 +                      "Ignore scan request (%d.%06d sec) on p2p_mgmt interface",
 +                      sec, usec);
 +              return;
 +      }
 +
 +      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);
 +      }
 +}
 +
 +
 +/**
 + * wpa_supplicant_delayed_sched_scan - Request a delayed scheduled scan
 + * @wpa_s: Pointer to wpa_supplicant data
 + * @sec: Number of seconds after which to scan
 + * @usec: Number of microseconds after which to scan
 + * Returns: 0 on success or -1 otherwise
 + *
 + * This function is used to schedule periodic scans for neighboring
 + * access points after the specified time.
 + */
 +int wpa_supplicant_delayed_sched_scan(struct wpa_supplicant *wpa_s,
 +                                    int sec, int usec)
 +{
 +      if (!wpa_s->sched_scan_supported)
 +              return -1;
 +
 +      eloop_register_timeout(sec, usec,
 +                             wpa_supplicant_delayed_sched_scan_timeout,
 +                             wpa_s, NULL);
 +
 +      return 0;
 +}
 +
 +
 +/**
 + * wpa_supplicant_req_sched_scan - Start a periodic scheduled scan
 + * @wpa_s: Pointer to wpa_supplicant data
 + * Returns: 0 is sched_scan was started or -1 otherwise
 + *
 + * This function is used to schedule periodic scans for neighboring
 + * access points repeating the scan continuously.
 + */
 +int wpa_supplicant_req_sched_scan(struct wpa_supplicant *wpa_s)
 +{
 +      struct wpa_driver_scan_params params;
 +      struct wpa_driver_scan_params *scan_params;
 +      enum wpa_states prev_state;
 +      struct wpa_ssid *ssid = NULL;
 +      struct wpabuf *extra_ie = NULL;
 +      int ret;
 +      unsigned int max_sched_scan_ssids;
 +      int wildcard = 0;
 +      int need_ssids;
 +
 +      if (!wpa_s->sched_scan_supported)
 +              return -1;
 +
 +      if (wpa_s->max_sched_scan_ssids > WPAS_MAX_SCAN_SSIDS)
 +              max_sched_scan_ssids = WPAS_MAX_SCAN_SSIDS;
 +      else
 +              max_sched_scan_ssids = wpa_s->max_sched_scan_ssids;
 +      if (max_sched_scan_ssids < 1 || wpa_s->conf->disable_scan_offload)
 +              return -1;
 +
 +      if (wpa_s->sched_scanning) {
 +              wpa_dbg(wpa_s, MSG_DEBUG, "Already sched scanning");
 +              return 0;
 +      }
 +
 +      need_ssids = 0;
 +      for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
 +              if (!wpas_network_disabled(wpa_s, ssid) && !ssid->scan_ssid) {
 +                      /* Use wildcard SSID to find this network */
 +                      wildcard = 1;
 +              } else if (!wpas_network_disabled(wpa_s, ssid) &&
 +                         ssid->ssid_len)
 +                      need_ssids++;
 +
 +#ifdef CONFIG_WPS
 +              if (!wpas_network_disabled(wpa_s, ssid) &&
 +                  ssid->key_mgmt == WPA_KEY_MGMT_WPS) {
 +                      /*
 +                       * Normal scan is more reliable and faster for WPS
 +                       * operations and since these are for short periods of
 +                       * time, the benefit of trying to use sched_scan would
 +                       * be limited.
 +                       */
 +                      wpa_dbg(wpa_s, MSG_DEBUG, "Use normal scan instead of "
 +                              "sched_scan for WPS");
 +                      return -1;
 +              }
 +#endif /* CONFIG_WPS */
 +      }
 +      if (wildcard)
 +              need_ssids++;
 +
 +      if (wpa_s->normal_scans < 3 &&
 +          (need_ssids <= wpa_s->max_scan_ssids ||
 +           wpa_s->max_scan_ssids >= (int) max_sched_scan_ssids)) {
 +              /*
 +               * When normal scan can speed up operations, use that for the
 +               * first operations before starting the sched_scan to allow
 +               * user space sleep more. We do this only if the normal scan
 +               * has functionality that is suitable for this or if the
 +               * sched_scan does not have better support for multiple SSIDs.
 +               */
 +              wpa_dbg(wpa_s, MSG_DEBUG, "Use normal scan instead of "
 +                      "sched_scan for initial scans (normal_scans=%d)",
 +                      wpa_s->normal_scans);
 +              return -1;
 +      }
 +
 +      os_memset(&params, 0, sizeof(params));
 +
 +      /* If we can't allocate space for the filters, we just don't filter */
 +      params.filter_ssids = os_calloc(wpa_s->max_match_sets,
 +                                      sizeof(struct wpa_driver_scan_filter));
 +
 +      prev_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);
 +
 +      if (wpa_s->autoscan_params != NULL) {
 +              scan_params = wpa_s->autoscan_params;
 +              goto scan;
 +      }
 +
 +      /* Find the starting point from which to continue scanning */
 +      ssid = wpa_s->conf->ssid;
 +      if (wpa_s->prev_sched_ssid) {
 +              while (ssid) {
 +                      if (ssid == wpa_s->prev_sched_ssid) {
 +                              ssid = ssid->next;
 +                              break;
 +                      }
 +                      ssid = ssid->next;
 +              }
 +      }
 +
 +      if (!ssid || !wpa_s->prev_sched_ssid) {
 +              wpa_dbg(wpa_s, MSG_DEBUG, "Beginning of SSID list");
 +              if (wpa_s->conf->sched_scan_interval)
 +                      wpa_s->sched_scan_interval =
 +                              wpa_s->conf->sched_scan_interval;
 +              if (wpa_s->sched_scan_interval == 0)
 +                      wpa_s->sched_scan_interval = 10;
 +              wpa_s->sched_scan_timeout = max_sched_scan_ssids * 2;
 +              wpa_s->first_sched_scan = 1;
 +              ssid = wpa_s->conf->ssid;
 +              wpa_s->prev_sched_ssid = ssid;
 +      }
 +
 +      if (wildcard) {
 +              wpa_dbg(wpa_s, MSG_DEBUG, "Add wildcard SSID to sched_scan");
 +              params.num_ssids++;
 +      }
 +
 +      while (ssid) {
 +              if (wpas_network_disabled(wpa_s, ssid))
 +                      goto next;
 +
 +              if (params.num_filter_ssids < wpa_s->max_match_sets &&
 +                  params.filter_ssids && ssid->ssid && ssid->ssid_len) {
 +                      wpa_dbg(wpa_s, MSG_DEBUG, "add to filter ssid: %s",
 +                              wpa_ssid_txt(ssid->ssid, ssid->ssid_len));
 +                      os_memcpy(params.filter_ssids[params.num_filter_ssids].ssid,
 +                                ssid->ssid, ssid->ssid_len);
 +                      params.filter_ssids[params.num_filter_ssids].ssid_len =
 +                              ssid->ssid_len;
 +                      params.num_filter_ssids++;
 +              } else if (params.filter_ssids && ssid->ssid && ssid->ssid_len)
 +              {
 +                      wpa_dbg(wpa_s, MSG_DEBUG, "Not enough room for SSID "
 +                              "filter for sched_scan - drop filter");
 +                      os_free(params.filter_ssids);
 +                      params.filter_ssids = NULL;
 +                      params.num_filter_ssids = 0;
 +              }
 +
 +              if (ssid->scan_ssid && ssid->ssid && ssid->ssid_len) {
 +                      if (params.num_ssids == max_sched_scan_ssids)
 +                              break; /* only room for broadcast SSID */
 +                      wpa_dbg(wpa_s, MSG_DEBUG,
 +                              "add to active scan ssid: %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++;
 +                      if (params.num_ssids >= max_sched_scan_ssids) {
 +                              wpa_s->prev_sched_ssid = ssid;
 +                              do {
 +                                      ssid = ssid->next;
 +                              } while (ssid &&
 +                                       (wpas_network_disabled(wpa_s, ssid) ||
 +                                        !ssid->scan_ssid));
 +                              break;
 +                      }
 +              }
 +
 +      next:
 +              wpa_s->prev_sched_ssid = ssid;
 +              ssid = ssid->next;
 +      }
 +
 +      if (params.num_filter_ssids == 0) {
 +              os_free(params.filter_ssids);
 +              params.filter_ssids = NULL;
 +      }
 +
 +      extra_ie = wpa_supplicant_extra_ies(wpa_s);
 +      if (extra_ie) {
 +              params.extra_ies = wpabuf_head(extra_ie);
 +              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:
 +      if (ssid || !wpa_s->first_sched_scan) {
 +              wpa_dbg(wpa_s, MSG_DEBUG,
 +                      "Starting sched scan: interval %d timeout %d",
 +                      wpa_s->sched_scan_interval, wpa_s->sched_scan_timeout);
 +      } else {
 +              wpa_dbg(wpa_s, MSG_DEBUG,
 +                      "Starting sched scan: interval %d (no timeout)",
 +                      wpa_s->sched_scan_interval);
 +      }
 +
 +      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);
 +      os_free(params.filter_ssids);
 +      if (ret) {
 +              wpa_msg(wpa_s, MSG_WARNING, "Failed to initiate sched scan");
 +              if (prev_state != wpa_s->wpa_state)
 +                      wpa_supplicant_set_state(wpa_s, prev_state);
 +              return ret;
 +      }
 +
 +      /* If we have more SSIDs to scan, add a timeout so we scan them too */
 +      if (ssid || !wpa_s->first_sched_scan) {
 +              wpa_s->sched_scan_timed_out = 0;
 +              eloop_register_timeout(wpa_s->sched_scan_timeout, 0,
 +                                     wpa_supplicant_sched_scan_timeout,
 +                                     wpa_s, NULL);
 +              wpa_s->first_sched_scan = 0;
 +              wpa_s->sched_scan_timeout /= 2;
 +              wpa_s->sched_scan_interval *= 2;
 +              if (wpa_s->sched_scan_timeout < wpa_s->sched_scan_interval) {
 +                      wpa_s->sched_scan_interval = 10;
 +                      wpa_s->sched_scan_timeout = max_sched_scan_ssids * 2;
 +              }
 +      }
 +
 +      /* If there is no more ssids, start next time from the beginning */
 +      if (!ssid)
 +              wpa_s->prev_sched_ssid = NULL;
 +
 +      return 0;
 +}
 +
 +
 +/**
 + * wpa_supplicant_cancel_scan - Cancel a scheduled scan request
 + * @wpa_s: Pointer to wpa_supplicant data
 + *
 + * This function is used to cancel a scan request scheduled with
 + * wpa_supplicant_req_scan().
 + */
 +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);
 +}
 +
 +
 +/**
 + * 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);
 +}
 +
 +
 +/**
 + * wpa_supplicant_cancel_sched_scan - Stop running scheduled scans
 + * @wpa_s: Pointer to wpa_supplicant data
 + *
 + * This function is used to stop a periodic scheduled scan.
 + */
 +void wpa_supplicant_cancel_sched_scan(struct wpa_supplicant *wpa_s)
 +{
 +      if (!wpa_s->sched_scanning)
 +              return;
 +
 +      wpa_dbg(wpa_s, MSG_DEBUG, "Cancelling sched scan");
 +      eloop_cancel_timeout(wpa_supplicant_sched_scan_timeout, wpa_s, NULL);
 +      wpa_supplicant_stop_sched_scan(wpa_s);
 +}
 +
 +
 +/**
 + * wpa_supplicant_notify_scanning - Indicate possible scan state change
 + * @wpa_s: Pointer to wpa_supplicant data
 + * @scanning: Whether scanning is currently in progress
 + *
 + * This function is to generate scanning notifycations. It is called whenever
 + * there may have been a change in scanning (scan started, completed, stopped).
 + * wpas_notify_scanning() is called whenever the scanning state changed from the
 + * previously notified state.
 + */
 +void wpa_supplicant_notify_scanning(struct wpa_supplicant *wpa_s,
 +                                  int scanning)
 +{
 +      if (wpa_s->scanning != scanning) {
 +              wpa_s->scanning = scanning;
 +              wpas_notify_scanning(wpa_s);
 +      }
 +}
 +
 +
 +static int wpa_scan_get_max_rate(const struct wpa_scan_res *res)
 +{
 +      int rate = 0;
 +      const u8 *ie;
 +      int i;
 +
 +      ie = wpa_scan_get_ie(res, WLAN_EID_SUPP_RATES);
 +      for (i = 0; ie && i < ie[1]; i++) {
 +              if ((ie[i + 2] & 0x7f) > rate)
 +                      rate = ie[i + 2] & 0x7f;
 +      }
 +
 +      ie = wpa_scan_get_ie(res, WLAN_EID_EXT_SUPP_RATES);
 +      for (i = 0; ie && i < ie[1]; i++) {
 +              if ((ie[i + 2] & 0x7f) > rate)
 +                      rate = ie[i + 2] & 0x7f;
 +      }
 +
 +      return rate;
 +}
 +
 +
 +/**
 + * wpa_scan_get_ie - Fetch a specified information element from a scan result
 + * @res: Scan result entry
 + * @ie: Information element identitifier (WLAN_EID_*)
 + * 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.
 + */
 +const u8 * wpa_scan_get_ie(const struct wpa_scan_res *res, u8 ie)
 +{
 +      const u8 *end, *pos;
 +
 +      pos = (const u8 *) (res + 1);
 +      end = pos + res->ie_len;
 +
 +      while (pos + 1 < end) {
 +              if (pos + 2 + pos[1] > end)
 +                      break;
 +              if (pos[0] == ie)
 +                      return pos;
 +              pos += 2 + pos[1];
 +      }
 +
 +      return NULL;
 +}
 +
 +
 +/**
 + * wpa_scan_get_vendor_ie - Fetch vendor information element 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.
 + */
 +const u8 * wpa_scan_get_vendor_ie(const struct wpa_scan_res *res,
 +                                u32 vendor_type)
 +{
 +      const u8 *end, *pos;
 +
 +      pos = (const u8 *) (res + 1);
 +      end = pos + res->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_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)
 + * Returns: Pointer to the information element payload or %NULL if not found
 + *
 + * This function returns concatenated payload of possibly fragmented vendor
 + * specific information elements in the scan result. The caller is responsible
 + * for freeing the returned buffer.
 + */
 +struct wpabuf * wpa_scan_get_vendor_ie_multi(const struct wpa_scan_res *res,
 +                                           u32 vendor_type)
 +{
 +      struct wpabuf *buf;
 +      const u8 *end, *pos;
 +
 +      buf = wpabuf_alloc(res->ie_len);
 +      if (buf == NULL)
 +              return NULL;
 +
 +      pos = (const u8 *) (res + 1);
 +      end = pos + res->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]))
 +                      wpabuf_put_data(buf, pos + 2 + 4, pos[1] - 4);
 +              pos += 2 + pos[1];
 +      }
 +
 +      if (wpabuf_len(buf) == 0) {
 +              wpabuf_free(buf);
 +              buf = NULL;
 +      }
 +
 +      return buf;
 +}
 +
 +
 +/*
 + * Channels with a great SNR can operate at full rate. What is a great SNR?
 + * This doc https://supportforums.cisco.com/docs/DOC-12954 says, "the general
 + * rule of thumb is that any SNR above 20 is good." This one
 + * http://www.cisco.com/en/US/tech/tk722/tk809/technologies_q_and_a_item09186a00805e9a96.shtml#qa23
 + * recommends 25 as a minimum SNR for 54 Mbps data rate. 30 is chosen here as a
 + * conservative value.
 + */
 +#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 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;
 +      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 ||
 +              wpa_scan_get_ie(wa, WLAN_EID_RSN) != NULL;
 +      wpa_b = wpa_scan_get_vendor_ie(wb, WPA_IE_VENDOR_TYPE) != NULL ||
 +              wpa_scan_get_ie(wb, WLAN_EID_RSN) != NULL;
 +
 +      if (wpa_b && !wpa_a)
 +              return 1;
 +      if (!wpa_b && wpa_a)
 +              return -1;
 +
 +      /* privacy support preferred */
 +      if ((wa->caps & IEEE80211_CAP_PRIVACY) == 0 &&
 +          (wb->caps & IEEE80211_CAP_PRIVACY))
 +              return 1;
 +      if ((wa->caps & IEEE80211_CAP_PRIVACY) &&
 +          (wb->caps & IEEE80211_CAP_PRIVACY) == 0)
 +              return -1;
 +
 +      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(wb->snr, GREAT_SNR);
 +      } else {
 +              /* 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;
 +      }
 +
 +      /* 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)) {
 +              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;
 +      }
 +
 +      /* 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_full == snr_a_full)
 +              return wb->qual - wa->qual;
 +      return snr_b_full - snr_a_full;
 +#undef MIN
 +}
 +
 +
 +#ifdef CONFIG_WPS
 +/* Compare function for sorting scan results when searching a WPS AP for
 + * provisioning. Return >0 if @b is considered better. */
 +static int wpa_scan_result_wps_compar(const void *a, const void *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 uses_wps_a, uses_wps_b;
 +      struct wpabuf *wps_a, *wps_b;
 +      int res;
 +
 +      /* Optimization - check WPS IE existence before allocated memory and
 +       * doing full reassembly. */
 +      uses_wps_a = wpa_scan_get_vendor_ie(wa, WPS_IE_VENDOR_TYPE) != NULL;
 +      uses_wps_b = wpa_scan_get_vendor_ie(wb, WPS_IE_VENDOR_TYPE) != NULL;
 +      if (uses_wps_a && !uses_wps_b)
 +              return -1;
 +      if (!uses_wps_a && uses_wps_b)
 +              return 1;
 +
 +      if (uses_wps_a && uses_wps_b) {
 +              wps_a = wpa_scan_get_vendor_ie_multi(wa, WPS_IE_VENDOR_TYPE);
 +              wps_b = wpa_scan_get_vendor_ie_multi(wb, WPS_IE_VENDOR_TYPE);
 +              res = wps_ap_priority_compar(wps_a, wps_b);
 +              wpabuf_free(wps_a);
 +              wpabuf_free(wps_b);
 +              if (res)
 +                      return res;
 +      }
 +
 +      /*
 +       * Do not use current AP security policy as a sorting criteria during
 +       * WPS provisioning step since the AP may get reconfigured at the
 +       * completion of provisioning.
 +       */
 +
 +      /* all things being equal, use signal level; if signal levels are
 +       * identical, use quality values since some drivers may only report
 +       * that value and leave the signal level zero */
 +      if (wb->level == wa->level)
 +              return wb->qual - wa->qual;
 +      return wb->level - wa->level;
 +}
 +#endif /* CONFIG_WPS */
 +
 +
 +static void dump_scan_res(struct wpa_scan_results *scan_res)
 +{
 +#ifndef CONFIG_NO_STDOUT_DEBUG
 +      size_t i;
 +
 +      if (scan_res->res == NULL || scan_res->num == 0)
 +              return;
 +
 +      wpa_printf(MSG_EXCESSIVE, "Sorted scan results");
 +
 +      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) {
 +                      int noise_valid = !(r->flags & WPA_SCAN_NOISE_INVALID);
 +
 +                      wpa_printf(MSG_EXCESSIVE, MACSTR " freq=%d qual=%d "
 +                                 "noise=%d%s level=%d snr=%d%s flags=0x%x age=%u est=%u",
 +                                 MAC2STR(r->bssid), r->freq, r->qual,
 +                                 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 est=%u",
 +                                 MAC2STR(r->bssid), r->freq, r->qual,
 +                                 r->noise, r->level, r->flags, r->age,
 +                                 r->est_throughput);
 +              }
 +              pos = (u8 *) (r + 1);
 +              if (r->ie_len)
 +                      wpa_hexdump(MSG_EXCESSIVE, "IEs", pos, r->ie_len);
 +              pos += r->ie_len;
 +              if (r->beacon_ie_len)
 +                      wpa_hexdump(MSG_EXCESSIVE, "Beacon IEs",
 +                                  pos, r->beacon_ie_len);
 +      }
 +#endif /* CONFIG_NO_STDOUT_DEBUG */
 +}
 +
 +
 +/**
 + * wpa_supplicant_filter_bssid_match - Is the specified BSSID allowed
 + * @wpa_s: Pointer to wpa_supplicant data
 + * @bssid: BSSID to check
 + * Returns: 0 if the BSSID is filtered or 1 if not
 + *
 + * This function is used to filter out specific BSSIDs from scan reslts mainly
 + * for testing purposes (SET bssid_filter ctrl_iface command).
 + */
 +int wpa_supplicant_filter_bssid_match(struct wpa_supplicant *wpa_s,
 +                                    const u8 *bssid)
 +{
 +      size_t i;
 +
 +      if (wpa_s->bssid_filter == NULL)
 +              return 1;
 +
 +      for (i = 0; i < wpa_s->bssid_filter_count; i++) {
 +              if (os_memcmp(wpa_s->bssid_filter + i * ETH_ALEN, bssid,
 +                            ETH_ALEN) == 0)
 +                      return 1;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +static void filter_scan_res(struct wpa_supplicant *wpa_s,
 +                          struct wpa_scan_results *res)
 +{
 +      size_t i, j;
 +
 +      if (wpa_s->bssid_filter == NULL)
 +              return;
 +
 +      for (i = 0, j = 0; i < res->num; i++) {
 +              if (wpa_supplicant_filter_bssid_match(wpa_s,
 +                                                    res->res[i]->bssid)) {
 +                      res->res[j++] = res->res[i];
 +              } else {
 +                      os_free(res->res[i]);
 +                      res->res[i] = NULL;
 +              }
 +      }
 +
 +      if (res->num != j) {
 +              wpa_printf(MSG_DEBUG, "Filtered out %d scan results",
 +                         (int) (res->num - j));
 +              res->num = j;
 +      }
 +}
 +
 +
 +/*
 + * 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
 + * @info: Information about what was scanned or %NULL if not available
 + * @new_scan: Whether a new scan was performed
 + * Returns: Scan results, %NULL on failure
 + *
 + * This function request the current scan results from the driver and updates
 + * the local BSS list wpa_s->bss. The caller is responsible for freeing the
 + * results with wpa_scan_results_free().
 + */
 +struct wpa_scan_results *
 +wpa_supplicant_get_scan_results(struct wpa_supplicant *wpa_s,
 +                              struct scan_info *info, int new_scan)
 +{
 +      struct wpa_scan_results *scan_res;
 +      size_t i;
 +      int (*compar)(const void *, const void *) = wpa_scan_result_compar;
 +
 +      scan_res = wpa_drv_get_scan_results2(wpa_s);
 +      if (scan_res == NULL) {
 +              wpa_dbg(wpa_s, MSG_DEBUG, "Failed to get scan results");
 +              return NULL;
 +      }
 +      if (scan_res->fetch_time.sec == 0) {
 +              /*
 +               * Make sure we have a valid timestamp if the driver wrapper
 +               * does not set this.
 +               */
 +              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_searching(wpa_s)) {
 +              wpa_dbg(wpa_s, MSG_DEBUG, "WPS: Order scan results with WPS "
 +                      "provisioning rules");
 +              compar = wpa_scan_result_wps_compar;
 +      }
 +#endif /* CONFIG_WPS */
 +
 +      qsort(scan_res->res, scan_res->num, sizeof(struct wpa_scan_res *),
 +            compar);
 +      dump_scan_res(scan_res);
 +
 +      wpa_bss_update_start(wpa_s);
 +      for (i = 0; i < scan_res->num; i++)
 +              wpa_bss_update_scan_res(wpa_s, scan_res->res[i],
 +                                      &scan_res->fetch_time);
 +      wpa_bss_update_end(wpa_s, info, new_scan);
 +
 +      return scan_res;
 +}
 +
 +
 +/**
 + * wpa_supplicant_update_scan_results - Update scan results from the driver
 + * @wpa_s: Pointer to wpa_supplicant data
 + * Returns: 0 on success, -1 on failure
 + *
 + * This function updates the BSS table within wpa_supplicant based on the
 + * currently available scan results from the driver without requesting a new
 + * scan. This is used in cases where the driver indicates an association
 + * (including roaming within ESS) and wpa_supplicant does not yet have the
 + * needed information to complete the connection (e.g., to perform validation
 + * steps in 4-way handshake).
 + */
 +int wpa_supplicant_update_scan_results(struct wpa_supplicant *wpa_s)
 +{
 +      struct wpa_scan_results *scan_res;
 +      scan_res = wpa_supplicant_get_scan_results(wpa_s, NULL, 0);
 +      if (scan_res == NULL)
 +              return -1;
 +      wpa_scan_results_free(scan_res);
 +
 +      return 0;
 +}
 +
 +
 +/**
 + * scan_only_handler - Reports scan results
 + */
 +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");
 +      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);
 +      }
 +}
 +
 +
 +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;
 +}
index 178811371409c033909c28e93bec9a748132cab6,0000000000000000000000000000000000000000..f2e5a43b978fd0a5ad8447cef7c0bcaa53b89e6a
mode 100644,000000..100644
--- /dev/null
@@@ -1,1616 -1,0 +1,1662 @@@
-               if (group < 0)
 +/*
 + * wpa_supplicant - SME
 + * 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.
 + */
 +
 +#include "includes.h"
 +
 +#include "common.h"
 +#include "utils/eloop.h"
 +#include "common/ieee802_11_defs.h"
 +#include "common/ieee802_11_common.h"
 +#include "eapol_supp/eapol_supp_sm.h"
 +#include "common/wpa_common.h"
 +#include "common/sae.h"
 +#include "rsn_supp/wpa.h"
 +#include "rsn_supp/pmksa_cache.h"
 +#include "config.h"
 +#include "wpa_supplicant_i.h"
 +#include "driver_i.h"
 +#include "wpas_glue.h"
 +#include "wps_supplicant.h"
 +#include "p2p_supplicant.h"
 +#include "notify.h"
 +#include "bss.h"
 +#include "scan.h"
 +#include "sme.h"
 +#include "hs20_supplicant.h"
 +
 +#define SME_AUTH_TIMEOUT 5
 +#define SME_ASSOC_TIMEOUT 5
 +
 +static void sme_auth_timer(void *eloop_ctx, void *timeout_ctx);
 +static void sme_assoc_timer(void *eloop_ctx, void *timeout_ctx);
 +static void sme_obss_scan_timeout(void *eloop_ctx, void *timeout_ctx);
 +#ifdef CONFIG_IEEE80211W
 +static void sme_stop_sa_query(struct wpa_supplicant *wpa_s);
 +#endif /* CONFIG_IEEE80211W */
 +
 +
 +#ifdef CONFIG_SAE
 +
 +static int index_within_array(const int *array, int idx)
 +{
 +      int i;
 +      for (i = 0; i < idx; i++) {
 +              if (array[i] <= 0)
 +                      return 0;
 +      }
 +      return 1;
 +}
 +
 +
 +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, 0 };
 +
 +      if (!groups || groups[0] <= 0)
 +              groups = default_groups;
 +
 +      /* Configuration may have changed, so validate current index */
 +      if (!index_within_array(groups, wpa_s->sme.sae_group_index))
 +              return -1;
 +
 +      for (;;) {
 +              int group = groups[wpa_s->sme.sae_group_index];
-           !wpas_valid_bss_ssid(wpa_s, cwork->bss, cwork->ssid)) {
++              if (group <= 0)
 +                      break;
 +              if (sae_set_group(&wpa_s->sme.sae, group) == 0) {
 +                      wpa_dbg(wpa_s, MSG_DEBUG, "SME: Selected SAE group %d",
 +                              wpa_s->sme.sae.group);
 +                     return 0;
 +              }
 +              wpa_s->sme.sae_group_index++;
 +      }
 +
 +      return -1;
 +}
 +
 +
 +static struct wpabuf * sme_auth_build_sae_commit(struct wpa_supplicant *wpa_s,
 +                                               struct wpa_ssid *ssid,
 +                                               const u8 *bssid)
 +{
 +      struct wpabuf *buf;
 +      size_t len;
 +
 +      if (ssid->passphrase == NULL) {
 +              wpa_printf(MSG_DEBUG, "SAE: No password available");
 +              return NULL;
 +      }
 +
 +      if (sme_set_sae_group(wpa_s) < 0) {
 +              wpa_printf(MSG_DEBUG, "SAE: Failed to select group");
 +              return NULL;
 +      }
 +
 +      if (sae_prepare_commit(wpa_s->own_addr, bssid,
 +                             (u8 *) ssid->passphrase,
 +                             os_strlen(ssid->passphrase),
 +                             &wpa_s->sme.sae) < 0) {
 +              wpa_printf(MSG_DEBUG, "SAE: Could not pick PWE");
 +              return NULL;
 +      }
 +
 +      len = wpa_s->sme.sae_token ? wpabuf_len(wpa_s->sme.sae_token) : 0;
 +      buf = wpabuf_alloc(4 + SAE_COMMIT_MAX_LEN + len);
 +      if (buf == NULL)
 +              return NULL;
 +
 +      wpabuf_put_le16(buf, 1); /* Transaction seq# */
 +      wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS);
 +      sae_write_commit(&wpa_s->sme.sae, buf, wpa_s->sme.sae_token);
 +
 +      return buf;
 +}
 +
 +
 +static struct wpabuf * sme_auth_build_sae_confirm(struct wpa_supplicant *wpa_s)
 +{
 +      struct wpabuf *buf;
 +
 +      buf = wpabuf_alloc(4 + SAE_CONFIRM_MAX_LEN);
 +      if (buf == NULL)
 +              return NULL;
 +
 +      wpabuf_put_le16(buf, 2); /* Transaction seq# */
 +      wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS);
 +      sae_write_confirm(&wpa_s->sme.sae, buf);
 +
 +      return buf;
 +}
 +
 +#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)
 +{
 +      struct wpa_driver_auth_params params;
 +      struct wpa_ssid *old_ssid;
 +#ifdef CONFIG_IEEE80211R
 +      const u8 *ie;
 +#endif /* CONFIG_IEEE80211R */
 +#ifdef CONFIG_IEEE80211R
 +      const u8 *md = NULL;
 +#endif /* CONFIG_IEEE80211R */
 +      int i, bssid_changed;
 +      struct wpabuf *resp = NULL;
 +      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));
 +      wpa_s->reassociate = 0;
 +
 +      params.freq = bss->freq;
 +      params.bssid = bss->bssid;
 +      params.ssid = bss->ssid;
 +      params.ssid_len = bss->ssid_len;
 +      params.p2p = ssid->p2p_group;
 +
 +      if (wpa_s->sme.ssid_len != params.ssid_len ||
 +          os_memcmp(wpa_s->sme.ssid, params.ssid, params.ssid_len) != 0)
 +              wpa_s->sme.prev_bssid_set = 0;
 +
 +      wpa_s->sme.freq = params.freq;
 +      os_memcpy(wpa_s->sme.ssid, params.ssid, params.ssid_len);
 +      wpa_s->sme.ssid_len = params.ssid_len;
 +
 +      params.auth_alg = WPA_AUTH_ALG_OPEN;
 +#ifdef IEEE8021X_EAPOL
 +      if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) {
 +              if (ssid->leap) {
 +                      if (ssid->non_leap == 0)
 +                              params.auth_alg = WPA_AUTH_ALG_LEAP;
 +                      else
 +                              params.auth_alg |= WPA_AUTH_ALG_LEAP;
 +              }
 +      }
 +#endif /* IEEE8021X_EAPOL */
 +      wpa_dbg(wpa_s, MSG_DEBUG, "Automatic auth_alg selection: 0x%x",
 +              params.auth_alg);
 +      if (ssid->auth_alg) {
 +              params.auth_alg = ssid->auth_alg;
 +              wpa_dbg(wpa_s, MSG_DEBUG, "Overriding auth_alg selection: "
 +                      "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_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 */
 +
 +      for (i = 0; i < NUM_WEP_KEYS; i++) {
 +              if (ssid->wep_key_len[i])
 +                      params.wep_key[i] = ssid->wep_key[i];
 +              params.wep_key_len[i] = ssid->wep_key_len[i];
 +      }
 +      params.wep_tx_keyidx = ssid->wep_tx_keyidx;
 +
 +      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);
 +
 +      if ((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,
 +                                          wpa_s->current_ssid,
 +                                          try_opportunistic) == 0)
 +                      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) &&
 +                 wpa_key_mgmt_wpa_ieee8021x(ssid->key_mgmt)) {
 +              /*
 +               * Both WPA and non-WPA IEEE 802.1X enabled in configuration -
 +               * use non-WPA since the scan results did not indicate that the
 +               * AP is using WPA or WPA2.
 +               */
 +              wpa_supplicant_set_non_wpa_policy(wpa_s, ssid);
 +              wpa_s->sme.assoc_req_ie_len = 0;
 +      } else if (wpa_key_mgmt_wpa_any(ssid->key_mgmt)) {
 +              wpa_s->sme.assoc_req_ie_len = sizeof(wpa_s->sme.assoc_req_ie);
 +              if (wpa_supplicant_set_suites(wpa_s, NULL, 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 (no "
 +                              "scan results)");
 +                      wpas_connect_work_done(wpa_s);
 +                      return;
 +              }
 +#ifdef CONFIG_WPS
 +      } else if (ssid->key_mgmt & WPA_KEY_MGMT_WPS) {
 +              struct wpabuf *wps_ie;
 +              wps_ie = wps_build_assoc_req_ie(wpas_wps_get_req_type(ssid));
 +              if (wps_ie && wpabuf_len(wps_ie) <=
 +                  sizeof(wpa_s->sme.assoc_req_ie)) {
 +                      wpa_s->sme.assoc_req_ie_len = wpabuf_len(wps_ie);
 +                      os_memcpy(wpa_s->sme.assoc_req_ie, wpabuf_head(wps_ie),
 +                                wpa_s->sme.assoc_req_ie_len);
 +              } else
 +                      wpa_s->sme.assoc_req_ie_len = 0;
 +              wpabuf_free(wps_ie);
 +              wpa_supplicant_set_non_wpa_policy(wpa_s, ssid);
 +#endif /* CONFIG_WPS */
 +      } else {
 +              wpa_supplicant_set_non_wpa_policy(wpa_s, ssid);
 +              wpa_s->sme.assoc_req_ie_len = 0;
 +      }
 +
 +#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);
 +      }
 +
 +      if (md && wpa_key_mgmt_ft(ssid->key_mgmt)) {
 +              if (wpa_s->sme.assoc_req_ie_len + 5 <
 +                  sizeof(wpa_s->sme.assoc_req_ie)) {
 +                      struct rsn_mdie *mdie;
 +                      u8 *pos = wpa_s->sme.assoc_req_ie +
 +                              wpa_s->sme.assoc_req_ie_len;
 +                      *pos++ = WLAN_EID_MOBILITY_DOMAIN;
 +                      *pos++ = sizeof(*mdie);
 +                      mdie = (struct rsn_mdie *) pos;
 +                      os_memcpy(mdie->mobility_domain, md,
 +                                MOBILITY_DOMAIN_ID_LEN);
 +                      mdie->ft_capab = md[MOBILITY_DOMAIN_ID_LEN];
 +                      wpa_s->sme.assoc_req_ie_len += 5;
 +              }
 +
 +              if (wpa_s->sme.ft_used &&
 +                  os_memcmp(md, wpa_s->sme.mobility_domain, 2) == 0 &&
 +                  wpa_sm_has_ptk(wpa_s->wpa)) {
 +                      wpa_dbg(wpa_s, MSG_DEBUG, "SME: Trying to use FT "
 +                              "over-the-air");
 +                      params.auth_alg = WPA_AUTH_ALG_FT;
 +                      params.ie = wpa_s->sme.ft_ies;
 +                      params.ie_len = wpa_s->sme.ft_ies_len;
 +              }
 +      }
 +#endif /* CONFIG_IEEE80211R */
 +
 +#ifdef CONFIG_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;
 +              if (rsn && wpa_parse_wpa_ie(rsn, 2 + rsn[1], &_ie) == 0 &&
 +                  _ie.capabilities &
 +                  (WPA_CAPABILITY_MFPC | WPA_CAPABILITY_MFPR)) {
 +                      wpa_dbg(wpa_s, MSG_DEBUG, "SME: Selected AP supports "
 +                              "MFP: require MFP");
 +                      wpa_s->sme.mfp = MGMT_FRAME_PROTECTION_REQUIRED;
 +              }
 +      }
 +#endif /* CONFIG_IEEE80211W */
 +
 +#ifdef CONFIG_P2P
 +      if (wpa_s->global->p2p) {
 +              u8 *pos;
 +              size_t len;
 +              int res;
 +              pos = wpa_s->sme.assoc_req_ie + wpa_s->sme.assoc_req_ie_len;
 +              len = sizeof(wpa_s->sme.assoc_req_ie) -
 +                      wpa_s->sme.assoc_req_ie_len;
 +              res = wpas_p2p_assoc_req_ie(wpa_s, bss, pos, len,
 +                                          ssid->p2p_group);
 +              if (res >= 0)
 +                      wpa_s->sme.assoc_req_ie_len += res;
 +      }
 +#endif /* CONFIG_P2P */
 +
 +#ifdef CONFIG_HS20
 +      if (is_hs20_network(wpa_s, ssid, bss)) {
 +              struct wpabuf *hs20;
 +              hs20 = wpabuf_alloc(20);
 +              if (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 */
 +
++#ifdef CONFIG_FST
++      if (wpa_s->fst_ies) {
++              int fst_ies_len = wpabuf_len(wpa_s->fst_ies);
++
++              if (wpa_s->sme.assoc_req_ie_len + fst_ies_len <=
++                  sizeof(wpa_s->sme.assoc_req_ie)) {
++                      os_memcpy(wpa_s->sme.assoc_req_ie +
++                                wpa_s->sme.assoc_req_ie_len,
++                                wpabuf_head(wpa_s->fst_ies),
++                                fst_ies_len);
++                      wpa_s->sme.assoc_req_ie_len += fst_ies_len;
++              }
++      }
++#endif /* CONFIG_FST */
++
 +      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)
 +                      pos += 2 + pos[1];
 +              os_memmove(pos + ext_capab_len, pos,
 +                         wpa_s->sme.assoc_req_ie_len -
 +                         (pos - wpa_s->sme.assoc_req_ie));
 +              wpa_s->sme.assoc_req_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_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 (!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) {
 +                      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;
 +      }
 +#endif /* CONFIG_SAE */
 +
 +      wpa_supplicant_cancel_sched_scan(wpa_s);
 +      wpa_supplicant_cancel_scan(wpa_s);
 +
 +      wpa_msg(wpa_s, MSG_INFO, "SME: Trying to authenticate with " MACSTR
 +              " (SSID='%s' freq=%d MHz)", MAC2STR(params.bssid),
 +              wpa_ssid_txt(params.ssid, params.ssid_len), params.freq);
 +
 +      wpa_clear_keys(wpa_s, bss->bssid);
 +      wpa_supplicant_set_state(wpa_s, WPA_AUTHENTICATING);
 +      old_ssid = wpa_s->current_ssid;
 +      wpa_s->current_ssid = ssid;
 +      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)
 +              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 "
 +                      "driver failed");
 +              wpas_connection_failed(wpa_s, bss->bssid);
 +              wpa_supplicant_mark_disassoc(wpa_s);
 +              wpabuf_free(resp);
 +              wpas_connect_work_done(wpa_s);
 +              return;
 +      }
 +
 +      eloop_register_timeout(SME_AUTH_TIMEOUT, 0, sme_auth_timer, wpa_s,
 +                             NULL);
 +
 +      /*
 +       * Association will be started based on the authentication event from
 +       * the driver.
 +       */
 +
 +      wpabuf_free(resp);
 +}
 +
 +
 +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 ||
-               if (sae_parse_commit(&wpa_s->sme.sae, data, len, NULL, NULL,
-                                    groups) != WLAN_STATUS_SUCCESS)
++          !wpas_valid_bss_ssid(wpa_s, cwork->bss, cwork->ssid) ||
++          wpas_network_disabled(wpa_s, 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 */
 +
 +      if (radio_add_work(wpa_s, bss->freq, "sme-connect", 1,
 +                         sme_auth_start_cb, cwork) < 0)
 +              wpas_connect_work_free(cwork);
 +}
 +
 +
 +#ifdef CONFIG_SAE
 +
 +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);
 +
 +      if (auth_transaction == 1 &&
 +          status_code == WLAN_STATUS_ANTI_CLOGGING_TOKEN_REQ &&
 +          wpa_s->sme.sae.state == SAE_COMMITTED &&
 +          wpa_s->current_bss && wpa_s->current_ssid) {
 +              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 + sizeof(le16),
 +                                                       len - sizeof(le16));
 +              sme_send_authentication(wpa_s, wpa_s->current_bss,
 +                                      wpa_s->current_ssid, 1);
 +              return 0;
 +      }
 +
 +      if (auth_transaction == 1 &&
 +          status_code == WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED &&
 +          wpa_s->sme.sae.state == SAE_COMMITTED &&
 +          wpa_s->current_bss && wpa_s->current_ssid) {
 +              wpa_dbg(wpa_s, MSG_DEBUG, "SME: SAE group not supported");
 +              wpa_s->sme.sae_group_index++;
 +              if (sme_set_sae_group(wpa_s) < 0)
 +                      return -1; /* no other groups enabled */
 +              wpa_dbg(wpa_s, MSG_DEBUG, "SME: Try next enabled SAE group");
 +              sme_send_authentication(wpa_s, wpa_s->current_bss,
 +                                      wpa_s->current_ssid, 1);
 +              return 0;
 +      }
 +
 +      if (status_code != WLAN_STATUS_SUCCESS)
 +              return -1;
 +
 +      if (auth_transaction == 1) {
++              u16 res;
++
 +              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;
-               wpa_dbg(wpa_s, MSG_DEBUG, "SME: Authentication failed (status "
-                       "code %d)", data->auth.status_code);
++              res = sae_parse_commit(&wpa_s->sme.sae, data, len, NULL, NULL,
++                                     groups);
++              if (res == SAE_SILENTLY_DISCARD) {
++                      wpa_printf(MSG_DEBUG,
++                                 "SAE: Drop commit message due to reflection attack");
++                      return 0;
++              }
++              if (res != WLAN_STATUS_SUCCESS)
 +                      return -1;
 +
 +              if (sae_process_commit(&wpa_s->sme.sae) < 0) {
 +                      wpa_printf(MSG_DEBUG, "SAE: Failed to process peer "
 +                                 "commit");
 +                      return -1;
 +              }
 +
 +              wpabuf_free(wpa_s->sme.sae_token);
 +              wpa_s->sme.sae_token = NULL;
 +              sme_send_authentication(wpa_s, wpa_s->current_bss,
 +                                      wpa_s->current_ssid, 0);
 +              return 0;
 +      } else if (auth_transaction == 2) {
 +              wpa_dbg(wpa_s, MSG_DEBUG, "SME SAE confirm");
 +              if (wpa_s->sme.sae.state != SAE_CONFIRMED)
 +                      return -1;
 +              if (sae_check_confirm(&wpa_s->sme.sae, data, len) < 0)
 +                      return -1;
 +              wpa_s->sme.sae.state = SAE_ACCEPTED;
 +              sae_clear_temp_data(&wpa_s->sme.sae);
 +              return 1;
 +      }
 +
 +      return -1;
 +}
 +#endif /* CONFIG_SAE */
 +
 +
 +void sme_event_auth(struct wpa_supplicant *wpa_s, union wpa_event_data *data)
 +{
 +      struct wpa_ssid *ssid = wpa_s->current_ssid;
 +
 +      if (ssid == NULL) {
 +              wpa_dbg(wpa_s, MSG_DEBUG, "SME: Ignore authentication event "
 +                      "when network is not selected");
 +              return;
 +      }
 +
 +      if (wpa_s->wpa_state != WPA_AUTHENTICATING) {
 +              wpa_dbg(wpa_s, MSG_DEBUG, "SME: Ignore authentication event "
 +                      "when not in authenticating state");
 +              return;
 +      }
 +
 +      if (os_memcmp(wpa_s->pending_bssid, data->auth.peer, ETH_ALEN) != 0) {
 +              wpa_dbg(wpa_s, MSG_DEBUG, "SME: Ignore authentication with "
 +                      "unexpected peer " MACSTR,
 +                      MAC2STR(data->auth.peer));
 +              return;
 +      }
 +
 +      wpa_dbg(wpa_s, MSG_DEBUG, "SME: Authentication response: peer=" MACSTR
 +              " auth_type=%d auth_transaction=%d status_code=%d",
 +              MAC2STR(data->auth.peer), data->auth.auth_type,
 +              data->auth.auth_transaction, data->auth.status_code);
 +      wpa_hexdump(MSG_MSGDUMP, "SME: Authentication response IEs",
 +                  data->auth.ies, data->auth.ies_len);
 +
 +      eloop_cancel_timeout(sme_auth_timer, wpa_s, NULL);
 +
 +#ifdef CONFIG_SAE
 +      if (data->auth.auth_type == WLAN_AUTH_SAE) {
 +              int res;
 +              res = sme_sae_auth(wpa_s, data->auth.auth_transaction,
 +                                 data->auth.status_code, data->auth.ies,
 +                                 data->auth.ies_len);
 +              if (res < 0) {
 +                      wpas_connection_failed(wpa_s, wpa_s->pending_bssid);
 +                      wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
 +
 +              }
 +              if (res != 1)
 +                      return;
 +
 +              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_s->pending_bssid);
 +      }
 +#endif /* CONFIG_SAE */
 +
 +      if (data->auth.status_code != WLAN_STATUS_SUCCESS) {
-               union wpa_event_data edata;
-               os_memset(&edata, 0, sizeof(edata));
-               edata.ft_ies.ies = data->auth.ies;
-               edata.ft_ies.ies_len = data->auth.ies_len;
-               os_memcpy(edata.ft_ies.target_ap, data->auth.peer, ETH_ALEN);
-               wpa_supplicant_event(wpa_s, EVENT_FT_RESPONSE, &edata);
++              char *ie_txt = NULL;
++
++              if (data->auth.ies && data->auth.ies_len) {
++                      size_t buflen = 2 * data->auth.ies_len + 1;
++                      ie_txt = os_malloc(buflen);
++                      if (ie_txt) {
++                              wpa_snprintf_hex(ie_txt, buflen, data->auth.ies,
++                                               data->auth.ies_len);
++                      }
++              }
++              wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_AUTH_REJECT MACSTR
++                      " auth_type=%u auth_transaction=%u status_code=%u ie=%s",
++                      MAC2STR(data->auth.peer), data->auth.auth_type,
++                      data->auth.auth_transaction, data->auth.status_code,
++                      ie_txt);
++              os_free(ie_txt);
 +
 +              if (data->auth.status_code !=
 +                  WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG ||
 +                  wpa_s->sme.auth_alg == data->auth.auth_type ||
 +                  wpa_s->current_ssid->auth_alg == WPA_AUTH_ALG_LEAP) {
 +                      wpas_connection_failed(wpa_s, wpa_s->pending_bssid);
 +                      wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
 +                      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;
 +
 +                      wpa_dbg(wpa_s, MSG_DEBUG, "SME: Trying SHARED auth");
 +                      wpa_supplicant_associate(wpa_s, wpa_s->current_bss,
 +                                               wpa_s->current_ssid);
 +                      return;
 +
 +              case WLAN_AUTH_SHARED_KEY:
 +                      wpa_s->current_ssid->auth_alg = WPA_AUTH_ALG_LEAP;
 +
 +                      wpa_dbg(wpa_s, MSG_DEBUG, "SME: Trying LEAP auth");
 +                      wpa_supplicant_associate(wpa_s, wpa_s->current_bss,
 +                                               wpa_s->current_ssid);
 +                      return;
 +
 +              default:
 +                      return;
 +              }
 +      }
 +
 +#ifdef CONFIG_IEEE80211R
 +      if (data->auth.auth_type == WLAN_AUTH_FT) {
++              if (wpa_ft_process_response(wpa_s->wpa, data->auth.ies,
++                                          data->auth.ies_len, 0,
++                                          data->auth.peer, NULL, 0) < 0) {
++                      wpa_dbg(wpa_s, MSG_DEBUG,
++                              "SME: FT Authentication response processing failed");
++                      wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DISCONNECTED "bssid="
++                              MACSTR
++                              " reason=%d locally_generated=1",
++                              MAC2STR(wpa_s->pending_bssid),
++                              WLAN_REASON_DEAUTH_LEAVING);
++                      wpas_connection_failed(wpa_s, wpa_s->pending_bssid);
++                      wpa_supplicant_mark_disassoc(wpa_s);
++                      return;
++              }
 +      }
 +#endif /* CONFIG_IEEE80211R */
 +
 +      sme_associate(wpa_s, ssid->mode, data->auth.peer,
 +                    data->auth.auth_type);
 +}
 +
 +
 +void sme_associate(struct wpa_supplicant *wpa_s, enum wpas_mode mode,
 +                 const u8 *bssid, u16 auth_type)
 +{
 +      struct wpa_driver_associate_params params;
 +      struct ieee802_11_elems elems;
 +#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 */
 +
 +      os_memset(&params, 0, sizeof(params));
 +      params.bssid = bssid;
 +      params.ssid = wpa_s->sme.ssid;
 +      params.ssid_len = wpa_s->sme.ssid_len;
 +      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_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));
 +      params.htcaps = (u8 *) &htcaps;
 +      params.htcaps_mask = (u8 *) &htcaps_mask;
 +      wpa_supplicant_apply_ht_overrides(wpa_s, wpa_s->current_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, wpa_s->current_ssid, &params);
 +#endif /* CONFIG_VHT_OVERRIDES */
 +#ifdef CONFIG_IEEE80211R
 +      if (auth_type == WLAN_AUTH_FT && wpa_s->sme.ft_ies) {
 +              params.wpa_ie = wpa_s->sme.ft_ies;
 +              params.wpa_ie_len = wpa_s->sme.ft_ies_len;
 +      }
 +#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.freq);
 +
 +      wpa_supplicant_set_state(wpa_s, WPA_ASSOCIATING);
 +
 +      if (params.wpa_ie == NULL ||
 +          ieee802_11_parse_elems(params.wpa_ie, params.wpa_ie_len, &elems, 0)
 +          < 0) {
 +              wpa_dbg(wpa_s, MSG_DEBUG, "SME: Could not parse own IEs?!");
 +              os_memset(&elems, 0, sizeof(elems));
 +      }
 +      if (elems.rsn_ie) {
 +              params.wpa_proto = WPA_PROTO_RSN;
 +              wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, elems.rsn_ie - 2,
 +                                      elems.rsn_ie_len + 2);
 +      } else if (elems.wpa_ie) {
 +              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)
 +              params.p2p = 1;
 +
 +      if (wpa_s->parent->set_sta_uapsd)
 +              params.uapsd = wpa_s->parent->sta_uapsd;
 +      else
 +              params.uapsd = -1;
 +
 +      if (wpa_drv_associate(wpa_s, &params) < 0) {
 +              wpa_msg(wpa_s, MSG_INFO, "SME: Association request to the "
 +                      "driver failed");
 +              wpas_connection_failed(wpa_s, wpa_s->pending_bssid);
 +              wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
 +              os_memset(wpa_s->pending_bssid, 0, ETH_ALEN);
 +              return;
 +      }
 +
 +      eloop_register_timeout(SME_ASSOC_TIMEOUT, 0, sme_assoc_timer, wpa_s,
 +                             NULL);
 +}
 +
 +
 +int sme_update_ft_ies(struct wpa_supplicant *wpa_s, const u8 *md,
 +                    const u8 *ies, size_t ies_len)
 +{
 +      if (md == NULL || ies == NULL) {
 +              wpa_dbg(wpa_s, MSG_DEBUG, "SME: Remove mobility domain");
 +              os_free(wpa_s->sme.ft_ies);
 +              wpa_s->sme.ft_ies = NULL;
 +              wpa_s->sme.ft_ies_len = 0;
 +              wpa_s->sme.ft_used = 0;
 +              return 0;
 +      }
 +
 +      os_memcpy(wpa_s->sme.mobility_domain, md, MOBILITY_DOMAIN_ID_LEN);
 +      wpa_hexdump(MSG_DEBUG, "SME: FT IEs", ies, ies_len);
 +      os_free(wpa_s->sme.ft_ies);
 +      wpa_s->sme.ft_ies = os_malloc(ies_len);
 +      if (wpa_s->sme.ft_ies == NULL)
 +              return -1;
 +      os_memcpy(wpa_s->sme.ft_ies, ies, ies_len);
 +      wpa_s->sme.ft_ies_len = ies_len;
 +      return 0;
 +}
 +
 +
 +static void sme_deauth(struct wpa_supplicant *wpa_s)
 +{
 +      int bssid_changed;
 +
 +      bssid_changed = !is_zero_ether_addr(wpa_s->bssid);
 +
 +      if (wpa_drv_deauthenticate(wpa_s, wpa_s->pending_bssid,
 +                                 WLAN_REASON_DEAUTH_LEAVING) < 0) {
 +              wpa_msg(wpa_s, MSG_INFO, "SME: Deauth request to the driver "
 +                      "failed");
 +      }
 +      wpa_s->sme.prev_bssid_set = 0;
 +
 +      wpas_connection_failed(wpa_s, wpa_s->pending_bssid);
 +      wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
 +      os_memset(wpa_s->bssid, 0, ETH_ALEN);
 +      os_memset(wpa_s->pending_bssid, 0, ETH_ALEN);
 +      if (bssid_changed)
 +              wpas_notify_bssid_changed(wpa_s);
 +}
 +
 +
 +void sme_event_assoc_reject(struct wpa_supplicant *wpa_s,
 +                          union wpa_event_data *data)
 +{
 +      wpa_dbg(wpa_s, MSG_DEBUG, "SME: Association with " MACSTR " failed: "
 +              "status code %d", MAC2STR(wpa_s->pending_bssid),
 +              data->assoc_reject.status_code);
 +
 +      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
 +       * if the authentication is left pending.. Some roaming cases might
 +       * benefit from using the previous authentication, so this could be
 +       * optimized in the future.
 +       */
 +      sme_deauth(wpa_s);
 +}
 +
 +
 +void sme_event_auth_timed_out(struct wpa_supplicant *wpa_s,
 +                            union wpa_event_data *data)
 +{
 +      wpa_dbg(wpa_s, MSG_DEBUG, "SME: Authentication timed out");
 +      wpas_connection_failed(wpa_s, wpa_s->pending_bssid);
 +      wpa_supplicant_mark_disassoc(wpa_s);
 +}
 +
 +
 +void sme_event_assoc_timed_out(struct wpa_supplicant *wpa_s,
 +                             union wpa_event_data *data)
 +{
 +      wpa_dbg(wpa_s, MSG_DEBUG, "SME: Association timed out");
 +      wpas_connection_failed(wpa_s, wpa_s->pending_bssid);
 +      wpa_supplicant_mark_disassoc(wpa_s);
 +}
 +
 +
 +void sme_event_disassoc(struct wpa_supplicant *wpa_s,
 +                      struct disassoc_info *info)
 +{
 +      wpa_dbg(wpa_s, MSG_DEBUG, "SME: Disassociation event received");
 +      if (wpa_s->sme.prev_bssid_set) {
 +              /*
 +               * cfg80211/mac80211 can get into somewhat confused state if
 +               * the AP only disassociates us and leaves us in authenticated
 +               * state. For now, force the state to be cleared to avoid
 +               * confusing errors if we try to associate with the AP again.
 +               */
 +              wpa_dbg(wpa_s, MSG_DEBUG, "SME: Deauthenticate to clear "
 +                      "driver state");
 +              wpa_drv_deauthenticate(wpa_s, wpa_s->sme.prev_bssid,
 +                                     WLAN_REASON_DEAUTH_LEAVING);
 +      }
 +}
 +
 +
 +static void sme_auth_timer(void *eloop_ctx, void *timeout_ctx)
 +{
 +      struct wpa_supplicant *wpa_s = eloop_ctx;
 +      if (wpa_s->wpa_state == WPA_AUTHENTICATING) {
 +              wpa_msg(wpa_s, MSG_DEBUG, "SME: Authentication timeout");
 +              sme_deauth(wpa_s);
 +      }
 +}
 +
 +
 +static void sme_assoc_timer(void *eloop_ctx, void *timeout_ctx)
 +{
 +      struct wpa_supplicant *wpa_s = eloop_ctx;
 +      if (wpa_s->wpa_state == WPA_ASSOCIATING) {
 +              wpa_msg(wpa_s, MSG_DEBUG, "SME: Association timeout");
 +              sme_deauth(wpa_s);
 +      }
 +}
 +
 +
 +void sme_state_changed(struct wpa_supplicant *wpa_s)
 +{
 +      /* Make sure timers are cleaned up appropriately. */
 +      if (wpa_s->wpa_state != WPA_ASSOCIATING)
 +              eloop_cancel_timeout(sme_assoc_timer, wpa_s, NULL);
 +      if (wpa_s->wpa_state != WPA_AUTHENTICATING)
 +              eloop_cancel_timeout(sme_auth_timer, wpa_s, NULL);
 +}
 +
 +
 +void sme_disassoc_while_authenticating(struct wpa_supplicant *wpa_s,
 +                                     const u8 *prev_pending_bssid)
 +{
 +      /*
 +       * mac80211-workaround to force deauth on failed auth cmd,
 +       * requires us to remain in authenticating state to allow the
 +       * second authentication attempt to be continued properly.
 +       */
 +      wpa_dbg(wpa_s, MSG_DEBUG, "SME: Allow pending authentication "
 +              "to proceed after disconnection event");
 +      wpa_supplicant_set_state(wpa_s, WPA_AUTHENTICATING);
 +      os_memcpy(wpa_s->pending_bssid, prev_pending_bssid, ETH_ALEN);
 +
 +      /*
 +       * Re-arm authentication timer in case auth fails for whatever reason.
 +       */
 +      eloop_cancel_timeout(sme_auth_timer, wpa_s, NULL);
 +      eloop_register_timeout(SME_AUTH_TIMEOUT, 0, sme_auth_timer, wpa_s,
 +                             NULL);
 +}
 +
 +
 +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);
 +      wpa_s->sme.ft_ies = NULL;
 +      wpa_s->sme.ft_ies_len = 0;
 +#ifdef CONFIG_IEEE80211W
 +      sme_stop_sa_query(wpa_s);
 +#endif /* CONFIG_IEEE80211W */
 +      sme_clear_on_disassoc(wpa_s);
 +
 +      eloop_cancel_timeout(sme_assoc_timer, wpa_s, NULL);
 +      eloop_cancel_timeout(sme_auth_timer, wpa_s, NULL);
 +      eloop_cancel_timeout(sme_obss_scan_timeout, wpa_s, NULL);
 +}
 +
 +
 +static void sme_send_2040_bss_coex(struct wpa_supplicant *wpa_s,
 +                                 const u8 *chan_list, u8 num_channels,
 +                                 u8 num_intol)
 +{
 +      struct ieee80211_2040_bss_coex_ie *bc_ie;
 +      struct ieee80211_2040_intol_chan_report *ic_report;
 +      struct wpabuf *buf;
 +
 +      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) +
 +                         sizeof(struct ieee80211_2040_intol_chan_report) +
 +                         num_channels);
 +      if (buf == NULL)
 +              return;
 +
 +      wpabuf_put_u8(buf, WLAN_ACTION_PUBLIC);
 +      wpabuf_put_u8(buf, WLAN_PA_20_40_BSS_COEX);
 +
 +      bc_ie = wpabuf_put(buf, sizeof(*bc_ie));
 +      bc_ie->element_id = WLAN_EID_20_40_BSS_COEXISTENCE;
 +      bc_ie->length = 1;
 +      if (num_intol)
 +              bc_ie->coex_param |= WLAN_20_40_BSS_COEX_20MHZ_WIDTH_REQ;
 +
 +      if (num_channels > 0) {
 +              ic_report = wpabuf_put(buf, sizeof(*ic_report));
 +              ic_report->element_id = WLAN_EID_20_40_BSS_INTOLERANT;
 +              ic_report->length = num_channels + 1;
 +              ic_report->op_class = 0;
 +              os_memcpy(wpabuf_put(buf, num_channels), chan_list,
 +                        num_channels);
 +      }
 +
 +      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_msg(wpa_s, MSG_INFO,
 +                      "SME: Failed to send 20/40 BSS Coexistence frame");
 +      }
 +
 +      wpabuf_free(buf);
 +}
 +
 +
 +int sme_proc_obss_scan(struct wpa_supplicant *wpa_s)
 +{
 +      struct wpa_bss *bss;
 +      const u8 *ie;
 +      u16 ht_cap;
 +      u8 chan_list[P2P_MAX_CHANNELS], channel;
 +      u8 num_channels = 0, num_intol = 0, i;
 +
 +      if (!wpa_s->sme.sched_obss_scan)
 +              return 0;
 +
 +      wpa_s->sme.sched_obss_scan = 0;
 +      if (!wpa_s->current_bss || wpa_s->wpa_state != WPA_COMPLETED)
 +              return 1;
 +
 +      /*
 +       * Check whether AP uses regulatory triplet or channel triplet in
 +       * country info. Right now the operating class of the BSS channel
 +       * width trigger event is "unknown" (IEEE Std 802.11-2012 10.15.12),
 +       * based on the assumption that operating class triplet is not used in
 +       * beacon frame. If the First Channel Number/Operating Extension
 +       * Identifier octet has a positive integer value of 201 or greater,
 +       * then its operating class triplet.
 +       *
 +       * TODO: If Supported Operating Classes element is present in beacon
 +       * frame, have to lookup operating class in Annex E and fill them in
 +       * 2040 coex frame.
 +       */
 +      ie = wpa_bss_get_ie(wpa_s->current_bss, WLAN_EID_COUNTRY);
 +      if (ie && (ie[1] >= 6) && (ie[5] >= 201))
 +              return 1;
 +
 +      os_memset(chan_list, 0, sizeof(chan_list));
 +
 +      dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
 +              /* Skip other band bss */
 +              enum hostapd_hw_mode mode;
 +              mode = ieee80211_freq_to_chan(bss->freq, &channel);
 +              if (mode != HOSTAPD_MODE_IEEE80211G &&
 +                  mode != HOSTAPD_MODE_IEEE80211B)
 +                      continue;
 +
 +              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])
 +                                      break;
 +                      }
 +                      if (i != num_channels)
 +                              continue;
 +
 +                      chan_list[num_channels++] = channel;
 +              }
 +      }
 +
 +      sme_send_2040_bss_coex(wpa_s, chan_list, num_channels, num_intol);
 +      return 1;
 +}
 +
 +
 +static struct hostapd_hw_modes * get_mode(struct hostapd_hw_modes *modes,
 +                                        u16 num_modes,
 +                                        enum hostapd_hw_mode mode)
 +{
 +      u16 i;
 +
 +      for (i = 0; i < num_modes; i++) {
 +              if (modes[i].mode == mode)
 +                      return &modes[i];
 +      }
 +
 +      return NULL;
 +}
 +
 +
 +static void wpa_obss_scan_freqs_list(struct wpa_supplicant *wpa_s,
 +                                   struct wpa_driver_scan_params *params)
 +{
 +      /* 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,
 +                      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;
 +              freq = mode->channels[i].freq;
 +              if (freq - 10 >= end || freq + 10 <= start)
 +                      continue; /* not affected */
 +              params->freqs[count++] = freq;
 +      }
 +}
 +
 +
 +static void sme_obss_scan_timeout(void *eloop_ctx, void *timeout_ctx)
 +{
 +      struct wpa_supplicant *wpa_s = eloop_ctx;
 +      struct wpa_driver_scan_params params;
 +
 +      if (!wpa_s->current_bss) {
 +              wpa_printf(MSG_DEBUG, "SME OBSS: Ignore scan request");
 +              return;
 +      }
 +
 +      os_memset(&params, 0, sizeof(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))
 +              wpa_printf(MSG_DEBUG, "SME OBSS: Failed to trigger scan");
 +      else
 +              wpa_s->sme.sched_obss_scan = 1;
 +      os_free(params.freqs);
 +
 +      eloop_register_timeout(wpa_s->sme.obss_scan_int, 0,
 +                             sme_obss_scan_timeout, wpa_s, NULL);
 +}
 +
 +
 +void sme_sched_obss_scan(struct wpa_supplicant *wpa_s, int enable)
 +{
 +      const u8 *ie;
 +      struct wpa_bss *bss = wpa_s->current_bss;
 +      struct wpa_ssid *ssid = wpa_s->current_ssid;
 +      struct hostapd_hw_modes *hw_mode = NULL;
 +      int i;
 +
 +      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;
 +
 +      /*
 +       * Schedule OBSS scan if driver is using station SME in wpa_supplicant
 +       * or it expects OBSS scan to be performed by wpa_supplicant.
 +       */
 +      if (!((wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) ||
 +            (wpa_s->drv_flags & WPA_DRIVER_FLAGS_OBSS_SCAN)) ||
 +          ssid == NULL || ssid->mode != IEEE80211_MODE_INFRA)
 +              return;
 +
 +      if (!wpa_s->hw.modes)
 +              return;
 +
 +      /* only HT caps in 11g mode are relevant */
 +      for (i = 0; i < wpa_s->hw.num_modes; i++) {
 +              hw_mode = &wpa_s->hw.modes[i];
 +              if (hw_mode->mode == HOSTAPD_MODE_IEEE80211G)
 +                      break;
 +      }
 +
 +      /* Driver does not support HT40 for 11g or doesn't have 11g. */
 +      if (i == wpa_s->hw.num_modes || !hw_mode ||
 +          !(hw_mode->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET))
 +              return;
 +
 +      if (bss == NULL || bss->freq < 2400 || bss->freq > 2500)
 +              return; /* Not associated on 2.4 GHz band */
 +
 +      /* Check whether AP supports HT40 */
 +      ie = wpa_bss_get_ie(wpa_s->current_bss, WLAN_EID_HT_CAP);
 +      if (!ie || ie[1] < 2 ||
 +          !(WPA_GET_LE16(ie + 2) & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET))
 +              return; /* AP does not support HT40 */
 +
 +      ie = wpa_bss_get_ie(wpa_s->current_bss,
 +                          WLAN_EID_OVERLAPPING_BSS_SCAN_PARAMS);
 +      if (!ie || ie[1] < 14)
 +              return; /* AP does not request OBSS scans */
 +
 +      wpa_s->sme.obss_scan_int = WPA_GET_LE16(ie + 6);
 +      if (wpa_s->sme.obss_scan_int < 10) {
 +              wpa_printf(MSG_DEBUG, "SME: Invalid OBSS Scan Interval %u "
 +                         "replaced with the minimum 10 sec",
 +                         wpa_s->sme.obss_scan_int);
 +              wpa_s->sme.obss_scan_int = 10;
 +      }
 +      wpa_printf(MSG_DEBUG, "SME: OBSS Scan Interval %u sec",
 +                 wpa_s->sme.obss_scan_int);
 +      eloop_register_timeout(wpa_s->sme.obss_scan_int, 0,
 +                             sme_obss_scan_timeout, wpa_s, NULL);
 +}
 +
 +
 +#ifdef CONFIG_IEEE80211W
 +
 +static const unsigned int sa_query_max_timeout = 1000;
 +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_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");
 +              sme_stop_sa_query(wpa_s);
 +              wpa_supplicant_deauthenticate(
 +                      wpa_s, WLAN_REASON_PREV_AUTH_NOT_VALID);
 +              return 1;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +static void sme_send_sa_query_req(struct wpa_supplicant *wpa_s,
 +                                const u8 *trans_id)
 +{
 +      u8 req[2 + WLAN_SA_QUERY_TR_ID_LEN];
 +      wpa_dbg(wpa_s, MSG_DEBUG, "SME: Sending SA Query Request to "
 +              MACSTR, MAC2STR(wpa_s->bssid));
 +      wpa_hexdump(MSG_DEBUG, "SME: SA Query Transaction ID",
 +                  trans_id, WLAN_SA_QUERY_TR_ID_LEN);
 +      req[0] = WLAN_ACTION_SA_QUERY;
 +      req[1] = WLAN_SA_QUERY_REQUEST;
 +      os_memcpy(req + 2, trans_id, WLAN_SA_QUERY_TR_ID_LEN);
 +      if (wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
 +                              wpa_s->own_addr, wpa_s->bssid,
 +                              req, sizeof(req), 0) < 0)
 +              wpa_msg(wpa_s, MSG_INFO, "SME: Failed to send SA Query "
 +                      "Request");
 +}
 +
 +
 +static void sme_sa_query_timer(void *eloop_ctx, void *timeout_ctx)
 +{
 +      struct wpa_supplicant *wpa_s = eloop_ctx;
 +      unsigned int timeout, sec, usec;
 +      u8 *trans_id, *nbuf;
 +
 +      if (wpa_s->sme.sa_query_count > 0 &&
 +          sme_check_sa_query_timeout(wpa_s))
 +              return;
 +
 +      nbuf = os_realloc_array(wpa_s->sme.sa_query_trans_id,
 +                              wpa_s->sme.sa_query_count + 1,
 +                              WLAN_SA_QUERY_TR_ID_LEN);
 +      if (nbuf == NULL)
 +              return;
 +      if (wpa_s->sme.sa_query_count == 0) {
 +              /* Starting a new SA Query procedure */
 +              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++;
 +
 +      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;
 +      usec = (timeout % 1000) * 1024;
 +      eloop_register_timeout(sec, usec, sme_sa_query_timer, wpa_s, NULL);
 +
 +      wpa_dbg(wpa_s, MSG_DEBUG, "SME: Association SA Query attempt %d",
 +              wpa_s->sme.sa_query_count);
 +
 +      sme_send_sa_query_req(wpa_s, trans_id);
 +}
 +
 +
 +static void sme_start_sa_query(struct wpa_supplicant *wpa_s)
 +{
 +      sme_sa_query_timer(wpa_s, NULL);
 +}
 +
 +
 +static void sme_stop_sa_query(struct wpa_supplicant *wpa_s)
 +{
 +      eloop_cancel_timeout(sme_sa_query_timer, wpa_s, NULL);
 +      os_free(wpa_s->sme.sa_query_trans_id);
 +      wpa_s->sme.sa_query_trans_id = NULL;
 +      wpa_s->sme.sa_query_count = 0;
 +}
 +
 +
 +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 (wpas_get_ssid_pmf(wpa_s, ssid) == NO_MGMT_FRAME_PROTECTION)
 +              return;
 +      if (os_memcmp(sa, wpa_s->bssid, ETH_ALEN) != 0)
 +              return;
 +      if (reason_code != WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA &&
 +          reason_code != WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA)
 +              return;
 +      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);
 +}
 +
 +
 +void sme_sa_query_rx(struct wpa_supplicant *wpa_s, const u8 *sa,
 +                   const u8 *data, size_t len)
 +{
 +      int i;
 +
 +      if (wpa_s->sme.sa_query_trans_id == NULL ||
 +          len < 1 + WLAN_SA_QUERY_TR_ID_LEN ||
 +          data[0] != WLAN_SA_QUERY_RESPONSE)
 +              return;
 +      wpa_dbg(wpa_s, MSG_DEBUG, "SME: Received SA Query response from "
 +              MACSTR " (trans_id %02x%02x)", MAC2STR(sa), data[1], data[2]);
 +
 +      if (os_memcmp(sa, wpa_s->bssid, ETH_ALEN) != 0)
 +              return;
 +
 +      for (i = 0; i < wpa_s->sme.sa_query_count; i++) {
 +              if (os_memcmp(wpa_s->sme.sa_query_trans_id +
 +                            i * WLAN_SA_QUERY_TR_ID_LEN,
 +                            data + 1, WLAN_SA_QUERY_TR_ID_LEN) == 0)
 +                      break;
 +      }
 +
 +      if (i >= wpa_s->sme.sa_query_count) {
 +              wpa_dbg(wpa_s, MSG_DEBUG, "SME: No matching SA Query "
 +                      "transaction identifier found");
 +              return;
 +      }
 +
 +      wpa_dbg(wpa_s, MSG_DEBUG, "SME: Reply to pending SA Query received "
 +              "from " MACSTR, MAC2STR(sa));
 +      sme_stop_sa_query(wpa_s);
 +}
 +
 +#endif /* CONFIG_IEEE80211W */
index 5a0af0dc9bba358198187eadb40cfa5316b40e0c,0000000000000000000000000000000000000000..7ddae3d3b6b885663abf52ff542bea3cb067e581
mode 100644,000000..100644
--- /dev/null
@@@ -1,4088 -1,0 +1,4399 @@@
- static const char *wpa_cli_version =
 +/*
 + * WPA Supplicant - command line interface for wpa_supplicant daemon
 + * 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 "includes.h"
 +
 +#ifdef CONFIG_CTRL_IFACE
 +
 +#ifdef CONFIG_CTRL_IFACE_UNIX
 +#include <dirent.h>
 +#endif /* CONFIG_CTRL_IFACE_UNIX */
 +
 +#include "common/wpa_ctrl.h"
 +#include "utils/common.h"
 +#include "utils/eloop.h"
 +#include "utils/edit.h"
 +#include "utils/list.h"
 +#include "common/version.h"
 +#include "common/ieee802_11_defs.h"
 +#ifdef ANDROID
 +#include <cutils/properties.h>
 +#endif /* ANDROID */
 +
 +
- static const char *wpa_cli_license =
++static const char *const wpa_cli_version =
 +"wpa_cli v" VERSION_STR "\n"
 +"Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi> and contributors";
 +
 +
- static const char *wpa_cli_full_license =
++static const char *const wpa_cli_license =
 +"This software may be distributed under the terms of the BSD license.\n"
 +"See README for more details.\n";
 +
-              "        [-P<pid file>] [-g<global ctrl>] [-G<ping interval>]  "
++static const char *const wpa_cli_full_license =
 +"This software may be distributed under the terms of the BSD license.\n"
 +"\n"
 +"Redistribution and use in source and binary forms, with or without\n"
 +"modification, are permitted provided that the following conditions are\n"
 +"met:\n"
 +"\n"
 +"1. Redistributions of source code must retain the above copyright\n"
 +"   notice, this list of conditions and the following disclaimer.\n"
 +"\n"
 +"2. Redistributions in binary form must reproduce the above copyright\n"
 +"   notice, this list of conditions and the following disclaimer in the\n"
 +"   documentation and/or other materials provided with the distribution.\n"
 +"\n"
 +"3. Neither the name(s) of the above-listed copyright holder(s) nor the\n"
 +"   names of its contributors may be used to endorse or promote products\n"
 +"   derived from this software without specific prior written permission.\n"
 +"\n"
 +"THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n"
 +"\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n"
 +"LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n"
 +"A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n"
 +"OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n"
 +"SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n"
 +"LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n"
 +"DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n"
 +"THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n"
 +"(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n"
 +"OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
 +"\n";
 +
 +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 = -1;
 +static int wpa_cli_last_id = 0;
 +#ifndef CONFIG_CTRL_IFACE_DIR
 +#define CONFIG_CTRL_IFACE_DIR "/var/run/wpa_supplicant"
 +#endif /* CONFIG_CTRL_IFACE_DIR */
 +static const char *ctrl_iface_dir = CONFIG_CTRL_IFACE_DIR;
++static const char *client_socket_dir = NULL;
 +static char *ctrl_ifname = NULL;
 +static const char *pid_file = NULL;
 +static const char *action_file = NULL;
 +static int ping_interval = 5;
 +static int interactive = 0;
 +static char *ifname_prefix = NULL;
 +
 +struct cli_txt_entry {
 +      struct dl_list list;
 +      char *txt;
 +};
 +
 +static DEFINE_DL_LIST(bsses); /* struct cli_txt_entry */
 +static DEFINE_DL_LIST(p2p_peers); /* struct cli_txt_entry */
 +static DEFINE_DL_LIST(p2p_groups); /* struct cli_txt_entry */
 +static DEFINE_DL_LIST(ifnames); /* struct cli_txt_entry */
++static DEFINE_DL_LIST(networks); /* struct cli_txt_entry */
 +
 +
 +static void print_help(const char *cmd);
 +static void wpa_cli_mon_receive(int sock, void *eloop_ctx, void *sock_ctx);
 +static void wpa_cli_close_connection(void);
 +static char * wpa_cli_get_default_ifname(void);
 +static char ** wpa_list_cmd_list(void);
++static void update_networks(struct wpa_ctrl *ctrl);
 +
 +
 +static void usage(void)
 +{
 +      printf("wpa_cli [-p<path to ctrl sockets>] [-i<ifname>] [-hvB] "
 +             "[-a<action file>] \\\n"
- static void cli_txt_list_del_word(struct dl_list *txt_list, const char *txt)
++             "        [-P<pid file>] [-g<global ctrl>] [-G<ping interval>] "
++             "\\\n"
++             "        [-s<wpa_client_socket_file_path>] "
 +             "[command..]\n"
 +             "  -h = help (show this usage text)\n"
 +             "  -v = shown version information\n"
 +             "  -a = run in daemon mode executing the action file based on "
 +             "events from\n"
 +             "       wpa_supplicant\n"
 +             "  -B = run a daemon in the background\n"
 +             "  default path: " CONFIG_CTRL_IFACE_DIR "\n"
 +             "  default interface: first interface found in socket path\n");
 +      print_help(NULL);
 +}
 +
 +
 +static void cli_txt_list_free(struct cli_txt_entry *e)
 +{
 +      dl_list_del(&e->list);
 +      os_free(e->txt);
 +      os_free(e);
 +}
 +
 +
 +static void cli_txt_list_flush(struct dl_list *list)
 +{
 +      struct cli_txt_entry *e;
 +      while ((e = dl_list_first(list, struct cli_txt_entry, list)))
 +              cli_txt_list_free(e);
 +}
 +
 +
 +static struct cli_txt_entry * cli_txt_list_get(struct dl_list *txt_list,
 +                                             const char *txt)
 +{
 +      struct cli_txt_entry *e;
 +      dl_list_for_each(e, txt_list, struct cli_txt_entry, list) {
 +              if (os_strcmp(e->txt, txt) == 0)
 +                      return e;
 +      }
 +      return NULL;
 +}
 +
 +
 +static void cli_txt_list_del(struct dl_list *txt_list, const char *txt)
 +{
 +      struct cli_txt_entry *e;
 +      e = cli_txt_list_get(txt_list, txt);
 +      if (e)
 +              cli_txt_list_free(e);
 +}
 +
 +
 +static void cli_txt_list_del_addr(struct dl_list *txt_list, const char *txt)
 +{
 +      u8 addr[ETH_ALEN];
 +      char buf[18];
 +      if (hwaddr_aton(txt, addr) < 0)
 +              return;
 +      os_snprintf(buf, sizeof(buf), MACSTR, MAC2STR(addr));
 +      cli_txt_list_del(txt_list, buf);
 +}
 +
 +
 +#ifdef CONFIG_P2P
-       end = os_strchr(txt, ' ');
++static void cli_txt_list_del_word(struct dl_list *txt_list, const char *txt,
++                                int separator)
 +{
 +      const char *end;
 +      char *buf;
- static int cli_txt_list_add_word(struct dl_list *txt_list, const char *txt)
++      end = os_strchr(txt, separator);
 +      if (end == NULL)
 +              end = txt + os_strlen(txt);
 +      buf = dup_binstr(txt, end - txt);
 +      if (buf == NULL)
 +              return;
 +      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)
 +{
 +      struct cli_txt_entry *e;
 +      e = cli_txt_list_get(txt_list, txt);
 +      if (e)
 +              return 0;
 +      e = os_zalloc(sizeof(*e));
 +      if (e == NULL)
 +              return -1;
 +      e->txt = os_strdup(txt);
 +      if (e->txt == NULL) {
 +              os_free(e);
 +              return -1;
 +      }
 +      dl_list_add(txt_list, &e->list);
 +      return 0;
 +}
 +
 +
 +#ifdef CONFIG_P2P
 +static int cli_txt_list_add_addr(struct dl_list *txt_list, const char *txt)
 +{
 +      u8 addr[ETH_ALEN];
 +      char buf[18];
 +      if (hwaddr_aton(txt, addr) < 0)
 +              return -1;
 +      os_snprintf(buf, sizeof(buf), MACSTR, MAC2STR(addr));
 +      return cli_txt_list_add(txt_list, buf);
 +}
++#endif /* CONFIG_P2P */
 +
 +
-       end = os_strchr(txt, ' ');
++static int cli_txt_list_add_word(struct dl_list *txt_list, const char *txt,
++                               int separator)
 +{
 +      const char *end;
 +      char *buf;
 +      int ret;
- #endif /* CONFIG_P2P */
++      end = os_strchr(txt, separator);
 +      if (end == NULL)
 +              end = txt + os_strlen(txt);
 +      buf = dup_binstr(txt, end - txt);
 +      if (buf == NULL)
 +              return -1;
 +      ret = cli_txt_list_add(txt_list, buf);
 +      os_free(buf);
 +      return ret;
 +}
-       ctrl_conn = wpa_ctrl_open(cfile);
 +
 +
 +static char ** cli_txt_list_array(struct dl_list *txt_list)
 +{
 +      unsigned int i, count = dl_list_len(txt_list);
 +      char **res;
 +      struct cli_txt_entry *e;
 +
 +      res = os_calloc(count + 1, sizeof(char *));
 +      if (res == NULL)
 +              return NULL;
 +
 +      i = 0;
 +      dl_list_for_each(e, txt_list, struct cli_txt_entry, list) {
 +              res[i] = os_strdup(e->txt);
 +              if (res[i] == NULL)
 +                      break;
 +              i++;
 +      }
 +
 +      return res;
 +}
 +
 +
 +static int get_cmd_arg_num(const char *str, int pos)
 +{
 +      int arg = 0, i;
 +
 +      for (i = 0; i <= pos; i++) {
 +              if (str[i] != ' ') {
 +                      arg++;
 +                      while (i <= pos && str[i] != ' ')
 +                              i++;
 +              }
 +      }
 +
 +      if (arg > 0)
 +              arg--;
 +      return arg;
 +}
 +
 +
 +static int str_starts(const char *src, const char *match)
 +{
 +      return os_strncmp(src, match, os_strlen(match)) == 0;
 +}
 +
 +
 +static int wpa_cli_show_event(const char *event)
 +{
 +      const char *start;
 +
 +      start = os_strchr(event, '>');
 +      if (start == NULL)
 +              return 1;
 +
 +      start++;
 +      /*
 +       * Skip BSS added/removed events since they can be relatively frequent
 +       * and are likely of not much use for an interactive user.
 +       */
 +      if (str_starts(start, WPA_EVENT_BSS_ADDED) ||
 +          str_starts(start, WPA_EVENT_BSS_REMOVED))
 +              return 0;
 +
 +      return 1;
 +}
 +
 +
 +static int wpa_cli_open_connection(const char *ifname, int attach)
 +{
 +#if defined(CONFIG_CTRL_IFACE_UDP) || defined(CONFIG_CTRL_IFACE_NAMED_PIPE)
 +      ctrl_conn = wpa_ctrl_open(ifname);
 +      if (ctrl_conn == NULL)
 +              return -1;
 +
 +      if (attach && interactive)
 +              mon_conn = wpa_ctrl_open(ifname);
 +      else
 +              mon_conn = NULL;
 +#else /* CONFIG_CTRL_IFACE_UDP || CONFIG_CTRL_IFACE_NAMED_PIPE */
 +      char *cfile = NULL;
 +      int flen, res;
 +
 +      if (ifname == NULL)
 +              return -1;
 +
 +#ifdef ANDROID
 +      if (access(ctrl_iface_dir, F_OK) < 0) {
 +              cfile = os_strdup(ifname);
 +              if (cfile == NULL)
 +                      return -1;
 +      }
 +#endif /* ANDROID */
 +
++      if (client_socket_dir && client_socket_dir[0] &&
++          access(client_socket_dir, F_OK) < 0) {
++              perror(client_socket_dir);
++              os_free(cfile);
++              return -1;
++      }
++
 +      if (cfile == NULL) {
 +              flen = os_strlen(ctrl_iface_dir) + os_strlen(ifname) + 2;
 +              cfile = os_malloc(flen);
 +              if (cfile == NULL)
 +                      return -1;
 +              res = os_snprintf(cfile, flen, "%s/%s", ctrl_iface_dir,
 +                                ifname);
 +              if (os_snprintf_error(flen, res)) {
 +                      os_free(cfile);
 +                      return -1;
 +              }
 +      }
 +
-               mon_conn = wpa_ctrl_open(cfile);
++      ctrl_conn = wpa_ctrl_open2(cfile, client_socket_dir);
 +      if (ctrl_conn == NULL) {
 +              os_free(cfile);
 +              return -1;
 +      }
 +
 +      if (attach && interactive)
-               "eapol_version", "ap_scan", "disable_scan_offload",
-               "fast_reauth", "opensc_engine_path", "pkcs11_engine_path",
-               "pkcs11_module_path", "openssl_ciphers",
-               "pcsc_reader", "pcsc_pin",
-               "driver_param", "dot11RSNAConfigPMKLifetime",
++              mon_conn = wpa_ctrl_open2(cfile, client_socket_dir);
 +      else
 +              mon_conn = NULL;
 +      os_free(cfile);
 +#endif /* CONFIG_CTRL_IFACE_UDP || CONFIG_CTRL_IFACE_NAMED_PIPE */
 +
 +      if (mon_conn) {
 +              if (wpa_ctrl_attach(mon_conn) == 0) {
 +                      wpa_cli_attached = 1;
 +                      if (interactive)
 +                              eloop_register_read_sock(
 +                                      wpa_ctrl_get_fd(mon_conn),
 +                                      wpa_cli_mon_receive, NULL, NULL);
 +              } else {
 +                      printf("Warning: Failed to attach to "
 +                             "wpa_supplicant.\n");
 +                      wpa_cli_close_connection();
 +                      return -1;
 +              }
 +      }
 +
 +      return 0;
 +}
 +
 +
 +static void wpa_cli_close_connection(void)
 +{
 +      if (ctrl_conn == NULL)
 +              return;
 +
 +      if (wpa_cli_attached) {
 +              wpa_ctrl_detach(interactive ? mon_conn : ctrl_conn);
 +              wpa_cli_attached = 0;
 +      }
 +      wpa_ctrl_close(ctrl_conn);
 +      ctrl_conn = NULL;
 +      if (mon_conn) {
 +              eloop_unregister_read_sock(wpa_ctrl_get_fd(mon_conn));
 +              wpa_ctrl_close(mon_conn);
 +              mon_conn = NULL;
 +      }
 +}
 +
 +
 +static void wpa_cli_msg_cb(char *msg, size_t len)
 +{
 +      printf("%s\n", msg);
 +}
 +
 +
 +static int _wpa_ctrl_command(struct wpa_ctrl *ctrl, char *cmd, int print)
 +{
 +      char buf[4096];
 +      size_t len;
 +      int ret;
 +
 +      if (ctrl_conn == NULL) {
 +              printf("Not connected to wpa_supplicant - command dropped.\n");
 +              return -1;
 +      }
 +      if (ifname_prefix) {
 +              os_snprintf(buf, sizeof(buf), "IFNAME=%s %s",
 +                          ifname_prefix, cmd);
 +              buf[sizeof(buf) - 1] = '\0';
 +              cmd = buf;
 +      }
 +      len = sizeof(buf) - 1;
 +      ret = wpa_ctrl_request(ctrl, cmd, os_strlen(cmd), buf, &len,
 +                             wpa_cli_msg_cb);
 +      if (ret == -2) {
 +              printf("'%s' command timed out.\n", cmd);
 +              return -2;
 +      } else if (ret < 0) {
 +              printf("'%s' command failed.\n", cmd);
 +              return -1;
 +      }
 +      if (print) {
 +              buf[len] = '\0';
 +              printf("%s", buf);
 +              if (interactive && len > 0 && buf[len - 1] != '\n')
 +                      printf("\n");
 +      }
 +      return 0;
 +}
 +
 +
 +static int wpa_ctrl_command(struct wpa_ctrl *ctrl, char *cmd)
 +{
 +      return _wpa_ctrl_command(ctrl, cmd, 1);
 +}
 +
 +
 +static int write_cmd(char *buf, size_t buflen, const char *cmd, int argc,
 +                   char *argv[])
 +{
 +      int i, res;
 +      char *pos, *end;
 +
 +      pos = buf;
 +      end = buf + buflen;
 +
 +      res = os_snprintf(pos, end - pos, "%s", cmd);
 +      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 (os_snprintf_error(end - pos, res))
 +                      goto fail;
 +              pos += res;
 +      }
 +
 +      buf[buflen - 1] = '\0';
 +      return 0;
 +
 +fail:
 +      printf("Too long command\n");
 +      return -1;
 +}
 +
 +
 +static int wpa_cli_cmd(struct wpa_ctrl *ctrl, const char *cmd, int min_args,
 +                     int argc, char *argv[])
 +{
 +      char buf[4096];
 +      if (argc < min_args) {
 +              printf("Invalid %s command - at least %d argument%s "
 +                     "required.\n", cmd, min_args,
 +                     min_args > 1 ? "s are" : " is");
 +              return -1;
 +      }
 +      if (write_cmd(buf, sizeof(buf), cmd, argc, argv) < 0)
 +              return -1;
 +      return wpa_ctrl_command(ctrl, buf);
 +}
 +
 +
 +static int wpa_cli_cmd_ifname(struct wpa_ctrl *ctrl, int argc, char *argv[])
 +{
 +      return wpa_ctrl_command(ctrl, "IFNAME");
 +}
 +
 +
 +static int wpa_cli_cmd_status(struct wpa_ctrl *ctrl, int argc, char *argv[])
 +{
 +      if (argc > 0 && os_strcmp(argv[0], "verbose") == 0)
 +              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");
++#ifdef ANDROID
++      if (argc > 0 && os_strcmp(argv[0], "no_events") == 0)
++              return wpa_ctrl_command(ctrl, "STATUS-NO_EVENTS");
++#endif /* ANDROID */
 +      return wpa_ctrl_command(ctrl, "STATUS");
 +}
 +
 +
 +static int wpa_cli_cmd_ping(struct wpa_ctrl *ctrl, int argc, char *argv[])
 +{
 +      return wpa_ctrl_command(ctrl, "PING");
 +}
 +
 +
 +static int wpa_cli_cmd_relog(struct wpa_ctrl *ctrl, int argc, char *argv[])
 +{
 +      return wpa_ctrl_command(ctrl, "RELOG");
 +}
 +
 +
 +static int wpa_cli_cmd_note(struct wpa_ctrl *ctrl, int argc, char *argv[])
 +{
 +      return wpa_cli_cmd(ctrl, "NOTE", 1, argc, argv);
 +}
 +
 +
 +static int wpa_cli_cmd_mib(struct wpa_ctrl *ctrl, int argc, char *argv[])
 +{
 +      return wpa_ctrl_command(ctrl, "MIB");
 +}
 +
 +
 +static int wpa_cli_cmd_pmksa(struct wpa_ctrl *ctrl, int argc, char *argv[])
 +{
 +      return wpa_ctrl_command(ctrl, "PMKSA");
 +}
 +
 +
 +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);
 +      return 0;
 +}
 +
 +
 +static char ** wpa_cli_complete_help(const char *str, int pos)
 +{
 +      int arg = get_cmd_arg_num(str, pos);
 +      char **res = NULL;
 +
 +      switch (arg) {
 +      case 1:
 +              res = wpa_list_cmd_list();
 +              break;
 +      }
 +
 +      return res;
 +}
 +
 +
 +static int wpa_cli_cmd_license(struct wpa_ctrl *ctrl, int argc, char *argv[])
 +{
 +      printf("%s\n\n%s\n", wpa_cli_version, wpa_cli_full_license);
 +      return 0;
 +}
 +
 +
 +static int wpa_cli_cmd_quit(struct wpa_ctrl *ctrl, int argc, char *argv[])
 +{
 +      wpa_cli_quit = 1;
 +      if (interactive)
 +              eloop_terminate();
 +      return 0;
 +}
 +
 +
 +static int wpa_cli_cmd_set(struct wpa_ctrl *ctrl, int argc, char *argv[])
 +{
 +      char cmd[256];
 +      int res;
 +
 +      if (argc == 1) {
 +              res = os_snprintf(cmd, sizeof(cmd), "SET %s ", argv[0]);
 +              if (os_snprintf_error(sizeof(cmd), res)) {
 +                      printf("Too long SET command.\n");
 +                      return -1;
 +              }
 +              return wpa_ctrl_command(ctrl, cmd);
 +      }
 +
 +      return wpa_cli_cmd(ctrl, "SET", 2, argc, argv);
 +}
 +
 +
 +static char ** wpa_cli_complete_set(const char *str, int pos)
 +{
 +      int arg = get_cmd_arg_num(str, pos);
 +      const char *fields[] = {
 +              /* runtime values */
 +              "EAPOL::heldPeriod", "EAPOL::authPeriod", "EAPOL::startPeriod",
 +              "EAPOL::maxStart", "dot11RSNAConfigPMKLifetime",
 +              "dot11RSNAConfigPMKReauthThreshold", "dot11RSNAConfigSATimeout",
 +              "wps_fragment_size", "wps_version_number", "ampdu",
 +              "tdls_testing", "tdls_disabled", "pno", "radio_disabled",
 +              "uapsd", "ps", "wifi_display", "bssid_filter", "disallow_aps",
 +              "no_keep_alive",
 +              /* global configuration parameters */
-               "update_config", "load_dynamic_eap", "uuid", "device_name",
-               "manufacturer", "model_name", "model_number", "serial_number",
-               "device_type", "os_version", "config_methods",
-               "wps_cred_processing", "wps_vendor_ext_m1", "sec_device_type",
++#ifdef CONFIG_CTRL_IFACE
++              "ctrl_interface", "no_ctrl_interface", "ctrl_interface_group",
++#endif /* CONFIG_CTRL_IFACE */
++              "eapol_version", "ap_scan", "bgscan",
++#ifdef CONFIG_MESH
++              "user_mpm", "max_peer_links", "mesh_max_inactivity",
++              "dot11RSNASAERetransPeriod",
++#endif /* CONFIG_MESH */
++              "disable_scan_offload", "fast_reauth", "opensc_engine_path",
++              "pkcs11_engine_path", "pkcs11_module_path", "openssl_ciphers",
++              "pcsc_reader", "pcsc_pin", "external_sim", "driver_param",
++              "dot11RSNAConfigPMKLifetime",
 +              "dot11RSNAConfigPMKReauthThreshold",
 +              "dot11RSNAConfigSATimeout",
-               "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",
-               "disassoc_low_ack", "hs20", "interworking", "hessid",
-               "access_network_type", "pbc_in_m1", "autoscan",
-               "wps_nfc_dev_pw_id", "wps_nfc_dh_pubkey", "wps_nfc_dh_privkey",
-               "wps_nfc_dev_pw", "ext_password_backend",
++#ifndef CONFIG_NO_CONFIG_WRITE
++              "update_config",
++#endif /* CONFIG_NO_CONFIG_WRITE */
++              "load_dynamic_eap",
++#ifdef CONFIG_WPS
++              "uuid", "device_name", "manufacturer", "model_name",
++              "model_number", "serial_number", "device_type", "os_version",
++              "config_methods", "wps_cred_processing", "wps_vendor_ext_m1",
++#endif /* CONFIG_WPS */
++#ifdef CONFIG_P2P
++              "sec_device_type",
 +              "p2p_listen_reg_class", "p2p_listen_channel",
-               "sae_groups", "dtim_period", "beacon_int", "ap_vendor_elements",
-               "ignore_old_scan_res", "freq_list", "external_sim",
-               "tdls_external_control", "p2p_search_delay"
++              "p2p_oper_reg_class", "p2p_oper_channel", "p2p_go_intent",
++              "p2p_ssid_postfix", "persistent_reconnect", "p2p_intra_bss",
++              "p2p_group_idle", "p2p_passphrase_len", "p2p_pref_chan",
++              "p2p_no_go_freq", "p2p_add_cli_chan",
++              "p2p_optimize_listen_chan", "p2p_go_ht40", "p2p_go_vht",
++              "p2p_disabled", "p2p_go_ctwindow", "p2p_no_group_iface",
++              "p2p_ignore_shared_freq", "ip_addr_go", "ip_addr_mask",
++              "ip_addr_start", "ip_addr_end",
++#endif /* CONFIG_P2P */
++              "country", "bss_max_count", "bss_expiration_age",
++              "bss_expiration_scan_count", "filter_ssids", "filter_rssi",
++              "max_num_sta", "disassoc_low_ack",
++#ifdef CONFIG_HS20
++              "hs20",
++#endif /* CONFIG_HS20 */
++              "interworking", "hessid", "access_network_type", "pbc_in_m1",
++              "autoscan", "wps_nfc_dev_pw_id", "wps_nfc_dh_pubkey",
++              "wps_nfc_dh_privkey", "wps_nfc_dev_pw", "ext_password_backend",
 +              "p2p_go_max_inactivity", "auto_interworking", "okc", "pmf",
-               char ssid_hex[2 * 32 + 1];
++              "sae_groups", "dtim_period", "beacon_int",
++              "ap_vendor_elements", "ignore_old_scan_res", "freq_list",
++              "scan_cur_freq", "sched_scan_interval",
++              "tdls_external_control", "osu_dir", "wowlan_triggers",
++              "p2p_search_delay", "mac_addr", "rand_addr_lifetime",
++              "preassoc_mac_addr", "key_mgmt_offload", "passive_scan",
++              "reassoc_same_bss_optim", "wps_priority"
 +      };
 +      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 > 1 && os_strncasecmp(str, "set bssid_filter ", 17) == 0)
 +              return cli_txt_list_array(&bsses);
 +
 +      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[])
 +{
 +      return wpa_cli_cmd(ctrl, "GET", 1, argc, argv);
 +}
 +
 +
++static char ** wpa_cli_complete_get(const char *str, int pos)
++{
++      int arg = get_cmd_arg_num(str, pos);
++      const char *fields[] = {
++#ifdef CONFIG_CTRL_IFACE
++              "ctrl_interface", "ctrl_interface_group",
++#endif /* CONFIG_CTRL_IFACE */
++              "eapol_version", "ap_scan",
++#ifdef CONFIG_MESH
++              "user_mpm", "max_peer_links", "mesh_max_inactivity",
++#endif /* CONFIG_MESH */
++              "disable_scan_offload", "fast_reauth", "opensc_engine_path",
++              "pkcs11_engine_path", "pkcs11_module_path", "openssl_ciphers",
++              "pcsc_reader", "pcsc_pin", "external_sim", "driver_param",
++              "dot11RSNAConfigPMKLifetime",
++              "dot11RSNAConfigPMKReauthThreshold",
++              "dot11RSNAConfigSATimeout",
++#ifndef CONFIG_NO_CONFIG_WRITE
++              "update_config",
++#endif /* CONFIG_NO_CONFIG_WRITE */
++#ifdef CONFIG_WPS
++              "device_name", "manufacturer", "model_name", "model_number",
++              "serial_number", "config_methods", "wps_cred_processing",
++#endif /* CONFIG_WPS */
++#ifdef CONFIG_P2P
++              "p2p_listen_reg_class", "p2p_listen_channel",
++              "p2p_oper_reg_class", "p2p_oper_channel", "p2p_go_intent",
++              "p2p_ssid_postfix", "persistent_reconnect", "p2p_intra_bss",
++              "p2p_group_idle", "p2p_passphrase_len", "p2p_add_cli_chan",
++              "p2p_optimize_listen_chan", "p2p_go_ht40", "p2p_go_vht",
++              "p2p_disabled", "p2p_go_ctwindow", "p2p_no_group_iface",
++              "p2p_ignore_shared_freq", "ip_addr_go", "ip_addr_mask",
++              "ip_addr_start", "ip_addr_end",
++#endif /* CONFIG_P2P */
++              "bss_max_count", "bss_expiration_age",
++              "bss_expiration_scan_count", "filter_ssids", "filter_rssi",
++              "max_num_sta", "disassoc_low_ack",
++#ifdef CONFIG_HS20
++              "hs20",
++#endif /* CONFIG_HS20 */
++              "interworking", "access_network_type", "pbc_in_m1", "autoscan",
++              "wps_nfc_dev_pw_id", "ext_password_backend",
++              "p2p_go_max_inactivity", "auto_interworking", "okc", "pmf",
++              "dtim_period", "beacon_int", "ignore_old_scan_res",
++              "scan_cur_freq", "sched_scan_interval",
++              "tdls_external_control", "osu_dir", "wowlan_triggers",
++              "p2p_search_delay", "mac_addr", "rand_addr_lifetime",
++              "preassoc_mac_addr", "key_mgmt_offload", "passive_scan",
++              "reassoc_same_bss_optim"
++      };
++      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;
++      }
++
++      return NULL;
++}
++
++
 +static int wpa_cli_cmd_logoff(struct wpa_ctrl *ctrl, int argc, char *argv[])
 +{
 +      return wpa_ctrl_command(ctrl, "LOGOFF");
 +}
 +
 +
 +static int wpa_cli_cmd_logon(struct wpa_ctrl *ctrl, int argc, char *argv[])
 +{
 +      return wpa_ctrl_command(ctrl, "LOGON");
 +}
 +
 +
 +static int wpa_cli_cmd_reassociate(struct wpa_ctrl *ctrl, int argc,
 +                                 char *argv[])
 +{
 +      return wpa_ctrl_command(ctrl, "REASSOCIATE");
 +}
 +
 +
 +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[])
 +{
 +      return wpa_cli_cmd(ctrl, "PREAUTH", 1, argc, argv);
 +}
 +
 +
 +static int wpa_cli_cmd_ap_scan(struct wpa_ctrl *ctrl, int argc, char *argv[])
 +{
 +      return wpa_cli_cmd(ctrl, "AP_SCAN", 1, argc, argv);
 +}
 +
 +
 +static int wpa_cli_cmd_scan_interval(struct wpa_ctrl *ctrl, int argc,
 +                                   char *argv[])
 +{
 +      return wpa_cli_cmd(ctrl, "SCAN_INTERVAL", 1, argc, argv);
 +}
 +
 +
 +static int wpa_cli_cmd_bss_expire_age(struct wpa_ctrl *ctrl, int argc,
 +                                    char *argv[])
 +{
 +      return wpa_cli_cmd(ctrl, "BSS_EXPIRE_AGE", 1, argc, argv);
 +}
 +
 +
 +static int wpa_cli_cmd_bss_expire_count(struct wpa_ctrl *ctrl, int argc,
 +                                      char *argv[])
 +{
 +      return wpa_cli_cmd(ctrl, "BSS_EXPIRE_COUNT", 1, argc, argv);
 +}
 +
 +
 +static int wpa_cli_cmd_bss_flush(struct wpa_ctrl *ctrl, int argc, char *argv[])
 +{
 +      char cmd[256];
 +      int res;
 +
 +      if (argc < 1)
 +              res = os_snprintf(cmd, sizeof(cmd), "BSS_FLUSH 0");
 +      else
 +              res = os_snprintf(cmd, sizeof(cmd), "BSS_FLUSH %s", argv[0]);
 +      if (os_snprintf_error(sizeof(cmd), res)) {
 +              printf("Too long BSS_FLUSH command.\n");
 +              return -1;
 +      }
 +      return wpa_ctrl_command(ctrl, cmd);
 +}
 +
 +
 +static int wpa_cli_cmd_stkstart(struct wpa_ctrl *ctrl, int argc,
 +                              char *argv[])
 +{
 +      return wpa_cli_cmd(ctrl, "STKSTART", 1, argc, argv);
 +}
 +
 +
 +static int wpa_cli_cmd_ft_ds(struct wpa_ctrl *ctrl, int argc, char *argv[])
 +{
 +      return wpa_cli_cmd(ctrl, "FT_DS", 1, argc, argv);
 +}
 +
 +
 +static int wpa_cli_cmd_wps_pbc(struct wpa_ctrl *ctrl, int argc, char *argv[])
 +{
 +      return wpa_cli_cmd(ctrl, "WPS_PBC", 0, argc, argv);
 +}
 +
 +
 +static int wpa_cli_cmd_wps_pin(struct wpa_ctrl *ctrl, int argc, char *argv[])
 +{
 +      if (argc == 0) {
 +              printf("Invalid WPS_PIN command: need one or two arguments:\n"
 +                     "- BSSID: use 'any' to select any\n"
 +                     "- PIN: optional, used only with devices that have no "
 +                     "display\n");
 +              return -1;
 +      }
 +
 +      return wpa_cli_cmd(ctrl, "WPS_PIN", 1, argc, argv);
 +}
 +
 +
 +static int wpa_cli_cmd_wps_check_pin(struct wpa_ctrl *ctrl, int argc,
 +                                   char *argv[])
 +{
 +      return wpa_cli_cmd(ctrl, "WPS_CHECK_PIN", 1, argc, argv);
 +}
 +
 +
 +static int wpa_cli_cmd_wps_cancel(struct wpa_ctrl *ctrl, int argc,
 +                                char *argv[])
 +{
 +      return wpa_ctrl_command(ctrl, "WPS_CANCEL");
 +}
 +
 +
 +#ifdef CONFIG_WPS_NFC
 +
 +static int wpa_cli_cmd_wps_nfc(struct wpa_ctrl *ctrl, int argc, char *argv[])
 +{
 +      return wpa_cli_cmd(ctrl, "WPS_NFC", 0, argc, argv);
 +}
 +
 +
 +static int wpa_cli_cmd_wps_nfc_config_token(struct wpa_ctrl *ctrl, int argc,
 +                                          char *argv[])
 +{
 +      return wpa_cli_cmd(ctrl, "WPS_NFC_CONFIG_TOKEN", 1, argc, argv);
 +}
 +
 +
 +static int wpa_cli_cmd_wps_nfc_token(struct wpa_ctrl *ctrl, int argc,
 +                                   char *argv[])
 +{
 +      return wpa_cli_cmd(ctrl, "WPS_NFC_TOKEN", 1, argc, argv);
 +}
 +
 +
 +static int wpa_cli_cmd_wps_nfc_tag_read(struct wpa_ctrl *ctrl, int argc,
 +                                      char *argv[])
 +{
 +      int ret;
 +      char *buf;
 +      size_t buflen;
 +
 +      if (argc != 1) {
 +              printf("Invalid 'wps_nfc_tag_read' command - one argument "
 +                     "is required.\n");
 +              return -1;
 +      }
 +
 +      buflen = 18 + os_strlen(argv[0]);
 +      buf = os_malloc(buflen);
 +      if (buf == NULL)
 +              return -1;
 +      os_snprintf(buf, buflen, "WPS_NFC_TAG_READ %s", argv[0]);
 +
 +      ret = wpa_ctrl_command(ctrl, buf);
 +      os_free(buf);
 +
 +      return ret;
 +}
 +
 +
 +static int wpa_cli_cmd_nfc_get_handover_req(struct wpa_ctrl *ctrl, int argc,
 +                                          char *argv[])
 +{
 +      return wpa_cli_cmd(ctrl, "NFC_GET_HANDOVER_REQ", 2, argc, argv);
 +}
 +
 +
 +static int wpa_cli_cmd_nfc_get_handover_sel(struct wpa_ctrl *ctrl, int argc,
 +                                          char *argv[])
 +{
 +      return wpa_cli_cmd(ctrl, "NFC_GET_HANDOVER_SEL", 2, argc, argv);
 +}
 +
 +
 +static int wpa_cli_cmd_nfc_report_handover(struct wpa_ctrl *ctrl, int argc,
 +                                         char *argv[])
 +{
 +      return wpa_cli_cmd(ctrl, "NFC_REPORT_HANDOVER", 4, argc, argv);
 +}
 +
 +#endif /* CONFIG_WPS_NFC */
 +
 +
 +static int wpa_cli_cmd_wps_reg(struct wpa_ctrl *ctrl, int argc, char *argv[])
 +{
 +      char cmd[256];
 +      int res;
 +
 +      if (argc == 2)
 +              res = os_snprintf(cmd, sizeof(cmd), "WPS_REG %s %s",
 +                                argv[0], argv[1]);
 +      else if (argc == 5 || argc == 6) {
-               for (i = 0; i < 32; i++) {
++              char ssid_hex[2 * SSID_MAX_LEN + 1];
 +              char key_hex[2 * 64 + 1];
 +              int i;
 +
 +              ssid_hex[0] = '\0';
-               char ssid_hex[2 * 32 + 1];
++              for (i = 0; i < SSID_MAX_LEN; i++) {
 +                      if (argv[2][i] == '\0')
 +                              break;
 +                      os_snprintf(&ssid_hex[i * 2], 3, "%02x", argv[2][i]);
 +              }
 +
 +              key_hex[0] = '\0';
 +              if (argc == 6) {
 +                      for (i = 0; i < 64; i++) {
 +                              if (argv[5][i] == '\0')
 +                                      break;
 +                              os_snprintf(&key_hex[i * 2], 3, "%02x",
 +                                          argv[5][i]);
 +                      }
 +              }
 +
 +              res = os_snprintf(cmd, sizeof(cmd),
 +                                "WPS_REG %s %s %s %s %s %s",
 +                                argv[0], argv[1], ssid_hex, argv[3], argv[4],
 +                                key_hex);
 +      } else {
 +              printf("Invalid WPS_REG command: need two arguments:\n"
 +                     "- BSSID of the target AP\n"
 +                     "- AP PIN\n");
 +              printf("Alternatively, six arguments can be used to "
 +                     "reconfigure the AP:\n"
 +                     "- BSSID of the target AP\n"
 +                     "- AP PIN\n"
 +                     "- new SSID\n"
 +                     "- new auth (OPEN, WPAPSK, WPA2PSK)\n"
 +                     "- new encr (NONE, WEP, TKIP, CCMP)\n"
 +                     "- new key\n");
 +              return -1;
 +      }
 +
 +      if (os_snprintf_error(sizeof(cmd), res)) {
 +              printf("Too long WPS_REG command.\n");
 +              return -1;
 +      }
 +      return wpa_ctrl_command(ctrl, cmd);
 +}
 +
 +
 +static int wpa_cli_cmd_wps_ap_pin(struct wpa_ctrl *ctrl, int argc,
 +                                char *argv[])
 +{
 +      return wpa_cli_cmd(ctrl, "WPS_AP_PIN", 1, argc, argv);
 +}
 +
 +
 +static int wpa_cli_cmd_wps_er_start(struct wpa_ctrl *ctrl, int argc,
 +                                  char *argv[])
 +{
 +      return wpa_cli_cmd(ctrl, "WPS_ER_START", 0, argc, argv);
 +}
 +
 +
 +static int wpa_cli_cmd_wps_er_stop(struct wpa_ctrl *ctrl, int argc,
 +                                 char *argv[])
 +{
 +      return wpa_ctrl_command(ctrl, "WPS_ER_STOP");
 +
 +}
 +
 +
 +static int wpa_cli_cmd_wps_er_pin(struct wpa_ctrl *ctrl, int argc,
 +                                char *argv[])
 +{
 +      if (argc < 2) {
 +              printf("Invalid WPS_ER_PIN command: need at least two "
 +                     "arguments:\n"
 +                     "- UUID: use 'any' to select any\n"
 +                     "- PIN: Enrollee PIN\n"
 +                     "optional: - Enrollee MAC address\n");
 +              return -1;
 +      }
 +
 +      return wpa_cli_cmd(ctrl, "WPS_ER_PIN", 2, argc, argv);
 +}
 +
 +
 +static int wpa_cli_cmd_wps_er_pbc(struct wpa_ctrl *ctrl, int argc,
 +                                char *argv[])
 +{
 +      return wpa_cli_cmd(ctrl, "WPS_ER_PBC", 1, argc, argv);
 +}
 +
 +
 +static int wpa_cli_cmd_wps_er_learn(struct wpa_ctrl *ctrl, int argc,
 +                                  char *argv[])
 +{
 +      if (argc != 2) {
 +              printf("Invalid WPS_ER_LEARN command: need two arguments:\n"
 +                     "- UUID: specify which AP to use\n"
 +                     "- PIN: AP PIN\n");
 +              return -1;
 +      }
 +
 +      return wpa_cli_cmd(ctrl, "WPS_ER_LEARN", 2, argc, argv);
 +}
 +
 +
 +static int wpa_cli_cmd_wps_er_set_config(struct wpa_ctrl *ctrl, int argc,
 +                                       char *argv[])
 +{
 +      if (argc != 2) {
 +              printf("Invalid WPS_ER_SET_CONFIG command: need two "
 +                     "arguments:\n"
 +                     "- UUID: specify which AP to use\n"
 +                     "- Network configuration id\n");
 +              return -1;
 +      }
 +
 +      return wpa_cli_cmd(ctrl, "WPS_ER_SET_CONFIG", 2, argc, argv);
 +}
 +
 +
 +static int wpa_cli_cmd_wps_er_config(struct wpa_ctrl *ctrl, int argc,
 +                                   char *argv[])
 +{
 +      char cmd[256];
 +      int res;
 +
 +      if (argc == 5 || argc == 6) {
-               for (i = 0; i < 32; i++) {
++              char ssid_hex[2 * SSID_MAX_LEN + 1];
 +              char key_hex[2 * 64 + 1];
 +              int i;
 +
 +              ssid_hex[0] = '\0';
-       return wpa_ctrl_command(ctrl, "ADD_NETWORK");
++              for (i = 0; i < SSID_MAX_LEN; i++) {
 +                      if (argv[2][i] == '\0')
 +                              break;
 +                      os_snprintf(&ssid_hex[i * 2], 3, "%02x", argv[2][i]);
 +              }
 +
 +              key_hex[0] = '\0';
 +              if (argc == 6) {
 +                      for (i = 0; i < 64; i++) {
 +                              if (argv[5][i] == '\0')
 +                                      break;
 +                              os_snprintf(&key_hex[i * 2], 3, "%02x",
 +                                          argv[5][i]);
 +                      }
 +              }
 +
 +              res = os_snprintf(cmd, sizeof(cmd),
 +                                "WPS_ER_CONFIG %s %s %s %s %s %s",
 +                                argv[0], argv[1], ssid_hex, argv[3], argv[4],
 +                                key_hex);
 +      } else {
 +              printf("Invalid WPS_ER_CONFIG command: need six arguments:\n"
 +                     "- AP UUID\n"
 +                     "- AP PIN\n"
 +                     "- new SSID\n"
 +                     "- new auth (OPEN, WPAPSK, WPA2PSK)\n"
 +                     "- new encr (NONE, WEP, TKIP, CCMP)\n"
 +                     "- new key\n");
 +              return -1;
 +      }
 +
 +      if (os_snprintf_error(sizeof(cmd), res)) {
 +              printf("Too long WPS_ER_CONFIG command.\n");
 +              return -1;
 +      }
 +      return wpa_ctrl_command(ctrl, cmd);
 +}
 +
 +
 +#ifdef CONFIG_WPS_NFC
 +static int wpa_cli_cmd_wps_er_nfc_config_token(struct wpa_ctrl *ctrl, int argc,
 +                                             char *argv[])
 +{
 +      if (argc != 2) {
 +              printf("Invalid WPS_ER_NFC_CONFIG_TOKEN command: need two "
 +                     "arguments:\n"
 +                     "- WPS/NDEF: token format\n"
 +                     "- UUID: specify which AP to use\n");
 +              return -1;
 +      }
 +
 +      return wpa_cli_cmd(ctrl, "WPS_ER_NFC_CONFIG_TOKEN", 2, argc, argv);
 +}
 +#endif /* CONFIG_WPS_NFC */
 +
 +
 +static int wpa_cli_cmd_ibss_rsn(struct wpa_ctrl *ctrl, int argc, char *argv[])
 +{
 +      return wpa_cli_cmd(ctrl, "IBSS_RSN", 1, argc, argv);
 +}
 +
 +
 +static int wpa_cli_cmd_level(struct wpa_ctrl *ctrl, int argc, char *argv[])
 +{
 +      return wpa_cli_cmd(ctrl, "LEVEL", 1, argc, argv);
 +}
 +
 +
 +static int wpa_cli_cmd_identity(struct wpa_ctrl *ctrl, int argc, char *argv[])
 +{
 +      char cmd[256], *pos, *end;
 +      int i, ret;
 +
 +      if (argc < 2) {
 +              printf("Invalid IDENTITY command: needs two arguments "
 +                     "(network id and identity)\n");
 +              return -1;
 +      }
 +
 +      end = cmd + sizeof(cmd);
 +      pos = cmd;
 +      ret = os_snprintf(pos, end - pos, WPA_CTRL_RSP "IDENTITY-%s:%s",
 +                        argv[0], argv[1]);
 +      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 (os_snprintf_error(end - pos, ret)) {
 +                      printf("Too long IDENTITY command.\n");
 +                      return -1;
 +              }
 +              pos += ret;
 +      }
 +
 +      return wpa_ctrl_command(ctrl, cmd);
 +}
 +
 +
 +static int wpa_cli_cmd_password(struct wpa_ctrl *ctrl, int argc, char *argv[])
 +{
 +      char cmd[256], *pos, *end;
 +      int i, ret;
 +
 +      if (argc < 2) {
 +              printf("Invalid PASSWORD command: needs two arguments "
 +                     "(network id and password)\n");
 +              return -1;
 +      }
 +
 +      end = cmd + sizeof(cmd);
 +      pos = cmd;
 +      ret = os_snprintf(pos, end - pos, WPA_CTRL_RSP "PASSWORD-%s:%s",
 +                        argv[0], argv[1]);
 +      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 (os_snprintf_error(end - pos, ret)) {
 +                      printf("Too long PASSWORD command.\n");
 +                      return -1;
 +              }
 +              pos += ret;
 +      }
 +
 +      return wpa_ctrl_command(ctrl, cmd);
 +}
 +
 +
 +static int wpa_cli_cmd_new_password(struct wpa_ctrl *ctrl, int argc,
 +                                  char *argv[])
 +{
 +      char cmd[256], *pos, *end;
 +      int i, ret;
 +
 +      if (argc < 2) {
 +              printf("Invalid NEW_PASSWORD command: needs two arguments "
 +                     "(network id and password)\n");
 +              return -1;
 +      }
 +
 +      end = cmd + sizeof(cmd);
 +      pos = cmd;
 +      ret = os_snprintf(pos, end - pos, WPA_CTRL_RSP "NEW_PASSWORD-%s:%s",
 +                        argv[0], argv[1]);
 +      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 (os_snprintf_error(end - pos, ret)) {
 +                      printf("Too long NEW_PASSWORD command.\n");
 +                      return -1;
 +              }
 +              pos += ret;
 +      }
 +
 +      return wpa_ctrl_command(ctrl, cmd);
 +}
 +
 +
 +static int wpa_cli_cmd_pin(struct wpa_ctrl *ctrl, int argc, char *argv[])
 +{
 +      char cmd[256], *pos, *end;
 +      int i, ret;
 +
 +      if (argc < 2) {
 +              printf("Invalid PIN command: needs two arguments "
 +                     "(network id and pin)\n");
 +              return -1;
 +      }
 +
 +      end = cmd + sizeof(cmd);
 +      pos = cmd;
 +      ret = os_snprintf(pos, end - pos, WPA_CTRL_RSP "PIN-%s:%s",
 +                        argv[0], argv[1]);
 +      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 (os_snprintf_error(end - pos, ret)) {
 +                      printf("Too long PIN command.\n");
 +                      return -1;
 +              }
 +              pos += ret;
 +      }
 +      return wpa_ctrl_command(ctrl, cmd);
 +}
 +
 +
 +static int wpa_cli_cmd_otp(struct wpa_ctrl *ctrl, int argc, char *argv[])
 +{
 +      char cmd[256], *pos, *end;
 +      int i, ret;
 +
 +      if (argc < 2) {
 +              printf("Invalid OTP command: needs two arguments (network "
 +                     "id and password)\n");
 +              return -1;
 +      }
 +
 +      end = cmd + sizeof(cmd);
 +      pos = cmd;
 +      ret = os_snprintf(pos, end - pos, WPA_CTRL_RSP "OTP-%s:%s",
 +                        argv[0], argv[1]);
 +      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 (os_snprintf_error(end - pos, ret)) {
 +                      printf("Too long OTP command.\n");
 +                      return -1;
 +              }
 +              pos += ret;
 +      }
 +
 +      return wpa_ctrl_command(ctrl, cmd);
 +}
 +
 +
 +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[])
 +{
 +      char cmd[256], *pos, *end;
 +      int i, ret;
 +
 +      if (argc < 2) {
 +              printf("Invalid PASSPHRASE command: needs two arguments "
 +                     "(network id and passphrase)\n");
 +              return -1;
 +      }
 +
 +      end = cmd + sizeof(cmd);
 +      pos = cmd;
 +      ret = os_snprintf(pos, end - pos, WPA_CTRL_RSP "PASSPHRASE-%s:%s",
 +                        argv[0], argv[1]);
 +      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 (os_snprintf_error(end - pos, ret)) {
 +                      printf("Too long PASSPHRASE command.\n");
 +                      return -1;
 +              }
 +              pos += ret;
 +      }
 +
 +      return wpa_ctrl_command(ctrl, cmd);
 +}
 +
 +
 +static int wpa_cli_cmd_bssid(struct wpa_ctrl *ctrl, int argc, char *argv[])
 +{
 +      if (argc < 2) {
 +              printf("Invalid BSSID command: needs two arguments (network "
 +                     "id and BSSID)\n");
 +              return -1;
 +      }
 +
 +      return wpa_cli_cmd(ctrl, "BSSID", 2, argc, argv);
 +}
 +
 +
 +static int wpa_cli_cmd_blacklist(struct wpa_ctrl *ctrl, int argc, char *argv[])
 +{
 +      return wpa_cli_cmd(ctrl, "BLACKLIST", 0, argc, argv);
 +}
 +
 +
 +static int wpa_cli_cmd_log_level(struct wpa_ctrl *ctrl, int argc, char *argv[])
 +{
 +      return wpa_cli_cmd(ctrl, "LOG_LEVEL", 0, argc, argv);
 +}
 +
 +
 +static int wpa_cli_cmd_list_networks(struct wpa_ctrl *ctrl, int argc,
 +                                   char *argv[])
 +{
 +      return wpa_ctrl_command(ctrl, "LIST_NETWORKS");
 +}
 +
 +
 +static int wpa_cli_cmd_select_network(struct wpa_ctrl *ctrl, int argc,
 +                                    char *argv[])
 +{
 +      return wpa_cli_cmd(ctrl, "SELECT_NETWORK", 1, argc, argv);
 +}
 +
 +
 +static int wpa_cli_cmd_enable_network(struct wpa_ctrl *ctrl, int argc,
 +                                    char *argv[])
 +{
 +      return wpa_cli_cmd(ctrl, "ENABLE_NETWORK", 1, argc, argv);
 +}
 +
 +
 +static int wpa_cli_cmd_disable_network(struct wpa_ctrl *ctrl, int argc,
 +                                     char *argv[])
 +{
 +      return wpa_cli_cmd(ctrl, "DISABLE_NETWORK", 1, argc, argv);
 +}
 +
 +
 +static int wpa_cli_cmd_add_network(struct wpa_ctrl *ctrl, int argc,
 +                                 char *argv[])
 +{
-       return wpa_cli_cmd(ctrl, "REMOVE_NETWORK", 1, argc, argv);
++      int res = wpa_ctrl_command(ctrl, "ADD_NETWORK");
++      if (interactive)
++              update_networks(ctrl);
++      return res;
 +}
 +
 +
 +static int wpa_cli_cmd_remove_network(struct wpa_ctrl *ctrl, int argc,
 +                                    char *argv[])
 +{
-                      "driver_param bridge_name\n");
++      int res = wpa_cli_cmd(ctrl, "REMOVE_NETWORK", 1, argc, argv);
++      if (interactive)
++              update_networks(ctrl);
++      return res;
 +}
 +
 +
 +static void wpa_cli_show_network_variables(void)
 +{
 +      printf("set_network variables:\n"
 +             "  ssid (network name, SSID)\n"
 +             "  psk (WPA passphrase or pre-shared key)\n"
 +             "  key_mgmt (key management protocol)\n"
 +             "  identity (EAP identity)\n"
 +             "  password (EAP password)\n"
 +             "  ...\n"
 +             "\n"
 +             "Note: Values are entered in the same format as the "
 +             "configuration file is using,\n"
 +             "i.e., strings values need to be inside double quotation "
 +             "marks.\n"
 +             "For example: set_network 1 ssid \"network name\"\n"
 +             "\n"
 +             "Please see wpa_supplicant.conf documentation for full list "
 +             "of\navailable variables.\n");
 +}
 +
 +
 +static int wpa_cli_cmd_set_network(struct wpa_ctrl *ctrl, int argc,
 +                                 char *argv[])
 +{
 +      if (argc == 0) {
 +              wpa_cli_show_network_variables();
 +              return 0;
 +      }
 +
 +      if (argc < 3) {
 +              printf("Invalid SET_NETWORK command: needs three arguments\n"
 +                     "(network id, variable name, and value)\n");
 +              return -1;
 +      }
 +
 +      return wpa_cli_cmd(ctrl, "SET_NETWORK", 3, argc, argv);
 +}
 +
 +
 +static int wpa_cli_cmd_get_network(struct wpa_ctrl *ctrl, int argc,
 +                                 char *argv[])
 +{
 +      if (argc == 0) {
 +              wpa_cli_show_network_variables();
 +              return 0;
 +      }
 +
 +      if (argc != 2) {
 +              printf("Invalid GET_NETWORK command: needs two arguments\n"
 +                     "(network id and variable name)\n");
 +              return -1;
 +      }
 +
 +      return wpa_cli_cmd(ctrl, "GET_NETWORK", 2, argc, argv);
 +}
 +
 +
++static const char *network_fields[] = {
++      "ssid", "scan_ssid", "bssid", "bssid_blacklist",
++      "bssid_whitelist", "psk", "proto", "key_mgmt",
++      "bg_scan_period", "pairwise", "group", "auth_alg", "scan_freq",
++      "freq_list",
++#ifdef IEEE8021X_EAPOL
++      "eap", "identity", "anonymous_identity", "password", "ca_cert",
++      "ca_path", "client_cert", "private_key", "private_key_passwd",
++      "dh_file", "subject_match", "altsubject_match",
++      "domain_suffix_match", "domain_match", "ca_cert2", "ca_path2",
++      "client_cert2", "private_key2", "private_key2_passwd",
++      "dh_file2", "subject_match2", "altsubject_match2",
++      "domain_suffix_match2", "domain_match2", "phase1", "phase2",
++      "pcsc", "pin", "engine_id", "key_id", "cert_id", "ca_cert_id",
++      "pin2", "engine2_id", "key2_id", "cert2_id", "ca_cert2_id",
++      "engine", "engine2", "eapol_flags", "sim_num",
++      "openssl_ciphers", "erp",
++#endif /* IEEE8021X_EAPOL */
++      "wep_key0", "wep_key1", "wep_key2", "wep_key3",
++      "wep_tx_keyidx", "priority",
++#ifdef IEEE8021X_EAPOL
++      "eap_workaround", "pac_file", "fragment_size", "ocsp",
++#endif /* IEEE8021X_EAPOL */
++#ifdef CONFIG_MESH
++      "mode", "no_auto_peer",
++#else /* CONFIG_MESH */
++      "mode",
++#endif /* CONFIG_MESH */
++      "proactive_key_caching", "disabled", "id_str",
++#ifdef CONFIG_IEEE80211W
++      "ieee80211w",
++#endif /* CONFIG_IEEE80211W */
++      "peerkey", "mixed_cell", "frequency", "fixed_freq",
++#ifdef CONFIG_MESH
++      "mesh_basic_rates", "dot11MeshMaxRetries",
++      "dot11MeshRetryTimeout", "dot11MeshConfirmTimeout",
++      "dot11MeshHoldingTimeout",
++#endif /* CONFIG_MESH */
++      "wpa_ptk_rekey", "bgscan", "ignore_broadcast_ssid",
++#ifdef CONFIG_P2P
++      "go_p2p_dev_addr", "p2p_client_list", "psk_list",
++#endif /* CONFIG_P2P */
++#ifdef CONFIG_HT_OVERRIDES
++      "disable_ht", "disable_ht40", "disable_sgi", "disable_ldpc",
++      "ht40_intolerant", "disable_max_amsdu", "ampdu_factor",
++      "ampdu_density", "ht_mcs",
++#endif /* CONFIG_HT_OVERRIDES */
++#ifdef CONFIG_VHT_OVERRIDES
++      "disable_vht", "vht_capa", "vht_capa_mask", "vht_rx_mcs_nss_1",
++      "vht_rx_mcs_nss_2", "vht_rx_mcs_nss_3", "vht_rx_mcs_nss_4",
++      "vht_rx_mcs_nss_5", "vht_rx_mcs_nss_6", "vht_rx_mcs_nss_7",
++      "vht_rx_mcs_nss_8", "vht_tx_mcs_nss_1", "vht_tx_mcs_nss_2",
++      "vht_tx_mcs_nss_3", "vht_tx_mcs_nss_4", "vht_tx_mcs_nss_5",
++      "vht_tx_mcs_nss_6", "vht_tx_mcs_nss_7", "vht_tx_mcs_nss_8",
++#endif /* CONFIG_VHT_OVERRIDES */
++      "ap_max_inactivity", "dtim_period", "beacon_int",
++#ifdef CONFIG_MACSEC
++      "macsec_policy",
++#endif /* CONFIG_MACSEC */
++#ifdef CONFIG_HS20
++      "update_identifier",
++#endif /* CONFIG_HS20 */
++      "mac_addr"
++};
++
++
++static char ** wpa_cli_complete_network(const char *str, int pos)
++{
++      int arg = get_cmd_arg_num(str, pos);
++      int i, num_fields = ARRAY_SIZE(network_fields);
++      char **res = NULL;
++
++      switch (arg) {
++      case 1:
++              res = cli_txt_list_array(&networks);
++              break;
++      case 2:
++              res = os_calloc(num_fields + 1, sizeof(char *));
++              if (res == NULL)
++                      return NULL;
++              for (i = 0; i < num_fields; i++) {
++                      res[i] = os_strdup(network_fields[i]);
++                      if (res[i] == NULL)
++                              break;
++              }
++      }
++      return res;
++}
++
++
++static char ** wpa_cli_complete_network_id(const char *str, int pos)
++{
++      int arg = get_cmd_arg_num(str, pos);
++      if (arg == 1)
++              return cli_txt_list_array(&networks);
++      return NULL;
++}
++
++
 +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 char ** wpa_cli_complete_dup_network(const char *str, int pos)
++{
++      int arg = get_cmd_arg_num(str, pos);
++      int i, num_fields = ARRAY_SIZE(network_fields);
++      char **res = NULL;
++
++      switch (arg) {
++      case 1:
++      case 2:
++              res = cli_txt_list_array(&networks);
++              break;
++      case 3:
++              res = os_calloc(num_fields + 1, sizeof(char *));
++              if (res == NULL)
++                      return NULL;
++              for (i = 0; i < num_fields; i++) {
++                      res[i] = os_strdup(network_fields[i]);
++                      if (res[i] == NULL)
++                              break;
++              }
++      }
++      return res;
++}
++
++
 +static int wpa_cli_cmd_list_creds(struct wpa_ctrl *ctrl, int argc,
 +                                char *argv[])
 +{
 +      return wpa_ctrl_command(ctrl, "LIST_CREDS");
 +}
 +
 +
 +static int wpa_cli_cmd_add_cred(struct wpa_ctrl *ctrl, int argc, char *argv[])
 +{
 +      return wpa_ctrl_command(ctrl, "ADD_CRED");
 +}
 +
 +
 +static int wpa_cli_cmd_remove_cred(struct wpa_ctrl *ctrl, int argc,
 +                                 char *argv[])
 +{
 +      return wpa_cli_cmd(ctrl, "REMOVE_CRED", 1, argc, argv);
 +}
 +
 +
 +static int wpa_cli_cmd_set_cred(struct wpa_ctrl *ctrl, int argc, char *argv[])
 +{
 +      if (argc != 3) {
 +              printf("Invalid SET_CRED command: needs three arguments\n"
 +                     "(cred id, variable name, and value)\n");
 +              return -1;
 +      }
 +
 +      return wpa_cli_cmd(ctrl, "SET_CRED", 3, argc, 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[])
 +{
 +      return wpa_ctrl_command(ctrl, "DISCONNECT");
 +}
 +
 +
 +static int wpa_cli_cmd_reconnect(struct wpa_ctrl *ctrl, int argc,
 +                                char *argv[])
 +{
 +      return wpa_ctrl_command(ctrl, "RECONNECT");
 +}
 +
 +
 +static int wpa_cli_cmd_save_config(struct wpa_ctrl *ctrl, int argc,
 +                                 char *argv[])
 +{
 +      return wpa_ctrl_command(ctrl, "SAVE_CONFIG");
 +}
 +
 +
 +static int wpa_cli_cmd_scan(struct wpa_ctrl *ctrl, int argc, char *argv[])
 +{
 +      return wpa_cli_cmd(ctrl, "SCAN", 0, argc, argv);
 +}
 +
 +
 +static int wpa_cli_cmd_scan_results(struct wpa_ctrl *ctrl, int argc,
 +                                  char *argv[])
 +{
 +      return wpa_ctrl_command(ctrl, "SCAN_RESULTS");
 +}
 +
 +
 +static int wpa_cli_cmd_bss(struct wpa_ctrl *ctrl, int argc, char *argv[])
 +{
 +      return wpa_cli_cmd(ctrl, "BSS", 1, argc, argv);
 +}
 +
 +
 +static char ** wpa_cli_complete_bss(const char *str, int pos)
 +{
 +      int arg = get_cmd_arg_num(str, pos);
 +      char **res = NULL;
 +
 +      switch (arg) {
 +      case 1:
 +              res = cli_txt_list_array(&bsses);
 +              break;
 +      }
 +
 +      return res;
 +}
 +
 +
 +static int wpa_cli_cmd_get_capability(struct wpa_ctrl *ctrl, int argc,
 +                                    char *argv[])
 +{
 +      if (argc < 1 || argc > 2) {
 +              printf("Invalid GET_CAPABILITY command: need either one or "
 +                     "two arguments\n");
 +              return -1;
 +      }
 +
 +      if ((argc == 2) && os_strcmp(argv[1], "strict") != 0) {
 +              printf("Invalid GET_CAPABILITY command: second argument, "
 +                     "if any, must be 'strict'\n");
 +              return -1;
 +      }
 +
 +      return wpa_cli_cmd(ctrl, "GET_CAPABILITY", 1, argc, argv);
 +}
 +
 +
 +static int wpa_cli_list_interfaces(struct wpa_ctrl *ctrl)
 +{
 +      printf("Available interfaces:\n");
 +      return wpa_ctrl_command(ctrl, "INTERFACES");
 +}
 +
 +
 +static int wpa_cli_cmd_interface(struct wpa_ctrl *ctrl, int argc, char *argv[])
 +{
 +      if (argc < 1) {
 +              wpa_cli_list_interfaces(ctrl);
 +              return 0;
 +      }
 +
 +      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) == 0) {
 +              printf("Connected to interface '%s.\n", ctrl_ifname);
 +      } else {
 +              printf("Could not connect to interface '%s' - re-trying\n",
 +                     ctrl_ifname);
 +      }
 +      return 0;
 +}
 +
 +
 +static int wpa_cli_cmd_reconfigure(struct wpa_ctrl *ctrl, int argc,
 +                                 char *argv[])
 +{
 +      return wpa_ctrl_command(ctrl, "RECONFIGURE");
 +}
 +
 +
 +static int wpa_cli_cmd_terminate(struct wpa_ctrl *ctrl, int argc,
 +                               char *argv[])
 +{
 +      return wpa_ctrl_command(ctrl, "TERMINATE");
 +}
 +
 +
 +static int wpa_cli_cmd_interface_add(struct wpa_ctrl *ctrl, int argc,
 +                                   char *argv[])
 +{
 +      char cmd[256];
 +      int res;
 +
 +      if (argc < 1) {
 +              printf("Invalid INTERFACE_ADD command: needs at least one "
 +                     "argument (interface name)\n"
 +                     "All arguments: ifname confname driver ctrl_interface "
-        * <driver_param>TAB<bridge_name>
++                     "driver_param bridge_name [create]\n");
 +              return -1;
 +      }
 +
 +      /*
 +       * INTERFACE_ADD <ifname>TAB<confname>TAB<driver>TAB<ctrl_interface>TAB
-                         "INTERFACE_ADD %s\t%s\t%s\t%s\t%s\t%s",
++       * <driver_param>TAB<bridge_name>[TAB<create>]
 +       */
 +      res = os_snprintf(cmd, sizeof(cmd),
-                         argc > 5 ? argv[5] : "");
++                        "INTERFACE_ADD %s\t%s\t%s\t%s\t%s\t%s\t%s",
 +                        argv[0],
 +                        argc > 1 ? argv[1] : "", argc > 2 ? argv[2] : "",
 +                        argc > 3 ? argv[3] : "", argc > 4 ? argv[4] : "",
- static struct wpa_cli_cmd wpa_cli_commands[] = {
++                        argc > 5 ? argv[5] : "", argc > 6 ? argv[6] : "");
 +      if (os_snprintf_error(sizeof(cmd), res))
 +              return -1;
 +      cmd[sizeof(cmd) - 1] = '\0';
 +      return wpa_ctrl_command(ctrl, cmd);
 +}
 +
 +
 +static int wpa_cli_cmd_interface_remove(struct wpa_ctrl *ctrl, int argc,
 +                                      char *argv[])
 +{
 +      return wpa_cli_cmd(ctrl, "INTERFACE_REMOVE", 1, argc, argv);
 +}
 +
 +
 +static int wpa_cli_cmd_interface_list(struct wpa_ctrl *ctrl, int argc,
 +                                    char *argv[])
 +{
 +      return wpa_ctrl_command(ctrl, "INTERFACE_LIST");
 +}
 +
 +
 +#ifdef CONFIG_AP
 +static int wpa_cli_cmd_sta(struct wpa_ctrl *ctrl, int argc, char *argv[])
 +{
 +      return wpa_cli_cmd(ctrl, "STA", 1, argc, argv);
 +}
 +
 +
 +static int wpa_ctrl_command_sta(struct wpa_ctrl *ctrl, char *cmd,
 +                              char *addr, size_t addr_len)
 +{
 +      char buf[4096], *pos;
 +      size_t len;
 +      int ret;
 +
 +      if (ctrl_conn == NULL) {
 +              printf("Not connected to hostapd - command dropped.\n");
 +              return -1;
 +      }
 +      len = sizeof(buf) - 1;
 +      ret = wpa_ctrl_request(ctrl, cmd, os_strlen(cmd), buf, &len,
 +                             wpa_cli_msg_cb);
 +      if (ret == -2) {
 +              printf("'%s' command timed out.\n", cmd);
 +              return -2;
 +      } else if (ret < 0) {
 +              printf("'%s' command failed.\n", cmd);
 +              return -1;
 +      }
 +
 +      buf[len] = '\0';
 +      if (os_memcmp(buf, "FAIL", 4) == 0)
 +              return -1;
 +      printf("%s", buf);
 +
 +      pos = buf;
 +      while (*pos != '\0' && *pos != '\n')
 +              pos++;
 +      *pos = '\0';
 +      os_strlcpy(addr, buf, addr_len);
 +      return 0;
 +}
 +
 +
 +static int wpa_cli_cmd_all_sta(struct wpa_ctrl *ctrl, int argc, char *argv[])
 +{
 +      char addr[32], cmd[64];
 +
 +      if (wpa_ctrl_command_sta(ctrl, "STA-FIRST", addr, sizeof(addr)))
 +              return 0;
 +      do {
 +              os_snprintf(cmd, sizeof(cmd), "STA-NEXT %s", addr);
 +      } while (wpa_ctrl_command_sta(ctrl, cmd, addr, sizeof(addr)) == 0);
 +
 +      return -1;
 +}
 +
 +
 +static int wpa_cli_cmd_deauthenticate(struct wpa_ctrl *ctrl, int argc,
 +                                    char *argv[])
 +{
 +      return wpa_cli_cmd(ctrl, "DEAUTHENTICATE", 1, argc, argv);
 +}
 +
 +
 +static int wpa_cli_cmd_disassociate(struct wpa_ctrl *ctrl, int argc,
 +                                  char *argv[])
 +{
 +      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 */
 +
 +
 +static int wpa_cli_cmd_suspend(struct wpa_ctrl *ctrl, int argc, char *argv[])
 +{
 +      return wpa_ctrl_command(ctrl, "SUSPEND");
 +}
 +
 +
 +static int wpa_cli_cmd_resume(struct wpa_ctrl *ctrl, int argc, char *argv[])
 +{
 +      return wpa_ctrl_command(ctrl, "RESUME");
 +}
 +
 +
 +#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[])
 +{
 +      return wpa_cli_cmd(ctrl, "ROAM", 1, argc, 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[])
 +{
 +      return wpa_cli_cmd(ctrl, "P2P_FIND", 0, argc, argv);
 +}
 +
 +
 +static char ** wpa_cli_complete_p2p_find(const char *str, int pos)
 +{
 +      char **res = NULL;
 +      int arg = get_cmd_arg_num(str, pos);
 +
 +      res = os_calloc(6, sizeof(char *));
 +      if (res == NULL)
 +              return NULL;
 +      res[0] = os_strdup("type=social");
 +      if (res[0] == NULL) {
 +              os_free(res);
 +              return NULL;
 +      }
 +      res[1] = os_strdup("type=progressive");
 +      if (res[1] == NULL)
 +              return res;
 +      res[2] = os_strdup("delay=");
 +      if (res[2] == NULL)
 +              return res;
 +      res[3] = os_strdup("dev_id=");
 +      if (res[3] == NULL)
 +              return res;
 +      if (arg == 1)
 +              res[4] = os_strdup("[timeout]");
 +
 +      return res;
 +}
 +
 +
 +static int wpa_cli_cmd_p2p_stop_find(struct wpa_ctrl *ctrl, int argc,
 +                                   char *argv[])
 +{
 +      return wpa_ctrl_command(ctrl, "P2P_STOP_FIND");
 +}
 +
 +
 +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[])
 +{
 +      return wpa_cli_cmd(ctrl, "P2P_CONNECT", 2, argc, argv);
 +}
 +
 +
 +static char ** wpa_cli_complete_p2p_connect(const char *str, int pos)
 +{
 +      int arg = get_cmd_arg_num(str, pos);
 +      char **res = NULL;
 +
 +      switch (arg) {
 +      case 1:
 +              res = cli_txt_list_array(&p2p_peers);
 +              break;
 +      }
 +
 +      return res;
 +}
 +
 +
 +static int wpa_cli_cmd_p2p_listen(struct wpa_ctrl *ctrl, int argc,
 +                                char *argv[])
 +{
 +      return wpa_cli_cmd(ctrl, "P2P_LISTEN", 0, argc, argv);
 +}
 +
 +
 +static int wpa_cli_cmd_p2p_group_remove(struct wpa_ctrl *ctrl, int argc,
 +                                      char *argv[])
 +{
 +      return wpa_cli_cmd(ctrl, "P2P_GROUP_REMOVE", 1, argc, argv);
 +}
 +
 +
 +static char ** wpa_cli_complete_p2p_group_remove(const char *str, int pos)
 +{
 +      int arg = get_cmd_arg_num(str, pos);
 +      char **res = NULL;
 +
 +      switch (arg) {
 +      case 1:
 +              res = cli_txt_list_array(&p2p_groups);
 +              break;
 +      }
 +
 +      return res;
 +}
 +
 +
 +static int wpa_cli_cmd_p2p_group_add(struct wpa_ctrl *ctrl, int argc,
 +                                      char *argv[])
 +{
 +      return wpa_cli_cmd(ctrl, "P2P_GROUP_ADD", 0, argc, argv);
 +}
 +
 +
 +static int wpa_cli_cmd_p2p_prov_disc(struct wpa_ctrl *ctrl, int argc,
 +                                   char *argv[])
 +{
 +      if (argc != 2 && argc != 3) {
 +              printf("Invalid P2P_PROV_DISC command: needs at least "
 +                     "two arguments, address and config method\n"
 +                     "(display, keypad, or pbc) and an optional join\n");
 +              return -1;
 +      }
 +
 +      return wpa_cli_cmd(ctrl, "P2P_PROV_DISC", 2, argc, argv);
 +}
 +
 +
 +static int wpa_cli_cmd_p2p_get_passphrase(struct wpa_ctrl *ctrl, int argc,
 +                                        char *argv[])
 +{
 +      return wpa_ctrl_command(ctrl, "P2P_GET_PASSPHRASE");
 +}
 +
 +
 +static int wpa_cli_cmd_p2p_serv_disc_req(struct wpa_ctrl *ctrl, int argc,
 +                                       char *argv[])
 +{
 +      char cmd[4096];
 +
 +      if (argc < 2) {
 +              printf("Invalid P2P_SERV_DISC_REQ command: needs two "
 +                     "or more arguments (address and TLVs)\n");
 +              return -1;
 +      }
 +
 +      if (write_cmd(cmd, sizeof(cmd), "P2P_SERV_DISC_REQ", argc, argv) < 0)
 +              return -1;
 +      return wpa_ctrl_command(ctrl, cmd);
 +}
 +
 +
 +static int wpa_cli_cmd_p2p_serv_disc_cancel_req(struct wpa_ctrl *ctrl,
 +                                              int argc, char *argv[])
 +{
 +      return wpa_cli_cmd(ctrl, "P2P_SERV_DISC_CANCEL_REQ", 1, argc, argv);
 +}
 +
 +
 +static int wpa_cli_cmd_p2p_serv_disc_resp(struct wpa_ctrl *ctrl, int argc,
 +                                        char *argv[])
 +{
 +      char cmd[4096];
 +      int res;
 +
 +      if (argc != 4) {
 +              printf("Invalid P2P_SERV_DISC_RESP command: needs four "
 +                     "arguments (freq, address, dialog token, and TLVs)\n");
 +              return -1;
 +      }
 +
 +      res = os_snprintf(cmd, sizeof(cmd), "P2P_SERV_DISC_RESP %s %s %s %s",
 +                        argv[0], argv[1], argv[2], argv[3]);
 +      if (os_snprintf_error(sizeof(cmd), res))
 +              return -1;
 +      cmd[sizeof(cmd) - 1] = '\0';
 +      return wpa_ctrl_command(ctrl, cmd);
 +}
 +
 +
 +static int wpa_cli_cmd_p2p_service_update(struct wpa_ctrl *ctrl, int argc,
 +                                        char *argv[])
 +{
 +      return wpa_ctrl_command(ctrl, "P2P_SERVICE_UPDATE");
 +}
 +
 +
 +static int wpa_cli_cmd_p2p_serv_disc_external(struct wpa_ctrl *ctrl,
 +                                            int argc, char *argv[])
 +{
 +      return wpa_cli_cmd(ctrl, "P2P_SERV_DISC_EXTERNAL", 1, argc, argv);
 +}
 +
 +
 +static int wpa_cli_cmd_p2p_service_flush(struct wpa_ctrl *ctrl, int argc,
 +                                       char *argv[])
 +{
 +      return wpa_ctrl_command(ctrl, "P2P_SERVICE_FLUSH");
 +}
 +
 +
 +static int wpa_cli_cmd_p2p_service_add(struct wpa_ctrl *ctrl, int argc,
 +                                     char *argv[])
 +{
 +      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);
 +}
 +
 +
 +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;
 +      }
 +
 +      return wpa_cli_cmd(ctrl, "P2P_SERVICE_REP", 5, argc, argv);
 +}
 +
 +
 +static int wpa_cli_cmd_p2p_service_del(struct wpa_ctrl *ctrl, int argc,
 +                                     char *argv[])
 +{
 +      char cmd[4096];
 +      int res;
 +
 +      if (argc != 2 && argc != 3) {
 +              printf("Invalid P2P_SERVICE_DEL command: needs two or three "
 +                     "arguments\n");
 +              return -1;
 +      }
 +
 +      if (argc == 3)
 +              res = os_snprintf(cmd, sizeof(cmd),
 +                                "P2P_SERVICE_DEL %s %s %s",
 +                                argv[0], argv[1], argv[2]);
 +      else
 +              res = os_snprintf(cmd, sizeof(cmd),
 +                                "P2P_SERVICE_DEL %s %s",
 +                                argv[0], argv[1]);
 +      if (os_snprintf_error(sizeof(cmd), res))
 +              return -1;
 +      cmd[sizeof(cmd) - 1] = '\0';
 +      return wpa_ctrl_command(ctrl, cmd);
 +}
 +
 +
 +static int wpa_cli_cmd_p2p_reject(struct wpa_ctrl *ctrl,
 +                                int argc, char *argv[])
 +{
 +      return wpa_cli_cmd(ctrl, "P2P_REJECT", 1, argc, argv);
 +}
 +
 +
 +static int wpa_cli_cmd_p2p_invite(struct wpa_ctrl *ctrl,
 +                                int argc, char *argv[])
 +{
 +      return wpa_cli_cmd(ctrl, "P2P_INVITE", 1, argc, argv);
 +}
 +
 +
 +static int wpa_cli_cmd_p2p_peer(struct wpa_ctrl *ctrl, int argc, char *argv[])
 +{
 +      return wpa_cli_cmd(ctrl, "P2P_PEER", 1, argc, argv);
 +}
 +
 +
 +static char ** wpa_cli_complete_p2p_peer(const char *str, int pos)
 +{
 +      int arg = get_cmd_arg_num(str, pos);
 +      char **res = NULL;
 +
 +      switch (arg) {
 +      case 1:
 +              res = cli_txt_list_array(&p2p_peers);
 +              break;
 +      }
 +
 +      return res;
 +}
 +
 +
 +static int wpa_ctrl_command_p2p_peer(struct wpa_ctrl *ctrl, char *cmd,
 +                                   char *addr, size_t addr_len,
 +                                   int discovered)
 +{
 +      char buf[4096], *pos;
 +      size_t len;
 +      int ret;
 +
 +      if (ctrl_conn == NULL)
 +              return -1;
 +      len = sizeof(buf) - 1;
 +      ret = wpa_ctrl_request(ctrl, cmd, os_strlen(cmd), buf, &len,
 +                             wpa_cli_msg_cb);
 +      if (ret == -2) {
 +              printf("'%s' command timed out.\n", cmd);
 +              return -2;
 +      } else if (ret < 0) {
 +              printf("'%s' command failed.\n", cmd);
 +              return -1;
 +      }
 +
 +      buf[len] = '\0';
 +      if (os_memcmp(buf, "FAIL", 4) == 0)
 +              return -1;
 +
 +      pos = buf;
 +      while (*pos != '\0' && *pos != '\n')
 +              pos++;
 +      *pos++ = '\0';
 +      os_strlcpy(addr, buf, addr_len);
 +      if (!discovered || os_strstr(pos, "[PROBE_REQ_ONLY]") == NULL)
 +              printf("%s\n", addr);
 +      return 0;
 +}
 +
 +
 +static int wpa_cli_cmd_p2p_peers(struct wpa_ctrl *ctrl, int argc, char *argv[])
 +{
 +      char addr[32], cmd[64];
 +      int discovered;
 +
 +      discovered = argc > 0 && os_strcmp(argv[0], "discovered") == 0;
 +
 +      if (wpa_ctrl_command_p2p_peer(ctrl, "P2P_PEER FIRST",
 +                                    addr, sizeof(addr), discovered))
 +              return -1;
 +      do {
 +              os_snprintf(cmd, sizeof(cmd), "P2P_PEER NEXT-%s", addr);
 +      } while (wpa_ctrl_command_p2p_peer(ctrl, cmd, addr, sizeof(addr),
 +                       discovered) == 0);
 +
 +      return 0;
 +}
 +
 +
 +static int wpa_cli_cmd_p2p_set(struct wpa_ctrl *ctrl, int argc, char *argv[])
 +{
 +      return wpa_cli_cmd(ctrl, "P2P_SET", 2, argc, 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");
 +}
 +
 +
 +static int wpa_cli_cmd_p2p_cancel(struct wpa_ctrl *ctrl, int argc,
 +                                char *argv[])
 +{
 +      return wpa_ctrl_command(ctrl, "P2P_CANCEL");
 +}
 +
 +
 +static int wpa_cli_cmd_p2p_unauthorize(struct wpa_ctrl *ctrl, int argc,
 +                                     char *argv[])
 +{
 +      return wpa_cli_cmd(ctrl, "P2P_UNAUTHORIZE", 1, argc, argv);
 +}
 +
 +
 +static int wpa_cli_cmd_p2p_presence_req(struct wpa_ctrl *ctrl, int argc,
 +                                      char *argv[])
 +{
 +      if (argc != 0 && argc != 2 && argc != 4) {
 +              printf("Invalid P2P_PRESENCE_REQ command: needs two arguments "
 +                     "(preferred duration, interval; in microsecods).\n"
 +                     "Optional second pair can be used to provide "
 +                     "acceptable values.\n");
 +              return -1;
 +      }
 +
 +      return wpa_cli_cmd(ctrl, "P2P_PRESENCE_REQ", 0, argc, argv);
 +}
 +
 +
 +static int wpa_cli_cmd_p2p_ext_listen(struct wpa_ctrl *ctrl, int argc,
 +                                    char *argv[])
 +{
 +      if (argc != 0 && argc != 2) {
 +              printf("Invalid P2P_EXT_LISTEN command: needs two arguments "
 +                     "(availability period, availability interval; in "
 +                     "millisecods).\n"
 +                     "Extended Listen Timing can be cancelled with this "
 +                     "command when used without parameters.\n");
 +              return -1;
 +      }
 +
 +      return wpa_cli_cmd(ctrl, "P2P_EXT_LISTEN", 0, argc, argv);
 +}
 +
 +
 +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,
 +                                     char *argv[])
 +{
 +      char cmd[100];
 +      int res;
 +
 +      if (argc != 1 && argc != 2) {
 +              printf("Invalid WFD_SUBELEM_SET command: needs one or two "
 +                     "arguments (subelem, hexdump)\n");
 +              return -1;
 +      }
 +
 +      res = os_snprintf(cmd, sizeof(cmd), "WFD_SUBELEM_SET %s %s",
 +                        argv[0], argc > 1 ? argv[1] : "");
 +      if (os_snprintf_error(sizeof(cmd), res))
 +              return -1;
 +      cmd[sizeof(cmd) - 1] = '\0';
 +      return wpa_ctrl_command(ctrl, cmd);
 +}
 +
 +
 +static int wpa_cli_cmd_wfd_subelem_get(struct wpa_ctrl *ctrl, int argc,
 +                                     char *argv[])
 +{
 +      char cmd[100];
 +      int res;
 +
 +      if (argc != 1) {
 +              printf("Invalid WFD_SUBELEM_GET command: needs one "
 +                     "argument (subelem)\n");
 +              return -1;
 +      }
 +
 +      res = os_snprintf(cmd, sizeof(cmd), "WFD_SUBELEM_GET %s",
 +                        argv[0]);
 +      if (os_snprintf_error(sizeof(cmd), res))
 +              return -1;
 +      cmd[sizeof(cmd) - 1] = '\0';
 +      return wpa_ctrl_command(ctrl, cmd);
 +}
 +#endif /* CONFIG_WIFI_DISPLAY */
 +
 +
 +#ifdef CONFIG_INTERWORKING
 +static int wpa_cli_cmd_fetch_anqp(struct wpa_ctrl *ctrl, int argc,
 +                                char *argv[])
 +{
 +      return wpa_ctrl_command(ctrl, "FETCH_ANQP");
 +}
 +
 +
 +static int wpa_cli_cmd_stop_fetch_anqp(struct wpa_ctrl *ctrl, int argc,
 +                                     char *argv[])
 +{
 +      return wpa_ctrl_command(ctrl, "STOP_FETCH_ANQP");
 +}
 +
 +
 +static int wpa_cli_cmd_interworking_select(struct wpa_ctrl *ctrl, int argc,
 +                                         char *argv[])
 +{
 +      return wpa_cli_cmd(ctrl, "INTERWORKING_SELECT", 0, argc, argv);
 +}
 +
 +
 +static int wpa_cli_cmd_interworking_connect(struct wpa_ctrl *ctrl, int argc,
 +                                          char *argv[])
 +{
 +      return wpa_cli_cmd(ctrl, "INTERWORKING_CONNECT", 1, argc, argv);
 +}
 +
 +
 +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);
 +}
 +
 +
 +static int wpa_cli_cmd_gas_request(struct wpa_ctrl *ctrl, int argc,
 +                                 char *argv[])
 +{
 +      return wpa_cli_cmd(ctrl, "GAS_REQUEST", 2, argc, argv);
 +}
 +
 +
 +static int wpa_cli_cmd_gas_response_get(struct wpa_ctrl *ctrl, int argc,
 +                                      char *argv[])
 +{
 +      return wpa_cli_cmd(ctrl, "GAS_RESPONSE_GET", 2, argc, argv);
 +}
 +#endif /* CONFIG_INTERWORKING */
 +
 +
 +#ifdef CONFIG_HS20
 +
 +static int wpa_cli_cmd_hs20_anqp_get(struct wpa_ctrl *ctrl, int argc,
 +                                   char *argv[])
 +{
 +      return wpa_cli_cmd(ctrl, "HS20_ANQP_GET", 2, argc, argv);
 +}
 +
 +
 +static int wpa_cli_cmd_get_nai_home_realm_list(struct wpa_ctrl *ctrl, int argc,
 +                                             char *argv[])
 +{
 +      char cmd[512];
 +
 +      if (argc == 0) {
 +              printf("Command needs one or two arguments (dst mac addr and "
 +                     "optional home realm)\n");
 +              return -1;
 +      }
 +
 +      if (write_cmd(cmd, sizeof(cmd), "HS20_GET_NAI_HOME_REALM_LIST",
 +                    argc, argv) < 0)
 +              return -1;
 +
 +      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 */
 +
 +
 +static int wpa_cli_cmd_sta_autoconnect(struct wpa_ctrl *ctrl, int argc,
 +                                     char *argv[])
 +{
 +      return wpa_cli_cmd(ctrl, "STA_AUTOCONNECT", 1, argc, argv);
 +}
 +
 +
 +static int wpa_cli_cmd_tdls_discover(struct wpa_ctrl *ctrl, int argc,
 +                                   char *argv[])
 +{
 +      return wpa_cli_cmd(ctrl, "TDLS_DISCOVER", 1, argc, argv);
 +}
 +
 +
 +static int wpa_cli_cmd_tdls_setup(struct wpa_ctrl *ctrl, int argc,
 +                                char *argv[])
 +{
 +      return wpa_cli_cmd(ctrl, "TDLS_SETUP", 1, argc, argv);
 +}
 +
 +
 +static int wpa_cli_cmd_tdls_teardown(struct wpa_ctrl *ctrl, int argc,
 +                                   char *argv[])
 +{
 +      return wpa_cli_cmd(ctrl, "TDLS_TEARDOWN", 1, argc, argv);
 +}
 +
 +
++static int wpa_cli_cmd_tdls_link_status(struct wpa_ctrl *ctrl, int argc,
++                                      char *argv[])
++{
++      return wpa_cli_cmd(ctrl, "TDLS_LINK_STATUS", 1, argc, argv);
++}
++
++
 +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[])
 +{
 +      return wpa_ctrl_command(ctrl, "SIGNAL_POLL");
 +}
 +
 +
 +static int wpa_cli_cmd_pktcnt_poll(struct wpa_ctrl *ctrl, int argc,
 +                                 char *argv[])
 +{
 +      return wpa_ctrl_command(ctrl, "PKTCNT_POLL");
 +}
 +
 +
 +static int wpa_cli_cmd_reauthenticate(struct wpa_ctrl *ctrl, int argc,
 +                                    char *argv[])
 +{
 +      return wpa_ctrl_command(ctrl, "REAUTHENTICATE");
 +}
 +
 +
 +#ifdef CONFIG_AUTOSCAN
 +
 +static int wpa_cli_cmd_autoscan(struct wpa_ctrl *ctrl, int argc, char *argv[])
 +{
 +      if (argc == 0)
 +              return wpa_ctrl_command(ctrl, "AUTOSCAN ");
 +
 +      return wpa_cli_cmd(ctrl, "AUTOSCAN", 0, argc, argv);
 +}
 +
 +#endif /* CONFIG_AUTOSCAN */
 +
 +
 +#ifdef CONFIG_WNM
 +
 +static int wpa_cli_cmd_wnm_sleep(struct wpa_ctrl *ctrl, int argc, char *argv[])
 +{
 +      return wpa_cli_cmd(ctrl, "WNM_SLEEP", 0, argc, argv);
 +}
 +
 +
 +static int wpa_cli_cmd_wnm_bss_query(struct wpa_ctrl *ctrl, int argc, char *argv[])
 +{
 +      return wpa_cli_cmd(ctrl, "WNM_BSS_QUERY", 1, argc, argv);
 +}
 +
 +#endif /* CONFIG_WNM */
 +
 +
 +static int wpa_cli_cmd_raw(struct wpa_ctrl *ctrl, int argc, char *argv[])
 +{
 +      if (argc == 0)
 +              return -1;
 +      return wpa_cli_cmd(ctrl, argv[0], 0, argc - 1, &argv[1]);
 +}
 +
 +
 +#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);
 +}
 +
 +
++static int wpa_cli_cmd_get_pref_freq_list(struct wpa_ctrl *ctrl, int argc,
++                                        char *argv[])
++{
++      return wpa_cli_cmd(ctrl, "GET_PREF_FREQ_LIST", 1, argc, argv);
++}
++
++
 +enum wpa_cli_cmd_flags {
 +      cli_cmd_flag_none               = 0x00,
 +      cli_cmd_flag_sensitive          = 0x01
 +};
 +
 +struct wpa_cli_cmd {
 +      const char *cmd;
 +      int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]);
 +      char ** (*completion)(const char *str, int pos);
 +      enum wpa_cli_cmd_flags flags;
 +      const char *usage;
 +};
 +
-       { "get", wpa_cli_cmd_get, NULL,
++static const struct wpa_cli_cmd wpa_cli_commands[] = {
 +      { "status", wpa_cli_cmd_status, NULL,
 +        cli_cmd_flag_none,
 +        "[verbose] = get current WPA/EAPOL/EAP status" },
 +      { "ifname", wpa_cli_cmd_ifname, NULL,
 +        cli_cmd_flag_none,
 +        "= get current interface name" },
 +      { "ping", wpa_cli_cmd_ping, NULL,
 +        cli_cmd_flag_none,
 +        "= pings wpa_supplicant" },
 +      { "relog", wpa_cli_cmd_relog, NULL,
 +        cli_cmd_flag_none,
 +        "= re-open log-file (allow rolling logs)" },
 +      { "note", wpa_cli_cmd_note, NULL,
 +        cli_cmd_flag_none,
 +        "<text> = add a note to wpa_supplicant debug log" },
 +      { "mib", wpa_cli_cmd_mib, NULL,
 +        cli_cmd_flag_none,
 +        "= get MIB variables (dot1x, dot11)" },
 +      { "help", wpa_cli_cmd_help, wpa_cli_complete_help,
 +        cli_cmd_flag_none,
 +        "[command] = show usage help" },
 +      { "interface", wpa_cli_cmd_interface, NULL,
 +        cli_cmd_flag_none,
 +        "[ifname] = show interfaces/select interface" },
 +      { "level", wpa_cli_cmd_level, NULL,
 +        cli_cmd_flag_none,
 +        "<debug level> = change debug level" },
 +      { "license", wpa_cli_cmd_license, NULL,
 +        cli_cmd_flag_none,
 +        "= show full wpa_cli license" },
 +      { "quit", wpa_cli_cmd_quit, NULL,
 +        cli_cmd_flag_none,
 +        "= exit wpa_cli" },
 +      { "set", wpa_cli_cmd_set, wpa_cli_complete_set,
 +        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" },
-       { "select_network", wpa_cli_cmd_select_network, NULL,
++      { "get", wpa_cli_cmd_get, wpa_cli_complete_get,
 +        cli_cmd_flag_none,
 +        "<name> = get information" },
 +      { "logon", wpa_cli_cmd_logon, NULL,
 +        cli_cmd_flag_none,
 +        "= IEEE 802.1X EAPOL state machine logon" },
 +      { "logoff", wpa_cli_cmd_logoff, NULL,
 +        cli_cmd_flag_none,
 +        "= IEEE 802.1X EAPOL state machine logoff" },
 +      { "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" },
 +      { "identity", wpa_cli_cmd_identity, NULL,
 +        cli_cmd_flag_none,
 +        "<network id> <identity> = configure identity for an SSID" },
 +      { "password", wpa_cli_cmd_password, NULL,
 +        cli_cmd_flag_sensitive,
 +        "<network id> <password> = configure password for an SSID" },
 +      { "new_password", wpa_cli_cmd_new_password, NULL,
 +        cli_cmd_flag_sensitive,
 +        "<network id> <password> = change password for an SSID" },
 +      { "pin", wpa_cli_cmd_pin, NULL,
 +        cli_cmd_flag_sensitive,
 +        "<network id> <pin> = configure pin for an SSID" },
 +      { "otp", wpa_cli_cmd_otp, NULL,
 +        cli_cmd_flag_sensitive,
 +        "<network id> <password> = configure one-time-password for an SSID"
 +      },
 +      { "passphrase", wpa_cli_cmd_passphrase, NULL,
 +        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" },
 +      { "blacklist", wpa_cli_cmd_blacklist, wpa_cli_complete_bss,
 +        cli_cmd_flag_none,
 +        "<BSSID> = add a BSSID to the blacklist\n"
 +        "blacklist clear = clear the blacklist\n"
 +        "blacklist = display the blacklist" },
 +      { "log_level", wpa_cli_cmd_log_level, NULL,
 +        cli_cmd_flag_none,
 +        "<level> [<timestamp>] = update the log level/timestamp\n"
 +        "log_level = display the current log level and log options" },
 +      { "list_networks", wpa_cli_cmd_list_networks, NULL,
 +        cli_cmd_flag_none,
 +        "= list configured networks" },
-       { "enable_network", wpa_cli_cmd_enable_network, NULL,
++      { "select_network", wpa_cli_cmd_select_network,
++        wpa_cli_complete_network_id,
 +        cli_cmd_flag_none,
 +        "<network id> = select a network (disable others)" },
-       { "disable_network", wpa_cli_cmd_disable_network, NULL,
++      { "enable_network", wpa_cli_cmd_enable_network,
++        wpa_cli_complete_network_id,
 +        cli_cmd_flag_none,
 +        "<network id> = enable a network" },
-       { "remove_network", wpa_cli_cmd_remove_network, NULL,
++      { "disable_network", wpa_cli_cmd_disable_network,
++        wpa_cli_complete_network_id,
 +        cli_cmd_flag_none,
 +        "<network id> = disable a network" },
 +      { "add_network", wpa_cli_cmd_add_network, NULL,
 +        cli_cmd_flag_none,
 +        "= add a network" },
-       { "set_network", wpa_cli_cmd_set_network, NULL,
++      { "remove_network", wpa_cli_cmd_remove_network,
++        wpa_cli_complete_network_id,
 +        cli_cmd_flag_none,
 +        "<network id> = remove a network" },
-       { "get_network", wpa_cli_cmd_get_network, NULL,
++      { "set_network", wpa_cli_cmd_set_network, wpa_cli_complete_network,
 +        cli_cmd_flag_sensitive,
 +        "<network id> <variable> <value> = set network variables (shows\n"
 +        "  list of variables when run without arguments)" },
-       { "dup_network", wpa_cli_cmd_dup_network, NULL,
++      { "get_network", wpa_cli_cmd_get_network, wpa_cli_complete_network,
 +        cli_cmd_flag_none,
 +        "<network id> <variable> = get network variables" },
-         "= get capabilies" },
++      { "dup_network", wpa_cli_cmd_dup_network, wpa_cli_complete_dup_network,
 +        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" },
 +      { "add_cred", wpa_cli_cmd_add_cred, NULL,
 +        cli_cmd_flag_none,
 +        "= add a credential" },
 +      { "remove_cred", wpa_cli_cmd_remove_cred, NULL,
 +        cli_cmd_flag_none,
 +        "<cred id> = remove a credential" },
 +      { "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" },
 +      { "disconnect", wpa_cli_cmd_disconnect, NULL,
 +        cli_cmd_flag_none,
 +        "= disconnect and wait for reassociate/reconnect command before\n"
 +        "  connecting" },
 +      { "reconnect", wpa_cli_cmd_reconnect, NULL,
 +        cli_cmd_flag_none,
 +        "= like reassociate, but only takes effect if already disconnected"
 +      },
 +      { "scan", wpa_cli_cmd_scan, NULL,
 +        cli_cmd_flag_none,
 +        "= request new BSS scan" },
 +      { "scan_results", wpa_cli_cmd_scan_results, NULL,
 +        cli_cmd_flag_none,
 +        "= get latest scan results" },
 +      { "bss", wpa_cli_cmd_bss, wpa_cli_complete_bss,
 +        cli_cmd_flag_none,
 +        "<<idx> | <bssid>> = get detailed scan result info" },
 +      { "get_capability", wpa_cli_cmd_get_capability, NULL,
 +        cli_cmd_flag_none,
 +        "<eap/pairwise/group/key_mgmt/proto/auth_alg/channels/freq/modes> "
- static void print_cmd_help(struct wpa_cli_cmd *cmd, const char *pad)
++        "= get capabilities" },
 +      { "reconfigure", wpa_cli_cmd_reconfigure, NULL,
 +        cli_cmd_flag_none,
 +        "= force wpa_supplicant to re-read its configuration file" },
 +      { "terminate", wpa_cli_cmd_terminate, NULL,
 +        cli_cmd_flag_none,
 +        "= terminate wpa_supplicant" },
 +      { "interface_add", wpa_cli_cmd_interface_add, NULL,
 +        cli_cmd_flag_none,
 +        "<ifname> <confname> <driver> <ctrl_interface> <driver_param>\n"
 +        "  <bridge_name> = adds new interface, all parameters but <ifname>\n"
 +        "  are optional" },
 +      { "interface_remove", wpa_cli_cmd_interface_remove, NULL,
 +        cli_cmd_flag_none,
 +        "<ifname> = removes the interface" },
 +      { "interface_list", wpa_cli_cmd_interface_list, NULL,
 +        cli_cmd_flag_none,
 +        "= list available interfaces" },
 +      { "ap_scan", wpa_cli_cmd_ap_scan, NULL,
 +        cli_cmd_flag_none,
 +        "<value> = set ap_scan parameter" },
 +      { "scan_interval", wpa_cli_cmd_scan_interval, NULL,
 +        cli_cmd_flag_none,
 +        "<value> = set scan_interval parameter (in seconds)" },
 +      { "bss_expire_age", wpa_cli_cmd_bss_expire_age, NULL,
 +        cli_cmd_flag_none,
 +        "<value> = set BSS expiration age parameter" },
 +      { "bss_expire_count", wpa_cli_cmd_bss_expire_count, NULL,
 +        cli_cmd_flag_none,
 +        "<value> = set BSS expiration scan count parameter" },
 +      { "bss_flush", wpa_cli_cmd_bss_flush, NULL,
 +        cli_cmd_flag_none,
 +        "<value> = set BSS flush age (0 by default)" },
 +      { "stkstart", wpa_cli_cmd_stkstart, NULL,
 +        cli_cmd_flag_none,
 +        "<addr> = request STK negotiation with <addr>" },
 +      { "ft_ds", wpa_cli_cmd_ft_ds, wpa_cli_complete_bss,
 +        cli_cmd_flag_none,
 +        "<addr> = request over-the-DS FT with <addr>" },
 +      { "wps_pbc", wpa_cli_cmd_wps_pbc, wpa_cli_complete_bss,
 +        cli_cmd_flag_none,
 +        "[BSSID] = start Wi-Fi Protected Setup: Push Button Configuration" },
 +      { "wps_pin", wpa_cli_cmd_wps_pin, wpa_cli_complete_bss,
 +        cli_cmd_flag_sensitive,
 +        "<BSSID> [PIN] = start WPS PIN method (returns PIN, if not "
 +        "hardcoded)" },
 +      { "wps_check_pin", wpa_cli_cmd_wps_check_pin, NULL,
 +        cli_cmd_flag_sensitive,
 +        "<PIN> = verify PIN checksum" },
 +      { "wps_cancel", wpa_cli_cmd_wps_cancel, NULL, cli_cmd_flag_none,
 +        "Cancels the pending WPS operation" },
 +#ifdef CONFIG_WPS_NFC
 +      { "wps_nfc", wpa_cli_cmd_wps_nfc, wpa_cli_complete_bss,
 +        cli_cmd_flag_none,
 +        "[BSSID] = start Wi-Fi Protected Setup: NFC" },
 +      { "wps_nfc_config_token", wpa_cli_cmd_wps_nfc_config_token, NULL,
 +        cli_cmd_flag_none,
 +        "<WPS|NDEF> = build configuration token" },
 +      { "wps_nfc_token", wpa_cli_cmd_wps_nfc_token, NULL,
 +        cli_cmd_flag_none,
 +        "<WPS|NDEF> = create password token" },
 +      { "wps_nfc_tag_read", wpa_cli_cmd_wps_nfc_tag_read, NULL,
 +        cli_cmd_flag_sensitive,
 +        "<hexdump of payload> = report read NFC tag with WPS data" },
 +      { "nfc_get_handover_req", wpa_cli_cmd_nfc_get_handover_req, NULL,
 +        cli_cmd_flag_none,
 +        "<NDEF> <WPS> = create NFC handover request" },
 +      { "nfc_get_handover_sel", wpa_cli_cmd_nfc_get_handover_sel, NULL,
 +        cli_cmd_flag_none,
 +        "<NDEF> <WPS> = create 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 "
 +        "NFC handover" },
 +#endif /* CONFIG_WPS_NFC */
 +      { "wps_reg", wpa_cli_cmd_wps_reg, wpa_cli_complete_bss,
 +        cli_cmd_flag_sensitive,
 +        "<BSSID> <AP PIN> = start WPS Registrar to configure an AP" },
 +      { "wps_ap_pin", wpa_cli_cmd_wps_ap_pin, NULL,
 +        cli_cmd_flag_sensitive,
 +        "[params..] = enable/disable AP PIN" },
 +      { "wps_er_start", wpa_cli_cmd_wps_er_start, NULL,
 +        cli_cmd_flag_none,
 +        "[IP address] = start Wi-Fi Protected Setup External Registrar" },
 +      { "wps_er_stop", wpa_cli_cmd_wps_er_stop, NULL,
 +        cli_cmd_flag_none,
 +        "= stop Wi-Fi Protected Setup External Registrar" },
 +      { "wps_er_pin", wpa_cli_cmd_wps_er_pin, NULL,
 +        cli_cmd_flag_sensitive,
 +        "<UUID> <PIN> = add an Enrollee PIN to External Registrar" },
 +      { "wps_er_pbc", wpa_cli_cmd_wps_er_pbc, NULL,
 +        cli_cmd_flag_none,
 +        "<UUID> = accept an Enrollee PBC using External Registrar" },
 +      { "wps_er_learn", wpa_cli_cmd_wps_er_learn, NULL,
 +        cli_cmd_flag_sensitive,
 +        "<UUID> <PIN> = learn AP configuration" },
 +      { "wps_er_set_config", wpa_cli_cmd_wps_er_set_config, NULL,
 +        cli_cmd_flag_none,
 +        "<UUID> <network id> = set AP configuration for enrolling" },
 +      { "wps_er_config", wpa_cli_cmd_wps_er_config, NULL,
 +        cli_cmd_flag_sensitive,
 +        "<UUID> <PIN> <SSID> <auth> <encr> <key> = configure AP" },
 +#ifdef CONFIG_WPS_NFC
 +      { "wps_er_nfc_config_token", wpa_cli_cmd_wps_er_nfc_config_token, NULL,
 +        cli_cmd_flag_none,
 +        "<WPS/NDEF> <UUID> = build NFC configuration token" },
 +#endif /* CONFIG_WPS_NFC */
 +      { "ibss_rsn", wpa_cli_cmd_ibss_rsn, NULL,
 +        cli_cmd_flag_none,
 +        "<addr> = request RSN authentication with <addr> in IBSS" },
 +#ifdef CONFIG_AP
 +      { "sta", wpa_cli_cmd_sta, NULL,
 +        cli_cmd_flag_none,
 +        "<addr> = get information about an associated station (AP)" },
 +      { "all_sta", wpa_cli_cmd_all_sta, NULL,
 +        cli_cmd_flag_none,
 +        "= get information about all associated stations (AP)" },
 +      { "deauthenticate", wpa_cli_cmd_deauthenticate, NULL,
 +        cli_cmd_flag_none,
 +        "<addr> = deauthenticate a station" },
 +      { "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" },
 +      { "p2p_listen", wpa_cli_cmd_p2p_listen, NULL, cli_cmd_flag_none,
 +        "[timeout] = listen for P2P Devices for up-to timeout seconds" },
 +      { "p2p_group_remove", wpa_cli_cmd_p2p_group_remove,
 +        wpa_cli_complete_p2p_group_remove, cli_cmd_flag_none,
 +        "<ifname> = remove P2P group interface (terminate group if GO)" },
 +      { "p2p_group_add", wpa_cli_cmd_p2p_group_add, NULL, cli_cmd_flag_none,
 +        "[ht40] = add a new P2P group (local end as GO)" },
 +      { "p2p_prov_disc", wpa_cli_cmd_p2p_prov_disc,
 +        wpa_cli_complete_p2p_peer, cli_cmd_flag_none,
 +        "<addr> <method> = request provisioning discovery" },
 +      { "p2p_get_passphrase", wpa_cli_cmd_p2p_get_passphrase, NULL,
 +        cli_cmd_flag_none,
 +        "= get the passphrase for a group (GO only)" },
 +      { "p2p_serv_disc_req", wpa_cli_cmd_p2p_serv_disc_req,
 +        wpa_cli_complete_p2p_peer, cli_cmd_flag_none,
 +        "<addr> <TLVs> = schedule service discovery request" },
 +      { "p2p_serv_disc_cancel_req", wpa_cli_cmd_p2p_serv_disc_cancel_req,
 +        NULL, cli_cmd_flag_none,
 +        "<id> = cancel pending service discovery request" },
 +      { "p2p_serv_disc_resp", wpa_cli_cmd_p2p_serv_disc_resp, NULL,
 +        cli_cmd_flag_none,
 +        "<freq> <addr> <dialog token> <TLVs> = service discovery response" },
 +      { "p2p_service_update", wpa_cli_cmd_p2p_service_update, NULL,
 +        cli_cmd_flag_none,
 +        "= indicate change in local services" },
 +      { "p2p_serv_disc_external", wpa_cli_cmd_p2p_serv_disc_external, NULL,
 +        cli_cmd_flag_none,
 +        "<external> = set external processing of service discovery" },
 +      { "p2p_service_flush", wpa_cli_cmd_p2p_service_flush, NULL,
 +        cli_cmd_flag_none,
 +        "= remove all stored service entries" },
 +      { "p2p_service_add", wpa_cli_cmd_p2p_service_add, NULL,
 +        cli_cmd_flag_none,
 +        "<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 "
 +        "service" },
 +      { "p2p_reject", wpa_cli_cmd_p2p_reject, wpa_cli_complete_p2p_peer,
 +        cli_cmd_flag_none,
 +        "<addr> = reject connection attempts from a specific peer" },
 +      { "p2p_invite", wpa_cli_cmd_p2p_invite, NULL,
 +        cli_cmd_flag_none,
 +        "<cmd> [peer=addr] = invite peer" },
 +      { "p2p_peers", wpa_cli_cmd_p2p_peers, NULL, cli_cmd_flag_none,
 +        "[discovered] = list known (optionally, only fully discovered) P2P "
 +        "peers" },
 +      { "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, 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" },
 +      { "p2p_cancel", wpa_cli_cmd_p2p_cancel, NULL, cli_cmd_flag_none,
 +        "= cancel P2P group formation" },
 +      { "p2p_unauthorize", wpa_cli_cmd_p2p_unauthorize,
 +        wpa_cli_complete_p2p_peer, cli_cmd_flag_none,
 +        "<address> = unauthorize a peer" },
 +      { "p2p_presence_req", wpa_cli_cmd_p2p_presence_req, NULL,
 +        cli_cmd_flag_none,
 +        "[<duration> <interval>] [<duration> <interval>] = request GO "
 +        "presence" },
 +      { "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,
 +        "<subelem> [contents] = set Wi-Fi Display subelement" },
 +      { "wfd_subelem_get", wpa_cli_cmd_wfd_subelem_get, NULL,
 +        cli_cmd_flag_none,
 +        "<subelem> = get Wi-Fi Display subelement" },
 +#endif /* CONFIG_WIFI_DISPLAY */
 +#ifdef CONFIG_INTERWORKING
 +      { "fetch_anqp", wpa_cli_cmd_fetch_anqp, NULL, cli_cmd_flag_none,
 +        "= fetch ANQP information for all APs" },
 +      { "stop_fetch_anqp", wpa_cli_cmd_stop_fetch_anqp, NULL,
 +        cli_cmd_flag_none,
 +        "= stop fetch_anqp operation" },
 +      { "interworking_select", wpa_cli_cmd_interworking_select, NULL,
 +        cli_cmd_flag_none,
 +        "[auto] = perform Interworking network selection" },
 +      { "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" },
 +      { "gas_request", wpa_cli_cmd_gas_request, wpa_cli_complete_bss,
 +        cli_cmd_flag_none,
 +        "<addr> <AdvProtoID> [QueryReq] = GAS request" },
 +      { "gas_response_get", wpa_cli_cmd_gas_response_get,
 +        wpa_cli_complete_bss, cli_cmd_flag_none,
 +        "<addr> <dialog token> [start,len] = Fetch last GAS response" },
 +#endif /* CONFIG_INTERWORKING */
 +#ifdef CONFIG_HS20
 +      { "hs20_anqp_get", wpa_cli_cmd_hs20_anqp_get, wpa_cli_complete_bss,
 +        cli_cmd_flag_none,
 +        "<addr> <subtype>[,<subtype>]... = request HS 2.0 ANQP information"
 +      },
 +      { "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,
 +        "<0/1> = disable/enable automatic reconnection" },
 +      { "tdls_discover", wpa_cli_cmd_tdls_discover, NULL,
 +        cli_cmd_flag_none,
 +        "<addr> = request TDLS discovery with <addr>" },
 +      { "tdls_setup", wpa_cli_cmd_tdls_setup, NULL,
 +        cli_cmd_flag_none,
 +        "<addr> = request TDLS setup with <addr>" },
 +      { "tdls_teardown", wpa_cli_cmd_tdls_teardown, NULL,
 +        cli_cmd_flag_none,
 +        "<addr> = tear down TDLS with <addr>" },
++      { "tdls_link_status", wpa_cli_cmd_tdls_link_status, NULL,
++        cli_cmd_flag_none,
++        "<addr> = TDLS link status 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" },
 +      { "pktcnt_poll", wpa_cli_cmd_pktcnt_poll, NULL,
 +        cli_cmd_flag_none,
 +        "= get TX/RX packet counters" },
 +      { "reauthenticate", wpa_cli_cmd_reauthenticate, NULL,
 +        cli_cmd_flag_none,
 +        "= trigger IEEE 802.1X/EAPOL reauthentication" },
 +#ifdef CONFIG_AUTOSCAN
 +      { "autoscan", wpa_cli_cmd_autoscan, NULL, cli_cmd_flag_none,
 +        "[params] = Set or unset (if none) autoscan parameters" },
 +#endif /* CONFIG_AUTOSCAN */
 +#ifdef CONFIG_WNM
 +      { "wnm_sleep", wpa_cli_cmd_wnm_sleep, NULL, cli_cmd_flag_none,
 +        "<enter/exit> [interval=#] = enter/exit WNM-Sleep mode" },
 +      { "wnm_bss_query", wpa_cli_cmd_wnm_bss_query, NULL, cli_cmd_flag_none,
 +        "<query reason> = Send BSS Transition Management Query" },
 +#endif /* CONFIG_WNM */
 +      { "raw", wpa_cli_cmd_raw, NULL, cli_cmd_flag_sensitive,
 +        "<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"
 +      },
++      { "get_pref_freq_list", wpa_cli_cmd_get_pref_freq_list, NULL,
++        cli_cmd_flag_none,
++        "<interface type> = retrieve preferred freq list for the specified interface type" },
 +      { NULL, NULL, NULL, cli_cmd_flag_none, NULL }
 +};
 +
 +
 +/*
 + * Prints command usage, lines are padded with the specified string.
 + */
-       struct wpa_cli_cmd *cmd, *match = NULL;
++static void print_cmd_help(const struct wpa_cli_cmd *cmd, const char *pad)
 +{
 +      char c;
 +      size_t n;
 +
 +      printf("%s%s ", pad, cmd->cmd);
 +      for (n = 0; (c = cmd->usage[n]); n++) {
 +              printf("%c", c);
 +              if (c == '\n')
 +                      printf("%s", pad);
 +      }
 +      printf("\n");
 +}
 +
 +
 +static void print_help(const char *cmd)
 +{
 +      int n;
 +      printf("commands:\n");
 +      for (n = 0; wpa_cli_commands[n].cmd; n++) {
 +              if (cmd == NULL || str_starts(wpa_cli_commands[n].cmd, cmd))
 +                      print_cmd_help(&wpa_cli_commands[n], "  ");
 +      }
 +}
 +
 +
 +static int wpa_cli_edit_filter_history_cb(void *ctx, const char *cmd)
 +{
 +      const char *c, *delim;
 +      int n;
 +      size_t len;
 +
 +      delim = os_strchr(cmd, ' ');
 +      if (delim)
 +              len = delim - cmd;
 +      else
 +              len = os_strlen(cmd);
 +
 +      for (n = 0; (c = wpa_cli_commands[n].cmd); n++) {
 +              if (os_strncasecmp(cmd, c, len) == 0 && len == os_strlen(c))
 +                      return (wpa_cli_commands[n].flags &
 +                              cli_cmd_flag_sensitive);
 +      }
 +      return 0;
 +}
 +
 +
 +static char ** wpa_list_cmd_list(void)
 +{
 +      char **res;
 +      int i, count;
 +      struct cli_txt_entry *e;
 +
 +      count = ARRAY_SIZE(wpa_cli_commands);
 +      count += dl_list_len(&p2p_groups);
 +      count += dl_list_len(&ifnames);
 +      res = os_calloc(count + 1, sizeof(char *));
 +      if (res == NULL)
 +              return NULL;
 +
 +      for (i = 0; wpa_cli_commands[i].cmd; i++) {
 +              res[i] = os_strdup(wpa_cli_commands[i].cmd);
 +              if (res[i] == NULL)
 +                      break;
 +      }
 +
 +      dl_list_for_each(e, &p2p_groups, struct cli_txt_entry, list) {
 +              size_t len = 8 + os_strlen(e->txt);
 +              res[i] = os_malloc(len);
 +              if (res[i] == NULL)
 +                      break;
 +              os_snprintf(res[i], len, "ifname=%s", e->txt);
 +              i++;
 +      }
 +
 +      dl_list_for_each(e, &ifnames, struct cli_txt_entry, list) {
 +              res[i] = os_strdup(e->txt);
 +              if (res[i] == NULL)
 +                      break;
 +              i++;
 +      }
 +
 +      return res;
 +}
 +
 +
 +static char ** wpa_cli_cmd_completion(const char *cmd, const char *str,
 +                                    int pos)
 +{
 +      int i;
 +
 +      for (i = 0; wpa_cli_commands[i].cmd; i++) {
 +              if (os_strcasecmp(wpa_cli_commands[i].cmd, cmd) == 0) {
 +                      if (wpa_cli_commands[i].completion)
 +                              return wpa_cli_commands[i].completion(str,
 +                                                                    pos);
 +                      edit_clear_line();
 +                      printf("\r%s\n", wpa_cli_commands[i].usage);
 +                      edit_redraw();
 +                      break;
 +              }
 +      }
 +
 +      return NULL;
 +}
 +
 +
 +static char ** wpa_cli_edit_completion_cb(void *ctx, const char *str, int pos)
 +{
 +      char **res;
 +      const char *end;
 +      char *cmd;
 +
 +      if (pos > 7 && os_strncasecmp(str, "IFNAME=", 7) == 0) {
 +              end = os_strchr(str, ' ');
 +              if (end && pos > end - str) {
 +                      pos -= end - str + 1;
 +                      str = end + 1;
 +              }
 +      }
 +
 +      end = os_strchr(str, ' ');
 +      if (end == NULL || str + pos < end)
 +              return wpa_list_cmd_list();
 +
 +      cmd = os_malloc(pos + 1);
 +      if (cmd == NULL)
 +              return NULL;
 +      os_memcpy(cmd, str, pos);
 +      cmd[end - str] = '\0';
 +      res = wpa_cli_cmd_completion(cmd, str, pos);
 +      os_free(cmd);
 +      return res;
 +}
 +
 +
 +static int wpa_request(struct wpa_ctrl *ctrl, int argc, char *argv[])
 +{
-               cli_txt_list_add_word(&p2p_groups, s + 1);
++      const struct wpa_cli_cmd *cmd, *match = NULL;
 +      int count;
 +      int ret = 0;
 +
 +      if (argc > 1 && os_strncasecmp(argv[0], "IFNAME=", 7) == 0) {
 +              ifname_prefix = argv[0] + 7;
 +              argv = &argv[1];
 +              argc--;
 +      } else
 +              ifname_prefix = NULL;
 +
 +      if (argc == 0)
 +              return -1;
 +
 +      count = 0;
 +      cmd = wpa_cli_commands;
 +      while (cmd->cmd) {
 +              if (os_strncasecmp(cmd->cmd, argv[0], os_strlen(argv[0])) == 0)
 +              {
 +                      match = cmd;
 +                      if (os_strcasecmp(cmd->cmd, argv[0]) == 0) {
 +                              /* we have an exact match */
 +                              count = 1;
 +                              break;
 +                      }
 +                      count++;
 +              }
 +              cmd++;
 +      }
 +
 +      if (count > 1) {
 +              printf("Ambiguous command '%s'; possible commands:", argv[0]);
 +              cmd = wpa_cli_commands;
 +              while (cmd->cmd) {
 +                      if (os_strncasecmp(cmd->cmd, argv[0],
 +                                         os_strlen(argv[0])) == 0) {
 +                              printf(" %s", cmd->cmd);
 +                      }
 +                      cmd++;
 +              }
 +              printf("\n");
 +              ret = 1;
 +      } else if (count == 0) {
 +              printf("Unknown command '%s'\n", argv[0]);
 +              ret = 1;
 +      } else {
 +              ret = match->handler(ctrl, argc - 1, &argv[1]);
 +      }
 +
 +      return ret;
 +}
 +
 +
 +static int str_match(const char *a, const char *b)
 +{
 +      return os_strncmp(a, b, os_strlen(b)) == 0;
 +}
 +
 +
 +static int wpa_cli_exec(const char *program, const char *arg1,
 +                      const char *arg2)
 +{
 +      char *arg;
 +      size_t len;
 +      int res;
 +
 +      len = os_strlen(arg1) + os_strlen(arg2) + 2;
 +      arg = os_malloc(len);
 +      if (arg == NULL)
 +              return -1;
 +      os_snprintf(arg, len, "%s %s", arg1, arg2);
 +      res = os_exec(program, arg, 1);
 +      os_free(arg);
 +
 +      return res;
 +}
 +
 +
 +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];
 +
++      if (eloop_terminated())
++              return;
++
 +      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 = prev;
 +      }
 +
 +      if (str_match(pos, WPA_EVENT_CONNECTED)) {
 +              int new_id = -1;
 +              os_unsetenv("WPA_ID");
 +              os_unsetenv("WPA_ID_STR");
 +              os_unsetenv("WPA_CTRL_DIR");
 +
 +              pos = os_strstr(pos, "[id=");
 +              if (pos)
 +                      copy = os_strdup(pos + 4);
 +
 +              if (copy) {
 +                      pos2 = id = copy;
 +                      while (*pos2 && *pos2 != ' ')
 +                              pos2++;
 +                      *pos2++ = '\0';
 +                      new_id = atoi(id);
 +                      os_setenv("WPA_ID", id, 1);
 +                      while (*pos2 && *pos2 != '=')
 +                              pos2++;
 +                      if (*pos2 == '=')
 +                              pos2++;
 +                      id = pos2;
 +                      while (*pos2 && *pos2 != ']')
 +                              pos2++;
 +                      *pos2 = '\0';
 +                      os_setenv("WPA_ID_STR", id, 1);
 +                      os_free(copy);
 +              }
 +
 +              os_setenv("WPA_CTRL_DIR", ctrl_iface_dir, 1);
 +
 +              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, ifname, "CONNECTED");
 +              }
 +      } else if (str_match(pos, WPA_EVENT_DISCONNECTED)) {
 +              if (wpa_cli_connected) {
 +                      wpa_cli_connected = 0;
 +                      wpa_cli_exec(action_file, ifname, "DISCONNECTED");
 +              }
 +      } else if (str_match(pos, MESH_GROUP_STARTED)) {
 +              wpa_cli_exec(action_file, ctrl_ifname, pos);
 +      } else if (str_match(pos, MESH_GROUP_REMOVED)) {
 +              wpa_cli_exec(action_file, ctrl_ifname, pos);
 +      } else if (str_match(pos, MESH_PEER_CONNECTED)) {
 +              wpa_cli_exec(action_file, ctrl_ifname, pos);
 +      } 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, ifname, pos);
 +      } else if (str_match(pos, WPS_EVENT_SUCCESS)) {
 +              wpa_cli_exec(action_file, ifname, pos);
 +      } else if (str_match(pos, WPS_EVENT_FAIL)) {
 +              wpa_cli_exec(action_file, ifname, pos);
 +      } else if (str_match(pos, AP_STA_CONNECTED)) {
 +              wpa_cli_exec(action_file, ifname, pos);
 +      } else if (str_match(pos, AP_STA_DISCONNECTED)) {
 +              wpa_cli_exec(action_file, ifname, pos);
 +      } else if (str_match(pos, ESS_DISASSOC_IMMINENT)) {
 +              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;
 +      }
 +}
 +
 +
 +#ifndef CONFIG_ANSI_C_EXTRA
 +static void wpa_cli_action_cb(char *msg, size_t len)
 +{
 +      wpa_cli_action_process(msg);
 +}
 +#endif /* CONFIG_ANSI_C_EXTRA */
 +
 +
 +static void wpa_cli_reconnect(void)
 +{
 +      wpa_cli_close_connection();
 +      if (wpa_cli_open_connection(ctrl_ifname, 1) < 0)
 +              return;
 +
 +      if (interactive) {
 +              edit_clear_line();
 +              printf("\rConnection to wpa_supplicant re-established\n");
 +              edit_redraw();
 +      }
 +}
 +
 +
 +static void cli_event(const char *str)
 +{
 +      const char *start, *s;
 +
 +      start = os_strchr(str, '>');
 +      if (start == NULL)
 +              return;
 +
 +      start++;
 +
 +      if (str_starts(start, WPA_EVENT_BSS_ADDED)) {
 +              s = os_strchr(start, ' ');
 +              if (s == NULL)
 +                      return;
 +              s = os_strchr(s + 1, ' ');
 +              if (s == NULL)
 +                      return;
 +              cli_txt_list_add(&bsses, s + 1);
 +              return;
 +      }
 +
 +      if (str_starts(start, WPA_EVENT_BSS_REMOVED)) {
 +              s = os_strchr(start, ' ');
 +              if (s == NULL)
 +                      return;
 +              s = os_strchr(s + 1, ' ');
 +              if (s == NULL)
 +                      return;
 +              cli_txt_list_del_addr(&bsses, s + 1);
 +              return;
 +      }
 +
 +#ifdef CONFIG_P2P
 +      if (str_starts(start, P2P_EVENT_DEVICE_FOUND)) {
 +              s = os_strstr(start, " p2p_dev_addr=");
 +              if (s == NULL)
 +                      return;
 +              cli_txt_list_add_addr(&p2p_peers, s + 14);
 +              return;
 +      }
 +
 +      if (str_starts(start, P2P_EVENT_DEVICE_LOST)) {
 +              s = os_strstr(start, " p2p_dev_addr=");
 +              if (s == NULL)
 +                      return;
 +              cli_txt_list_del_addr(&p2p_peers, s + 14);
 +              return;
 +      }
 +
 +      if (str_starts(start, P2P_EVENT_GROUP_STARTED)) {
 +              s = os_strchr(start, ' ');
 +              if (s == NULL)
 +                      return;
-               cli_txt_list_del_word(&p2p_groups, s + 1);
++              cli_txt_list_add_word(&p2p_groups, s + 1, ' ');
 +              return;
 +      }
 +
 +      if (str_starts(start, P2P_EVENT_GROUP_REMOVED)) {
 +              s = os_strchr(start, ' ');
 +              if (s == NULL)
 +                      return;
-       fd_set rfds;
-       int fd, res;
-       struct timeval tv;
-       char buf[256]; /* note: large enough to fit in unsolicited messages */
-       size_t len;
++              cli_txt_list_del_word(&p2p_groups, s + 1, ' ');
 +              return;
 +      }
 +#endif /* CONFIG_P2P */
 +}
 +
 +
 +static int check_terminating(const char *msg)
 +{
 +      const char *pos = msg;
 +
 +      if (*pos == '<') {
 +              /* skip priority */
 +              pos = os_strchr(pos, '>');
 +              if (pos)
 +                      pos++;
 +              else
 +                      pos = msg;
 +      }
 +
 +      if (str_match(pos, WPA_EVENT_TERMINATING) && ctrl_conn) {
 +              edit_clear_line();
 +              printf("\rConnection to wpa_supplicant lost - trying to "
 +                     "reconnect\n");
 +              edit_redraw();
 +              wpa_cli_attached = 0;
 +              wpa_cli_close_connection();
 +              return 1;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +static void wpa_cli_recv_pending(struct wpa_ctrl *ctrl, int action_monitor)
 +{
 +      if (ctrl_conn == NULL) {
 +              wpa_cli_reconnect();
 +              return;
 +      }
 +      while (wpa_ctrl_pending(ctrl) > 0) {
 +              char buf[4096];
 +              size_t len = sizeof(buf) - 1;
 +              if (wpa_ctrl_recv(ctrl, buf, &len) == 0) {
 +                      buf[len] = '\0';
 +                      if (action_monitor)
 +                              wpa_cli_action_process(buf);
 +                      else {
 +                              cli_event(buf);
 +                              if (wpa_cli_show_event(buf)) {
 +                                      edit_clear_line();
 +                                      printf("\r%s\n", buf);
 +                                      edit_redraw();
 +                              }
 +
 +                              if (interactive && check_terminating(buf) > 0)
 +                                      return;
 +                      }
 +              } else {
 +                      printf("Could not read pending message.\n");
 +                      break;
 +              }
 +      }
 +
 +      if (wpa_ctrl_pending(ctrl) < 0) {
 +              printf("Connection to wpa_supplicant lost - trying to "
 +                     "reconnect\n");
 +              wpa_cli_reconnect();
 +      }
 +}
 +
 +#define max_args 10
 +
 +static int tokenize_cmd(char *cmd, char *argv[])
 +{
 +      char *pos;
 +      int argc = 0;
 +
 +      pos = cmd;
 +      for (;;) {
 +              while (*pos == ' ')
 +                      pos++;
 +              if (*pos == '\0')
 +                      break;
 +              argv[argc] = pos;
 +              argc++;
 +              if (argc == max_args)
 +                      break;
 +              if (*pos == '"') {
 +                      char *pos2 = os_strrchr(pos, '"');
 +                      if (pos2)
 +                              pos = pos2 + 1;
 +              }
 +              while (*pos != '\0' && *pos != ' ')
 +                      pos++;
 +              if (*pos == ' ')
 +                      *pos++ = '\0';
 +      }
 +
 +      return argc;
 +}
 +
 +
 +static void wpa_cli_ping(void *eloop_ctx, void *timeout_ctx)
 +{
 +      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();
 +      eloop_register_timeout(ping_interval, 0, wpa_cli_ping, NULL, NULL);
 +}
 +
 +
 +static void wpa_cli_mon_receive(int sock, void *eloop_ctx, void *sock_ctx)
 +{
 +      wpa_cli_recv_pending(mon_conn, 0);
 +}
 +
 +
 +static void wpa_cli_edit_cmd_cb(void *ctx, char *cmd)
 +{
 +      char *argv[max_args];
 +      int argc;
 +      argc = tokenize_cmd(cmd, argv);
 +      if (argc)
 +              wpa_request(ctrl_conn, argc, argv);
 +}
 +
 +
 +static void wpa_cli_edit_eof_cb(void *ctx)
 +{
 +      eloop_terminate();
 +}
 +
 +
 +static int warning_displayed = 0;
 +static char *hfile = NULL;
 +static int edit_started = 0;
 +
 +static void start_edit(void)
 +{
 +      char *home;
 +      char *ps = NULL;
 +
 +#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE
 +      ps = wpa_ctrl_get_remote_ifname(ctrl_conn);
 +#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
 +
++#ifdef CONFIG_WPA_CLI_HISTORY_DIR
++      home = CONFIG_WPA_CLI_HISTORY_DIR;
++#else /* CONFIG_WPA_CLI_HISTORY_DIR */
 +      home = getenv("HOME");
++#endif /* CONFIG_WPA_CLI_HISTORY_DIR */
 +      if (home) {
 +              const char *fname = ".wpa_cli_history";
 +              int hfile_len = os_strlen(home) + 1 + os_strlen(fname) + 1;
 +              hfile = os_malloc(hfile_len);
 +              if (hfile)
 +                      os_snprintf(hfile, hfile_len, "%s/%s", home, fname);
 +      }
 +
 +      if (edit_init(wpa_cli_edit_cmd_cb, wpa_cli_edit_eof_cb,
 +                    wpa_cli_edit_completion_cb, NULL, hfile, ps) < 0) {
 +              eloop_terminate();
 +              return;
 +      }
 +
 +      edit_started = 1;
 +      eloop_register_timeout(ping_interval, 0, wpa_cli_ping, NULL, NULL);
 +}
 +
 +
 +static void update_bssid_list(struct wpa_ctrl *ctrl)
 +{
 +      char buf[4096];
 +      size_t len = sizeof(buf);
 +      int ret;
 +      char *cmd = "BSS RANGE=ALL MASK=0x2";
 +      char *pos, *end;
 +
 +      if (ctrl == NULL)
 +              return;
 +      ret = wpa_ctrl_request(ctrl, cmd, os_strlen(cmd), buf, &len, NULL);
 +      if (ret < 0)
 +              return;
 +      buf[len] = '\0';
 +
 +      pos = buf;
 +      while (pos) {
 +              pos = os_strstr(pos, "bssid=");
 +              if (pos == NULL)
 +                      break;
 +              pos += 6;
 +              end = os_strchr(pos, '\n');
 +              if (end == NULL)
 +                      break;
 +              *end = '\0';
 +              cli_txt_list_add(&bsses, pos);
 +              pos = end + 1;
 +      }
 +}
 +
 +
 +static void update_ifnames(struct wpa_ctrl *ctrl)
 +{
 +      char buf[4096];
 +      size_t len = sizeof(buf);
 +      int ret;
 +      char *cmd = "INTERFACES";
 +      char *pos, *end;
 +      char txt[200];
 +
 +      cli_txt_list_flush(&ifnames);
 +
 +      if (ctrl == NULL)
 +              return;
 +      ret = wpa_ctrl_request(ctrl, cmd, os_strlen(cmd), buf, &len, NULL);
 +      if (ret < 0)
 +              return;
 +      buf[len] = '\0';
 +
 +      pos = buf;
 +      while (pos) {
 +              end = os_strchr(pos, '\n');
 +              if (end == NULL)
 +                      break;
 +              *end = '\0';
 +              ret = os_snprintf(txt, sizeof(txt), "ifname=%s", pos);
 +              if (!os_snprintf_error(sizeof(txt), ret))
 +                      cli_txt_list_add(&ifnames, txt);
 +              pos = end + 1;
 +      }
 +}
 +
 +
++static void update_networks(struct wpa_ctrl *ctrl)
++{
++      char buf[4096];
++      size_t len = sizeof(buf);
++      int ret;
++      char *cmd = "LIST_NETWORKS";
++      char *pos, *end;
++      int header = 1;
++
++      cli_txt_list_flush(&networks);
++
++      if (ctrl == NULL)
++              return;
++      ret = wpa_ctrl_request(ctrl, cmd, os_strlen(cmd), buf, &len, NULL);
++      if (ret < 0)
++              return;
++      buf[len] = '\0';
++
++      pos = buf;
++      while (pos) {
++              end = os_strchr(pos, '\n');
++              if (end == NULL)
++                      break;
++              *end = '\0';
++              if (!header)
++                      cli_txt_list_add_word(&networks, pos, '\t');
++              header = 0;
++              pos = end + 1;
++      }
++}
++
++
 +static void try_connection(void *eloop_ctx, void *timeout_ctx)
 +{
 +      if (ctrl_conn)
 +              goto done;
 +
 +      if (ctrl_ifname == NULL)
 +              ctrl_ifname = wpa_cli_get_default_ifname();
 +
 +      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 ? ctrl_ifname : "(nil)");
 +                      warning_displayed = 1;
 +              }
 +              eloop_register_timeout(1, 0, try_connection, NULL, NULL);
 +              return;
 +      }
 +
 +      update_bssid_list(ctrl_conn);
++      update_networks(ctrl_conn);
 +
 +      if (warning_displayed)
 +              printf("Connection established.\n");
 +
 +done:
 +      start_edit();
 +}
 +
 +
 +static void wpa_cli_interactive(void)
 +{
 +      printf("\nInteractive mode\n\n");
 +
 +      eloop_register_timeout(0, 0, try_connection, NULL, NULL);
 +      eloop_run();
 +      eloop_cancel_timeout(try_connection, NULL, NULL);
 +
 +      cli_txt_list_flush(&p2p_peers);
 +      cli_txt_list_flush(&p2p_groups);
 +      cli_txt_list_flush(&bsses);
 +      cli_txt_list_flush(&ifnames);
++      cli_txt_list_flush(&networks);
 +      if (edit_started)
 +              edit_deinit(hfile, wpa_cli_edit_filter_history_cb);
 +      os_free(hfile);
 +      eloop_cancel_timeout(wpa_cli_ping, NULL, NULL);
 +      wpa_cli_close_connection();
 +}
 +
 +
++static void wpa_cli_action_ping(void *eloop_ctx, void *timeout_ctx)
++{
++      struct wpa_ctrl *ctrl = eloop_ctx;
++      char buf[256];
++      size_t len;
++
++      /* verify that connection is still working */
++      len = sizeof(buf) - 1;
++      if (wpa_ctrl_request(ctrl, "PING", 4, buf, &len,
++                           wpa_cli_action_cb) < 0 ||
++          len < 4 || os_memcmp(buf, "PONG", 4) != 0) {
++              printf("wpa_supplicant did not reply to PING command - exiting\n");
++              eloop_terminate();
++              return;
++      }
++      eloop_register_timeout(ping_interval, 0, wpa_cli_action_ping,
++                             ctrl, NULL);
++}
++
++
++static void wpa_cli_action_receive(int sock, void *eloop_ctx, void *sock_ctx)
++{
++      struct wpa_ctrl *ctrl = eloop_ctx;
++
++      wpa_cli_recv_pending(ctrl, 1);
++}
++
++
 +static void wpa_cli_action(struct wpa_ctrl *ctrl)
 +{
 +#ifdef CONFIG_ANSI_C_EXTRA
 +      /* TODO: ANSI C version(?) */
 +      printf("Action processing not supported in ANSI C build.\n");
 +#else /* CONFIG_ANSI_C_EXTRA */
-       while (!wpa_cli_quit) {
-               FD_ZERO(&rfds);
-               FD_SET(fd, &rfds);
-               tv.tv_sec = ping_interval;
-               tv.tv_usec = 0;
-               res = select(fd + 1, &rfds, NULL, NULL, &tv);
-               if (res < 0 && errno != EINTR) {
-                       perror("select");
-                       break;
-               }
-               if (FD_ISSET(fd, &rfds))
-                       wpa_cli_recv_pending(ctrl, 1);
-               else {
-                       /* verify that connection is still working */
-                       len = sizeof(buf) - 1;
-                       if (wpa_ctrl_request(ctrl, "PING", 4, buf, &len,
-                                            wpa_cli_action_cb) < 0 ||
-                           len < 4 || os_memcmp(buf, "PONG", 4) != 0) {
-                               printf("wpa_supplicant did not reply to PING "
-                                      "command - exiting\n");
-                               break;
-                       }
-               }
-       }
++      int fd;
 +
 +      fd = wpa_ctrl_get_fd(ctrl);
- #ifdef ANDROID
-               char ifprop[PROPERTY_VALUE_MAX];
-               if (property_get("wifi.interface", ifprop, NULL) != 0) {
-                       ifname = os_strdup(ifprop);
-                       printf("Using interface '%s'\n", ifname);
-                       return ifname;
-               }
- #endif /* ANDROID */
++      eloop_register_timeout(ping_interval, 0, wpa_cli_action_ping,
++                             ctrl, NULL);
++      eloop_register_read_sock(fd, wpa_cli_action_receive, ctrl, NULL);
++      eloop_run();
++      eloop_cancel_timeout(wpa_cli_action_ping, ctrl, NULL);
++      eloop_unregister_read_sock(fd);
 +#endif /* CONFIG_ANSI_C_EXTRA */
 +}
 +
 +
 +static void wpa_cli_cleanup(void)
 +{
 +      wpa_cli_close_connection();
 +      if (pid_file)
 +              os_daemonize_terminate(pid_file);
 +
 +      os_program_deinit();
 +}
 +
 +
 +static void wpa_cli_terminate(int sig, void *ctx)
 +{
 +      eloop_terminate();
 +}
 +
 +
 +static char * wpa_cli_get_default_ifname(void)
 +{
 +      char *ifname = NULL;
 +
++#ifdef ANDROID
++      char ifprop[PROPERTY_VALUE_MAX];
++      if (property_get("wifi.interface", ifprop, NULL) != 0) {
++              ifname = os_strdup(ifprop);
++              printf("Using interface '%s'\n", ifname ? ifname : "N/A");
++      }
++#else /* ANDROID */
 +#ifdef CONFIG_CTRL_IFACE_UNIX
 +      struct dirent *dent;
 +      DIR *dir = opendir(ctrl_iface_dir);
 +      if (!dir) {
-               c = getopt(argc, argv, "a:Bg:G:hi:p:P:v");
 +              return NULL;
 +      }
 +      while ((dent = readdir(dir))) {
 +#ifdef _DIRENT_HAVE_D_TYPE
 +              /*
 +               * Skip the file if it is not a socket. Also accept
 +               * DT_UNKNOWN (0) in case the C library or underlying
 +               * file system does not support d_type.
 +               */
 +              if (dent->d_type != DT_SOCK && dent->d_type != DT_UNKNOWN)
 +                      continue;
 +#endif /* _DIRENT_HAVE_D_TYPE */
 +              if (os_strcmp(dent->d_name, ".") == 0 ||
 +                  os_strcmp(dent->d_name, "..") == 0)
 +                      continue;
 +              printf("Selected interface '%s'\n", dent->d_name);
 +              ifname = os_strdup(dent->d_name);
 +              break;
 +      }
 +      closedir(dir);
 +#endif /* CONFIG_CTRL_IFACE_UNIX */
 +
 +#ifdef CONFIG_CTRL_IFACE_NAMED_PIPE
 +      char buf[4096], *pos;
 +      size_t len;
 +      struct wpa_ctrl *ctrl;
 +      int ret;
 +
 +      ctrl = wpa_ctrl_open(NULL);
 +      if (ctrl == NULL)
 +              return NULL;
 +
 +      len = sizeof(buf) - 1;
 +      ret = wpa_ctrl_request(ctrl, "INTERFACES", 10, buf, &len, NULL);
 +      if (ret >= 0) {
 +              buf[len] = '\0';
 +              pos = os_strchr(buf, '\n');
 +              if (pos)
 +                      *pos = '\0';
 +              ifname = os_strdup(buf);
 +      }
 +      wpa_ctrl_close(ctrl);
 +#endif /* CONFIG_CTRL_IFACE_NAMED_PIPE */
++#endif /* ANDROID */
 +
 +      return ifname;
 +}
 +
 +
 +int main(int argc, char *argv[])
 +{
 +      int c;
 +      int daemonize = 0;
 +      int ret = 0;
 +      const char *global = NULL;
 +
 +      if (os_program_init())
 +              return -1;
 +
 +      for (;;) {
++              c = getopt(argc, argv, "a:Bg:G:hi:p:P:s:v");
 +              if (c < 0)
 +                      break;
 +              switch (c) {
 +              case 'a':
 +                      action_file = optarg;
 +                      break;
 +              case 'B':
 +                      daemonize = 1;
 +                      break;
 +              case 'g':
 +                      global = optarg;
 +                      break;
 +              case 'G':
 +                      ping_interval = atoi(optarg);
 +                      break;
 +              case 'h':
 +                      usage();
 +                      return 0;
 +              case 'v':
 +                      printf("%s\n", wpa_cli_version);
 +                      return 0;
 +              case 'i':
 +                      os_free(ctrl_ifname);
 +                      ctrl_ifname = os_strdup(optarg);
 +                      break;
 +              case 'p':
 +                      ctrl_iface_dir = optarg;
 +                      break;
 +              case 'P':
 +                      pid_file = optarg;
 +                      break;
++              case 's':
++                      client_socket_dir = optarg;
++                      break;
 +              default:
 +                      usage();
 +                      return -1;
 +              }
 +      }
 +
 +      interactive = (argc == optind) && (action_file == NULL);
 +
 +      if (interactive)
 +              printf("%s\n\n%s\n\n", wpa_cli_version, wpa_cli_license);
 +
 +      if (eloop_init())
 +              return -1;
 +
 +      if (global) {
 +#ifdef CONFIG_CTRL_IFACE_NAMED_PIPE
 +              ctrl_conn = wpa_ctrl_open(NULL);
 +#else /* CONFIG_CTRL_IFACE_NAMED_PIPE */
 +              ctrl_conn = wpa_ctrl_open(global);
 +#endif /* CONFIG_CTRL_IFACE_NAMED_PIPE */
 +              if (ctrl_conn == NULL) {
 +                      fprintf(stderr, "Failed to connect to wpa_supplicant "
 +                              "global interface: %s  error: %s\n",
 +                              global, strerror(errno));
 +                      return -1;
 +              }
 +
 +              if (interactive) {
 +                      update_ifnames(ctrl_conn);
 +                      mon_conn = wpa_ctrl_open(global);
 +                      if (mon_conn) {
 +                              if (wpa_ctrl_attach(mon_conn) == 0) {
 +                                      wpa_cli_attached = 1;
 +                                      eloop_register_read_sock(
 +                                              wpa_ctrl_get_fd(mon_conn),
 +                                              wpa_cli_mon_receive,
 +                                              NULL, NULL);
 +                              } else {
 +                                      printf("Failed to open monitor "
 +                                             "connection through global "
 +                                             "control interface\n");
 +                              }
 +                      }
 +              }
 +      }
 +
 +      eloop_register_signal_terminate(wpa_cli_terminate, NULL);
 +
 +      if (ctrl_ifname == NULL)
 +              ctrl_ifname = wpa_cli_get_default_ifname();
 +
 +      if (interactive) {
 +              wpa_cli_interactive();
 +      } else {
 +              if (!global &&
 +                  wpa_cli_open_connection(ctrl_ifname, 0) < 0) {
 +                      fprintf(stderr, "Failed to connect to non-global "
 +                              "ctrl_ifname: %s  error: %s\n",
 +                              ctrl_ifname ? ctrl_ifname : "(nil)",
 +                              strerror(errno));
 +                      return -1;
 +              }
 +
 +              if (action_file) {
 +                      if (wpa_ctrl_attach(ctrl_conn) == 0) {
 +                              wpa_cli_attached = 1;
 +                      } else {
 +                              printf("Warning: Failed to attach to "
 +                                     "wpa_supplicant.\n");
 +                              return -1;
 +                      }
 +              }
 +
 +              if (daemonize && os_daemonize(pid_file))
 +                      return -1;
 +
 +              if (action_file)
 +                      wpa_cli_action(ctrl_conn);
 +              else
 +                      ret = wpa_request(ctrl_conn, argc - optind,
 +                                        &argv[optind]);
 +      }
 +
 +      os_free(ctrl_ifname);
 +      eloop_destroy();
 +      wpa_cli_cleanup();
 +
 +      return ret;
 +}
 +
 +#else /* CONFIG_CTRL_IFACE */
 +int main(int argc, char *argv[])
 +{
 +      printf("CONFIG_CTRL_IFACE not defined - wpa_cli disabled\n");
 +      return -1;
 +}
 +#endif /* CONFIG_CTRL_IFACE */
index ac38d69d5d1b2e9b890ab79a6005025379e19c48,0000000000000000000000000000000000000000..850ec405b42c28692796b3ff0529ea65af84a5e1
mode 100644,000000..100644
--- /dev/null
@@@ -1,1033 -1,0 +1,1156 @@@
-       struct wpa_driver_ops *driver;
 +/*
 + * WPA Supplicant / privileged helper program
 + * Copyright (c) 2007-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"
 +#ifdef __linux__
 +#include <fcntl.h>
 +#endif /* __linux__ */
 +#include <sys/un.h>
 +#include <sys/stat.h>
 +
 +#include "common.h"
 +#include "eloop.h"
 +#include "common/version.h"
 +#include "drivers/driver.h"
 +#include "l2_packet/l2_packet.h"
 +#include "common/privsep_commands.h"
 +#include "common/ieee802_11_defs.h"
 +
 +
 +struct wpa_priv_interface {
 +      struct wpa_priv_interface *next;
 +      char *driver_name;
 +      char *ifname;
 +      char *sock_name;
 +      int fd;
 +
-       if (iface->driver->init == NULL)
++      const struct wpa_driver_ops *driver;
 +      void *drv_priv;
++      void *drv_global_priv;
 +      struct sockaddr_un drv_addr;
 +      int wpas_registered;
 +
 +      /* TODO: add support for multiple l2 connections */
 +      struct l2_packet_data *l2;
 +      struct sockaddr_un l2_addr;
 +};
 +
 +
 +static void wpa_priv_cmd_register(struct wpa_priv_interface *iface,
 +                                struct sockaddr_un *from)
 +{
 +      if (iface->drv_priv) {
 +              wpa_printf(MSG_DEBUG, "Cleaning up forgotten driver instance");
 +              if (iface->driver->deinit)
 +                      iface->driver->deinit(iface->drv_priv);
 +              iface->drv_priv = NULL;
++              if (iface->drv_global_priv) {
++                      iface->driver->global_deinit(iface->drv_global_priv);
++                      iface->drv_global_priv = NULL;
++              }
 +              iface->wpas_registered = 0;
 +      }
 +
 +      if (iface->l2) {
 +              wpa_printf(MSG_DEBUG, "Cleaning up forgotten l2_packet "
 +                         "instance");
 +              l2_packet_deinit(iface->l2);
 +              iface->l2 = NULL;
 +      }
 +
-       iface->drv_priv = iface->driver->init(iface, iface->ifname);
++      if (iface->driver->init2) {
++              if (iface->driver->global_init) {
++                      iface->drv_global_priv = iface->driver->global_init();
++                      if (!iface->drv_global_priv) {
++                              wpa_printf(MSG_INFO,
++                                         "Failed to initialize driver global context");
++                              return;
++                      }
++              } else {
++                      iface->drv_global_priv = NULL;
++              }
++              iface->drv_priv = iface->driver->init2(iface, iface->ifname,
++                                                     iface->drv_global_priv);
++      } else if (iface->driver->init) {
++              iface->drv_priv = iface->driver->init(iface, iface->ifname);
++      } else {
 +              return;
-       if (assoc->ssid_len > 32)
++      }
 +      if (iface->drv_priv == NULL) {
 +              wpa_printf(MSG_DEBUG, "Failed to initialize driver wrapper");
 +              return;
 +      }
 +
 +      wpa_printf(MSG_DEBUG, "Driver wrapper '%s' initialized for interface "
 +                 "'%s'", iface->driver_name, iface->ifname);
 +
 +      os_memcpy(&iface->drv_addr, from, sizeof(iface->drv_addr));
 +      iface->wpas_registered = 1;
 +
 +      if (iface->driver->set_param &&
 +          iface->driver->set_param(iface->drv_priv, NULL) < 0) {
 +              wpa_printf(MSG_ERROR, "Driver interface rejected param");
 +      }
 +}
 +
 +
 +static void wpa_priv_cmd_unregister(struct wpa_priv_interface *iface,
 +                                  struct sockaddr_un *from)
 +{
 +      if (iface->drv_priv) {
 +              if (iface->driver->deinit)
 +                      iface->driver->deinit(iface->drv_priv);
 +              iface->drv_priv = NULL;
++              if (iface->drv_global_priv) {
++                      iface->driver->global_deinit(iface->drv_global_priv);
++                      iface->drv_global_priv = NULL;
++              }
 +              iface->wpas_registered = 0;
 +      }
 +}
 +
 +
 +static void wpa_priv_cmd_scan(struct wpa_priv_interface *iface,
 +                            char *buf, size_t len)
 +{
 +      struct wpa_driver_scan_params params;
 +
 +      if (iface->drv_priv == NULL)
 +              return;
 +
 +      os_memset(&params, 0, sizeof(params));
 +      if (len) {
 +              params.ssids[0].ssid = (u8 *) buf;
 +              params.ssids[0].ssid_len = len;
 +              params.num_ssids = 1;
 +      }
 +
 +      if (iface->driver->scan2)
 +              iface->driver->scan2(iface->drv_priv, &params);
 +}
 +
 +
 +static void wpa_priv_get_scan_results2(struct wpa_priv_interface *iface,
 +                                     struct sockaddr_un *from)
 +{
 +      struct wpa_scan_results *res;
 +      u8 *buf = NULL, *pos, *end;
 +      int val;
 +      size_t i;
 +
 +      res = iface->driver->get_scan_results2(iface->drv_priv);
 +      if (res == NULL)
 +              goto fail;
 +
 +      buf = os_malloc(60000);
 +      if (buf == NULL)
 +              goto fail;
 +      pos = buf;
 +      end = buf + 60000;
 +      val = res->num;
 +      os_memcpy(pos, &val, sizeof(int));
 +      pos += sizeof(int);
 +
 +      for (i = 0; i < res->num; i++) {
 +              struct wpa_scan_res *r = res->res[i];
 +              val = sizeof(*r) + r->ie_len;
 +              if (end - pos < (int) sizeof(int) + val)
 +                      break;
 +              os_memcpy(pos, &val, sizeof(int));
 +              pos += sizeof(int);
 +              os_memcpy(pos, r, val);
 +              pos += val;
 +      }
 +
 +      sendto(iface->fd, buf, pos - buf, 0, (struct sockaddr *) from,
 +             sizeof(*from));
 +
 +      os_free(buf);
 +      wpa_scan_results_free(res);
 +      return;
 +
 +fail:
 +      os_free(buf);
 +      wpa_scan_results_free(res);
 +      sendto(iface->fd, "", 0, 0, (struct sockaddr *) from, sizeof(*from));
 +}
 +
 +
 +static void wpa_priv_cmd_get_scan_results(struct wpa_priv_interface *iface,
 +                                        struct sockaddr_un *from)
 +{
 +      if (iface->drv_priv == NULL)
 +              return;
 +
 +      if (iface->driver->get_scan_results2)
 +              wpa_priv_get_scan_results2(iface, from);
 +      else
 +              sendto(iface->fd, "", 0, 0, (struct sockaddr *) from,
 +                     sizeof(*from));
 +}
 +
 +
++static void wpa_priv_cmd_authenticate(struct wpa_priv_interface *iface,
++                                    void *buf, size_t len)
++{
++      struct wpa_driver_auth_params params;
++      struct privsep_cmd_authenticate *auth;
++      int res, i;
++
++      if (iface->drv_priv == NULL || iface->driver->authenticate == NULL)
++              return;
++
++      if (len < sizeof(*auth)) {
++              wpa_printf(MSG_DEBUG, "Invalid authentication request");
++              return;
++      }
++
++      auth = buf;
++      if (sizeof(*auth) + auth->ie_len + auth->sae_data_len > len) {
++              wpa_printf(MSG_DEBUG, "Authentication request overflow");
++              return;
++      }
++
++      os_memset(&params, 0, sizeof(params));
++      params.freq = auth->freq;
++      params.bssid = auth->bssid;
++      params.ssid = auth->ssid;
++      if (auth->ssid_len > SSID_MAX_LEN)
++              return;
++      params.ssid_len = auth->ssid_len;
++      params.auth_alg = auth->auth_alg;
++      for (i = 0; i < 4; i++) {
++              if (auth->wep_key_len[i]) {
++                      params.wep_key[i] = auth->wep_key[i];
++                      params.wep_key_len[i] = auth->wep_key_len[i];
++              }
++      }
++      params.wep_tx_keyidx = auth->wep_tx_keyidx;
++      params.local_state_change = auth->local_state_change;
++      params.p2p = auth->p2p;
++      if (auth->ie_len) {
++              params.ie = (u8 *) (auth + 1);
++              params.ie_len = auth->ie_len;
++      }
++      if (auth->sae_data_len) {
++              params.sae_data = ((u8 *) (auth + 1)) + auth->ie_len;
++              params.sae_data_len = auth->sae_data_len;
++      }
++
++      res = iface->driver->authenticate(iface->drv_priv, &params);
++      wpa_printf(MSG_DEBUG, "drv->authenticate: res=%d", res);
++}
++
++
 +static void wpa_priv_cmd_associate(struct wpa_priv_interface *iface,
 +                                 void *buf, size_t len)
 +{
 +      struct wpa_driver_associate_params params;
 +      struct privsep_cmd_associate *assoc;
 +      u8 *bssid;
 +      int res;
 +
 +      if (iface->drv_priv == NULL || iface->driver->associate == NULL)
 +              return;
 +
 +      if (len < sizeof(*assoc)) {
 +              wpa_printf(MSG_DEBUG, "Invalid association request");
 +              return;
 +      }
 +
 +      assoc = buf;
 +      if (sizeof(*assoc) + assoc->wpa_ie_len > len) {
 +              wpa_printf(MSG_DEBUG, "Association request overflow");
 +              return;
 +      }
 +
 +      os_memset(&params, 0, sizeof(params));
 +      bssid = assoc->bssid;
 +      if (bssid[0] | bssid[1] | bssid[2] | bssid[3] | bssid[4] | bssid[5])
 +              params.bssid = bssid;
 +      params.ssid = assoc->ssid;
-       u8 ssid[sizeof(int) + 32];
++      if (assoc->ssid_len > SSID_MAX_LEN)
 +              return;
 +      params.ssid_len = assoc->ssid_len;
 +      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;
 +      }
 +      params.pairwise_suite = assoc->pairwise_suite;
 +      params.group_suite = assoc->group_suite;
 +      params.key_mgmt_suite = assoc->key_mgmt_suite;
 +      params.auth_alg = assoc->auth_alg;
 +      params.mode = assoc->mode;
 +
 +      res = iface->driver->associate(iface->drv_priv, &params);
 +      wpa_printf(MSG_DEBUG, "drv->associate: res=%d", res);
 +}
 +
 +
 +static void wpa_priv_cmd_get_bssid(struct wpa_priv_interface *iface,
 +                                 struct sockaddr_un *from)
 +{
 +      u8 bssid[ETH_ALEN];
 +
 +      if (iface->drv_priv == NULL)
 +              goto fail;
 +
 +      if (iface->driver->get_bssid == NULL ||
 +          iface->driver->get_bssid(iface->drv_priv, bssid) < 0)
 +              goto fail;
 +
 +      sendto(iface->fd, bssid, ETH_ALEN, 0, (struct sockaddr *) from,
 +             sizeof(*from));
 +      return;
 +
 +fail:
 +      sendto(iface->fd, "", 0, 0, (struct sockaddr *) from, sizeof(*from));
 +}
 +
 +
 +static void wpa_priv_cmd_get_ssid(struct wpa_priv_interface *iface,
 +                                struct sockaddr_un *from)
 +{
-       if (res < 0 || res > 32)
++      u8 ssid[sizeof(int) + SSID_MAX_LEN];
 +      int res;
 +
 +      if (iface->drv_priv == NULL)
 +              goto fail;
 +
 +      if (iface->driver->get_ssid == NULL)
 +              goto fail;
 +
 +      res = iface->driver->get_ssid(iface->drv_priv, &ssid[sizeof(int)]);
-       if (proto != ETH_P_EAPOL && proto != ETH_P_RSN_PREAUTH) {
++      if (res < 0 || res > SSID_MAX_LEN)
 +              goto fail;
 +      os_memcpy(ssid, &res, sizeof(int));
 +
 +      sendto(iface->fd, ssid, sizeof(ssid), 0, (struct sockaddr *) from,
 +             sizeof(*from));
 +      return;
 +
 +fail:
 +      sendto(iface->fd, "", 0, 0, (struct sockaddr *) from, sizeof(*from));
 +}
 +
 +
 +static void wpa_priv_cmd_set_key(struct wpa_priv_interface *iface,
 +                               void *buf, size_t len)
 +{
 +      struct privsep_cmd_set_key *params;
 +      int res;
 +
 +      if (iface->drv_priv == NULL || iface->driver->set_key == NULL)
 +              return;
 +
 +      if (len != sizeof(*params)) {
 +              wpa_printf(MSG_DEBUG, "Invalid set_key request");
 +              return;
 +      }
 +
 +      params = buf;
 +
 +      res = iface->driver->set_key(iface->ifname, iface->drv_priv,
 +                                   params->alg,
 +                                   params->addr, params->key_idx,
 +                                   params->set_tx,
 +                                   params->seq_len ? params->seq : NULL,
 +                                   params->seq_len,
 +                                   params->key_len ? params->key : NULL,
 +                                   params->key_len);
 +      wpa_printf(MSG_DEBUG, "drv->set_key: res=%d", res);
 +}
 +
 +
 +static void wpa_priv_cmd_get_capa(struct wpa_priv_interface *iface,
 +                                struct sockaddr_un *from)
 +{
 +      struct wpa_driver_capa capa;
 +
 +      if (iface->drv_priv == NULL)
 +              goto fail;
 +
 +      if (iface->driver->get_capa == NULL ||
 +          iface->driver->get_capa(iface->drv_priv, &capa) < 0)
 +              goto fail;
 +
++      /* For now, no support for passing extended_capa pointers */
++      capa.extended_capa = NULL;
++      capa.extended_capa_mask = NULL;
++      capa.extended_capa_len = 0;
 +      sendto(iface->fd, &capa, sizeof(capa), 0, (struct sockaddr *) from,
 +             sizeof(*from));
 +      return;
 +
 +fail:
 +      sendto(iface->fd, "", 0, 0, (struct sockaddr *) from, sizeof(*from));
 +}
 +
 +
 +static void wpa_priv_l2_rx(void *ctx, const u8 *src_addr, const u8 *buf,
 +                         size_t len)
 +{
 +      struct wpa_priv_interface *iface = ctx;
 +      struct msghdr msg;
 +      struct iovec io[2];
 +
 +      io[0].iov_base = (u8 *) src_addr;
 +      io[0].iov_len = ETH_ALEN;
 +      io[1].iov_base = (u8 *) buf;
 +      io[1].iov_len = len;
 +
 +      os_memset(&msg, 0, sizeof(msg));
 +      msg.msg_iov = io;
 +      msg.msg_iovlen = 2;
 +      msg.msg_name = &iface->l2_addr;
 +      msg.msg_namelen = sizeof(iface->l2_addr);
 +
 +      if (sendmsg(iface->fd, &msg, 0) < 0) {
 +              wpa_printf(MSG_ERROR, "sendmsg(l2 rx): %s", strerror(errno));
 +      }
 +}
 +
 +
 +static void wpa_priv_cmd_l2_register(struct wpa_priv_interface *iface,
 +                                   struct sockaddr_un *from,
 +                                   void *buf, size_t len)
 +{
 +      int *reg_cmd = buf;
 +      u8 own_addr[ETH_ALEN];
 +      int res;
 +      u16 proto;
 +
 +      if (len != 2 * sizeof(int)) {
 +              wpa_printf(MSG_DEBUG, "Invalid l2_register length %lu",
 +                         (unsigned long) len);
 +              return;
 +      }
 +
 +      proto = reg_cmd[0];
-               wpa_printf(MSG_DEBUG, "Unsupported driver event %d - TODO",
-                          event);
++      if (proto != ETH_P_EAPOL && proto != ETH_P_RSN_PREAUTH &&
++          proto != ETH_P_80211_ENCAP) {
 +              wpa_printf(MSG_DEBUG, "Refused l2_packet connection for "
 +                         "ethertype 0x%x", proto);
 +              return;
 +      }
 +
 +      if (iface->l2) {
 +              wpa_printf(MSG_DEBUG, "Cleaning up forgotten l2_packet "
 +                         "instance");
 +              l2_packet_deinit(iface->l2);
 +              iface->l2 = NULL;
 +      }
 +
 +      os_memcpy(&iface->l2_addr, from, sizeof(iface->l2_addr));
 +
 +      iface->l2 = l2_packet_init(iface->ifname, NULL, proto,
 +                                 wpa_priv_l2_rx, iface, reg_cmd[1]);
 +      if (iface->l2 == NULL) {
 +              wpa_printf(MSG_DEBUG, "Failed to initialize l2_packet "
 +                         "instance for protocol %d", proto);
 +              return;
 +      }
 +
 +      if (l2_packet_get_own_addr(iface->l2, own_addr) < 0) {
 +              wpa_printf(MSG_DEBUG, "Failed to get own address from "
 +                         "l2_packet");
 +              l2_packet_deinit(iface->l2);
 +              iface->l2 = NULL;
 +              return;
 +      }
 +
 +      res = sendto(iface->fd, own_addr, ETH_ALEN, 0,
 +                   (struct sockaddr *) from, sizeof(*from));
 +      wpa_printf(MSG_DEBUG, "L2 registration: res=%d", res);
 +}
 +
 +
 +static void wpa_priv_cmd_l2_unregister(struct wpa_priv_interface *iface,
 +                                     struct sockaddr_un *from)
 +{
 +      if (iface->l2) {
 +              l2_packet_deinit(iface->l2);
 +              iface->l2 = NULL;
 +      }
 +}
 +
 +
 +static void wpa_priv_cmd_l2_notify_auth_start(struct wpa_priv_interface *iface,
 +                                            struct sockaddr_un *from)
 +{
 +      if (iface->l2)
 +              l2_packet_notify_auth_start(iface->l2);
 +}
 +
 +
 +static void wpa_priv_cmd_l2_send(struct wpa_priv_interface *iface,
 +                               struct sockaddr_un *from,
 +                               void *buf, size_t len)
 +{
 +      u8 *dst_addr;
 +      u16 proto;
 +      int res;
 +
 +      if (iface->l2 == NULL)
 +              return;
 +
 +      if (len < ETH_ALEN + 2) {
 +              wpa_printf(MSG_DEBUG, "Too short L2 send packet (len=%lu)",
 +                         (unsigned long) len);
 +              return;
 +      }
 +
 +      dst_addr = buf;
 +      os_memcpy(&proto, buf + ETH_ALEN, 2);
 +
 +      if (proto != ETH_P_EAPOL && proto != ETH_P_RSN_PREAUTH) {
 +              wpa_printf(MSG_DEBUG, "Refused l2_packet send for ethertype "
 +                         "0x%x", proto);
 +              return;
 +      }
 +
 +      res = l2_packet_send(iface->l2, dst_addr, proto, buf + ETH_ALEN + 2,
 +                           len - ETH_ALEN - 2);
 +      wpa_printf(MSG_DEBUG, "L2 send: res=%d", res);
 +}
 +
 +
 +static void wpa_priv_cmd_set_country(struct wpa_priv_interface *iface,
 +                                   char *buf)
 +{
 +      if (iface->drv_priv == NULL || iface->driver->set_country == NULL ||
 +          *buf == '\0')
 +              return;
 +
 +      iface->driver->set_country(iface->drv_priv, buf);
 +}
 +
 +
 +static void wpa_priv_receive(int sock, void *eloop_ctx, void *sock_ctx)
 +{
 +      struct wpa_priv_interface *iface = eloop_ctx;
 +      char buf[2000], *pos;
 +      void *cmd_buf;
 +      size_t cmd_len;
 +      int res, cmd;
 +      struct sockaddr_un from;
 +      socklen_t fromlen = sizeof(from);
 +
 +      res = recvfrom(sock, buf, sizeof(buf), 0, (struct sockaddr *) &from,
 +                     &fromlen);
 +      if (res < 0) {
 +              wpa_printf(MSG_ERROR, "recvfrom: %s", strerror(errno));
 +              return;
 +      }
 +
 +      if (res < (int) sizeof(int)) {
 +              wpa_printf(MSG_DEBUG, "Too short command (len=%d)", res);
 +              return;
 +      }
 +
 +      os_memcpy(&cmd, buf, sizeof(int));
 +      wpa_printf(MSG_DEBUG, "Command %d for interface %s",
 +                 cmd, iface->ifname);
 +      cmd_buf = &buf[sizeof(int)];
 +      cmd_len = res - sizeof(int);
 +
 +      switch (cmd) {
 +      case PRIVSEP_CMD_REGISTER:
 +              wpa_priv_cmd_register(iface, &from);
 +              break;
 +      case PRIVSEP_CMD_UNREGISTER:
 +              wpa_priv_cmd_unregister(iface, &from);
 +              break;
 +      case PRIVSEP_CMD_SCAN:
 +              wpa_priv_cmd_scan(iface, cmd_buf, cmd_len);
 +              break;
 +      case PRIVSEP_CMD_GET_SCAN_RESULTS:
 +              wpa_priv_cmd_get_scan_results(iface, &from);
 +              break;
 +      case PRIVSEP_CMD_ASSOCIATE:
 +              wpa_priv_cmd_associate(iface, cmd_buf, cmd_len);
 +              break;
 +      case PRIVSEP_CMD_GET_BSSID:
 +              wpa_priv_cmd_get_bssid(iface, &from);
 +              break;
 +      case PRIVSEP_CMD_GET_SSID:
 +              wpa_priv_cmd_get_ssid(iface, &from);
 +              break;
 +      case PRIVSEP_CMD_SET_KEY:
 +              wpa_priv_cmd_set_key(iface, cmd_buf, cmd_len);
 +              break;
 +      case PRIVSEP_CMD_GET_CAPA:
 +              wpa_priv_cmd_get_capa(iface, &from);
 +              break;
 +      case PRIVSEP_CMD_L2_REGISTER:
 +              wpa_priv_cmd_l2_register(iface, &from, cmd_buf, cmd_len);
 +              break;
 +      case PRIVSEP_CMD_L2_UNREGISTER:
 +              wpa_priv_cmd_l2_unregister(iface, &from);
 +              break;
 +      case PRIVSEP_CMD_L2_NOTIFY_AUTH_START:
 +              wpa_priv_cmd_l2_notify_auth_start(iface, &from);
 +              break;
 +      case PRIVSEP_CMD_L2_SEND:
 +              wpa_priv_cmd_l2_send(iface, &from, cmd_buf, cmd_len);
 +              break;
 +      case PRIVSEP_CMD_SET_COUNTRY:
 +              pos = cmd_buf;
 +              if (pos + cmd_len >= buf + sizeof(buf))
 +                      break;
 +              pos[cmd_len] = '\0';
 +              wpa_priv_cmd_set_country(iface, pos);
 +              break;
++      case PRIVSEP_CMD_AUTHENTICATE:
++              wpa_priv_cmd_authenticate(iface, cmd_buf, cmd_len);
++              break;
 +      }
 +}
 +
 +
 +static void wpa_priv_interface_deinit(struct wpa_priv_interface *iface)
 +{
 +      if (iface->drv_priv && iface->driver->deinit)
 +              iface->driver->deinit(iface->drv_priv);
 +
 +      if (iface->fd >= 0) {
 +              eloop_unregister_read_sock(iface->fd);
 +              close(iface->fd);
 +              unlink(iface->sock_name);
 +      }
 +
 +      if (iface->l2)
 +              l2_packet_deinit(iface->l2);
 +
 +      os_free(iface->ifname);
 +      os_free(iface->driver_name);
 +      os_free(iface->sock_name);
 +      os_free(iface);
 +}
 +
 +
 +static struct wpa_priv_interface *
 +wpa_priv_interface_init(const char *dir, const char *params)
 +{
 +      struct wpa_priv_interface *iface;
 +      char *pos;
 +      size_t len;
 +      struct sockaddr_un addr;
 +      int i;
 +
 +      pos = os_strchr(params, ':');
 +      if (pos == NULL)
 +              return NULL;
 +
 +      iface = os_zalloc(sizeof(*iface));
 +      if (iface == NULL)
 +              return NULL;
 +      iface->fd = -1;
 +
 +      len = pos - params;
 +      iface->driver_name = dup_binstr(params, len);
 +      if (iface->driver_name == NULL) {
 +              wpa_priv_interface_deinit(iface);
 +              return NULL;
 +      }
 +
 +      for (i = 0; wpa_drivers[i]; i++) {
 +              if (os_strcmp(iface->driver_name,
 +                            wpa_drivers[i]->name) == 0) {
 +                      iface->driver = wpa_drivers[i];
 +                      break;
 +              }
 +      }
 +      if (iface->driver == NULL) {
 +              wpa_printf(MSG_ERROR, "Unsupported driver '%s'",
 +                         iface->driver_name);
 +              wpa_priv_interface_deinit(iface);
 +              return NULL;
 +      }
 +
 +      pos++;
 +      iface->ifname = os_strdup(pos);
 +      if (iface->ifname == NULL) {
 +              wpa_priv_interface_deinit(iface);
 +              return NULL;
 +      }
 +
 +      len = os_strlen(dir) + 1 + os_strlen(iface->ifname);
 +      iface->sock_name = os_malloc(len + 1);
 +      if (iface->sock_name == NULL) {
 +              wpa_priv_interface_deinit(iface);
 +              return NULL;
 +      }
 +
 +      os_snprintf(iface->sock_name, len + 1, "%s/%s", dir, iface->ifname);
 +      if (os_strlen(iface->sock_name) >= sizeof(addr.sun_path)) {
 +              wpa_priv_interface_deinit(iface);
 +              return NULL;
 +      }
 +
 +      iface->fd = socket(PF_UNIX, SOCK_DGRAM, 0);
 +      if (iface->fd < 0) {
 +              wpa_printf(MSG_ERROR, "socket(PF_UNIX): %s", strerror(errno));
 +              wpa_priv_interface_deinit(iface);
 +              return NULL;
 +      }
 +
 +      os_memset(&addr, 0, sizeof(addr));
 +      addr.sun_family = AF_UNIX;
 +      os_strlcpy(addr.sun_path, iface->sock_name, sizeof(addr.sun_path));
 +
 +      if (bind(iface->fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
 +              wpa_printf(MSG_DEBUG, "bind(PF_UNIX) failed: %s",
 +                         strerror(errno));
 +              if (connect(iface->fd, (struct sockaddr *) &addr,
 +                          sizeof(addr)) < 0) {
 +                      wpa_printf(MSG_DEBUG, "Socket exists, but does not "
 +                                 "allow connections - assuming it was "
 +                                 "leftover from forced program termination");
 +                      if (unlink(iface->sock_name) < 0) {
 +                              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) {
 +                              wpa_printf(MSG_ERROR,
 +                                         "wpa-priv-iface-init: bind(PF_UNIX): %s",
 +                                         strerror(errno));
 +                              goto fail;
 +                      }
 +                      wpa_printf(MSG_DEBUG, "Successfully replaced leftover "
 +                                 "socket '%s'", iface->sock_name);
 +              } else {
 +                      wpa_printf(MSG_INFO, "Socket exists and seems to be "
 +                                 "in use - cannot override it");
 +                      wpa_printf(MSG_INFO, "Delete '%s' manually if it is "
 +                                 "not used anymore", iface->sock_name);
 +                      goto fail;
 +              }
 +      }
 +
 +      if (chmod(iface->sock_name, S_IRWXU | S_IRWXG | S_IRWXO) < 0) {
 +              wpa_printf(MSG_ERROR, "chmod: %s", strerror(errno));
 +              goto fail;
 +      }
 +
 +      eloop_register_read_sock(iface->fd, wpa_priv_receive, iface, NULL);
 +
 +      return iface;
 +
 +fail:
 +      wpa_priv_interface_deinit(iface);
 +      return NULL;
 +}
 +
 +
 +static int wpa_priv_send_event(struct wpa_priv_interface *iface, int event,
 +                             const void *data, size_t data_len)
 +{
 +      struct msghdr msg;
 +      struct iovec io[2];
 +
 +      io[0].iov_base = &event;
 +      io[0].iov_len = sizeof(event);
 +      io[1].iov_base = (u8 *) data;
 +      io[1].iov_len = data_len;
 +
 +      os_memset(&msg, 0, sizeof(msg));
 +      msg.msg_iov = io;
 +      msg.msg_iovlen = data ? 2 : 1;
 +      msg.msg_name = &iface->drv_addr;
 +      msg.msg_namelen = sizeof(iface->drv_addr);
 +
 +      if (sendmsg(iface->fd, &msg, 0) < 0) {
 +              wpa_printf(MSG_ERROR, "sendmsg(wpas_socket): %s",
 +                         strerror(errno));
 +              return -1;
 +      }
 +
 +      return 0;
 +}
 +
 +
++static void wpa_priv_send_auth(struct wpa_priv_interface *iface,
++                             union wpa_event_data *data)
++{
++      size_t buflen = sizeof(struct privsep_event_auth) + data->auth.ies_len;
++      struct privsep_event_auth *auth;
++      u8 *buf, *pos;
++
++      buf = os_malloc(buflen);
++      if (buf == NULL)
++              return;
++
++      auth = (struct privsep_event_auth *) buf;
++      pos = (u8 *) (auth + 1);
++
++      os_memcpy(auth->peer, data->auth.peer, ETH_ALEN);
++      os_memcpy(auth->bssid, data->auth.bssid, ETH_ALEN);
++      auth->auth_type = data->auth.auth_type;
++      auth->auth_transaction = data->auth.auth_transaction;
++      auth->status_code = data->auth.status_code;
++      if (data->auth.ies) {
++              os_memcpy(pos, data->auth.ies, data->auth.ies_len);
++              auth->ies_len = data->auth.ies_len;
++      }
++
++      wpa_priv_send_event(iface, PRIVSEP_EVENT_AUTH, buf, buflen);
++
++      os_free(buf);
++}
++
++
 +static void wpa_priv_send_assoc(struct wpa_priv_interface *iface, int event,
 +                              union wpa_event_data *data)
 +{
 +      size_t buflen = 3 * sizeof(int);
 +      u8 *buf, *pos;
 +      int len;
 +
 +      if (data) {
 +              buflen += data->assoc_info.req_ies_len +
 +                      data->assoc_info.resp_ies_len +
 +                      data->assoc_info.beacon_ies_len;
 +      }
 +
 +      buf = os_malloc(buflen);
 +      if (buf == NULL)
 +              return;
 +
 +      pos = buf;
 +
 +      if (data && data->assoc_info.req_ies) {
 +              len = data->assoc_info.req_ies_len;
 +              os_memcpy(pos, &len, sizeof(int));
 +              pos += sizeof(int);
 +              os_memcpy(pos, data->assoc_info.req_ies, len);
 +              pos += len;
 +      } else {
 +              len = 0;
 +              os_memcpy(pos, &len, sizeof(int));
 +              pos += sizeof(int);
 +      }
 +
 +      if (data && data->assoc_info.resp_ies) {
 +              len = data->assoc_info.resp_ies_len;
 +              os_memcpy(pos, &len, sizeof(int));
 +              pos += sizeof(int);
 +              os_memcpy(pos, data->assoc_info.resp_ies, len);
 +              pos += len;
 +      } else {
 +              len = 0;
 +              os_memcpy(pos, &len, sizeof(int));
 +              pos += sizeof(int);
 +      }
 +
 +      if (data && data->assoc_info.beacon_ies) {
 +              len = data->assoc_info.beacon_ies_len;
 +              os_memcpy(pos, &len, sizeof(int));
 +              pos += sizeof(int);
 +              os_memcpy(pos, data->assoc_info.beacon_ies, len);
 +              pos += len;
 +      } else {
 +              len = 0;
 +              os_memcpy(pos, &len, sizeof(int));
 +              pos += sizeof(int);
 +      }
 +
 +      wpa_priv_send_event(iface, event, buf, buflen);
 +
 +      os_free(buf);
 +}
 +
 +
 +static void wpa_priv_send_interface_status(struct wpa_priv_interface *iface,
 +                                         union wpa_event_data *data)
 +{
 +      int ievent;
 +      size_t len, maxlen;
 +      u8 *buf;
 +      char *ifname;
 +
 +      if (data == NULL)
 +              return;
 +
 +      ievent = data->interface_status.ievent;
 +      maxlen = sizeof(data->interface_status.ifname);
 +      ifname = data->interface_status.ifname;
 +      for (len = 0; len < maxlen && ifname[len]; len++)
 +              ;
 +
 +      buf = os_malloc(sizeof(int) + len);
 +      if (buf == NULL)
 +              return;
 +
 +      os_memcpy(buf, &ievent, sizeof(int));
 +      os_memcpy(buf + sizeof(int), ifname, len);
 +
 +      wpa_priv_send_event(iface, PRIVSEP_EVENT_INTERFACE_STATUS,
 +                          buf, sizeof(int) + len);
 +
 +      os_free(buf);
 +
 +}
 +
 +
 +static void wpa_priv_send_ft_response(struct wpa_priv_interface *iface,
 +                                    union wpa_event_data *data)
 +{
 +      size_t len;
 +      u8 *buf, *pos;
 +
 +      if (data == NULL || data->ft_ies.ies == NULL)
 +              return;
 +
 +      len = sizeof(int) + ETH_ALEN + data->ft_ies.ies_len;
 +      buf = os_malloc(len);
 +      if (buf == NULL)
 +              return;
 +
 +      pos = buf;
 +      os_memcpy(pos, &data->ft_ies.ft_action, sizeof(int));
 +      pos += sizeof(int);
 +      os_memcpy(pos, data->ft_ies.target_ap, ETH_ALEN);
 +      pos += ETH_ALEN;
 +      os_memcpy(pos, data->ft_ies.ies, data->ft_ies.ies_len);
 +
 +      wpa_priv_send_event(iface, PRIVSEP_EVENT_FT_RESPONSE, buf, len);
 +
 +      os_free(buf);
 +
 +}
 +
 +
 +void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
 +                        union wpa_event_data *data)
 +{
 +      struct wpa_priv_interface *iface = ctx;
 +
 +      wpa_printf(MSG_DEBUG, "%s - event=%d", __func__, event);
 +
 +      if (!iface->wpas_registered) {
 +              wpa_printf(MSG_DEBUG, "Driver event received, but "
 +                         "wpa_supplicant not registered");
 +              return;
 +      }
 +
 +      switch (event) {
 +      case EVENT_ASSOC:
 +              wpa_priv_send_assoc(iface, PRIVSEP_EVENT_ASSOC, data);
 +              break;
 +      case EVENT_DISASSOC:
 +              wpa_priv_send_event(iface, PRIVSEP_EVENT_DISASSOC, NULL, 0);
 +              break;
 +      case EVENT_ASSOCINFO:
 +              if (data == NULL)
 +                      return;
 +              wpa_priv_send_assoc(iface, PRIVSEP_EVENT_ASSOCINFO, data);
 +              break;
 +      case EVENT_MICHAEL_MIC_FAILURE:
 +              if (data == NULL)
 +                      return;
 +              wpa_priv_send_event(iface, PRIVSEP_EVENT_MICHAEL_MIC_FAILURE,
 +                                  &data->michael_mic_failure.unicast,
 +                                  sizeof(int));
 +              break;
++      case EVENT_SCAN_STARTED:
++              wpa_priv_send_event(iface, PRIVSEP_EVENT_SCAN_STARTED, NULL,
++                                  0);
++              break;
 +      case EVENT_SCAN_RESULTS:
 +              wpa_priv_send_event(iface, PRIVSEP_EVENT_SCAN_RESULTS, NULL,
 +                                  0);
 +              break;
 +      case EVENT_INTERFACE_STATUS:
 +              wpa_priv_send_interface_status(iface, data);
 +              break;
 +      case EVENT_PMKID_CANDIDATE:
 +              if (data == NULL)
 +                      return;
 +              wpa_priv_send_event(iface, PRIVSEP_EVENT_PMKID_CANDIDATE,
 +                                  &data->pmkid_candidate,
 +                                  sizeof(struct pmkid_candidate));
 +              break;
 +      case EVENT_STKSTART:
 +              if (data == NULL)
 +                      return;
 +              wpa_priv_send_event(iface, PRIVSEP_EVENT_STKSTART,
 +                                  &data->stkstart.peer, ETH_ALEN);
 +              break;
 +      case EVENT_FT_RESPONSE:
 +              wpa_priv_send_ft_response(iface, data);
 +              break;
++      case EVENT_AUTH:
++              wpa_priv_send_auth(iface, data);
++              break;
 +      default:
-              "  wpa_priv [-Bdd] [-P<pid file>] <driver:ifname> "
-              "[driver:ifname ...]\n");
++              wpa_printf(MSG_DEBUG, "Unsupported driver event %d (%s) - TODO",
++                         event, event_to_string(event));
 +              break;
 +      }
 +}
 +
 +
 +void wpa_supplicant_rx_eapol(void *ctx, const u8 *src_addr,
 +                           const u8 *buf, size_t len)
 +{
 +      struct wpa_priv_interface *iface = ctx;
 +      struct msghdr msg;
 +      struct iovec io[3];
 +      int event = PRIVSEP_EVENT_RX_EAPOL;
 +
 +      wpa_printf(MSG_DEBUG, "RX EAPOL from driver");
 +      io[0].iov_base = &event;
 +      io[0].iov_len = sizeof(event);
 +      io[1].iov_base = (u8 *) src_addr;
 +      io[1].iov_len = ETH_ALEN;
 +      io[2].iov_base = (u8 *) buf;
 +      io[2].iov_len = len;
 +
 +      os_memset(&msg, 0, sizeof(msg));
 +      msg.msg_iov = io;
 +      msg.msg_iovlen = 3;
 +      msg.msg_name = &iface->drv_addr;
 +      msg.msg_namelen = sizeof(iface->drv_addr);
 +
 +      if (sendmsg(iface->fd, &msg, 0) < 0)
 +              wpa_printf(MSG_ERROR, "sendmsg(wpas_socket): %s",
 +                         strerror(errno));
 +}
 +
 +
 +static void wpa_priv_terminate(int sig, void *signal_ctx)
 +{
 +      wpa_printf(MSG_DEBUG, "wpa_priv termination requested");
 +      eloop_terminate();
 +}
 +
 +
 +static void wpa_priv_fd_workaround(void)
 +{
 +#ifdef __linux__
 +      int s, i;
 +      /* When started from pcmcia-cs scripts, wpa_supplicant might start with
 +       * fd 0, 1, and 2 closed. This will cause some issues because many
 +       * places in wpa_supplicant are still printing out to stdout. As a
 +       * workaround, make sure that fd's 0, 1, and 2 are not used for other
 +       * sockets. */
 +      for (i = 0; i < 3; i++) {
 +              s = open("/dev/null", O_RDWR);
 +              if (s > 2) {
 +                      close(s);
 +                      break;
 +              }
 +      }
 +#endif /* __linux__ */
 +}
 +
 +
 +static void usage(void)
 +{
 +      printf("wpa_priv v" VERSION_STR "\n"
 +             "Copyright (c) 2007-2009, Jouni Malinen <j@w1.fi> and "
 +             "contributors\n"
 +             "\n"
 +             "usage:\n"
-                       goto out;
++             "  wpa_priv [-Bdd] [-c<ctrl dir>] [-P<pid file>] "
++             "<driver:ifname> \\\n"
++             "           [driver:ifname ...]\n");
 +}
 +
 +
 +int main(int argc, char *argv[])
 +{
 +      int c, i;
 +      int ret = -1;
 +      char *pid_file = NULL;
 +      int daemonize = 0;
 +      char *ctrl_dir = "/var/run/wpa_priv";
 +      struct wpa_priv_interface *interfaces = NULL, *iface;
 +
 +      if (os_program_init())
 +              return -1;
 +
 +      wpa_priv_fd_workaround();
 +
 +      for (;;) {
 +              c = getopt(argc, argv, "Bc:dP:");
 +              if (c < 0)
 +                      break;
 +              switch (c) {
 +              case 'B':
 +                      daemonize++;
 +                      break;
 +              case 'c':
 +                      ctrl_dir = optarg;
 +                      break;
 +              case 'd':
 +                      wpa_debug_level--;
 +                      break;
 +              case 'P':
 +                      pid_file = os_rel2abs_path(optarg);
 +                      break;
 +              default:
 +                      usage();
-               goto out;
++                      goto out2;
 +              }
 +      }
 +
 +      if (optind >= argc) {
 +              usage();
-               goto out;
++              goto out2;
 +      }
 +
 +      wpa_printf(MSG_DEBUG, "wpa_priv control directory: '%s'", ctrl_dir);
 +
 +      if (eloop_init()) {
 +              wpa_printf(MSG_ERROR, "Failed to initialize event loop");
-       os_daemonize_terminate(pid_file);
++              goto out2;
 +      }
 +
 +      for (i = optind; i < argc; i++) {
 +              wpa_printf(MSG_DEBUG, "Adding driver:interface %s", argv[i]);
 +              iface = wpa_priv_interface_init(ctrl_dir, argv[i]);
 +              if (iface == NULL)
 +                      goto out;
 +              iface->next = interfaces;
 +              interfaces = iface;
 +      }
 +
 +      if (daemonize && os_daemonize(pid_file))
 +              goto out;
 +
 +      eloop_register_signal_terminate(wpa_priv_terminate, NULL);
 +      eloop_run();
 +
 +      ret = 0;
 +
 +out:
 +      iface = interfaces;
 +      while (iface) {
 +              struct wpa_priv_interface *prev = iface;
 +              iface = iface->next;
 +              wpa_priv_interface_deinit(prev);
 +      }
 +
 +      eloop_destroy();
 +
++out2:
++      if (daemonize)
++              os_daemonize_terminate(pid_file);
 +      os_free(pid_file);
 +      os_program_deinit();
 +
 +      return ret;
 +}
index 19fb8900bd756620321adeb398bc6db1e2403bef,0000000000000000000000000000000000000000..ef55fdcf79c086685fc93494639f1fecab15a58e
mode 100644,000000..100644
--- /dev/null
@@@ -1,5545 -1,0 +1,5870 @@@
- const char *wpa_supplicant_version =
 +/*
 + * WPA Supplicant
 + * 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.
 + *
 + * This file implements functions for registering and unregistering
 + * %wpa_supplicant interfaces. In addition, this file contains number of
 + * functions for managing network connections.
 + */
 +
 +#include "includes.h"
 +
 +#include "common.h"
 +#include "crypto/random.h"
 +#include "crypto/sha1.h"
 +#include "eapol_supp/eapol_supp_sm.h"
 +#include "eap_peer/eap.h"
 +#include "eap_peer/eap_proxy.h"
 +#include "eap_server/eap_methods.h"
 +#include "rsn_supp/wpa.h"
 +#include "eloop.h"
 +#include "config.h"
 +#include "utils/ext_password.h"
 +#include "l2_packet/l2_packet.h"
 +#include "wpa_supplicant_i.h"
 +#include "driver_i.h"
 +#include "ctrl_iface.h"
 +#include "pcsc_funcs.h"
 +#include "common/version.h"
 +#include "rsn_supp/preauth.h"
 +#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 "fst/fst.h"
 +#include "blacklist.h"
 +#include "wpas_glue.h"
 +#include "wps_supplicant.h"
 +#include "ibss_rsn.h"
 +#include "sme.h"
 +#include "gas_query.h"
 +#include "ap.h"
 +#include "p2p_supplicant.h"
 +#include "wifi_display.h"
 +#include "notify.h"
 +#include "bgscan.h"
 +#include "autoscan.h"
 +#include "bss.h"
 +#include "scan.h"
 +#include "offchannel.h"
 +#include "hs20_supplicant.h"
 +#include "wnm_sta.h"
 +#include "wpas_kay.h"
 +#include "mesh.h"
 +
- const char *wpa_supplicant_license =
++const char *const wpa_supplicant_version =
 +"wpa_supplicant v" VERSION_STR "\n"
 +"Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi> and contributors";
 +
- const char *wpa_supplicant_full_license1 =
++const char *const wpa_supplicant_license =
 +"This software may be distributed under the terms of the BSD license.\n"
 +"See README for more details.\n"
 +#ifdef EAP_TLS_OPENSSL
 +"\nThis product includes software developed by the OpenSSL Project\n"
 +"for use in the OpenSSL Toolkit (http://www.openssl.org/)\n"
 +#endif /* EAP_TLS_OPENSSL */
 +;
 +
 +#ifndef CONFIG_NO_STDOUT_DEBUG
 +/* Long text divided into parts in order to fit in C89 strings size limits. */
- const char *wpa_supplicant_full_license2 =
++const char *const wpa_supplicant_full_license1 =
 +"";
- const char *wpa_supplicant_full_license3 =
++const char *const wpa_supplicant_full_license2 =
 +"This software may be distributed under the terms of the BSD license.\n"
 +"\n"
 +"Redistribution and use in source and binary forms, with or without\n"
 +"modification, are permitted provided that the following conditions are\n"
 +"met:\n"
 +"\n";
- const char *wpa_supplicant_full_license4 =
++const char *const wpa_supplicant_full_license3 =
 +"1. Redistributions of source code must retain the above copyright\n"
 +"   notice, this list of conditions and the following disclaimer.\n"
 +"\n"
 +"2. Redistributions in binary form must reproduce the above copyright\n"
 +"   notice, this list of conditions and the following disclaimer in the\n"
 +"   documentation and/or other materials provided with the distribution.\n"
 +"\n";
- const char *wpa_supplicant_full_license5 =
++const char *const wpa_supplicant_full_license4 =
 +"3. Neither the name(s) of the above-listed copyright holder(s) nor the\n"
 +"   names of its contributors may be used to endorse or promote products\n"
 +"   derived from this software without specific prior written permission.\n"
 +"\n"
 +"THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n"
 +"\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n"
 +"LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n"
 +"A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n";
-               wpa_s->own_disconnect_req = 1;
++const char *const wpa_supplicant_full_license5 =
 +"OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n"
 +"SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n"
 +"LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n"
 +"DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n"
 +"THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n"
 +"(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n"
 +"OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
 +"\n";
 +#endif /* CONFIG_NO_STDOUT_DEBUG */
 +
 +/* Configure default/group WEP keys for static WEP */
 +int wpa_set_wep_keys(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid)
 +{
 +      int i, set = 0;
 +
 +      for (i = 0; i < NUM_WEP_KEYS; i++) {
 +              if (ssid->wep_key_len[i] == 0)
 +                      continue;
 +
 +              set = 1;
 +              wpa_drv_set_key(wpa_s, WPA_ALG_WEP, NULL,
 +                              i, i == ssid->wep_tx_keyidx, NULL, 0,
 +                              ssid->wep_key[i], ssid->wep_key_len[i]);
 +      }
 +
 +      return set;
 +}
 +
 +
 +int wpa_supplicant_set_wpa_none_key(struct wpa_supplicant *wpa_s,
 +                                  struct wpa_ssid *ssid)
 +{
 +      u8 key[32];
 +      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. */
 +
 +      if (ssid->mode != WPAS_MODE_IBSS) {
 +              wpa_msg(wpa_s, MSG_INFO, "WPA: Invalid mode %d (not "
 +                      "IBSS/ad-hoc) for WPA-None", ssid->mode);
 +              return -1;
 +      }
 +
 +      if (!ssid->psk_set) {
 +              wpa_msg(wpa_s, MSG_INFO, "WPA: No PSK configured for "
 +                      "WPA-None");
 +              return -1;
 +      }
 +
 +      switch (wpa_s->group_cipher) {
 +      case WPA_CIPHER_CCMP:
 +              os_memcpy(key, ssid->psk, 16);
 +              keylen = 16;
 +              alg = WPA_ALG_CCMP;
 +              break;
 +      case WPA_CIPHER_GCMP:
 +              os_memcpy(key, ssid->psk, 16);
 +              keylen = 16;
 +              alg = WPA_ALG_GCMP;
 +              break;
 +      case WPA_CIPHER_TKIP:
 +              /* WPA-None uses the same Michael MIC key for both TX and RX */
 +              os_memcpy(key, ssid->psk, 16 + 8);
 +              os_memcpy(key + 16 + 8, ssid->psk + 16, 8);
 +              keylen = 32;
 +              alg = WPA_ALG_TKIP;
 +              break;
 +      default:
 +              wpa_msg(wpa_s, MSG_INFO, "WPA: Invalid group cipher %d for "
 +                      "WPA-None", wpa_s->group_cipher);
 +              return -1;
 +      }
 +
 +      /* TODO: should actually remember the previously used seq#, both for TX
 +       * and RX from each STA.. */
 +
 +      ret = wpa_drv_set_key(wpa_s, alg, NULL, 0, 1, seq, 6, key, keylen);
 +      os_memset(key, 0, sizeof(key));
 +      return ret;
 +}
 +
 +
 +static void wpa_supplicant_timeout(void *eloop_ctx, void *timeout_ctx)
 +{
 +      struct wpa_supplicant *wpa_s = eloop_ctx;
 +      const u8 *bssid = wpa_s->bssid;
 +      if (is_zero_ether_addr(bssid))
 +              bssid = wpa_s->pending_bssid;
 +      wpa_msg(wpa_s, MSG_INFO, "Authentication with " MACSTR " timed out.",
 +              MAC2STR(bssid));
 +      wpa_blacklist_add(wpa_s, bssid);
 +      wpa_sm_notify_disassoc(wpa_s->wpa);
 +      wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING);
 +      wpa_s->reassociate = 1;
 +
 +      /*
 +       * If we timed out, the AP or the local radio may be busy.
 +       * So, wait a second until scanning again.
 +       */
 +      wpa_supplicant_req_scan(wpa_s, 1, 0);
 +}
 +
 +
 +/**
 + * wpa_supplicant_req_auth_timeout - Schedule a timeout for authentication
 + * @wpa_s: Pointer to wpa_supplicant data
 + * @sec: Number of seconds after which to time out authentication
 + * @usec: Number of microseconds after which to time out authentication
 + *
 + * This function is used to schedule a timeout for the current authentication
 + * attempt.
 + */
 +void wpa_supplicant_req_auth_timeout(struct wpa_supplicant *wpa_s,
 +                                   int sec, int usec)
 +{
 +      if (wpa_s->conf->ap_scan == 0 &&
 +          (wpa_s->drv_flags & WPA_DRIVER_FLAGS_WIRED))
 +              return;
 +
 +      wpa_dbg(wpa_s, MSG_DEBUG, "Setting authentication timeout: %d sec "
 +              "%d usec", sec, usec);
 +      eloop_cancel_timeout(wpa_supplicant_timeout, wpa_s, NULL);
 +      eloop_register_timeout(sec, usec, wpa_supplicant_timeout, wpa_s, NULL);
 +}
 +
 +
 +/**
 + * wpa_supplicant_cancel_auth_timeout - Cancel authentication timeout
 + * @wpa_s: Pointer to wpa_supplicant data
 + *
 + * This function is used to cancel authentication timeout scheduled with
 + * wpa_supplicant_req_auth_timeout() and it is called when authentication has
 + * been completed.
 + */
 +void wpa_supplicant_cancel_auth_timeout(struct wpa_supplicant *wpa_s)
 +{
 +      wpa_dbg(wpa_s, MSG_DEBUG, "Cancelling authentication timeout");
 +      eloop_cancel_timeout(wpa_supplicant_timeout, wpa_s, NULL);
 +      wpa_blacklist_del(wpa_s, wpa_s->bssid);
 +}
 +
 +
 +/**
 + * wpa_supplicant_initiate_eapol - Configure EAPOL state machine
 + * @wpa_s: Pointer to wpa_supplicant data
 + *
 + * This function is used to configure EAPOL state machine based on the selected
 + * authentication mode.
 + */
 +void wpa_supplicant_initiate_eapol(struct wpa_supplicant *wpa_s)
 +{
 +#ifdef IEEE8021X_EAPOL
 +      struct eapol_config eapol_conf;
 +      struct wpa_ssid *ssid = wpa_s->current_ssid;
 +
 +#ifdef CONFIG_IBSS_RSN
 +      if (ssid->mode == WPAS_MODE_IBSS &&
 +          wpa_s->key_mgmt != WPA_KEY_MGMT_NONE &&
 +          wpa_s->key_mgmt != WPA_KEY_MGMT_WPA_NONE) {
 +              /*
 +               * RSN IBSS authentication is per-STA and we can disable the
 +               * per-BSSID EAPOL authentication.
 +               */
 +              eapol_sm_notify_portControl(wpa_s->eapol, ForceAuthorized);
 +              eapol_sm_notify_eap_success(wpa_s->eapol, TRUE);
 +              eapol_sm_notify_eap_fail(wpa_s->eapol, FALSE);
 +              return;
 +      }
 +#endif /* CONFIG_IBSS_RSN */
 +
 +      eapol_sm_notify_eap_success(wpa_s->eapol, FALSE);
 +      eapol_sm_notify_eap_fail(wpa_s->eapol, FALSE);
 +
 +      if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE ||
 +          wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE)
 +              eapol_sm_notify_portControl(wpa_s->eapol, ForceAuthorized);
 +      else
 +              eapol_sm_notify_portControl(wpa_s->eapol, Auto);
 +
 +      os_memset(&eapol_conf, 0, sizeof(eapol_conf));
 +      if (wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) {
 +              eapol_conf.accept_802_1x_keys = 1;
 +              eapol_conf.required_keys = 0;
 +              if (ssid->eapol_flags & EAPOL_FLAG_REQUIRE_KEY_UNICAST) {
 +                      eapol_conf.required_keys |= EAPOL_REQUIRE_KEY_UNICAST;
 +              }
 +              if (ssid->eapol_flags & EAPOL_FLAG_REQUIRE_KEY_BROADCAST) {
 +                      eapol_conf.required_keys |=
 +                              EAPOL_REQUIRE_KEY_BROADCAST;
 +              }
 +
 +              if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_WIRED)
 +                      eapol_conf.required_keys = 0;
 +      }
 +      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 */
 +}
 +
 +
 +/**
 + * wpa_supplicant_set_non_wpa_policy - Set WPA parameters to non-WPA mode
 + * @wpa_s: Pointer to wpa_supplicant data
 + * @ssid: Configuration data for the network
 + *
 + * This function is used to configure WPA state machine and related parameters
 + * to a mode where WPA is not enabled. This is called as part of the
 + * authentication configuration when the selected network does not use WPA.
 + */
 +void wpa_supplicant_set_non_wpa_policy(struct wpa_supplicant *wpa_s,
 +                                     struct wpa_ssid *ssid)
 +{
 +      int i;
 +
 +      if (ssid->key_mgmt & WPA_KEY_MGMT_WPS)
 +              wpa_s->key_mgmt = WPA_KEY_MGMT_WPS;
 +      else if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA)
 +              wpa_s->key_mgmt = WPA_KEY_MGMT_IEEE8021X_NO_WPA;
 +      else
 +              wpa_s->key_mgmt = WPA_KEY_MGMT_NONE;
 +      wpa_sm_set_ap_wpa_ie(wpa_s->wpa, NULL, 0);
 +      wpa_sm_set_ap_rsn_ie(wpa_s->wpa, NULL, 0);
 +      wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, NULL, 0);
 +      wpa_s->pairwise_cipher = WPA_CIPHER_NONE;
 +      wpa_s->group_cipher = WPA_CIPHER_NONE;
 +      wpa_s->mgmt_group_cipher = 0;
 +
 +      for (i = 0; i < NUM_WEP_KEYS; i++) {
 +              if (ssid->wep_key_len[i] > 5) {
 +                      wpa_s->pairwise_cipher = WPA_CIPHER_WEP104;
 +                      wpa_s->group_cipher = WPA_CIPHER_WEP104;
 +                      break;
 +              } else if (ssid->wep_key_len[i] > 0) {
 +                      wpa_s->pairwise_cipher = WPA_CIPHER_WEP40;
 +                      wpa_s->group_cipher = WPA_CIPHER_WEP40;
 +                      break;
 +              }
 +      }
 +
 +      wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_RSN_ENABLED, 0);
 +      wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_KEY_MGMT, wpa_s->key_mgmt);
 +      wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_PAIRWISE,
 +                       wpa_s->pairwise_cipher);
 +      wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_GROUP, wpa_s->group_cipher);
 +#ifdef CONFIG_IEEE80211W
 +      wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_MGMT_GROUP,
 +                       wpa_s->mgmt_group_cipher);
 +#endif /* CONFIG_IEEE80211W */
 +
 +      pmksa_cache_clear_current(wpa_s->wpa);
 +}
 +
 +
 +void free_hw_features(struct wpa_supplicant *wpa_s)
 +{
 +      int i;
 +      if (wpa_s->hw.modes == NULL)
 +              return;
 +
 +      for (i = 0; i < wpa_s->hw.num_modes; i++) {
 +              os_free(wpa_s->hw.modes[i].channels);
 +              os_free(wpa_s->hw.modes[i].rates);
 +      }
 +
 +      os_free(wpa_s->hw.modes);
 +      wpa_s->hw.modes = NULL;
 +}
 +
 +
 +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);
 +      wpa_s->scard = NULL;
 +      wpa_sm_set_scard_ctx(wpa_s->wpa, NULL);
 +      eapol_sm_register_scard_ctx(wpa_s->eapol, NULL);
 +      l2_packet_deinit(wpa_s->l2);
 +      wpa_s->l2 = NULL;
 +      if (wpa_s->l2_br) {
 +              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;
 +              for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next)
 +                      wpas_notify_network_removed(wpa_s, ssid);
 +      }
 +
 +      os_free(wpa_s->confname);
 +      wpa_s->confname = NULL;
 +
 +      os_free(wpa_s->confanother);
 +      wpa_s->confanother = NULL;
 +
 +      wpa_sm_set_eapol(wpa_s->wpa, NULL);
 +      eapol_sm_deinit(wpa_s->eapol);
 +      wpa_s->eapol = NULL;
 +
 +      rsn_preauth_deinit(wpa_s->wpa);
 +
 +#ifdef CONFIG_TDLS
 +      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;
 +      wpa_blacklist_clear(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);
 +#ifdef CONFIG_DELAYED_MIC_ERROR_REPORT
 +      eloop_cancel_timeout(wpa_supplicant_delayed_mic_error_report,
 +                           wpa_s, NULL);
 +#endif /* CONFIG_DELAYED_MIC_ERROR_REPORT */
 +
++      eloop_cancel_timeout(wpas_network_reenabled, wpa_s, NULL);
++
 +      wpas_wps_deinit(wpa_s);
 +
 +      wpabuf_free(wpa_s->pending_eapol_rx);
 +      wpa_s->pending_eapol_rx = NULL;
 +
 +#ifdef CONFIG_IBSS_RSN
 +      ibss_rsn_deinit(wpa_s->ibss_rsn);
 +      wpa_s->ibss_rsn = NULL;
 +#endif /* CONFIG_IBSS_RSN */
 +
 +      sme_deinit(wpa_s);
 +
 +#ifdef CONFIG_AP
 +      wpa_supplicant_ap_deinit(wpa_s);
 +#endif /* CONFIG_AP */
 +
 +      wpas_p2p_deinit(wpa_s);
 +
 +#ifdef CONFIG_OFFCHANNEL
 +      offchannel_deinit(wpa_s);
 +#endif /* CONFIG_OFFCHANNEL */
 +
 +      wpa_supplicant_cancel_sched_scan(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);
 +
++      /*
++       * Need to remove any pending gas-query radio work before the
++       * gas_query_deinit() call because gas_query::work has not yet been set
++       * for works that have not been started. gas_query_free() will be unable
++       * to cancel such pending radio works and once the pending gas-query
++       * radio work eventually gets removed, the deinit notification call to
++       * gas_query_start_cb() would result in dereferencing freed memory.
++       */
++      if (wpa_s->radio)
++              radio_remove_works(wpa_s, "gas-query", 0);
 +      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;
 +
 +      os_free(wpa_s->disallow_aps_bssid);
 +      wpa_s->disallow_aps_bssid = NULL;
 +      os_free(wpa_s->disallow_aps_ssid);
 +      wpa_s->disallow_aps_ssid = NULL;
 +
 +      wnm_bss_keep_alive_deinit(wpa_s);
 +#ifdef CONFIG_WNM
 +      wnm_deallocate_memory(wpa_s);
 +#endif /* CONFIG_WNM */
 +
 +      ext_password_deinit(wpa_s->ext_pw);
 +      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);
 +}
 +
 +
 +/**
 + * wpa_clear_keys - Clear keys configured for the driver
 + * @wpa_s: Pointer to wpa_supplicant data
 + * @addr: Previously used BSSID or %NULL if not available
 + *
 + * This function clears the encryption keys that has been previously configured
 + * for the driver.
 + */
 +void wpa_clear_keys(struct wpa_supplicant *wpa_s, const u8 *addr)
 +{
 +      int i, max;
 +
 +#ifdef CONFIG_IEEE80211W
 +      max = 6;
 +#else /* CONFIG_IEEE80211W */
 +      max = 4;
 +#endif /* CONFIG_IEEE80211W */
 +
 +      /* 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) */
 +              wpa_drv_mlme_setprotection(
 +                      wpa_s, addr,
 +                      MLME_SETPROTECTION_PROTECT_TYPE_NONE,
 +                      MLME_SETPROTECTION_KEY_TYPE_PAIRWISE);
 +      }
 +      wpa_s->keys_cleared = (u32) -1;
 +}
 +
 +
 +/**
 + * wpa_supplicant_state_txt - Get the connection state name as a text string
 + * @state: State (wpa_state; WPA_*)
 + * Returns: The state name as a printable text string
 + */
 +const char * wpa_supplicant_state_txt(enum wpa_states state)
 +{
 +      switch (state) {
 +      case WPA_DISCONNECTED:
 +              return "DISCONNECTED";
 +      case WPA_INACTIVE:
 +              return "INACTIVE";
 +      case WPA_INTERFACE_DISABLED:
 +              return "INTERFACE_DISABLED";
 +      case WPA_SCANNING:
 +              return "SCANNING";
 +      case WPA_AUTHENTICATING:
 +              return "AUTHENTICATING";
 +      case WPA_ASSOCIATING:
 +              return "ASSOCIATING";
 +      case WPA_ASSOCIATED:
 +              return "ASSOCIATED";
 +      case WPA_4WAY_HANDSHAKE:
 +              return "4WAY_HANDSHAKE";
 +      case WPA_GROUP_HANDSHAKE:
 +              return "GROUP_HANDSHAKE";
 +      case WPA_COMPLETED:
 +              return "COMPLETED";
 +      default:
 +              return "UNKNOWN";
 +      }
 +}
 +
 +
 +#ifdef CONFIG_BGSCAN
 +
 +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) {
 +              if (bgscan_init(wpa_s, wpa_s->current_ssid, name)) {
 +                      wpa_dbg(wpa_s, MSG_DEBUG, "Failed to initialize "
 +                              "bgscan");
 +                      /*
 +                       * Live without bgscan; it is only used as a roaming
 +                       * optimization, so the initial connection is not
 +                       * affected.
 +                       */
 +              } else {
 +                      struct wpa_scan_results *scan_res;
 +                      wpa_s->bgscan_ssid = wpa_s->current_ssid;
 +                      scan_res = wpa_supplicant_get_scan_results(wpa_s, NULL,
 +                                                                 0);
 +                      if (scan_res) {
 +                              bgscan_notify_scan(wpa_s, scan_res);
 +                              wpa_scan_results_free(scan_res);
 +                      }
 +              }
 +      } else
 +              wpa_s->bgscan_ssid = NULL;
 +}
 +
 +
 +static void wpa_supplicant_stop_bgscan(struct wpa_supplicant *wpa_s)
 +{
 +      if (wpa_s->bgscan_ssid != NULL) {
 +              bgscan_deinit(wpa_s);
 +              wpa_s->bgscan_ssid = NULL;
 +      }
 +}
 +
 +#endif /* CONFIG_BGSCAN */
 +
 +
 +static void wpa_supplicant_start_autoscan(struct wpa_supplicant *wpa_s)
 +{
 +      if (autoscan_init(wpa_s, 0))
 +              wpa_dbg(wpa_s, MSG_DEBUG, "Failed to initialize autoscan");
 +}
 +
 +
 +static void wpa_supplicant_stop_autoscan(struct wpa_supplicant *wpa_s)
 +{
 +      autoscan_deinit(wpa_s);
 +}
 +
 +
 +void wpa_supplicant_reinit_autoscan(struct wpa_supplicant *wpa_s)
 +{
 +      if (wpa_s->wpa_state == WPA_DISCONNECTED ||
 +          wpa_s->wpa_state == WPA_SCANNING) {
 +              autoscan_deinit(wpa_s);
 +              wpa_supplicant_start_autoscan(wpa_s);
 +      }
 +}
 +
 +
 +/**
 + * wpa_supplicant_set_state - Set current connection state
 + * @wpa_s: Pointer to wpa_supplicant data
 + * @state: The new connection state
 + *
 + * This function is called whenever the connection state changes, e.g.,
 + * association is completed for WPA/WPA2 4-Way Handshake is started.
 + */
 +void wpa_supplicant_set_state(struct wpa_supplicant *wpa_s,
 +                            enum wpa_states state)
 +{
 +      enum wpa_states old_state = wpa_s->wpa_state;
 +
 +      wpa_dbg(wpa_s, MSG_DEBUG, "State: %s -> %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;
 +      }
 +
++#ifdef CONFIG_P2P
++      /*
++       * P2PS client has to reply to Probe Request frames received on the
++       * group operating channel. Enable Probe Request frame reporting for
++       * P2P connected client in case p2p_cli_probe configuration property is
++       * set to 1.
++       */
++      if (wpa_s->conf->p2p_cli_probe && wpa_s->current_ssid &&
++          wpa_s->current_ssid->mode == WPAS_MODE_INFRA &&
++          wpa_s->current_ssid->p2p_group) {
++              if (state == WPA_COMPLETED && !wpa_s->p2p_cli_probe) {
++                      wpa_dbg(wpa_s, MSG_DEBUG,
++                              "P2P: Enable CLI Probe Request RX reporting");
++                      wpa_s->p2p_cli_probe =
++                              wpa_drv_probe_req_report(wpa_s, 1) >= 0;
++              } else if (state != WPA_COMPLETED && wpa_s->p2p_cli_probe) {
++                      wpa_dbg(wpa_s, MSG_DEBUG,
++                              "P2P: Disable CLI Probe Request RX reporting");
++                      wpa_s->p2p_cli_probe = 0;
++                      wpa_drv_probe_req_report(wpa_s, 0);
++              }
++      }
++#endif /* CONFIG_P2P */
++
 +      if (state != WPA_SCANNING)
 +              wpa_supplicant_notify_scanning(wpa_s, 0);
 +
 +      if (state == WPA_COMPLETED && wpa_s->new_connection) {
 +              struct wpa_ssid *ssid = wpa_s->current_ssid;
 +#if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG)
 +              wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_CONNECTED "- Connection to "
 +                      MACSTR " completed [id=%d id_str=%s]",
 +                      MAC2STR(wpa_s->bssid),
 +                      ssid ? ssid->id : -1,
 +                      ssid && ssid->id_str ? ssid->id_str : "");
 +#endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */
 +              wpas_clear_temp_disabled(wpa_s, ssid, 1);
++              wpa_blacklist_clear(wpa_s);
 +              wpa_s->extra_blacklist_count = 0;
 +              wpa_s->new_connection = 0;
 +              wpa_drv_set_operstate(wpa_s, 1);
 +#ifndef IEEE8021X_EAPOL
 +              wpa_drv_set_supp_port(wpa_s, 1);
 +#endif /* IEEE8021X_EAPOL */
 +              wpa_s->after_wps = 0;
 +              wpa_s->known_wps_freq = 0;
 +              wpas_p2p_completed(wpa_s);
 +
 +              sme_sched_obss_scan(wpa_s, 1);
 +      } else if (state == WPA_DISCONNECTED || state == WPA_ASSOCIATING ||
 +                 state == WPA_ASSOCIATED) {
 +              wpa_s->new_connection = 1;
 +              wpa_drv_set_operstate(wpa_s, 0);
 +#ifndef IEEE8021X_EAPOL
 +              wpa_drv_set_supp_port(wpa_s, 0);
 +#endif /* IEEE8021X_EAPOL */
 +              sme_sched_obss_scan(wpa_s, 0);
 +      }
 +      wpa_s->wpa_state = state;
 +
 +#ifdef CONFIG_BGSCAN
 +      if (state == WPA_COMPLETED)
 +              wpa_supplicant_start_bgscan(wpa_s);
 +      else if (state < WPA_ASSOCIATED)
 +              wpa_supplicant_stop_bgscan(wpa_s);
 +#endif /* CONFIG_BGSCAN */
 +
 +      if (state == WPA_AUTHENTICATING)
 +              wpa_supplicant_stop_autoscan(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);
 +      }
 +}
 +
 +
 +void wpa_supplicant_terminate_proc(struct wpa_global *global)
 +{
 +      int pending = 0;
 +#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;
 +#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)
 +              return;
 +      eloop_terminate();
 +}
 +
 +
 +static void wpa_supplicant_terminate(int sig, void *signal_ctx)
 +{
 +      struct wpa_global *global = signal_ctx;
 +      wpa_supplicant_terminate_proc(global);
 +}
 +
 +
 +void wpa_supplicant_clear_status(struct wpa_supplicant *wpa_s)
 +{
 +      enum wpa_states old_state = wpa_s->wpa_state;
 +
 +      wpa_s->pairwise_cipher = 0;
 +      wpa_s->group_cipher = 0;
 +      wpa_s->mgmt_group_cipher = 0;
 +      wpa_s->key_mgmt = 0;
 +      if (wpa_s->wpa_state != WPA_INTERFACE_DISABLED)
 +              wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
 +
 +      if (wpa_s->wpa_state != old_state)
 +              wpas_notify_state_changed(wpa_s, wpa_s->wpa_state, old_state);
 +}
 +
 +
 +/**
 + * wpa_supplicant_reload_configuration - Reload configuration data
 + * @wpa_s: Pointer to wpa_supplicant data
 + * Returns: 0 on success or -1 if configuration parsing failed
 + *
 + * This function can be used to request that the configuration data is reloaded
 + * (e.g., after configuration file change). This function is reloading
 + * configuration only for one interface, so this may need to be called multiple
 + * times if %wpa_supplicant is controlling multiple interfaces and all
 + * interfaces need reconfiguration.
 + */
 +int wpa_supplicant_reload_configuration(struct wpa_supplicant *wpa_s)
 +{
 +      struct wpa_config *conf;
 +      int reconf_ctrl;
 +      int old_ap_scan;
 +
 +      if (wpa_s->confname == NULL)
 +              return -1;
 +      conf = wpa_config_read(wpa_s->confname, NULL);
 +      if (conf == NULL) {
 +              wpa_msg(wpa_s, MSG_ERROR, "Failed to parse the configuration "
 +                      "file '%s' - exiting", wpa_s->confname);
 +              return -1;
 +      }
 +      wpa_config_read(wpa_s->confanother, conf);
 +
 +      conf->changed_parameters = (unsigned int) -1;
 +
 +      reconf_ctrl = !!conf->ctrl_interface != !!wpa_s->conf->ctrl_interface
 +              || (conf->ctrl_interface && wpa_s->conf->ctrl_interface &&
 +                  os_strcmp(conf->ctrl_interface,
 +                            wpa_s->conf->ctrl_interface) != 0);
 +
 +      if (reconf_ctrl && wpa_s->ctrl_iface) {
 +              wpa_supplicant_ctrl_iface_deinit(wpa_s->ctrl_iface);
 +              wpa_s->ctrl_iface = NULL;
 +      }
 +
 +      eapol_sm_invalidate_cached_session(wpa_s->eapol);
 +      if (wpa_s->current_ssid) {
-               wpa_sm_set_pmk(wpa_s->wpa, ssid->psk, PMK_LEN, NULL);
++              if (wpa_s->wpa_state >= WPA_AUTHENTICATING)
++                      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, openssl_ciphers.
 +       */
 +      if (wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt)) {
 +              /*
 +               * Clear forced success to clear EAP state for next
 +               * authentication.
 +               */
 +              eapol_sm_notify_eap_success(wpa_s->eapol, FALSE);
 +      }
 +      eapol_sm_notify_config(wpa_s->eapol, NULL, NULL);
 +      wpa_sm_set_config(wpa_s->wpa, NULL);
 +      wpa_sm_pmksa_cache_flush(wpa_s->wpa, NULL);
 +      wpa_sm_set_fast_reauth(wpa_s->wpa, wpa_s->conf->fast_reauth);
 +      rsn_preauth_deinit(wpa_s->wpa);
 +
 +      old_ap_scan = wpa_s->conf->ap_scan;
 +      wpa_config_free(wpa_s->conf);
 +      wpa_s->conf = conf;
 +      if (old_ap_scan != wpa_s->conf->ap_scan)
 +              wpas_notify_ap_scan_changed(wpa_s);
 +
 +      if (reconf_ctrl)
 +              wpa_s->ctrl_iface = wpa_supplicant_ctrl_iface_init(wpa_s);
 +
 +      wpa_supplicant_update_config(wpa_s);
 +
 +      wpa_supplicant_clear_status(wpa_s);
 +      if (wpa_supplicant_enabled_networks(wpa_s)) {
 +              wpa_s->reassociate = 1;
 +              wpa_supplicant_req_scan(wpa_s, 0, 0);
 +      }
 +      wpa_dbg(wpa_s, MSG_DEBUG, "Reconfiguration completed");
 +      return 0;
 +}
 +
 +
 +static void wpa_supplicant_reconfig(int sig, void *signal_ctx)
 +{
 +      struct wpa_global *global = signal_ctx;
 +      struct wpa_supplicant *wpa_s;
 +      for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
 +              wpa_dbg(wpa_s, MSG_DEBUG, "Signal %d received - reconfiguring",
 +                      sig);
 +              if (wpa_supplicant_reload_configuration(wpa_s) < 0) {
 +                      wpa_supplicant_terminate_proc(global);
 +              }
 +      }
 +}
 +
 +
 +static int wpa_supplicant_suites_from_ai(struct wpa_supplicant *wpa_s,
 +                                       struct wpa_ssid *ssid,
 +                                       struct wpa_ie_data *ie)
 +{
 +      int ret = wpa_sm_parse_own_wpa_ie(wpa_s->wpa, ie);
 +      if (ret) {
 +              if (ret == -2) {
 +                      wpa_msg(wpa_s, MSG_INFO, "WPA: Failed to parse WPA IE "
 +                              "from association info");
 +              }
 +              return -1;
 +      }
 +
 +      wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Using WPA IE from AssocReq to set "
 +              "cipher suites");
 +      if (!(ie->group_cipher & ssid->group_cipher)) {
 +              wpa_msg(wpa_s, MSG_INFO, "WPA: Driver used disabled group "
 +                      "cipher 0x%x (mask 0x%x) - reject",
 +                      ie->group_cipher, ssid->group_cipher);
 +              return -1;
 +      }
 +      if (!(ie->pairwise_cipher & ssid->pairwise_cipher)) {
 +              wpa_msg(wpa_s, MSG_INFO, "WPA: Driver used disabled pairwise "
 +                      "cipher 0x%x (mask 0x%x) - reject",
 +                      ie->pairwise_cipher, ssid->pairwise_cipher);
 +              return -1;
 +      }
 +      if (!(ie->key_mgmt & ssid->key_mgmt)) {
 +              wpa_msg(wpa_s, MSG_INFO, "WPA: Driver used disabled key "
 +                      "management 0x%x (mask 0x%x) - reject",
 +                      ie->key_mgmt, ssid->key_mgmt);
 +              return -1;
 +      }
 +
 +#ifdef CONFIG_IEEE80211W
 +      if (!(ie->capabilities & WPA_CAPABILITY_MFPC) &&
 +          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");
 +              return -1;
 +      }
 +#endif /* CONFIG_IEEE80211W */
 +
 +      return 0;
 +}
 +
 +
 +/**
 + * wpa_supplicant_set_suites - Set authentication and encryption parameters
 + * @wpa_s: Pointer to wpa_supplicant data
 + * @bss: Scan results for the selected BSS, or %NULL if not available
 + * @ssid: Configuration data for the selected network
 + * @wpa_ie: Buffer for the WPA/RSN IE
 + * @wpa_ie_len: Maximum wpa_ie buffer size on input. This is changed to be the
 + * used buffer length in case the functions returns success.
 + * Returns: 0 on success or -1 on failure
 + *
 + * This function is used to configure authentication and encryption parameters
 + * based on the network configuration and scan result for the selected BSS (if
 + * available).
 + */
 +int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s,
 +                            struct wpa_bss *bss, struct wpa_ssid *ssid,
 +                            u8 *wpa_ie, size_t *wpa_ie_len)
 +{
 +      struct wpa_ie_data ie;
 +      int sel, proto;
 +      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 = bss_osen = NULL;
 +
 +      if (bss_rsn && (ssid->proto & WPA_PROTO_RSN) &&
 +          wpa_parse_wpa_ie(bss_rsn, 2 + bss_rsn[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, "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 &&
 +                 (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_OSEN)
 +                      proto = WPA_PROTO_OSEN;
 +              else if (ssid->proto & WPA_PROTO_RSN)
 +                      proto = WPA_PROTO_RSN;
 +              else
 +                      proto = WPA_PROTO_WPA;
 +              if (wpa_supplicant_suites_from_ai(wpa_s, ssid, &ie) < 0) {
 +                      os_memset(&ie, 0, sizeof(ie));
 +                      ie.group_cipher = ssid->group_cipher;
 +                      ie.pairwise_cipher = ssid->pairwise_cipher;
 +                      ie.key_mgmt = ssid->key_mgmt;
 +#ifdef CONFIG_IEEE80211W
 +                      ie.mgmt_group_cipher =
 +                              ssid->ieee80211w != NO_MGMT_FRAME_PROTECTION ?
 +                              WPA_CIPHER_AES_128_CMAC : 0;
 +#endif /* CONFIG_IEEE80211W */
 +                      wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Set cipher suites "
 +                              "based on configuration");
 +              } else
 +                      proto = ie.proto;
 +      }
 +
 +      wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Selected cipher suites: group %d "
 +              "pairwise %d key_mgmt %d proto %d",
 +              ie.group_cipher, ie.pairwise_cipher, ie.key_mgmt, proto);
 +#ifdef CONFIG_IEEE80211W
 +      if (ssid->ieee80211w) {
 +              wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Selected mgmt group cipher %d",
 +                      ie.mgmt_group_cipher);
 +      }
 +#endif /* CONFIG_IEEE80211W */
 +
 +      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 | WPA_PROTO_OSEN)));
 +
 +      if (bss || !wpa_s->ap_ies_from_associnfo) {
 +              if (wpa_sm_set_ap_wpa_ie(wpa_s->wpa, bss_wpa,
 +                                       bss_wpa ? 2 + bss_wpa[1] : 0) ||
 +                  wpa_sm_set_ap_rsn_ie(wpa_s->wpa, bss_rsn,
 +                                       bss_rsn ? 2 + bss_rsn[1] : 0))
 +                      return -1;
 +      }
 +
 +      sel = ie.group_cipher & ssid->group_cipher;
 +      wpa_s->group_cipher = wpa_pick_group_cipher(sel);
 +      if (wpa_s->group_cipher < 0) {
 +              wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to select group "
 +                      "cipher");
 +              return -1;
 +      }
 +      wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using GTK %s",
 +              wpa_cipher_txt(wpa_s->group_cipher));
 +
 +      sel = ie.pairwise_cipher & ssid->pairwise_cipher;
 +      wpa_s->pairwise_cipher = wpa_pick_pairwise_cipher(sel, 1);
 +      if (wpa_s->pairwise_cipher < 0) {
 +              wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to select pairwise "
 +                      "cipher");
 +              return -1;
 +      }
 +      wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using PTK %s",
 +              wpa_cipher_txt(wpa_s->pairwise_cipher));
 +
 +      sel = ie.key_mgmt & ssid->key_mgmt;
 +#ifdef CONFIG_SAE
 +      if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SAE))
 +              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;
 +              wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT FT/802.1X");
 +      } else if (sel & WPA_KEY_MGMT_FT_PSK) {
 +              wpa_s->key_mgmt = WPA_KEY_MGMT_FT_PSK;
 +              wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT FT/PSK");
 +#endif /* CONFIG_IEEE80211R */
 +#ifdef CONFIG_SAE
 +      } else if (sel & WPA_KEY_MGMT_SAE) {
 +              wpa_s->key_mgmt = WPA_KEY_MGMT_SAE;
 +              wpa_dbg(wpa_s, MSG_DEBUG, "RSN: using KEY_MGMT SAE");
 +      } else if (sel & WPA_KEY_MGMT_FT_SAE) {
 +              wpa_s->key_mgmt = WPA_KEY_MGMT_FT_SAE;
 +              wpa_dbg(wpa_s, MSG_DEBUG, "RSN: using KEY_MGMT FT/SAE");
 +#endif /* CONFIG_SAE */
 +#ifdef CONFIG_IEEE80211W
 +      } else if (sel & WPA_KEY_MGMT_IEEE8021X_SHA256) {
 +              wpa_s->key_mgmt = WPA_KEY_MGMT_IEEE8021X_SHA256;
 +              wpa_dbg(wpa_s, MSG_DEBUG,
 +                      "WPA: using KEY_MGMT 802.1X with SHA256");
 +      } else if (sel & WPA_KEY_MGMT_PSK_SHA256) {
 +              wpa_s->key_mgmt = WPA_KEY_MGMT_PSK_SHA256;
 +              wpa_dbg(wpa_s, MSG_DEBUG,
 +                      "WPA: using KEY_MGMT PSK with SHA256");
 +#endif /* CONFIG_IEEE80211W */
 +      } else if (sel & WPA_KEY_MGMT_IEEE8021X) {
 +              wpa_s->key_mgmt = WPA_KEY_MGMT_IEEE8021X;
 +              wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT 802.1X");
 +      } else if (sel & WPA_KEY_MGMT_PSK) {
 +              wpa_s->key_mgmt = WPA_KEY_MGMT_PSK;
 +              wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT WPA-PSK");
 +      } 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");
 +              return -1;
 +      }
 +
 +      wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_KEY_MGMT, wpa_s->key_mgmt);
 +      wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_PAIRWISE,
 +                       wpa_s->pairwise_cipher);
 +      wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_GROUP, wpa_s->group_cipher);
 +
 +#ifdef CONFIG_IEEE80211W
 +      sel = ie.mgmt_group_cipher;
 +      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");
 +      }
 +      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,
 +                       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)) {
 +              wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to generate WPA IE");
 +              return -1;
 +      }
 +
 +      if (wpa_key_mgmt_wpa_psk(ssid->key_mgmt)) {
-       if (cwork->bss_removed || !wpas_valid_bss_ssid(wpa_s, bss, ssid)) {
++              int psk_set = 0;
++
++              if (ssid->psk_set) {
++                      wpa_sm_set_pmk(wpa_s->wpa, ssid->psk, PMK_LEN, NULL);
++                      psk_set = 1;
++              }
 +#ifndef CONFIG_NO_PBKDF2
 +              if (bss && ssid->bssid_set && ssid->ssid_len == 0 &&
 +                  ssid->passphrase) {
 +                      u8 psk[PMK_LEN];
 +                      pbkdf2_sha1(ssid->passphrase, bss->ssid, bss->ssid_len,
 +                                  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, NULL);
++                      psk_set = 1;
 +                      os_memset(psk, 0, sizeof(psk));
 +              }
 +#endif /* CONFIG_NO_PBKDF2 */
 +#ifdef CONFIG_EXT_PASSWORD
 +              if (ssid->ext_psk) {
 +                      struct wpabuf *pw = ext_password_get(wpa_s->ext_pw,
 +                                                           ssid->ext_psk);
 +                      char pw_str[64 + 1];
 +                      u8 psk[PMK_LEN];
 +
 +                      if (pw == NULL) {
 +                              wpa_msg(wpa_s, MSG_INFO, "EXT PW: No PSK "
 +                                      "found from external storage");
 +                              return -1;
 +                      }
 +
 +                      if (wpabuf_len(pw) < 8 || wpabuf_len(pw) > 64) {
 +                              wpa_msg(wpa_s, MSG_INFO, "EXT PW: Unexpected "
 +                                      "PSK length %d in external storage",
 +                                      (int) wpabuf_len(pw));
 +                              ext_password_free(pw);
 +                              return -1;
 +                      }
 +
 +                      os_memcpy(pw_str, wpabuf_head(pw), wpabuf_len(pw));
 +                      pw_str[wpabuf_len(pw)] = '\0';
 +
 +#ifndef CONFIG_NO_PBKDF2
 +                      if (wpabuf_len(pw) >= 8 && wpabuf_len(pw) < 64 && bss)
 +                      {
 +                              pbkdf2_sha1(pw_str, bss->ssid, bss->ssid_len,
 +                                          4096, psk, PMK_LEN);
 +                              os_memset(pw_str, 0, sizeof(pw_str));
 +                              wpa_hexdump_key(MSG_MSGDUMP, "PSK (from "
 +                                              "external passphrase)",
 +                                              psk, PMK_LEN);
 +                              wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN, NULL);
++                              psk_set = 1;
 +                              os_memset(psk, 0, sizeof(psk));
 +                      } else
 +#endif /* CONFIG_NO_PBKDF2 */
 +                      if (wpabuf_len(pw) == 2 * PMK_LEN) {
 +                              if (hexstr2bin(pw_str, psk, PMK_LEN) < 0) {
 +                                      wpa_msg(wpa_s, MSG_INFO, "EXT PW: "
 +                                              "Invalid PSK hex string");
 +                                      os_memset(pw_str, 0, sizeof(pw_str));
 +                                      ext_password_free(pw);
 +                                      return -1;
 +                              }
 +                              wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN, NULL);
++                              psk_set = 1;
 +                              os_memset(psk, 0, sizeof(psk));
 +                      } else {
 +                              wpa_msg(wpa_s, MSG_INFO, "EXT PW: No suitable "
 +                                      "PSK available");
 +                              os_memset(pw_str, 0, sizeof(pw_str));
 +                              ext_password_free(pw);
 +                              return -1;
 +                      }
 +
 +                      os_memset(pw_str, 0, sizeof(pw_str));
 +                      ext_password_free(pw);
 +              }
 +#endif /* CONFIG_EXT_PASSWORD */
++
++              if (!psk_set) {
++                      wpa_msg(wpa_s, MSG_INFO,
++                              "No PSK available for association");
++                      return -1;
++              }
 +      } else
 +              wpa_sm_set_pmk_from_pmksa(wpa_s->wpa);
 +
 +      return 0;
 +}
 +
 +
 +static void wpas_ext_capab_byte(struct wpa_supplicant *wpa_s, u8 *pos, int idx)
 +{
 +      *pos = 0x00;
 +
 +      switch (idx) {
 +      case 0: /* Bits 0-7 */
 +              break;
 +      case 1: /* Bits 8-15 */
 +              break;
 +      case 2: /* Bits 16-23 */
 +#ifdef CONFIG_WNM
 +              *pos |= 0x02; /* Bit 17 - WNM-Sleep Mode */
 +              *pos |= 0x08; /* Bit 19 - BSS Transition */
 +#endif /* CONFIG_WNM */
 +              break;
 +      case 3: /* Bits 24-31 */
 +#ifdef CONFIG_WNM
 +              *pos |= 0x02; /* Bit 25 - SSID List */
 +#endif /* CONFIG_WNM */
 +#ifdef CONFIG_INTERWORKING
 +              if (wpa_s->conf->interworking)
 +                      *pos |= 0x80; /* Bit 31 - Interworking */
 +#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;
 +      }
 +}
 +
 +
 +int wpas_build_ext_capab(struct wpa_supplicant *wpa_s, u8 *buf, size_t buflen)
 +{
 +      u8 *pos = buf;
 +      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;
 +      for (i = 0; i < len; i++, pos++) {
 +              wpas_ext_capab_byte(wpa_s, pos, i);
 +
 +              if (i < wpa_s->extended_capa_len) {
 +                      *pos &= ~wpa_s->extended_capa_mask[i];
 +                      *pos |= wpa_s->extended_capa[i];
 +              }
 +      }
 +
 +      while (len > 0 && buf[1 + len] == 0) {
 +              len--;
 +              buf[1] = len;
 +      }
 +      if (len == 0)
 +              return 0;
 +
 +      return 2 + len;
 +}
 +
 +
 +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
 + * @bss: Scan results for the selected BSS, or %NULL if not available
 + * @ssid: Configuration data for the selected network
 + *
 + * This function is used to request %wpa_supplicant to associate with a BSS.
 + */
 +void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,
 +                            struct wpa_bss *bss, struct wpa_ssid *ssid)
 +{
 +      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);
 +      wpa_s->ibss_rsn = NULL;
 +#endif /* CONFIG_IBSS_RSN */
 +
 +      if (ssid->mode == WPAS_MODE_AP || ssid->mode == WPAS_MODE_P2P_GO ||
 +          ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION) {
 +#ifdef CONFIG_AP
 +              if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_AP)) {
 +                      wpa_msg(wpa_s, MSG_INFO, "Driver does not support AP "
 +                              "mode");
 +                      return;
 +              }
 +              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;
 +#else /* CONFIG_AP */
 +              wpa_msg(wpa_s, MSG_ERROR, "AP mode support not included in "
 +                      "the build");
 +#endif /* CONFIG_AP */
 +              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),
 +                              bss->ie_len);
 +#endif /* CONFIG_TDLS */
 +
 +      if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) &&
 +          ssid->mode == IEEE80211_MODE_INFRA) {
 +              sme_authenticate(wpa_s, bss, ssid);
 +              return;
 +      }
 +
 +      if (wpa_s->connect_work) {
 +              wpa_dbg(wpa_s, MSG_DEBUG, "Reject wpa_supplicant_associate() call since connect_work exist");
 +              return;
 +      }
 +
 +      if (radio_work_pending(wpa_s, "connect")) {
 +              wpa_dbg(wpa_s, MSG_DEBUG, "Reject wpa_supplicant_associate() call since pending work exist");
 +              return;
 +      }
 +
 +      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);
 +      }
 +}
 +
 +
 +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;
 +
-       wpa_supplicant_cancel_sched_scan(wpa_s);
++      if (cwork->bss_removed || !wpas_valid_bss_ssid(wpa_s, bss, ssid) ||
++          wpas_network_disabled(wpa_s, 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_s->current_bss = bss;
++      if (!wpa_s->pno)
++              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 &&
 +                 wpa_key_mgmt_wpa_ieee8021x(ssid->key_mgmt)) {
 +              /*
 +               * Both WPA and non-WPA IEEE 802.1X enabled in configuration -
 +               * use non-WPA since the scan results did not indicate that the
 +               * AP is using WPA or WPA2.
 +               */
 +              wpa_supplicant_set_non_wpa_policy(wpa_s, ssid);
 +              wpa_ie_len = 0;
 +              wpa_s->wpa_proto = 0;
 +      } else if (wpa_key_mgmt_wpa_any(ssid->key_mgmt)) {
 +              wpa_ie_len = sizeof(wpa_ie);
 +              if (wpa_supplicant_set_suites(wpa_s, NULL, ssid,
 +                                            wpa_ie, &wpa_ie_len)) {
 +                      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
 +      } else if (ssid->key_mgmt & WPA_KEY_MGMT_WPS) {
 +              struct wpabuf *wps_ie;
 +              wps_ie = wps_build_assoc_req_ie(wpas_wps_get_req_type(ssid));
 +              if (wps_ie && wpabuf_len(wps_ie) <= sizeof(wpa_ie)) {
 +                      wpa_ie_len = wpabuf_len(wps_ie);
 +                      os_memcpy(wpa_ie, wpabuf_head(wps_ie), wpa_ie_len);
 +              } else
 +                      wpa_ie_len = 0;
 +              wpabuf_free(wps_ie);
 +              wpa_supplicant_set_non_wpa_policy(wpa_s, ssid);
 +              if (!bss || (bss->caps & IEEE80211_CAP_PRIVACY))
 +                      params.wps = WPS_MODE_PRIVACY;
 +              else
 +                      params.wps = WPS_MODE_OPEN;
 +              wpa_s->wpa_proto = 0;
 +#endif /* CONFIG_WPS */
 +      } else {
 +              wpa_supplicant_set_non_wpa_policy(wpa_s, ssid);
 +              wpa_ie_len = 0;
 +              wpa_s->wpa_proto = 0;
 +      }
 +
 +#ifdef CONFIG_P2P
 +      if (wpa_s->global->p2p) {
 +              u8 *pos;
 +              size_t len;
 +              int res;
 +              pos = wpa_ie + wpa_ie_len;
 +              len = sizeof(wpa_ie) - wpa_ie_len;
 +              res = wpas_p2p_assoc_req_ie(wpa_s, bss, pos, len,
 +                                          ssid->p2p_group);
 +              if (res >= 0)
 +                      wpa_ie_len += res;
 +      }
 +
 +      wpa_s->cross_connect_disallowed = 0;
 +      if (bss) {
 +              struct wpabuf *p2p;
 +              p2p = wpa_bss_get_vendor_ie_multi(bss, P2P_IE_VENDOR_TYPE);
 +              if (p2p) {
 +                      wpa_s->cross_connect_disallowed =
 +                              p2p_get_cross_connect_disallowed(p2p);
 +                      wpabuf_free(p2p);
 +                      wpa_dbg(wpa_s, MSG_DEBUG, "P2P: WLAN AP %s cross "
 +                              "connection",
 +                              wpa_s->cross_connect_disallowed ?
 +                              "disallows" : "allows");
 +              }
 +      }
 +
 +      os_memset(wpa_s->p2p_ip_addr_info, 0, sizeof(wpa_s->p2p_ip_addr_info));
 +#endif /* CONFIG_P2P */
 +
 +#ifdef CONFIG_HS20
 +      if (is_hs20_network(wpa_s, ssid, bss)) {
 +              struct wpabuf *hs20;
 +              hs20 = wpabuf_alloc(20);
 +              if (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 */
 +
 +      /*
 +       * 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);
 +              }
 +      }
 +
++#ifdef CONFIG_FST
++      if (wpa_s->fst_ies) {
++              int fst_ies_len = wpabuf_len(wpa_s->fst_ies);
++
++              if (wpa_ie_len + fst_ies_len <= sizeof(wpa_ie)) {
++                      os_memcpy(wpa_ie + wpa_ie_len,
++                                wpabuf_head(wpa_s->fst_ies), fst_ies_len);
++                      wpa_ie_len += fst_ies_len;
++              }
++      }
++#endif /* CONFIG_FST */
++
 +      wpa_clear_keys(wpa_s, bss ? bss->bssid : NULL);
 +      use_crypt = 1;
 +      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)
 +                      use_crypt = 0;
 +              if (wpa_set_wep_keys(wpa_s, ssid)) {
 +                      use_crypt = 1;
 +                      wep_keys_set = 1;
 +              }
 +      }
 +      if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPS)
 +              use_crypt = 0;
 +
 +#ifdef IEEE8021X_EAPOL
 +      if (wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) {
 +              if ((ssid->eapol_flags &
 +                   (EAPOL_FLAG_REQUIRE_KEY_UNICAST |
 +                    EAPOL_FLAG_REQUIRE_KEY_BROADCAST)) == 0 &&
 +                  !wep_keys_set) {
 +                      use_crypt = 0;
 +              } else {
 +                      /* Assume that dynamic WEP-104 keys will be used and
 +                       * set cipher suites in order for drivers to expect
 +                       * encryption. */
 +                      cipher_pairwise = cipher_group = WPA_CIPHER_WEP104;
 +              }
 +      }
 +#endif /* IEEE8021X_EAPOL */
 +
 +      if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE) {
 +              /* Set the key before (and later after) association */
 +              wpa_supplicant_set_wpa_none_key(wpa_s, ssid);
 +      }
 +
 +      wpa_supplicant_set_state(wpa_s, WPA_ASSOCIATING);
 +      if (bss) {
 +              params.ssid = bss->ssid;
 +              params.ssid_len = bss->ssid_len;
 +              if (!wpas_driver_bss_selection(wpa_s) || ssid->bssid_set) {
 +                      wpa_printf(MSG_DEBUG, "Limit connection to BSSID "
 +                                 MACSTR " freq=%u MHz based on scan results "
 +                                 "(bssid_set=%d)",
 +                                 MAC2STR(bss->bssid), bss->freq,
 +                                 ssid->bssid_set);
 +                      params.bssid = bss->bssid;
 +                      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;
 +      }
 +
 +      if (ssid->mode == WPAS_MODE_IBSS && ssid->bssid_set &&
 +          wpa_s->conf->ap_scan == 2) {
 +              params.bssid = ssid->bssid;
 +              params.fixed_bssid = 1;
 +      }
 +
 +      /* 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 = wpa_s->key_mgmt;
 +      params.wpa_proto = wpa_s->wpa_proto;
 +      params.auth_alg = algs;
 +      params.mode = ssid->mode;
 +      params.bg_scan_period = ssid->bg_scan_period;
 +      for (i = 0; i < NUM_WEP_KEYS; i++) {
 +              if (ssid->wep_key_len[i])
 +                      params.wep_key[i] = ssid->wep_key[i];
 +              params.wep_key_len[i] = ssid->wep_key_len[i];
 +      }
 +      params.wep_tx_keyidx = ssid->wep_tx_keyidx;
 +
 +      if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE) &&
 +          (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 = 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;
 +              if (rsn && wpa_parse_wpa_ie(rsn, 2 + rsn[1], &ie) == 0 &&
 +                  ie.capabilities &
 +                  (WPA_CAPABILITY_MFPC | WPA_CAPABILITY_MFPR)) {
 +                      wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Selected AP supports "
 +                              "MFP: require MFP");
 +                      params.mgmt_frame_protection =
 +                              MGMT_FRAME_PROTECTION_REQUIRED;
 +              }
 +      }
 +#endif /* CONFIG_IEEE80211W */
 +
 +      params.p2p = ssid->p2p_group;
 +
 +      if (wpa_s->parent->set_sta_uapsd)
 +              params.uapsd = wpa_s->parent->sta_uapsd;
 +      else
 +              params.uapsd = -1;
 +
 +#ifdef CONFIG_HT_OVERRIDES
 +      os_memset(&htcaps, 0, sizeof(htcaps));
 +      os_memset(&htcaps_mask, 0, sizeof(htcaps_mask));
 +      params.htcaps = (u8 *) &htcaps;
 +      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) {
 +              wpa_msg(wpa_s, MSG_INFO, "Association request to the driver "
 +                      "failed");
 +              if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SANE_ERROR_CODES) {
 +                      /*
 +                       * The driver is known to mean what is saying, so we
 +                       * can stop right here; the association will not
 +                       * succeed.
 +                       */
 +                      wpas_connection_failed(wpa_s, wpa_s->pending_bssid);
 +                      wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
 +                      os_memset(wpa_s->pending_bssid, 0, ETH_ALEN);
 +                      return;
 +              }
 +              /* try to continue anyway; new association will be tried again
 +               * after timeout */
 +              assoc_failed = 1;
 +      }
 +
 +      if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE) {
 +              /* Set the key after the association just in case association
 +               * cleared the previously configured key. */
 +              wpa_supplicant_set_wpa_none_key(wpa_s, ssid);
 +              /* No need to timeout authentication since there is no key
 +               * management. */
 +              wpa_supplicant_cancel_auth_timeout(wpa_s);
 +              wpa_supplicant_set_state(wpa_s, WPA_COMPLETED);
 +#ifdef CONFIG_IBSS_RSN
 +      } else if (ssid->mode == WPAS_MODE_IBSS &&
 +                 wpa_s->key_mgmt != WPA_KEY_MGMT_NONE &&
 +                 wpa_s->key_mgmt != WPA_KEY_MGMT_WPA_NONE) {
 +              /*
 +               * RSN IBSS authentication is per-STA and we can disable the
 +               * per-BSSID authentication.
 +               */
 +              wpa_supplicant_cancel_auth_timeout(wpa_s);
 +#endif /* CONFIG_IBSS_RSN */
 +      } else {
 +              /* Timeout for IEEE 802.11 authentication and association */
 +              int timeout = 60;
 +
 +              if (assoc_failed) {
 +                      /* give IBSS a bit more time */
 +                      timeout = ssid->mode == WPAS_MODE_IBSS ? 10 : 5;
 +              } else if (wpa_s->conf->ap_scan == 1) {
 +                      /* give IBSS a bit more time */
 +                      timeout = ssid->mode == WPAS_MODE_IBSS ? 20 : 10;
 +              }
 +              wpa_supplicant_req_auth_timeout(wpa_s, timeout, 0);
 +      }
 +
 +      if (wep_keys_set &&
 +          (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC)) {
 +              /* Set static WEP keys again */
 +              wpa_set_wep_keys(wpa_s, ssid);
 +      }
 +
 +      if (wpa_s->current_ssid && wpa_s->current_ssid != ssid) {
 +              /*
 +               * Do not allow EAP session resumption between different
 +               * network configurations.
 +               */
 +              eapol_sm_invalidate_cached_session(wpa_s->eapol);
 +      }
 +      old_ssid = wpa_s->current_ssid;
 +      wpa_s->current_ssid = ssid;
-       if (wpa_s->reassociate && !wpa_s->disconnected) {
++      if (!wpas_driver_bss_selection(wpa_s) || ssid->bssid_set)
++              wpa_s->current_bss = bss;
 +      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)
 +              wpas_notify_network_changed(wpa_s);
 +}
 +
 +
 +static void wpa_supplicant_clear_connection(struct wpa_supplicant *wpa_s,
 +                                          const u8 *addr)
 +{
 +      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);
 +      wpa_sm_set_config(wpa_s->wpa, NULL);
 +      eapol_sm_notify_config(wpa_s->eapol, NULL, NULL);
 +      if (old_ssid != wpa_s->current_ssid)
 +              wpas_notify_network_changed(wpa_s);
 +      eloop_cancel_timeout(wpa_supplicant_timeout, wpa_s, NULL);
 +}
 +
 +
 +/**
 + * wpa_supplicant_deauthenticate - Deauthenticate the current connection
 + * @wpa_s: Pointer to wpa_supplicant data
 + * @reason_code: IEEE 802.11 reason code for the deauthenticate frame
 + *
 + * This function is used to request %wpa_supplicant to deauthenticate from the
 + * current AP.
 + */
 +void wpa_supplicant_deauthenticate(struct wpa_supplicant *wpa_s,
 +                                 int reason_code)
 +{
 +      u8 *addr = NULL;
 +      union wpa_event_data event;
 +      int zero_addr = 0;
 +
 +      wpa_dbg(wpa_s, MSG_DEBUG, "Request to deauthenticate - bssid=" MACSTR
 +              " pending_bssid=" MACSTR " reason=%d state=%s",
 +              MAC2STR(wpa_s->bssid), MAC2STR(wpa_s->pending_bssid),
 +              reason_code, wpa_supplicant_state_txt(wpa_s->wpa_state));
 +
 +      if (!is_zero_ether_addr(wpa_s->bssid))
 +              addr = wpa_s->bssid;
 +      else if (!is_zero_ether_addr(wpa_s->pending_bssid) &&
 +               (wpa_s->wpa_state == WPA_AUTHENTICATING ||
 +                wpa_s->wpa_state == WPA_ASSOCIATING))
 +              addr = wpa_s->pending_bssid;
 +      else if (wpa_s->wpa_state == WPA_ASSOCIATING) {
 +              /*
 +               * When using driver-based BSS selection, we may not know the
 +               * BSSID with which we are currently trying to associate. We
 +               * need to notify the driver of this disconnection even in such
 +               * a case, so use the all zeros address here.
 +               */
 +              addr = wpa_s->bssid;
 +              zero_addr = 1;
 +      }
 +
 +#ifdef CONFIG_TDLS
 +      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));
 +              event.deauth_info.reason_code = (u16) reason_code;
 +              event.deauth_info.locally_generated = 1;
 +              wpa_supplicant_event(wpa_s, EVENT_DEAUTH, &event);
 +              if (zero_addr)
 +                      addr = NULL;
 +      }
 +
 +      wpa_supplicant_clear_connection(wpa_s, addr);
 +}
 +
 +static void wpa_supplicant_enable_one_network(struct wpa_supplicant *wpa_s,
 +                                            struct wpa_ssid *ssid)
 +{
 +      if (!ssid || !ssid->disabled || ssid->disabled == 2)
 +              return;
 +
 +      ssid->disabled = 0;
 +      wpas_clear_temp_disabled(wpa_s, ssid, 1);
 +      wpas_notify_network_enabled_changed(wpa_s, ssid);
 +
 +      /*
 +       * Try to reassociate since there is no current configuration and a new
 +       * network was made available.
 +       */
 +      if (!wpa_s->current_ssid && !wpa_s->disconnected)
 +              wpa_s->reassociate = 1;
 +}
 +
 +
 +/**
 + * wpa_supplicant_enable_network - Mark a configured network as enabled
 + * @wpa_s: wpa_supplicant structure for a network interface
 + * @ssid: wpa_ssid structure for a configured network or %NULL
 + *
 + * Enables the specified network or all networks if no network specified.
 + */
 +void wpa_supplicant_enable_network(struct wpa_supplicant *wpa_s,
 +                                 struct wpa_ssid *ssid)
 +{
 +      if (ssid == NULL) {
 +              for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next)
 +                      wpa_supplicant_enable_one_network(wpa_s, ssid);
 +      } else
 +              wpa_supplicant_enable_one_network(wpa_s, ssid);
 +
-               if (wpa_supplicant_fast_associate(wpa_s) != 1)
++      if (wpa_s->reassociate && !wpa_s->disconnected &&
++          (!wpa_s->current_ssid ||
++           wpa_s->wpa_state == WPA_DISCONNECTED ||
++           wpa_s->wpa_state == WPA_SCANNING)) {
 +              if (wpa_s->sched_scanning) {
 +                      wpa_printf(MSG_DEBUG, "Stop ongoing sched_scan to add "
 +                                 "new network to scan filters");
 +                      wpa_supplicant_cancel_sched_scan(wpa_s);
 +              }
 +
-               wpa_s->own_disconnect_req = 1;
++              if (wpa_supplicant_fast_associate(wpa_s) != 1) {
++                      wpa_s->scan_req = NORMAL_SCAN_REQ;
 +                      wpa_supplicant_req_scan(wpa_s, 0, 0);
++              }
 +      }
 +}
 +
 +
 +/**
 + * wpa_supplicant_disable_network - Mark a configured network as disabled
 + * @wpa_s: wpa_supplicant structure for a network interface
 + * @ssid: wpa_ssid structure for a configured network or %NULL
 + *
 + * Disables the specified network or all networks if no network specified.
 + */
 +void wpa_supplicant_disable_network(struct wpa_supplicant *wpa_s,
 +                                  struct wpa_ssid *ssid)
 +{
 +      struct wpa_ssid *other_ssid;
 +      int was_disabled;
 +
 +      if (ssid == NULL) {
 +              if (wpa_s->sched_scanning)
 +                      wpa_supplicant_cancel_sched_scan(wpa_s);
 +
 +              for (other_ssid = wpa_s->conf->ssid; other_ssid;
 +                   other_ssid = other_ssid->next) {
 +                      was_disabled = other_ssid->disabled;
 +                      if (was_disabled == 2)
 +                              continue; /* do not change persistent P2P group
 +                                         * data */
 +
 +                      other_ssid->disabled = 1;
 +
 +                      if (was_disabled != other_ssid->disabled)
 +                              wpas_notify_network_enabled_changed(
 +                                      wpa_s, other_ssid);
 +              }
 +              if (wpa_s->current_ssid)
 +                      wpa_supplicant_deauthenticate(
 +                              wpa_s, WLAN_REASON_DEAUTH_LEAVING);
 +      } else if (ssid->disabled != 2) {
 +              if (ssid == wpa_s->current_ssid)
 +                      wpa_supplicant_deauthenticate(
 +                              wpa_s, WLAN_REASON_DEAUTH_LEAVING);
 +
 +              was_disabled = ssid->disabled;
 +
 +              ssid->disabled = 1;
 +
 +              if (was_disabled != ssid->disabled) {
 +                      wpas_notify_network_enabled_changed(wpa_s, ssid);
 +                      if (wpa_s->sched_scanning) {
 +                              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);
 +                      }
 +              }
 +      }
 +}
 +
 +
 +/**
 + * wpa_supplicant_select_network - Attempt association with a network
 + * @wpa_s: wpa_supplicant structure for a network interface
 + * @ssid: wpa_ssid structure for a configured network or %NULL for any network
 + */
 +void wpa_supplicant_select_network(struct wpa_supplicant *wpa_s,
 +                                 struct wpa_ssid *ssid)
 +{
 +
 +      struct wpa_ssid *other_ssid;
 +      int disconnected = 0;
 +
 +      if (ssid && ssid != wpa_s->current_ssid && wpa_s->current_ssid) {
-           wpa_supplicant_fast_associate(wpa_s) != 1)
++              if (wpa_s->wpa_state >= WPA_AUTHENTICATING)
++                      wpa_s->own_disconnect_req = 1;
 +              wpa_supplicant_deauthenticate(
 +                      wpa_s, WLAN_REASON_DEAUTH_LEAVING);
 +              disconnected = 1;
 +      }
 +
 +      if (ssid)
 +              wpas_clear_temp_disabled(wpa_s, ssid, 1);
 +
 +      /*
 +       * Mark all other networks disabled or mark all networks enabled if no
 +       * network specified.
 +       */
 +      for (other_ssid = wpa_s->conf->ssid; other_ssid;
 +           other_ssid = other_ssid->next) {
 +              int was_disabled = other_ssid->disabled;
 +              if (was_disabled == 2)
 +                      continue; /* do not change persistent P2P group data */
 +
 +              other_ssid->disabled = ssid ? (ssid->id != other_ssid->id) : 0;
 +              if (was_disabled && !other_ssid->disabled)
 +                      wpas_clear_temp_disabled(wpa_s, other_ssid, 0);
 +
 +              if (was_disabled != other_ssid->disabled)
 +                      wpas_notify_network_enabled_changed(wpa_s, other_ssid);
 +      }
 +
 +      if (ssid && ssid == wpa_s->current_ssid && wpa_s->current_ssid) {
 +              /* We are already associated with the selected network */
 +              wpa_printf(MSG_DEBUG, "Already associated with the "
 +                         "selected network - do nothing");
 +              return;
 +      }
 +
 +      if (ssid) {
 +              wpa_s->current_ssid = ssid;
 +              eapol_sm_notify_config(wpa_s->eapol, NULL, NULL);
 +              wpa_s->connect_without_scan =
 +                      (ssid->mode == WPAS_MODE_MESH) ? ssid : NULL;
++
++              /*
++               * Don't optimize next scan freqs since a new ESS has been
++               * selected.
++               */
++              os_free(wpa_s->next_scan_freqs);
++              wpa_s->next_scan_freqs = NULL;
 +      } else {
 +              wpa_s->connect_without_scan = NULL;
 +      }
 +
 +      wpa_s->disconnected = 0;
 +      wpa_s->reassociate = 1;
 +
 +      if (wpa_s->connect_without_scan ||
-       u8 ssid[MAX_SSID_LEN];
++          wpa_supplicant_fast_associate(wpa_s) != 1) {
++              wpa_s->scan_req = NORMAL_SCAN_REQ;
 +              wpa_supplicant_req_scan(wpa_s, 0, disconnected ? 100000 : 0);
++      }
 +
 +      if (ssid)
 +              wpas_notify_network_selected(wpa_s, ssid);
 +}
 +
 +
 +/**
 + * 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
 + * Returns: 0 if succeed or -1 if ap_scan has an invalid value
 + *
 + */
 +int wpa_supplicant_set_ap_scan(struct wpa_supplicant *wpa_s, int ap_scan)
 +{
 +
 +      int old_ap_scan;
 +
 +      if (ap_scan < 0 || ap_scan > 2)
 +              return -1;
 +
++      if (ap_scan == 2 && os_strcmp(wpa_s->driver->name, "nl80211") == 0) {
++              wpa_printf(MSG_INFO,
++                         "Note: nl80211 driver interface is not designed to be used with ap_scan=2; this can result in connection failures");
++      }
++
 +#ifdef ANDROID
 +      if (ap_scan == 2 && ap_scan != wpa_s->conf->ap_scan &&
 +          wpa_s->wpa_state >= WPA_ASSOCIATING &&
 +          wpa_s->wpa_state < WPA_COMPLETED) {
 +              wpa_printf(MSG_ERROR, "ap_scan = %d (%d) rejected while "
 +                         "associating", wpa_s->conf->ap_scan, ap_scan);
 +              return 0;
 +      }
 +#endif /* ANDROID */
 +
 +      old_ap_scan = wpa_s->conf->ap_scan;
 +      wpa_s->conf->ap_scan = ap_scan;
 +
 +      if (old_ap_scan != wpa_s->conf->ap_scan)
 +              wpas_notify_ap_scan_changed(wpa_s);
 +
 +      return 0;
 +}
 +
 +
 +/**
 + * wpa_supplicant_set_bss_expiration_age - Set BSS entry expiration age
 + * @wpa_s: wpa_supplicant structure for a network interface
 + * @expire_age: Expiration age in seconds
 + * Returns: 0 if succeed or -1 if expire_age has an invalid value
 + *
 + */
 +int wpa_supplicant_set_bss_expiration_age(struct wpa_supplicant *wpa_s,
 +                                        unsigned int bss_expire_age)
 +{
 +      if (bss_expire_age < 10) {
 +              wpa_msg(wpa_s, MSG_ERROR, "Invalid bss expiration age %u",
 +                      bss_expire_age);
 +              return -1;
 +      }
 +      wpa_msg(wpa_s, MSG_DEBUG, "Setting bss expiration age: %d sec",
 +              bss_expire_age);
 +      wpa_s->conf->bss_expiration_age = bss_expire_age;
 +
 +      return 0;
 +}
 +
 +
 +/**
 + * wpa_supplicant_set_bss_expiration_count - Set BSS entry expiration scan count
 + * @wpa_s: wpa_supplicant structure for a network interface
 + * @expire_count: number of scans after which an unseen BSS is reclaimed
 + * Returns: 0 if succeed or -1 if expire_count has an invalid value
 + *
 + */
 +int wpa_supplicant_set_bss_expiration_count(struct wpa_supplicant *wpa_s,
 +                                          unsigned int bss_expire_count)
 +{
 +      if (bss_expire_count < 1) {
 +              wpa_msg(wpa_s, MSG_ERROR, "Invalid bss expiration count %u",
 +                      bss_expire_count);
 +              return -1;
 +      }
 +      wpa_msg(wpa_s, MSG_DEBUG, "Setting bss expiration scan count: %u",
 +              bss_expire_count);
 +      wpa_s->conf->bss_expiration_scan_count = bss_expire_count;
 +
 +      return 0;
 +}
 +
 +
 +/**
 + * wpa_supplicant_set_scan_interval - Set scan interval
 + * @wpa_s: wpa_supplicant structure for a network interface
 + * @scan_interval: scan interval in seconds
 + * Returns: 0 if succeed or -1 if scan_interval has an invalid value
 + *
 + */
 +int wpa_supplicant_set_scan_interval(struct wpa_supplicant *wpa_s,
 +                                   int scan_interval)
 +{
 +      if (scan_interval < 0) {
 +              wpa_msg(wpa_s, MSG_ERROR, "Invalid scan interval %d",
 +                      scan_interval);
 +              return -1;
 +      }
 +      wpa_msg(wpa_s, MSG_DEBUG, "Setting scan interval: %d sec",
 +              scan_interval);
 +      wpa_supplicant_update_scan_int(wpa_s, scan_interval);
 +
 +      return 0;
 +}
 +
 +
 +/**
 + * wpa_supplicant_set_debug_params - Set global debug params
 + * @global: wpa_global structure
 + * @debug_level: debug level
 + * @debug_timestamp: determines if show timestamp in debug data
 + * @debug_show_keys: determines if show keys in debug data
 + * Returns: 0 if succeed or -1 if debug_level has wrong value
 + */
 +int wpa_supplicant_set_debug_params(struct wpa_global *global, int debug_level,
 +                                  int debug_timestamp, int debug_show_keys)
 +{
 +
 +      int old_level, old_timestamp, old_show_keys;
 +
 +      /* check for allowed debuglevels */
 +      if (debug_level != MSG_EXCESSIVE &&
 +          debug_level != MSG_MSGDUMP &&
 +          debug_level != MSG_DEBUG &&
 +          debug_level != MSG_INFO &&
 +          debug_level != MSG_WARNING &&
 +          debug_level != MSG_ERROR)
 +              return -1;
 +
 +      old_level = wpa_debug_level;
 +      old_timestamp = wpa_debug_timestamp;
 +      old_show_keys = wpa_debug_show_keys;
 +
 +      wpa_debug_level = debug_level;
 +      wpa_debug_timestamp = debug_timestamp ? 1 : 0;
 +      wpa_debug_show_keys = debug_show_keys ? 1 : 0;
 +
 +      if (wpa_debug_level != old_level)
 +              wpas_notify_debug_level_changed(global);
 +      if (wpa_debug_timestamp != old_timestamp)
 +              wpas_notify_debug_timestamp_changed(global);
 +      if (wpa_debug_show_keys != old_show_keys)
 +              wpas_notify_debug_show_keys_changed(global);
 +
 +      return 0;
 +}
 +
 +
 +/**
 + * wpa_supplicant_get_ssid - Get a pointer to the current network structure
 + * @wpa_s: Pointer to wpa_supplicant data
 + * Returns: A pointer to the current network structure or %NULL on failure
 + */
 +struct wpa_ssid * wpa_supplicant_get_ssid(struct wpa_supplicant *wpa_s)
 +{
 +      struct wpa_ssid *entry;
-               wpa_supplicant_req_auth_timeout(
-                       wpa_s,
-                       (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) ?
-                       70 : 10, 0);
++      u8 ssid[SSID_MAX_LEN];
 +      int res;
 +      size_t ssid_len;
 +      u8 bssid[ETH_ALEN];
 +      int wired;
 +
 +      res = wpa_drv_get_ssid(wpa_s, ssid);
 +      if (res < 0) {
 +              wpa_msg(wpa_s, MSG_WARNING, "Could not read SSID from "
 +                      "driver");
 +              return NULL;
 +      }
 +      ssid_len = res;
 +
 +      if (wpa_drv_get_bssid(wpa_s, bssid) < 0) {
 +              wpa_msg(wpa_s, MSG_WARNING, "Could not read BSSID from "
 +                      "driver");
 +              return NULL;
 +      }
 +
 +      wired = wpa_s->conf->ap_scan == 0 &&
 +              (wpa_s->drv_flags & WPA_DRIVER_FLAGS_WIRED);
 +
 +      entry = wpa_s->conf->ssid;
 +      while (entry) {
 +              if (!wpas_network_disabled(wpa_s, entry) &&
 +                  ((ssid_len == entry->ssid_len &&
 +                    os_memcmp(ssid, entry->ssid, ssid_len) == 0) || wired) &&
 +                  (!entry->bssid_set ||
 +                   os_memcmp(bssid, entry->bssid, ETH_ALEN) == 0))
 +                      return entry;
 +#ifdef CONFIG_WPS
 +              if (!wpas_network_disabled(wpa_s, entry) &&
 +                  (entry->key_mgmt & WPA_KEY_MGMT_WPS) &&
 +                  (entry->ssid == NULL || entry->ssid_len == 0) &&
 +                  (!entry->bssid_set ||
 +                   os_memcmp(bssid, entry->bssid, ETH_ALEN) == 0))
 +                      return entry;
 +#endif /* CONFIG_WPS */
 +
 +              if (!wpas_network_disabled(wpa_s, entry) && entry->bssid_set &&
 +                  entry->ssid_len == 0 &&
 +                  os_memcmp(bssid, entry->bssid, ETH_ALEN) == 0)
 +                      return entry;
 +
 +              entry = entry->next;
 +      }
 +
 +      return NULL;
 +}
 +
 +
 +static int select_driver(struct wpa_supplicant *wpa_s, int i)
 +{
 +      struct wpa_global *global = wpa_s->global;
 +
 +      if (wpa_drivers[i]->global_init && global->drv_priv[i] == NULL) {
 +              global->drv_priv[i] = wpa_drivers[i]->global_init();
 +              if (global->drv_priv[i] == NULL) {
 +                      wpa_printf(MSG_ERROR, "Failed to initialize driver "
 +                                 "'%s'", wpa_drivers[i]->name);
 +                      return -1;
 +              }
 +      }
 +
 +      wpa_s->driver = wpa_drivers[i];
 +      wpa_s->global_drv_priv = global->drv_priv[i];
 +
 +      return 0;
 +}
 +
 +
 +static int wpa_supplicant_set_driver(struct wpa_supplicant *wpa_s,
 +                                   const char *name)
 +{
 +      int i;
 +      size_t len;
 +      const char *pos, *driver = name;
 +
 +      if (wpa_s == NULL)
 +              return -1;
 +
 +      if (wpa_drivers[0] == NULL) {
 +              wpa_msg(wpa_s, MSG_ERROR, "No driver interfaces build into "
 +                      "wpa_supplicant");
 +              return -1;
 +      }
 +
 +      if (name == NULL) {
 +              /* default to first driver in the list */
 +              return select_driver(wpa_s, 0);
 +      }
 +
 +      do {
 +              pos = os_strchr(driver, ',');
 +              if (pos)
 +                      len = pos - driver;
 +              else
 +                      len = os_strlen(driver);
 +
 +              for (i = 0; wpa_drivers[i]; i++) {
 +                      if (os_strlen(wpa_drivers[i]->name) == len &&
 +                          os_strncmp(driver, wpa_drivers[i]->name, len) ==
 +                          0) {
 +                              /* First driver that succeeds wins */
 +                              if (select_driver(wpa_s, i) == 0)
 +                                      return 0;
 +                      }
 +              }
 +
 +              driver = pos + 1;
 +      } while (pos);
 +
 +      wpa_msg(wpa_s, MSG_ERROR, "Unsupported driver '%s'", name);
 +      return -1;
 +}
 +
 +
 +/**
 + * wpa_supplicant_rx_eapol - Deliver a received EAPOL frame to wpa_supplicant
 + * @ctx: Context pointer (wpa_s); this is the ctx variable registered
 + *    with struct wpa_driver_ops::init()
 + * @src_addr: Source address of the EAPOL frame
 + * @buf: EAPOL data starting from the EAPOL header (i.e., no Ethernet header)
 + * @len: Length of the EAPOL data
 + *
 + * This function is called for each received EAPOL frame. Most driver
 + * interfaces rely on more generic OS mechanism for receiving frames through
 + * l2_packet, but if such a mechanism is not available, the driver wrapper may
 + * take care of received EAPOL frames and deliver them to the core supplicant
 + * code by calling this function.
 + */
 +void wpa_supplicant_rx_eapol(void *ctx, const u8 *src_addr,
 +                           const u8 *buf, size_t len)
 +{
 +      struct wpa_supplicant *wpa_s = ctx;
 +
 +      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
 +           !wpa_s->ap_iface &&
 +#endif /* CONFIG_AP */
 +           os_memcmp(src_addr, wpa_s->bssid, ETH_ALEN) != 0)) {
 +              /*
 +               * There is possible race condition between receiving the
 +               * association event and the EAPOL frame since they are coming
 +               * through different paths from the driver. In order to avoid
 +               * issues in trying to process the EAPOL frame before receiving
 +               * association information, lets queue it for processing until
 +               * the association event is received. This may also be needed in
 +               * driver-based roaming case, so also use src_addr != BSSID as a
 +               * trigger if we have previously confirmed that the
 +               * Authenticator uses BSSID as the src_addr (which is not the
 +               * case with wired IEEE 802.1X).
 +               */
 +              wpa_dbg(wpa_s, MSG_DEBUG, "Not associated - Delay processing "
 +                      "of received EAPOL frame (state=%s bssid=" MACSTR ")",
 +                      wpa_supplicant_state_txt(wpa_s->wpa_state),
 +                      MAC2STR(wpa_s->bssid));
 +              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_reltime(&wpa_s->pending_eapol_rx_time);
 +                      os_memcpy(wpa_s->pending_eapol_rx_src, src_addr,
 +                                ETH_ALEN);
 +              }
 +              return;
 +      }
 +
 +      wpa_s->last_eapol_matches_bssid =
 +              os_memcmp(src_addr, wpa_s->bssid, ETH_ALEN) == 0;
 +
 +#ifdef CONFIG_AP
 +      if (wpa_s->ap_iface) {
 +              wpa_supplicant_ap_rx_eapol(wpa_s, src_addr, buf, len);
 +              return;
 +      }
 +#endif /* CONFIG_AP */
 +
 +      if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE) {
 +              wpa_dbg(wpa_s, MSG_DEBUG, "Ignored received EAPOL frame since "
 +                      "no key management is configured");
 +              return;
 +      }
 +
 +      if (wpa_s->eapol_received == 0 &&
 +          (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE) ||
 +           !wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt) ||
 +           wpa_s->wpa_state != WPA_COMPLETED) &&
 +          (wpa_s->current_ssid == NULL ||
 +           wpa_s->current_ssid->mode != IEEE80211_MODE_IBSS)) {
 +              /* Timeout for completing IEEE 802.1X and WPA authentication */
-           wpas_p2p_add_p2pdev_interface(wpa_s, iface->conf_p2p_dev) < 0) {
++              int timeout = 10;
++
++              if (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) {
++                      /* Use longer timeout for IEEE 802.1X/EAP */
++                      timeout = 70;
++              }
++
++#ifdef CONFIG_WPS
++              if (wpa_s->current_ssid && wpa_s->current_bss &&
++                  (wpa_s->current_ssid->key_mgmt & WPA_KEY_MGMT_WPS) &&
++                  eap_is_wps_pin_enrollee(&wpa_s->current_ssid->eap)) {
++                      /*
++                       * Use shorter timeout if going through WPS AP iteration
++                       * for PIN config method with an AP that does not
++                       * advertise Selected Registrar.
++                       */
++                      struct wpabuf *wps_ie;
++
++                      wps_ie = wpa_bss_get_vendor_ie_multi(
++                              wpa_s->current_bss, WPS_IE_VENDOR_TYPE);
++                      if (wps_ie &&
++                          !wps_is_addr_authorized(wps_ie, wpa_s->own_addr, 1))
++                              timeout = 10;
++                      wpabuf_free(wps_ie);
++              }
++#endif /* CONFIG_WPS */
++
++              wpa_supplicant_req_auth_timeout(wpa_s, timeout, 0);
 +      }
 +      wpa_s->eapol_received++;
 +
 +      if (wpa_s->countermeasures) {
 +              wpa_msg(wpa_s, MSG_INFO, "WPA: Countermeasures - dropped "
 +                      "EAPOL packet");
 +              return;
 +      }
 +
 +#ifdef CONFIG_IBSS_RSN
 +      if (wpa_s->current_ssid &&
 +          wpa_s->current_ssid->mode == WPAS_MODE_IBSS) {
 +              ibss_rsn_rx_eapol(wpa_s->ibss_rsn, src_addr, buf, len);
 +              return;
 +      }
 +#endif /* CONFIG_IBSS_RSN */
 +
 +      /* Source address of the incoming EAPOL frame could be compared to the
 +       * current BSSID. However, it is possible that a centralized
 +       * Authenticator could be using another MAC address than the BSSID of
 +       * an AP, so just allow any address to be used for now. The replies are
 +       * still sent to the current BSSID (if available), though. */
 +
 +      os_memcpy(wpa_s->last_eapol_src, src_addr, ETH_ALEN);
 +      if (!wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt) &&
 +          eapol_sm_rx_eapol(wpa_s->eapol, src_addr, buf, len) > 0)
 +              return;
 +      wpa_drv_poll(wpa_s);
 +      if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE))
 +              wpa_sm_rx_eapol(wpa_s->wpa, src_addr, buf, len);
 +      else if (wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt)) {
 +              /*
 +               * Set portValid = TRUE here since we are going to skip 4-way
 +               * handshake processing which would normally set portValid. We
 +               * need this to allow the EAPOL state machines to be completed
 +               * without going through EAPOL-Key handshake.
 +               */
 +              eapol_sm_notify_portValid(wpa_s->eapol, TRUE);
 +      }
 +}
 +
 +
 +int wpa_supplicant_update_mac_addr(struct wpa_supplicant *wpa_s)
 +{
 +      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),
 +                                         ETH_P_EAPOL,
 +                                         wpa_supplicant_rx_eapol, wpa_s, 0);
 +              if (wpa_s->l2 == NULL)
 +                      return -1;
 +      } else {
 +              const u8 *addr = wpa_drv_get_mac_addr(wpa_s);
 +              if (addr)
 +                      os_memcpy(wpa_s->own_addr, addr, ETH_ALEN);
 +      }
 +
 +      if (wpa_s->l2 && l2_packet_get_own_addr(wpa_s->l2, wpa_s->own_addr)) {
 +              wpa_msg(wpa_s, MSG_ERROR, "Failed to get own L2 address");
 +              return -1;
 +      }
 +
 +      wpa_sm_set_own_addr(wpa_s->wpa, wpa_s->own_addr);
 +
 +      return 0;
 +}
 +
 +
 +static void wpa_supplicant_rx_eapol_bridge(void *ctx, const u8 *src_addr,
 +                                         const u8 *buf, size_t len)
 +{
 +      struct wpa_supplicant *wpa_s = ctx;
 +      const struct l2_ethhdr *eth;
 +
 +      if (len < sizeof(*eth))
 +              return;
 +      eth = (const struct l2_ethhdr *) buf;
 +
 +      if (os_memcmp(eth->h_dest, wpa_s->own_addr, ETH_ALEN) != 0 &&
 +          !(eth->h_dest[0] & 0x01)) {
 +              wpa_dbg(wpa_s, MSG_DEBUG, "RX EAPOL from " MACSTR " to " MACSTR
 +                      " (bridge - not for this interface - ignore)",
 +                      MAC2STR(src_addr), MAC2STR(eth->h_dest));
 +              return;
 +      }
 +
 +      wpa_dbg(wpa_s, MSG_DEBUG, "RX EAPOL from " MACSTR " to " MACSTR
 +              " (bridge)", MAC2STR(src_addr), MAC2STR(eth->h_dest));
 +      wpa_supplicant_rx_eapol(wpa_s, src_addr, buf + sizeof(*eth),
 +                              len - sizeof(*eth));
 +}
 +
 +
 +/**
 + * wpa_supplicant_driver_init - Initialize driver interface parameters
 + * @wpa_s: Pointer to wpa_supplicant data
 + * Returns: 0 on success, -1 on failure
 + *
 + * This function is called to initialize driver interface parameters.
 + * wpa_drv_init() must have been called before this function to initialize the
 + * driver interface.
 + */
 +int wpa_supplicant_driver_init(struct wpa_supplicant *wpa_s)
 +{
 +      static int interface_count = 0;
 +
 +      if (wpa_supplicant_update_mac_addr(wpa_s) < 0)
 +              return -1;
 +
 +      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_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'",
 +                              wpa_s->bridge_ifname);
 +                      return -1;
 +              }
 +      }
 +
++      if (wpa_s->conf->ap_scan == 2 &&
++          os_strcmp(wpa_s->driver->name, "nl80211") == 0) {
++              wpa_printf(MSG_INFO,
++                         "Note: nl80211 driver interface is not designed to be used with ap_scan=2; this can result in connection failures");
++      }
++
 +      wpa_clear_keys(wpa_s, NULL);
 +
 +      /* Make sure that TKIP countermeasures are not left enabled (could
 +       * happen if wpa_supplicant is killed during countermeasures. */
 +      wpa_drv_set_countermeasures(wpa_s, 0);
 +
 +      wpa_dbg(wpa_s, MSG_DEBUG, "RSN: flushing PMKID list in the driver");
 +      wpa_drv_flush_pmkid(wpa_s);
 +
 +      wpa_s->prev_scan_ssid = WILDCARD_SSID_SCAN;
 +      wpa_s->prev_scan_wildcard = 0;
 +
 +      if (wpa_supplicant_enabled_networks(wpa_s)) {
 +              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 % 3,
 +                                              100000);
 +#endif /* ANDROID */
 +              interface_count++;
 +      } else
 +              wpa_supplicant_set_state(wpa_s, WPA_INACTIVE);
 +
 +      return 0;
 +}
 +
 +
 +static int wpa_supplicant_daemon(const char *pid_file)
 +{
 +      wpa_printf(MSG_DEBUG, "Daemonize..");
 +      return os_daemonize(pid_file);
 +}
 +
 +
 +static struct wpa_supplicant *
 +wpa_supplicant_alloc(struct wpa_supplicant *parent)
 +{
 +      struct wpa_supplicant *wpa_s;
 +
 +      wpa_s = os_zalloc(sizeof(*wpa_s));
 +      if (wpa_s == NULL)
 +              return NULL;
 +      wpa_s->scan_req = INITIAL_SCAN_REQ;
 +      wpa_s->scan_interval = 5;
 +      wpa_s->new_connection = 1;
 +      wpa_s->parent = parent ? parent : wpa_s;
 +      wpa_s->sched_scanning = 0;
 +
 +      return wpa_s;
 +}
 +
 +
 +#ifdef CONFIG_HT_OVERRIDES
 +
 +static int wpa_set_htcap_mcs(struct wpa_supplicant *wpa_s,
 +                           struct ieee80211_ht_capabilities *htcaps,
 +                           struct ieee80211_ht_capabilities *htcaps_mask,
 +                           const char *ht_mcs)
 +{
 +      /* parse ht_mcs into hex array */
 +      int i;
 +      const char *tmp = ht_mcs;
 +      char *end = NULL;
 +
 +      /* If ht_mcs is null, do not set anything */
 +      if (!ht_mcs)
 +              return 0;
 +
 +      /* This is what we are setting in the kernel */
 +      os_memset(&htcaps->supported_mcs_set, 0, IEEE80211_HT_MCS_MASK_LEN);
 +
 +      wpa_msg(wpa_s, MSG_DEBUG, "set_htcap, ht_mcs -:%s:-", ht_mcs);
 +
 +      for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++) {
 +              errno = 0;
 +              long v = strtol(tmp, &end, 16);
 +              if (errno == 0) {
 +                      wpa_msg(wpa_s, MSG_DEBUG,
 +                              "htcap value[%i]: %ld end: %p  tmp: %p",
 +                              i, v, end, tmp);
 +                      if (end == tmp)
 +                              break;
 +
 +                      htcaps->supported_mcs_set[i] = v;
 +                      tmp = end;
 +              } else {
 +                      wpa_msg(wpa_s, MSG_ERROR,
 +                              "Failed to parse ht-mcs: %s, error: %s\n",
 +                              ht_mcs, strerror(errno));
 +                      return -1;
 +              }
 +      }
 +
 +      /*
 +       * If we were able to parse any values, then set mask for the MCS set.
 +       */
 +      if (i) {
 +              os_memset(&htcaps_mask->supported_mcs_set, 0xff,
 +                        IEEE80211_HT_MCS_MASK_LEN - 1);
 +              /* skip the 3 reserved bits */
 +              htcaps_mask->supported_mcs_set[IEEE80211_HT_MCS_MASK_LEN - 1] =
 +                      0x1f;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +static int wpa_disable_max_amsdu(struct wpa_supplicant *wpa_s,
 +                               struct ieee80211_ht_capabilities *htcaps,
 +                               struct ieee80211_ht_capabilities *htcaps_mask,
 +                               int disabled)
 +{
 +      le16 msk;
 +
 +      wpa_msg(wpa_s, MSG_DEBUG, "set_disable_max_amsdu: %d", disabled);
 +
 +      if (disabled == -1)
 +              return 0;
 +
 +      msk = host_to_le16(HT_CAP_INFO_MAX_AMSDU_SIZE);
 +      htcaps_mask->ht_capabilities_info |= msk;
 +      if (disabled)
 +              htcaps->ht_capabilities_info &= msk;
 +      else
 +              htcaps->ht_capabilities_info |= msk;
 +
 +      return 0;
 +}
 +
 +
 +static int wpa_set_ampdu_factor(struct wpa_supplicant *wpa_s,
 +                              struct ieee80211_ht_capabilities *htcaps,
 +                              struct ieee80211_ht_capabilities *htcaps_mask,
 +                              int factor)
 +{
 +      wpa_msg(wpa_s, MSG_DEBUG, "set_ampdu_factor: %d", factor);
 +
 +      if (factor == -1)
 +              return 0;
 +
 +      if (factor < 0 || factor > 3) {
 +              wpa_msg(wpa_s, MSG_ERROR, "ampdu_factor: %d out of range. "
 +                      "Must be 0-3 or -1", factor);
 +              return -EINVAL;
 +      }
 +
 +      htcaps_mask->a_mpdu_params |= 0x3; /* 2 bits for factor */
 +      htcaps->a_mpdu_params &= ~0x3;
 +      htcaps->a_mpdu_params |= factor & 0x3;
 +
 +      return 0;
 +}
 +
 +
 +static int wpa_set_ampdu_density(struct wpa_supplicant *wpa_s,
 +                               struct ieee80211_ht_capabilities *htcaps,
 +                               struct ieee80211_ht_capabilities *htcaps_mask,
 +                               int density)
 +{
 +      wpa_msg(wpa_s, MSG_DEBUG, "set_ampdu_density: %d", density);
 +
 +      if (density == -1)
 +              return 0;
 +
 +      if (density < 0 || density > 7) {
 +              wpa_msg(wpa_s, MSG_ERROR,
 +                      "ampdu_density: %d out of range. Must be 0-7 or -1.",
 +                      density);
 +              return -EINVAL;
 +      }
 +
 +      htcaps_mask->a_mpdu_params |= 0x1C;
 +      htcaps->a_mpdu_params &= ~(0x1C);
 +      htcaps->a_mpdu_params |= (density << 2) & 0x1C;
 +
 +      return 0;
 +}
 +
 +
 +static int wpa_set_disable_ht40(struct wpa_supplicant *wpa_s,
 +                              struct ieee80211_ht_capabilities *htcaps,
 +                              struct ieee80211_ht_capabilities *htcaps_mask,
 +                              int disabled)
 +{
 +      /* Masking these out disables HT40 */
 +      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);
 +
 +      if (disabled)
 +              htcaps->ht_capabilities_info &= ~msk;
 +      else
 +              htcaps->ht_capabilities_info |= msk;
 +
 +      htcaps_mask->ht_capabilities_info |= msk;
 +
 +      return 0;
 +}
 +
 +
 +static int wpa_set_disable_sgi(struct wpa_supplicant *wpa_s,
 +                             struct ieee80211_ht_capabilities *htcaps,
 +                             struct ieee80211_ht_capabilities *htcaps_mask,
 +                             int disabled)
 +{
 +      /* Masking these out disables SGI */
 +      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);
 +
 +      if (disabled)
 +              htcaps->ht_capabilities_info &= ~msk;
 +      else
 +              htcaps->ht_capabilities_info |= msk;
 +
 +      htcaps_mask->ht_capabilities_info |= msk;
 +
 +      return 0;
 +}
 +
 +
 +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)
 +{
 +      struct ieee80211_ht_capabilities *htcaps;
 +      struct ieee80211_ht_capabilities *htcaps_mask;
 +
 +      if (!ssid)
 +              return;
 +
 +      params->disable_ht = ssid->disable_ht;
 +      if (!params->htcaps || !params->htcaps_mask)
 +              return;
 +
 +      htcaps = (struct ieee80211_ht_capabilities *) params->htcaps;
 +      htcaps_mask = (struct ieee80211_ht_capabilities *) params->htcaps_mask;
 +      wpa_set_htcap_mcs(wpa_s, htcaps, htcaps_mask, ssid->ht_mcs);
 +      wpa_disable_max_amsdu(wpa_s, htcaps, htcaps_mask,
 +                            ssid->disable_max_amsdu);
 +      wpa_set_ampdu_factor(wpa_s, htcaps, htcaps_mask, ssid->ampdu_factor);
 +      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 */
 +
 +
 +#ifdef CONFIG_VHT_OVERRIDES
 +void wpa_supplicant_apply_vht_overrides(
 +      struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
 +      struct wpa_driver_associate_params *params)
 +{
 +      struct ieee80211_vht_capabilities *vhtcaps;
 +      struct ieee80211_vht_capabilities *vhtcaps_mask;
 +
 +      if (!ssid)
 +              return;
 +
 +      params->disable_vht = ssid->disable_vht;
 +
 +      vhtcaps = (void *) params->vhtcaps;
 +      vhtcaps_mask = (void *) params->vhtcaps_mask;
 +
 +      if (!vhtcaps || !vhtcaps_mask)
 +              return;
 +
 +      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 |=           \
 +                      3 << 2 * (i - 1);                               \
 +              vhtcaps->vht_supported_mcs_set.tx_map |=                \
 +                      ssid->vht_tx_mcs_nss_ ##i << 2 * (i - 1);       \
 +      }                                                               \
 +      if (ssid->vht_rx_mcs_nss_ ##i >= 0) {                           \
 +              vhtcaps_mask->vht_supported_mcs_set.rx_map |=           \
 +                      3 << 2 * (i - 1);                               \
 +              vhtcaps->vht_supported_mcs_set.rx_map |=                \
 +                      ssid->vht_rx_mcs_nss_ ##i << 2 * (i - 1);       \
 +      }
 +
 +      OVERRIDE_MCS(1);
 +      OVERRIDE_MCS(2);
 +      OVERRIDE_MCS(3);
 +      OVERRIDE_MCS(4);
 +      OVERRIDE_MCS(5);
 +      OVERRIDE_MCS(6);
 +      OVERRIDE_MCS(7);
 +      OVERRIDE_MCS(8);
 +}
 +#endif /* CONFIG_VHT_OVERRIDES */
 +
 +
 +static int pcsc_reader_init(struct wpa_supplicant *wpa_s)
 +{
 +#ifdef PCSC_FUNCS
 +      size_t len;
 +
 +      if (!wpa_s->conf->pcsc_reader)
 +              return 0;
 +
 +      wpa_s->scard = scard_init(wpa_s->conf->pcsc_reader);
 +      if (!wpa_s->scard)
 +              return 1;
 +
 +      if (wpa_s->conf->pcsc_pin &&
 +          scard_set_pin(wpa_s->scard, wpa_s->conf->pcsc_pin) < 0) {
 +              scard_deinit(wpa_s->scard);
 +              wpa_s->scard = NULL;
 +              wpa_msg(wpa_s, MSG_ERROR, "PC/SC PIN validation failed");
 +              return -1;
 +      }
 +
 +      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);
 +
 +      wpa_sm_set_scard_ctx(wpa_s->wpa, wpa_s->scard);
 +      eapol_sm_register_scard_ctx(wpa_s->eapol, wpa_s->scard);
 +#endif /* PCSC_FUNCS */
 +
 +      return 0;
 +}
 +
 +
 +int wpas_init_ext_pw(struct wpa_supplicant *wpa_s)
 +{
 +      char *val, *pos;
 +
 +      ext_password_deinit(wpa_s->ext_pw);
 +      wpa_s->ext_pw = NULL;
 +      eapol_sm_set_ext_pw_ctx(wpa_s->eapol, NULL);
 +
 +      if (!wpa_s->conf->ext_password_backend)
 +              return 0;
 +
 +      val = os_strdup(wpa_s->conf->ext_password_backend);
 +      if (val == NULL)
 +              return -1;
 +      pos = os_strchr(val, ':');
 +      if (pos)
 +              *pos++ = '\0';
 +
 +      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;
 +}
 +
 +
++#ifdef CONFIG_FST
++
++static const u8 * wpas_fst_get_bssid_cb(void *ctx)
++{
++      struct wpa_supplicant *wpa_s = ctx;
++
++      return (is_zero_ether_addr(wpa_s->bssid) ||
++              wpa_s->wpa_state != WPA_COMPLETED) ? NULL : wpa_s->bssid;
++}
++
++
++static void wpas_fst_get_channel_info_cb(void *ctx,
++                                       enum hostapd_hw_mode *hw_mode,
++                                       u8 *channel)
++{
++      struct wpa_supplicant *wpa_s = ctx;
++
++      if (wpa_s->current_bss) {
++              *hw_mode = ieee80211_freq_to_chan(wpa_s->current_bss->freq,
++                                                channel);
++      } else if (wpa_s->hw.num_modes) {
++              *hw_mode = wpa_s->hw.modes[0].mode;
++      } else {
++              WPA_ASSERT(0);
++              *hw_mode = 0;
++      }
++}
++
++
++static int wpas_fst_get_hw_modes(void *ctx, struct hostapd_hw_modes **modes)
++{
++      struct wpa_supplicant *wpa_s = ctx;
++
++      *modes = wpa_s->hw.modes;
++      return wpa_s->hw.num_modes;
++}
++
++
++static void wpas_fst_set_ies_cb(void *ctx, const struct wpabuf *fst_ies)
++{
++      struct wpa_supplicant *wpa_s = ctx;
++
++      wpa_hexdump_buf(MSG_DEBUG, "FST: Set IEs", fst_ies);
++      wpa_s->fst_ies = fst_ies;
++}
++
++
++static int wpas_fst_send_action_cb(void *ctx, const u8 *da, struct wpabuf *data)
++{
++      struct wpa_supplicant *wpa_s = ctx;
++
++      WPA_ASSERT(os_memcmp(wpa_s->bssid, da, ETH_ALEN) == 0);
++      return wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
++                                        wpa_s->own_addr, wpa_s->bssid,
++                                        wpabuf_head(data), wpabuf_len(data),
++                                 0);
++}
++
++
++static const struct wpabuf * wpas_fst_get_mb_ie_cb(void *ctx, const u8 *addr)
++{
++      struct wpa_supplicant *wpa_s = ctx;
++
++      WPA_ASSERT(os_memcmp(wpa_s->bssid, addr, ETH_ALEN) == 0);
++      return wpa_s->received_mb_ies;
++}
++
++
++static void wpas_fst_update_mb_ie_cb(void *ctx, const u8 *addr,
++                                   const u8 *buf, size_t size)
++{
++      struct wpa_supplicant *wpa_s = ctx;
++      struct mb_ies_info info;
++
++      WPA_ASSERT(os_memcmp(wpa_s->bssid, addr, ETH_ALEN) == 0);
++
++      if (!mb_ies_info_by_ies(&info, buf, size)) {
++              wpabuf_free(wpa_s->received_mb_ies);
++              wpa_s->received_mb_ies = mb_ies_by_info(&info);
++      }
++}
++
++
++const u8 * wpas_fst_get_peer_first(void *ctx, struct fst_get_peer_ctx **get_ctx,
++                                 Boolean mb_only)
++{
++      struct wpa_supplicant *wpa_s = ctx;
++
++      *get_ctx = NULL;
++      if (!is_zero_ether_addr(wpa_s->bssid))
++              return (wpa_s->received_mb_ies || !mb_only) ?
++                      wpa_s->bssid : NULL;
++      return NULL;
++}
++
++
++const u8 * wpas_fst_get_peer_next(void *ctx, struct fst_get_peer_ctx **get_ctx,
++                                Boolean mb_only)
++{
++      return NULL;
++}
++
++void fst_wpa_supplicant_fill_iface_obj(struct wpa_supplicant *wpa_s,
++                                     struct fst_wpa_obj *iface_obj)
++{
++      iface_obj->ctx              = wpa_s;
++      iface_obj->get_bssid        = wpas_fst_get_bssid_cb;
++      iface_obj->get_channel_info = wpas_fst_get_channel_info_cb;
++      iface_obj->get_hw_modes     = wpas_fst_get_hw_modes;
++      iface_obj->set_ies          = wpas_fst_set_ies_cb;
++      iface_obj->send_action      = wpas_fst_send_action_cb;
++      iface_obj->get_mb_ie        = wpas_fst_get_mb_ie_cb;
++      iface_obj->update_mb_ie     = wpas_fst_update_mb_ie_cb;
++      iface_obj->get_peer_first   = wpas_fst_get_peer_first;
++      iface_obj->get_peer_next    = wpas_fst_get_peer_next;
++}
++#endif /* CONFIG_FST */
++
 +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));
 +      }
 +
 +      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;
 +
 +      return 0;
 +}
 +
 +
 +static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s,
 +                                   struct wpa_interface *iface)
 +{
 +      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,
 +                 iface->confname ? iface->confname : "N/A",
 +                 iface->driver ? iface->driver : "default",
 +                 iface->ctrl_interface ? iface->ctrl_interface : "N/A",
 +                 iface->bridge_ifname ? iface->bridge_ifname : "N/A");
 +
 +      if (iface->confname) {
 +#ifdef CONFIG_BACKEND_FILE
 +              wpa_s->confname = os_rel2abs_path(iface->confname);
 +              if (wpa_s->confname == NULL) {
 +                      wpa_printf(MSG_ERROR, "Failed to get absolute path "
 +                                 "for configuration file '%s'.",
 +                                 iface->confname);
 +                      return -1;
 +              }
 +              wpa_printf(MSG_DEBUG, "Configuration file '%s' -> '%s'",
 +                         iface->confname, wpa_s->confname);
 +#else /* CONFIG_BACKEND_FILE */
 +              wpa_s->confname = os_strdup(iface->confname);
 +#endif /* CONFIG_BACKEND_FILE */
 +              wpa_s->conf = wpa_config_read(wpa_s->confname, NULL);
 +              if (wpa_s->conf == NULL) {
 +                      wpa_printf(MSG_ERROR, "Failed to read or parse "
 +                                 "configuration '%s'.", wpa_s->confname);
 +                      return -1;
 +              }
 +              wpa_s->confanother = os_rel2abs_path(iface->confanother);
 +              wpa_config_read(wpa_s->confanother, wpa_s->conf);
 +
 +              /*
 +               * Override ctrl_interface and driver_param if set on command
 +               * line.
 +               */
 +              if (iface->ctrl_interface) {
 +                      os_free(wpa_s->conf->ctrl_interface);
 +                      wpa_s->conf->ctrl_interface =
 +                              os_strdup(iface->ctrl_interface);
 +              }
 +
 +              if (iface->driver_param) {
 +                      os_free(wpa_s->conf->driver_param);
 +                      wpa_s->conf->driver_param =
 +                              os_strdup(iface->driver_param);
 +              }
 +
 +              if (iface->p2p_mgmt && !iface->ctrl_interface) {
 +                      os_free(wpa_s->conf->ctrl_interface);
 +                      wpa_s->conf->ctrl_interface = NULL;
 +              }
 +      } else
 +              wpa_s->conf = wpa_config_alloc_empty(iface->ctrl_interface,
 +                                                   iface->driver_param);
 +
 +      if (wpa_s->conf == NULL) {
 +              wpa_printf(MSG_ERROR, "\nNo configuration found.");
 +              return -1;
 +      }
 +
 +      if (iface->ifname == NULL) {
 +              wpa_printf(MSG_ERROR, "\nInterface name is required.");
 +              return -1;
 +      }
 +      if (os_strlen(iface->ifname) >= sizeof(wpa_s->ifname)) {
 +              wpa_printf(MSG_ERROR, "\nToo long interface name '%s'.",
 +                         iface->ifname);
 +              return -1;
 +      }
 +      os_strlcpy(wpa_s->ifname, iface->ifname, sizeof(wpa_s->ifname));
 +
 +      if (iface->bridge_ifname) {
 +              if (os_strlen(iface->bridge_ifname) >=
 +                  sizeof(wpa_s->bridge_ifname)) {
 +                      wpa_printf(MSG_ERROR, "\nToo long bridge interface "
 +                                 "name '%s'.", iface->bridge_ifname);
 +                      return -1;
 +              }
 +              os_strlcpy(wpa_s->bridge_ifname, iface->bridge_ifname,
 +                         sizeof(wpa_s->bridge_ifname));
 +      }
 +
 +      /* RSNA Supplicant Key Management - INITIALIZE */
 +      eapol_sm_notify_portEnabled(wpa_s->eapol, FALSE);
 +      eapol_sm_notify_portValid(wpa_s->eapol, FALSE);
 +
 +      /* Initialize driver interface and register driver event handler before
 +       * L2 receive handler so that association events are processed before
 +       * EAPOL-Key packets if both become available for the same select()
 +       * call. */
 +      if (wpas_init_driver(wpa_s, iface) < 0)
 +              return -1;
 +
 +      if (wpa_supplicant_init_wpa(wpa_s) < 0)
 +              return -1;
 +
 +      wpa_sm_set_ifname(wpa_s->wpa, wpa_s->ifname,
 +                        wpa_s->bridge_ifname[0] ? wpa_s->bridge_ifname :
 +                        NULL);
 +      wpa_sm_set_fast_reauth(wpa_s->wpa, wpa_s->conf->fast_reauth);
 +
 +      if (wpa_s->conf->dot11RSNAConfigPMKLifetime &&
 +          wpa_sm_set_param(wpa_s->wpa, RSNA_PMK_LIFETIME,
 +                           wpa_s->conf->dot11RSNAConfigPMKLifetime)) {
 +              wpa_msg(wpa_s, MSG_ERROR, "Invalid WPA parameter value for "
 +                      "dot11RSNAConfigPMKLifetime");
 +              return -1;
 +      }
 +
 +      if (wpa_s->conf->dot11RSNAConfigPMKReauthThreshold &&
 +          wpa_sm_set_param(wpa_s->wpa, RSNA_PMK_REAUTH_THRESHOLD,
 +                           wpa_s->conf->dot11RSNAConfigPMKReauthThreshold)) {
 +              wpa_msg(wpa_s, MSG_ERROR, "Invalid WPA parameter value for "
 +                      "dot11RSNAConfigPMKReauthThreshold");
 +              return -1;
 +      }
 +
 +      if (wpa_s->conf->dot11RSNAConfigSATimeout &&
 +          wpa_sm_set_param(wpa_s->wpa, RSNA_SA_TIMEOUT,
 +                           wpa_s->conf->dot11RSNAConfigSATimeout)) {
 +              wpa_msg(wpa_s, MSG_ERROR, "Invalid WPA parameter value for "
 +                      "dot11RSNAConfigSATimeout");
 +              return -1;
 +      }
 +
 +      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;
 +              }
 +      }
 +
 +      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;
 +              wpa_s->sched_scan_supported = capa.sched_scan_supported;
 +              wpa_s->max_match_sets = capa.max_match_sets;
 +              wpa_s->max_remain_on_chan = capa.max_remain_on_chan;
 +              wpa_s->max_stations = capa.max_stations;
 +              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;
 +
 +      /*
 +       * Only take p2p_mgmt parameters when P2P Device is supported.
 +       * Doing it here as it determines whether l2_packet_init() will be done
 +       * during wpa_supplicant_driver_init().
 +       */
 +      if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE)
 +              wpa_s->p2p_mgmt = iface->p2p_mgmt;
 +      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;
 +
 +#ifdef CONFIG_TDLS
 +      if ((!iface->p2p_mgmt ||
 +           !(wpa_s->drv_flags &
 +             WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE)) &&
 +          wpa_tdls_init(wpa_s->wpa))
 +              return -1;
 +#endif /* CONFIG_TDLS */
 +
 +      if (wpa_s->conf->country[0] && wpa_s->conf->country[1] &&
 +          wpa_drv_set_country(wpa_s, wpa_s->conf->country)) {
 +              wpa_dbg(wpa_s, MSG_DEBUG, "Failed to set country");
 +              return -1;
 +      }
 +
++#ifdef CONFIG_FST
++      if (wpa_s->conf->fst_group_id) {
++              struct fst_iface_cfg cfg;
++              struct fst_wpa_obj iface_obj;
++
++              fst_wpa_supplicant_fill_iface_obj(wpa_s, &iface_obj);
++              os_strlcpy(cfg.group_id, wpa_s->conf->fst_group_id,
++                         sizeof(cfg.group_id));
++              cfg.priority = wpa_s->conf->fst_priority;
++              cfg.llt = wpa_s->conf->fst_llt;
++
++              wpa_s->fst = fst_attach(wpa_s->ifname, wpa_s->own_addr,
++                                      &iface_obj, &cfg);
++              if (!wpa_s->fst) {
++                      wpa_msg(wpa_s, MSG_ERROR,
++                              "FST: Cannot attach iface %s to group %s",
++                              wpa_s->ifname, cfg.group_id);
++                      return -1;
++              }
++      }
++#endif /* CONFIG_FST */
++
 +      if (wpas_wps_init(wpa_s))
 +              return -1;
 +
 +      if (wpa_supplicant_init_eapol(wpa_s) < 0)
 +              return -1;
 +      wpa_sm_set_eapol(wpa_s->wpa, wpa_s->eapol);
 +
 +      wpa_s->ctrl_iface = wpa_supplicant_ctrl_iface_init(wpa_s);
 +      if (wpa_s->ctrl_iface == NULL) {
 +              wpa_printf(MSG_ERROR,
 +                         "Failed to initialize control interface '%s'.\n"
 +                         "You may have another wpa_supplicant process "
 +                         "already running or the file was\n"
 +                         "left by an unclean termination of wpa_supplicant "
 +                         "in which case you will need\n"
 +                         "to manually remove this file before starting "
 +                         "wpa_supplicant again.\n",
 +                         wpa_s->conf->ctrl_interface);
 +              return -1;
 +      }
 +
 +      wpa_s->gas = gas_query_init(wpa_s);
 +      if (wpa_s->gas == NULL) {
 +              wpa_printf(MSG_ERROR, "Failed to initialize GAS query");
 +              return -1;
 +      }
 +
 +      if (iface->p2p_mgmt && wpas_p2p_init(wpa_s->global, wpa_s) < 0) {
 +              wpa_msg(wpa_s, MSG_ERROR, "Failed to init P2P");
 +              return -1;
 +      }
 +
 +      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 = 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 */
 +
 +      if (pcsc_reader_init(wpa_s) < 0)
 +              return -1;
 +
 +      if (wpas_init_ext_pw(wpa_s) < 0)
 +              return -1;
 +
 +      wpas_rrm_reset(wpa_s);
 +
 +      return 0;
 +}
 +
 +
 +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,
 +                                            WLAN_REASON_DEAUTH_LEAVING);
 +
 +              wpa_drv_set_countermeasures(wpa_s, 0);
 +              wpa_clear_keys(wpa_s, NULL);
 +      }
 +
 +      wpa_supplicant_cleanup(wpa_s);
 +      wpas_p2p_deinit_iface(wpa_s);
 +
 +      wpas_ctrl_radio_work_flush(wpa_s);
 +      radio_remove_interface(wpa_s);
 +
++#ifdef CONFIG_FST
++      if (wpa_s->fst) {
++              fst_detach(wpa_s->fst);
++              wpa_s->fst = NULL;
++      }
++      if (wpa_s->received_mb_ies) {
++              wpabuf_free(wpa_s->received_mb_ies);
++              wpa_s->received_mb_ies = NULL;
++      }
++#endif /* CONFIG_FST */
++
 +      if (wpa_s->drv_priv)
 +              wpa_drv_deinit(wpa_s);
 +
 +      if (notify)
 +              wpas_notify_iface_removed(wpa_s);
 +
 +      if (terminate)
 +              wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_TERMINATING);
 +
 +      if (wpa_s->ctrl_iface) {
 +              wpa_supplicant_ctrl_iface_deinit(wpa_s->ctrl_iface);
 +              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;
 +      }
 +
++      os_free(wpa_s->ssids_from_scan_req);
++
 +      os_free(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.
 + * This can be called before wpa_supplicant_run() to add interfaces before the
 + * main event loop has been started. In addition, new interfaces can be added
 + * dynamically while %wpa_supplicant is already running. This could happen,
 + * 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_supplicant *parent)
 +{
 +      struct wpa_supplicant *wpa_s;
 +      struct wpa_interface t_iface;
 +      struct wpa_ssid *ssid;
 +
 +      if (global == NULL || iface == NULL)
 +              return NULL;
 +
 +      wpa_s = wpa_supplicant_alloc(parent);
 +      if (wpa_s == NULL)
 +              return NULL;
 +
 +      wpa_s->global = global;
 +
 +      t_iface = *iface;
 +      if (global->params.override_driver) {
 +              wpa_printf(MSG_DEBUG, "Override interface parameter: driver "
 +                         "('%s' -> '%s')",
 +                         iface->driver, global->params.override_driver);
 +              t_iface.driver = global->params.override_driver;
 +      }
 +      if (global->params.override_ctrl_interface) {
 +              wpa_printf(MSG_DEBUG, "Override interface parameter: "
 +                         "ctrl_interface ('%s' -> '%s')",
 +                         iface->ctrl_interface,
 +                         global->params.override_ctrl_interface);
 +              t_iface.ctrl_interface =
 +                      global->params.override_ctrl_interface;
 +      }
 +      if (wpa_supplicant_init_iface(wpa_s, &t_iface)) {
 +              wpa_printf(MSG_DEBUG, "Failed to add interface %s",
 +                         iface->ifname);
 +              wpa_supplicant_deinit_iface(wpa_s, 0, 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);
 +      }
 +
 +      wpa_s->next = global->ifaces;
 +      global->ifaces = wpa_s;
 +
 +      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->global->p2p_disabled && !wpa_s->conf->p2p_disabled &&
 +          (wpa_s->drv_flags & WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE) &&
-           (!ssid->passphrase || ssid->ssid_len != 0) && !ssid->ext_psk)
++          wpas_p2p_add_p2pdev_interface(
++                  wpa_s, wpa_s->global->params.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;
 +}
 +
 +
 +/**
 + * wpa_supplicant_remove_iface - Remove a network interface
 + * @global: Pointer to global data from wpa_supplicant_init()
 + * @wpa_s: Pointer to the network interface to be removed
 + * Returns: 0 if interface was removed, -1 if interface was not found
 + *
 + * This function can be used to dynamically remove network interfaces from
 + * %wpa_supplicant, e.g., when a hotplug network adapter is ejected. In
 + * addition, this function is used to remove all remaining interfaces when
 + * %wpa_supplicant is terminated.
 + */
 +int wpa_supplicant_remove_iface(struct wpa_global *global,
 +                              struct wpa_supplicant *wpa_s,
 +                              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;
 +      if (prev == wpa_s) {
 +              global->ifaces = wpa_s->next;
 +      } else {
 +              while (prev && prev->next != wpa_s)
 +                      prev = prev->next;
 +              if (prev == NULL)
 +                      return -1;
 +              prev->next = wpa_s->next;
 +      }
 +
 +      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;
 +}
 +
 +
 +/**
 + * wpa_supplicant_get_eap_mode - Get the current EAP mode
 + * @wpa_s: Pointer to the network interface
 + * Returns: Pointer to the eap mode or the string "UNKNOWN" if not found
 + */
 +const char * wpa_supplicant_get_eap_mode(struct wpa_supplicant *wpa_s)
 +{
 +      const char *eapol_method;
 +
 +        if (wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt) == 0 &&
 +            wpa_s->key_mgmt != WPA_KEY_MGMT_IEEE8021X_NO_WPA) {
 +              return "NO-EAP";
 +      }
 +
 +      eapol_method = eapol_sm_get_method_name(wpa_s->eapol);
 +      if (eapol_method == NULL)
 +              return "UNKNOWN-EAP";
 +
 +      return eapol_method;
 +}
 +
 +
 +/**
 + * wpa_supplicant_get_iface - Get a new network interface
 + * @global: Pointer to global data from wpa_supplicant_init()
 + * @ifname: Interface name
 + * Returns: Pointer to the interface or %NULL if not found
 + */
 +struct wpa_supplicant * wpa_supplicant_get_iface(struct wpa_global *global,
 +                                               const char *ifname)
 +{
 +      struct wpa_supplicant *wpa_s;
 +
 +      for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
 +              if (os_strcmp(wpa_s->ifname, ifname) == 0)
 +                      return wpa_s;
 +      }
 +      return NULL;
 +}
 +
 +
 +#ifndef CONFIG_NO_WPA_MSG
 +static const char * wpa_supplicant_msg_ifname_cb(void *ctx)
 +{
 +      struct wpa_supplicant *wpa_s = ctx;
 +      if (wpa_s == NULL)
 +              return NULL;
 +      return wpa_s->ifname;
 +}
 +#endif /* CONFIG_NO_WPA_MSG */
 +
 +
++#ifndef WPA_SUPPLICANT_CLEANUP_INTERVAL
++#define WPA_SUPPLICANT_CLEANUP_INTERVAL 10
++#endif /* WPA_SUPPLICANT_CLEANUP_INTERVAL */
++
++/* Periodic cleanup tasks */
++static void wpas_periodic(void *eloop_ctx, void *timeout_ctx)
++{
++      struct wpa_global *global = eloop_ctx;
++      struct wpa_supplicant *wpa_s;
++
++      eloop_register_timeout(WPA_SUPPLICANT_CLEANUP_INTERVAL, 0,
++                             wpas_periodic, global, NULL);
++
++#ifdef CONFIG_P2P
++      if (global->p2p)
++              p2p_expire_peers(global->p2p);
++#endif /* CONFIG_P2P */
++
++      for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
++              wpa_bss_flush_by_age(wpa_s, wpa_s->conf->bss_expiration_age);
++#ifdef CONFIG_AP
++              ap_periodic(wpa_s);
++#endif /* CONFIG_AP */
++      }
++}
++
++
 +/**
 + * wpa_supplicant_init - Initialize %wpa_supplicant
 + * @params: Parameters for %wpa_supplicant
 + * Returns: Pointer to global %wpa_supplicant data, or %NULL on failure
 + *
 + * This function is used to initialize %wpa_supplicant. After successful
 + * initialization, the returned data pointer can be used to add and remove
 + * network interfaces, and eventually, to deinitialize %wpa_supplicant.
 + */
 +struct wpa_global * wpa_supplicant_init(struct wpa_params *params)
 +{
 +      struct wpa_global *global;
 +      int ret, i;
 +
 +      if (params == NULL)
 +              return NULL;
 +
 +#ifdef CONFIG_DRIVER_NDIS
 +      {
 +              void driver_ndis_init_ops(void);
 +              driver_ndis_init_ops();
 +      }
 +#endif /* CONFIG_DRIVER_NDIS */
 +
 +#ifndef CONFIG_NO_WPA_MSG
 +      wpa_msg_register_ifname_cb(wpa_supplicant_msg_ifname_cb);
 +#endif /* CONFIG_NO_WPA_MSG */
 +
 +      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) {
 +              ret = wpa_debug_open_linux_tracing();
 +              if (ret) {
 +                      wpa_printf(MSG_ERROR,
 +                                 "Failed to enable trace logging");
 +                      return NULL;
 +              }
 +      }
 +
 +      ret = eap_register_methods();
 +      if (ret) {
 +              wpa_printf(MSG_ERROR, "Failed to register EAP methods");
 +              if (ret == -2)
 +                      wpa_printf(MSG_ERROR, "Two or more EAP methods used "
 +                                 "the same EAP type.");
 +              return NULL;
 +      }
 +
 +      global = os_zalloc(sizeof(*global));
 +      if (global == NULL)
 +              return NULL;
 +      dl_list_init(&global->p2p_srv_bonjour);
 +      dl_list_init(&global->p2p_srv_upnp);
 +      global->params.daemonize = params->daemonize;
 +      global->params.wait_for_monitor = params->wait_for_monitor;
 +      global->params.dbus_ctrl_interface = params->dbus_ctrl_interface;
 +      if (params->pid_file)
 +              global->params.pid_file = os_strdup(params->pid_file);
 +      if (params->ctrl_interface)
 +              global->params.ctrl_interface =
 +                      os_strdup(params->ctrl_interface);
 +      if (params->ctrl_interface_group)
 +              global->params.ctrl_interface_group =
 +                      os_strdup(params->ctrl_interface_group);
 +      if (params->override_driver)
 +              global->params.override_driver =
 +                      os_strdup(params->override_driver);
 +      if (params->override_ctrl_interface)
 +              global->params.override_ctrl_interface =
 +                      os_strdup(params->override_ctrl_interface);
++#ifdef CONFIG_P2P
++      if (params->conf_p2p_dev)
++              global->params.conf_p2p_dev =
++                      os_strdup(params->conf_p2p_dev);
++#endif /* CONFIG_P2P */
 +      wpa_debug_level = global->params.wpa_debug_level =
 +              params->wpa_debug_level;
 +      wpa_debug_show_keys = global->params.wpa_debug_show_keys =
 +              params->wpa_debug_show_keys;
 +      wpa_debug_timestamp = global->params.wpa_debug_timestamp =
 +              params->wpa_debug_timestamp;
 +
 +      wpa_printf(MSG_DEBUG, "wpa_supplicant v" VERSION_STR);
 +
 +      if (eloop_init()) {
 +              wpa_printf(MSG_ERROR, "Failed to initialize event loop");
 +              wpa_supplicant_deinit(global);
 +              return NULL;
 +      }
 +
 +      random_init(params->entropy_file);
 +
 +      global->ctrl_iface = wpa_supplicant_global_ctrl_iface_init(global);
 +      if (global->ctrl_iface == NULL) {
 +              wpa_supplicant_deinit(global);
 +              return NULL;
 +      }
 +
 +      if (wpas_notify_supplicant_initialized(global)) {
 +              wpa_supplicant_deinit(global);
 +              return NULL;
 +      }
 +
 +      for (i = 0; wpa_drivers[i]; i++)
 +              global->drv_count++;
 +      if (global->drv_count == 0) {
 +              wpa_printf(MSG_ERROR, "No drivers enabled");
 +              wpa_supplicant_deinit(global);
 +              return NULL;
 +      }
 +      global->drv_priv = os_calloc(global->drv_count, sizeof(void *));
 +      if (global->drv_priv == NULL) {
 +              wpa_supplicant_deinit(global);
 +              return NULL;
 +      }
 +
 +#ifdef CONFIG_WIFI_DISPLAY
 +      if (wifi_display_init(global) < 0) {
 +              wpa_printf(MSG_ERROR, "Failed to initialize Wi-Fi Display");
 +              wpa_supplicant_deinit(global);
 +              return NULL;
 +      }
 +#endif /* CONFIG_WIFI_DISPLAY */
 +
++      eloop_register_timeout(WPA_SUPPLICANT_CLEANUP_INTERVAL, 0,
++                             wpas_periodic, global, NULL);
++
 +      return global;
 +}
 +
 +
 +/**
 + * wpa_supplicant_run - Run the %wpa_supplicant main event loop
 + * @global: Pointer to global data from wpa_supplicant_init()
 + * Returns: 0 after successful event loop run, -1 on failure
 + *
 + * This function starts the main event loop and continues running as long as
 + * there are any remaining events. In most cases, this function is running as
 + * long as the %wpa_supplicant process in still in use.
 + */
 +int wpa_supplicant_run(struct wpa_global *global)
 +{
 +      struct wpa_supplicant *wpa_s;
 +
 +      if (global->params.daemonize &&
 +          wpa_supplicant_daemon(global->params.pid_file))
 +              return -1;
 +
 +      if (global->params.wait_for_monitor) {
 +              for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next)
 +                      if (wpa_s->ctrl_iface)
 +                              wpa_supplicant_ctrl_iface_wait(
 +                                      wpa_s->ctrl_iface);
 +      }
 +
 +      eloop_register_signal_terminate(wpa_supplicant_terminate, global);
 +      eloop_register_signal_reconfig(wpa_supplicant_reconfig, global);
 +
 +      eloop_run();
 +
 +      return 0;
 +}
 +
 +
 +/**
 + * wpa_supplicant_deinit - Deinitialize %wpa_supplicant
 + * @global: Pointer to global data from wpa_supplicant_init()
 + *
 + * This function is called to deinitialize %wpa_supplicant and to free all
 + * allocated resources. Remaining network interfaces will also be removed.
 + */
 +void wpa_supplicant_deinit(struct wpa_global *global)
 +{
 +      int i;
 +
 +      if (global == NULL)
 +              return;
 +
++      eloop_cancel_timeout(wpas_periodic, global, NULL);
++
 +#ifdef CONFIG_WIFI_DISPLAY
 +      wifi_display_deinit(global);
 +#endif /* CONFIG_WIFI_DISPLAY */
 +
 +      while (global->ifaces)
 +              wpa_supplicant_remove_iface(global, global->ifaces, 1);
 +
 +      if (global->ctrl_iface)
 +              wpa_supplicant_global_ctrl_iface_deinit(global->ctrl_iface);
 +
 +      wpas_notify_supplicant_deinitialized(global);
 +
 +      eap_peer_unregister_methods();
 +#ifdef CONFIG_AP
 +      eap_server_unregister_methods();
 +#endif /* CONFIG_AP */
 +
 +      for (i = 0; wpa_drivers[i] && global->drv_priv; i++) {
 +              if (!global->drv_priv[i])
 +                      continue;
 +              wpa_drivers[i]->global_deinit(global->drv_priv[i]);
 +      }
 +      os_free(global->drv_priv);
 +
 +      random_deinit();
 +
 +      eloop_destroy();
 +
 +      if (global->params.pid_file) {
 +              os_daemonize_terminate(global->params.pid_file);
 +              os_free(global->params.pid_file);
 +      }
 +      os_free(global->params.ctrl_interface);
 +      os_free(global->params.ctrl_interface_group);
 +      os_free(global->params.override_driver);
 +      os_free(global->params.override_ctrl_interface);
++#ifdef CONFIG_P2P
++      os_free(global->params.conf_p2p_dev);
++#endif /* CONFIG_P2P */
 +
 +      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();
 +      wpa_debug_close_file();
 +      wpa_debug_close_linux_tracing();
 +}
 +
 +
 +void wpa_supplicant_update_config(struct wpa_supplicant *wpa_s)
 +{
 +      if ((wpa_s->conf->changed_parameters & CFG_CHANGED_COUNTRY) &&
 +          wpa_s->conf->country[0] && wpa_s->conf->country[1]) {
 +              char country[3];
 +              country[0] = wpa_s->conf->country[0];
 +              country[1] = wpa_s->conf->country[1];
 +              country[2] = '\0';
 +              if (wpa_drv_set_country(wpa_s, country) < 0) {
 +                      wpa_printf(MSG_ERROR, "Failed to set country code "
 +                                 "'%s'", country);
 +              }
 +      }
 +
 +      if (wpa_s->conf->changed_parameters & CFG_CHANGED_EXT_PW_BACKEND)
 +              wpas_init_ext_pw(wpa_s);
 +
 +#ifdef CONFIG_WPS
 +      wpas_wps_update_config(wpa_s);
 +#endif /* CONFIG_WPS */
 +      wpas_p2p_update_config(wpa_s);
 +      wpa_s->conf->changed_parameters = 0;
 +}
 +
 +
 +void add_freq(int *freqs, int *num_freqs, int freq)
 +{
 +      int i;
 +
 +      for (i = 0; i < *num_freqs; i++) {
 +              if (freqs[i] == freq)
 +                      return;
 +      }
 +
 +      freqs[*num_freqs] = freq;
 +      (*num_freqs)++;
 +}
 +
 +
 +static int * get_bss_freqs_in_ess(struct wpa_supplicant *wpa_s)
 +{
 +      struct wpa_bss *bss, *cbss;
 +      const int max_freqs = 10;
 +      int *freqs;
 +      int num_freqs = 0;
 +
 +      freqs = os_calloc(max_freqs + 1, sizeof(int));
 +      if (freqs == NULL)
 +              return NULL;
 +
 +      cbss = wpa_s->current_bss;
 +
 +      dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
 +              if (bss == cbss)
 +                      continue;
 +              if (bss->ssid_len == cbss->ssid_len &&
 +                  os_memcmp(bss->ssid, cbss->ssid, bss->ssid_len) == 0 &&
 +                  wpa_blacklist_get(wpa_s, bss->bssid) == NULL) {
 +                      add_freq(freqs, &num_freqs, bss->freq);
 +                      if (num_freqs == max_freqs)
 +                              break;
 +              }
 +      }
 +
 +      if (num_freqs == 0) {
 +              os_free(freqs);
 +              freqs = NULL;
 +      }
 +
 +      return freqs;
 +}
 +
 +
 +void wpas_connection_failed(struct wpa_supplicant *wpa_s, const u8 *bssid)
 +{
 +      int timeout;
 +      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) {
 +              wpa_dbg(wpa_s, MSG_DEBUG, "Ignore connection failure "
 +                      "indication since interface has been put into "
 +                      "disconnected state");
 +              return;
 +      }
 +
 +      /*
 +       * Add the failed BSSID into the blacklist and speed up next scan
 +       * attempt if there could be other APs that could accept association.
 +       * The current blacklist count indicates how many times we have tried
 +       * connecting to this AP and multiple attempts mean that other APs are
 +       * either not available or has already been tried, so that we can start
 +       * increasing the delay here to avoid constant scanning.
 +       */
 +      count = wpa_blacklist_add(wpa_s, bssid);
 +      if (count == 1 && wpa_s->current_bss) {
 +              /*
 +               * This BSS was not in the blacklist before. If there is
 +               * another BSS available for the same ESS, we should try that
 +               * next. Otherwise, we may as well try this one once more
 +               * before allowing other, likely worse, ESSes to be considered.
 +               */
 +              freqs = get_bss_freqs_in_ess(wpa_s);
 +              if (freqs) {
 +                      wpa_dbg(wpa_s, MSG_DEBUG, "Another BSS in this ESS "
 +                              "has been seen; try it next");
 +                      wpa_blacklist_add(wpa_s, bssid);
 +                      /*
 +                       * On the next scan, go through only the known channels
 +                       * used in this ESS based on previous scans to speed up
 +                       * common load balancing use case.
 +                       */
 +                      os_free(wpa_s->next_scan_freqs);
 +                      wpa_s->next_scan_freqs = freqs;
 +              }
 +      }
 +
 +      /*
 +       * Add previous failure count in case the temporary blacklist was
 +       * cleared due to no other BSSes being available.
 +       */
 +      count += wpa_s->extra_blacklist_count;
 +
 +      if (count > 3 && wpa_s->current_ssid) {
 +              wpa_printf(MSG_DEBUG, "Continuous association failures - "
 +                         "consider temporary network disabling");
 +              wpas_auth_failed(wpa_s, "CONN_FAILED");
 +      }
 +
 +      switch (count) {
 +      case 1:
 +              timeout = 100;
 +              break;
 +      case 2:
 +              timeout = 500;
 +              break;
 +      case 3:
 +              timeout = 1000;
 +              break;
 +      case 4:
 +              timeout = 5000;
 +              break;
 +      default:
 +              timeout = 10000;
 +              break;
 +      }
 +
 +      wpa_dbg(wpa_s, MSG_DEBUG, "Blacklist count %d --> request scan in %d "
 +              "ms", count, timeout);
 +
 +      /*
 +       * TODO: if more than one possible AP is available in scan results,
 +       * could try the other ones before requesting a new scan.
 +       */
 +      wpa_supplicant_req_scan(wpa_s, timeout / 1000,
 +                              1000 * (timeout % 1000));
 +}
 +
 +
 +int wpas_driver_bss_selection(struct wpa_supplicant *wpa_s)
 +{
 +      return wpa_s->conf->ap_scan == 2 ||
 +              (wpa_s->drv_flags & WPA_DRIVER_FLAGS_BSS_SELECTION);
 +}
 +
 +
 +#if defined(CONFIG_CTRL_IFACE) || defined(CONFIG_CTRL_IFACE_DBUS_NEW)
 +int wpa_supplicant_ctrl_iface_ctrl_rsp_handle(struct wpa_supplicant *wpa_s,
 +                                            struct wpa_ssid *ssid,
 +                                            const char *field,
 +                                            const char *value)
 +{
 +#ifdef IEEE8021X_EAPOL
 +      struct eap_peer_config *eap = &ssid->eap;
 +
 +      wpa_printf(MSG_DEBUG, "CTRL_IFACE: response handle field=%s", field);
 +      wpa_hexdump_ascii_key(MSG_DEBUG, "CTRL_IFACE: response value",
 +                            (const u8 *) value, os_strlen(value));
 +
 +      switch (wpa_supplicant_ctrl_req_from_string(field)) {
 +      case WPA_CTRL_REQ_EAP_IDENTITY:
 +              os_free(eap->identity);
 +              eap->identity = (u8 *) os_strdup(value);
 +              eap->identity_len = os_strlen(value);
 +              eap->pending_req_identity = 0;
 +              if (ssid == wpa_s->current_ssid)
 +                      wpa_s->reassociate = 1;
 +              break;
 +      case WPA_CTRL_REQ_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;
 +              if (ssid == wpa_s->current_ssid)
 +                      wpa_s->reassociate = 1;
 +              break;
 +      case WPA_CTRL_REQ_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;
 +              if (ssid == wpa_s->current_ssid)
 +                      wpa_s->reassociate = 1;
 +              break;
 +      case WPA_CTRL_REQ_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:
 +              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);
 +              eap->pending_req_otp = NULL;
 +              eap->pending_req_otp_len = 0;
 +              break;
 +      case WPA_CTRL_REQ_EAP_PASSPHRASE:
 +              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;
++      case WPA_CTRL_REQ_PSK_PASSPHRASE:
++              if (wpa_config_set(ssid, "psk", value, 0) < 0)
++                      return -1;
++              ssid->mem_only_psk = 1;
++              if (ssid->passphrase)
++                      wpa_config_update_psk(ssid);
++              if (wpa_s->wpa_state == WPA_SCANNING && !wpa_s->scanning)
++                      wpa_supplicant_req_scan(wpa_s, 0, 0);
++              break;
 +      default:
 +              wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown field '%s'", field);
 +              return -1;
 +      }
 +
 +      return 0;
 +#else /* IEEE8021X_EAPOL */
 +      wpa_printf(MSG_DEBUG, "CTRL_IFACE: IEEE 802.1X not included");
 +      return -1;
 +#endif /* IEEE8021X_EAPOL */
 +}
 +#endif /* CONFIG_CTRL_IFACE || CONFIG_CTRL_IFACE_DBUS_NEW */
 +
 +
 +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;
 +
 +      if (ssid->disabled)
 +              return 1;
 +
 +      if (wpa_s->drv_capa_known)
 +              drv_enc = wpa_s->drv_enc;
 +      else
 +              drv_enc = (unsigned int) -1;
 +
 +      for (i = 0; i < NUM_WEP_KEYS; i++) {
 +              size_t len = ssid->wep_key_len[i];
 +              if (len == 0)
 +                      continue;
 +              if (len == 5 && (drv_enc & WPA_DRIVER_CAPA_ENC_WEP40))
 +                      continue;
 +              if (len == 13 && (drv_enc & WPA_DRIVER_CAPA_ENC_WEP104))
 +                      continue;
 +              if (len == 16 && (drv_enc & WPA_DRIVER_CAPA_ENC_WEP128))
 +                      continue;
 +              return 1; /* invalid WEP key */
 +      }
 +
 +      if (wpa_key_mgmt_wpa_psk(ssid->key_mgmt) && !ssid->psk_set &&
-                   ifs->current_ssid->mode == WPAS_MODE_P2P_GO)
++          (!ssid->passphrase || ssid->ssid_len != 0) && !ssid->ext_psk &&
++          !ssid->mem_only_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)
 +              return 1;
 +      if (wpa_s->global->conc_pref == WPA_CONC_PREF_STA)
 +              return 0;
 +      return -1;
 +}
 +
 +
 +void wpas_auth_failed(struct wpa_supplicant *wpa_s, char *reason)
 +{
 +      struct wpa_ssid *ssid = wpa_s->current_ssid;
 +      int dur;
 +      struct os_reltime now;
 +
 +      if (ssid == NULL) {
 +              wpa_printf(MSG_DEBUG, "Authentication failure but no known "
 +                         "SSID block");
 +              return;
 +      }
 +
 +      if (ssid->key_mgmt == WPA_KEY_MGMT_WPS)
 +              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 > 10)
 +              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;
 +
 +      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 reason=%s",
 +              ssid->id, wpa_ssid_txt(ssid->ssid, ssid->ssid_len),
 +              ssid->auth_failures, dur, reason);
 +}
 +
 +
 +void wpas_clear_temp_disabled(struct wpa_supplicant *wpa_s,
 +                            struct wpa_ssid *ssid, int clear_failures)
 +{
 +      if (ssid == NULL)
 +              return;
 +
 +      if (ssid->disabled_until.sec) {
 +              wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_REENABLED
 +                      "id=%d ssid=\"%s\"",
 +                      ssid->id, wpa_ssid_txt(ssid->ssid, ssid->ssid_len));
 +      }
 +      ssid->disabled_until.sec = 0;
 +      ssid->disabled_until.usec = 0;
 +      if (clear_failures)
 +              ssid->auth_failures = 0;
 +}
 +
 +
 +int disallowed_bssid(struct wpa_supplicant *wpa_s, const u8 *bssid)
 +{
 +      size_t i;
 +
 +      if (wpa_s->disallow_aps_bssid == NULL)
 +              return 0;
 +
 +      for (i = 0; i < wpa_s->disallow_aps_bssid_count; i++) {
 +              if (os_memcmp(wpa_s->disallow_aps_bssid + i * ETH_ALEN,
 +                            bssid, ETH_ALEN) == 0)
 +                      return 1;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +int disallowed_ssid(struct wpa_supplicant *wpa_s, const u8 *ssid,
 +                  size_t ssid_len)
 +{
 +      size_t i;
 +
 +      if (wpa_s->disallow_aps_ssid == NULL || ssid == NULL)
 +              return 0;
 +
 +      for (i = 0; i < wpa_s->disallow_aps_ssid_count; i++) {
 +              struct wpa_ssid_value *s = &wpa_s->disallow_aps_ssid[i];
 +              if (ssid_len == s->ssid_len &&
 +                  os_memcmp(ssid, s->ssid, ssid_len) == 0)
 +                      return 1;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +/**
 + * wpas_request_connection - Request a new connection
 + * @wpa_s: Pointer to the network interface
 + *
 + * This function is used to request a new connection to be found. It will mark
 + * the interface to allow reassociation and request a new scan to find a
 + * suitable network to connect to.
 + */
 +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;
 +      wpa_s->reassociate = 1;
 +
 +      if (wpa_supplicant_fast_associate(wpa_s) != 1)
 +              wpa_supplicant_req_scan(wpa_s, 0, 0);
 +      else
 +              wpa_s->reattach = 0;
 +}
 +
 +
 +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 get_shared_radio_freqs_data(struct wpa_supplicant *wpa_s,
 +                              struct wpa_used_freq_data *freqs_data,
 +                              unsigned int len)
 +{
 +      struct wpa_supplicant *ifs;
 +      u8 bssid[ETH_ALEN];
 +      int freq;
 +      unsigned int idx = 0, i;
 +
 +      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);
 +
 +      dl_list_for_each(ifs, &wpa_s->radio->ifaces, struct wpa_supplicant,
 +                       radio_list) {
 +              if (idx == len)
 +                      break;
 +
 +              if (ifs->current_ssid == NULL || ifs->assoc_freq == 0)
 +                      continue;
 +
 +              if (ifs->current_ssid->mode == WPAS_MODE_AP ||
-                       freqs_data[i].flags = ifs->current_ssid->p2p_group ?
++                  ifs->current_ssid->mode == WPAS_MODE_P2P_GO ||
++                  ifs->current_ssid->mode == WPAS_MODE_MESH)
 +                      freq = ifs->current_ssid->frequency;
 +              else if (wpa_drv_get_bssid(ifs, bssid) == 0)
 +                      freq = ifs->assoc_freq;
 +              else
 +                      continue;
 +
 +              /* 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 9980dbcdd910ea6eb720d737850670a051055314,0000000000000000000000000000000000000000..68c04768752b43872a43f5d26ab1783c33e3948c
mode 100644,000000..100644
--- /dev/null
@@@ -1,1512 -1,0 +1,1554 @@@
- #     EAP workarounds are disabled with eap_workarounds=0.
 +##### Example wpa_supplicant configuration file ###############################
 +#
 +# ***** Please check wpa_supplicant.conf(5) for details on these options *****
 +#
 +# This file describes configuration file format and lists all available option.
 +# Please also take a look at simpler configuration examples in 'examples'
 +# subdirectory.
 +#
 +# Empty lines and lines starting with # are ignored
 +
 +# NOTE! This file may contain password information and should probably be made
 +# readable only by root user on multiuser systems.
 +
 +# Note: All file paths in this configuration file should use full (absolute,
 +# not relative to working directory) path in order to allow working directory
 +# to be changed. This can happen if wpa_supplicant is run in the background.
 +
 +# Whether to allow wpa_supplicant to update (overwrite) configuration
 +#
 +# This option can be used to allow wpa_supplicant to overwrite configuration
 +# file whenever configuration is changed (e.g., new network block is added with
 +# wpa_cli or wpa_gui, or a password is changed). This is required for
 +# 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
 +
 +# global configuration (shared by all network blocks)
 +#
 +# Parameters for the control interface. If this is specified, wpa_supplicant
 +# will open a control interface that is available for external programs to
 +# manage wpa_supplicant. The meaning of this string depends on which control
 +# interface mechanism is used. For all cases, the existence of this parameter
 +# in configuration is used to determine whether the control interface is
 +# enabled.
 +#
 +# For UNIX domain sockets (default on Linux and BSD): This is a directory that
 +# will be created for UNIX domain sockets for listening to requests from
 +# external programs (CLI/GUI, etc.) for status information and configuration.
 +# The socket file will be named based on the interface name, so multiple
 +# wpa_supplicant processes can be run at the same time if more than one
 +# interface is used.
 +# /var/run/wpa_supplicant is the recommended directory for sockets and by
 +# default, wpa_cli will use it when trying to connect with wpa_supplicant.
 +#
 +# Access control for the control interface can be configured by setting the
 +# directory to allow only members of a group to use sockets. This way, it is
 +# possible to run wpa_supplicant as root (since it needs to change network
 +# configuration and open raw sockets) and still allow GUI/CLI components to be
 +# run as non-root users. However, since the control interface can be used to
 +# change the network configuration, this access needs to be protected in many
 +# cases. By default, wpa_supplicant is configured to use gid 0 (root). If you
 +# want to allow non-root users to use the control interface, add a new group
 +# and change this value to match with that group. Add users that should have
 +# control interface access to this group. If this variable is commented out or
 +# not included in the configuration file, group will not be changed from the
 +# value it got by default when the directory or socket was created.
 +#
 +# When configuring both the directory and group, use following format:
 +# DIR=/var/run/wpa_supplicant GROUP=wheel
 +# DIR=/var/run/wpa_supplicant GROUP=0
 +# (group can be either group name or gid)
 +#
 +ctrl_interface=/var/run/wpa_supplicant
 +
 +# IEEE 802.1X/EAPOL version
 +# wpa_supplicant is implemented based on IEEE Std 802.1X-2004 which defines
 +# EAPOL version 2. However, there are many APs that do not handle the new
 +# version number correctly (they seem to drop the frames completely). In order
 +# 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
 +# By default, wpa_supplicant requests driver to perform AP scanning and then
 +# uses the scan results to select a suitable AP. Another alternative is to
 +# allow the driver to take care of AP scanning and selection and use
 +# wpa_supplicant just to process EAPOL frames based on IEEE 802.11 association
 +# information from the driver.
 +# 1: wpa_supplicant initiates scanning and AP selection; if no APs matching to
 +#    the currently enabled networks are found, a new network (IBSS or AP mode
 +#    operation) may be initialized (if configured) (default)
 +# 0: driver takes care of scanning, AP selection, and IEEE 802.11 association
 +#    parameters (e.g., WPA IE generation); this mode can also be used with
 +#    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,
 +#    the network blocks in the configuration file are tried one by one until
 +#    the driver reports successful association; each network block should have
 +#    explicit security policy (i.e., only one option in the lists) for
 +#    key_mgmt, pairwise, group, proto variables
 +#
 +# For use in FreeBSD with the wlan module ap_scan must be set to 1.
++#
 +# When using IBSS or AP mode, ap_scan=2 mode can force the new network to be
 +# created immediately regardless of scan results. ap_scan=1 mode will first try
 +# to scan for existing networks and only if no matches with the enabled
 +# 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
 +
 +# OpenSSL Engine support
 +# These options can be used to load OpenSSL engines.
 +# The two engines that are supported currently are shown below:
 +# They are both from the opensc project (http://www.opensc.org/)
 +# By default no engines are loaded.
 +# make the opensc engine available
 +#opensc_engine_path=/usr/lib/opensc/engine_opensc.so
 +# make the pkcs11 engine available
 +#pkcs11_engine_path=/usr/lib/opensc/engine_pkcs11.so
 +# 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
 +# are included statically in the build, so these lines are not needed
 +#load_dynamic_eap=/usr/lib/wpa_supplicant/eap_tls.so
 +#load_dynamic_eap=/usr/lib/wpa_supplicant/eap_md5.so
 +
 +# Driver interface parameters
 +# 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"
 +
 +# Country code
 +# The ISO/IEC alpha2 country code for the country in which this device is
 +# currently operating.
 +#country=US
 +
 +# Maximum lifetime for PMKSA in seconds; default 43200
 +#dot11RSNAConfigPMKLifetime=43200
 +# Threshold for reauthentication (percentage of PMK lifetime); default 70
 +#dot11RSNAConfigPMKReauthThreshold=70
 +# Timeout for security association negotiation in seconds; default 60
 +#dot11RSNAConfigSATimeout=60
 +
 +# Wi-Fi Protected Setup (WPS) parameters
 +
 +# Universally Unique IDentifier (UUID; see RFC 4122) of the device
 +# If not configured, UUID will be generated based on the local MAC address.
 +#uuid=12345678-9abc-def0-1234-56789abcdef0
 +
 +# Device Name
 +# User-friendly description of device; up to 32 octets encoded in UTF-8
 +#device_name=Wireless Client
 +
 +# Manufacturer
 +# The manufacturer of the device (up to 64 ASCII characters)
 +#manufacturer=Company
 +
 +# Model Name
 +# Model of the device (up to 32 ASCII characters)
 +#model_name=cmodel
 +
 +# Model Number
 +# Additional device description (up to 32 ASCII characters)
 +#model_number=123
 +
 +# Serial Number
 +# Serial number of the device (up to 32 characters)
 +#serial_number=12345
 +
 +# Primary Device Type
 +# Used format: <categ>-<OUI>-<subcateg>
 +# categ = Category as an integer value
 +# OUI = OUI and type octet as a 4-octet hex-encoded value; 0050F204 for
 +#       default WPS OUI
 +# subcateg = OUI-specific Sub Category as an integer value
 +# Examples:
 +#   1-0050F204-1 (Computer / PC)
 +#   1-0050F204-2 (Computer / Server)
 +#   5-0050F204-1 (Storage / NAS)
 +#   6-0050F204-1 (Network Infrastructure / AP)
 +#device_type=1-0050F204-1
 +
 +# OS Version
 +# 4-octet operating system version number (hex string)
 +#os_version=01020300
 +
 +# Config Methods
 +# List of the supported 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
 +# For WSC 1.0:
 +#config_methods=label display push_button keypad
 +# For WSC 2.0:
 +#config_methods=label virtual_display virtual_push_button keypad
 +
 +# Credential processing
 +#   0 = process received credentials internally (default)
 +#   1 = do not process received credentials; just pass them over ctrl_iface to
 +#     external program(s)
 +#   2 = process received credentials internally and pass them over ctrl_iface
 +#     to external program(s)
 +#wps_cred_processing=0
 +
 +# Vendor attribute in WPS M1, e.g., Windows 7 Vertical Pairing
 +# The vendor attribute contents to be added in M1 (hex string)
 +#wps_vendor_ext_m1=000137100100020001
 +
 +# NFC password token for WPS
 +# These parameters can be used to configure a fixed NFC password token for the
 +# station. This can be generated, e.g., with nfc_pw_token. When these
 +# parameters are used, the station is assumed to be deployed with a NFC tag
 +# that includes the matching NFC password token (e.g., written based on the
 +# NDEF record from nfc_pw_token).
 +#
 +#wps_nfc_dev_pw_id: Device Password ID (16..65535)
 +#wps_nfc_dh_pubkey: Hexdump of DH Public Key
 +#wps_nfc_dh_privkey: Hexdump of DH Private Key
 +#wps_nfc_dev_pw: Hexdump of Device Password
 +
++# Priority for the networks added through WPS
++# This priority value will be set to each network profile that is added
++# by executing the WPS protocol.
++#wps_priority=0
++
 +# Maximum number of BSS entries to keep in memory
 +# Default: 200
 +# This can be used to limit memory use on the BSS entries (cached scan
 +# results). A larger value may be needed in environments that have huge number
 +# of APs when using ap_scan=1 mode.
 +#bss_max_count=200
 +
 +# Automatic scan
 +# 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=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>
 +#autoscan=periodic:30
 +# 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)
 +# 1 = only include configured SSIDs in scan results/BSS table
 +#filter_ssids=0
 +
 +# Password (and passphrase, etc.) backend for external storage
 +# format: <backend name>[:<optional backend parameters>]
 +#ext_password_backend=test:pw1=password|pw2=testing
 +
++
++# Disable P2P functionality
++# p2p_disabled=1
++
 +# Timeout in seconds to detect STA inactivity (default: 300 seconds)
 +#
 +# This timeout value is used in P2P GO mode to clean up
 +# 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
 +# with the global okc=1 parameter or with the per-network
 +# proactive_key_caching=1 parameter. With okc=1, OKC is enabled by default, but
 +# can be disabled with per-network proactive_key_caching=0 parameter.
 +#okc=0
 +
 +# Protected Management Frames default
 +# This parameter can be used to set the default behavior for the ieee80211w
 +# parameter. By default, PMF is disabled unless enabled with the global pmf=1/2
 +# parameter or with the per-network ieee80211w=1/2 parameter. With pmf=1/2, PMF
 +# is enabled/required by default, but can be disabled with the per-network
 +# ieee80211w parameter.
 +#pmf=0
 +
 +# Enabled SAE finite cyclic groups in preference order
 +# By default (if this parameter is not set), the mandatory group 19 (ECC group
 +# defined over a 256-bit prime order field) is preferred, but other groups are
 +# also enabled. If this parameter is set, the groups will be tried in the
 +# indicated order. The group values are listed in the IANA registry:
 +# http://www.iana.org/assignments/ipsec-registry/ipsec-registry.xml#ipsec-registry-9
 +#sae_groups=21 20 19 26 25
 +
 +# Default value for DTIM period (if not overridden in network block)
 +#dtim_period=2
 +
 +# Default value for Beacon interval (if not overridden in network block)
 +#beacon_int=100
 +
 +# Additional vendor specific elements for Beacon and Probe Response frames
 +# This parameter can be used to add additional vendor specific element(s) into
 +# the end of the Beacon and Probe Response frames. The format for these
 +# element(s) is a hexdump of the raw information elements (id+len+payload for
 +# one or more elements). This is used in AP and P2P GO modes.
 +#ap_vendor_elements=dd0411223301
 +
 +# Ignore scan results older than request
 +#
 +# The driver may have a cache of scan results that makes it return
 +# information that is older than our scan trigger. This parameter can
 +# be used to configure such old information to be ignored instead of
 +# allowing it to update the internal BSS table.
 +#ignore_old_scan_res=0
 +
 +# scan_cur_freq: Whether to scan only the current frequency
 +# 0:  Scan all available frequencies. (Default)
 +# 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
 +
 +# Homogenous ESS identifier
 +# If this is set, scans will be used to request response only from BSSes
 +# belonging to the specified Homogeneous ESS. This is used only if interworking
 +# is enabled.
 +# hessid=00:11:22:33:44:55
 +
 +# Automatic network selection behavior
 +# 0 = do not automatically go through Interworking network selection
 +#     (i.e., require explicit interworking_select command for this; default)
 +# 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
 +
 +# credential block
 +#
 +# Each credential used for automatic network selection is configured as a set
 +# of parameters that are compared to the information advertised by the APs when
 +# interworking_select and interworking_connect commands are used.
 +#
 +# 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
 +#     (and similarly in struct wpa_ssid for network blocks) to change the
 +#     Interworking automatic networking selection behavior. The matching
 +#     network (based on either an enabled network block or a credential)
 +#     with the highest priority value will be selected.
 +#
 +# pcsc: Use PC/SC and SIM/USIM card
 +#
 +# realm: Home Realm for Interworking
 +#
 +# username: Username for Interworking network selection
 +#
 +# password: Password for Interworking network selection
 +#
 +# ca_cert: CA certificate for Interworking network selection
 +#
 +# client_cert: File path to client certificate file (PEM/DER)
 +#     This field is used with Interworking networking selection for a case
 +#     where client certificate/private key is used for authentication
 +#     (EAP-TLS). Full path to the file should be used since working
 +#     directory may change when wpa_supplicant is run in the background.
 +#
 +#     Alternatively, a named configuration blob can be used by setting
 +#     this to blob://blob_name.
 +#
 +# private_key: File path to client private key file (PEM/DER/PFX)
 +#     When PKCS#12/PFX file (.p12/.pfx) is used, client_cert should be
 +#     commented out. Both the private key and certificate will be read
 +#     from the PKCS#12 file in this case. Full path to the file should be
 +#     used since working directory may change when wpa_supplicant is run
 +#     in the background.
 +#
 +#     Windows certificate store can be used by leaving client_cert out and
 +#     configuring private_key in one of the following formats:
 +#
 +#     cert://substring_to_match
 +#
 +#     hash://certificate_thumbprint_in_hex
 +#
 +#     For example: private_key="hash://63093aa9c47f56ae88334c7b65a4"
 +#
 +#     Note that when running wpa_supplicant as an application, the user
 +#     certificate store (My user account) is used, whereas computer store
 +#     (Computer account) is used when running wpasvc as a service.
 +#
 +#     Alternatively, a named configuration blob can be used by setting
 +#     this to blob://blob_name.
 +#
 +# private_key_passwd: Password for private key file
 +#
 +# imsi: IMSI in <MCC> | <MNC> | '-' | <MSIN> format
 +#
 +# milenage: Milenage parameters for SIM/USIM simulator in <Ki>:<OPc>:<SQN>
 +#     format
 +#
 +# 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. 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
 +#     Roaming Consortium OI that can be used to determine which access
 +#     points support authentication with this credential. This is an
 +#     alternative to the use of the realm parameter. When using Roaming
 +#     Consortium to match the network, the EAP parameters need to be
 +#     pre-configured with the credential since the NAI Realm information
 +#     may not be available or fetched.
 +#
 +# eap: Pre-configured EAP method
 +#     This optional field can be used to specify which EAP method will be
 +#     used with this credential. If not set, the EAP method is selected
 +#     automatically based on ANQP information (e.g., NAI Realm).
 +#
 +# phase1: Pre-configure Phase 1 (outer authentication) parameters
 +#     This optional field is used with like the 'eap' parameter.
 +#
 +# phase2: Pre-configure Phase 2 (inner authentication) parameters
 +#     This optional field is used with like the 'eap' parameter.
 +#
 +# excluded_ssid: Excluded SSID
 +#     This optional field can be used to excluded specific SSID(s) from
 +#     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={
 +#     realm="example.com"
 +#     username="user@example.com"
 +#     password="password"
 +#     ca_cert="/etc/wpa_supplicant/ca.pem"
 +#     domain="example.com"
 +#}
 +#
 +#cred={
 +#     imsi="310026-000000000"
 +#     milenage="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82"
 +#}
 +#
 +#cred={
 +#     realm="example.com"
 +#     username="user"
 +#     password="password"
 +#     ca_cert="/etc/wpa_supplicant/ca.pem"
 +#     domain="example.com"
 +#     roaming_consortium=223344
 +#     eap=TTLS
 +#     phase2="auth=MSCHAPV2"
 +#}
 +
 +# Hotspot 2.0
 +# hs20=1
 +
 +# network block
 +#
 +# Each network (usually AP's sharing the same SSID) is configured as a separate
 +# block in this configuration file. The network blocks are in preference order
 +# (the first match is used).
 +#
 +# network block fields:
 +#
 +# disabled:
 +#     0 = this network can be used (default)
 +#     1 = this network block is disabled (can be enabled through ctrl_iface,
 +#         e.g., with wpa_cli or wpa_gui)
 +#
 +# id_str: Network identifier string for external scripts. This value is passed
 +#     to external action script through wpa_cli as WPA_ID_STR environment
 +#     variable to make it easier to do network specific configuration.
 +#
 +# ssid: SSID (mandatory); network name in one of the optional formats:
 +#     - an ASCII string with double quotation
 +#     - a hex string (two characters per octet of SSID)
 +#     - a printf-escaped ASCII string P"<escaped string>"
 +#
 +# scan_ssid:
 +#     0 = do not scan this SSID with specific Probe Request frames (default)
 +#     1 = scan with SSID-specific Probe Request frames (this can be used to
 +#         find APs that hide (do not broadcast) SSID or use multiple SSIDs;
 +#         this will add latency to scanning, so enable this only when needed)
 +#
 +# bssid: BSSID (optional); if set, this network block is used only when
 +#     associating with the AP using the configured BSSID
 +#
 +# priority: priority group (integer)
 +# By default, all networks will get same priority group (0). If some of the
 +# networks are more desirable, this field can be used to change the order in
 +# which wpa_supplicant goes through the networks when selecting a BSS. The
 +# priority groups will be iterated in decreasing priority (i.e., the larger the
 +# priority value, the sooner the network is matched against the scan results).
 +# Within each priority group, networks will be selected based on security
 +# policy, signal strength, etc.
 +# Please note that AP scanning with scan_ssid=1 and ap_scan=2 mode are not
 +# using this priority to select the order for scanning. Instead, they try the
 +# networks in the order that they are listed in the configuration file.
 +#
 +# mode: IEEE 802.11 operation mode
 +# 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
 +# 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.
 +#
 +# frequency: Channel frequency in megahertz (MHz) for IBSS, e.g.,
 +# 2412 = IEEE 802.11b/g channel 1. This value is used to configure the initial
 +# channel for IBSS (adhoc) networks. It is ignored in the infrastructure mode.
 +# In addition, this value is only used by the station that creates the IBSS. If
 +# an IBSS network with the configured SSID is already present, the frequency of
 +# the network will be used instead of this configured value.
 +#
 +# scan_freq: List of frequencies to scan
 +# Space-separated list of frequencies in MHz to scan when searching for this
 +# BSS. If the subset of channels used by the network is known, this option can
 +# be used to optimize scanning to not occur on channels that the network does
 +# not use. Example: scan_freq=2412 2437 2462
 +#
 +# freq_list: Array of allowed frequencies
 +# Space-separated list of frequencies in MHz to allow for selecting the BSS. If
 +# set, scan results that do not match any of the specified frequencies are not
 +# considered when selecting a BSS.
 +#
 +# This can also be set on the outside of the network block. In this case,
 +# it limits the frequencies that will be scanned.
 +#
 +# bgscan: Background scanning
 +# wpa_supplicant behavior for background scanning can be specified by
 +# configuring a bgscan module. These modules are responsible for requesting
 +# background scans for the purpose of roaming within an ESS (i.e., within a
 +# single network block with all the APs using the same SSID). The bgscan
 +# parameter uses following format: "<bgscan module name>:<module parameters>"
 +# Following bgscan modules are available:
 +# simple - Periodic background scans based on signal strength
 +# bgscan="simple:<short bgscan interval in seconds>:<signal strength threshold>:
 +# <long interval>"
 +# bgscan="simple:30:-45:300"
 +# learn - Learn channels used by the network and try to avoid bgscans on other
 +# channels (experimental)
 +# 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
 +# RSN = WPA2/IEEE 802.11i (also WPA2 can be used as an alias for RSN)
 +# If not set, this defaults to: WPA RSN
 +#
 +# key_mgmt: list of accepted authenticated key management protocols
 +# WPA-PSK = WPA pre-shared key (this requires 'psk' field)
 +# WPA-EAP = WPA using EAP authentication
 +# IEEE8021X = IEEE 802.1X using EAP authentication and (optionally) dynamically
 +#     generated WEP keys
 +# NONE = WPA is not used; plaintext or static WEP could be used
 +# WPA-PSK-SHA256 = Like WPA-PSK but using stronger SHA256-based algorithms
 +# WPA-EAP-SHA256 = Like WPA-EAP but using stronger SHA256-based algorithms
 +# If not set, this defaults to: WPA-PSK WPA-EAP
 +#
 +# ieee80211w: whether management frame protection is enabled
 +# 0 = disabled (default unless changed with the global pmf parameter)
 +# 1 = optional
 +# 2 = required
 +# The most common configuration options for this based on the PMF (protected
 +# management frames) certification program are:
 +# PMF enabled: ieee80211w=1 and key_mgmt=WPA-EAP WPA-EAP-SHA256
 +# PMF required: ieee80211w=2 and key_mgmt=WPA-EAP-SHA256
 +# (and similarly for WPA-PSK and WPA-WPSK-SHA256 if WPA2-Personal is used)
 +#
 +# auth_alg: list of allowed IEEE 802.11 authentication algorithms
 +# OPEN = Open System authentication (required for WPA/WPA2)
 +# SHARED = Shared Key authentication (requires static WEP keys)
 +# LEAP = LEAP/Network EAP (only used with LEAP)
 +# If not set, automatic selection is used (Open System with LEAP enabled if
 +# LEAP is allowed as one of the EAP methods).
 +#
 +# pairwise: list of accepted pairwise (unicast) ciphers for WPA
 +# CCMP = AES in Counter mode with CBC-MAC [RFC 3610, IEEE 802.11i/D7.0]
 +# TKIP = Temporal Key Integrity Protocol [IEEE 802.11i/D7.0]
 +# NONE = Use only Group Keys (deprecated, should not be included if APs support
 +#     pairwise keys)
 +# If not set, this defaults to: CCMP TKIP
 +#
 +# group: list of accepted group (broadcast/multicast) ciphers for WPA
 +# CCMP = AES in Counter mode with CBC-MAC [RFC 3610, IEEE 802.11i/D7.0]
 +# TKIP = Temporal Key Integrity Protocol [IEEE 802.11i/D7.0]
 +# WEP104 = WEP (Wired Equivalent Privacy) with 104-bit key
 +# WEP40 = WEP (Wired Equivalent Privacy) with 40-bit key [IEEE 802.11]
 +# If not set, this defaults to: CCMP TKIP WEP104 WEP40
 +#
 +# psk: WPA preshared key; 256-bit pre-shared key
 +# The key used in WPA-PSK mode can be entered either as 64 hex-digits, i.e.,
 +# 32 bytes or as an ASCII passphrase (in which case, the real PSK will be
 +# generated using the passphrase and SSID). ASCII passphrase must be between
 +# 8 and 63 characters (inclusive). ext:<name of external PSK field> format can
 +# be used to indicate that the PSK/passphrase is stored in external storage.
 +# This field is not needed, if WPA-EAP is used.
 +# Note: Separate tool, wpa_passphrase, can be used to generate 256-bit keys
 +# from ASCII passphrase. This process uses lot of CPU and wpa_supplicant
 +# startup and reconfiguration time can be optimized by generating the PSK only
 +# only when the passphrase or SSID has actually changed.
 +#
++# mem_only_psk: Whether to keep PSK/passphrase only in memory
++# 0 = allow psk/passphrase to be stored to the configuration file
++# 1 = do not store psk/passphrase to the configuration file
++#mem_only_psk=0
++#
 +# eapol_flags: IEEE 802.1X/EAPOL options (bit field)
 +# Dynamic WEP key required for non-WPA mode
 +# 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 (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
 +# SSID, are allowed when selecting a BSS from scan results.
 +# 0 = disabled (default)
 +# 1 = enabled
 +#
 +# proactive_key_caching:
 +# Enable/disable opportunistic PMKSA caching for WPA2.
 +# 0 = disabled (default unless changed with the global okc parameter)
 +# 1 = enabled
 +#
 +# wep_key0..3: Static WEP key (ASCII in double quotation, e.g. "abcde" or
 +# hex without quotation, e.g., 0102030405)
 +# wep_tx_keyidx: Default WEP key index (TX) (0..3)
 +#
 +# peerkey: Whether PeerKey negotiation for direct links (IEEE 802.11e DLS) is
 +# allowed. This is only used with RSN/WPA2.
 +# 0 = disabled (default)
 +# 1 = enabled
 +#peerkey=1
 +#
 +# wpa_ptk_rekey: Maximum lifetime for PTK in seconds. This can be used to
 +# enforce rekeying of PTK to mitigate some attacks against TKIP deficiencies.
 +#
 +# Following fields are only used with internal EAP implementation.
 +# eap: space-separated list of accepted EAP methods
 +#     MD5 = EAP-MD5 (unsecure and does not generate keying material ->
 +#                     cannot be used with WPA; to be used as a Phase 2 method
 +#                     with EAP-PEAP or EAP-TTLS)
 +#       MSCHAPV2 = EAP-MSCHAPv2 (cannot be used separately with WPA; to be used
 +#             as a Phase 2 method with EAP-PEAP or EAP-TTLS)
 +#       OTP = EAP-OTP (cannot be used separately with WPA; to be used
 +#             as a Phase 2 method with EAP-PEAP or EAP-TTLS)
 +#       GTC = EAP-GTC (cannot be used separately with WPA; to be used
 +#             as a Phase 2 method with EAP-PEAP or EAP-TTLS)
 +#     TLS = EAP-TLS (client and server certificate)
 +#     PEAP = EAP-PEAP (with tunnelled EAP authentication)
 +#     TTLS = EAP-TTLS (with tunnelled EAP or PAP/CHAP/MSCHAP/MSCHAPV2
 +#                      authentication)
 +#     If not set, all compiled in methods are allowed.
 +#
 +# identity: Identity string for EAP
 +#     This field is also used to configure user NAI for
 +#     EAP-PSK/PAX/SAKE/GPSK.
 +# anonymous_identity: Anonymous identity string for EAP (to be used as the
 +#     unencrypted identity with EAP types that support different tunnelled
 +#     identity, e.g., EAP-TTLS). This field can also be used with
 +#     EAP-SIM/AKA/AKA' to store the pseudonym identity.
 +# password: Password string for EAP. This field can include either the
 +#     plaintext password (using ASCII or hex string) or a NtPasswordHash
 +#     (16-byte MD4 hash of password) in hash:<32 hex digits> format.
 +#     NtPasswordHash can only be used when the password is for MSCHAPv2 or
 +#     MSCHAP (EAP-MSCHAPv2, EAP-TTLS/MSCHAPv2, EAP-TTLS/MSCHAP, LEAP).
 +#     EAP-PSK (128-bit PSK), EAP-PAX (128-bit PSK), and EAP-SAKE (256-bit
 +#     PSK) is also configured using this field. For EAP-GPSK, this is a
 +#     variable length PSK. ext:<name of external password field> format can
 +#     be used to indicate that the password is stored in external storage.
 +# ca_cert: File path to CA certificate file (PEM/DER). This file can have one
 +#     or more trusted CA certificates. If ca_cert and ca_path are not
 +#     included, server certificate will not be verified. This is insecure and
 +#     a trusted CA certificate should always be configured when using
 +#     EAP-TLS/TTLS/PEAP. Full path should be used since working directory may
 +#     change when wpa_supplicant is run in the background.
 +#
 +#     Alternatively, this can be used to only perform matching of the server
 +#     certificate (SHA-256 hash of the DER encoded X.509 certificate). In
 +#     this case, the possible CA certificates in the server certificate chain
 +#     are ignored and only the server certificate is verified. This is
 +#     configured with the following format:
 +#     hash:://server/sha256/cert_hash_in_hex
 +#     For example: "hash://server/sha256/
 +#     5a1bc1296205e6fdbe3979728efe3920798885c1c4590b5f90f43222d239ca6a"
 +#
 +#     On Windows, trusted CA certificates can be loaded from the system
 +#     certificate store by setting this to cert_store://<name>, e.g.,
 +#     ca_cert="cert_store://CA" or ca_cert="cert_store://ROOT".
 +#     Note that when running wpa_supplicant as an application, the user
 +#     certificate store (My user account) is used, whereas computer store
 +#     (Computer account) is used when running wpasvc as a service.
 +# ca_path: Directory path for CA certificate files (PEM). This path may
 +#     contain multiple CA certificates in OpenSSL format. Common use for this
 +#     is to point to system trusted CA list which is often installed into
 +#     directory like /etc/ssl/certs. If configured, these certificates are
 +#     added to the list of trusted CAs. ca_cert may also be included in that
 +#     case, but it is not required.
 +# client_cert: File path to client certificate file (PEM/DER)
 +#     Full path should be used since working directory may change when
 +#     wpa_supplicant is run in the background.
 +#     Alternatively, a named configuration blob can be used by setting this
 +#     to blob://<blob name>.
 +# private_key: File path to client private key file (PEM/DER/PFX)
 +#     When PKCS#12/PFX file (.p12/.pfx) is used, client_cert should be
 +#     commented out. Both the private key and certificate will be read from
 +#     the PKCS#12 file in this case. Full path should be used since working
 +#     directory may change when wpa_supplicant is run in the background.
 +#     Windows certificate store can be used by leaving client_cert out and
 +#     configuring private_key in one of the following formats:
 +#     cert://substring_to_match
 +#     hash://certificate_thumbprint_in_hex
 +#     for example: private_key="hash://63093aa9c47f56ae88334c7b65a4"
 +#     Note that when running wpa_supplicant as an application, the user
 +#     certificate store (My user account) is used, whereas computer store
 +#     (Computer account) is used when running wpasvc as a service.
 +#     Alternatively, a named configuration blob can be used by setting this
 +#     to blob://<blob name>.
 +# private_key_passwd: Password for private key file (if left out, this will be
 +#     asked through control interface)
 +# dh_file: File path to DH/DSA parameters file (in PEM format)
 +#     This is an optional configuration file for setting parameters for an
 +#     ephemeral DH key exchange. In most cases, the default RSA
 +#     authentication does not use this configuration. However, it is possible
 +#     setup RSA to use ephemeral DH key exchange. In addition, ciphers with
 +#     DSA keys always use ephemeral DH keys. This can be used to achieve
 +#     forward secrecy. If the file is in DSA parameters format, it will be
 +#     automatically converted into DH params.
 +# subject_match: Substring to be matched against the subject of the
 +#     authentication server certificate. If this string is set, the server
 +#     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
 +#     contains one of the entries in an alternative subject name extension.
 +#     altSubjectName string is in following format: TYPE:VALUE
 +#     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")
 +#     'peapver' can be used to force which PEAP version (0 or 1) is used.
 +#     'peaplabel=1' can be used to force new label, "client PEAP encryption",
 +#     to be used during key derivation when PEAPv1 or newer. Most existing
 +#     PEAPv1 implementation seem to be using the old label, "client EAP
 +#     encryption", and wpa_supplicant is now using that as the default value.
 +#     Some servers, e.g., Radiator, may require peaplabel=1 configuration to
 +#     interoperate with PEAPv1; see eap_testing.txt for more details.
 +#     'peap_outer_success=0' can be used to terminate PEAP authentication on
 +#     tunneled EAP-Success. This is required with some RADIUS servers that
 +#     implement draft-josefsson-pppext-eap-tls-eap-05.txt (e.g.,
 +#     Lucent NavisRadius v4.4.0 with PEAP in "IETF Draft 5" mode)
 +#     include_tls_length=1 can be used to force wpa_supplicant to include
 +#     TLS Message Length field in all TLS messages even if they are not
 +#     fragmented.
 +#     sim_min_num_chal=3 can be used to configure EAP-SIM to require three
 +#     challenges (by default, it accepts 2 or 3)
 +#     result_ind=1 can be used to enable EAP-SIM and EAP-AKA to use
 +#     protected result indication.
 +#     'crypto_binding' option can be used to control PEAPv0 cryptobinding
 +#     behavior:
 +#      * 0 = do not use cryptobinding (default)
 +#      * 1 = use cryptobinding if server supports it
 +#      * 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). "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
 +# phase2 parameter when EAP-TLS is used within the inner tunnel):
 +# tls_allow_md5=1 - allow MD5-based certificate signatures (depending on the
 +#     TLS library, these may be disabled by default to enforce stronger
 +#     security)
 +# tls_disable_time_checks=1 - ignore certificate validity time (this requests
 +#     the TLS library to accept certificates even if they are not currently
 +#     valid, i.e., have expired or have not yet become valid; this should be
 +#     used only for testing purposes)
 +# tls_disable_session_ticket=1 - disable TLS Session Ticket extension
 +# tls_disable_session_ticket=0 - allow TLS Session Ticket extension to be used
 +#     Note: If not set, this is automatically set to 1 for EAP-TLS/PEAP/TTLS
 +#     as a workaround for broken authentication server implementations unless
++#     EAP workarounds are disabled with eap_workaround=0.
 +#     For EAP-FAST, this must be set to 0 (or left unconfigured for the
 +#     default value to be used automatically).
++# tls_disable_tlsv1_0=1 - disable use of TLSv1.0
 +# 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.
 +# ca_cert2: File path to CA certificate file. This file can have one or more
 +#     trusted CA certificates. If ca_cert2 and ca_path2 are not included,
 +#     server certificate will not be verified. This is insecure and a trusted
 +#     CA certificate should always be configured.
 +# ca_path2: Directory path for CA certificate files (PEM)
 +# client_cert2: File path to client certificate file
 +# private_key2: File path to client private key file
 +# 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. 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
 +#     fragmentation (e.g., EAP-TLS and EAP-PEAP). This value should be set
 +#     small enough to make the EAP messages fit in MTU of the network
 +#     interface used for EAPOL. The default value is suitable for most
 +#     cases.
 +#
 +# 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
 +#
 +# 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
 +#     provisioned or refreshed. Full path to the file should be used since
 +#     working directory may change when wpa_supplicant is run in the
 +#     background. Alternatively, a named configuration blob can be used by
 +#     setting this to blob://<blob name>
 +# phase1: fast_provisioning option can be used to enable in-line provisioning
 +#         of EAP-FAST credentials (PAC):
 +#         0 = disabled,
 +#         1 = allow unauthenticated provisioning,
 +#         2 = allow authenticated provisioning,
 +#         3 = allow both unauthenticated and authenticated provisioning
 +#     fast_max_pac_list_len=<num> option can be used to set the maximum
 +#             number of PAC entries to store in a PAC list (default: 10)
 +#     fast_pac_format=binary option can be used to select binary format for
 +#             storing PAC entries in order to save some space (the default
 +#             text format uses about 2.5 times the size of minimal binary
 +#             format)
 +#
 +# wpa_supplicant supports number of "EAP workarounds" to work around
 +# interoperability issues with incorrectly behaving authentication servers.
 +# These are enabled by default because some of the issues are present in large
 +# number of authentication servers. Strict EAP conformance mode can be
 +# configured by disabling workarounds with eap_workaround=0.
 +
 +# Station inactivity limit
 +#
 +# If a station does not send anything in ap_max_inactivity seconds, an
 +# empty data frame is sent to it in order to verify whether it is
 +# still in range. If this frame is not ACKed, the station will be
 +# disassociated and then deauthenticated. This feature is used to
 +# clear station table of old entries when the STAs move out of the
 +# range.
 +#
 +# The station can associate again with the AP if it is still in range;
 +# this inactivity poll is just used as a nicer way of verifying
 +# inactivity; i.e., client will not report broken connection because
 +# disassociation frame is not sent immediately without first polling
 +# the STA with a data frame.
 +# default: 300 (i.e., 5 minutes)
 +#ap_max_inactivity=300
 +
 +# DTIM period in Beacon intervals for AP mode (default: 2)
 +#dtim_period=2
 +
 +# 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
 +#
 +# disable_ht40: Whether HT-40 (802.11n) should be disabled.
 +# 0 = HT-40 enabled (if AP supports it)
 +# 1 = HT-40 disabled
 +#
 +# disable_sgi: Whether SGI (short guard interval) should be disabled.
 +# 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)
 +# ht_mcs="0xff 00 00 00 00 00 00 00 00 00 "   // Use MCS 0-7 only
 +# ht_mcs="0xff ff 00 00 00 00 00 00 00 00 "   // Use MCS 0-15 only
 +#
 +# disable_max_amsdu:  Whether MAX_AMSDU should be disabled.
 +# -1 = Do not make any changes.
 +# 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.
 +# 0-3 = Set AMPDU density (aka factor) to specified value.
 +
 +# disable_vht: Whether VHT should be disabled.
 +# 0 = VHT enabled (if AP supports it)
 +# 1 = VHT disabled
 +#
 +# vht_capa: VHT capabilities to set in the override
 +# vht_capa_mask: mask of VHT capabilities
 +#
 +# vht_rx_mcs_nss_1/2/3/4/5/6/7/8: override the MCS set for RX NSS 1-8
 +# vht_tx_mcs_nss_1/2/3/4/5/6/7/8: override the MCS set for TX NSS 1-8
 +#  0: MCS 0-7
 +#  1: MCS 0-8
 +#  2: MCS 0-9
 +#  3: not supported
 +
++##### Fast Session Transfer (FST) support #####################################
++#
++# The options in this section are only available when the build configuration
++# option CONFIG_FST is set while compiling hostapd. They allow this interface
++# to be a part of FST setup.
++#
++# FST is the transfer of a session from a channel to another channel, in the
++# same or different frequency bands.
++#
++# For detals, see IEEE Std 802.11ad-2012.
++
++# Identifier of an FST Group  the interface belongs to.
++#fst_group_id=bond0
++
++# Interface priority within the FST Group.
++# Announcing a higher priority for an interface means declaring it more
++# preferable for FST switch.
++# fst_priority is in 1..255 range with 1 being the lowest priority.
++#fst_priority=100
++
++# Default LLT value for this interface in milliseconds. The value used in case
++# no value provided during session setup. Default is 50 msec.
++# fst_llt is in 1..4294967 range (due to spec limitation, see 10.32.2.2
++# Transitioning between states).
++#fst_llt=100
++
 +# 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 RSN
 +network={
 +      ssid="ibss-rsn"
 +      key_mgmt=WPA-PSK
 +      proto=RSN
 +      psk="12345678"
 +      mode=1
 +      frequency=2412
 +      pairwise=CCMP
 +      group=CCMP
 +}
 +
 +# IBSS/ad-hoc network with WPA-None/TKIP (deprecated)
 +network={
 +      ssid="test adhoc"
 +      mode=1
 +      frequency=2412
 +      proto=WPA
 +      key_mgmt=WPA-NONE
 +      pairwise=NONE
 +      group=TKIP
 +      psk="secret passphrase"
 +}
 +
 +# open mesh network
 +network={
 +      ssid="test mesh"
 +      mode=5
 +      frequency=2437
 +      key_mgmt=NONE
 +}
 +
 +# secure (SAE + AMPE) network
 +network={
 +      ssid="secure mesh"
 +      mode=5
 +      frequency=2437
 +      key_mgmt=SAE
 +      psk="very 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 configuration blacklisting two APs - these will be ignored
 +# for this network.
 +network={
 +      ssid="example"
 +      psk="very secret passphrase"
 +      bssid_blacklist=02:11:22:33:44:55 02:22:aa:44:55:66
 +}
 +
 +# Example configuration limiting AP selection to a specific set of APs;
 +# any other AP not matching the masked address will be ignored.
 +network={
 +      ssid="example"
 +      psk="very secret passphrase"
 +      bssid_whitelist=02:55:ae:bc:00:00/ff:ff:ff:ff:00:00 00:00:77:66:55:44/00:00:ff:ff:ff:ff
 +}
 +
 +# Example config file that will only scan on channel 36.
 +freq_list=5180
 +network={
 +      key_mgmt=NONE
 +}
 +
 +
 +# Example MACsec configuration
 +#network={
 +#     key_mgmt=IEEE8021X
 +#     eap=TTLS
 +#     phase2="auth=PAP"
 +#     anonymous_identity="anonymous@example.com"
 +#     identity="user@example.com"
 +#     password="secretr"
 +#     ca_cert="/etc/cert/ca.pem"
 +#     eapol_flags=0
 +#     macsec_policy=1
 +#}
index 26ff216b02da4b2d0b9a0f93ef6287bba36a3e2e,0000000000000000000000000000000000000000..58df48c548ea592c0d1bb34ec38a2dcc11a8ea49
mode 100644,000000..100644
--- /dev/null
@@@ -1,1143 -1,0 +1,1170 @@@
- extern const char *wpa_supplicant_version;
- extern const char *wpa_supplicant_license;
 +/*
 + * wpa_supplicant - Internal definitions
 + * 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.
 + */
 +
 +#ifndef WPA_SUPPLICANT_I_H
 +#define WPA_SUPPLICANT_I_H
 +
 +#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_full_license1;
- extern const char *wpa_supplicant_full_license2;
- extern const char *wpa_supplicant_full_license3;
- extern const char *wpa_supplicant_full_license4;
- extern const char *wpa_supplicant_full_license5;
++extern const char *const wpa_supplicant_version;
++extern const char *const wpa_supplicant_license;
 +#ifndef CONFIG_NO_STDOUT_DEBUG
- #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 */
++extern const char *const wpa_supplicant_full_license1;
++extern const char *const wpa_supplicant_full_license2;
++extern const char *const wpa_supplicant_full_license3;
++extern const char *const wpa_supplicant_full_license4;
++extern const char *const wpa_supplicant_full_license5;
 +#endif /* CONFIG_NO_STDOUT_DEBUG */
 +
 +struct wpa_sm;
 +struct wpa_supplicant;
 +struct ibss_rsn;
 +struct scan_info;
 +struct wpa_bss;
 +struct wpa_scan_results;
 +struct hostapd_hw_modes;
 +struct wpa_driver_associate_params;
 +
 +/*
 + * Forward declarations of private structures used within the ctrl_iface
 + * backends. Other parts of wpa_supplicant do not have access to data stored in
 + * these structures.
 + */
 +struct ctrl_iface_priv;
 +struct ctrl_iface_global_priv;
 +struct wpas_dbus_priv;
 +
 +/**
 + * struct wpa_interface - Parameters for wpa_supplicant_add_iface()
 + */
 +struct wpa_interface {
 +      /**
 +       * confname - Configuration name (file or profile) name
 +       *
 +       * This can also be %NULL when a configuration file is not used. In
 +       * that case, ctrl_interface must be set to allow the interface to be
 +       * configured.
 +       */
 +      const char *confname;
 +
 +      /**
 +       * confanother - Additional configuration name (file or profile) name
 +       *
 +       * This can also be %NULL when the additional configuration file is not
 +       * used.
 +       */
 +      const char *confanother;
 +
-       u8 ssid[32];
 +      /**
 +       * ctrl_interface - Control interface parameter
 +       *
 +       * If a configuration file is not used, this variable can be used to
 +       * set the ctrl_interface parameter that would have otherwise been read
 +       * from the configuration file. If both confname and ctrl_interface are
 +       * set, ctrl_interface is used to override the value from configuration
 +       * file.
 +       */
 +      const char *ctrl_interface;
 +
 +      /**
 +       * driver - Driver interface name, or %NULL to use the default driver
 +       */
 +      const char *driver;
 +
 +      /**
 +       * driver_param - Driver interface parameters
 +       *
 +       * If a configuration file is not used, this variable can be used to
 +       * set the driver_param parameters that would have otherwise been read
 +       * from the configuration file. If both confname and driver_param are
 +       * set, driver_param is used to override the value from configuration
 +       * file.
 +       */
 +      const char *driver_param;
 +
 +      /**
 +       * ifname - Interface name
 +       */
 +      const char *ifname;
 +
 +      /**
 +       * bridge_ifname - Optional bridge interface name
 +       *
 +       * If the driver interface (ifname) is included in a Linux bridge
 +       * device, the bridge interface may need to be used for receiving EAPOL
 +       * frames. This can be enabled by setting this variable to enable
 +       * receiving of EAPOL frames from an additional interface.
 +       */
 +      const char *bridge_ifname;
 +
 +      /**
 +       * p2p_mgmt - Interface used for P2P management (P2P Device operations)
 +       *
 +       * Indicates whether wpas_p2p_init() must be called for this interface.
 +       * This is used only when the driver supports a dedicated P2P Device
 +       * interface that is not a network interface.
 +       */
 +      int p2p_mgmt;
 +};
 +
 +/**
 + * struct wpa_params - Parameters for wpa_supplicant_init()
 + */
 +struct wpa_params {
 +      /**
 +       * daemonize - Run %wpa_supplicant in the background
 +       */
 +      int daemonize;
 +
 +      /**
 +       * wait_for_monitor - Wait for a monitor program before starting
 +       */
 +      int wait_for_monitor;
 +
 +      /**
 +       * pid_file - Path to a PID (process ID) file
 +       *
 +       * If this and daemonize are set, process ID of the background process
 +       * will be written to the specified file.
 +       */
 +      char *pid_file;
 +
 +      /**
 +       * wpa_debug_level - Debugging verbosity level (e.g., MSG_INFO)
 +       */
 +      int wpa_debug_level;
 +
 +      /**
 +       * wpa_debug_show_keys - Whether keying material is included in debug
 +       *
 +       * This parameter can be used to allow keying material to be included
 +       * in debug messages. This is a security risk and this option should
 +       * not be enabled in normal configuration. If needed during
 +       * development or while troubleshooting, this option can provide more
 +       * details for figuring out what is happening.
 +       */
 +      int wpa_debug_show_keys;
 +
 +      /**
 +       * wpa_debug_timestamp - Whether to include timestamp in debug messages
 +       */
 +      int wpa_debug_timestamp;
 +
 +      /**
 +       * ctrl_interface - Global ctrl_iface path/parameter
 +       */
 +      char *ctrl_interface;
 +
 +      /**
 +       * ctrl_interface_group - Global ctrl_iface group
 +       */
 +      char *ctrl_interface_group;
 +
 +      /**
 +       * dbus_ctrl_interface - Enable the DBus control interface
 +       */
 +      int dbus_ctrl_interface;
 +
 +      /**
 +       * wpa_debug_file_path - Path of debug file or %NULL to use stdout
 +       */
 +      const char *wpa_debug_file_path;
 +
 +      /**
 +       * wpa_debug_syslog - Enable log output through syslog
 +       */
 +      int wpa_debug_syslog;
 +
 +      /**
 +       * wpa_debug_tracing - Enable log output through Linux tracing
 +       */
 +      int wpa_debug_tracing;
 +
 +      /**
 +       * override_driver - Optional driver parameter override
 +       *
 +       * This parameter can be used to override the driver parameter in
 +       * dynamic interface addition to force a specific driver wrapper to be
 +       * used instead.
 +       */
 +      char *override_driver;
 +
 +      /**
 +       * override_ctrl_interface - Optional ctrl_interface override
 +       *
 +       * This parameter can be used to override the ctrl_interface parameter
 +       * in dynamic interface addition to force a control interface to be
 +       * created.
 +       */
 +      char *override_ctrl_interface;
 +
 +      /**
 +       * entropy_file - Optional entropy file
 +       *
 +       * This parameter can be used to configure wpa_supplicant to maintain
 +       * its internal entropy store over restarts.
 +       */
 +      char *entropy_file;
++
++#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.
++       */
++      char *conf_p2p_dev;
++#endif /* CONFIG_P2P */
++
 +};
 +
 +struct p2p_srv_bonjour {
 +      struct dl_list list;
 +      struct wpabuf *query;
 +      struct wpabuf *resp;
 +};
 +
 +struct p2p_srv_upnp {
 +      struct dl_list list;
 +      u8 version;
 +      char *service;
 +};
 +
 +/**
 + * struct wpa_global - Internal, global data for all %wpa_supplicant interfaces
 + *
 + * This structure is initialized by calling wpa_supplicant_init() when starting
 + * %wpa_supplicant.
 + */
 +struct wpa_global {
 +      struct wpa_supplicant *ifaces;
 +      struct wpa_params params;
 +      struct ctrl_iface_global_priv *ctrl_iface;
 +      struct wpas_dbus_priv *dbus;
 +      void **drv_priv;
 +      size_t drv_count;
 +      struct os_time suspend_time;
 +      struct p2p_data *p2p;
 +      struct wpa_supplicant *p2p_init_wpa_s;
 +      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_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_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
 + */
 +enum offchannel_send_action_result {
 +      OFFCHANNEL_SEND_ACTION_SUCCESS /**< Frame was send and acknowledged */,
 +      OFFCHANNEL_SEND_ACTION_NO_ACK /**< Frame was sent, but not acknowledged
 +                                     */,
 +      OFFCHANNEL_SEND_ACTION_FAILED /**< Frame was not sent due to a failure
 +                                     */
 +};
 +
 +struct wps_ap_info {
 +      u8 bssid[ETH_ALEN];
 +      enum wps_ap_info_type {
 +              WPS_AP_NOT_SEL_REG,
 +              WPS_AP_SEL_REG,
 +              WPS_AP_SEL_REG_OUR
 +      } type;
 +      unsigned int tries;
 +      struct os_reltime last_attempt;
++      unsigned int pbc_active;
++      u8 uuid[WPS_UUID_LEN];
 +};
 +
 +struct wpa_ssid_value {
-       enum { WPA_SETBAND_AUTO, WPA_SETBAND_5G, WPA_SETBAND_2G } setband;
++      u8 ssid[SSID_MAX_LEN];
 +      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
 + *
 + * This structure contains the internal data for core wpa_supplicant code. This
 + * should be only used directly from the core code. However, a pointer to this
 + * data is used from other files as an arbitrary context pointer in calls to
 + * core functions.
 + */
 +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;
 +#endif /* CONFIG_CTRL_IFACE_DBUS */
 +#ifdef CONFIG_CTRL_IFACE_DBUS_NEW
 +      char *dbus_new_path;
 +      char *dbus_groupobj_path;
 +#ifdef CONFIG_AP
 +      char *preq_notify_peer;
 +#endif /* CONFIG_AP */
 +#endif /* CONFIG_CTRL_IFACE_DBUS_NEW */
 +      char bridge_ifname[16];
 +
 +      char *confname;
 +      char *confanother;
 +
 +      struct wpa_config *conf;
 +      int countermeasures;
 +      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;
 +
 +      /* Selected configuration (based on Beacon/ProbeResp WPA IE) */
 +      int pairwise_cipher;
 +      int group_cipher;
 +      int key_mgmt;
 +      int wpa_proto;
 +      int mgmt_group_cipher;
 +
 +      void *drv_priv; /* private data used by driver_ops */
 +      void *global_drv_priv;
 +
 +      u8 *bssid_filter;
 +      size_t bssid_filter_count;
 +
 +      u8 *disallow_aps_bssid;
 +      size_t disallow_aps_bssid_count;
 +      struct wpa_ssid_value *disallow_aps_ssid;
 +      size_t disallow_aps_ssid_count;
 +
-       struct wpa_driver_ops *driver;
++      enum set_band 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;
 +      struct wpa_ssid *prev_scan_ssid; /* previously scanned SSID;
 +                                        * NULL = not yet initialized (start
 +                                        * with wildcard SSID)
 +                                        * WILDCARD_SSID_SCAN = wildcard
 +                                        * SSID was used in the previous scan
 +                                        */
 +#define WILDCARD_SSID_SCAN ((struct wpa_ssid *) 1)
 +
 +      struct wpa_ssid *prev_sched_ssid; /* last SSID used in sched scan */
 +      int sched_scan_timeout;
 +      int sched_scan_interval;
 +      int first_sched_scan;
 +      int sched_scan_timed_out;
 +
 +      void (*scan_res_handler)(struct wpa_supplicant *wpa_s,
 +                               struct wpa_scan_results *scan_res);
 +      struct dl_list bss; /* struct wpa_bss::list */
 +      struct dl_list bss_id; /* struct wpa_bss::list_id */
 +      size_t num_bss;
 +      unsigned int bss_update_idx;
 +      unsigned int bss_next_id;
 +
 +       /*
 +        * Pointers to BSS entries in the order they were in the last scan
 +        * results.
 +        */
 +      struct wpa_bss **last_scan_res;
 +      unsigned int last_scan_res_used;
 +      unsigned int last_scan_res_size;
 +      struct os_reltime last_scan;
 +
-               u8 ssid[32];
++      const struct wpa_driver_ops *driver;
 +      int interface_removed; /* whether the network interface has been
 +                              * removed */
 +      struct wpa_sm *wpa;
 +      struct eapol_sm *eapol;
 +
 +      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;
 +
 +      int eapol_received; /* number of EAPOL packets received after the
 +                           * previous association event */
 +
 +      struct scard_data *scard;
 +      char imsi[20];
 +      int mnc_len;
 +
 +      unsigned char last_eapol_src[ETH_ALEN];
 +
 +      unsigned int keys_cleared; /* bitfield of key indexes that the driver is
 +                                  * known not to be configured with a key */
 +
 +      struct wpa_blacklist *blacklist;
 +
 +      /**
 +       * extra_blacklist_count - Sum of blacklist counts after last connection
 +       *
 +       * This variable is used to maintain a count of temporary blacklisting
 +       * failures (maximum number for any BSS) over blacklist clear
 +       * operations. This is needed for figuring out whether there has been
 +       * failures prior to the last blacklist clear operation which happens
 +       * whenever no other not-blacklisted BSS candidates are available. This
 +       * gets cleared whenever a connection has been established successfully.
 +       */
 +      int extra_blacklist_count;
 +
 +      /**
 +       * scan_req - Type of the scan request
 +       */
 +      enum scan_req_type {
 +              /**
 +               * NORMAL_SCAN_REQ - Normal scan request
 +               *
 +               * This is used for scans initiated by wpa_supplicant to find an
 +               * AP for a connection.
 +               */
 +              NORMAL_SCAN_REQ,
 +
 +              /**
 +               * INITIAL_SCAN_REQ - Initial scan request
 +               *
 +               * This is used for the first scan on an interface to force at
 +               * least one scan to be run even if the configuration does not
 +               * include any enabled networks.
 +               */
 +              INITIAL_SCAN_REQ,
 +
 +              /**
 +               * MANUAL_SCAN_REQ - Manual scan request
 +               *
 +               * This is used for scans where the user request a scan or
 +               * a specific wpa_supplicant operation (e.g., WPS) requires scan
 +               * to be run.
 +               */
 +              MANUAL_SCAN_REQ
 +      } scan_req, last_scan_req;
 +      enum wpa_states scan_prev_wpa_state;
 +      struct os_reltime scan_trigger_time, scan_start_time;
++      /* Minimum freshness requirement for connection purposes */
++      struct os_reltime scan_min_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;
 +
++      struct wpa_ssid_value *ssids_from_scan_req;
++      unsigned int num_ssids_from_scan_req;
++
 +      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
 +       * struct wpa_driver_capa in driver.h
 +       */
 +      unsigned int probe_resp_offloads;
 +
 +      /* extended capabilities supported by the driver */
 +      const u8 *extended_capa, *extended_capa_mask;
 +      unsigned int extended_capa_len;
 +
 +      int max_scan_ssids;
 +      int max_sched_scan_ssids;
 +      int sched_scan_supported;
 +      unsigned int max_match_sets;
 +      unsigned int max_remain_on_chan;
 +      unsigned int max_stations;
 +
 +      int pending_mic_error_report;
 +      int pending_mic_error_pairwise;
 +      int mic_errors_seen; /* Michael MIC errors with the current PTK */
 +
 +      struct wps_context *wps;
 +      int wps_success; /* WPS success event received */
 +      struct wps_er *wps_er;
 +      unsigned int wps_run;
++      struct os_reltime wps_pin_start_time;
 +      int blacklist_cleared;
 +
 +      struct wpabuf *pending_eapol_rx;
 +      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;
++      unsigned int added_vif:1;
 +
 +      struct os_reltime last_mac_addr_change;
 +      int last_mac_addr_style;
 +
 +      struct ibss_rsn *ibss_rsn;
 +
 +      int set_sta_uapsd;
 +      int sta_uapsd;
 +      int set_ap_uapsd;
 +      int ap_uapsd;
 +
 +#ifdef CONFIG_SME
 +      struct {
-       struct wpa_supplicant *p2p_dev;
++              u8 ssid[SSID_MAX_LEN];
 +              size_t ssid_len;
 +              int freq;
 +              u8 assoc_req_ie[200];
 +              size_t assoc_req_ie_len;
 +              int mfp;
 +              int ft_used;
 +              u8 mobility_domain[2];
 +              u8 *ft_ies;
 +              size_t ft_ies_len;
 +              u8 prev_bssid[ETH_ALEN];
 +              int prev_bssid_set;
 +              int auth_alg;
 +              int proto;
 +
 +              int sa_query_count; /* number of pending SA Query requests;
 +                                   * 0 = no SA Query in progress */
 +              int sa_query_timed_out;
 +              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_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;
 +#ifdef CONFIG_SAE
 +              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 */
 +
 +#ifdef CONFIG_AP
 +      struct hostapd_iface *ap_iface;
 +      void (*ap_configured_cb)(void *ctx, void *data);
 +      void *ap_configured_cb_ctx;
 +      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];
 +      u8 pending_action_dst[ETH_ALEN];
 +      u8 pending_action_bssid[ETH_ALEN];
 +      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,
 +                                          const u8 *data, size_t data_len,
 +                                          enum offchannel_send_action_result
 +                                          result);
 +      unsigned int roc_waiting_drv_freq;
 +      int action_tx_wait_time;
 +
 +      int p2p_mgmt;
 +
 +#ifdef CONFIG_P2P
-       u8 p2p_join_ssid[32];
 +      struct p2p_go_neg_results *go_params;
 +      int create_p2p_iface;
 +      u8 pending_interface_addr[ETH_ALEN];
 +      char pending_interface_name[100];
 +      int pending_interface_type;
 +      int p2p_group_idx;
 +      unsigned int pending_listen_freq;
 +      unsigned int pending_listen_duration;
 +      enum {
 +              NOT_P2P_GROUP_INTERFACE,
 +              P2P_GROUP_INTERFACE_PENDING,
 +              P2P_GROUP_INTERFACE_GO,
 +              P2P_GROUP_INTERFACE_CLIENT
 +      } p2p_group_interface;
 +      struct p2p_group *p2p_group;
 +      int p2p_long_listen; /* remaining time in long Listen state in ms */
 +      char p2p_pin[10];
 +      int p2p_wps_method;
 +      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];
 +      int pending_pd_before_join;
 +      u8 pending_join_iface_addr[ETH_ALEN];
 +      u8 pending_join_dev_addr[ETH_ALEN];
 +      int pending_join_wps_method;
-       unsigned int p2ps_join_addr_valid:1;
++      u8 p2p_join_ssid[SSID_MAX_LEN];
 +      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, AUTO_PD_ASP
 +      } pending_pd_use;
 +
 +      /*
 +       * Whether cross connection is disallowed by the AP to which this
 +       * interface is associated (only valid if there is an association).
 +       */
 +      int cross_connect_disallowed;
 +
 +      /*
 +       * Whether this P2P group is configured to use cross connection (only
 +       * valid if this is P2P GO interface). The actual cross connect packet
 +       * forwarding may not be configured depending on the uplink status.
 +       */
 +      int cross_connect_enabled;
 +
 +      /* Whether cross connection forwarding is in use at the moment. */
 +      int cross_connect_in_use;
 +
 +      /*
 +       * Uplink interface name for cross connection
 +       */
 +      char cross_connect_uplink[100];
 +
 +      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;
-  * Utility method to tell if a given network is a persistent group
++      unsigned int p2ps_method_config_any:1;
++      unsigned int p2p_cli_probe:1;
 +      int p2p_persistent_go_freq;
 +      int p2p_persistent_id;
 +      int p2p_go_intent;
 +      int p2p_connect_freq;
 +      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;
 +      const struct bgscan_ops *bgscan;
 +      void *bgscan_priv;
 +
 +      const struct autoscan_ops *autoscan;
 +      struct wpa_driver_scan_params *autoscan_params;
 +      void *autoscan_priv;
 +
 +      struct wpa_ssid *connect_without_scan;
 +
 +      struct wps_ap_info *wps_ap;
 +      size_t num_wps_ap;
 +      int wps_ap_iter;
 +
 +      int after_wps;
 +      int known_wps_freq;
 +      unsigned int wps_freq;
 +      int wps_fragment_size;
 +      int auto_reconnect_disabled;
 +
 +       /* Channel preferences for AP/P2P GO use */
 +      int best_24_freq;
 +      int best_5_freq;
 +      int best_overall_freq;
 +
 +      struct gas_query *gas;
 +
 +#ifdef CONFIG_INTERWORKING
 +      unsigned int fetch_anqp_in_progress:1;
 +      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;
 +
 +      struct {
 +              struct hostapd_hw_modes *modes;
 +              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, *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;
 +      u8 wnm_reply;
 +      u8 wnm_num_neighbor_report;
 +      u8 wnm_mode;
 +      u16 wnm_dissoc_timer;
 +      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;
++
++#ifdef CONFIG_FST
++      struct fst_iface *fst;
++      const struct wpabuf *fst_ies;
++      struct wpabuf *received_mb_ies;
++#endif /* CONFIG_FST */
 +};
 +
 +
 +/* wpa_supplicant.c */
 +void wpa_supplicant_apply_ht_overrides(
 +      struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
 +      struct wpa_driver_associate_params *params);
 +void wpa_supplicant_apply_vht_overrides(
 +      struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
 +      struct wpa_driver_associate_params *params);
 +
 +int wpa_set_wep_keys(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid);
 +int wpa_supplicant_set_wpa_none_key(struct wpa_supplicant *wpa_s,
 +                                  struct wpa_ssid *ssid);
 +
 +int wpa_supplicant_reload_configuration(struct wpa_supplicant *wpa_s);
 +
 +const char * wpa_supplicant_state_txt(enum wpa_states state);
 +int wpa_supplicant_update_mac_addr(struct wpa_supplicant *wpa_s);
 +int wpa_supplicant_driver_init(struct wpa_supplicant *wpa_s);
 +int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s,
 +                            struct wpa_bss *bss, struct wpa_ssid *ssid,
 +                            u8 *wpa_ie, size_t *wpa_ie_len);
 +void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,
 +                            struct wpa_bss *bss,
 +                            struct wpa_ssid *ssid);
 +void wpa_supplicant_set_non_wpa_policy(struct wpa_supplicant *wpa_s,
 +                                     struct wpa_ssid *ssid);
 +void wpa_supplicant_initiate_eapol(struct wpa_supplicant *wpa_s);
 +void wpa_clear_keys(struct wpa_supplicant *wpa_s, const u8 *addr);
 +void wpa_supplicant_req_auth_timeout(struct wpa_supplicant *wpa_s,
 +                                   int sec, int usec);
 +void wpa_supplicant_reinit_autoscan(struct wpa_supplicant *wpa_s);
 +void wpa_supplicant_set_state(struct wpa_supplicant *wpa_s,
 +                            enum wpa_states state);
 +struct wpa_ssid * wpa_supplicant_get_ssid(struct wpa_supplicant *wpa_s);
 +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);
 +
 +void wpa_supplicant_enable_network(struct wpa_supplicant *wpa_s,
 +                                 struct wpa_ssid *ssid);
 +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,
 +                                        unsigned int expire_age);
 +int wpa_supplicant_set_bss_expiration_count(struct wpa_supplicant *wpa_s,
 +                                          unsigned int expire_count);
 +int wpa_supplicant_set_scan_interval(struct wpa_supplicant *wpa_s,
 +                                   int scan_interval);
 +int wpa_supplicant_set_debug_params(struct wpa_global *global,
 +                                  int debug_level, int debug_timestamp,
 +                                  int debug_show_keys);
 +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_supplicant *parent);
 +int wpa_supplicant_remove_iface(struct wpa_global *global,
 +                              struct wpa_supplicant *wpa_s,
 +                              int terminate);
 +struct wpa_supplicant * wpa_supplicant_get_iface(struct wpa_global *global,
 +                                               const char *ifname);
 +struct wpa_global * wpa_supplicant_init(struct wpa_params *params);
 +int wpa_supplicant_run(struct wpa_global *global);
 +void wpa_supplicant_deinit(struct wpa_global *global);
 +
 +int wpa_supplicant_scard_init(struct wpa_supplicant *wpa_s,
 +                            struct wpa_ssid *ssid);
 +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);
 +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, 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, 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
 + * @wpa_s: Pointer to wpa_supplicant data
 + * @ssid: Pointer to the network block the reply is for
 + * @field: field the response is a reply for
 + * @value: value (ie, password, etc) for @field
 + * Returns: 0 on success, non-zero on error
 + *
 + * Helper function to handle replies to control interface requests.
 + */
 +int wpa_supplicant_ctrl_iface_ctrl_rsp_handle(struct wpa_supplicant *wpa_s,
 +                                            struct wpa_ssid *ssid,
 +                                            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,
 +                         struct wpa_bss *selected,
 +                         struct wpa_ssid *ssid);
 +void wpa_supplicant_stop_countermeasures(void *eloop_ctx, void *sock_ctx);
 +void wpa_supplicant_delayed_mic_error_report(void *eloop_ctx, void *sock_ctx);
 +void wnm_bss_keep_alive_deinit(struct wpa_supplicant *wpa_s);
 +int wpa_supplicant_fast_associate(struct wpa_supplicant *wpa_s);
 +struct wpa_bss * wpa_supplicant_pick_network(struct wpa_supplicant *wpa_s,
 +                                           struct wpa_ssid **selected_ssid);
 +
 +/* eap_register.c */
 +int eap_register_methods(void);
 +
 +/**
-       return ((ssid->disabled == 2) || ssid->p2p_persistent_group);
++ * Utility method to tell if a given network is for persistent group storage
 + * @ssid: Network object
 + * Returns: 1 if network is a persistent group, 0 otherwise
 + */
 +static inline int network_is_persistent_group(struct wpa_ssid *ssid)
 +{
++      return ssid->disabled == 2 && ssid->p2p_persistent_group;
 +}
 +
 +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);
 +
++void wpas_network_reenabled(void *eloop_ctx, void *timeout_ctx);
++
++#ifdef CONFIG_FST
++
++struct fst_wpa_obj;
++
++void fst_wpa_supplicant_fill_iface_obj(struct wpa_supplicant *wpa_s,
++                                     struct fst_wpa_obj *iface_obj);
++
++#endif /* CONFIG_FST */
++
 +#endif /* WPA_SUPPLICANT_I_H */
index 1bb82ba7169602bd25881ff6e2286d89645f9f0a,0000000000000000000000000000000000000000..29c22ba2c967a035b5e95fd336c6ffb7cdefc9dc
mode 100644,000000..100644
--- /dev/null
@@@ -1,1114 -1,0 +1,1129 @@@
-       char *buf;
-       size_t buflen;
-       int len;
 +/*
 + * WPA Supplicant - Glue code to setup EAPOL and RSN modules
 + * 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 "includes.h"
 +
 +#include "common.h"
 +#include "eapol_supp/eapol_supp_sm.h"
 +#include "rsn_supp/wpa.h"
 +#include "eloop.h"
 +#include "config.h"
 +#include "l2_packet/l2_packet.h"
 +#include "common/wpa_common.h"
 +#include "wpa_supplicant_i.h"
 +#include "driver_i.h"
 +#include "rsn_supp/pmksa_cache.h"
 +#include "sme.h"
 +#include "common/ieee802_11_defs.h"
 +#include "common/wpa_ctrl.h"
 +#include "wpas_glue.h"
 +#include "wps_supplicant.h"
 +#include "bss.h"
 +#include "scan.h"
 +#include "notify.h"
 +#include "wpas_kay.h"
 +
 +
 +#ifndef CONFIG_NO_CONFIG_BLOBS
 +#if defined(IEEE8021X_EAPOL) || !defined(CONFIG_NO_WPA)
 +static void wpa_supplicant_set_config_blob(void *ctx,
 +                                         struct wpa_config_blob *blob)
 +{
 +      struct wpa_supplicant *wpa_s = ctx;
 +      wpa_config_set_blob(wpa_s->conf, blob);
 +      if (wpa_s->conf->update_config) {
 +              int ret = wpa_config_write(wpa_s->confname, wpa_s->conf);
 +              if (ret) {
 +                      wpa_printf(MSG_DEBUG, "Failed to update config after "
 +                                 "blob set");
 +              }
 +      }
 +}
 +
 +
 +static const struct wpa_config_blob *
 +wpa_supplicant_get_config_blob(void *ctx, const char *name)
 +{
 +      struct wpa_supplicant *wpa_s = ctx;
 +      return wpa_config_get_blob(wpa_s->conf, name);
 +}
 +#endif /* defined(IEEE8021X_EAPOL) || !defined(CONFIG_NO_WPA) */
 +#endif /* CONFIG_NO_CONFIG_BLOBS */
 +
 +
 +#if defined(IEEE8021X_EAPOL) || !defined(CONFIG_NO_WPA)
 +static u8 * wpa_alloc_eapol(const struct wpa_supplicant *wpa_s, u8 type,
 +                          const void *data, u16 data_len,
 +                          size_t *msg_len, void **data_pos)
 +{
 +      struct ieee802_1x_hdr *hdr;
 +
 +      *msg_len = sizeof(*hdr) + data_len;
 +      hdr = os_malloc(*msg_len);
 +      if (hdr == NULL)
 +              return NULL;
 +
 +      hdr->version = wpa_s->conf->eapol_version;
 +      hdr->type = type;
 +      hdr->length = host_to_be16(data_len);
 +
 +      if (data)
 +              os_memcpy(hdr + 1, data, data_len);
 +      else
 +              os_memset(hdr + 1, 0, data_len);
 +
 +      if (data_pos)
 +              *data_pos = hdr + 1;
 +
 +      return (u8 *) hdr;
 +}
 +
 +
 +/**
 + * wpa_ether_send - Send Ethernet frame
 + * @wpa_s: Pointer to wpa_supplicant data
 + * @dest: Destination MAC address
 + * @proto: Ethertype in host byte order
 + * @buf: Frame payload starting from IEEE 802.1X header
 + * @len: Frame payload length
 + * Returns: >=0 on success, <0 on failure
 + */
 +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 -1;
 +}
 +#endif /* IEEE8021X_EAPOL || !CONFIG_NO_WPA */
 +
 +
 +#ifdef IEEE8021X_EAPOL
 +
 +/**
 + * wpa_supplicant_eapol_send - Send IEEE 802.1X EAPOL packet to Authenticator
 + * @ctx: Pointer to wpa_supplicant data (wpa_s)
 + * @type: IEEE 802.1X packet type (IEEE802_1X_TYPE_*)
 + * @buf: EAPOL payload (after IEEE 802.1X header)
 + * @len: EAPOL payload length
 + * Returns: >=0 on success, <0 on failure
 + *
 + * This function adds Ethernet and IEEE 802.1X header and sends the EAPOL frame
 + * to the current Authenticator.
 + */
 +static int wpa_supplicant_eapol_send(void *ctx, int type, const u8 *buf,
 +                                   size_t len)
 +{
 +      struct wpa_supplicant *wpa_s = ctx;
 +      u8 *msg, *dst, bssid[ETH_ALEN];
 +      size_t msglen;
 +      int res;
 +
 +      /* TODO: could add l2_packet_sendmsg that allows fragments to avoid
 +       * extra copy here */
 +
 +      if (wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt) ||
 +          wpa_s->key_mgmt == WPA_KEY_MGMT_NONE) {
 +              /* Current SSID is not using IEEE 802.1X/EAP, so drop possible
 +               * EAPOL frames (mainly, EAPOL-Start) from EAPOL state
 +               * machines. */
 +              wpa_printf(MSG_DEBUG, "WPA: drop TX EAPOL in non-IEEE 802.1X "
 +                         "mode (type=%d len=%lu)", type,
 +                         (unsigned long) len);
 +              return -1;
 +      }
 +
 +      if (pmksa_cache_get_current(wpa_s->wpa) &&
 +          type == IEEE802_1X_TYPE_EAPOL_START) {
 +              /*
 +               * 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)) {
 +              wpa_printf(MSG_DEBUG, "BSSID not set when trying to send an "
 +                         "EAPOL frame");
 +              if (wpa_drv_get_bssid(wpa_s, bssid) == 0 &&
 +                  !is_zero_ether_addr(bssid)) {
 +                      dst = bssid;
 +                      wpa_printf(MSG_DEBUG, "Using current BSSID " MACSTR
 +                                 " from the driver as the EAPOL destination",
 +                                 MAC2STR(dst));
 +              } else {
 +                      dst = wpa_s->last_eapol_src;
 +                      wpa_printf(MSG_DEBUG, "Using the source address of the"
 +                                 " last received EAPOL frame " MACSTR " as "
 +                                 "the EAPOL destination",
 +                                 MAC2STR(dst));
 +              }
 +      } else {
 +              /* BSSID was already set (from (Re)Assoc event, so use it as
 +               * the EAPOL destination. */
 +              dst = wpa_s->bssid;
 +      }
 +
 +      msg = wpa_alloc_eapol(wpa_s, type, buf, len, &msglen, NULL);
 +      if (msg == NULL)
 +              return -1;
 +
 +      wpa_printf(MSG_DEBUG, "TX EAPOL: dst=" MACSTR, MAC2STR(dst));
 +      wpa_hexdump(MSG_MSGDUMP, "TX EAPOL", msg, msglen);
 +      res = wpa_ether_send(wpa_s, dst, ETH_P_EAPOL, msg, msglen);
 +      os_free(msg);
 +      return res;
 +}
 +
 +
 +/**
 + * wpa_eapol_set_wep_key - set WEP key for the driver
 + * @ctx: Pointer to wpa_supplicant data (wpa_s)
 + * @unicast: 1 = individual unicast key, 0 = broadcast key
 + * @keyidx: WEP key index (0..3)
 + * @key: Pointer to key data
 + * @keylen: Key length in bytes
 + * Returns: 0 on success or < 0 on error.
 + */
 +static int wpa_eapol_set_wep_key(void *ctx, int unicast, int keyidx,
 +                               const u8 *key, size_t keylen)
 +{
 +      struct wpa_supplicant *wpa_s = ctx;
 +      if (wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) {
 +              int cipher = (keylen == 5) ? WPA_CIPHER_WEP40 :
 +                      WPA_CIPHER_WEP104;
 +              if (unicast)
 +                      wpa_s->pairwise_cipher = cipher;
 +              else
 +                      wpa_s->group_cipher = cipher;
 +      }
 +      return wpa_drv_set_key(wpa_s, WPA_ALG_WEP,
 +                             unicast ? wpa_s->bssid : NULL,
 +                             keyidx, unicast, NULL, 0, key, keylen);
 +}
 +
 +
 +static void wpa_supplicant_aborted_cached(void *ctx)
 +{
 +      struct wpa_supplicant *wpa_s = ctx;
 +      wpa_sm_aborted_cached(wpa_s->wpa);
 +}
 +
 +
 +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 - result=%s",
 +                 result_str(result));
 +
 +      if (wpas_wps_eapol_cb(wpa_s) > 0)
 +              return;
 +
 +      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 (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))
 +              return;
 +
 +      wpa_printf(MSG_DEBUG, "Configure PMK for driver-based RSN 4-way "
 +                 "handshake");
 +
 +      pmk_len = PMK_LEN;
 +      if (wpa_key_mgmt_ft(wpa_s->key_mgmt)) {
 +#ifdef CONFIG_IEEE80211R
 +              u8 buf[2 * PMK_LEN];
 +              wpa_printf(MSG_DEBUG, "RSN: Use FT XXKey as PMK for "
 +                         "driver-based 4-way hs and FT");
 +              res = eapol_sm_get_key(eapol, buf, 2 * PMK_LEN);
 +              if (res == 0) {
 +                      os_memcpy(pmk, buf + PMK_LEN, PMK_LEN);
 +                      os_memset(buf, 0, sizeof(buf));
 +              }
 +#else /* CONFIG_IEEE80211R */
 +              res = -1;
 +#endif /* CONFIG_IEEE80211R */
 +      } else {
 +              res = eapol_sm_get_key(eapol, pmk, PMK_LEN);
 +              if (res) {
 +                      /*
 +                       * EAP-LEAP is an exception from other EAP methods: it
 +                       * uses only 16-byte PMK.
 +                       */
 +                      res = eapol_sm_get_key(eapol, pmk, 16);
 +                      pmk_len = 16;
 +              }
 +      }
 +
 +      if (res) {
 +              wpa_printf(MSG_DEBUG, "Failed to get PMK from EAPOL state "
 +                         "machines");
 +              return;
 +      }
 +
 +      wpa_hexdump_key(MSG_DEBUG, "RSN: Configure PMK for driver-based 4-way "
 +                      "handshake", pmk, pmk_len);
 +
 +      if (wpa_drv_set_key(wpa_s, WPA_ALG_PMK, NULL, 0, 0, NULL, 0, pmk,
 +                          pmk_len)) {
 +              wpa_printf(MSG_DEBUG, "Failed to set PMK to the driver");
 +      }
 +
 +      wpa_supplicant_cancel_scan(wpa_s);
 +      wpa_supplicant_cancel_auth_timeout(wpa_s);
 +      wpa_supplicant_set_state(wpa_s, WPA_COMPLETED);
 +
 +}
 +
 +
 +static void wpa_supplicant_notify_eapol_done(void *ctx)
 +{
 +      struct wpa_supplicant *wpa_s = ctx;
 +      wpa_msg(wpa_s, MSG_DEBUG, "WPA: EAPOL processing complete");
 +      if (wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt)) {
 +              wpa_supplicant_set_state(wpa_s, WPA_4WAY_HANDSHAKE);
 +      } else {
 +              wpa_supplicant_cancel_auth_timeout(wpa_s);
 +              wpa_supplicant_set_state(wpa_s, WPA_COMPLETED);
 +      }
 +}
 +
 +#endif /* IEEE8021X_EAPOL */
 +
 +
 +#ifndef CONFIG_NO_WPA
 +
 +static int wpa_get_beacon_ie(struct wpa_supplicant *wpa_s)
 +{
 +      int ret = 0;
 +      struct wpa_bss *curr = NULL, *bss;
 +      struct wpa_ssid *ssid = wpa_s->current_ssid;
 +      const u8 *ie;
 +
 +      dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
 +              if (os_memcmp(bss->bssid, wpa_s->bssid, ETH_ALEN) != 0)
 +                      continue;
 +              if (ssid == NULL ||
 +                  ((bss->ssid_len == ssid->ssid_len &&
 +                    os_memcmp(bss->ssid, ssid->ssid, ssid->ssid_len) == 0) ||
 +                   ssid->ssid_len == 0)) {
 +                      curr = bss;
 +                      break;
 +              }
 +      }
 +
 +      if (curr) {
 +              ie = wpa_bss_get_vendor_ie(curr, WPA_IE_VENDOR_TYPE);
 +              if (wpa_sm_set_ap_wpa_ie(wpa_s->wpa, ie, ie ? 2 + ie[1] : 0))
 +                      ret = -1;
 +
 +              ie = wpa_bss_get_ie(curr, WLAN_EID_RSN);
 +              if (wpa_sm_set_ap_rsn_ie(wpa_s->wpa, ie, ie ? 2 + ie[1] : 0))
 +                      ret = -1;
 +      } else {
 +              ret = -1;
 +      }
 +
 +      return ret;
 +}
 +
 +
 +static int wpa_supplicant_get_beacon_ie(void *ctx)
 +{
 +      struct wpa_supplicant *wpa_s = ctx;
 +      if (wpa_get_beacon_ie(wpa_s) == 0) {
 +              return 0;
 +      }
 +
 +      /* No WPA/RSN IE found in the cached scan results. Try to get updated
 +       * scan results from the driver. */
 +      if (wpa_supplicant_update_scan_results(wpa_s) < 0)
 +              return -1;
 +
 +      return wpa_get_beacon_ie(wpa_s);
 +}
 +
 +
 +static u8 * _wpa_alloc_eapol(void *wpa_s, u8 type,
 +                           const void *data, u16 data_len,
 +                           size_t *msg_len, void **data_pos)
 +{
 +      return wpa_alloc_eapol(wpa_s, type, data, data_len, msg_len, data_pos);
 +}
 +
 +
 +static int _wpa_ether_send(void *wpa_s, const u8 *dest, u16 proto,
 +                         const u8 *buf, size_t len)
 +{
 +      return wpa_ether_send(wpa_s, dest, proto, buf, len);
 +}
 +
 +
 +static void _wpa_supplicant_cancel_auth_timeout(void *wpa_s)
 +{
 +      wpa_supplicant_cancel_auth_timeout(wpa_s);
 +}
 +
 +
 +static void _wpa_supplicant_set_state(void *wpa_s, enum wpa_states state)
 +{
 +      wpa_supplicant_set_state(wpa_s, state);
 +}
 +
 +
 +/**
 + * wpa_supplicant_get_state - Get the connection state
 + * @wpa_s: Pointer to wpa_supplicant data
 + * Returns: The current connection state (WPA_*)
 + */
 +static enum wpa_states wpa_supplicant_get_state(struct wpa_supplicant *wpa_s)
 +{
 +      return wpa_s->wpa_state;
 +}
 +
 +
 +static enum wpa_states _wpa_supplicant_get_state(void *wpa_s)
 +{
 +      return wpa_supplicant_get_state(wpa_s);
 +}
 +
 +
 +static void _wpa_supplicant_deauthenticate(void *wpa_s, int reason_code)
 +{
 +      wpa_supplicant_deauthenticate(wpa_s, reason_code);
 +      /* Schedule a scan to make sure we continue looking for networks */
 +      wpa_supplicant_req_scan(wpa_s, 5, 0);
 +}
 +
 +
 +static void * wpa_supplicant_get_network_ctx(void *wpa_s)
 +{
 +      return wpa_supplicant_get_ssid(wpa_s);
 +}
 +
 +
 +static int wpa_supplicant_get_bssid(void *ctx, u8 *bssid)
 +{
 +      struct wpa_supplicant *wpa_s = ctx;
 +      return wpa_drv_get_bssid(wpa_s, bssid);
 +}
 +
 +
 +static int wpa_supplicant_set_key(void *_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)
 +{
 +      struct wpa_supplicant *wpa_s = _wpa_s;
 +      if (alg == WPA_ALG_TKIP && key_idx == 0 && key_len == 32) {
 +              /* Clear the MIC error counter when setting a new PTK. */
 +              wpa_s->mic_errors_seen = 0;
 +      }
 +#ifdef CONFIG_TESTING_GET_GTK
 +      if (key_idx > 0 && addr && is_broadcast_ether_addr(addr) &&
 +          alg != WPA_ALG_NONE && key_len <= sizeof(wpa_s->last_gtk)) {
 +              os_memcpy(wpa_s->last_gtk, key, key_len);
 +              wpa_s->last_gtk_len = key_len;
 +      }
 +#endif /* CONFIG_TESTING_GET_GTK */
 +      return wpa_drv_set_key(wpa_s, alg, addr, key_idx, set_tx, seq, seq_len,
 +                             key, key_len);
 +}
 +
 +
 +static int wpa_supplicant_mlme_setprotection(void *wpa_s, const u8 *addr,
 +                                           int protection_type,
 +                                           int key_type)
 +{
 +      return wpa_drv_mlme_setprotection(wpa_s, addr, protection_type,
 +                                        key_type);
 +}
 +
 +
 +static int wpa_supplicant_add_pmkid(void *wpa_s,
 +                                  const u8 *bssid, const u8 *pmkid)
 +{
 +      return wpa_drv_add_pmkid(wpa_s, bssid, pmkid);
 +}
 +
 +
 +static int wpa_supplicant_remove_pmkid(void *wpa_s,
 +                                     const u8 *bssid, const u8 *pmkid)
 +{
 +      return wpa_drv_remove_pmkid(wpa_s, bssid, pmkid);
 +}
 +
 +
 +#ifdef CONFIG_IEEE80211R
 +static int wpa_supplicant_update_ft_ies(void *ctx, const u8 *md,
 +                                      const u8 *ies, size_t ies_len)
 +{
 +      struct wpa_supplicant *wpa_s = ctx;
 +      if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME)
 +              return sme_update_ft_ies(wpa_s, md, ies, ies_len);
 +      return wpa_drv_update_ft_ies(wpa_s, md, ies, ies_len);
 +}
 +
 +
 +static int wpa_supplicant_send_ft_action(void *ctx, u8 action,
 +                                       const u8 *target_ap,
 +                                       const u8 *ies, size_t ies_len)
 +{
 +      struct wpa_supplicant *wpa_s = ctx;
 +      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;
 +}
 +
 +
 +static int wpa_supplicant_mark_authenticated(void *ctx, const u8 *target_ap)
 +{
 +      struct wpa_supplicant *wpa_s = ctx;
 +      struct wpa_driver_auth_params params;
 +      struct wpa_bss *bss;
 +
 +      bss = wpa_bss_get_bssid(wpa_s, target_ap);
 +      if (bss == NULL)
 +              return -1;
 +
 +      os_memset(&params, 0, sizeof(params));
 +      params.bssid = target_ap;
 +      params.freq = bss->freq;
 +      params.ssid = bss->ssid;
 +      params.ssid_len = bss->ssid_len;
 +      params.auth_alg = WPA_AUTH_ALG_FT;
 +      params.local_state_change = 1;
 +      return wpa_drv_authenticate(wpa_s, &params);
 +}
 +#endif /* CONFIG_IEEE80211R */
 +
 +
 +#ifdef CONFIG_TDLS
 +
 +static int wpa_supplicant_tdls_get_capa(void *ctx, int *tdls_supported,
 +                                      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;
 +
 +      if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT)
 +              *tdls_supported = 1;
 +
 +      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, 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, peer_capab, initiator, buf,
 +                                    len);
 +}
 +
 +
 +static int wpa_supplicant_tdls_oper(void *ctx, int oper, const u8 *peer)
 +{
 +      struct wpa_supplicant *wpa_s = ctx;
 +      return wpa_drv_tdls_oper(wpa_s, oper, peer);
 +}
 +
 +
 +static int wpa_supplicant_tdls_peer_addset(
 +      void *ctx, const u8 *peer, 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, 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;
 +
 +      os_memset(&params, 0, sizeof(params));
 +
 +      params.addr = peer;
 +      params.aid = aid;
 +      params.capability = capability;
 +      params.flags = WPA_STA_TDLS_PEER | WPA_STA_AUTHORIZED;
 +
 +      /*
 +       * 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 (wmm || qosinfo)
 +              params.flags |= WPA_STA_WMM;
 +
 +      params.ht_capabilities = ht_capab;
 +      params.vht_capabilities = vht_capab;
 +      params.qosinfo = qosinfo;
 +      params.listen_interval = 0;
 +      params.supp_rates = supp_rates;
 +      params.supp_rates_len = supp_rates_len;
 +      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 */
 +
 +
 +enum wpa_ctrl_req_type wpa_supplicant_ctrl_req_from_string(const char *field)
 +{
 +      if (os_strcmp(field, "IDENTITY") == 0)
 +              return WPA_CTRL_REQ_EAP_IDENTITY;
 +      else if (os_strcmp(field, "PASSWORD") == 0)
 +              return WPA_CTRL_REQ_EAP_PASSWORD;
 +      else if (os_strcmp(field, "NEW_PASSWORD") == 0)
 +              return WPA_CTRL_REQ_EAP_NEW_PASSWORD;
 +      else if (os_strcmp(field, "PIN") == 0)
 +              return WPA_CTRL_REQ_EAP_PIN;
 +      else if (os_strcmp(field, "OTP") == 0)
 +              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;
++      else if (os_strcmp(field, "PSK_PASSPHRASE") == 0)
++              return WPA_CTRL_REQ_PSK_PASSPHRASE;
 +      return WPA_CTRL_REQ_UNKNOWN;
 +}
 +
 +
 +const char * wpa_supplicant_ctrl_req_to_string(enum wpa_ctrl_req_type field,
 +                                             const char *default_txt,
 +                                             const char **txt)
 +{
 +      const char *ret = NULL;
 +
 +      *txt = default_txt;
 +
 +      switch (field) {
 +      case WPA_CTRL_REQ_EAP_IDENTITY:
 +              *txt = "Identity";
 +              ret = "IDENTITY";
 +              break;
 +      case WPA_CTRL_REQ_EAP_PASSWORD:
 +              *txt = "Password";
 +              ret = "PASSWORD";
 +              break;
 +      case WPA_CTRL_REQ_EAP_NEW_PASSWORD:
 +              *txt = "New Password";
 +              ret = "NEW_PASSWORD";
 +              break;
 +      case WPA_CTRL_REQ_EAP_PIN:
 +              *txt = "PIN";
 +              ret = "PIN";
 +              break;
 +      case WPA_CTRL_REQ_EAP_OTP:
 +              ret = "OTP";
 +              break;
 +      case WPA_CTRL_REQ_EAP_PASSPHRASE:
 +              *txt = "Private key passphrase";
 +              ret = "PASSPHRASE";
 +              break;
 +      case WPA_CTRL_REQ_SIM:
 +              ret = "SIM";
 +              break;
++      case WPA_CTRL_REQ_PSK_PASSPHRASE:
++              *txt = "PSK or passphrase";
++              ret = "PSK_PASSPHRASE";
++              break;
 +      default:
 +              break;
 +      }
 +
 +      /* txt needs to be something */
 +      if (*txt == NULL) {
 +              wpa_printf(MSG_WARNING, "No message for request %d", field);
 +              ret = NULL;
 +      }
 +
 +      return ret;
 +}
 +
++
++void wpas_send_ctrl_req(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
++                      const char *field_name, const char *txt)
++{
++      char *buf;
++      size_t buflen;
++      int len;
++
++      buflen = 100 + os_strlen(txt) + ssid->ssid_len;
++      buf = os_malloc(buflen);
++      if (buf == NULL)
++              return;
++      len = os_snprintf(buf, buflen, "%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, WPA_CTRL_REQ "%s", buf);
++      os_free(buf);
++}
++
++
 +#ifdef IEEE8021X_EAPOL
 +#if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG)
 +static void wpa_supplicant_eap_param_needed(void *ctx,
 +                                          enum wpa_ctrl_req_type field,
 +                                          const char *default_txt)
 +{
 +      struct wpa_supplicant *wpa_s = ctx;
 +      struct wpa_ssid *ssid = wpa_s->current_ssid;
 +      const char *field_name, *txt = NULL;
-       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);
 +
 +      if (ssid == NULL)
 +              return;
 +
 +      wpas_notify_network_request(wpa_s, ssid, field, default_txt);
 +
 +      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;
 +      }
 +
 +      wpas_notify_eap_status(wpa_s, "eap parameter needed", field_name);
 +
-       if (wpa_s->conf->key_mgmt_offload)
++      wpas_send_ctrl_req(wpa_s, ssid, field_name, txt);
 +}
 +#else /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */
 +#define wpa_supplicant_eap_param_needed NULL
 +#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;
 +#ifdef CONFIG_AP
 +      if (wpa_s->ap_iface) {
 +              wpa_printf(MSG_DEBUG, "AP mode active - skip EAPOL Supplicant "
 +                         "port status: %s",
 +                         authorized ? "Authorized" : "Unauthorized");
 +              return;
 +      }
 +#endif /* CONFIG_AP */
 +      wpa_printf(MSG_DEBUG, "EAPOL: Supplicant port status: %s",
 +                 authorized ? "Authorized" : "Unauthorized");
 +      wpa_drv_set_supp_port(wpa_s, 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, altsubject,
 +                                num_altsubject, cert_hash, cert);
 +}
 +
 +
 +static void wpa_supplicant_status_cb(void *ctx, const char *status,
 +                                   const char *parameter)
 +{
 +      struct wpa_supplicant *wpa_s = ctx;
 +
 +      wpas_notify_eap_status(wpa_s, status, parameter);
 +}
 +
 +
 +static void wpa_supplicant_set_anon_id(void *ctx, const u8 *id, size_t len)
 +{
 +      struct wpa_supplicant *wpa_s = ctx;
 +      char *str;
 +      int res;
 +
 +      wpa_hexdump_ascii(MSG_DEBUG, "EAP method updated anonymous_identity",
 +                        id, len);
 +
 +      if (wpa_s->current_ssid == NULL)
 +              return;
 +
 +      if (id == NULL) {
 +              if (wpa_config_set(wpa_s->current_ssid, "anonymous_identity",
 +                                 "NULL", 0) < 0)
 +                      return;
 +      } else {
 +              str = os_malloc(len * 2 + 1);
 +              if (str == NULL)
 +                      return;
 +              wpa_snprintf_hex(str, len * 2 + 1, id, len);
 +              res = wpa_config_set(wpa_s->current_ssid, "anonymous_identity",
 +                                   str, 0);
 +              os_free(str);
 +              if (res < 0)
 +                      return;
 +      }
 +
 +      if (wpa_s->conf->update_config) {
 +              res = wpa_config_write(wpa_s->confname, wpa_s->conf);
 +              if (res) {
 +                      wpa_printf(MSG_DEBUG, "Failed to update config after "
 +                                 "anonymous_id update");
 +              }
 +      }
 +}
 +#endif /* IEEE8021X_EAPOL */
 +
 +
 +int wpa_supplicant_init_eapol(struct wpa_supplicant *wpa_s)
 +{
 +#ifdef IEEE8021X_EAPOL
 +      struct eapol_ctx *ctx;
 +      ctx = os_zalloc(sizeof(*ctx));
 +      if (ctx == NULL) {
 +              wpa_printf(MSG_ERROR, "Failed to allocate EAPOL context.");
 +              return -1;
 +      }
 +
 +      ctx->ctx = wpa_s;
 +      ctx->msg_ctx = wpa_s;
 +      ctx->eapol_send_ctx = wpa_s;
 +      ctx->preauth = 0;
 +      ctx->eapol_done_cb = wpa_supplicant_notify_eapol_done;
 +      ctx->eapol_send = wpa_supplicant_eapol_send;
 +      ctx->set_wep_key = wpa_eapol_set_wep_key;
 +#ifndef CONFIG_NO_CONFIG_BLOBS
 +      ctx->set_config_blob = wpa_supplicant_set_config_blob;
 +      ctx->get_config_blob = wpa_supplicant_get_config_blob;
 +#endif /* CONFIG_NO_CONFIG_BLOBS */
 +      ctx->aborted_cached = wpa_supplicant_aborted_cached;
 +      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;
 +      wpa_s->eapol = eapol_sm_init(ctx);
 +      if (wpa_s->eapol == NULL) {
 +              os_free(ctx);
 +              wpa_printf(MSG_ERROR, "Failed to initialize EAPOL state "
 +                         "machines.");
 +              return -1;
 +      }
 +#endif /* IEEE8021X_EAPOL */
 +
 +      return 0;
 +}
 +
 +
 +#ifndef CONFIG_NO_WPA
 +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, 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 &&
++          (wpa_s->drv_flags & WPA_DRIVER_FLAGS_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
 +      struct wpa_sm_ctx *ctx;
 +      ctx = os_zalloc(sizeof(*ctx));
 +      if (ctx == NULL) {
 +              wpa_printf(MSG_ERROR, "Failed to allocate WPA context.");
 +              return -1;
 +      }
 +
 +      ctx->ctx = wpa_s;
 +      ctx->msg_ctx = wpa_s;
 +      ctx->set_state = _wpa_supplicant_set_state;
 +      ctx->get_state = _wpa_supplicant_get_state;
 +      ctx->deauthenticate = _wpa_supplicant_deauthenticate;
 +      ctx->set_key = wpa_supplicant_set_key;
 +      ctx->get_network_ctx = wpa_supplicant_get_network_ctx;
 +      ctx->get_bssid = wpa_supplicant_get_bssid;
 +      ctx->ether_send = _wpa_ether_send;
 +      ctx->get_beacon_ie = wpa_supplicant_get_beacon_ie;
 +      ctx->alloc_eapol = _wpa_alloc_eapol;
 +      ctx->cancel_auth_timeout = _wpa_supplicant_cancel_auth_timeout;
 +      ctx->add_pmkid = wpa_supplicant_add_pmkid;
 +      ctx->remove_pmkid = wpa_supplicant_remove_pmkid;
 +#ifndef CONFIG_NO_CONFIG_BLOBS
 +      ctx->set_config_blob = wpa_supplicant_set_config_blob;
 +      ctx->get_config_blob = wpa_supplicant_get_config_blob;
 +#endif /* CONFIG_NO_CONFIG_BLOBS */
 +      ctx->mlme_setprotection = wpa_supplicant_mlme_setprotection;
 +#ifdef CONFIG_IEEE80211R
 +      ctx->update_ft_ies = wpa_supplicant_update_ft_ies;
 +      ctx->send_ft_action = wpa_supplicant_send_ft_action;
 +      ctx->mark_authenticated = wpa_supplicant_mark_authenticated;
 +#endif /* CONFIG_IEEE80211R */
 +#ifdef CONFIG_TDLS
 +      ctx->tdls_get_capa = wpa_supplicant_tdls_get_capa;
 +      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 */
 +
 +      return 0;
 +}
 +
 +
 +void wpa_supplicant_rsn_supp_set_config(struct wpa_supplicant *wpa_s,
 +                                      struct wpa_ssid *ssid)
 +{
 +      struct rsn_supp_config conf;
 +      if (ssid) {
 +              os_memset(&conf, 0, sizeof(conf));
 +              conf.network_ctx = ssid;
 +              conf.peerkey_enabled = ssid->peerkey;
 +              conf.allowed_pairwise_cipher = ssid->pairwise_cipher;
 +#ifdef IEEE8021X_EAPOL
 +              conf.proactive_key_caching = ssid->proactive_key_caching < 0 ?
 +                      wpa_s->conf->okc : ssid->proactive_key_caching;
 +              conf.eap_workaround = ssid->eap_workaround;
 +              conf.eap_conf_ctx = &ssid->eap;
 +#endif /* IEEE8021X_EAPOL */
 +              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);
 +}
index 9808c2265f996d2e9e1f9e8bc9c40b466c3db010,0000000000000000000000000000000000000000..5585e5615a652b3ba442a5343167b77f6d4f404a
mode 100644,000000..100644
--- /dev/null
@@@ -1,25 -1,0 +1,28 @@@
 +/*
 + * WPA Supplicant - Glue code to setup EAPOL and RSN modules
 + * Copyright (c) 2003-2008, Jouni Malinen <j@w1.fi>
 + *
 + * This software may be distributed under the terms of the BSD license.
 + * See README for more details.
 + */
 +
 +#ifndef WPAS_GLUE_H
 +#define WPAS_GLUE_H
 +
 +enum wpa_ctrl_req_type;
 +
 +int wpa_supplicant_init_eapol(struct wpa_supplicant *wpa_s);
 +int wpa_supplicant_init_wpa(struct wpa_supplicant *wpa_s);
 +void wpa_supplicant_rsn_supp_set_config(struct wpa_supplicant *wpa_s,
 +                                      struct wpa_ssid *ssid);
 +
 +const char * wpa_supplicant_ctrl_req_to_string(enum wpa_ctrl_req_type field,
 +                                             const char *default_txt,
 +                                             const char **txt);
 +
 +enum wpa_ctrl_req_type wpa_supplicant_ctrl_req_from_string(const char *field);
 +
++void wpas_send_ctrl_req(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
++                      const char *field_name, const char *txt);
++
 +#endif /* WPAS_GLUE_H */
index eabe986541c38ceb61e701a20501545cb83b4a6a,0000000000000000000000000000000000000000..60f761c81b80977ac62a1175a3ff5543e0c023cf
mode 100644,000000..100644
--- /dev/null
@@@ -1,2848 -1,0 +1,2902 @@@
-       return (wpa_s->assoc_freq > 2484) ? WPS_RF_50GHZ : WPS_RF_24GHZ;
 +/*
 + * wpa_supplicant / WPS integration
 + * 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.
 + */
 +
 +#include "includes.h"
 +
 +#include "common.h"
 +#include "eloop.h"
 +#include "uuid.h"
 +#include "crypto/random.h"
 +#include "crypto/dh_group5.h"
 +#include "common/ieee802_11_defs.h"
 +#include "common/ieee802_11_common.h"
 +#include "common/wpa_common.h"
 +#include "common/wpa_ctrl.h"
 +#include "eap_common/eap_wsc_common.h"
 +#include "eap_peer/eap.h"
 +#include "eapol_supp/eapol_supp_sm.h"
 +#include "rsn_supp/wpa.h"
 +#include "wps/wps_attr_parse.h"
 +#include "config.h"
 +#include "wpa_supplicant_i.h"
 +#include "driver_i.h"
 +#include "notify.h"
 +#include "blacklist.h"
 +#include "bss.h"
 +#include "scan.h"
 +#include "ap.h"
 +#include "p2p/p2p.h"
 +#include "p2p_supplicant.h"
 +#include "wps_supplicant.h"
 +
 +
 +#ifndef WPS_PIN_SCAN_IGNORE_SEL_REG
 +#define WPS_PIN_SCAN_IGNORE_SEL_REG 3
 +#endif /* WPS_PIN_SCAN_IGNORE_SEL_REG */
 +
++/*
++ * The minimum time in seconds before trying to associate to a WPS PIN AP that
++ * does not have Selected Registrar TRUE.
++ */
++#ifndef WPS_PIN_TIME_IGNORE_SEL_REG
++#define WPS_PIN_TIME_IGNORE_SEL_REG 5
++#endif /* WPS_PIN_TIME_IGNORE_SEL_REG */
++
 +static void wpas_wps_timeout(void *eloop_ctx, void *timeout_ctx);
 +static void wpas_clear_wps(struct wpa_supplicant *wpa_s);
 +
 +
 +static void wpas_wps_clear_ap_info(struct wpa_supplicant *wpa_s)
 +{
 +      os_free(wpa_s->wps_ap);
 +      wpa_s->wps_ap = NULL;
 +      wpa_s->num_wps_ap = 0;
 +      wpa_s->wps_ap_iter = 0;
 +}
 +
 +
 +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)) {
 +              const u8 *bssid = wpa_s->bssid;
 +              if (is_zero_ether_addr(bssid))
 +                      bssid = wpa_s->pending_bssid;
 +
 +              wpa_printf(MSG_DEBUG, "WPS: PIN registration with " MACSTR
 +                         " did not succeed - continue trying to find "
 +                         "suitable AP", MAC2STR(bssid));
 +              wpa_blacklist_add(wpa_s, bssid);
 +
 +              wpa_supplicant_deauthenticate(wpa_s,
 +                                            WLAN_REASON_DEAUTH_LEAVING);
 +              wpa_s->reassociate = 1;
 +              wpa_supplicant_req_scan(wpa_s,
 +                                      wpa_s->blacklist_cleared ? 5 : 0, 0);
 +              wpa_s->blacklist_cleared = 0;
 +              return 1;
 +      }
 +
 +      wpas_wps_clear_ap_info(wpa_s);
 +      eloop_cancel_timeout(wpas_wps_timeout, wpa_s, NULL);
 +      if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPS && !wpa_s->wps_success)
 +              wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_FAIL);
 +
 +      if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPS && wpa_s->current_ssid &&
 +          !(wpa_s->current_ssid->key_mgmt & WPA_KEY_MGMT_WPS)) {
 +              int disabled = wpa_s->current_ssid->disabled;
 +              unsigned int freq = wpa_s->assoc_freq;
 +              struct wpa_bss *bss;
 +              struct wpa_ssid *ssid = NULL;
 +              int use_fast_assoc = 0;
 +
 +              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) {
 +                      wpa_printf(MSG_DEBUG, "WPS: Current network is "
 +                                 "disabled - wait for user to enable");
 +                      return 1;
 +              }
 +              wpa_s->after_wps = 5;
 +              wpa_s->wps_freq = freq;
 +              wpa_s->normal_scans = 0;
 +              wpa_s->reassociate = 1;
 +
 +              wpa_printf(MSG_DEBUG, "WPS: Checking whether fast association "
 +                         "without a new scan can be used");
 +              bss = wpa_supplicant_pick_network(wpa_s, &ssid);
 +              if (bss) {
 +                      struct wpabuf *wps;
 +                      struct wps_parse_attr attr;
 +
 +                      wps = wpa_bss_get_vendor_ie_multi(bss,
 +                                                        WPS_IE_VENDOR_TYPE);
 +                      if (wps && wps_parse_msg(wps, &attr) == 0 &&
 +                          attr.wps_state &&
 +                          *attr.wps_state == WPS_STATE_CONFIGURED)
 +                              use_fast_assoc = 1;
 +                      wpabuf_free(wps);
 +              }
 +
 +              /*
 +               * 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;
 +      }
 +
 +      if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPS && wpa_s->current_ssid) {
 +              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;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +static void wpas_wps_security_workaround(struct wpa_supplicant *wpa_s,
 +                                       struct wpa_ssid *ssid,
 +                                       const struct wps_credential *cred)
 +{
 +      struct wpa_driver_capa capa;
 +      struct wpa_bss *bss;
 +      const u8 *ie;
 +      struct wpa_ie_data adv;
 +      int wpa2 = 0, ccmp = 0;
 +
 +      /*
 +       * Many existing WPS APs do not know how to negotiate WPA2 or CCMP in
 +       * case they are configured for mixed mode operation (WPA+WPA2 and
 +       * TKIP+CCMP). Try to use scan results to figure out whether the AP
 +       * actually supports stronger security and select that if the client
 +       * has support for it, too.
 +       */
 +
 +      if (wpa_drv_get_capa(wpa_s, &capa))
 +              return; /* Unknown what driver supports */
 +
 +      if (ssid->ssid == NULL)
 +              return;
 +      bss = wpa_bss_get(wpa_s, cred->mac_addr, ssid->ssid, ssid->ssid_len);
 +      if (bss == NULL) {
 +              wpa_printf(MSG_DEBUG, "WPS: The AP was not found from BSS "
 +                         "table - use credential as-is");
 +              return;
 +      }
 +
 +      wpa_printf(MSG_DEBUG, "WPS: AP found from BSS table");
 +
 +      ie = wpa_bss_get_ie(bss, WLAN_EID_RSN);
 +      if (ie && wpa_parse_wpa_ie(ie, 2 + ie[1], &adv) == 0) {
 +              wpa2 = 1;
 +              if (adv.pairwise_cipher & WPA_CIPHER_CCMP)
 +                      ccmp = 1;
 +      } else {
 +              ie = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE);
 +              if (ie && wpa_parse_wpa_ie(ie, 2 + ie[1], &adv) == 0 &&
 +                  adv.pairwise_cipher & WPA_CIPHER_CCMP)
 +                      ccmp = 1;
 +      }
 +
 +      if (ie == NULL && (ssid->proto & WPA_PROTO_WPA) &&
 +          (ssid->pairwise_cipher & WPA_CIPHER_TKIP)) {
 +              /*
 +               * TODO: This could be the initial AP configuration and the
 +               * Beacon contents could change shortly. Should request a new
 +               * scan and delay addition of the network until the updated
 +               * scan results are available.
 +               */
 +              wpa_printf(MSG_DEBUG, "WPS: The AP did not yet advertise WPA "
 +                         "support - use credential as-is");
 +              return;
 +      }
 +
 +      if (ccmp && !(ssid->pairwise_cipher & WPA_CIPHER_CCMP) &&
 +          (ssid->pairwise_cipher & WPA_CIPHER_TKIP) &&
 +          (capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) {
 +              wpa_printf(MSG_DEBUG, "WPS: Add CCMP into the credential "
 +                         "based on scan results");
 +              if (wpa_s->conf->ap_scan == 1)
 +                      ssid->pairwise_cipher |= WPA_CIPHER_CCMP;
 +              else
 +                      ssid->pairwise_cipher = WPA_CIPHER_CCMP;
 +      }
 +
 +      if (wpa2 && !(ssid->proto & WPA_PROTO_RSN) &&
 +          (ssid->proto & WPA_PROTO_WPA) &&
 +          (capa.enc & WPA_DRIVER_CAPA_ENC_CCMP)) {
 +              wpa_printf(MSG_DEBUG, "WPS: Add WPA2 into the credential "
 +                         "based on scan results");
 +              if (wpa_s->conf->ap_scan == 1)
 +                      ssid->proto |= WPA_PROTO_RSN;
 +              else
 +                      ssid->proto = WPA_PROTO_RSN;
 +      }
 +}
 +
 +
 +static void wpas_wps_remove_dup_network(struct wpa_supplicant *wpa_s,
 +                                      struct wpa_ssid *new_ssid)
 +{
 +      struct wpa_ssid *ssid, *next;
 +
 +      for (ssid = wpa_s->conf->ssid, next = ssid ? ssid->next : NULL; ssid;
 +           ssid = next, next = ssid ? ssid->next : NULL) {
 +              /*
 +               * new_ssid has already been added to the list in
 +               * wpas_wps_add_network(), so skip it.
 +               */
 +              if (ssid == new_ssid)
 +                      continue;
 +
 +              if (ssid->bssid_set || new_ssid->bssid_set) {
 +                      if (ssid->bssid_set != new_ssid->bssid_set)
 +                              continue;
 +                      if (os_memcmp(ssid->bssid, new_ssid->bssid, ETH_ALEN) !=
 +                          0)
 +                              continue;
 +              }
 +
 +              /* compare SSID */
 +              if (ssid->ssid_len == 0 || ssid->ssid_len != new_ssid->ssid_len)
 +                      continue;
 +
 +              if (ssid->ssid && new_ssid->ssid) {
 +                      if (os_memcmp(ssid->ssid, new_ssid->ssid,
 +                                    ssid->ssid_len) != 0)
 +                              continue;
 +              } else if (ssid->ssid || new_ssid->ssid)
 +                      continue;
 +
 +              /* compare security parameters */
 +              if (ssid->auth_alg != new_ssid->auth_alg ||
 +                  ssid->key_mgmt != new_ssid->key_mgmt ||
 +                  (ssid->group_cipher != new_ssid->group_cipher &&
 +                   !(ssid->group_cipher & new_ssid->group_cipher &
 +                     WPA_CIPHER_CCMP)))
 +                      continue;
 +
 +              /*
 +               * 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;
 +                      }
 +
 +                      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 ((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;
 +                      }
 +
 +                      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);
 +      }
 +}
 +
 +
 +static int wpa_supplicant_wps_cred(void *ctx,
 +                                 const struct wps_credential *cred)
 +{
 +      struct wpa_supplicant *wpa_s = ctx;
 +      struct wpa_ssid *ssid = wpa_s->current_ssid;
 +      u16 auth_type;
 +#ifdef CONFIG_WPS_REG_DISABLE_OPEN
 +      int registrar = 0;
 +#endif /* CONFIG_WPS_REG_DISABLE_OPEN */
 +
 +      if ((wpa_s->conf->wps_cred_processing == 1 ||
 +           wpa_s->conf->wps_cred_processing == 2) && cred->cred_attr) {
 +              size_t blen = cred->cred_attr_len * 2 + 1;
 +              char *buf = os_malloc(blen);
 +              if (buf) {
 +                      wpa_snprintf_hex(buf, blen,
 +                                       cred->cred_attr, cred->cred_attr_len);
 +                      wpa_msg(wpa_s, MSG_INFO, "%s%s",
 +                              WPS_EVENT_CRED_RECEIVED, buf);
 +                      os_free(buf);
 +              }
 +
 +              wpas_notify_wps_credential(wpa_s, cred);
 +      } else
 +              wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_CRED_RECEIVED);
 +
 +      wpa_hexdump_key(MSG_DEBUG, "WPS: Received Credential attribute",
 +                      cred->cred_attr, cred->cred_attr_len);
 +
 +      if (wpa_s->conf->wps_cred_processing == 1)
 +              return 0;
 +
 +      wpa_hexdump_ascii(MSG_DEBUG, "WPS: SSID", cred->ssid, cred->ssid_len);
 +      wpa_printf(MSG_DEBUG, "WPS: Authentication Type 0x%x",
 +                 cred->auth_type);
 +      wpa_printf(MSG_DEBUG, "WPS: Encryption Type 0x%x", cred->encr_type);
 +      wpa_printf(MSG_DEBUG, "WPS: Network Key Index %d", cred->key_idx);
 +      wpa_hexdump_key(MSG_DEBUG, "WPS: Network Key",
 +                      cred->key, cred->key_len);
 +      wpa_printf(MSG_DEBUG, "WPS: MAC Address " MACSTR,
 +                 MAC2STR(cred->mac_addr));
 +
 +      auth_type = cred->auth_type;
 +      if (auth_type == (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK)) {
 +              wpa_printf(MSG_DEBUG, "WPS: Workaround - convert mixed-mode "
 +                         "auth_type into WPA2PSK");
 +              auth_type = WPS_AUTH_WPA2PSK;
 +      }
 +
 +      if (auth_type != WPS_AUTH_OPEN &&
 +          auth_type != WPS_AUTH_WPAPSK &&
 +          auth_type != WPS_AUTH_WPA2PSK) {
 +              wpa_printf(MSG_DEBUG, "WPS: Ignored credentials for "
 +                         "unsupported authentication type 0x%x",
 +                         auth_type);
 +              return 0;
 +      }
 +
 +      if (auth_type == WPS_AUTH_WPAPSK || auth_type == WPS_AUTH_WPA2PSK) {
 +              if (cred->key_len < 8 || cred->key_len > 2 * PMK_LEN) {
 +                      wpa_printf(MSG_ERROR, "WPS: Reject PSK credential with "
 +                                 "invalid Network Key length %lu",
 +                                 (unsigned long) cred->key_len);
 +                      return -1;
 +              }
 +      }
 +
 +      if (ssid && (ssid->key_mgmt & WPA_KEY_MGMT_WPS)) {
 +              wpa_printf(MSG_DEBUG, "WPS: Replace WPS network block based "
 +                         "on the received credential");
 +#ifdef CONFIG_WPS_REG_DISABLE_OPEN
 +              if (ssid->eap.identity &&
 +                  ssid->eap.identity_len == WSC_ID_REGISTRAR_LEN &&
 +                  os_memcmp(ssid->eap.identity, WSC_ID_REGISTRAR,
 +                            WSC_ID_REGISTRAR_LEN) == 0)
 +                      registrar = 1;
 +#endif /* CONFIG_WPS_REG_DISABLE_OPEN */
 +              os_free(ssid->eap.identity);
 +              ssid->eap.identity = NULL;
 +              ssid->eap.identity_len = 0;
 +              os_free(ssid->eap.phase1);
 +              ssid->eap.phase1 = NULL;
 +              os_free(ssid->eap.eap_methods);
 +              ssid->eap.eap_methods = NULL;
 +              if (!ssid->p2p_group) {
 +                      ssid->temporary = 0;
 +                      ssid->bssid_set = 0;
 +              }
 +              ssid->disabled_until.sec = 0;
 +              ssid->disabled_until.usec = 0;
 +              ssid->auth_failures = 0;
 +      } else {
 +              wpa_printf(MSG_DEBUG, "WPS: Create a new network based on the "
 +                         "received credential");
 +              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);
 +      if (ssid->ssid) {
 +              os_memcpy(ssid->ssid, cred->ssid, cred->ssid_len);
 +              ssid->ssid_len = cred->ssid_len;
 +      }
 +
 +      switch (cred->encr_type) {
 +      case WPS_ENCR_NONE:
 +              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;
 +      }
 +
 +      switch (auth_type) {
 +      case WPS_AUTH_OPEN:
 +              ssid->auth_alg = WPA_AUTH_ALG_OPEN;
 +              ssid->key_mgmt = WPA_KEY_MGMT_NONE;
 +              ssid->proto = 0;
 +#ifdef CONFIG_WPS_REG_DISABLE_OPEN
 +              if (registrar) {
 +                      wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_OPEN_NETWORK
 +                              "id=%d - Credentials for an open "
 +                              "network disabled by default - use "
 +                              "'select_network %d' to enable",
 +                              ssid->id, ssid->id);
 +                      ssid->disabled = 1;
 +              }
 +#endif /* CONFIG_WPS_REG_DISABLE_OPEN */
 +              break;
 +      case WPS_AUTH_WPAPSK:
 +              ssid->auth_alg = WPA_AUTH_ALG_OPEN;
 +              ssid->key_mgmt = WPA_KEY_MGMT_PSK;
 +              ssid->proto = WPA_PROTO_WPA;
 +              break;
 +      case WPS_AUTH_WPA2PSK:
 +              ssid->auth_alg = WPA_AUTH_ALG_OPEN;
 +              ssid->key_mgmt = WPA_KEY_MGMT_PSK;
 +              ssid->proto = WPA_PROTO_RSN;
 +              break;
 +      }
 +
 +      if (ssid->key_mgmt == WPA_KEY_MGMT_PSK) {
 +              if (cred->key_len == 2 * PMK_LEN) {
 +                      if (hexstr2bin((const char *) cred->key, ssid->psk,
 +                                     PMK_LEN)) {
 +                              wpa_printf(MSG_ERROR, "WPS: Invalid Network "
 +                                         "Key");
 +                              return -1;
 +                      }
 +                      ssid->psk_set = 1;
 +                      ssid->export_keys = 1;
 +              } else if (cred->key_len >= 8 && cred->key_len < 2 * PMK_LEN) {
 +                      os_free(ssid->passphrase);
 +                      ssid->passphrase = os_malloc(cred->key_len + 1);
 +                      if (ssid->passphrase == NULL)
 +                              return -1;
 +                      os_memcpy(ssid->passphrase, cred->key, cred->key_len);
 +                      ssid->passphrase[cred->key_len] = '\0';
 +                      wpa_config_update_psk(ssid);
 +                      ssid->export_keys = 1;
 +              } else {
 +                      wpa_printf(MSG_ERROR, "WPS: Invalid Network Key "
 +                                 "length %lu",
 +                                 (unsigned long) cred->key_len);
 +                      return -1;
 +              }
 +      }
++      ssid->priority = wpa_s->conf->wps_priority;
 +
 +      wpas_wps_security_workaround(wpa_s, ssid, cred);
 +
 +      wpas_wps_remove_dup_network(wpa_s, ssid);
 +
 +#ifndef CONFIG_NO_CONFIG_WRITE
 +      if (wpa_s->conf->update_config &&
 +          wpa_config_write(wpa_s->confname, wpa_s->conf)) {
 +              wpa_printf(MSG_DEBUG, "WPS: Failed to update configuration");
 +              return -1;
 +      }
 +#endif /* CONFIG_NO_CONFIG_WRITE */
 +
++      if (ssid->priority)
++              wpa_config_update_prio_list(wpa_s->conf);
++
 +      /*
 +       * Optimize the post-WPS scan based on the channel used during
 +       * the provisioning in case EAP-Failure is not received.
 +       */
 +      wpa_s->after_wps = 5;
 +      wpa_s->wps_freq = wpa_s->assoc_freq;
 +
 +      return 0;
 +}
 +
 +
 +static void wpa_supplicant_wps_event_m2d(struct wpa_supplicant *wpa_s,
 +                                       struct wps_event_m2d *m2d)
 +{
 +      wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_M2D
 +              "dev_password_id=%d config_error=%d",
 +              m2d->dev_password_id, m2d->config_error);
 +      wpas_notify_wps_event_m2d(wpa_s, m2d);
 +#ifdef CONFIG_P2P
 +      if (wpa_s->parent && wpa_s->parent != wpa_s) {
 +              wpa_msg(wpa_s->parent, MSG_INFO, WPS_EVENT_M2D
 +                      "dev_password_id=%d config_error=%d",
 +                      m2d->dev_password_id, m2d->config_error);
 +      }
 +      if (m2d->config_error == WPS_CFG_MULTIPLE_PBC_DETECTED) {
 +              /*
 +               * Notify P2P from eloop timeout to avoid issues with the
 +               * interface getting removed while processing a message.
 +               */
 +              eloop_register_timeout(0, 0, wpas_p2p_pbc_overlap_cb, wpa_s,
 +                                     NULL);
 +      }
 +#endif /* CONFIG_P2P */
 +}
 +
 +
 +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)
 +{
 +      if (fail->error_indication > 0 &&
 +          fail->error_indication < NUM_WPS_EI_VALUES) {
 +              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_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_ei_str(fail->error_indication));
 +      } else {
 +              wpa_msg(wpa_s, MSG_INFO,
 +                      WPS_EVENT_FAIL "msg=%d config_error=%d",
 +                      fail->msg, fail->config_error);
 +              if (wpa_s->parent && wpa_s->parent != wpa_s)
 +                      wpa_msg(wpa_s->parent, MSG_INFO, WPS_EVENT_FAIL
 +                              "msg=%d config_error=%d",
 +                              fail->msg, fail->config_error);
 +      }
 +
 +      /*
 +       * 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);
 +      wpas_p2p_wps_failed(wpa_s, fail);
 +}
 +
 +
 +static void wpas_wps_reenable_networks_cb(void *eloop_ctx, void *timeout_ctx);
 +
 +static void wpas_wps_reenable_networks(struct wpa_supplicant *wpa_s)
 +{
 +      struct wpa_ssid *ssid;
 +      int changed = 0;
 +
 +      eloop_cancel_timeout(wpas_wps_reenable_networks_cb, wpa_s, NULL);
 +
 +      for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
 +              if (ssid->disabled_for_connect && ssid->disabled) {
 +                      ssid->disabled_for_connect = 0;
 +                      ssid->disabled = 0;
 +                      wpas_notify_network_enabled_changed(wpa_s, ssid);
 +                      changed++;
 +              }
 +      }
 +
 +      if (changed) {
 +#ifndef CONFIG_NO_CONFIG_WRITE
 +              if (wpa_s->conf->update_config &&
 +                  wpa_config_write(wpa_s->confname, wpa_s->conf)) {
 +                      wpa_printf(MSG_DEBUG, "WPS: Failed to update "
 +                                 "configuration");
 +              }
 +#endif /* CONFIG_NO_CONFIG_WRITE */
 +      }
 +}
 +
 +
 +static void wpas_wps_reenable_networks_cb(void *eloop_ctx, void *timeout_ctx)
 +{
 +      struct wpa_supplicant *wpa_s = eloop_ctx;
 +      /* Enable the networks disabled during wpas_wps_reassoc */
 +      wpas_wps_reenable_networks(wpa_s);
 +}
 +
 +
 +static void wpa_supplicant_wps_event_success(struct wpa_supplicant *wpa_s)
 +{
 +      wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_SUCCESS);
 +      wpa_s->wps_success = 1;
 +      wpas_notify_wps_event_success(wpa_s);
 +      if (wpa_s->current_ssid)
 +              wpas_clear_temp_disabled(wpa_s, wpa_s->current_ssid, 1);
 +      wpa_s->extra_blacklist_count = 0;
 +
 +      /*
 +       * Enable the networks disabled during wpas_wps_reassoc after 10
 +       * seconds. The 10 seconds timer is to allow the data connection to be
 +       * formed before allowing other networks to be selected.
 +       */
 +      eloop_register_timeout(10, 0, wpas_wps_reenable_networks_cb, wpa_s,
 +                             NULL);
 +
 +      wpas_p2p_wps_success(wpa_s, wpa_s->bssid, 0);
 +}
 +
 +
 +static void wpa_supplicant_wps_event_er_ap_add(struct wpa_supplicant *wpa_s,
 +                                             struct wps_event_er_ap *ap)
 +{
 +      char uuid_str[100];
 +      char dev_type[WPS_DEV_TYPE_BUFSIZE];
 +
 +      uuid_bin2str(ap->uuid, uuid_str, sizeof(uuid_str));
 +      if (ap->pri_dev_type)
 +              wps_dev_type_bin2str(ap->pri_dev_type, dev_type,
 +                                   sizeof(dev_type));
 +      else
 +              dev_type[0] = '\0';
 +
 +      wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_ER_AP_ADD "%s " MACSTR
 +              " pri_dev_type=%s wps_state=%d |%s|%s|%s|%s|%s|%s|",
 +              uuid_str, MAC2STR(ap->mac_addr), dev_type, ap->wps_state,
 +              ap->friendly_name ? ap->friendly_name : "",
 +              ap->manufacturer ? ap->manufacturer : "",
 +              ap->model_description ? ap->model_description : "",
 +              ap->model_name ? ap->model_name : "",
 +              ap->manufacturer_url ? ap->manufacturer_url : "",
 +              ap->model_url ? ap->model_url : "");
 +}
 +
 +
 +static void wpa_supplicant_wps_event_er_ap_remove(struct wpa_supplicant *wpa_s,
 +                                                struct wps_event_er_ap *ap)
 +{
 +      char uuid_str[100];
 +      uuid_bin2str(ap->uuid, uuid_str, sizeof(uuid_str));
 +      wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_ER_AP_REMOVE "%s", uuid_str);
 +}
 +
 +
 +static void wpa_supplicant_wps_event_er_enrollee_add(
 +      struct wpa_supplicant *wpa_s, struct wps_event_er_enrollee *enrollee)
 +{
 +      char uuid_str[100];
 +      char dev_type[WPS_DEV_TYPE_BUFSIZE];
 +
 +      uuid_bin2str(enrollee->uuid, uuid_str, sizeof(uuid_str));
 +      if (enrollee->pri_dev_type)
 +              wps_dev_type_bin2str(enrollee->pri_dev_type, dev_type,
 +                                   sizeof(dev_type));
 +      else
 +              dev_type[0] = '\0';
 +
 +      wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_ER_ENROLLEE_ADD "%s " MACSTR
 +              " M1=%d config_methods=0x%x dev_passwd_id=%d pri_dev_type=%s "
 +              "|%s|%s|%s|%s|%s|",
 +              uuid_str, MAC2STR(enrollee->mac_addr), enrollee->m1_received,
 +              enrollee->config_methods, enrollee->dev_passwd_id, dev_type,
 +              enrollee->dev_name ? enrollee->dev_name : "",
 +              enrollee->manufacturer ? enrollee->manufacturer : "",
 +              enrollee->model_name ? enrollee->model_name : "",
 +              enrollee->model_number ? enrollee->model_number : "",
 +              enrollee->serial_number ? enrollee->serial_number : "");
 +}
 +
 +
 +static void wpa_supplicant_wps_event_er_enrollee_remove(
 +      struct wpa_supplicant *wpa_s, struct wps_event_er_enrollee *enrollee)
 +{
 +      char uuid_str[100];
 +      uuid_bin2str(enrollee->uuid, uuid_str, sizeof(uuid_str));
 +      wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_ER_ENROLLEE_REMOVE "%s " MACSTR,
 +              uuid_str, MAC2STR(enrollee->mac_addr));
 +}
 +
 +
 +static void wpa_supplicant_wps_event_er_ap_settings(
 +      struct wpa_supplicant *wpa_s,
 +      struct wps_event_er_ap_settings *ap_settings)
 +{
 +      char uuid_str[100];
 +      char key_str[65];
 +      const struct wps_credential *cred = ap_settings->cred;
 +
 +      key_str[0] = '\0';
 +      if (cred->auth_type & (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK)) {
 +              if (cred->key_len >= 8 && cred->key_len <= 64) {
 +                      os_memcpy(key_str, cred->key, cred->key_len);
 +                      key_str[cred->key_len] = '\0';
 +              }
 +      }
 +
 +      uuid_bin2str(ap_settings->uuid, uuid_str, sizeof(uuid_str));
 +      /* Use wpa_msg_ctrl to avoid showing the key in debug log */
 +      wpa_msg_ctrl(wpa_s, MSG_INFO, WPS_EVENT_ER_AP_SETTINGS
 +                   "uuid=%s ssid=%s auth_type=0x%04x encr_type=0x%04x "
 +                   "key=%s",
 +                   uuid_str, wpa_ssid_txt(cred->ssid, cred->ssid_len),
 +                   cred->auth_type, cred->encr_type, key_str);
 +}
 +
 +
 +static void wpa_supplicant_wps_event_er_set_sel_reg(
 +      struct wpa_supplicant *wpa_s,
 +      struct wps_event_er_set_selected_registrar *ev)
 +{
 +      char uuid_str[100];
 +
 +      uuid_bin2str(ev->uuid, uuid_str, sizeof(uuid_str));
 +      switch (ev->state) {
 +      case WPS_ER_SET_SEL_REG_START:
 +              wpa_msg(wpa_s, MSG_DEBUG, WPS_EVENT_ER_SET_SEL_REG
 +                      "uuid=%s state=START sel_reg=%d dev_passwd_id=%u "
 +                      "sel_reg_config_methods=0x%x",
 +                      uuid_str, ev->sel_reg, ev->dev_passwd_id,
 +                      ev->sel_reg_config_methods);
 +              break;
 +      case WPS_ER_SET_SEL_REG_DONE:
 +              wpa_msg(wpa_s, MSG_DEBUG, WPS_EVENT_ER_SET_SEL_REG
 +                      "uuid=%s state=DONE", uuid_str);
 +              break;
 +      case WPS_ER_SET_SEL_REG_FAILED:
 +              wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_ER_SET_SEL_REG
 +                      "uuid=%s state=FAILED", uuid_str);
 +              break;
 +      }
 +}
 +
 +
 +static void wpa_supplicant_wps_event(void *ctx, enum wps_event event,
 +                                   union wps_event_data *data)
 +{
 +      struct wpa_supplicant *wpa_s = ctx;
 +      switch (event) {
 +      case WPS_EV_M2D:
 +              wpa_supplicant_wps_event_m2d(wpa_s, &data->m2d);
 +              break;
 +      case WPS_EV_FAIL:
 +              wpa_supplicant_wps_event_fail(wpa_s, &data->fail);
 +              break;
 +      case WPS_EV_SUCCESS:
 +              wpa_supplicant_wps_event_success(wpa_s);
 +              break;
 +      case WPS_EV_PWD_AUTH_FAIL:
 +#ifdef CONFIG_AP
 +              if (wpa_s->ap_iface && data->pwd_auth_fail.enrollee)
 +                      wpa_supplicant_ap_pwd_auth_fail(wpa_s);
 +#endif /* CONFIG_AP */
 +              break;
 +      case WPS_EV_PBC_OVERLAP:
 +              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;
 +      case WPS_EV_ER_AP_REMOVE:
 +              wpa_supplicant_wps_event_er_ap_remove(wpa_s, &data->ap);
 +              break;
 +      case WPS_EV_ER_ENROLLEE_ADD:
 +              wpa_supplicant_wps_event_er_enrollee_add(wpa_s,
 +                                                       &data->enrollee);
 +              break;
 +      case WPS_EV_ER_ENROLLEE_REMOVE:
 +              wpa_supplicant_wps_event_er_enrollee_remove(wpa_s,
 +                                                          &data->enrollee);
 +              break;
 +      case WPS_EV_ER_AP_SETTINGS:
 +              wpa_supplicant_wps_event_er_ap_settings(wpa_s,
 +                                                      &data->ap_settings);
 +              break;
 +      case WPS_EV_ER_SET_SELECTED_REGISTRAR:
 +              wpa_supplicant_wps_event_er_set_sel_reg(wpa_s,
 +                                                      &data->set_sel_reg);
 +              break;
 +      case WPS_EV_AP_PIN_SUCCESS:
 +              break;
 +      }
 +}
 +
 +
 +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;
 +
-                       if (wpa_s->scan_runs < WPS_PIN_SCAN_IGNORE_SEL_REG) {
-                               wpa_printf(MSG_DEBUG, "   skip - WPS AP "
-                                          "without active PIN Registrar");
++      return (wpa_s->assoc_freq > 50000) ? WPS_RF_60GHZ :
++              (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) ||
 +          eap_is_wps_pin_enrollee(&ssid->eap))
 +              return WPS_REQ_ENROLLEE;
 +      else
 +              return WPS_REQ_REGISTRAR;
 +}
 +
 +
 +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->own_disconnect_req = 1;
 +                              wpa_supplicant_deauthenticate(
 +                                      wpa_s, WLAN_REASON_DEAUTH_LEAVING);
 +                      }
 +                      id = ssid->id;
 +                      remove_ssid = ssid;
 +              } else
 +                      id = -1;
 +              ssid = ssid->next;
 +              if (id >= 0) {
 +                      if (prev_current == remove_ssid) {
 +                              wpa_sm_set_config(wpa_s->wpa, NULL);
 +                              eapol_sm_notify_config(wpa_s->eapol, NULL,
 +                                                     NULL);
 +                      }
 +                      wpas_notify_network_removed(wpa_s, remove_ssid);
 +                      wpa_config_remove_network(wpa_s->conf, id);
 +              }
 +      }
 +
 +      wpas_wps_clear_ap_info(wpa_s);
 +}
 +
 +
 +static void wpas_wps_timeout(void *eloop_ctx, void *timeout_ctx)
 +{
 +      struct wpa_supplicant *wpa_s = eloop_ctx;
++      union wps_event_data data;
++
 +      wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_TIMEOUT "Requested operation timed "
 +              "out");
++      os_memset(&data, 0, sizeof(data));
++      data.fail.config_error = WPS_CFG_MSG_TIMEOUT;
++      data.fail.error_indication = WPS_EI_NO_ERROR;
++      /*
++       * Call wpas_notify_wps_event_fail() directly instead of through
++       * wpa_supplicant_wps_event() which would end up registering unnecessary
++       * timeouts (those are only for the case where the failure happens
++       * during an EAP-WSC exchange).
++       */
++      wpas_notify_wps_event_fail(wpa_s, &data.fail);
 +      wpas_clear_wps(wpa_s);
 +}
 +
 +
 +static struct wpa_ssid * wpas_wps_add_network(struct wpa_supplicant *wpa_s,
 +                                            int registrar, const u8 *dev_addr,
 +                                            const u8 *bssid)
 +{
 +      struct wpa_ssid *ssid;
 +
 +      ssid = wpa_config_add_network(wpa_s->conf);
 +      if (ssid == NULL)
 +              return NULL;
 +      wpas_notify_network_added(wpa_s, ssid);
 +      wpa_config_set_network_defaults(ssid);
 +      ssid->temporary = 1;
 +      if (wpa_config_set(ssid, "key_mgmt", "WPS", 0) < 0 ||
 +          wpa_config_set(ssid, "eap", "WSC", 0) < 0 ||
 +          wpa_config_set(ssid, "identity", registrar ?
 +                         "\"" WSC_ID_REGISTRAR "\"" :
 +                         "\"" WSC_ID_ENROLLEE "\"", 0) < 0) {
 +              wpas_notify_network_removed(wpa_s, ssid);
 +              wpa_config_remove_network(wpa_s->conf, ssid->id);
 +              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;
 +              int count = 0;
 +#endif /* CONFIG_P2P */
 +
 +              os_memcpy(ssid->bssid, bssid, ETH_ALEN);
 +              ssid->bssid_set = 1;
 +
 +              /*
 +               * Note: With P2P, the SSID may change at the time the WPS
 +               * provisioning is started, so better not filter the AP based
 +               * on the current SSID in the scan results.
 +               */
 +#ifndef CONFIG_P2P
 +              dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
 +                      if (os_memcmp(bssid, bss->bssid, ETH_ALEN) != 0)
 +                              continue;
 +
 +                      os_free(ssid->ssid);
 +                      ssid->ssid = os_malloc(bss->ssid_len);
 +                      if (ssid->ssid == NULL)
 +                              break;
 +                      os_memcpy(ssid->ssid, bss->ssid, bss->ssid_len);
 +                      ssid->ssid_len = bss->ssid_len;
 +                      wpa_hexdump_ascii(MSG_DEBUG, "WPS: Picked SSID from "
 +                                        "scan results",
 +                                        ssid->ssid, ssid->ssid_len);
 +                      count++;
 +              }
 +
 +              if (count > 1) {
 +                      wpa_printf(MSG_DEBUG, "WPS: More than one SSID found "
 +                                 "for the AP; use wildcard");
 +                      os_free(ssid->ssid);
 +                      ssid->ssid = NULL;
 +                      ssid->ssid_len = 0;
 +              }
 +#endif /* CONFIG_P2P */
 +      }
 +
 +      return ssid;
 +}
 +
 +
 +static void wpas_wps_temp_disable(struct wpa_supplicant *wpa_s,
 +                                struct wpa_ssid *selected)
 +{
 +      struct wpa_ssid *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;
 +      while (ssid) {
 +              int was_disabled = ssid->disabled;
 +              ssid->disabled_for_connect = 0;
 +              /*
 +               * In case the network object corresponds to a persistent group
 +               * then do not send out network disabled signal. In addition,
 +               * do not change disabled status of persistent network objects
 +               * from 2 to 1 should we connect to another network.
 +               */
 +              if (was_disabled != 2) {
 +                      ssid->disabled = ssid != selected;
 +                      if (was_disabled != ssid->disabled) {
 +                              if (ssid->disabled)
 +                                      ssid->disabled_for_connect = 1;
 +                              wpas_notify_network_enabled_changed(wpa_s,
 +                                                                  ssid);
 +                      }
 +              }
 +              ssid = ssid->next;
 +      }
 +}
 +
 +
 +static void wpas_wps_reassoc(struct wpa_supplicant *wpa_s,
 +                           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 (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;
 +                      wpa_s->wps_freq = bss->freq;
 +              }
 +      }
 +
 +      wpas_wps_temp_disable(wpa_s, selected);
 +
 +      wpa_s->disconnected = 0;
 +      wpa_s->reassociate = 1;
 +      wpa_s->scan_runs = 0;
 +      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);
 +}
 +
 +
 +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, NULL, bssid);
 +      if (ssid == NULL)
 +              return -1;
 +      ssid->temporary = 1;
 +      ssid->p2p_group = p2p_group;
 +#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);
 +              if (ssid->ssid) {
 +                      ssid->ssid_len = wpa_s->go_params->ssid_len;
 +                      os_memcpy(ssid->ssid, wpa_s->go_params->ssid,
 +                                ssid->ssid_len);
 +                      wpa_hexdump_ascii(MSG_DEBUG, "WPS: Use specific AP "
 +                                        "SSID", ssid->ssid, ssid->ssid_len);
 +              }
 +      }
 +#endif /* CONFIG_P2P */
 +      if (wpa_config_set(ssid, "phase1", "\"pbc=1\"", 0) < 0)
 +              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);
 +      wpas_wps_reassoc(wpa_s, ssid, bssid, 0);
 +      return 0;
 +}
 +
 +
 +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 + 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);
 +      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) {
++              os_free(ssid->ssid);
 +              ssid->ssid = os_zalloc(wpa_s->go_params->ssid_len + 1);
 +              if (ssid->ssid) {
 +                      ssid->ssid_len = wpa_s->go_params->ssid_len;
 +                      os_memcpy(ssid->ssid, wpa_s->go_params->ssid,
 +                                ssid->ssid_len);
 +                      wpa_hexdump_ascii(MSG_DEBUG, "WPS: Use specific AP "
 +                                        "SSID", ssid->ssid, ssid->ssid_len);
 +              }
 +      }
 +#endif /* CONFIG_P2P */
 +      if (pin)
 +              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%s\"",
 +                          rpin, dev_pw_id, hash);
 +      }
 +      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, 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)
 +{
++      os_get_reltime(&wpa_s->wps_pin_start_time);
 +      return wpas_wps_start_dev_pw(wpa_s, NULL, bssid, pin, p2p_group,
 +                                   dev_pw_id, NULL, NULL, 0, 0);
 +}
 +
 +
++void wpas_wps_pbc_overlap(struct wpa_supplicant *wpa_s)
++{
++      union wps_event_data data;
++
++      os_memset(&data, 0, sizeof(data));
++      data.fail.config_error = WPS_CFG_MULTIPLE_PBC_DETECTED;
++      data.fail.error_indication = WPS_EI_NO_ERROR;
++      /*
++       * Call wpas_notify_wps_event_fail() directly instead of through
++       * wpa_supplicant_wps_event() which would end up registering unnecessary
++       * timeouts (those are only for the case where the failure happens
++       * during an EAP-WSC exchange).
++       */
++      wpas_notify_wps_event_fail(wpa_s, &data.fail);
++}
++
 +/* Cancel the wps pbc/pin requests */
 +int wpas_wps_cancel(struct wpa_supplicant *wpa_s)
 +{
 +#ifdef CONFIG_AP
 +      if (wpa_s->ap_iface) {
 +              wpa_printf(MSG_DEBUG, "WPS: Cancelling in AP mode");
 +              return wpa_supplicant_ap_wps_cancel(wpa_s);
 +      }
 +#endif /* CONFIG_AP */
 +
 +      if (wpa_s->wpa_state == WPA_SCANNING ||
 +          wpa_s->wpa_state == WPA_DISCONNECTED) {
 +              wpa_printf(MSG_DEBUG, "WPS: Cancel operation - cancel scan");
 +              wpa_supplicant_cancel_scan(wpa_s);
 +              wpas_clear_wps(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;
 +}
 +
 +
 +int wpas_wps_start_reg(struct wpa_supplicant *wpa_s, const u8 *bssid,
 +                     const char *pin, struct wps_new_ap_settings *settings)
 +{
 +      struct wpa_ssid *ssid;
 +      char val[200];
 +      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, 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 (os_snprintf_error(end - pos, res))
 +              return -1;
 +      pos += res;
 +      if (settings) {
 +              res = os_snprintf(pos, end - pos, " new_ssid=%s new_auth=%s "
 +                                "new_encr=%s new_key=%s",
 +                                settings->ssid_hex, settings->auth,
 +                                settings->encr, settings->key_hex);
 +              if (os_snprintf_error(end - pos, res))
 +                      return -1;
 +              pos += res;
 +      }
 +      res = os_snprintf(pos, end - pos, "\"");
 +      if (os_snprintf_error(end - pos, res))
 +              return -1;
 +      if (wpa_config_set(ssid, "phase1", val, 0) < 0)
 +              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);
 +      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 *p2p_dev_addr, const u8 *psk,
 +                             size_t psk_len)
 +{
 +      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 */
 +
 +      return 0;
 +}
 +
 +
 +static void wpas_wps_pin_needed_cb(void *ctx, const u8 *uuid_e,
 +                                 const struct wps_device_data *dev)
 +{
 +      char uuid[40], txt[400];
 +      int len;
 +      char devtype[WPS_DEV_TYPE_BUFSIZE];
 +      if (uuid_bin2str(uuid_e, uuid, sizeof(uuid)))
 +              return;
 +      wpa_printf(MSG_DEBUG, "WPS: PIN needed for UUID-E %s", uuid);
 +      len = os_snprintf(txt, sizeof(txt), "WPS-EVENT-PIN-NEEDED %s " MACSTR
 +                        " [%s|%s|%s|%s|%s|%s]",
 +                        uuid, MAC2STR(dev->mac_addr), dev->device_name,
 +                        dev->manufacturer, dev->model_name,
 +                        dev->model_number, dev->serial_number,
 +                        wps_dev_type_bin2str(dev->pri_dev_type, devtype,
 +                                             sizeof(devtype)));
 +      if (!os_snprintf_error(sizeof(txt), len))
 +              wpa_printf(MSG_INFO, "%s", txt);
 +}
 +
 +
 +static void wpas_wps_set_sel_reg_cb(void *ctx, int sel_reg, u16 dev_passwd_id,
 +                                  u16 sel_reg_config_methods)
 +{
 +#ifdef CONFIG_WPS_ER
 +      struct wpa_supplicant *wpa_s = ctx;
 +
 +      if (wpa_s->wps_er == NULL)
 +              return;
 +      wpa_printf(MSG_DEBUG, "WPS ER: SetSelectedRegistrar - sel_reg=%d "
 +                 "dev_password_id=%u sel_reg_config_methods=0x%x",
 +                 sel_reg, dev_passwd_id, sel_reg_config_methods);
 +      wps_er_set_sel_reg(wpa_s->wps_er, sel_reg, dev_passwd_id,
 +                         sel_reg_config_methods);
 +#endif /* CONFIG_WPS_ER */
 +}
 +
 +
 +static u16 wps_fix_config_methods(u16 config_methods)
 +{
 +      if ((config_methods &
 +           (WPS_CONFIG_DISPLAY | WPS_CONFIG_VIRT_DISPLAY |
 +            WPS_CONFIG_PHY_DISPLAY)) == WPS_CONFIG_DISPLAY) {
 +              wpa_printf(MSG_INFO, "WPS: Converting display to "
 +                         "virtual_display for WPS 2.0 compliance");
 +              config_methods |= WPS_CONFIG_VIRT_DISPLAY;
 +      }
 +      if ((config_methods &
 +           (WPS_CONFIG_PUSHBUTTON | WPS_CONFIG_VIRT_PUSHBUTTON |
 +            WPS_CONFIG_PHY_PUSHBUTTON)) == WPS_CONFIG_PUSHBUTTON) {
 +              wpa_printf(MSG_INFO, "WPS: Converting push_button to "
 +                         "virtual_push_button for WPS 2.0 compliance");
 +              config_methods |= WPS_CONFIG_VIRT_PUSHBUTTON;
 +      }
 +
 +      return config_methods;
 +}
 +
 +
 +static void wpas_wps_set_uuid(struct wpa_supplicant *wpa_s,
 +                            struct wps_context *wps)
 +{
 +      char buf[50];
 +      const char *src;
 +
 +      if (is_nil_uuid(wpa_s->conf->uuid)) {
 +              struct wpa_supplicant *first;
 +              first = wpa_s->global->ifaces;
 +              while (first && first->next)
 +                      first = first->next;
 +              if (first && first != wpa_s) {
 +                      if (wps != wpa_s->global->ifaces->wps)
 +                              os_memcpy(wps->uuid,
 +                                        wpa_s->global->ifaces->wps->uuid,
 +                                        WPS_UUID_LEN);
 +                      src = "from the first interface";
 +              } else {
 +                      uuid_gen_mac_addr(wpa_s->own_addr, wps->uuid);
 +                      src = "based on MAC address";
 +              }
 +      } else {
 +              os_memcpy(wps->uuid, wpa_s->conf->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);
 +}
 +
 +
 +static void wpas_wps_set_vendor_ext_m1(struct wpa_supplicant *wpa_s,
 +                                     struct wps_context *wps)
 +{
 +      wpabuf_free(wps->dev.vendor_ext_m1);
 +      wps->dev.vendor_ext_m1 = NULL;
 +
 +      if (wpa_s->conf->wps_vendor_ext_m1) {
 +              wps->dev.vendor_ext_m1 =
 +                      wpabuf_dup(wpa_s->conf->wps_vendor_ext_m1);
 +              if (!wps->dev.vendor_ext_m1) {
 +                      wpa_printf(MSG_ERROR, "WPS: Cannot "
 +                                 "allocate memory for vendor_ext_m1");
 +              }
 +      }
 +}
 +
 +
 +int wpas_wps_init(struct wpa_supplicant *wpa_s)
 +{
 +      struct wps_context *wps;
 +      struct wps_registrar_config rcfg;
 +      struct hostapd_hw_modes *modes;
 +      u16 m;
 +
 +      wps = os_zalloc(sizeof(*wps));
 +      if (wps == NULL)
 +              return -1;
 +
 +      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;
 +      wps->dev.manufacturer = wpa_s->conf->manufacturer;
 +      wps->dev.model_name = wpa_s->conf->model_name;
 +      wps->dev.model_number = wpa_s->conf->model_number;
 +      wps->dev.serial_number = wpa_s->conf->serial_number;
 +      wps->config_methods =
 +              wps_config_methods_str2bin(wpa_s->conf->config_methods);
 +      if ((wps->config_methods & (WPS_CONFIG_DISPLAY | WPS_CONFIG_LABEL)) ==
 +          (WPS_CONFIG_DISPLAY | WPS_CONFIG_LABEL)) {
 +              wpa_printf(MSG_ERROR, "WPS: Both Label and Display config "
 +                         "methods are not allowed at the same time");
 +              os_free(wps);
 +              return -1;
 +      }
 +      wps->config_methods = wps_fix_config_methods(wps->config_methods);
 +      wps->dev.config_methods = wps->config_methods;
 +      os_memcpy(wps->dev.pri_dev_type, wpa_s->conf->device_type,
 +                WPS_DEV_TYPE_LEN);
 +
 +      wps->dev.num_sec_dev_types = wpa_s->conf->num_sec_device_types;
 +      os_memcpy(wps->dev.sec_dev_type, wpa_s->conf->sec_device_type,
 +                WPS_DEV_TYPE_LEN * wps->dev.num_sec_dev_types);
 +
 +      wpas_wps_set_vendor_ext_m1(wpa_s, wps);
 +
 +      wps->dev.os_version = WPA_GET_BE32(wpa_s->conf->os_version);
 +      modes = wpa_s->hw.modes;
 +      if (modes) {
 +              for (m = 0; m < wpa_s->hw.num_modes; m++) {
 +                      if (modes[m].mode == HOSTAPD_MODE_IEEE80211B ||
 +                          modes[m].mode == HOSTAPD_MODE_IEEE80211G)
 +                              wps->dev.rf_bands |= WPS_RF_24GHZ;
 +                      else if (modes[m].mode == HOSTAPD_MODE_IEEE80211A)
 +                              wps->dev.rf_bands |= WPS_RF_50GHZ;
++                      else if (modes[m].mode == HOSTAPD_MODE_IEEE80211AD)
++                              wps->dev.rf_bands |= WPS_RF_60GHZ;
 +              }
 +      }
 +      if (wps->dev.rf_bands == 0) {
 +              /*
 +               * Default to claiming support for both bands if the driver
 +               * does not provide support for fetching supported bands.
 +               */
 +              wps->dev.rf_bands = WPS_RF_24GHZ | WPS_RF_50GHZ;
 +      }
 +      os_memcpy(wps->dev.mac_addr, wpa_s->own_addr, ETH_ALEN);
 +      wpas_wps_set_uuid(wpa_s, wps);
 +
 +      wps->auth_types = WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK;
 +      wps->encr_types = WPS_ENCR_AES | WPS_ENCR_TKIP;
 +
 +      os_memset(&rcfg, 0, sizeof(rcfg));
 +      rcfg.new_psk_cb = wpas_wps_new_psk_cb;
 +      rcfg.pin_needed_cb = wpas_wps_pin_needed_cb;
 +      rcfg.set_sel_reg_cb = wpas_wps_set_sel_reg_cb;
 +      rcfg.cb_ctx = wpa_s;
 +
 +      wps->registrar = wps_registrar_init(wps, &rcfg);
 +      if (wps->registrar == NULL) {
 +              wpa_printf(MSG_DEBUG, "Failed to initialize WPS Registrar");
 +              os_free(wps);
 +              return -1;
 +      }
 +
 +      wpa_s->wps = wps;
 +
 +      return 0;
 +}
 +
 +
 +#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);
 +      wpabuf_free(wpa_s->wps->dh_pubkey);
 +      wpabuf_free(wpa_s->wps->dh_privkey);
 +      wpabuf_free(wpa_s->wps->dev.vendor_ext_m1);
 +      os_free(wpa_s->wps->network_key);
 +      os_free(wpa_s->wps);
 +      wpa_s->wps = NULL;
 +}
 +
 +
 +int wpas_wps_ssid_bss_match(struct wpa_supplicant *wpa_s,
 +                          struct wpa_ssid *ssid, struct wpa_bss *bss)
 +{
 +      struct wpabuf *wps_ie;
 +
 +      if (!(ssid->key_mgmt & WPA_KEY_MGMT_WPS))
 +              return -1;
 +
 +      wps_ie = wpa_bss_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE);
 +      if (eap_is_wps_pbc_enrollee(&ssid->eap)) {
 +              if (!wps_ie) {
 +                      wpa_printf(MSG_DEBUG, "   skip - non-WPS AP");
 +                      return 0;
 +              }
 +
 +              if (!wps_is_selected_pbc_registrar(wps_ie)) {
 +                      wpa_printf(MSG_DEBUG, "   skip - WPS AP "
 +                                 "without active PBC Registrar");
 +                      wpabuf_free(wps_ie);
 +                      return 0;
 +              }
 +
 +              /* TODO: overlap detection */
 +              wpa_printf(MSG_DEBUG, "   selected based on WPS IE "
 +                         "(Active PBC)");
 +              wpabuf_free(wps_ie);
 +              return 1;
 +      }
 +
 +      if (eap_is_wps_pin_enrollee(&ssid->eap)) {
 +              if (!wps_ie) {
 +                      wpa_printf(MSG_DEBUG, "   skip - non-WPS AP");
 +                      return 0;
 +              }
 +
 +              /*
 +               * Start with WPS APs that advertise our address as an
 +               * authorized MAC (v2.0) or active PIN Registrar (v1.0) and
 +               * allow any WPS AP after couple of scans since some APs do not
 +               * set Selected Registrar attribute properly when using
 +               * external Registrar.
 +               */
 +              if (!wps_is_addr_authorized(wps_ie, wpa_s->own_addr, 1)) {
-       const u8 *sel_uuid, *uuid;
++                      struct os_reltime age;
++
++                      os_reltime_age(&wpa_s->wps_pin_start_time, &age);
++
++                      if (wpa_s->scan_runs < WPS_PIN_SCAN_IGNORE_SEL_REG ||
++                          age.sec < WPS_PIN_TIME_IGNORE_SEL_REG) {
++                              wpa_printf(MSG_DEBUG,
++                                         "   skip - WPS AP without active PIN Registrar (scan_runs=%d age=%d)",
++                                         wpa_s->scan_runs, (int) age.sec);
 +                              wpabuf_free(wps_ie);
 +                              return 0;
 +                      }
 +                      wpa_printf(MSG_DEBUG, "   selected based on WPS IE");
 +              } else {
 +                      wpa_printf(MSG_DEBUG, "   selected based on WPS IE "
 +                                 "(Authorized MAC or Active PIN)");
 +              }
 +              wpabuf_free(wps_ie);
 +              return 1;
 +      }
 +
 +      if (wps_ie) {
 +              wpa_printf(MSG_DEBUG, "   selected based on WPS IE");
 +              wpabuf_free(wps_ie);
 +              return 1;
 +      }
 +
 +      return -1;
 +}
 +
 +
 +int wpas_wps_ssid_wildcard_ok(struct wpa_supplicant *wpa_s,
 +                            struct wpa_ssid *ssid,
 +                            struct wpa_bss *bss)
 +{
 +      struct wpabuf *wps_ie = NULL;
 +      int ret = 0;
 +
 +      if (eap_is_wps_pbc_enrollee(&ssid->eap)) {
 +              wps_ie = wpa_bss_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE);
 +              if (wps_ie && wps_is_selected_pbc_registrar(wps_ie)) {
 +                      /* allow wildcard SSID for WPS PBC */
 +                      ret = 1;
 +              }
 +      } else if (eap_is_wps_pin_enrollee(&ssid->eap)) {
 +              wps_ie = wpa_bss_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE);
 +              if (wps_ie &&
 +                  (wps_is_addr_authorized(wps_ie, wpa_s->own_addr, 1) ||
 +                   wpa_s->scan_runs >= WPS_PIN_SCAN_IGNORE_SEL_REG)) {
 +                      /* allow wildcard SSID for WPS PIN */
 +                      ret = 1;
 +              }
 +      }
 +
 +      if (!ret && ssid->bssid_set &&
 +          os_memcmp(ssid->bssid, bss->bssid, ETH_ALEN) == 0) {
 +              /* allow wildcard SSID due to hardcoded BSSID match */
 +              ret = 1;
 +      }
 +
 +#ifdef CONFIG_WPS_STRICT
 +      if (wps_ie) {
 +              if (wps_validate_beacon_probe_resp(wps_ie, bss->beacon_ie_len >
 +                                                 0, bss->bssid) < 0)
 +                      ret = 0;
 +              if (bss->beacon_ie_len) {
 +                      struct wpabuf *bcn_wps;
 +                      bcn_wps = wpa_bss_get_vendor_ie_multi_beacon(
 +                              bss, WPS_IE_VENDOR_TYPE);
 +                      if (bcn_wps == NULL) {
 +                              wpa_printf(MSG_DEBUG, "WPS: Mandatory WPS IE "
 +                                         "missing from AP Beacon");
 +                              ret = 0;
 +                      } else {
 +                              if (wps_validate_beacon(wps_ie) < 0)
 +                                      ret = 0;
 +                              wpabuf_free(bcn_wps);
 +                      }
 +              }
 +      }
 +#endif /* CONFIG_WPS_STRICT */
 +
 +      wpabuf_free(wps_ie);
 +
 +      return ret;
 +}
 +
 +
 +int wpas_wps_scan_pbc_overlap(struct wpa_supplicant *wpa_s,
 +                            struct wpa_bss *selected, struct wpa_ssid *ssid)
 +{
-       struct wpa_bss *bss;
++      const u8 *sel_uuid;
 +      struct wpabuf *wps_ie;
 +      int ret = 0;
-       dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
-               struct wpabuf *ie;
-               if (bss == selected)
-                       continue;
-               ie = wpa_bss_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE);
-               if (!ie)
-                       continue;
-               if (!wps_is_selected_pbc_registrar(ie)) {
-                       wpabuf_free(ie);
++      size_t i;
 +
 +      if (!eap_is_wps_pbc_enrollee(&ssid->eap))
 +              return 0;
 +
 +      wpa_printf(MSG_DEBUG, "WPS: Check whether PBC session overlap is "
 +                 "present in scan results; selected BSSID " MACSTR,
 +                 MAC2STR(selected->bssid));
 +
 +      /* Make sure that only one AP is in active PBC mode */
 +      wps_ie = wpa_bss_get_vendor_ie_multi(selected, WPS_IE_VENDOR_TYPE);
 +      if (wps_ie) {
 +              sel_uuid = wps_get_uuid_e(wps_ie);
 +              wpa_hexdump(MSG_DEBUG, "WPS: UUID of the selected BSS",
 +                          sel_uuid, UUID_LEN);
 +      } else {
 +              wpa_printf(MSG_DEBUG, "WPS: Selected BSS does not include "
 +                         "WPS IE?!");
 +              sel_uuid = NULL;
 +      }
 +
-               }
++      for (i = 0; i < wpa_s->num_wps_ap; i++) {
++              struct wps_ap_info *ap = &wpa_s->wps_ap[i];
++
++              if (!ap->pbc_active ||
++                  os_memcmp(selected->bssid, ap->bssid, ETH_ALEN) == 0)
 +                      continue;
-                          MACSTR, MAC2STR(bss->bssid));
-               uuid = wps_get_uuid_e(ie);
++
 +              wpa_printf(MSG_DEBUG, "WPS: Another BSS in active PBC mode: "
-                           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) {
++                         MACSTR, MAC2STR(ap->bssid));
 +              wpa_hexdump(MSG_DEBUG, "WPS: UUID of the other BSS",
-                               MAC2STR(bss->bssid));
-                       wpabuf_free(ie);
++                          ap->uuid, UUID_LEN);
++              if (sel_uuid == NULL ||
++                  os_memcmp(sel_uuid, ap->uuid, UUID_LEN) != 0) {
 +                      ret = 1; /* PBC overlap */
 +                      wpa_msg(wpa_s, MSG_INFO, "WPS: PBC overlap detected: "
 +                              MACSTR " and " MACSTR,
 +                              MAC2STR(selected->bssid),
-               wpabuf_free(ie);
++                              MAC2STR(ap->bssid));
 +                      break;
 +              }
 +
 +              /* TODO: verify that this is reasonable dual-band situation */
-       if (ssid->ssid_len > 32)
 +      }
 +
 +      wpabuf_free(wps_ie);
 +
 +      return ret;
 +}
 +
 +
 +void wpas_wps_notify_scan_results(struct wpa_supplicant *wpa_s)
 +{
 +      struct wpa_bss *bss;
 +      unsigned int pbc = 0, auth = 0, pin = 0, wps = 0;
 +
 +      if (wpa_s->disconnected || wpa_s->wpa_state >= WPA_ASSOCIATED)
 +              return;
 +
 +      dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
 +              struct wpabuf *ie;
 +              ie = wpa_bss_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE);
 +              if (!ie)
 +                      continue;
 +              if (wps_is_selected_pbc_registrar(ie))
 +                      pbc++;
 +              else if (wps_is_addr_authorized(ie, wpa_s->own_addr, 0))
 +                      auth++;
 +              else if (wps_is_selected_pin_registrar(ie))
 +                      pin++;
 +              else
 +                      wps++;
 +              wpabuf_free(ie);
 +      }
 +
 +      if (pbc)
 +              wpa_msg_ctrl(wpa_s, MSG_INFO, WPS_EVENT_AP_AVAILABLE_PBC);
 +      else if (auth)
 +              wpa_msg_ctrl(wpa_s, MSG_INFO, WPS_EVENT_AP_AVAILABLE_AUTH);
 +      else if (pin)
 +              wpa_msg_ctrl(wpa_s, MSG_INFO, WPS_EVENT_AP_AVAILABLE_PIN);
 +      else if (wps)
 +              wpa_msg_ctrl(wpa_s, MSG_INFO, WPS_EVENT_AP_AVAILABLE);
 +}
 +
 +
 +int wpas_wps_searching(struct wpa_supplicant *wpa_s)
 +{
 +      struct wpa_ssid *ssid;
 +
 +      for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
 +              if ((ssid->key_mgmt & WPA_KEY_MGMT_WPS) && !ssid->disabled)
 +                      return 1;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +int wpas_wps_scan_result_text(const u8 *ies, size_t ies_len, char *buf,
 +                            char *end)
 +{
 +      struct wpabuf *wps_ie;
 +      int ret;
 +
 +      wps_ie = ieee802_11_vendor_ie_concat(ies, ies_len, WPS_DEV_OUI_WFA);
 +      if (wps_ie == NULL)
 +              return 0;
 +
 +      ret = wps_attr_text(wps_ie, buf, end);
 +      wpabuf_free(wps_ie);
 +      return ret;
 +}
 +
 +
 +int wpas_wps_er_start(struct wpa_supplicant *wpa_s, const char *filter)
 +{
 +#ifdef CONFIG_WPS_ER
 +      if (wpa_s->wps_er) {
 +              wps_er_refresh(wpa_s->wps_er);
 +              return 0;
 +      }
 +      wpa_s->wps_er = wps_er_init(wpa_s->wps, wpa_s->ifname, filter);
 +      if (wpa_s->wps_er == NULL)
 +              return -1;
 +      return 0;
 +#else /* CONFIG_WPS_ER */
 +      return 0;
 +#endif /* CONFIG_WPS_ER */
 +}
 +
 +
 +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 */
 +}
 +
 +
 +#ifdef CONFIG_WPS_ER
 +int wpas_wps_er_add_pin(struct wpa_supplicant *wpa_s, const u8 *addr,
 +                      const char *uuid, const char *pin)
 +{
 +      u8 u[UUID_LEN];
 +      const u8 *use_uuid = NULL;
 +      u8 addr_buf[ETH_ALEN];
 +
 +      if (os_strcmp(uuid, "any") == 0) {
 +      } else if (uuid_str2bin(uuid, u) == 0) {
 +              use_uuid = u;
 +      } else if (hwaddr_aton(uuid, addr_buf) == 0) {
 +              use_uuid = wps_er_get_sta_uuid(wpa_s->wps_er, addr_buf);
 +              if (use_uuid == NULL)
 +                      return -1;
 +      } else
 +              return -1;
 +      return wps_registrar_add_pin(wpa_s->wps->registrar, addr,
 +                                   use_uuid,
 +                                   (const u8 *) pin, os_strlen(pin), 300);
 +}
 +
 +
 +int wpas_wps_er_pbc(struct wpa_supplicant *wpa_s, const char *uuid)
 +{
 +      u8 u[UUID_LEN], *use_uuid = NULL;
 +      u8 addr[ETH_ALEN], *use_addr = NULL;
 +
 +      if (uuid_str2bin(uuid, u) == 0)
 +              use_uuid = u;
 +      else if (hwaddr_aton(uuid, addr) == 0)
 +              use_addr = addr;
 +      else
 +              return -1;
 +      return wps_er_pbc(wpa_s->wps_er, use_uuid, use_addr);
 +}
 +
 +
 +int wpas_wps_er_learn(struct wpa_supplicant *wpa_s, const char *uuid,
 +                    const char *pin)
 +{
 +      u8 u[UUID_LEN], *use_uuid = NULL;
 +      u8 addr[ETH_ALEN], *use_addr = NULL;
 +
 +      if (uuid_str2bin(uuid, u) == 0)
 +              use_uuid = u;
 +      else if (hwaddr_aton(uuid, addr) == 0)
 +              use_addr = addr;
 +      else
 +              return -1;
 +
 +      return wps_er_learn(wpa_s->wps_er, use_uuid, use_addr, (const u8 *) pin,
 +                          os_strlen(pin));
 +}
 +
 +
 +static int wpas_wps_network_to_cred(struct wpa_ssid *ssid,
 +                                  struct wps_credential *cred)
 +{
 +      os_memset(cred, 0, sizeof(*cred));
-       int r;
++      if (ssid->ssid_len > SSID_MAX_LEN)
 +              return -1;
 +      os_memcpy(cred->ssid, ssid->ssid, ssid->ssid_len);
 +      cred->ssid_len = ssid->ssid_len;
 +      if (ssid->key_mgmt & WPA_KEY_MGMT_PSK) {
 +              cred->auth_type = (ssid->proto & WPA_PROTO_RSN) ?
 +                      WPS_AUTH_WPA2PSK : WPS_AUTH_WPAPSK;
 +              if (ssid->pairwise_cipher & WPA_CIPHER_CCMP)
 +                      cred->encr_type = WPS_ENCR_AES;
 +              else
 +                      cred->encr_type = WPS_ENCR_TKIP;
 +              if (ssid->passphrase) {
 +                      cred->key_len = os_strlen(ssid->passphrase);
 +                      if (cred->key_len >= 64)
 +                              return -1;
 +                      os_memcpy(cred->key, ssid->passphrase, cred->key_len);
 +              } else if (ssid->psk_set) {
 +                      cred->key_len = 32;
 +                      os_memcpy(cred->key, ssid->psk, 32);
 +              } else
 +                      return -1;
 +      } else {
 +              cred->auth_type = WPS_AUTH_OPEN;
 +              cred->encr_type = WPS_ENCR_NONE;
 +      }
 +
 +      return 0;
 +}
 +
 +
 +int wpas_wps_er_set_config(struct wpa_supplicant *wpa_s, const char *uuid,
 +                         int id)
 +{
 +      u8 u[UUID_LEN], *use_uuid = NULL;
 +      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;
 +      else if (hwaddr_aton(uuid, addr) == 0)
 +              use_addr = addr;
 +      else
 +              return -1;
 +      ssid = wpa_config_get_network(wpa_s->conf, id);
 +      if (ssid == NULL || ssid->ssid == NULL)
 +              return -1;
 +
 +      if (wpas_wps_network_to_cred(ssid, &cred) < 0)
 +              return -1;
 +      ret = wps_er_set_config(wpa_s->wps_er, use_uuid, use_addr, &cred);
 +      os_memset(&cred, 0, sizeof(cred));
 +      return ret;
 +}
 +
 +
 +int wpas_wps_er_config(struct wpa_supplicant *wpa_s, const char *uuid,
 +                     const char *pin, struct wps_new_ap_settings *settings)
 +{
 +      u8 u[UUID_LEN], *use_uuid = NULL;
 +      u8 addr[ETH_ALEN], *use_addr = NULL;
 +      struct wps_credential cred;
 +      size_t len;
 +
 +      if (uuid_str2bin(uuid, u) == 0)
 +              use_uuid = u;
 +      else if (hwaddr_aton(uuid, addr) == 0)
 +              use_addr = addr;
 +      else
 +              return -1;
 +      if (settings->ssid_hex == NULL || settings->auth == NULL ||
 +          settings->encr == NULL || settings->key_hex == NULL)
 +              return -1;
 +
 +      os_memset(&cred, 0, sizeof(cred));
 +      len = os_strlen(settings->ssid_hex);
 +      if ((len & 1) || len > 2 * sizeof(cred.ssid) ||
 +          hexstr2bin(settings->ssid_hex, cred.ssid, len / 2))
 +              return -1;
 +      cred.ssid_len = len / 2;
 +
 +      len = os_strlen(settings->key_hex);
 +      if ((len & 1) || len > 2 * sizeof(cred.key) ||
 +          hexstr2bin(settings->key_hex, cred.key, len / 2))
 +              return -1;
 +      cred.key_len = len / 2;
 +
 +      if (os_strcmp(settings->auth, "OPEN") == 0)
 +              cred.auth_type = WPS_AUTH_OPEN;
 +      else if (os_strcmp(settings->auth, "WPAPSK") == 0)
 +              cred.auth_type = WPS_AUTH_WPAPSK;
 +      else if (os_strcmp(settings->auth, "WPA2PSK") == 0)
 +              cred.auth_type = WPS_AUTH_WPA2PSK;
 +      else
 +              return -1;
 +
 +      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)
 +              cred.encr_type = WPS_ENCR_AES;
 +      else
 +              return -1;
 +
 +      return wps_er_config(wpa_s->wps_er, use_uuid, use_addr,
 +                           (const u8 *) pin, os_strlen(pin), &cred);
 +}
 +
 +
 +#ifdef CONFIG_WPS_NFC
 +struct wpabuf * wpas_wps_er_nfc_config_token(struct wpa_supplicant *wpa_s,
 +                                           int ndef, const char *uuid)
 +{
 +      struct wpabuf *ret;
 +      u8 u[UUID_LEN], *use_uuid = NULL;
 +      u8 addr[ETH_ALEN], *use_addr = NULL;
 +
 +      if (!wpa_s->wps_er)
 +              return NULL;
 +
 +      if (uuid_str2bin(uuid, u) == 0)
 +              use_uuid = u;
 +      else if (hwaddr_aton(uuid, addr) == 0)
 +              use_addr = addr;
 +      else
 +              return NULL;
 +
 +      ret = wps_er_nfc_config_token(wpa_s->wps_er, use_uuid, use_addr);
 +      if (ndef && ret) {
 +              struct wpabuf *tmp;
 +              tmp = ndef_build_wifi(ret);
 +              wpabuf_free(ret);
 +              if (tmp == NULL)
 +                      return NULL;
 +              ret = tmp;
 +      }
 +
 +      return ret;
 +}
 +#endif /* CONFIG_WPS_NFC */
 +
 +
 +static int callbacks_pending = 0;
 +
 +static void wpas_wps_terminate_cb(void *ctx)
 +{
 +      wpa_printf(MSG_DEBUG, "WPS ER: Terminated");
 +      if (--callbacks_pending <= 0)
 +              eloop_terminate();
 +}
 +#endif /* CONFIG_WPS_ER */
 +
 +
 +int wpas_wps_terminate_pending(struct wpa_supplicant *wpa_s)
 +{
 +#ifdef CONFIG_WPS_ER
 +      if (wpa_s->wps_er) {
 +              callbacks_pending++;
 +              wps_er_deinit(wpa_s->wps_er, wpas_wps_terminate_cb, wpa_s);
 +              wpa_s->wps_er = NULL;
 +              return 1;
 +      }
 +#endif /* CONFIG_WPS_ER */
 +      return 0;
 +}
 +
 +
 +void wpas_wps_update_config(struct wpa_supplicant *wpa_s)
 +{
 +      struct wps_context *wps = wpa_s->wps;
 +
 +      if (wps == NULL)
 +              return;
 +
 +      if (wpa_s->conf->changed_parameters & CFG_CHANGED_CONFIG_METHODS) {
 +              wps->config_methods = wps_config_methods_str2bin(
 +                      wpa_s->conf->config_methods);
 +              if ((wps->config_methods &
 +                   (WPS_CONFIG_DISPLAY | WPS_CONFIG_LABEL)) ==
 +                  (WPS_CONFIG_DISPLAY | WPS_CONFIG_LABEL)) {
 +                      wpa_printf(MSG_ERROR, "WPS: Both Label and Display "
 +                                 "config methods are not allowed at the "
 +                                 "same time");
 +                      wps->config_methods &= ~WPS_CONFIG_LABEL;
 +              }
 +      }
 +      wps->config_methods = wps_fix_config_methods(wps->config_methods);
 +      wps->dev.config_methods = wps->config_methods;
 +
 +      if (wpa_s->conf->changed_parameters & CFG_CHANGED_DEVICE_TYPE)
 +              os_memcpy(wps->dev.pri_dev_type, wpa_s->conf->device_type,
 +                        WPS_DEV_TYPE_LEN);
 +
 +      if (wpa_s->conf->changed_parameters & CFG_CHANGED_SEC_DEVICE_TYPE) {
 +              wps->dev.num_sec_dev_types = wpa_s->conf->num_sec_device_types;
 +              os_memcpy(wps->dev.sec_dev_type, wpa_s->conf->sec_device_type,
 +                        wps->dev.num_sec_dev_types * WPS_DEV_TYPE_LEN);
 +      }
 +
 +      if (wpa_s->conf->changed_parameters & CFG_CHANGED_VENDOR_EXTENSION)
 +              wpas_wps_set_vendor_ext_m1(wpa_s, wps);
 +
 +      if (wpa_s->conf->changed_parameters & CFG_CHANGED_OS_VERSION)
 +              wps->dev.os_version = WPA_GET_BE32(wpa_s->conf->os_version);
 +
 +      if (wpa_s->conf->changed_parameters & CFG_CHANGED_UUID)
 +              wpas_wps_set_uuid(wpa_s, wps);
 +
 +      if (wpa_s->conf->changed_parameters &
 +          (CFG_CHANGED_DEVICE_NAME | CFG_CHANGED_WPS_STRING)) {
 +              /* Update pointers to make sure they refer current values */
 +              wps->dev.device_name = wpa_s->conf->device_name;
 +              wps->dev.manufacturer = wpa_s->conf->manufacturer;
 +              wps->dev.model_name = wpa_s->conf->model_name;
 +              wps->dev.model_number = wpa_s->conf->model_number;
 +              wps->dev.serial_number = wpa_s->conf->serial_number;
 +      }
 +}
 +
 +
 +#ifdef CONFIG_WPS_NFC
 +
 +#ifdef CONFIG_WPS_ER
 +static struct wpabuf *
 +wpas_wps_network_config_token(struct wpa_supplicant *wpa_s, int ndef,
 +                            struct wpa_ssid *ssid)
 +{
 +      struct wpabuf *ret;
 +      struct wps_credential cred;
 +
 +      if (wpas_wps_network_to_cred(ssid, &cred) < 0)
 +              return NULL;
 +
 +      ret = wps_er_config_token_from_cred(wpa_s->wps, &cred);
 +
 +      if (ndef && ret) {
 +              struct wpabuf *tmp;
 +              tmp = ndef_build_wifi(ret);
 +              wpabuf_free(ret);
 +              if (tmp == NULL)
 +                      return NULL;
 +              ret = tmp;
 +      }
 +
 +      return ret;
 +}
 +#endif /* CONFIG_WPS_ER */
 +
 +
 +struct wpabuf * wpas_wps_nfc_config_token(struct wpa_supplicant *wpa_s,
 +                                        int ndef, const char *id_str)
 +{
 +#ifdef CONFIG_WPS_ER
 +      if (id_str) {
 +              int id;
 +              char *end = NULL;
 +              struct wpa_ssid *ssid;
 +
 +              id = strtol(id_str, &end, 10);
 +              if (end && *end)
 +                      return NULL;
 +
 +              ssid = wpa_config_get_network(wpa_s->conf, id);
 +              if (ssid == NULL)
 +                      return NULL;
 +              return wpas_wps_network_config_token(wpa_s, ndef, ssid);
 +      }
 +#endif /* CONFIG_WPS_ER */
 +#ifdef CONFIG_AP
 +      if (wpa_s->ap_iface)
 +              return wpas_ap_wps_nfc_config_token(wpa_s, ndef);
 +#endif /* CONFIG_AP */
 +      return NULL;
 +}
 +
 +
 +struct wpabuf * wpas_wps_nfc_token(struct wpa_supplicant *wpa_s, int ndef)
 +{
 +      if (wpa_s->conf->wps_nfc_pw_from_config) {
 +              return wps_nfc_token_build(ndef,
 +                                         wpa_s->conf->wps_nfc_dev_pw_id,
 +                                         wpa_s->conf->wps_nfc_dh_pubkey,
 +                                         wpa_s->conf->wps_nfc_dev_pw);
 +      }
 +
 +      return wps_nfc_token_gen(ndef, &wpa_s->conf->wps_nfc_dev_pw_id,
 +                               &wpa_s->conf->wps_nfc_dh_pubkey,
 +                               &wpa_s->conf->wps_nfc_dh_privkey,
 +                               &wpa_s->conf->wps_nfc_dev_pw);
 +}
 +
 +
 +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_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);
 +      wpabuf_free(wps->dh_privkey);
 +      wps->dh_privkey = wpabuf_dup(wpa_s->conf->wps_nfc_dh_privkey);
 +      wps->dh_pubkey = wpabuf_dup(wpa_s->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;
 +              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);
 +      if (wps->dh_ctx == NULL) {
 +              wpabuf_free(wps->dh_pubkey);
 +              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;
 +      }
 +
 +      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)
 +{
 +      /*
 +       * Disable existing networks temporarily to allow the newly learned
 +       * credential to be preferred. Enable the temporarily disabled networks
 +       * after 10 seconds.
 +       */
 +      wpas_wps_temp_disable(wpa_s, NULL);
 +      eloop_register_timeout(10, 0, wpas_wps_reenable_networks_cb, wpa_s,
 +                             NULL);
 +
 +      if (wps_oob_use_cred(wpa_s->wps, attr) < 0)
 +              return -1;
 +
 +      if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED)
 +              return 0;
 +
 +      if (attr->ap_channel) {
 +              u16 chan = WPA_GET_BE16(attr->ap_channel);
 +              int freq = 0;
 +
 +              if (chan >= 1 && chan <= 13)
 +                      freq = 2407 + 5 * chan;
 +              else if (chan == 14)
 +                      freq = 2484;
 +              else if (chan >= 30)
 +                      freq = 5000 + 5 * chan;
 +
 +              if (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;
 +}
 +
 +
 +#ifdef CONFIG_WPS_ER
 +static int wpas_wps_add_nfc_password_token(struct wpa_supplicant *wpa_s,
 +                                         struct wps_parse_attr *attr)
 +{
 +      return wps_registrar_add_nfc_password_token(
 +              wpa_s->wps->registrar, attr->oob_dev_password,
 +              attr->oob_dev_password_len);
 +}
 +#endif /* CONFIG_WPS_ER */
 +
 +
 +static int wpas_wps_nfc_tag_process(struct wpa_supplicant *wpa_s,
 +                                  const struct wpabuf *wps)
 +{
 +      struct wps_parse_attr attr;
 +
 +      wpa_hexdump_buf(MSG_DEBUG, "WPS: Received NFC tag payload", wps);
 +
 +      if (wps_parse_msg(wps, &attr)) {
 +              wpa_printf(MSG_DEBUG, "WPS: Ignore invalid data from NFC tag");
 +              return -1;
 +      }
 +
 +      if (attr.num_cred)
 +              return wpas_wps_use_cred(wpa_s, &attr);
 +
 +#ifdef CONFIG_WPS_ER
 +      if (attr.oob_dev_password)
 +              return wpas_wps_add_nfc_password_token(wpa_s, &attr);
 +#endif /* CONFIG_WPS_ER */
 +
 +      wpa_printf(MSG_DEBUG, "WPS: Ignore unrecognized NFC tag");
 +      return -1;
 +}
 +
 +
 +int wpas_wps_nfc_tag_read(struct wpa_supplicant *wpa_s,
 +                        const struct wpabuf *data, int forced_freq)
 +{
 +      const struct wpabuf *wps = data;
 +      struct wpabuf *tmp = NULL;
 +      int ret;
 +
 +      if (wpabuf_len(data) < 4)
 +              return -1;
 +
 +      if (*wpabuf_head_u8(data) != 0x10) {
 +              /* 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;
 +              }
 +              wps = tmp;
 +      }
 +
 +      ret = wpas_wps_nfc_tag_process(wpa_s, wps);
 +      wpabuf_free(tmp);
 +      return ret;
 +}
 +
 +
 +struct wpabuf * wpas_wps_nfc_handover_req(struct wpa_supplicant *wpa_s,
 +                                        int ndef)
 +{
 +      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
 +
 +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 (wps == NULL)
 +              return NULL;
 +
 +      if (uuid == NULL)
 +              return NULL;
 +      if (uuid_str2bin(uuid, u) == 0)
 +              use_uuid = u;
 +      else if (hwaddr_aton(uuid, addr) == 0)
 +              use_addr = addr;
 +      else
 +              return NULL;
 +
 +      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);
 +              wpabuf_free(ret);
 +              if (tmp == NULL)
 +                      return NULL;
 +              ret = tmp;
 +      }
 +
 +      return ret;
 +#else /* CONFIG_WPS_ER */
 +      return NULL;
 +#endif /* CONFIG_WPS_ER */
 +}
 +#endif /* CONFIG_WPS_NFC */
 +
 +
 +struct wpabuf * wpas_wps_nfc_handover_sel(struct wpa_supplicant *wpa_s,
 +                                        int ndef, int cr, const char *uuid)
 +{
 +      struct wpabuf *ret;
 +      if (!cr)
 +              return NULL;
 +      ret = wpas_ap_wps_nfc_handover_sel(wpa_s, ndef);
 +      if (ret)
 +              return ret;
 +      return wpas_wps_er_nfc_handover_sel(wpa_s, ndef, uuid);
 +}
 +
 +
 +static int wpas_wps_nfc_rx_handover_sel(struct wpa_supplicant *wpa_s,
 +                                      const struct wpabuf *data)
 +{
 +      struct wpabuf *wps;
 +      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(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;
++              else if (chan >= 1 && chan <= 4 &&
++                       (attr.rf_bands == NULL ||
++                        *attr.rf_bands & WPS_RF_60GHZ))
++                      freq = 56160 + 2160 * 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;
 +}
 +
 +
 +int wpas_wps_nfc_report_handover(struct wpa_supplicant *wpa_s,
 +                               const struct wpabuf *req,
 +                               const struct wpabuf *sel)
 +{
 +      wpa_printf(MSG_DEBUG, "NFC: WPS connection handover reported");
 +      wpa_hexdump_buf_key(MSG_DEBUG, "WPS: Carrier record in request", req);
 +      wpa_hexdump_buf_key(MSG_DEBUG, "WPS: Carrier record in select", sel);
 +      return wpas_wps_nfc_rx_handover_sel(wpa_s, sel);
 +}
 +
 +
 +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 */
 +
 +
 +static void wpas_wps_dump_ap_info(struct wpa_supplicant *wpa_s)
 +{
 +      size_t i;
 +      struct os_reltime now;
 +
 +      if (wpa_debug_level > MSG_DEBUG)
 +              return;
 +
 +      if (wpa_s->wps_ap == NULL)
 +              return;
 +
 +      os_get_reltime(&now);
 +
 +      for (i = 0; i < wpa_s->num_wps_ap; i++) {
 +              struct wps_ap_info *ap = &wpa_s->wps_ap[i];
 +              struct wpa_blacklist *e = wpa_blacklist_get(wpa_s, ap->bssid);
 +
 +              wpa_printf(MSG_DEBUG, "WPS: AP[%d] " MACSTR " type=%d "
 +                         "tries=%d last_attempt=%d sec ago blacklist=%d",
 +                         (int) i, MAC2STR(ap->bssid), ap->type, ap->tries,
 +                         ap->last_attempt.sec > 0 ?
 +                         (int) now.sec - (int) ap->last_attempt.sec : -1,
 +                         e ? e->count : 0);
 +      }
 +}
 +
 +
 +static struct wps_ap_info * wpas_wps_get_ap_info(struct wpa_supplicant *wpa_s,
 +                                               const u8 *bssid)
 +{
 +      size_t i;
 +
 +      if (wpa_s->wps_ap == NULL)
 +              return NULL;
 +
 +      for (i = 0; i < wpa_s->num_wps_ap; i++) {
 +              struct wps_ap_info *ap = &wpa_s->wps_ap[i];
 +              if (os_memcmp(ap->bssid, bssid, ETH_ALEN) == 0)
 +                      return ap;
 +      }
 +
 +      return NULL;
 +}
 +
 +
 +static void wpas_wps_update_ap_info_bss(struct wpa_supplicant *wpa_s,
 +                                      struct wpa_scan_res *res)
 +{
 +      struct wpabuf *wps;
 +      enum wps_ap_info_type type;
 +      struct wps_ap_info *ap;
-       wpabuf_free(wps);
++      int r, pbc_active;
++      const u8 *uuid;
 +
 +      if (wpa_scan_get_vendor_ie(res, WPS_IE_VENDOR_TYPE) == NULL)
 +              return;
 +
 +      wps = wpa_scan_get_vendor_ie_multi(res, WPS_IE_VENDOR_TYPE);
 +      if (wps == NULL)
 +              return;
 +
 +      r = wps_is_addr_authorized(wps, wpa_s->own_addr, 1);
 +      if (r == 2)
 +              type = WPS_AP_SEL_REG_OUR;
 +      else if (r == 1)
 +              type = WPS_AP_SEL_REG;
 +      else
 +              type = WPS_AP_NOT_SEL_REG;
 +
-               return;
++      uuid = wps_get_uuid_e(wps);
++      pbc_active = wps_is_selected_pbc_registrar(wps);
 +
 +      ap = wpas_wps_get_ap_info(wpa_s, res->bssid);
 +      if (ap) {
 +              if (ap->type != type) {
 +                      wpa_printf(MSG_DEBUG, "WPS: AP " MACSTR
 +                                 " changed type %d -> %d",
 +                                 MAC2STR(res->bssid), ap->type, type);
 +                      ap->type = type;
 +                      if (type != WPS_AP_NOT_SEL_REG)
 +                              wpa_blacklist_del(wpa_s, ap->bssid);
 +              }
-               return;
++              ap->pbc_active = pbc_active;
++              if (uuid)
++                      os_memcpy(ap->uuid, uuid, WPS_UUID_LEN);
++              goto out;
 +      }
 +
 +      ap = os_realloc_array(wpa_s->wps_ap, wpa_s->num_wps_ap + 1,
 +                            sizeof(struct wps_ap_info));
 +      if (ap == NULL)
++              goto out;
 +
 +      wpa_s->wps_ap = ap;
 +      ap = &wpa_s->wps_ap[wpa_s->num_wps_ap];
 +      wpa_s->num_wps_ap++;
 +
 +      os_memset(ap, 0, sizeof(*ap));
 +      os_memcpy(ap->bssid, res->bssid, ETH_ALEN);
 +      ap->type = type;
++      ap->pbc_active = pbc_active;
++      if (uuid)
++              os_memcpy(ap->uuid, uuid, WPS_UUID_LEN);
 +      wpa_printf(MSG_DEBUG, "WPS: AP " MACSTR " type %d added",
 +                 MAC2STR(ap->bssid), ap->type);
++
++out:
++      wpabuf_free(wps);
 +}
 +
 +
 +void wpas_wps_update_ap_info(struct wpa_supplicant *wpa_s,
 +                           struct wpa_scan_results *scan_res)
 +{
 +      size_t i;
 +
 +      for (i = 0; i < scan_res->num; i++)
 +              wpas_wps_update_ap_info_bss(wpa_s, scan_res->res[i]);
 +
 +      wpas_wps_dump_ap_info(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_reltime(&ap->last_attempt);
 +}
index 683bd50e4bab05b46ea8ea005e42cd473dcd0ccb,0000000000000000000000000000000000000000..3c25ca86dc65e7d757b85d22e69e931107f75aae
mode 100644,000000..100644
--- /dev/null
@@@ -1,151 -1,0 +1,152 @@@
 +/*
 + * wpa_supplicant / WPS integration
 + * Copyright (c) 2008-2012, Jouni Malinen <j@w1.fi>
 + *
 + * This software may be distributed under the terms of the BSD license.
 + * See README for more details.
 + */
 +
 +#ifndef WPS_SUPPLICANT_H
 +#define WPS_SUPPLICANT_H
 +
 +struct wpa_scan_results;
 +
 +#ifdef CONFIG_WPS
 +
 +#include "wps/wps.h"
 +#include "wps/wps_defs.h"
 +
 +struct wpa_bss;
 +
 +struct wps_new_ap_settings {
 +      const char *ssid_hex;
 +      const char *auth;
 +      const char *encr;
 +      const char *key_hex;
 +};
 +
 +int wpas_wps_init(struct wpa_supplicant *wpa_s);
 +void wpas_wps_deinit(struct wpa_supplicant *wpa_s);
 +int wpas_wps_eapol_cb(struct wpa_supplicant *wpa_s);
 +enum wps_request_type wpas_wps_get_req_type(struct wpa_ssid *ssid);
 +int wpas_wps_start_pbc(struct wpa_supplicant *wpa_s, const u8 *bssid,
 +                     int p2p_group);
 +int wpas_wps_start_pin(struct wpa_supplicant *wpa_s, const u8 *bssid,
 +                     const char *pin, int p2p_group, u16 dev_pw_id);
++void wpas_wps_pbc_overlap(struct wpa_supplicant *wpa_s);
 +int wpas_wps_cancel(struct wpa_supplicant *wpa_s);
 +int wpas_wps_start_reg(struct wpa_supplicant *wpa_s, const u8 *bssid,
 +                     const char *pin, struct wps_new_ap_settings *settings);
 +int wpas_wps_ssid_bss_match(struct wpa_supplicant *wpa_s,
 +                          struct wpa_ssid *ssid, struct wpa_bss *bss);
 +int wpas_wps_ssid_wildcard_ok(struct wpa_supplicant *wpa_s,
 +                            struct wpa_ssid *ssid, struct wpa_bss *bss);
 +int wpas_wps_scan_pbc_overlap(struct wpa_supplicant *wpa_s,
 +                            struct wpa_bss *selected, struct wpa_ssid *ssid);
 +void wpas_wps_notify_scan_results(struct wpa_supplicant *wpa_s);
 +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);
 +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);
 +int wpas_wps_er_learn(struct wpa_supplicant *wpa_s, const char *uuid,
 +                    const char *pin);
 +int wpas_wps_er_set_config(struct wpa_supplicant *wpa_s, const char *uuid,
 +                         int id);
 +int wpas_wps_er_config(struct wpa_supplicant *wpa_s, const char *uuid,
 +                     const char *pin, struct wps_new_ap_settings *settings);
 +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);
 +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 *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, 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_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);
 +
 +#else /* CONFIG_WPS */
 +
 +static inline int wpas_wps_init(struct wpa_supplicant *wpa_s)
 +{
 +      return 0;
 +}
 +
 +static inline void wpas_wps_deinit(struct wpa_supplicant *wpa_s)
 +{
 +}
 +
 +static inline int wpas_wps_eapol_cb(struct wpa_supplicant *wpa_s)
 +{
 +      return 0;
 +}
 +
 +static inline u8 wpas_wps_get_req_type(struct wpa_ssid *ssid)
 +{
 +      return 0;
 +}
 +
 +static inline int wpas_wps_ssid_bss_match(struct wpa_supplicant *wpa_s,
 +                                        struct wpa_ssid *ssid,
 +                                        struct wpa_bss *bss)
 +{
 +      return -1;
 +}
 +
 +static inline int wpas_wps_ssid_wildcard_ok(struct wpa_supplicant *wpa_s,
 +                                          struct wpa_ssid *ssid,
 +                                          struct wpa_bss *bss)
 +{
 +      return 0;
 +}
 +
 +static inline int wpas_wps_scan_pbc_overlap(struct wpa_supplicant *wpa_s,
 +                                          struct wpa_bss *selected,
 +                                          struct wpa_ssid *ssid)
 +{
 +      return 0;
 +}
 +
 +static inline void wpas_wps_notify_scan_results(struct wpa_supplicant *wpa_s)
 +{
 +}
 +
 +static inline int wpas_wps_searching(struct wpa_supplicant *wpa_s)
 +{
 +      return 0;
 +}
 +
 +static inline void wpas_wps_update_ap_info(struct wpa_supplicant *wpa_s,
 +                                         struct wpa_scan_results *scan_res)
 +{
 +}
 +
 +static inline void wpas_wps_notify_assoc(struct wpa_supplicant *wpa_s,
 +                                       const u8 *bssid)
 +{
 +}
 +
 +#endif /* CONFIG_WPS */
 +
 +#endif /* WPS_SUPPLICANT_H */
index 46cd9ef9a03ced29c4cfa8c2bb133534000309e3,0000000000000000000000000000000000000000..5c03f7d21d0e33a0eff67d3312df0a1e46ec2c07
mode 100644,000000..100644
--- /dev/null
@@@ -1,133 -1,0 +1,134 @@@
- SRCS+=        crypto_openssl.c random.c sha1-prf.c sha256-prf.c
 +# $FreeBSD$
 +
 +.if ${MK_OPENSSL} != "no" && !defined(RELEASE_CRUNCH)
++SRCS+=        crypto_openssl.c random.c sha1-prf.c sha256-prf.c sha256-tlsprf.c
 +LIBADD+=      ssl crypto
 +CFLAGS+= -DCONFIG_SHA256
 +.else
 +CFLAGS+=-DCONFIG_CRYPTO_INTERNAL
 +SRCS+=        crypto_internal.c random.c
 +CONFIG_INTERNAL_AES=y
 +CONFIG_INTERNAL_DES=y
 +CONFIG_INTERNAL_MD4=y
 +CONFIG_INTERNAL_MD5=y
 +CONFIG_INTERNAL_RC4=y
 +CONFIG_INTERNAL_SHA1=y
 +NEED_SHA256=y
 +CONFIG_INTERNAL_SHA256=y
 +CONFIG_INTERNAL_TLS=y
 +CONFIG_INTERNAL_DH5=y
 +CONFIG_INTERNAL_DH=y
 +NEED_AES_ENC=true
++NEED_AES_CBC=true
 +.endif
 +
 +.if defined(TLS_FUNCS)
 +NEED_TLS_PRF=y
 +.if defined(CONFIG_INTERNAL_TLS)
 +CFLAGS+=-DCONFIG_INTERNAL_LIBTOMMATH \
 +      -DCONFIG_TLS_INTERNAL_CLIENT
 +SRCS+=        asn1.c \
 +      bignum.c \
 +      crypto_internal-cipher.c \
 +      crypto_internal-modexp.c \
 +      crypto_internal-rsa.c \
 +      pkcs1.c \
 +      pkcs5.c \
 +      pkcs8.c \
 +      rsa.c \
 +      tls_internal.c \
 +      tlsv1_common.c \
 +      tlsv1_record.c \
 +      tlsv1_cred.c \
 +      tlsv1_client.c \
 +      tlsv1_client_write.c \
 +      tlsv1_client_read.c \
 +      x509v3.c
 +NEED_DES=y
 +NEED_MD4=y
 +NEED_RC4=y
 +.else
 +CFLAGS+=-DEAP_TLS_OPENSSL
 +SRCS+=        tls_openssl.c
 +.endif
 +.endif
 +
 +.if defined(CONFIG_INTERNAL_AES)
 +SRCS+=        aes-unwrap.c aes-wrap.c \
 +      aes-internal.c \
 +      aes-internal-dec.c \
 +      aes-internal-enc.c
 +.endif
 +
 +.if defined(NEED_AES_CBC)
 +SRCS+=        aes-cbc.c
 +.endif
 +
 +.if defined(NEED_AES_EAX)
 +SRCS+=        aes-eax.c
 +NEED_AES_CTR=y
 +.endif
 +
 +.if defined(NEED_AES_CTR)
 +SRCS+=        aes-ctr.c
 +.endif
 +
 +.if defined(NEED_AES_ENCBLOCK)
 +SRCS+=        aes-encblock.c
 +.endif
 +
 +.if defined(NEED_AES_OMAC1)
 +SRCS+=        aes-omac1.c
 +.endif
 +
 +.if defined(NEED_DES)
 +.if defined(CONFIG_INTERNAL_DES)
 +SRCS+=        des-internal.c
 +.endif
 +.endif
 +
 +.if defined(NEED_MD4)
 +.if defined(CONFIG_INTERNAL_MD4)
 +SRCS+=        md4-internal.c
 +.endif
 +.endif
 +
 +.if defined(CONFIG_INTERNAL_MD5)
 +SRCS+=        md5.c md5-internal.c
 +.endif
 +
 +.if defined(NEED_FIPS186_2_PRF)
 +.if defined(CONFIG_INTERNAL_SHA1)
 +SRCS+=        fips_prf_internal.c
 +.else
 +SRCS+=        fips_prf_openssl.c
 +.endif
 +.endif
 +
 +.if defined(CONFIG_INTERNAL_RC4)
 +SRCS+=        rc4.c
 +.endif
 +
 +.if defined(CONFIG_INTERNAL_SHA1)
 +SRCS+=        sha1-internal.c sha1-pbkdf2.c sha1.c sha1-prf.c
 +.endif
 +
 +.if defined(NEED_SHA256)
 +CFLAGS+=-DCONFIG_SHA256
 +SRCS+=        sha256.c
 +.if defined(CONFIG_INTERNAL_SHA256)
 +SRCS+=        sha256-internal.c sha256-prf.c
 +.endif
 +.endif
 +
 +.if defined(NEED_TLS_PRF)
 +SRCS+=        sha1-tlsprf.c
 +.endif
 +
 +.if defined(CONFIG_INTERNAL_DH5)
 +SRCS+=        dh_group5.c
 +.endif
 +
 +.if defined(CONFIG_INTERNAL_DH)
 +SRCS+=        dh_groups.c
 +.endif
index 743f91771b5743307d1aaaf91fe9622eb6bcb468,0000000000000000000000000000000000000000..4437839b7ad123201dc6ce20e8023cf364b4e6c8
mode 100644,000000..100644
--- /dev/null
@@@ -1,123 -1,0 +1,122 @@@
- NEED_AES_CBC=y
 +# $FreeBSD$
 +
 +.include <src.opts.mk>
 +.include "${.CURDIR}/../Makefile.inc"
 +
 +.PATH.c:${HOSTAPD_DISTDIR} \
 +      ${WPA_DISTDIR}/src/drivers
 +
 +PROG= hostapd
 +SRCS= accounting.c aes-omac1.c ap_config.c ap_drv_ops.c ap_mlme.c authsrv.c \
 +      base64.c beacon.c bss_load.c chap.c common.c config_file.c \
 +      ctrl_iface.c \
 +      ctrl_iface_ap.c driver_common.c l2_packet_freebsd.c driver_bsd.c \
 +      drivers.c drv_callbacks.c eap_common.c eap_peap_common.c \
 +      eap_register.c eap_server.c eap_server_methods.c eap_user_db.c \
 +      eapol_auth_dump.c eapol_auth_sm.c eloop.c gas.c gas_serv.c hostapd.c \
 +      hs20.c http_client.c http_server.c httpread.c \
 +      hw_features_common.c ieee802_11_auth.c \
 +      ieee802_11_common.c ieee802_11_shared.c ieee802_1x.c ip_addr.c \
 +      main.c ms_funcs.c os_unix.c peerkey_auth.c pmksa_cache_auth.c \
 +      preauth_auth.c radius.c radius_client.c radius_das.c sta_info.c \
 +      tkip_countermeasures.c upnp_xml.c utils.c uuid.c vlan_init.c \
 +      wpa_auth.c wpa_auth_glue.c wpa_auth_ie.c wpa_common.c wpa_debug.c \
 +      wpabuf.c wps.c wps_attr_build.c wps_attr_parse.c wps_attr_process.c \
 +      wps_common.c wps_dev_attr.c wps_enrollee.c wps_hostapd.c \
 +      wps_registrar.c wps_upnp.c wps_upnp_ap.c wps_upnp_event.c \
 +      wps_upnp_ssdp.c wps_upnp_web.c
 +
 +MAN=  hostapd.8 hostapd.conf.5
 +
 +.if ${MK_EXAMPLES} != "no"
 +FILESDIR= ${SHAREDIR}/examples/hostapd
 +.PATH:        ${HOSTAPD_DISTDIR}
 +FILES=        hostapd.conf hostapd.eap_user hostapd.wpa_psk
 +.endif
 +
 +CFLAGS+=-DCONFIG_DRIVER_BSD \
 +      -DCONFIG_DRIVER_RADIUS_ACL \
 +      -DCONFIG_HS20 \
 +      -DCONFIG_INTERWORKING \
 +      -DCONFIG_PEERKEY \
 +      -DCONFIG_RSN_PREAUTH \
 +      -DCONFIG_WPS \
 +      -DCONFIG_WPS2 \
 +      -DCONFIG_WPS_UPNP \
 +      -DHOSTAPD
 +.if ${MK_INET6} != "no"
 +CFLAGS+= -DCONFIG_IPV6
 +.endif
 +#CFLAGS+= -g
 +LIBADD+=      pcap util
 +
 +# User customizations for wpa_supplicant/hostapd build environment
 +CFLAGS+=${HOSTAPD_CFLAGS}
 +#DPADD+=${HOSTAPD_DPADD}
 +LDADD+=${HOSTAPD_LDADD}
 +#LDFLAGS+=${HOSTAPD_LDFLAGS}
 +
 +CFLAGS+=-DDPKCS12_FUNCS \
 +      -DEAP_SERVER \
 +      -DEAP_SERVER_GTC \
 +      -DEAP_SERVER_IDENTITY \
 +      -DEAP_SERVER_MD5 \
 +      -DEAP_SERVER_MSCHAPV2 \
 +      -DEAP_SERVER_PEAP \
 +      -DEAP_SERVER_TLS \
 +      -DEAP_SERVER_TTLS \
 +      -DEAP_SERVER_WSC \
 +      -DEAP_TLS_FUNCS
 +
 +SRCS+=        eap_server_gtc.c \
 +      eap_server_identity.c \
 +      eap_server_md5.c \
 +      eap_server_mschapv2.c \
 +      eap_server_peap.c \
 +      eap_server_tls.c \
 +      eap_server_tls_common.c \
 +      eap_server_ttls.c \
 +      eap_server_wsc.c \
 +      eap_wsc_common.c
 +TLS_FUNCS=y
 +
 +.if !empty(CFLAGS:M*-DCONFIG_WPS)
 +NEED_SIM_COMMON=y
 +.endif
 +
 +.if !empty(CFLAGS:M*-DEAP_SERVER_AKA)
 +SRCS+=        eap_server_aka.c
 +NEED_SIM_COMMON=y
 +.endif
 +
 +.if !empty(CFLAGS:M*-DEAP_SERVER_SIM)
 +SRCS+=        eap_server_sim.c
 +NEED_SIM_COMMON=y
 +.endif
 +
 +.if defined(NEED_SIM_COMMON)
 +SRCS+=        eap_sim_common.c \
 +      eap_sim_db.c
 +NEED_FIPS186_2_PRF=y
 +.endif
 +
 +.if !empty(CFLAGS:M*-DEAP_SERVER_GPSK)
 +CFLAGS+=-DEAP_GPSK_SHA256
 +SRCS+=        eap_server_gpsk.c \
 +      eap_gpsk_common.c
 +NEED_AES_OMAC1=y
 +.endif
 +
 +.if !empty(CFLAGS:M*-DEAP_SERVER_PAX)
 +SRCS+=        eap_server_pax.c \
 +      eap_pax_common.c
 +.endif
 +
 +.if !empty(CFLAGS:M*-DEAP_SERVER_SAKE)
 +SRCS+=        eap_server_sake.c \
 +      eap_sake_common.c
 +.endif
 +
 +.include "${.CURDIR}/../Makefile.crypto"
 +
 +.include <bsd.prog.mk>
index badae0dddd5d932fcabbdc19e0f0235a860d9a3f,0000000000000000000000000000000000000000..65b0c77b45f78f442d6e3cf71b5b639bd044a985
mode 100644,000000..100644
--- /dev/null
@@@ -1,151 -1,0 +1,147 @@@
- .if !empty(CFLAGS:M-DCONFIG_WPS)
- NEED_AES_CBC=y
- .endif
 +# $FreeBSD$
 +
 +.include <src.opts.mk>
 +
 +.include "${.CURDIR}/../Makefile.inc"
 +
 +.PATH.c:${WPA_SUPPLICANT_DISTDIR} \
 +      ${WPA_DISTDIR}/src/drivers
 +
 +PROG= wpa_supplicant
 +SRCS= ap_drv_ops.c base64.c blacklist.c bss.c common.c config.c \
 +      config_file.c ctrl_iface.c ctrl_iface_unix.c driver_bsd.c \
 +      driver_common.c driver_ndis.c driver_wired.c drivers.c \
 +      eap_register.c eloop.c events.c gas.c gas_query.c hs20.c \
 +      hs20_supplicant.c http_client.c http_server.c httpread.c \
 +      hw_features_common.c \
 +      ieee802_11_common.c ieee802_11_shared.c \
 +      interworking.c l2_packet_freebsd.c main.c \
 +      notify.c offchannel.c os_unix.c peerkey.c pmksa_cache.c \
 +      preauth.c scan.c upnp_xml.c uuid.c wmm_ac.c \
 +      wpa.c wpa_common.c wpa_debug.c \
 +      wpa_ft.c wpa_ie.c wpa_supplicant.c wpabuf.c wpas_glue.c wps.c \
 +      wps_attr_build.c wps_attr_parse.c wps_attr_process.c \
 +      wps_common.c wps_dev_attr.c wps_enrollee.c wps_registrar.c \
 +      wps_supplicant.c wps_upnp.c wps_upnp_ap.c wps_upnp_event.c \
 +      wps_upnp_ssdp.c wps_upnp_web.c Packet32.c
 +
 +MAN=  wpa_supplicant.8 wpa_supplicant.conf.5
 +
 +.if ${MK_EXAMPLES} != "no"
 +FILESDIR= ${SHAREDIR}/examples/etc
 +.PATH:        ${WPA_SUPPLICANT_DISTDIR}
 +FILES=        wpa_supplicant.conf
 +.endif
 +
 +CFLAGS+=-DCONFIG_BACKEND_FILE \
 +      -DCONFIG_DEBUG_SYSLOG \
 +      -DCONFIG_DRIVER_BSD \
 +      -DCONFIG_DRIVER_NDIS \
 +      -DCONFIG_DRIVER_WIRED \
 +      -DCONFIG_GAS \
 +      -DCONFIG_HS20 \
 +      -DCONFIG_IEEE80211R \
 +      -DCONFIG_INTERWORKING \
 +      -DCONFIG_PEERKEY \
 +      -DCONFIG_PRIVSEP \
 +      -DCONFIG_SMARTCARD \
 +      -DCONFIG_TERMINATE_ONLASTIF \
 +      -DCONFIG_TLS=openssl \
 +      -DCONFIG_WPS \
 +      -DCONFIG_WPS2 \
 +      -DCONFIG_WPS_UPNP \
 +      -DPKCS12_FUNCS
 +#CFLAGS+= -g
 +LIBADD=       pcap util
 +
 +# User customizations to the wpa_supplicant build environment
 +CFLAGS+=${WPA_SUPPLICANT_CFLAGS}
 +#DPADD+=${WPA_SUPPLICANT_DPADD}
 +LDADD+=${WPA_SUPPLICANT_LDADD}
 +#LDFLAGS+=${WPA_SUPPLICANT_LDFLAGS}
 +
 +.if ${MK_WPA_SUPPLICANT_EAPOL} != "no"
 +CFLAGS+=-DEAP_GTC \
 +      -DEAP_LEAP \
 +      -DEAP_MD5 \
 +      -DEAP_MSCHAPv2 \
 +      -DEAP_OTP \
 +      -DEAP_PEAP \
 +      -DEAP_PSK \
 +      -DEAP_TLS \
 +      -DEAP_TTLS \
 +      -DIEEE8021X_EAPOL
 +SRCS+=        chap.c \
 +      eap.c \
 +      eap_common.c \
 +      eap_gtc.c \
 +      eap_leap.c \
 +      eap_md5.c \
 +      eap_methods.c \
 +      eap_mschapv2.c \
 +      eap_otp.c \
 +      eap_peap.c \
 +      eap_peap_common.c \
 +      eap_psk.c \
 +      eap_psk_common.c \
 +      eap_tls.c \
 +      eap_tls_common.c \
 +      eap_ttls.c \
 +      eapol_supp_sm.c \
 +      ms_funcs.c \
 +      mschapv2.c
 +TLS_FUNCS=y
 +NEED_AES_EAX=y
 +NEED_AES_ENCBLOCK=y
 +NEED_AES_OMAC1=y
 +.endif
 +
 +.if !empty(CFLAGS:M*-DEAP_AKA)
 +SRCS+=        eap_aka.c
 +NEED_SIM_COMMON=y
 +NEED_AES_CBC=y
 +.endif
 +
 +.if !empty(CFLAGS:M*-DEAP_SIM)
 +SRCS+=        eap_sim.c
 +NEED_SIM_COMMON=y
 +NEED_AES_CBC=y
 +.endif
 +
 +.if defined(NEED_SIM_COMMON)
 +SRCS+=        eap_sim_common.c
 +NEED_FIPS186_2_PRF=y
 +.endif
 +
 +# PC/SC interface for smartcards (USIM, GSM SIM)
 +# GSM/UMTS authentication algorithm (for EAP-SIM/EAP-AKA)
 +# NB: requires devel/pcsc-lite
 +#
 +# WPA_SUPPLICANT_CFLAGS=-DEAP_AKA -DPCSC_FUNCS -I/usr/local/include/PCSC
 +# WPA_SUPPLICANT_LDADD=-L/usr/local/lib
 +#
 +.if !empty(CFLAGS:M*-DPCSC_FUNCS)
 +SRCS+=        pcsc_funcs.c
 +LIBADD+=      pcslite pthread
 +.endif
 +
 +.if !empty(CFLAGS:M*-DEAP_GPSK)
 +CFLAGS+=-DEAP_GPSK_SHA256
 +SRCS+=        eap_gpsk.c \
 +      eap_gpsk_common.c
 +NEED_AES_OMAC1=y
 +.endif
 +
 +.if !empty(CFLAGS:M*-DEAP_PAX)
 +SRCS+=        eap_pax.c \
 +      eap_pax_common.c
 +.endif
 +
 +.if !empty(CFLAGS:M*-DEAP_SAKE)
 +SRCS+=        eap_sake.c \
 +      eap_sake_common.c
 +.endif
 +
 +.include "${.CURDIR}/../Makefile.crypto"
 +
 +.include <bsd.prog.mk>